feat: Implementar PWA, Analytics, Reportes PDF y mejoras OCR
FASE 1 - PWA y Frontend: - Crear templates/base.html, dashboard.html, analytics.html, executive.html - Crear static/css/main.css con diseño responsivo - Agregar static/js/app.js, pwa.js, camera.js, charts.js - Implementar manifest.json y service-worker.js para PWA - Soporte para captura de tickets desde cámara móvil FASE 2 - Analytics: - Crear módulo analytics/ con predictions.py, trends.py, comparisons.py - Implementar predicción básica con promedio móvil + tendencia lineal - Agregar endpoints /api/analytics/trends, predictions, comparisons - Integrar Chart.js para gráficas interactivas FASE 3 - Reportes PDF: - Crear módulo reports/ con pdf_generator.py - Implementar SalesReportPDF con generar_reporte_diario y ejecutivo - Agregar comando /reporte [diario|semanal|ejecutivo] - Agregar endpoints /api/reports/generate y /api/reports/download FASE 4 - Mejoras OCR: - Crear módulo ocr/ con processor.py, preprocessor.py, patterns.py - Implementar AmountDetector con patrones múltiples de montos - Agregar preprocesador adaptativo con pipelines para diferentes condiciones - Soporte para corrección de rotación (deskew) y threshold Otsu Dependencias agregadas: - reportlab, matplotlib (PDF) - scipy, pandas (analytics) - imutils, deskew, cachetools (OCR) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
122
sales-bot/static/js/pwa.js
Normal file
122
sales-bot/static/js/pwa.js
Normal file
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* Sales Bot - PWA Registration and Install Prompt
|
||||
*/
|
||||
|
||||
// Register Service Worker
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', async () => {
|
||||
try {
|
||||
const registration = await navigator.serviceWorker.register('/service-worker.js');
|
||||
console.log('Service Worker registered:', registration.scope);
|
||||
|
||||
// Check for updates
|
||||
registration.addEventListener('updatefound', () => {
|
||||
const newWorker = registration.installing;
|
||||
newWorker.addEventListener('statechange', () => {
|
||||
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
|
||||
// New version available
|
||||
showUpdatePrompt();
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Service Worker registration failed:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Install prompt handling
|
||||
let deferredPrompt;
|
||||
|
||||
window.addEventListener('beforeinstallprompt', (e) => {
|
||||
e.preventDefault();
|
||||
deferredPrompt = e;
|
||||
showInstallPrompt();
|
||||
});
|
||||
|
||||
function showInstallPrompt() {
|
||||
// Create install prompt UI
|
||||
const prompt = document.createElement('div');
|
||||
prompt.id = 'install-prompt';
|
||||
prompt.className = 'install-prompt show';
|
||||
prompt.innerHTML = `
|
||||
<span>Instalar Sales Bot en tu dispositivo</span>
|
||||
<button class="btn btn-primary" onclick="installPWA()">Instalar</button>
|
||||
<button class="btn btn-secondary" onclick="dismissInstallPrompt()">Ahora no</button>
|
||||
`;
|
||||
document.body.appendChild(prompt);
|
||||
}
|
||||
|
||||
async function installPWA() {
|
||||
if (!deferredPrompt) return;
|
||||
|
||||
deferredPrompt.prompt();
|
||||
const { outcome } = await deferredPrompt.userChoice;
|
||||
|
||||
console.log('Install prompt outcome:', outcome);
|
||||
deferredPrompt = null;
|
||||
dismissInstallPrompt();
|
||||
|
||||
if (outcome === 'accepted') {
|
||||
if (window.Utils) {
|
||||
window.Utils.showNotification('App instalada correctamente', 'success');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function dismissInstallPrompt() {
|
||||
const prompt = document.getElementById('install-prompt');
|
||||
if (prompt) {
|
||||
prompt.remove();
|
||||
}
|
||||
}
|
||||
|
||||
function showUpdatePrompt() {
|
||||
const updateBanner = document.createElement('div');
|
||||
updateBanner.id = 'update-banner';
|
||||
updateBanner.style.cssText = `
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #00d4ff;
|
||||
color: #000;
|
||||
padding: 15px;
|
||||
text-align: center;
|
||||
z-index: 9999;
|
||||
`;
|
||||
updateBanner.innerHTML = `
|
||||
<span>Nueva version disponible</span>
|
||||
<button onclick="updateApp()" style="margin-left: 15px; padding: 5px 15px; border: none; border-radius: 4px; cursor: pointer;">
|
||||
Actualizar
|
||||
</button>
|
||||
`;
|
||||
document.body.appendChild(updateBanner);
|
||||
}
|
||||
|
||||
function updateApp() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.getRegistration().then(registration => {
|
||||
if (registration && registration.waiting) {
|
||||
registration.waiting.postMessage({ type: 'SKIP_WAITING' });
|
||||
}
|
||||
});
|
||||
}
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
// Detect if running as PWA
|
||||
function isPWA() {
|
||||
return window.matchMedia('(display-mode: standalone)').matches ||
|
||||
window.navigator.standalone === true;
|
||||
}
|
||||
|
||||
// Add PWA class to body if running as installed app
|
||||
if (isPWA()) {
|
||||
document.body.classList.add('pwa-mode');
|
||||
}
|
||||
|
||||
// Export
|
||||
window.installPWA = installPWA;
|
||||
window.dismissInstallPrompt = dismissInstallPrompt;
|
||||
window.isPWA = isPWA;
|
||||
Reference in New Issue
Block a user