diff --git a/src/modules/rendicontazione/pages/RendicontazioniMie.js b/src/modules/rendicontazione/pages/RendicontazioniMie.js
index 25475fb..eabb2cd 100644
--- a/src/modules/rendicontazione/pages/RendicontazioniMie.js
+++ b/src/modules/rendicontazione/pages/RendicontazioniMie.js
@@ -9,16 +9,18 @@ import { Skeleton } from 'primereact/skeleton';
import { Dialog } from 'primereact/dialog';
import { InputText } from 'primereact/inputtext';
import { Checkbox } from 'primereact/checkbox';
+import { Card } from 'primereact/card';
+import { Divider } from 'primereact/divider';
import RendicontazioneService from '../service/rendicontazioneService';
const STATUS_TAGS = {
- DRAFT: { severity: 'warning', label: 'In compilazione' },
- SUBMITTED: { severity: 'info', label: 'Inviata' },
- UNDER_REVIEW: { severity: 'info', label: 'In valutazione' },
- APPROVED: { severity: 'success', label: 'Approvata' },
- REJECTED: { severity: 'danger', label: 'Respinta' },
- AWAITING_AMENDMENT: { severity: 'warning', label: 'Soccorso istruttorio' }
+ DRAFT: { severity: 'warning', label: 'In compilazione', icon: 'pi pi-pencil' },
+ SUBMITTED: { severity: 'info', label: 'Inviata', icon: 'pi pi-send' },
+ UNDER_REVIEW: { severity: 'info', label: 'In valutazione', icon: 'pi pi-eye' },
+ APPROVED: { severity: 'success', label: 'Approvata', icon: 'pi pi-check-circle' },
+ REJECTED: { severity: 'danger', label: 'Respinta', icon: 'pi pi-times-circle' },
+ AWAITING_AMENDMENT: { severity: 'warning', label: 'Soccorso istruttorio',icon: 'pi pi-exclamation-triangle' }
};
const fmtEur = (v) => {
@@ -26,6 +28,106 @@ const fmtEur = (v) => {
return `€ ${n.toLocaleString('it-IT', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
};
+// -------- Sottocomponenti di presentazione --------
+
+const StatTile = ({ label, value, accent = 'var(--text-color)', muted = false }) => (
+
+
+ {label}
+
+
+ {value}
+
+
+);
+
+const TrancheRow = ({ tranche, onOpen, isLast }) => {
+ const tag = STATUS_TAGS[tranche.status] || { severity: 'secondary', label: tranche.status, icon: 'pi pi-circle' };
+ const isEditable = tranche.status === 'DRAFT' || tranche.status === 'AWAITING_AMENDMENT';
+ const hasContent = (tranche.invoice_count || tranche.ula_count || tranche.document_count);
+
+ return (
+
+ {/* Icona circolare con numero tranche */}
+
+ T{tranche.sequence_number}
+
+
+ {/* Descrizione tranche */}
+
+
+ {tranche.period_label || {__('Nessun periodo indicato','gepafin')} }
+
+
+ {hasContent ? (
+ <>
+
+ {tranche.invoice_count || 0} {__('fatture','gepafin')}
+ ·
+
+ {tranche.ula_count || 0} {__('dipendenti','gepafin')}
+ ·
+
+ {tranche.document_count || 0} {__('documenti','gepafin')}
+ >
+ ) : (
+ {__('Nessun contenuto ancora inserito','gepafin')}
+ )}
+
+
+
+ {/* Stato + CTA */}
+
+
onOpen(tranche.id)}
+ />
+
+ );
+};
+
+// -------- Componente principale --------
+
const RendicontazioniMie = () => {
const navigate = useNavigate();
const toast = useRef(null);
@@ -58,152 +160,213 @@ const RendicontazioniMie = () => {
call_name: app.call_name,
max_tranches: app.max_tranches,
next_seq: nextSeq,
- max_remission_next: app.max_remission_next_tranche,
show_copy_ula: nextSeq > 1,
+ max_remission_next: app.max_remission_next_tranche,
});
- setStartForm({ period_label: '', copy_ula: true });
+ setStartForm({ period_label: '', copy_ula: nextSeq > 1 });
};
const confirmStart = () => {
if (!startDialog) return;
setStarting(true);
- RendicontazioneService.startPractice(startDialog.application_id,
+ RendicontazioneService.startPractice(
+ startDialog.application_id,
(resp) => {
setStarting(false);
setStartDialog(null);
- toast.current?.show({
- severity: 'success',
- summary: __('Tranche avviata', 'gepafin'),
- detail: resp?.message
- });
- const newId = resp?.data?.id;
- if (newId) navigate(`/rendicontazioni/${newId}`);
- else load();
+ toast.current?.show({ severity: 'success', summary: resp?.message || __('Tranche avviata', 'gepafin') });
+ navigate(`/rendicontazioni/${resp.data.id}`);
},
(err) => {
setStarting(false);
- toast.current?.show({
- severity: 'error',
- summary: __('Avvio fallito', 'gepafin'),
- detail: err?.detail || err?.message
- });
+ toast.current?.show({ severity: 'error', summary: __('Avvio fallito', 'gepafin'), detail: err?.detail });
},
{
- period_label: startForm.period_label || null,
- copy_ula_from_previous: startDialog.show_copy_ula ? startForm.copy_ula : false,
+ period_label: startForm.period_label?.trim() || null,
+ copy_ula_from_previous: startForm.copy_ula,
}
);
};
const renderApplicationCard = (app) => {
- const hasTranches = (app.tranches?.length || 0) > 0;
- const nextSeq = (app.tranches?.length || 0) + 1;
+ const tranchesCount = app.tranches?.length || 0;
+ const hasTranches = tranchesCount > 0;
+ const nextSeq = tranchesCount + 1;
const canStart = !!app.can_start_new;
const blockReason = app.start_blocked_reason;
+ const progressPct = app.max_remission_global > 0
+ ? Math.min(100, (app.already_approved_sum / app.max_remission_global) * 100)
+ : 0;
- return (
-
- {/* HEADER: bando + erogato */}
-
-
-
- {app.call_name || `Bando #${app.call_id}`}
-
-
- {app.company_name || ''} · {__('Domanda', 'gepafin')} #{app.application_id}
-
-
-
-
- {__('Finanziamento erogato','gepafin')}
-
-
{fmtEur(app.amount_erogato)}
+ const headerTemplate = (
+
+
+
+ {app.call_name || `Bando #${app.call_id}`}
+
+
+
+ {app.company_name || '—'}
+ ·
+ {__('Domanda', 'gepafin')} #{app.application_id}
-
- {/* METRICHE cap/approvato/disponibile/tranches in griglia 4 colonne */}
-
-
- {__('Cap remissione totale', 'gepafin')}
- {fmtEur(app.max_remission_global)}
-
-
- {__('Già approvato', 'gepafin')}
- 0 ? 'var(--green-700)' : 'inherit' }}>
- {fmtEur(app.already_approved_sum)}
-
-
-
- {__('Disponibile prossima tranche', 'gepafin')}
-
- {fmtEur(app.max_remission_next_tranche)}
-
-
-
- {__('Tranches', 'gepafin')}
- {app.tranches?.length || 0} / {app.max_tranches}
-
-
-
- {/* LISTA TRANCHES */}
- {hasTranches && (
-
- )}
-
- {/* AZIONI: nuova tranche */}
-
- {!canStart && blockReason && (
-
-
- {blockReason}
-
- )}
-
openStartDialog(app)} />
+
+
+ {__('Finanziamento erogato','gepafin')}
+
+
+ {fmtEur(app.amount_erogato)}
+
);
+
+ return (
+
+
+ {/* BLOCCO TOTALI — 4 stat tile affiancati */}
+
+
+
+
+
+
+
+ {/* PROGRESS BAR utilizzo cap */}
+ {app.max_remission_global > 0 && (
+
+
+
= 100 ? 'var(--red-500)' : '#16a34a',
+ transition: 'width 0.3s',
+ }} />
+
+
+ {progressPct.toFixed(1)}% {__('del cap già utilizzato','gepafin')}
+
+
+ )}
+
+ {/* TRANCHES */}
+ {hasTranches ? (
+
+
+ {__('Elenco tranche','gepafin')}
+
+
+ {app.tranches.map((t, idx) => (
+ navigate(`/rendicontazioni/${id}`)}
+ isLast={idx === app.tranches.length - 1}
+ />
+ ))}
+
+
+ ) : (
+
+
+ {__('Non hai ancora avviato nessuna tranche di rendicontazione per questo bando.','gepafin')}
+
+ )}
+
+ {/* FOOTER — bottone nuova tranche */}
+
+
+ {!canStart && blockReason ? (
+
+
+ {blockReason}
+
+ ) :
}
+
+
openStartDialog(app)}
+ />
+
+
+ );
};
return (
@@ -212,56 +375,75 @@ const RendicontazioniMie = () => {
{__('Le mie rendicontazioni', 'gepafin')}
-
{__('Per ogni pratica finanziata puoi avviare la rendicontazione delle spese e il calcolo della remissione del debito. I bandi che prevedono più tranches permettono rendicontazioni multi-fase.', 'gepafin')}
+
{__('Per ogni pratica finanziata puoi avviare la rendicontazione delle spese e il calcolo della remissione del debito. I bandi che prevedono piu tranches permettono rendicontazioni multi-fase.', 'gepafin')}
{loading && (
-
-
+
+
+
)}
{!loading && apps.length === 0 && (
-
-
-
{__('Non ci sono rendicontazioni disponibili al momento.', 'gepafin')}
-
- {__('Le rendicontazioni diventano disponibili dopo la firma del contratto e quando l\'ente ha pubblicato lo schema di rendicontazione per il bando.', 'gepafin')}
-
-
+
+
+
+
{__('Non ci sono rendicontazioni disponibili al momento.', 'gepafin')}
+
+ {__('Le rendicontazioni diventano disponibili dopo la firma del contratto e quando l\'ente ha pubblicato lo schema di rendicontazione per il bando.', 'gepafin')}
+
+
+
)}
- {!loading && apps.length > 0 && (
-
- {apps.map(renderApplicationCard)}
-
- )}
+ {!loading && apps.length > 0 && apps.map(renderApplicationCard)}
-
!starting && setStartDialog(null)}
- modal
- footer={(
-
- setStartDialog(null)} outlined disabled={starting} />
-
-
- )}>
+ {/* START DIALOG */}
+ !starting && setStartDialog(null)}
+ modal
+ footer={(
+
+ setStartDialog(null)} outlined disabled={starting} />
+
+
+ )}
+ >
{startDialog && (
-
- {__('Stai per avviare la tranche', 'gepafin')}
- {' '}T{startDialog.next_seq} / {startDialog.max_tranches}
- {' '}{__('del bando', 'gepafin')} {startDialog.call_name} .
-
-
- {__('Cap remissione disponibile per questa tranche', 'gepafin')}:
- {' '}{fmtEur(startDialog.max_remission_next)}
-
+
+
+ {__('Stai per avviare la tranche', 'gepafin')}
+ {' '}T{startDialog.next_seq} / {startDialog.max_tranches}
+ {' '}{__('del bando', 'gepafin')}
+
+
+ {startDialog.call_name}
+
+
+ {__('Cap remissione disponibile', 'gepafin')}:
+ {' '}{fmtEur(startDialog.max_remission_next)}
+
+
{__('Periodo / fase (opzionale)', 'gepafin')}
@@ -269,22 +451,22 @@ const RendicontazioniMie = () => {
onChange={(e) => setStartForm(f => ({ ...f, period_label: e.target.value }))}
placeholder={__('es. "I trimestre 2021", "Stato avanzamento II"', 'gepafin')}
disabled={starting} />
-
+
{__('Descrizione libera per identificare la tranche. Apparirà sul verbale.', 'gepafin')}
{startDialog.show_copy_ula && (
-
+
setStartForm(f => ({ ...f, copy_ula: e.checked }))}
disabled={starting} />
-
+
{__('Copia i dipendenti ULA dalla tranche precedente', 'gepafin')}
-
+
{__('Se attivo, i dipendenti censiti nella tranche precedente saranno precaricati. Potrai modificarli o rimuoverli prima di inviare.', 'gepafin')}