// Tenants view — Mossad is single-tenant per session today, so there's exactly one tenant // card to show. Its name/branding come from the TENANTS fixture (tenant-data.jsx), but every // number below is computed live from the alerts/cases/SARs/customers already loaded for the // logged-in tenant — nothing here is a static number anymore. function statusPill(status) { if (status === "healthy") return { cls: "pill low", label: "Healthy" }; if (status === "watch") return { cls: "pill med", label: "Watch" }; if (status === "attention") return { cls: "pill high", label: "Attention" }; if (status === "onboarding") return { cls: "pill info", label: "Onboarding" }; return { cls: "pill", label: status }; } // Sparkline tuned for the tenant card — wider, with axis baseline. function TenantSpark({ data, color, height = 56 }) { if (!data.length) return null; const max = Math.max(...data, 1); const min = 0; const W = 320, H = height, P = 4; const pts = data.map((v, i) => { const x = P + (i / (data.length - 1)) * (W - P * 2); const y = H - P - ((v - min) / (max - min || 1)) * (H - P * 2); return [x, y]; }); const d = pts.map((p, i) => (i === 0 ? "M" : "L") + p[0].toFixed(1) + " " + p[1].toFixed(1)).join(" "); const area = `${d} L ${pts[pts.length - 1][0].toFixed(1)} ${H - P} L ${pts[0][0].toFixed(1)} ${H - P} Z`; return ( ); } // Real 30-day alert volume, bucketed by day from the alerts already loaded for this tenant. function alertVolumeByDay(alerts, days = 30) { const buckets = []; const keys = []; const start = new Date(); start.setHours(0, 0, 0, 0); start.setDate(start.getDate() - (days - 1)); for (let i = 0; i < days; i++) { const d = new Date(start); d.setDate(d.getDate() + i); keys.push(d.toISOString().slice(0, 10)); buckets.push(0); } const index = new Map(keys.map((k, i) => [k, i])); alerts.forEach(a => { const ts = a.opened || a._raw?.created_at; if (!ts) return; const day = new Date(ts).toISOString().slice(0, 10); const i = index.get(day); if (i != null) buckets[i] += 1; }); return buckets; } // Real current severity mix — a snapshot, not a fake 12-month trend. function severityMix(alerts) { const mix = { low: 0, med: 0, high: 0, severe: 0 }; alerts.forEach(a => { if (a.severity === "critical") mix.severe += 1; else if (a.severity === "high") mix.high += 1; else if (a.severity === "med" || a.severity === "medium") mix.med += 1; else mix.low += 1; }); return mix; } function SeverityMixBar({ mix }) { const order = [ { k: "low", label: "Low", color: "var(--risk-low)" }, { k: "med", label: "Medium", color: "var(--risk-med)" }, { k: "high", label: "High", color: "oklch(56% 0.20 35)" }, { k: "severe", label: "Critical", color: "var(--risk-high)" }, ]; const total = order.reduce((s, o) => s + mix[o.k], 0) || 1; return (