"use client"; import { useMemo } from "react"; import { SignInButton, SignedIn, SignedOut } from "@clerk/nextjs"; import { Area, AreaChart, Bar, BarChart, CartesianGrid, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis, } from "recharts"; import { Activity, Clock, PenSquare, Timer, Users } from "lucide-react"; import { DashboardSidebar } from "@/components/organisms/DashboardSidebar"; import { DashboardShell } from "@/components/templates/DashboardShell"; import { Button } from "@/components/ui/button"; import MetricSparkline from "@/components/charts/metric-sparkline"; import { useAuthedQuery } from "@/lib/api-query"; type RangeKey = "24h" | "7d"; type BucketKey = "hour" | "day"; type SeriesPoint = { period: string; value: number; }; type WipPoint = { period: string; inbox: number; in_progress: number; review: number; }; type RangeSeries = { range: RangeKey; bucket: BucketKey; points: SeriesPoint[]; }; type WipRangeSeries = { range: RangeKey; bucket: BucketKey; points: WipPoint[]; }; type SeriesSet = { primary: RangeSeries; comparison: RangeSeries; }; type WipSeriesSet = { primary: WipRangeSeries; comparison: WipRangeSeries; }; type DashboardMetrics = { range: RangeKey; generated_at: string; kpis: { active_agents: number; tasks_in_progress: number; error_rate_pct: number; median_cycle_time_hours_7d: number | null; }; throughput: SeriesSet; cycle_time: SeriesSet; error_rate: SeriesSet; wip: WipSeriesSet; }; const hourFormatter = new Intl.DateTimeFormat("en-US", { hour: "numeric" }); const dayFormatter = new Intl.DateTimeFormat("en-US", { month: "short", day: "numeric", }); const updatedFormatter = new Intl.DateTimeFormat("en-US", { hour: "numeric", minute: "2-digit", }); const formatPeriod = (value: string, bucket: BucketKey) => { const date = new Date(value); if (Number.isNaN(date.getTime())) return ""; return bucket === "hour" ? hourFormatter.format(date) : dayFormatter.format(date); }; const formatNumber = (value: number) => value.toLocaleString("en-US"); const formatPercent = (value: number) => `${value.toFixed(1)}%`; const formatHours = (value: number | null) => value === null || !Number.isFinite(value) ? "--" : `${value.toFixed(1)}h`; const calcProgress = (values?: number[]) => { if (!values || values.length === 0) return 0; const max = Math.max(...values); if (!Number.isFinite(max) || max <= 0) return 0; const latest = values[values.length - 1] ?? 0; return Math.max(0, Math.min(100, Math.round((latest / max) * 100))); }; function buildSeries(series: RangeSeries) { return series.points.map((point) => ({ period: formatPeriod(point.period, series.bucket), value: Number(point.value ?? 0), })); } function buildWipSeries(series: WipRangeSeries) { return series.points.map((point) => ({ period: formatPeriod(point.period, series.bucket), inbox: Number(point.inbox ?? 0), in_progress: Number(point.in_progress ?? 0), review: Number(point.review ?? 0), })); } function buildSparkline(series: RangeSeries) { return { values: series.points.map((point) => Number(point.value ?? 0)), labels: series.points.map((point) => formatPeriod(point.period, series.bucket)), }; } function buildWipSparkline(series: WipRangeSeries, key: keyof WipPoint) { return { values: series.points.map((point) => Number(point[key] ?? 0)), labels: series.points.map((point) => formatPeriod(point.period, series.bucket)), }; } type TooltipProps = { active?: boolean; payload?: Array<{ value?: number; name?: string; color?: string }>; label?: string; formatter?: (value: number, name?: string) => string; }; function TooltipCard({ active, payload, label, formatter }: TooltipProps) { if (!active || !payload?.length) return null; return (
{label}
{payload.map((entry) => (
{entry.name} {formatter ? formatter(Number(entry.value ?? 0), entry.name) : entry.value}
))}
); } function KpiCard({ label, value, sublabel, icon, progress = 0, }: { label: string; value: string; sublabel?: string; icon: React.ReactNode; progress?: number; }) { return (

{label}

{icon}

{value}

{sublabel ? (

{sublabel}

) : null}
); } function ChartCard({ title, subtitle, children, sparkline, }: { title: string; subtitle: string; children: React.ReactNode; sparkline?: { values: number[]; labels: string[] }; }) { return (

{title}

{subtitle}

24h
{children}
{sparkline ? (
7d trend
) : null}
); } export default function DashboardPage() { const metricsQuery = useAuthedQuery( ["metrics", "dashboard", "24h"], "/api/v1/metrics/dashboard?range=24h", { refetchInterval: 15_000, refetchOnMount: "always", }, ); const metrics = metricsQuery.data ?? null; const throughputSeries = useMemo( () => (metrics ? buildSeries(metrics.throughput.primary) : []), [metrics], ); const cycleSeries = useMemo( () => (metrics ? buildSeries(metrics.cycle_time.primary) : []), [metrics], ); const errorSeries = useMemo( () => (metrics ? buildSeries(metrics.error_rate.primary) : []), [metrics], ); const wipSeries = useMemo( () => (metrics ? buildWipSeries(metrics.wip.primary) : []), [metrics], ); const throughputSpark = useMemo( () => (metrics ? buildSparkline(metrics.throughput.comparison) : null), [metrics], ); const cycleSpark = useMemo( () => (metrics ? buildSparkline(metrics.cycle_time.comparison) : null), [metrics], ); const errorSpark = useMemo( () => (metrics ? buildSparkline(metrics.error_rate.comparison) : null), [metrics], ); const wipSpark = useMemo( () => (metrics ? buildWipSparkline(metrics.wip.comparison, "in_progress") : null), [metrics], ); const activeProgress = useMemo( () => (metrics ? Math.min(100, metrics.kpis.active_agents * 12.5) : 0), [metrics], ); const wipProgress = useMemo( () => calcProgress(wipSpark?.values), [wipSpark], ); const errorProgress = useMemo( () => calcProgress(errorSpark?.values), [errorSpark], ); const cycleProgress = useMemo( () => calcProgress(cycleSpark?.values), [cycleSpark], ); const updatedAtLabel = useMemo(() => { if (!metrics?.generated_at) return null; const date = new Date(metrics.generated_at); if (Number.isNaN(date.getTime())) return null; return updatedFormatter.format(date); }, [metrics]); return (

Sign in to access the dashboard.

Dashboard

Monitor your mission control operations

{updatedAtLabel ? (
Updated {updatedAtLabel}
) : null}
{metricsQuery.error ? (
{metricsQuery.error.message}
) : null} {metricsQuery.isLoading && !metrics ? (
Loading dashboard metrics…
) : null} {metrics ? ( <>
} progress={activeProgress} /> } progress={wipProgress} /> } progress={errorProgress} /> } progress={cycleProgress} />
formatNumber(v)} />} /> `${v.toFixed(1)}h`} />} /> formatPercent(v)} />} /> formatNumber(v)} />} />
) : null}
); }