// Dashboard di Gruppo const KpiHero = ({ label, value, unit, delta, deltaAbs, spark, invert }) => (
{label}
{value.toLocaleString('it-IT', { minimumFractionDigits: 1, maximumFractionDigits: 1 })} {unit}
); const CompanyCard = ({ c, onClick }) => { const revLabel = c.sector === 'Fondazione' ? 'Entrate YTD' : 'Fatturato YTD'; const marLabel = c.sector === 'Fondazione' ? 'Copertura costi' : 'Margine %'; return (
{c.short}
{c.name}
{c.sectorTag}
{c.status === 'green' ? 'OK' : c.status === 'amber' ? 'Attenz.' : 'Critico'}
{revLabel}
{c.fatturato.value.toFixed(1)} M€
0 ? 'var(--green)' : 'var(--red)' }}> {c.fatturato.delta > 0 ? '+' : ''}{c.fatturato.delta.toFixed(1)}%
{marLabel}
{c.margine.value.toFixed(1)}%
0 ? 'var(--green)' : 'var(--red)' }}> {c.margine.delta > 0 ? '+' : ''}{c.margine.delta.toFixed(1)} p.p.
Cash position
{c.cash.value.toFixed(1)} M€
0 ? 'var(--green)' : 'var(--red)' }}> {c.cash.delta > 0 ? '+' : ''}{c.cash.delta.toFixed(1)}%
); }; // Treemap (sector allocation, hand-computed squarified-ish layout) const Treemap = ({ data }) => { // Simple row-based layout const total = data.reduce((s, d) => s + d.value, 0); // Row 1: first 2 items (large), Row 2: rest const [a, b, c, d] = data; return (
{a.label}
{a.pct}%
{a.value.toFixed(1)} M€
{b.label}
{b.pct}%
{b.value.toFixed(1)} M€
{c.label}
{c.pct}%
{c.value.toFixed(1)}
{d.label}
{d.pct}%
{d.value.toFixed(1)}
); }; // Donut / asset class const AssetDonut = ({ data }) => { const total = data.reduce((s, d) => s + d.value, 0); const colors = ['#2A3B48', '#5B7A6B', '#A89468', '#C4BFB3']; let acc = 0; const r = 60, cx = 80, cy = 80; const segs = data.map((d, i) => { const startAngle = (acc / total) * 2 * Math.PI - Math.PI / 2; acc += d.value; const endAngle = (acc / total) * 2 * Math.PI - Math.PI / 2; const large = endAngle - startAngle > Math.PI ? 1 : 0; const x1 = cx + r * Math.cos(startAngle), y1 = cy + r * Math.sin(startAngle); const x2 = cx + r * Math.cos(endAngle), y2 = cy + r * Math.sin(endAngle); return { d: `M${cx},${cy} L${x1},${y1} A${r},${r} 0 ${large} 1 ${x2},${y2} Z`, color: colors[i] }; }); return (
{segs.map((s, i) => )} {total.toFixed(0)} M€ totale
{data.map((d, i) => (
{d.label} {d.pct}% {d.value.toFixed(1)}
))}
); }; const AlertsList = ({ alerts }) => (
{alerts.map((a, i) => (
{a.severity === 'critical' ? 'Critico' : a.severity === 'warning' ? 'Avviso' : 'Info'} {a.text} {a.company} {a.date} {a.days !== null && a.days !== undefined && · {a.days}gg}
))}
); const Dashboard = ({ onCompanyClick, horizon = 'tattico' }) => { const D = window.REGULA_DATA; const k = D.group.kpis; // Horizon-aware configuration const horizonConfig = { operativo: { subtitle: 'Vista operativa · settimana corrente · 4 società · refresh 14:22', periodLabel: 'Settimana 17 · 2026', compareLabel: 'vs. sett. precedente', briefLabel: 'Brief giornaliero', kpis: [ { ...k.liquidita, label: 'Liquidità disponibile' }, { label: 'Pagamenti prossimi 7gg', value: 3.24, unit: 'M€', delta: -1.2, deltaAbs: '−0.1 M€', spark: [2.8, 3.0, 3.1, 3.2, 3.1, 3.25, 3.24] }, { label: 'Incassi attesi 7gg', value: 2.98, unit: 'M€', delta: 4.8, deltaAbs: '+0.14 M€', spark: [2.4, 2.5, 2.7, 2.8, 2.85, 2.9, 2.98] }, { ...k.debito, label: 'Utilizzo linee B/T', value: 18.4, deltaAbs: '−0.8 M€', spark: [22,21,20,19.5,19,18.6,18.4] } ], sectionTitle: 'Cash position per società · oggi', showForecasts: false }, tattico: { subtitle: 'Dashboard consolidata · ' + D.group.period + ' · 4 società · Ultimo refresh 14:22', periodLabel: 'Apr 2026 — YTD', compareLabel: 'vs. YTD 2025', briefLabel: 'Brief AI', kpis: [k.patrimonio, k.liquidita, k.ebitda, { ...k.debito }], sectionTitle: 'Società del gruppo', showForecasts: true }, strategico: { subtitle: 'Orizzonte strategico · Piano industriale 2026–2030 · 4 società', periodLabel: 'Piano 2026–2030', compareLabel: 'vs. piano precedente', briefLabel: 'Proiezione piano', kpis: [ { label: 'Patrimonio netto target 2030', value: 418.0, unit: 'M€', delta: 46.9, deltaAbs: '+133 M€', spark: [284, 300, 320, 345, 370, 395, 418] }, { label: 'EBITDA target 2030', value: 58.6, unit: 'M€', delta: 52.6, deltaAbs: '+20.2 M€', spark: [38, 42, 46, 50, 53, 56, 58.6] }, { label: 'Capex cumulato 5y', value: 142.0, unit: 'M€', delta: 0, deltaAbs: 'pianificato', spark: [0, 24, 52, 82, 108, 128, 142] }, { label: 'IRR medio progetti', value: 11.8, unit: '%', delta: 1.8, deltaAbs: 'vs hurdle 10%', spark: [9, 9.5, 10.2, 10.8, 11.2, 11.5, 11.8] } ], sectionTitle: 'Società · traiettoria al 2030', showForecasts: true } }; const H = horizonConfig[horizon] || horizonConfig.tattico; return (

{D.group.name}

{H.subtitle}
{H.kpis.map((kpi, i) => )}
{H.sectionTitle}
4 società · click per drill-down · orizzonte {horizon}
{D.companies.map(c => onCompanyClick(c.id)} />)}
Allocazione per asset class
284.6 M€
Allocazione per settore
4 settori
{horizon === 'operativo' ? 'Alert operativi · oggi e prossimi 7gg' : horizon === 'strategico' ? 'Milestone piano industriale' : 'Alert & Scadenze'}
{horizon === 'operativo' ? '1 Urgente' : '2 Critici'} 3 Avvisi
a.days !== null && a.days <= 30) : D.alerts} />
); }; Object.assign(window, { Dashboard });