// MyProjectsView - Vista progetti assegnati per utenti function MyProjectsView({ pendingProjectId, onPendingHandled, onViewProject }) { const { user: currentUser, isAdmin } = useAuth(); const toast = useToast(); const [projects, setProjects] = React.useState([]); const [timelines, setTimelines] = React.useState({}); const [loading, setLoading] = React.useState(true); const [editingProject, setEditingProject] = React.useState(null); const [timelineProject, setTimelineProject] = React.useState(null); const [confirmModal, setConfirmModal] = React.useState(null); const loadData = async () => { setLoading(true); try { const projectsData = await ApiUtils.fetchProjects(); // Sort by creation time (newest first) and force new array reference for React to detect changes const sortedProjects = [...projectsData].sort((a, b) => new Date(b.created_at) - new Date(a.created_at)); setProjects(sortedProjects); // Fetch timeline data for each project to get step progress const timelinePromises = projectsData.map(async (project) => { try { const timeline = await ApiUtils.getProjectTimeline(project.id, false); return { projectId: project.id, timeline }; } catch (err) { console.error(`Failed to load timeline for project ${project.id}:`, err); return { projectId: project.id, timeline: null }; } }); const timelineResults = await Promise.all(timelinePromises); const timelinesMap = {}; timelineResults.forEach(({ projectId, timeline }) => { timelinesMap[projectId] = timeline; }); setTimelines(timelinesMap); } catch (err) { toast('Errore nel caricamento progetti: ' + err.message, 'error'); } finally { setLoading(false); } }; React.useEffect(() => { loadData(); }, []); // Handle pending project from notification click React.useEffect(() => { if (pendingProjectId && projects.length > 0 && !loading) { const project = projects.find(p => p.id === pendingProjectId); if (project) { setTimelineProject(project); } // Clear the pending project if (onPendingHandled) { onPendingHandled(); } } }, [pendingProjectId, projects, loading, onPendingHandled]); const getProgressInfo = (projectId) => { const timeline = timelines[projectId]; if (!timeline || !timeline.steps || timeline.steps.length === 0) { return { completed: 0, total: 0, percent: 0, lastUpdated: null }; } const completed = timeline.steps.filter(s => s.status === 'completato').length; const total = timeline.steps.length; const percent = Math.round((completed / total) * 100); // Find the most recent update across all steps let lastUpdated = null; timeline.steps.forEach(step => { const stepDate = step.updated_at ? new Date(step.updated_at) : null; if (stepDate && (!lastUpdated || stepDate > lastUpdated)) { lastUpdated = stepDate; } }); return { completed, total, percent, lastUpdated }; }; // Use shared utilities const formatRelativeTime = Formatters.formatRelativeTime; // Refresh timeline data for a specific project after step changes const refreshProjectTimeline = async (projectId) => { try { const timeline = await ApiUtils.getProjectTimeline(projectId, false); setTimelines(prev => ({ ...prev, [projectId]: timeline })); } catch (err) { console.error(`Failed to refresh timeline for project ${projectId}:`, err); } }; const handleCancel = (project) => { setConfirmModal({ type: 'cancel', project, title: 'Annulla Pratica', message: `Sei sicuro di voler annullare la pratica "${project.name}"?\n\nIl workflow sara bloccato e non sara possibile modificare gli stati degli step.`, confirmText: 'Annulla Pratica', variant: 'danger', }); }; const handleUncancel = (project) => { setConfirmModal({ type: 'uncancel', project, title: 'Ripristina Pratica', message: `Sei sicuro di voler ripristinare la pratica "${project.name}"?\n\nIl workflow sara sbloccato.`, confirmText: 'Ripristina', variant: 'info', }); }; const executeConfirmedAction = async () => { if (!confirmModal) return; const { type, project } = confirmModal; setConfirmModal({ ...confirmModal, loading: true }); try { if (type === 'cancel') { await ApiUtils.cancelProject(project.id); toast('Pratica annullata', 'success'); } else if (type === 'uncancel') { await ApiUtils.uncancelProject(project.id); toast('Pratica ripristinata', 'success'); } setConfirmModal(null); loadData(); } catch (err) { toast('Errore: ' + err.message, 'error'); setConfirmModal(null); } }; // If admin, redirect to full projects view if (isAdmin) { return (
Come admin, usa la sezione "Pratiche" nel menu Gestione per vedere tutte le pratiche.
); } return (
{/* Header */}

Le Mie Pratiche

Pratiche a cui sei assegnato

{/* Projects Grid */} {loading ? (

Caricamento progetti...

) : projects.length === 0 ? (

Non sei ancora assegnato a nessuna pratica

Contatta un amministratore per essere assegnato a una pratica

) : (
{projects.map(project => { const progressInfo = getProgressInfo(project.id); return (
{/* Cancelled Banner */} {project.is_cancelled && (
Annullato
)} {/* Progress Header - Clickable */}
onViewProject && onViewProject(project.id)} >
Avanzamento = 50 ? 'bg-blue-100 text-blue-700' : progressInfo.percent > 0 ? 'bg-amber-100 text-amber-700' : 'bg-slate-100 text-slate-500' }`}> {progressInfo.completed}/{progressInfo.total}
{progressInfo.lastUpdated ? `Agg. ${formatRelativeTime(progressInfo.lastUpdated)}` : '\u00A0'}
{progressInfo.percent}%
{/* Main Content - Clickable */}
onViewProject && onViewProject(project.id)} > {/* Title & Priority */}

{project.name}

{/* Meta Info - fixed height section */}
{project.start_date ? new Date(project.start_date).toLocaleDateString('it-IT', { day: '2-digit', month: 'short', year: 'numeric' }) : 'Data non impostata' }
{/* Scadenza */}
Scadenza: {project.due_date ? new Date(project.due_date).toLocaleDateString('it-IT', { day: '2-digit', month: 'short', year: 'numeric' }) : 'Non impostata' }
{/* Creato da */}
Creato da: {project.created_by_name || 'N/A'}
{/* Assegnato a te da */} {project.my_assignment?.assigned_by_name && (
Assegnato a te da: {project.my_assignment.assigned_by_name}
)} {project.reference_id && (
{project.reference_id}
)}
{/* Description & Notes - flex-grow to push footer down */}
{project.description && (

{project.description}

)} {project.notes && (

Note:

{project.notes}

)}
{/* Footer - always at bottom */}
{/* Cancel/Uncancel button */} {project.is_cancelled ? ( ) : ( )}
); })}
)} {/* Edit Project Modal */} {editingProject && ( setEditingProject(null)} onSuccess={() => { setEditingProject(null); loadData(); toast('Pratica aggiornata con successo', 'success'); }} /> )} {/* Timeline Modal */} {timelineProject && ( setTimelineProject(null)} onStepChange={refreshProjectTimeline} canEdit={true} isAssigned={true} /> )} {/* Confirmation Modal */} {confirmModal && ( setConfirmModal(null)} /> )}
); } window.MyProjectsView = MyProjectsView;