style(rendicontazioniMie): redesign dashboard benef con Card/StatTile/TrancheRow
Layout precedente aveva div con inline-style grezzi, totali affastellati in flex unico, righe tranche plain senza gerarchia visiva. Redesign completo mantenendo identica la logica: - Card PrimeReact con header strutturato (titolo bando + azienda/domanda + importo) - StatTile sub-component (label uppercase 0.75rem + value bold 1.15rem, bordo sinistro colorato slate/green/primary/gray) - Progress bar percentuale cap utilizzato (verde <100%, rosso >=100%) - TrancheRow sub-component con icona circolare T1/T2 e icone pi-file/users/paperclip per metadati - Tag stato con icona (pencil/send/eye/check-circle/times-circle/exclamation-triangle) - Divider PrimeReact tra corpo e footer - Empty state tranches con message informativo su surface-50 - Dialog avvio tranche con box riepilogativo colorato Il file era stato scritto su disco in sessione precedente ma non committato. Committo ora prima della chiusura sessione.
This commit is contained in:
@@ -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 }) => (
|
||||
<div style={{
|
||||
flex: '1 1 180px',
|
||||
minWidth: '160px',
|
||||
padding: '0.75rem 1rem',
|
||||
background: 'white',
|
||||
borderLeft: `3px solid ${accent}`,
|
||||
borderRadius: '4px',
|
||||
boxShadow: '0 1px 2px rgba(0,0,0,0.04)',
|
||||
}}>
|
||||
<div style={{
|
||||
fontSize: '0.75rem',
|
||||
letterSpacing: '0.02em',
|
||||
color: 'var(--text-color-secondary)',
|
||||
textTransform: 'uppercase',
|
||||
marginBottom: '0.25rem',
|
||||
}}>
|
||||
{label}
|
||||
</div>
|
||||
<div style={{
|
||||
fontSize: '1.15rem',
|
||||
fontWeight: 700,
|
||||
color: muted ? 'var(--text-color-secondary)' : accent,
|
||||
}}>
|
||||
{value}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
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 (
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '1rem',
|
||||
padding: '0.85rem 0',
|
||||
borderBottom: isLast ? 'none' : '1px solid var(--surface-border)',
|
||||
}}>
|
||||
{/* Icona circolare con numero tranche */}
|
||||
<div style={{
|
||||
flexShrink: 0,
|
||||
width: '2.5rem',
|
||||
height: '2.5rem',
|
||||
borderRadius: '50%',
|
||||
background: 'var(--primary-color)',
|
||||
color: 'white',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
fontWeight: 700,
|
||||
fontSize: '0.9rem',
|
||||
}}>
|
||||
T{tranche.sequence_number}
|
||||
</div>
|
||||
|
||||
{/* Descrizione tranche */}
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div style={{ fontWeight: 600, marginBottom: '2px' }}>
|
||||
{tranche.period_label || <span style={{ color: 'var(--text-color-secondary)', fontWeight: 400, fontStyle: 'italic' }}>{__('Nessun periodo indicato','gepafin')}</span>}
|
||||
</div>
|
||||
<div style={{ fontSize: '0.85rem', color: 'var(--text-color-secondary)' }}>
|
||||
{hasContent ? (
|
||||
<>
|
||||
<i className="pi pi-file" style={{ marginRight: '4px', fontSize: '0.8em' }} />
|
||||
{tranche.invoice_count || 0} {__('fatture','gepafin')}
|
||||
<span style={{ margin: '0 6px' }}>·</span>
|
||||
<i className="pi pi-users" style={{ marginRight: '4px', fontSize: '0.8em' }} />
|
||||
{tranche.ula_count || 0} {__('dipendenti','gepafin')}
|
||||
<span style={{ margin: '0 6px' }}>·</span>
|
||||
<i className="pi pi-paperclip" style={{ marginRight: '4px', fontSize: '0.8em' }} />
|
||||
{tranche.document_count || 0} {__('documenti','gepafin')}
|
||||
</>
|
||||
) : (
|
||||
<em>{__('Nessun contenuto ancora inserito','gepafin')}</em>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Stato + CTA */}
|
||||
<Tag icon={tag.icon} value={tag.label} severity={tag.severity} />
|
||||
<Button
|
||||
icon={isEditable ? 'pi pi-pencil' : 'pi pi-eye'}
|
||||
size="small"
|
||||
outlined={!isEditable}
|
||||
severity={isEditable ? null : 'secondary'}
|
||||
label={isEditable ? __('Continua','gepafin') : __('Apri','gepafin')}
|
||||
onClick={() => onOpen(tranche.id)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// -------- 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 (
|
||||
<div key={app.application_id} className="appPageSection__withBorder" style={{ marginBottom: 24 }}>
|
||||
{/* HEADER: bando + erogato */}
|
||||
<div className="row">
|
||||
<div style={{ flex: 1 }}>
|
||||
<h2 style={{ color: 'var(--primary-color)' }}>
|
||||
{app.call_name || `Bando #${app.call_id}`}
|
||||
</h2>
|
||||
<p style={{ margin: '4px 0 0', color: 'var(--text-color-secondary)', fontSize: 14 }}>
|
||||
{app.company_name || ''} · {__('Domanda', 'gepafin')} #{app.application_id}
|
||||
</p>
|
||||
</div>
|
||||
<div style={{ textAlign: 'right' }}>
|
||||
<div style={{ fontSize: 13, color: 'var(--text-color-secondary)' }}>
|
||||
{__('Finanziamento erogato','gepafin')}
|
||||
</div>
|
||||
<div style={{ fontSize: 20, fontWeight: 700 }}>{fmtEur(app.amount_erogato)}</div>
|
||||
const headerTemplate = (
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'flex-start',
|
||||
gap: '1rem',
|
||||
padding: '1.25rem 1.5rem 0.75rem',
|
||||
}}>
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<h3 style={{ margin: 0, color: 'var(--primary-color)', fontSize: '1.2rem' }}>
|
||||
{app.call_name || `Bando #${app.call_id}`}
|
||||
</h3>
|
||||
<div style={{ color: 'var(--text-color-secondary)', fontSize: '0.85rem', marginTop: '4px' }}>
|
||||
<i className="pi pi-building" style={{ marginRight: '4px' }} />
|
||||
{app.company_name || '—'}
|
||||
<span style={{ margin: '0 6px' }}>·</span>
|
||||
{__('Domanda', 'gepafin')} <strong>#{app.application_id}</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* METRICHE cap/approvato/disponibile/tranches in griglia 4 colonne */}
|
||||
<div className="appPageSection__row autoFlow" style={{ gap: '2rem', padding: '4px 0' }}>
|
||||
<p className="appPageSection__pMeta" style={{ flex: '1 1 auto', minWidth: 140 }}>
|
||||
<span>{__('Cap remissione totale', 'gepafin')}</span>
|
||||
<span>{fmtEur(app.max_remission_global)}</span>
|
||||
</p>
|
||||
<p className="appPageSection__pMeta" style={{ flex: '1 1 auto', minWidth: 140 }}>
|
||||
<span>{__('Già approvato', 'gepafin')}</span>
|
||||
<span style={{ color: app.already_approved_sum > 0 ? 'var(--green-700)' : 'inherit' }}>
|
||||
{fmtEur(app.already_approved_sum)}
|
||||
</span>
|
||||
</p>
|
||||
<p className="appPageSection__pMeta" style={{ flex: '1 1 auto', minWidth: 170 }}>
|
||||
<span>{__('Disponibile prossima tranche', 'gepafin')}</span>
|
||||
<span style={{ color: 'var(--primary-color)' }}>
|
||||
{fmtEur(app.max_remission_next_tranche)}
|
||||
</span>
|
||||
</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>
|
||||
|
||||
{/* LISTA TRANCHES */}
|
||||
{hasTranches && (
|
||||
<ul className="appPageSection__list" style={{ borderTop: '1px solid var(--button-secondary-borderColor)' }}>
|
||||
{app.tranches.map((t) => {
|
||||
const tag = STATUS_TAGS[t.status] || { severity: 'secondary', label: t.status };
|
||||
const isEditable = t.status === 'DRAFT';
|
||||
return (
|
||||
<li key={t.id} className="appPageSection__listItem row">
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem', flex: 1 }}>
|
||||
<div style={{ width: 36, fontWeight: 700, color: 'var(--primary-color)', fontSize: 16 }}>
|
||||
T{t.sequence_number}
|
||||
</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ fontWeight: 500 }}>
|
||||
{t.period_label || (
|
||||
<span style={{ color: 'var(--text-color-secondary)', fontStyle: 'italic' }}>
|
||||
{__('nessun periodo indicato','gepafin')}
|
||||
</span>
|
||||
)}
|
||||
</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} />
|
||||
<Button icon={isEditable ? 'pi pi-pencil' : 'pi pi-eye'}
|
||||
size="small" outlined={!isEditable}
|
||||
label={isEditable ? __('Continua','gepafin') : __('Apri','gepafin')}
|
||||
onClick={() => navigate(`/rendicontazioni/${t.id}`)} />
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
|
||||
{/* AZIONI: nuova tranche */}
|
||||
<div className="appPageSection__actions" style={{ justifyContent: 'flex-end', alignItems: 'center', padding: '12px 0 0' }}>
|
||||
{!canStart && blockReason && (
|
||||
<small style={{ color: 'var(--text-color-secondary)', fontStyle: 'italic' }}>
|
||||
<i className="pi pi-info-circle" style={{ marginRight: 4 }} />
|
||||
{blockReason}
|
||||
</small>
|
||||
)}
|
||||
<Button icon="pi pi-plus-circle"
|
||||
label={hasTranches
|
||||
? `${__('+ Nuova tranche','gepafin')} (T${nextSeq})`
|
||||
: __('+ Avvia rendicontazione','gepafin')}
|
||||
severity={canStart ? 'success' : 'secondary'}
|
||||
disabled={!canStart}
|
||||
outlined={!canStart}
|
||||
tooltip={!canStart ? blockReason : undefined}
|
||||
tooltipOptions={{ position: 'top' }}
|
||||
onClick={() => openStartDialog(app)} />
|
||||
<div style={{ textAlign: 'right', flexShrink: 0 }}>
|
||||
<div style={{ fontSize: '0.72rem', color: 'var(--text-color-secondary)', textTransform: 'uppercase', letterSpacing: '0.02em' }}>
|
||||
{__('Finanziamento erogato','gepafin')}
|
||||
</div>
|
||||
<div style={{ fontSize: '1.4rem', fontWeight: 700, lineHeight: 1.1 }}>
|
||||
{fmtEur(app.amount_erogato)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Card key={app.application_id}
|
||||
header={headerTemplate}
|
||||
style={{ marginBottom: '1.5rem', borderRadius: '8px', overflow: 'hidden' }}>
|
||||
|
||||
{/* BLOCCO TOTALI — 4 stat tile affiancati */}
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
gap: '0.75rem',
|
||||
padding: '0.25rem 0.25rem 1rem',
|
||||
}}>
|
||||
<StatTile
|
||||
label={__('Cap remissione totale', 'gepafin')}
|
||||
value={fmtEur(app.max_remission_global)}
|
||||
accent="#475569"
|
||||
/>
|
||||
<StatTile
|
||||
label={__('Già approvato', 'gepafin')}
|
||||
value={fmtEur(app.already_approved_sum)}
|
||||
accent="#16a34a"
|
||||
muted={!app.already_approved_sum}
|
||||
/>
|
||||
<StatTile
|
||||
label={__('Disponibile prossima tranche', 'gepafin')}
|
||||
value={fmtEur(app.max_remission_next_tranche)}
|
||||
accent="var(--primary-color)"
|
||||
/>
|
||||
<StatTile
|
||||
label={__('Tranches', 'gepafin')}
|
||||
value={`${tranchesCount} / ${app.max_tranches}`}
|
||||
accent="#64748b"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* PROGRESS BAR utilizzo cap */}
|
||||
{app.max_remission_global > 0 && (
|
||||
<div style={{ padding: '0 0.25rem 0.75rem' }}>
|
||||
<div style={{
|
||||
height: '6px',
|
||||
background: 'var(--surface-200)',
|
||||
borderRadius: '3px',
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
}}>
|
||||
<div style={{
|
||||
height: '100%',
|
||||
width: `${progressPct}%`,
|
||||
background: progressPct >= 100 ? 'var(--red-500)' : '#16a34a',
|
||||
transition: 'width 0.3s',
|
||||
}} />
|
||||
</div>
|
||||
<div style={{
|
||||
fontSize: '0.75rem',
|
||||
color: 'var(--text-color-secondary)',
|
||||
marginTop: '4px',
|
||||
textAlign: 'right',
|
||||
}}>
|
||||
{progressPct.toFixed(1)}% {__('del cap già utilizzato','gepafin')}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* TRANCHES */}
|
||||
{hasTranches ? (
|
||||
<div style={{ padding: '0.5rem 0.25rem 0' }}>
|
||||
<div style={{
|
||||
fontSize: '0.75rem',
|
||||
color: 'var(--text-color-secondary)',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: '0.02em',
|
||||
marginBottom: '0.25rem',
|
||||
fontWeight: 600,
|
||||
}}>
|
||||
{__('Elenco tranche','gepafin')}
|
||||
</div>
|
||||
<div style={{ borderTop: '1px solid var(--surface-border)' }}>
|
||||
{app.tranches.map((t, idx) => (
|
||||
<TrancheRow
|
||||
key={t.id}
|
||||
tranche={t}
|
||||
onOpen={(id) => navigate(`/rendicontazioni/${id}`)}
|
||||
isLast={idx === app.tranches.length - 1}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div style={{
|
||||
padding: '1.5rem',
|
||||
textAlign: 'center',
|
||||
color: 'var(--text-color-secondary)',
|
||||
background: 'var(--surface-50)',
|
||||
borderRadius: '6px',
|
||||
fontSize: '0.9rem',
|
||||
}}>
|
||||
<i className="pi pi-info-circle" style={{ marginRight: '6px' }} />
|
||||
{__('Non hai ancora avviato nessuna tranche di rendicontazione per questo bando.','gepafin')}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* FOOTER — bottone nuova tranche */}
|
||||
<Divider style={{ margin: '1rem 0 0.75rem' }} />
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
gap: '0.75rem',
|
||||
flexWrap: 'wrap',
|
||||
}}>
|
||||
{!canStart && blockReason ? (
|
||||
<div style={{
|
||||
fontSize: '0.85rem',
|
||||
color: 'var(--text-color-secondary)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '6px',
|
||||
}}>
|
||||
<i className="pi pi-info-circle" />
|
||||
<em>{blockReason}</em>
|
||||
</div>
|
||||
) : <span />}
|
||||
|
||||
<Button
|
||||
icon="pi pi-plus-circle"
|
||||
iconPos="left"
|
||||
label={hasTranches
|
||||
? `${__('Nuova tranche','gepafin')} (T${nextSeq})`
|
||||
: __('Avvia rendicontazione','gepafin')}
|
||||
severity={canStart ? 'success' : null}
|
||||
disabled={!canStart}
|
||||
outlined={!canStart}
|
||||
tooltip={!canStart ? blockReason : undefined}
|
||||
tooltipOptions={{ position: 'top' }}
|
||||
onClick={() => openStartDialog(app)}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -212,56 +375,75 @@ const RendicontazioniMie = () => {
|
||||
|
||||
<div className="appPage__pageHeader">
|
||||
<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 più 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 piu tranches permettono rendicontazioni multi-fase.', 'gepafin')}</p>
|
||||
</div>
|
||||
|
||||
<div className="appPage__spacer"></div>
|
||||
|
||||
{loading && (
|
||||
<div className="appPageSection">
|
||||
<Skeleton width="100%" height="10rem" />
|
||||
<div>
|
||||
<Skeleton width="100%" height="14rem" style={{ marginBottom: '1rem' }} />
|
||||
<Skeleton width="100%" height="14rem" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!loading && apps.length === 0 && (
|
||||
<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' }} />
|
||||
<p>{__('Non ci sono rendicontazioni disponibili al momento.', 'gepafin')}</p>
|
||||
<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')}
|
||||
</small>
|
||||
</div>
|
||||
<Card>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
padding: '2.5rem 1.5rem',
|
||||
textAlign: 'center',
|
||||
}}>
|
||||
<i className="pi pi-inbox" style={{ fontSize: '2.5rem', color: 'var(--text-color-secondary)', marginBottom: '0.75rem' }} />
|
||||
<p style={{ fontSize: '1rem', margin: 0 }}>{__('Non ci sono rendicontazioni disponibili al momento.', 'gepafin')}</p>
|
||||
<small className="text-color-secondary" style={{ marginTop: '0.5rem', maxWidth: '28rem' }}>
|
||||
{__('Le rendicontazioni diventano disponibili dopo la firma del contratto e quando l\'ente ha pubblicato lo schema di rendicontazione per il bando.', 'gepafin')}
|
||||
</small>
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{!loading && apps.length > 0 && (
|
||||
<div className="appPageSection">
|
||||
{apps.map(renderApplicationCard)}
|
||||
</div>
|
||||
)}
|
||||
{!loading && apps.length > 0 && apps.map(renderApplicationCard)}
|
||||
|
||||
<Dialog header={__('Avvia nuova tranche di rendicontazione', 'gepafin')}
|
||||
visible={!!startDialog} style={{ width: '32rem' }}
|
||||
onHide={() => !starting && setStartDialog(null)}
|
||||
modal
|
||||
footer={(
|
||||
<div>
|
||||
<Button label={__('Annulla', 'gepafin')} icon="pi pi-times"
|
||||
onClick={() => setStartDialog(null)} outlined disabled={starting} />
|
||||
<Button label={__('Avvia tranche', 'gepafin')} icon="pi pi-play" iconPos="right"
|
||||
severity="success" loading={starting} onClick={confirmStart} />
|
||||
</div>
|
||||
)}>
|
||||
{/* START DIALOG */}
|
||||
<Dialog
|
||||
header={__('Avvia nuova tranche di rendicontazione', 'gepafin')}
|
||||
visible={!!startDialog}
|
||||
style={{ width: '32rem' }}
|
||||
onHide={() => !starting && setStartDialog(null)}
|
||||
modal
|
||||
footer={(
|
||||
<div>
|
||||
<Button label={__('Annulla', 'gepafin')} icon="pi pi-times"
|
||||
onClick={() => setStartDialog(null)} outlined disabled={starting} />
|
||||
<Button label={__('Avvia tranche', 'gepafin')} icon="pi pi-play" iconPos="right"
|
||||
severity="success" loading={starting} onClick={confirmStart} />
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
{startDialog && (
|
||||
<div>
|
||||
<p style={{ marginTop: 0 }}>
|
||||
{__('Stai per avviare la tranche', 'gepafin')}
|
||||
{' '}<strong>T{startDialog.next_seq}</strong> / {startDialog.max_tranches}
|
||||
{' '}{__('del bando', 'gepafin')} <strong>{startDialog.call_name}</strong>.
|
||||
</p>
|
||||
<p style={{ color: 'var(--text-color-secondary)' }}>
|
||||
{__('Cap remissione disponibile per questa tranche', 'gepafin')}:
|
||||
{' '}<strong>{fmtEur(startDialog.max_remission_next)}</strong>
|
||||
</p>
|
||||
<div style={{
|
||||
padding: '0.75rem 1rem',
|
||||
background: 'var(--surface-50)',
|
||||
borderRadius: '6px',
|
||||
marginBottom: '1rem',
|
||||
}}>
|
||||
<div style={{ fontSize: '0.9rem' }}>
|
||||
{__('Stai per avviare la tranche', 'gepafin')}
|
||||
{' '}<strong>T{startDialog.next_seq}</strong> / {startDialog.max_tranches}
|
||||
{' '}{__('del bando', 'gepafin')}
|
||||
</div>
|
||||
<div style={{ fontSize: '1rem', fontWeight: 700, marginTop: '4px', color: 'var(--primary-color)' }}>
|
||||
{startDialog.call_name}
|
||||
</div>
|
||||
<div style={{ fontSize: '0.85rem', color: 'var(--text-color-secondary)', marginTop: '6px' }}>
|
||||
{__('Cap remissione disponibile', 'gepafin')}:
|
||||
{' '}<strong style={{ color: '#16a34a' }}>{fmtEur(startDialog.max_remission_next)}</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="appForm__field">
|
||||
<label>{__('Periodo / fase (opzionale)', 'gepafin')}</label>
|
||||
@@ -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} />
|
||||
<small style={{ color: 'var(--text-color-secondary)' }}>
|
||||
<small className="text-color-secondary">
|
||||
{__('Descrizione libera per identificare la tranche. Apparirà sul verbale.', 'gepafin')}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
{startDialog.show_copy_ula && (
|
||||
<div className="appForm__field" style={{ marginTop: '1rem' }}>
|
||||
<div className="appForm__row">
|
||||
<div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
|
||||
<Checkbox inputId="copy_ula" checked={startForm.copy_ula}
|
||||
onChange={(e) => setStartForm(f => ({ ...f, copy_ula: e.checked }))}
|
||||
disabled={starting} />
|
||||
<label htmlFor="copy_ula" style={{ cursor: 'pointer' }}>
|
||||
<label htmlFor="copy_ula" style={{ cursor: 'pointer', margin: 0 }}>
|
||||
{__('Copia i dipendenti ULA dalla tranche precedente', 'gepafin')}
|
||||
</label>
|
||||
</div>
|
||||
<small style={{ color: 'var(--text-color-secondary)' }}>
|
||||
<small className="text-color-secondary">
|
||||
{__('Se attivo, i dipendenti censiti nella tranche precedente saranno precaricati. Potrai modificarli o rimuoverli prima di inviare.', 'gepafin')}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user