11870e15d3
- New MilestoneStepper primitive (horizontal per-phase progress) - Token-migrate portal shell, phase cards, sidebar cards, kanban to dual-theme - Soft-tint status pills (emerald/amber/muted) replacing solid badges - embedded prop on ClientDashboard fixes double-header in multi-project tabs Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
107 lines
3.6 KiB
TypeScript
107 lines
3.6 KiB
TypeScript
import type { ClientView } from '@/lib/client-view';
|
|
|
|
interface PaymentStatusProps {
|
|
accepted_total: string;
|
|
payments: ClientView['payments'];
|
|
/** Label for the total row. Defaults to "Totale Investimento" */
|
|
totalLabel?: string;
|
|
/** Override the displayed amount (retainer: show offer accepted_total, not project total) */
|
|
overrideAmount?: string;
|
|
/** When true, hide individual payment rows (Acconto/Saldo) — used for retainer offers */
|
|
hideRows?: boolean;
|
|
}
|
|
|
|
type PaymentStatusValue = 'da_saldare' | 'inviata' | 'saldato';
|
|
|
|
const statusConfig: Record<
|
|
PaymentStatusValue,
|
|
{ label: string; pillClass: string }
|
|
> = {
|
|
da_saldare: {
|
|
label: 'Da Saldare',
|
|
pillClass:
|
|
'bg-amber-50 text-amber-700 border-amber-100 dark:bg-amber-500/10 dark:text-amber-400 dark:border-amber-500/20',
|
|
},
|
|
inviata: {
|
|
label: 'Inviata',
|
|
pillClass:
|
|
'bg-blue-50 text-blue-700 border-blue-100 dark:bg-blue-500/10 dark:text-blue-400 dark:border-blue-500/20',
|
|
},
|
|
saldato: {
|
|
label: 'Saldato',
|
|
pillClass:
|
|
'bg-emerald-50 text-emerald-700 border-emerald-100 dark:bg-emerald-500/10 dark:text-emerald-400 dark:border-emerald-500/20',
|
|
},
|
|
};
|
|
|
|
export function PaymentStatus({
|
|
accepted_total,
|
|
payments,
|
|
totalLabel = 'Totale Investimento',
|
|
overrideAmount,
|
|
hideRows = false,
|
|
}: PaymentStatusProps) {
|
|
const displayAmount = overrideAmount ?? accepted_total;
|
|
const totalFormatted = parseFloat(displayAmount || '0').toLocaleString('it-IT', {
|
|
style: 'currency',
|
|
currency: 'EUR',
|
|
minimumFractionDigits: 2,
|
|
maximumFractionDigits: 2,
|
|
});
|
|
|
|
return (
|
|
<div className="rounded-xl border border-border-light bg-card shadow-card overflow-hidden">
|
|
{/* Totale — unico importo visibile al cliente (LOCKED) */}
|
|
<div className="px-5 py-5 border-b border-border-light bg-muted">
|
|
<p className="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-1">
|
|
{totalLabel}
|
|
</p>
|
|
<p className="text-2xl font-bold text-foreground tracking-tight">
|
|
{totalFormatted}
|
|
</p>
|
|
</div>
|
|
|
|
{/* Righe pagamento: solo etichetta + stato, MAI importo singolo — omesse se hideRows */}
|
|
{!hideRows && (
|
|
<div className="px-5 py-4 space-y-2">
|
|
{payments.length === 0 ? (
|
|
<p className="text-sm text-muted-foreground italic py-2">
|
|
Nessun piano di pagamento configurato.
|
|
</p>
|
|
) : (
|
|
payments.map((payment) => {
|
|
const status = payment.status as PaymentStatusValue;
|
|
const config = statusConfig[status] ?? statusConfig.da_saldare;
|
|
|
|
return (
|
|
<div
|
|
key={payment.id}
|
|
className="flex items-center justify-between gap-4 rounded-lg border border-border-light p-3"
|
|
>
|
|
<p className="text-xs font-semibold text-foreground">
|
|
{payment.label}
|
|
</p>
|
|
|
|
{/* Pill stato — nessun importo */}
|
|
<span
|
|
className={`rounded border px-2 py-1 text-[10px] font-bold uppercase shrink-0 ${config.pillClass}`}
|
|
>
|
|
{config.label}
|
|
</span>
|
|
</div>
|
|
);
|
|
})
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{/* Nota informativa */}
|
|
<div className="px-5 py-3 border-t border-border-light bg-muted">
|
|
<p className="text-[11px] text-muted-foreground italic">
|
|
Per dettagli o domande sui pagamenti, contattaci direttamente.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|