// §3.8 Rate Health Dashboard — Completeness, Expiry, Validation function RateHealth({ user, perms, onAddRate }) { const [tab, setTab] = useState("completeness"); const tabs = [ { id: "completeness", label: "Completeness" }, { id: "expiry", label: "Expiry Alerts", count: EXPIRY_ALERTS.length }, { id: "validation", label: "Quality Validation", count: VALIDATION_RESULTS.filter(r => r.severity === "Error").length }, ]; return (
{(perms.edit || perms.admin) && ( )} } /> {tab === "completeness" && } {tab === "expiry" && } {tab === "validation" && }
); } function HealthKPIs() { const totalExpected = COMPLETENESS.length; const present = COMPLETENESS.filter(c => c.status === "Present").length; const missing = COMPLETENESS.filter(c => c.status === "Missing").length; const expiring = COMPLETENESS.filter(c => c.status === "Expiring").length; const overallPct = Math.round((present / totalExpected) * 100); const errCount = VALIDATION_RESULTS.filter(r => r.severity === "Error").length; const warnCount = VALIDATION_RESULTS.filter(r => r.severity === "Warning").length; return (
Overall Completeness
{overallPct}%
{present} / {totalExpected} combinations
Missing Rates
5 ? "var(--red)" : "var(--ink)" }}>{missing}
across 5 terminals · click to add
Expiring < 60 days
{EXPIRY_ALERTS.length}
↑ 2 vs last week
Open Validation Issues
{VALIDATION_RESULTS.length}
{errCount} errors · {warnCount} warnings
); } // ============== COMPLETENESS PANEL ============== function Completeness({ onAddRate }) { const [filters, setFilters] = useState({ terminal: "", costType: "", status: "" }); const rows = COMPLETENESS.filter(r => (!filters.terminal || r.terminal === filters.terminal) && (!filters.costType || r.costType === filters.costType) && (!filters.status || r.status === filters.status) ); // Per-terminal summary cards const summaries = Object.entries(COMPLETENESS_SUMMARY).map(([t, s]) => ({ code: t, name: TERMINAL_BY_CODE[t]?.name, ...s, pct: Math.round((s.present / s.total) * 100) })).sort((a, b) => a.pct - b.pct); return (
{/* Per-terminal cards */}
{summaries.map(s => (
{s.name}
{s.code}
● {s.present} present ● {s.missing} missing ● {s.expiring} expiring
))}
({ value: t, label: TERMINAL_BY_CODE[t].name }))} onChange={v => setFilters({ ...filters, terminal: v })} /> c.code === filters.costType)?.name : ""} options={COST_TYPES.map(c => ({ value: c.code, label: c.name }))} onChange={v => setFilters({ ...filters, costType: v })} /> setFilters({ ...filters, status: v })} />
Present Missing Expiring
Edit matrix}>
{rows.slice(0, 18).map((r, i) => ( r.status === "Missing" && onAddRate && onAddRate({ terminal: r.terminal, costType: r.costType, equipment: r.equipment, cargo: r.cargo })} style={{ cursor: r.status === "Missing" ? "pointer" : "default", background: r.status === "Missing" ? "#fef4f4" : r.status === "Expiring" ? "#fdf6e7" : null, }}> ))}
Terminal Cost Type Equipment Cargo Status Last Updated
{TERMINAL_BY_CODE[r.terminal]?.name}
{r.terminal}
{COST_TYPES.find(c => c.code === r.costType)?.name} {r.equipment} {r.cargo} {r.status === "Present" && ✓ Present} {r.status === "Missing" && ✕ Missing} {r.status === "Expiring" && ⚠ Expiring in {r.expiryDays}d} {r.lastUpdated || "—"} {r.status === "Missing" && } {r.status === "Expiring" && }
); } // ============== EXPIRY PANEL ============== function ExpiryAlerts() { const [window, setWindow] = useState(60); const expired = [ { id: "R-09988", terminal: "CNXMG", costType: "Reefer Plug In/Out", expiry: "2025-04-30", days: -14, focal: "—" }, { id: "R-09974", terminal: "JPYOK", costType: "Storage", expiry: "2025-05-01", days: -13, focal: "Sato T." }, ]; const within = EXPIRY_ALERTS.filter(a => a.days <= window); return (
Alert window
{[30, 60, 90].map(w => ( ))}
≤ 14 days 15–30 days 31–60 days
{within.sort((a, b) => a.days - b.days).map(r => { const color = r.days <= 14 ? "var(--red)" : r.days <= 30 ? "var(--amber)" : "#d8a83a"; return ( ); })}
Rate ID Terminal Cost Type Equip / Cargo Rate Expiry Days Left Assigned Focal
{r.id}
{r.terminalName}
{r.terminal}
{r.costType} {r.equipment} · {r.cargo} {fmt.num(r.rate)} {r.ccy} {r.expiry}
{r.days}d
{r.focal}
{expired.map(r => ( ))}
Rate IDTerminalCost TypeExpiredDays overdueAssigned Focal
{r.id} {r.terminal} {r.costType} {r.expiry} {Math.abs(r.days)}d ago {r.focal}
); } // ============== VALIDATION PANEL ============== function Validation() { const [severity, setSeverity] = useState(""); const rows = VALIDATION_RESULTS.filter(r => !severity || r.severity === severity); const errCount = VALIDATION_RESULTS.filter(r => r.severity === "Error").length; const warnCount = VALIDATION_RESULTS.filter(r => r.severity === "Warning").length; return (
{[ { v: "", l: `All ${VALIDATION_RESULTS.length}` }, { v: "Error", l: `Errors ${errCount}`, c: "var(--red)" }, { v: "Warning", l: `Warnings ${warnCount}`, c: "var(--amber)" }, ].map(s => ( ))}
Last run: 2025-05-13 14:08 · 1,284 rates scanned
{rows.map((r, i) => ( ))}
Rule Severity Terminal Target Detail
{r.rule}
{r.name}
{r.severity === "Error" && Error} {r.severity === "Warning" && Warning} {r.terminal} {r.target} {r.detail} {r.rule === "QV-02" && }
{VALIDATION_RULES.map((r, i) => (
{r.id} {r.name} {r.severity === "Error" ? "Blocks save" : "Warns"}
{r.desc}
))}
); } Object.assign(window, { RateHealth });