// Phase 4 closure (PERF-M1 + P-H3): memoized dashboard chart panels. // // Pre-Phase-4 the four chart panels lived as inline JSX inside // DashboardPage's return statement. DashboardPage has 9 useQuery hooks // (health / summary / issuers / statusCounts / expirationTimeline / // jobTrends / issuanceRate / certs / jobs) and each refetch — including // the per-tab refocus refetches the Phase 2 work narrowed but didn't // eliminate for the live-tile cohort — forced React to re-evaluate every // chart's JSX subtree, including the Recharts ResponsiveContainer // reconciliation that the library uses under the hood (~10-50 ms each // for charts with non-trivial data). // // Post-Phase-4 each chart is its own React.memo-wrapped component. When // only `summary` updates, the four chart panels skip re-render entirely // because their `data` prop didn't change. When `jobTrends` updates, // only `JobTrendsLineChart` re-renders; the other three panels skip. // // React.memo's default equality is referential (Object.is). The parent // DashboardPage passes the query result's `.data` arrays directly — TanStack // Query returns a stable reference until the underlying data actually // changes (it caches via queryKey), so referential equality is the // correct check for this layer. No custom areEqual function needed. import { memo } from 'react'; import { BarChart, Bar, LineChart, Line, PieChart, Pie, Cell, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend, } from 'recharts'; // ─── Shared helpers ────────────────────────────────────── /** PascalCase → space-separated for display ("RenewalInProgress" → "Renewal In Progress"). */ const formatStatus = (s: string) => s.replace(/([a-z])([A-Z])/g, '$1 $2'); /** "2026-05-10" → "5/10" for compact x-axis labels. */ const formatShortDate = (dateStr: string) => { const d = new Date(dateStr + 'T00:00:00'); return `${d.getMonth() + 1}/${d.getDate()}`; }; interface TooltipPayloadEntry { color?: string; name?: string; value?: number | string; } interface CustomTooltipProps { active?: boolean; payload?: TooltipPayloadEntry[]; label?: string; } const CustomTooltip = ({ active, payload, label }: CustomTooltipProps) => { if (!active || !payload?.length) return null; return (

{label}

{payload.map((entry, i) => (

{entry.name}: {typeof entry.value === 'number' && entry.name?.includes('rate') ? `${entry.value.toFixed(1)}%` : entry.value}

))}
); }; interface ChartCardProps { title: string; children: React.ReactNode; } export function ChartCard({ title, children }: ChartCardProps) { return (

{title}

{children}
); } // ─── Memoized chart panels ─────────────────────────────── export interface PieDatum { name: string; value: number; fill: string; } /** Certificates-by-Status pie chart. Re-renders only when `data` ref changes. */ export const CertsByStatusPieChart = memo(function CertsByStatusPieChart({ data }: { data: PieDatum[] }) { return ( {data.length > 0 ? ( `${formatStatus(name || '')}: ${value}`} labelLine={false} > {data.map((entry, index) => ( ))} } /> {formatStatus(value)}} /> ) : (
No certificate data
)}
); }); export interface WeeklyExpirationDatum { week: string; count: number; } /** Expiration Heatmap bar chart. Re-renders only when `data` ref changes. */ export const ExpirationTimelineBarChart = memo(function ExpirationTimelineBarChart({ data }: { data: WeeklyExpirationDatum[] }) { return ( {data.length > 0 ? ( } /> ) : (
No expiration data
)}
); }); export interface JobTrendDatum { date: string; completed_count: number; failed_count: number; } /** Job Success/Failure trend line chart. Re-renders only when `data` ref changes. */ export const JobTrendsLineChart = memo(function JobTrendsLineChart({ data }: { data: JobTrendDatum[] }) { return ( {data.length > 0 ? ( } /> {value}} /> ) : (
No job trend data
)}
); }); export interface IssuanceRateDatum { date: string; issued_count: number; } /** Certificate Issuance Rate bar chart. Re-renders only when `data` ref changes. */ export const IssuanceRateBarChart = memo(function IssuanceRateBarChart({ data }: { data: IssuanceRateDatum[] }) { return ( {data.length > 0 ? ( } /> ) : (
No issuance data
)}
); });