fix(dashboard-benef): allinea RendicontazioniMie al pattern grafico Gepafin
La pagina 'Le mie rendicontazioni' usava div con style inline (border, padding, background white) e classi inventate/flex wrapper custom che ignoravano il sistema CSS di Gepafin (appPage, appPageSection, appPageSection__withBorder, __list, __listItem, __pMeta, __actions) usato ovunque altrove (Dashboard, Bandi, Domande). Risultato: card piena larghezza schermo, tipografia incoerente, spaziature al 100% invece di contenute dentro il pattern standard. Riscritta usando esclusivamente le classi di /assets/scss/components/appPage.scss: - appPage + appPage__pageHeader (wrapper + header con bordo sinistro colorato) - appPage__spacer per distacco - wrapper esterno appPageSection (ora le card sono contenute dentro la sezione) - ogni application-card e' un appPageSection__withBorder (bordo 1px + radius 6 + padding 17px gia stilati) con .row interno flex + .rowContent - metriche cap/approvato/disponibile/tranches usano appPageSection__pMeta (2 span, il secondo a sinistra/destra) invece di flex custom - lista tranches usa ul.appPageSection__list + li.appPageSection__listItem.row che ha gia padding 15px + border-bottom gestiti dallo scss - icone + tag in appPageSection__iconActions - bottoni nuova-tranche in appPageSection__actions con justify-content:flex-end - empty state in appPageSection__withBorder centrato - colori CSS variables (var(--primary-color), var(--text-color-secondary), var(--green-700), var(--button-secondary-borderColor)) — niente colori inline Nessuna modifica alla logica (load, openStartDialog, confirmStart, nav). Dialog 'Avvia nuova tranche' invariato (gia era OK con appForm__field).
This commit is contained in:
@@ -31,7 +31,7 @@ const RendicontazioniMie = () => {
|
|||||||
const toast = useRef(null);
|
const toast = useRef(null);
|
||||||
const [apps, setApps] = useState([]);
|
const [apps, setApps] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [startDialog, setStartDialog] = useState(null); // { application_id, max_tranches, next_seq, show_copy_ula }
|
const [startDialog, setStartDialog] = useState(null);
|
||||||
const [startForm, setStartForm] = useState({ period_label: '', copy_ula: true });
|
const [startForm, setStartForm] = useState({ period_label: '', copy_ula: true });
|
||||||
const [starting, setStarting] = useState(false);
|
const [starting, setStarting] = useState(false);
|
||||||
|
|
||||||
@@ -58,30 +58,39 @@ const RendicontazioniMie = () => {
|
|||||||
call_name: app.call_name,
|
call_name: app.call_name,
|
||||||
max_tranches: app.max_tranches,
|
max_tranches: app.max_tranches,
|
||||||
next_seq: nextSeq,
|
next_seq: nextSeq,
|
||||||
show_copy_ula: nextSeq > 1,
|
|
||||||
max_remission_next: app.max_remission_next_tranche,
|
max_remission_next: app.max_remission_next_tranche,
|
||||||
|
show_copy_ula: nextSeq > 1,
|
||||||
});
|
});
|
||||||
setStartForm({ period_label: '', copy_ula: nextSeq > 1 });
|
setStartForm({ period_label: '', copy_ula: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmStart = () => {
|
const confirmStart = () => {
|
||||||
if (!startDialog) return;
|
if (!startDialog) return;
|
||||||
setStarting(true);
|
setStarting(true);
|
||||||
RendicontazioneService.startPractice(
|
RendicontazioneService.startPractice(startDialog.application_id,
|
||||||
startDialog.application_id,
|
|
||||||
(resp) => {
|
(resp) => {
|
||||||
setStarting(false);
|
setStarting(false);
|
||||||
setStartDialog(null);
|
setStartDialog(null);
|
||||||
toast.current?.show({ severity: 'success', summary: resp?.message || __('Tranche avviata', 'gepafin') });
|
toast.current?.show({
|
||||||
navigate(`/rendicontazioni/${resp.data.id}`);
|
severity: 'success',
|
||||||
|
summary: __('Tranche avviata', 'gepafin'),
|
||||||
|
detail: resp?.message
|
||||||
|
});
|
||||||
|
const newId = resp?.data?.id;
|
||||||
|
if (newId) navigate(`/rendicontazioni/${newId}`);
|
||||||
|
else load();
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
setStarting(false);
|
setStarting(false);
|
||||||
toast.current?.show({ severity: 'error', summary: __('Avvio fallito', 'gepafin'), detail: err?.detail });
|
toast.current?.show({
|
||||||
|
severity: 'error',
|
||||||
|
summary: __('Avvio fallito', 'gepafin'),
|
||||||
|
detail: err?.detail || err?.message
|
||||||
|
});
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
period_label: startForm.period_label?.trim() || null,
|
period_label: startForm.period_label || null,
|
||||||
copy_ula_from_previous: startForm.copy_ula,
|
copy_ula_from_previous: startDialog.show_copy_ula ? startForm.copy_ula : false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -93,79 +102,92 @@ const RendicontazioniMie = () => {
|
|||||||
const blockReason = app.start_blocked_reason;
|
const blockReason = app.start_blocked_reason;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={app.application_id}
|
<div key={app.application_id} className="appPageSection__withBorder" style={{ marginBottom: 24 }}>
|
||||||
className="appPageSection"
|
{/* HEADER: bando + erogato */}
|
||||||
style={{ marginBottom: '1.5rem', padding: '1.25rem', border: '1px solid var(--surface-border)', borderRadius: '8px', background: 'white' }}>
|
<div className="row">
|
||||||
{/* HEADER CARD */}
|
<div style={{ flex: 1 }}>
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: '1rem', marginBottom: '0.75rem' }}>
|
<h2 style={{ color: 'var(--primary-color)' }}>
|
||||||
<div>
|
|
||||||
<h3 style={{ margin: 0, color: 'var(--primary-color)' }}>
|
|
||||||
{app.call_name || `Bando #${app.call_id}`}
|
{app.call_name || `Bando #${app.call_id}`}
|
||||||
</h3>
|
</h2>
|
||||||
<div style={{ color: 'var(--text-color-secondary)', fontSize: '0.9em', marginTop: '2pt' }}>
|
<p style={{ margin: '4px 0 0', color: 'var(--text-color-secondary)', fontSize: 14 }}>
|
||||||
{app.company_name || ''} · {__('Domanda', 'gepafin')} #{app.application_id}
|
{app.company_name || ''} · {__('Domanda', 'gepafin')} #{app.application_id}
|
||||||
</div>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ textAlign: 'right' }}>
|
<div style={{ textAlign: 'right' }}>
|
||||||
<div style={{ fontSize: '0.8em', color: 'var(--text-color-secondary)' }}>{__('Finanziamento erogato','gepafin')}</div>
|
<div style={{ fontSize: 13, color: 'var(--text-color-secondary)' }}>
|
||||||
<div style={{ fontSize: '1.3em', fontWeight: 700 }}>{fmtEur(app.amount_erogato)}</div>
|
{__('Finanziamento erogato','gepafin')}
|
||||||
|
</div>
|
||||||
|
<div style={{ fontSize: 20, fontWeight: 700 }}>{fmtEur(app.amount_erogato)}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* CAP INFO */}
|
{/* METRICHE cap/approvato/disponibile/tranches in griglia 4 colonne */}
|
||||||
<div style={{ display: 'flex', gap: '1.5rem', padding: '0.75rem 1rem', background: 'var(--surface-50)', borderRadius: '6px', fontSize: '0.9em', flexWrap: 'wrap' }}>
|
<div className="appPageSection__row autoFlow" style={{ gap: '2rem', padding: '4px 0' }}>
|
||||||
<div>
|
<p className="appPageSection__pMeta" style={{ flex: '1 1 auto', minWidth: 140 }}>
|
||||||
<div style={{ fontSize: '0.8em', color: 'var(--text-color-secondary)' }}>{__('Cap remissione totale', 'gepafin')}</div>
|
<span>{__('Cap remissione totale', 'gepafin')}</span>
|
||||||
<strong>{fmtEur(app.max_remission_global)}</strong>
|
<span>{fmtEur(app.max_remission_global)}</span>
|
||||||
</div>
|
</p>
|
||||||
<div>
|
<p className="appPageSection__pMeta" style={{ flex: '1 1 auto', minWidth: 140 }}>
|
||||||
<div style={{ fontSize: '0.8em', color: 'var(--text-color-secondary)' }}>{__('Già approvato', 'gepafin')}</div>
|
<span>{__('Già approvato', 'gepafin')}</span>
|
||||||
<strong style={{ color: app.already_approved_sum > 0 ? '#22543d' : 'inherit' }}>{fmtEur(app.already_approved_sum)}</strong>
|
<span style={{ color: app.already_approved_sum > 0 ? 'var(--green-700)' : 'inherit' }}>
|
||||||
</div>
|
{fmtEur(app.already_approved_sum)}
|
||||||
<div>
|
</span>
|
||||||
<div style={{ fontSize: '0.8em', color: 'var(--text-color-secondary)' }}>{__('Disponibile prossima tranche', 'gepafin')}</div>
|
</p>
|
||||||
<strong style={{ color: 'var(--primary-color)' }}>{fmtEur(app.max_remission_next_tranche)}</strong>
|
<p className="appPageSection__pMeta" style={{ flex: '1 1 auto', minWidth: 170 }}>
|
||||||
</div>
|
<span>{__('Disponibile prossima tranche', 'gepafin')}</span>
|
||||||
<div>
|
<span style={{ color: 'var(--primary-color)' }}>
|
||||||
<div style={{ fontSize: '0.8em', color: 'var(--text-color-secondary)' }}>{__('Tranches', 'gepafin')}</div>
|
{fmtEur(app.max_remission_next_tranche)}
|
||||||
<strong>{app.tranches?.length || 0} / {app.max_tranches}</strong>
|
</span>
|
||||||
</div>
|
</p>
|
||||||
|
<p className="appPageSection__pMeta" style={{ flex: '0 0 auto', minWidth: 90 }}>
|
||||||
|
<span>{__('Tranches', 'gepafin')}</span>
|
||||||
|
<span>{app.tranches?.length || 0} / {app.max_tranches}</span>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* TRANCHES LIST */}
|
{/* LISTA TRANCHES */}
|
||||||
{hasTranches && (
|
{hasTranches && (
|
||||||
<div style={{ marginTop: '1rem' }}>
|
<ul className="appPageSection__list" style={{ borderTop: '1px solid var(--button-secondary-borderColor)' }}>
|
||||||
{app.tranches.map((t) => {
|
{app.tranches.map((t) => {
|
||||||
const tag = STATUS_TAGS[t.status] || { severity: 'secondary', label: t.status };
|
const tag = STATUS_TAGS[t.status] || { severity: 'secondary', label: t.status };
|
||||||
const isEditable = t.status === 'DRAFT';
|
const isEditable = t.status === 'DRAFT';
|
||||||
return (
|
return (
|
||||||
<div key={t.id}
|
<li key={t.id} className="appPageSection__listItem row">
|
||||||
style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', padding: '0.6rem 0.75rem', borderBottom: '1px solid var(--surface-border)' }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem', flex: 1 }}>
|
||||||
<div style={{ width: '3rem', fontWeight: 700, color: 'var(--primary-color)' }}>
|
<div style={{ width: 36, fontWeight: 700, color: 'var(--primary-color)', fontSize: 16 }}>
|
||||||
T{t.sequence_number}
|
T{t.sequence_number}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ flex: 1 }}>
|
<div style={{ flex: 1 }}>
|
||||||
<div>{t.period_label || <span className="text-color-secondary">{__('nessun periodo indicato','gepafin')}</span>}</div>
|
<div style={{ fontWeight: 500 }}>
|
||||||
<small className="text-color-secondary">
|
{t.period_label || (
|
||||||
{t.invoice_count || 0} {__('fatture','gepafin')} · {t.ula_count || 0} {__('dipendenti','gepafin')} · {t.document_count || 0} {__('doc','gepafin')}
|
<span style={{ color: 'var(--text-color-secondary)', fontStyle: 'italic' }}>
|
||||||
</small>
|
{__('nessun periodo indicato','gepafin')}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<div style={{ fontSize: 13, color: 'var(--text-color-secondary)', marginTop: 2 }}>
|
||||||
|
{t.invoice_count || 0} {__('fatture','gepafin')} · {t.ula_count || 0} {__('dipendenti','gepafin')} · {t.document_count || 0} {__('doc','gepafin')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="appPageSection__iconActions">
|
||||||
<Tag value={tag.label} severity={tag.severity} />
|
<Tag value={tag.label} severity={tag.severity} />
|
||||||
<Button icon={isEditable ? 'pi pi-pencil' : 'pi pi-eye'}
|
<Button icon={isEditable ? 'pi pi-pencil' : 'pi pi-eye'}
|
||||||
size="small" outlined={!isEditable}
|
size="small" outlined={!isEditable}
|
||||||
label={isEditable ? __('Continua','gepafin') : __('Apri','gepafin')}
|
label={isEditable ? __('Continua','gepafin') : __('Apri','gepafin')}
|
||||||
onClick={() => navigate(`/rendicontazioni/${t.id}`)} />
|
onClick={() => navigate(`/rendicontazioni/${t.id}`)} />
|
||||||
</div>
|
</div>
|
||||||
|
</li>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</ul>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* BOTTONE NUOVA TRANCHE */}
|
{/* AZIONI: nuova tranche */}
|
||||||
<div style={{ marginTop: '1rem', display: 'flex', justifyContent: 'flex-end', alignItems: 'center', gap: '0.75rem' }}>
|
<div className="appPageSection__actions" style={{ justifyContent: 'flex-end', alignItems: 'center', padding: '12px 0 0' }}>
|
||||||
{!canStart && blockReason && (
|
{!canStart && blockReason && (
|
||||||
<small className="text-color-secondary" style={{ fontStyle: 'italic' }}>
|
<small style={{ color: 'var(--text-color-secondary)', fontStyle: 'italic' }}>
|
||||||
<i className="pi pi-info-circle" style={{ marginRight: '4pt' }} />
|
<i className="pi pi-info-circle" style={{ marginRight: 4 }} />
|
||||||
{blockReason}
|
{blockReason}
|
||||||
</small>
|
</small>
|
||||||
)}
|
)}
|
||||||
@@ -190,26 +212,33 @@ const RendicontazioniMie = () => {
|
|||||||
|
|
||||||
<div className="appPage__pageHeader">
|
<div className="appPage__pageHeader">
|
||||||
<h1>{__('Le mie rendicontazioni', 'gepafin')}</h1>
|
<h1>{__('Le mie rendicontazioni', 'gepafin')}</h1>
|
||||||
<p>{__('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')}</p>
|
<p>{__('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')}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="appPage__spacer"></div>
|
<div className="appPage__spacer"></div>
|
||||||
|
|
||||||
{loading && <Skeleton width="100%" height="10rem" />}
|
{loading && (
|
||||||
|
<div className="appPageSection">
|
||||||
|
<Skeleton width="100%" height="10rem" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{!loading && apps.length === 0 && (
|
{!loading && apps.length === 0 && (
|
||||||
<div className="appPageSection" style={{ alignItems: 'center', padding: '3rem 2rem' }}>
|
<div className="appPageSection__withBorder" style={{ alignItems: 'center', textAlign: 'center', padding: '3rem 2rem' }}>
|
||||||
<i className="pi pi-inbox" style={{ fontSize: '2.5rem', color: 'var(--text-color-secondary)', marginBottom: '0.75rem' }} />
|
<i className="pi pi-inbox" style={{ fontSize: '2.5rem', color: 'var(--text-color-secondary)', marginBottom: '0.75rem' }} />
|
||||||
<p>{__('Non ci sono rendicontazioni disponibili al momento.', 'gepafin')}</p>
|
<p>{__('Non ci sono rendicontazioni disponibili al momento.', 'gepafin')}</p>
|
||||||
<small className="text-color-secondary">
|
<small style={{ color: 'var(--text-color-secondary)' }}>
|
||||||
{__('Le rendicontazioni diventano disponibili dopo la firma del contratto e quando l\'ente ha pubblicato lo schema di rendicontazione per il bando.', 'gepafin')}
|
{__('Le rendicontazioni diventano disponibili dopo la firma del contratto e quando l\'ente ha pubblicato lo schema di rendicontazione per il bando.', 'gepafin')}
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!loading && apps.length > 0 && apps.map(renderApplicationCard)}
|
{!loading && apps.length > 0 && (
|
||||||
|
<div className="appPageSection">
|
||||||
|
{apps.map(renderApplicationCard)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* START DIALOG */}
|
|
||||||
<Dialog header={__('Avvia nuova tranche di rendicontazione', 'gepafin')}
|
<Dialog header={__('Avvia nuova tranche di rendicontazione', 'gepafin')}
|
||||||
visible={!!startDialog} style={{ width: '32rem' }}
|
visible={!!startDialog} style={{ width: '32rem' }}
|
||||||
onHide={() => !starting && setStartDialog(null)}
|
onHide={() => !starting && setStartDialog(null)}
|
||||||
@@ -240,7 +269,7 @@ const RendicontazioniMie = () => {
|
|||||||
onChange={(e) => setStartForm(f => ({ ...f, period_label: e.target.value }))}
|
onChange={(e) => setStartForm(f => ({ ...f, period_label: e.target.value }))}
|
||||||
placeholder={__('es. "I trimestre 2021", "Stato avanzamento II"', 'gepafin')}
|
placeholder={__('es. "I trimestre 2021", "Stato avanzamento II"', 'gepafin')}
|
||||||
disabled={starting} />
|
disabled={starting} />
|
||||||
<small className="text-color-secondary">
|
<small style={{ color: 'var(--text-color-secondary)' }}>
|
||||||
{__('Descrizione libera per identificare la tranche. Apparirà sul verbale.', 'gepafin')}
|
{__('Descrizione libera per identificare la tranche. Apparirà sul verbale.', 'gepafin')}
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
@@ -255,7 +284,7 @@ const RendicontazioniMie = () => {
|
|||||||
{__('Copia i dipendenti ULA dalla tranche precedente', 'gepafin')}
|
{__('Copia i dipendenti ULA dalla tranche precedente', 'gepafin')}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<small className="text-color-secondary">
|
<small style={{ color: 'var(--text-color-secondary)' }}>
|
||||||
{__('Se attivo, i dipendenti censiti nella tranche precedente saranno precaricati. Potrai modificarli o rimuoverli prima di inviare.', 'gepafin')}
|
{__('Se attivo, i dipendenti censiti nella tranche precedente saranno precaricati. Potrai modificarli o rimuoverli prima di inviare.', 'gepafin')}
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user