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 { Dialog } from 'primereact/dialog';
|
||||||
import { InputText } from 'primereact/inputtext';
|
import { InputText } from 'primereact/inputtext';
|
||||||
import { Checkbox } from 'primereact/checkbox';
|
import { Checkbox } from 'primereact/checkbox';
|
||||||
|
import { Card } from 'primereact/card';
|
||||||
|
import { Divider } from 'primereact/divider';
|
||||||
|
|
||||||
import RendicontazioneService from '../service/rendicontazioneService';
|
import RendicontazioneService from '../service/rendicontazioneService';
|
||||||
|
|
||||||
const STATUS_TAGS = {
|
const STATUS_TAGS = {
|
||||||
DRAFT: { severity: 'warning', label: 'In compilazione' },
|
DRAFT: { severity: 'warning', label: 'In compilazione', icon: 'pi pi-pencil' },
|
||||||
SUBMITTED: { severity: 'info', label: 'Inviata' },
|
SUBMITTED: { severity: 'info', label: 'Inviata', icon: 'pi pi-send' },
|
||||||
UNDER_REVIEW: { severity: 'info', label: 'In valutazione' },
|
UNDER_REVIEW: { severity: 'info', label: 'In valutazione', icon: 'pi pi-eye' },
|
||||||
APPROVED: { severity: 'success', label: 'Approvata' },
|
APPROVED: { severity: 'success', label: 'Approvata', icon: 'pi pi-check-circle' },
|
||||||
REJECTED: { severity: 'danger', label: 'Respinta' },
|
REJECTED: { severity: 'danger', label: 'Respinta', icon: 'pi pi-times-circle' },
|
||||||
AWAITING_AMENDMENT: { severity: 'warning', label: 'Soccorso istruttorio' }
|
AWAITING_AMENDMENT: { severity: 'warning', label: 'Soccorso istruttorio',icon: 'pi pi-exclamation-triangle' }
|
||||||
};
|
};
|
||||||
|
|
||||||
const fmtEur = (v) => {
|
const fmtEur = (v) => {
|
||||||
@@ -26,6 +28,106 @@ const fmtEur = (v) => {
|
|||||||
return `€ ${n.toLocaleString('it-IT', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
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 RendicontazioniMie = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const toast = useRef(null);
|
const toast = useRef(null);
|
||||||
@@ -58,151 +160,212 @@ 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,
|
||||||
max_remission_next: app.max_remission_next_tranche,
|
|
||||||
show_copy_ula: nextSeq > 1,
|
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 = () => {
|
const confirmStart = () => {
|
||||||
if (!startDialog) return;
|
if (!startDialog) return;
|
||||||
setStarting(true);
|
setStarting(true);
|
||||||
RendicontazioneService.startPractice(startDialog.application_id,
|
RendicontazioneService.startPractice(
|
||||||
|
startDialog.application_id,
|
||||||
(resp) => {
|
(resp) => {
|
||||||
setStarting(false);
|
setStarting(false);
|
||||||
setStartDialog(null);
|
setStartDialog(null);
|
||||||
toast.current?.show({
|
toast.current?.show({ severity: 'success', summary: resp?.message || __('Tranche avviata', 'gepafin') });
|
||||||
severity: 'success',
|
navigate(`/rendicontazioni/${resp.data.id}`);
|
||||||
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({
|
toast.current?.show({ severity: 'error', summary: __('Avvio fallito', 'gepafin'), detail: err?.detail });
|
||||||
severity: 'error',
|
|
||||||
summary: __('Avvio fallito', 'gepafin'),
|
|
||||||
detail: err?.detail || err?.message
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
period_label: startForm.period_label || null,
|
period_label: startForm.period_label?.trim() || null,
|
||||||
copy_ula_from_previous: startDialog.show_copy_ula ? startForm.copy_ula : false,
|
copy_ula_from_previous: startForm.copy_ula,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderApplicationCard = (app) => {
|
const renderApplicationCard = (app) => {
|
||||||
const hasTranches = (app.tranches?.length || 0) > 0;
|
const tranchesCount = app.tranches?.length || 0;
|
||||||
const nextSeq = (app.tranches?.length || 0) + 1;
|
const hasTranches = tranchesCount > 0;
|
||||||
|
const nextSeq = tranchesCount + 1;
|
||||||
const canStart = !!app.can_start_new;
|
const canStart = !!app.can_start_new;
|
||||||
const blockReason = app.start_blocked_reason;
|
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 (
|
const headerTemplate = (
|
||||||
<div key={app.application_id} className="appPageSection__withBorder" style={{ marginBottom: 24 }}>
|
<div style={{
|
||||||
{/* HEADER: bando + erogato */}
|
display: 'flex',
|
||||||
<div className="row">
|
justifyContent: 'space-between',
|
||||||
<div style={{ flex: 1 }}>
|
alignItems: 'flex-start',
|
||||||
<h2 style={{ color: 'var(--primary-color)' }}>
|
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}`}
|
{app.call_name || `Bando #${app.call_id}`}
|
||||||
</h2>
|
</h3>
|
||||||
<p style={{ margin: '4px 0 0', color: 'var(--text-color-secondary)', fontSize: 14 }}>
|
<div style={{ color: 'var(--text-color-secondary)', fontSize: '0.85rem', marginTop: '4px' }}>
|
||||||
{app.company_name || ''} · {__('Domanda', 'gepafin')} #{app.application_id}
|
<i className="pi pi-building" style={{ marginRight: '4px' }} />
|
||||||
</p>
|
{app.company_name || '—'}
|
||||||
|
<span style={{ margin: '0 6px' }}>·</span>
|
||||||
|
{__('Domanda', 'gepafin')} <strong>#{app.application_id}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ textAlign: 'right' }}>
|
</div>
|
||||||
<div style={{ fontSize: 13, color: 'var(--text-color-secondary)' }}>
|
<div style={{ textAlign: 'right', flexShrink: 0 }}>
|
||||||
|
<div style={{ fontSize: '0.72rem', color: 'var(--text-color-secondary)', textTransform: 'uppercase', letterSpacing: '0.02em' }}>
|
||||||
{__('Finanziamento erogato','gepafin')}
|
{__('Finanziamento erogato','gepafin')}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ fontSize: 20, fontWeight: 700 }}>{fmtEur(app.amount_erogato)}</div>
|
<div style={{ fontSize: '1.4rem', fontWeight: 700, lineHeight: 1.1 }}>
|
||||||
</div>
|
{fmtEur(app.amount_erogato)}
|
||||||
</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>
|
||||||
</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>
|
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>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* AZIONI: nuova tranche */}
|
{/* TRANCHES */}
|
||||||
<div className="appPageSection__actions" style={{ justifyContent: 'flex-end', alignItems: 'center', padding: '12px 0 0' }}>
|
{hasTranches ? (
|
||||||
{!canStart && blockReason && (
|
<div style={{ padding: '0.5rem 0.25rem 0' }}>
|
||||||
<small style={{ color: 'var(--text-color-secondary)', fontStyle: 'italic' }}>
|
<div style={{
|
||||||
<i className="pi pi-info-circle" style={{ marginRight: 4 }} />
|
fontSize: '0.75rem',
|
||||||
{blockReason}
|
color: 'var(--text-color-secondary)',
|
||||||
</small>
|
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>
|
||||||
)}
|
)}
|
||||||
<Button icon="pi pi-plus-circle"
|
|
||||||
|
{/* 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
|
label={hasTranches
|
||||||
? `${__('+ Nuova tranche','gepafin')} (T${nextSeq})`
|
? `${__('Nuova tranche','gepafin')} (T${nextSeq})`
|
||||||
: __('+ Avvia rendicontazione','gepafin')}
|
: __('Avvia rendicontazione','gepafin')}
|
||||||
severity={canStart ? 'success' : 'secondary'}
|
severity={canStart ? 'success' : null}
|
||||||
disabled={!canStart}
|
disabled={!canStart}
|
||||||
outlined={!canStart}
|
outlined={!canStart}
|
||||||
tooltip={!canStart ? blockReason : undefined}
|
tooltip={!canStart ? blockReason : undefined}
|
||||||
tooltipOptions={{ position: 'top' }}
|
tooltipOptions={{ position: 'top' }}
|
||||||
onClick={() => openStartDialog(app)} />
|
onClick={() => openStartDialog(app)}
|
||||||
</div>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -212,35 +375,43 @@ 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 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>
|
||||||
|
|
||||||
<div className="appPage__spacer"></div>
|
<div className="appPage__spacer"></div>
|
||||||
|
|
||||||
{loading && (
|
{loading && (
|
||||||
<div className="appPageSection">
|
<div>
|
||||||
<Skeleton width="100%" height="10rem" />
|
<Skeleton width="100%" height="14rem" style={{ marginBottom: '1rem' }} />
|
||||||
|
<Skeleton width="100%" height="14rem" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!loading && apps.length === 0 && (
|
{!loading && apps.length === 0 && (
|
||||||
<div className="appPageSection__withBorder" style={{ alignItems: 'center', textAlign: 'center', padding: '3rem 2rem' }}>
|
<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' }} />
|
<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 style={{ fontSize: '1rem', margin: 0 }}>{__('Non ci sono rendicontazioni disponibili al momento.', 'gepafin')}</p>
|
||||||
<small style={{ color: 'var(--text-color-secondary)' }}>
|
<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')}
|
{__('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>
|
||||||
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!loading && apps.length > 0 && (
|
{!loading && apps.length > 0 && apps.map(renderApplicationCard)}
|
||||||
<div className="appPageSection">
|
|
||||||
{apps.map(renderApplicationCard)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Dialog header={__('Avvia nuova tranche di rendicontazione', 'gepafin')}
|
{/* START DIALOG */}
|
||||||
visible={!!startDialog} style={{ width: '32rem' }}
|
<Dialog
|
||||||
|
header={__('Avvia nuova tranche di rendicontazione', 'gepafin')}
|
||||||
|
visible={!!startDialog}
|
||||||
|
style={{ width: '32rem' }}
|
||||||
onHide={() => !starting && setStartDialog(null)}
|
onHide={() => !starting && setStartDialog(null)}
|
||||||
modal
|
modal
|
||||||
footer={(
|
footer={(
|
||||||
@@ -250,18 +421,29 @@ const RendicontazioniMie = () => {
|
|||||||
<Button label={__('Avvia tranche', 'gepafin')} icon="pi pi-play" iconPos="right"
|
<Button label={__('Avvia tranche', 'gepafin')} icon="pi pi-play" iconPos="right"
|
||||||
severity="success" loading={starting} onClick={confirmStart} />
|
severity="success" loading={starting} onClick={confirmStart} />
|
||||||
</div>
|
</div>
|
||||||
)}>
|
)}
|
||||||
|
>
|
||||||
{startDialog && (
|
{startDialog && (
|
||||||
<div>
|
<div>
|
||||||
<p style={{ marginTop: 0 }}>
|
<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')}
|
{__('Stai per avviare la tranche', 'gepafin')}
|
||||||
{' '}<strong>T{startDialog.next_seq}</strong> / {startDialog.max_tranches}
|
{' '}<strong>T{startDialog.next_seq}</strong> / {startDialog.max_tranches}
|
||||||
{' '}{__('del bando', 'gepafin')} <strong>{startDialog.call_name}</strong>.
|
{' '}{__('del bando', 'gepafin')}
|
||||||
</p>
|
</div>
|
||||||
<p style={{ color: 'var(--text-color-secondary)' }}>
|
<div style={{ fontSize: '1rem', fontWeight: 700, marginTop: '4px', color: 'var(--primary-color)' }}>
|
||||||
{__('Cap remissione disponibile per questa tranche', 'gepafin')}:
|
{startDialog.call_name}
|
||||||
{' '}<strong>{fmtEur(startDialog.max_remission_next)}</strong>
|
</div>
|
||||||
</p>
|
<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">
|
<div className="appForm__field">
|
||||||
<label>{__('Periodo / fase (opzionale)', 'gepafin')}</label>
|
<label>{__('Periodo / fase (opzionale)', 'gepafin')}</label>
|
||||||
@@ -269,22 +451,22 @@ 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 style={{ color: 'var(--text-color-secondary)' }}>
|
<small className="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>
|
||||||
|
|
||||||
{startDialog.show_copy_ula && (
|
{startDialog.show_copy_ula && (
|
||||||
<div className="appForm__field" style={{ marginTop: '1rem' }}>
|
<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}
|
<Checkbox inputId="copy_ula" checked={startForm.copy_ula}
|
||||||
onChange={(e) => setStartForm(f => ({ ...f, copy_ula: e.checked }))}
|
onChange={(e) => setStartForm(f => ({ ...f, copy_ula: e.checked }))}
|
||||||
disabled={starting} />
|
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')}
|
{__('Copia i dipendenti ULA dalla tranche precedente', 'gepafin')}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</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')}
|
{__('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