// Main App Component with Authentication const { useState, useEffect } = React; function App() { const { isAuthenticated, loading: authLoading } = useAuth(); const toast = useToast(); const [currentView, setCurrentView] = useState('welcome'); const [queueData, setQueueData] = useState({ jobs: [], started: false, pending_count: 0 }); const [selectedJob, setSelectedJob] = useState(null); const [emailSettings, setEmailSettings] = useState({ mail_provider: 'virgilio', phone_number: '', email: '' }); const [confirmModal, setConfirmModal] = useState(null); // { type, jobId } const [pendingProjectId, setPendingProjectId] = useState(null); // Project to auto-open from notification const [selectedProjectId, setSelectedProjectId] = useState(null); // Project for detail view const [focusCommentId, setFocusCommentId] = useState(null); // Comment to scroll to and highlight const [focusStepId, setFocusStepId] = useState(null); // Step to expand when focusing on a comment const [prepopulatedData, setPrepopulatedData] = useState(null); // { personalData, projectId, projectName } for registration from project // SSE for queue updates (only when authenticated) useEffect(() => { if (!isAuthenticated) return; let es = null; let retryTimeout = null; const connect = () => { es = new EventSource('/queue/stream'); es.onmessage = (e) => { try { setQueueData(JSON.parse(e.data)); } catch (err) { console.error(err); } }; es.onerror = () => { es.close(); retryTimeout = setTimeout(connect, 3000); }; }; connect(); fetch('/queue/status').then(r => r.json()).then(setQueueData).catch(console.error); return () => { if (es) es.close(); if (retryTimeout) clearTimeout(retryTimeout); }; }, [isAuthenticated]); const handleStart = async () => { try { const r = await fetch('/queue/start', { method: 'POST' }); const data = await r.json(); if (data.status === 'success') toast('Pipeline avviata', 'success'); } catch (e) { toast('Errore avvio', 'error'); } }; const handleStop = async () => { try { const r = await fetch('/queue/stop', { method: 'POST' }); const data = await r.json(); if (data.status === 'success') toast('Pipeline fermata', 'info'); } catch (e) { toast('Errore arresto', 'error'); } }; const handleRemoveJob = (id) => { setConfirmModal({ type: 'archive', jobId: id }); }; const handleRetryJob = async (id) => { try { const r = await fetch(`/queue/job/${id}/retry`, { method: 'POST' }); const data = await r.json(); if (r.ok && data.status === 'success') toast('Lavoro in coda per nuovo tentativo', 'success'); } catch (e) { toast('Errore retry', 'error'); } }; const handleCancelJob = (id) => { setConfirmModal({ type: 'cancel', jobId: id }); }; const executeJobAction = async () => { if (!confirmModal) return; const { type, jobId } = confirmModal; setConfirmModal({ ...confirmModal, loading: true }); try { if (type === 'archive') { const r = await fetch(`/queue/job/${jobId}`, { method: 'DELETE' }); if (r.ok) toast('Lavoro archiviato', 'success'); } else if (type === 'cancel') { const r = await fetch(`/queue/job/${jobId}/cancel`, { method: 'POST' }); if (r.ok) toast('Richiesta annullamento inviata', 'info'); } setConfirmModal(null); } catch (e) { toast(type === 'archive' ? 'Errore archiviazione' : 'Errore annullamento', 'error'); setConfirmModal(null); } }; // Handle notification click - navigate to the relevant project/comment const handleNotificationNavigate = (notification) => { if (!notification.data?.project_id) return; const notificationType = notification.type; const isCommentNotification = ['comment_added', 'project_comment_added', 'reaction_added'].includes(notificationType); if (isCommentNotification && notification.data?.comment_id) { // Navigate directly to project detail with comment focus setSelectedProjectId(notification.data.project_id); setFocusCommentId(notification.data.comment_id); setFocusStepId(notification.data.step_id || null); setCurrentView('project-detail'); } else { // Default behavior: navigate to projects list with the project open setPendingProjectId(notification.data.project_id); setCurrentView('projects'); } }; // Clear pending project ID (called by views after handling it) const clearPendingProject = () => { setPendingProjectId(null); }; // Navigate to project detail view const handleViewProject = (projectId) => { setSelectedProjectId(projectId); setFocusCommentId(null); // Clear any focus state from notifications setFocusStepId(null); setCurrentView('project-detail'); }; // Navigate to project detail with comment focus (for notifications) const handleViewProjectWithComment = (projectId, commentId, stepId) => { setSelectedProjectId(projectId); setFocusCommentId(commentId || null); setFocusStepId(stepId || null); setCurrentView('project-detail'); }; // Navigate back from project detail const handleBackFromProject = () => { setSelectedProjectId(null); setFocusCommentId(null); setFocusStepId(null); setCurrentView('projects'); }; // Clear focus state after component has handled scrolling const clearFocusState = () => { setFocusCommentId(null); setFocusStepId(null); }; // Handle triggering email registration from a project const handleTriggerRegistration = (project) => { console.log('Triggering registration from project:', project); console.log('Project personal_data:', project.personal_data); console.log('Project residence_data:', project.residence_data); console.log('Project id_document:', project.id_document); // Transform project data to NewRegistrationView format const personalData = { // Personal data last_name: project.personal_data?.last_name || '', first_name: project.personal_data?.first_name || '', date_of_birth: project.personal_data?.date_of_birth || '', gender: project.personal_data?.gender || '', fiscal_code: project.personal_data?.fiscal_code || '', birth_city: project.personal_data?.birth_city || '', birth_province: project.personal_data?.birth_province || '', birth_nation: project.personal_data?.birth_nation || '', // Residence data address: project.residence_data?.address || '', numero_civico: project.residence_data?.number || '', cap: project.residence_data?.cap || '', residence_city: project.residence_data?.city || '', residence_province: project.residence_data?.province || '', residence_nation: project.residence_data?.nation || '', // Document data document_type: project.id_document?.type || '', document_number: project.id_document?.number || '', document_start_date: project.id_document?.start_date || '', document_end_date: project.id_document?.end_date || '', release_city: project.id_document?.release_city || '', release_province: project.id_document?.release_province || '', release_entity: project.id_document?.release_entity || '', }; console.log('Transformed personalData:', personalData); setPrepopulatedData({ personalData, projectId: project.id, projectName: project.name, }); setCurrentView('new'); }; // Clear prepopulated data const clearPrepopulatedData = () => { setPrepopulatedData(null); }; const viewConfig = { welcome: { title: 'Inizia qui', subtitle: 'Riepilogo attivita e notifiche' }, dashboard: { title: 'Dashboard', subtitle: 'Panoramica della pipeline' }, new: { title: 'Nuova Registrazione', subtitle: 'Inserisci i dati per una nuova registrazione' }, queue: { title: 'Coda Lavori', subtitle: 'Gestisci i lavori in coda' }, profiles: { title: 'Profili Email', subtitle: 'Gestisci i profili email attivi' }, analytics: { title: 'Statistiche', subtitle: 'Analisi delle performance' }, archive: { title: 'Archivio', subtitle: 'Cronologia dei lavori' }, projects: { title: 'Pratiche', subtitle: 'Gestisci i progetti e le assegnazioni' }, 'project-detail': { title: 'Dettaglio Pratica', subtitle: 'Visualizza e gestisci la pratica' }, referenti: { title: 'Referenti', subtitle: 'Persone di riferimento esterne per i progetti' }, notifications: { title: 'Notifiche', subtitle: 'Tutte le tue notifiche' }, users: { title: 'Gestione Utenti', subtitle: 'Gestisci gli utenti del sistema' }, performance: { title: 'Performance', subtitle: 'Analisi dettagliata delle prestazioni del team' }, settings: { title: 'Impostazioni', subtitle: 'Gestisci il tuo account e le preferenze' }, }; // Show loading spinner while checking auth if (authLoading) { return (
Loading...