From bf2a9452dcb82903d6255f83bcdca7c3df29366e Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Wed, 4 Feb 2026 21:23:11 +0530 Subject: [PATCH] feat(dashboard): Revamp UI components for improved layout and styling --- frontend/src/app/dashboard/page.tsx | 534 ++++++++++-------- frontend/src/app/globals.css | 36 +- frontend/src/components/atoms/BrandMark.tsx | 9 +- .../components/organisms/DashboardSidebar.tsx | 41 +- .../src/components/organisms/UserMenu.tsx | 26 +- .../components/templates/DashboardShell.tsx | 34 +- 6 files changed, 374 insertions(+), 306 deletions(-) diff --git a/frontend/src/app/dashboard/page.tsx b/frontend/src/app/dashboard/page.tsx index cdebe48..09e0eff 100644 --- a/frontend/src/app/dashboard/page.tsx +++ b/frontend/src/app/dashboard/page.tsx @@ -16,6 +16,7 @@ import { 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"; @@ -97,6 +98,13 @@ 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) => ({ @@ -138,8 +146,8 @@ type TooltipProps = { function TooltipCard({ active, payload, label, formatter }: TooltipProps) { if (!active || !payload?.length) return null; return ( -
-
{label}
+
+
{label}
{payload.map((entry) => (
@@ -164,31 +172,37 @@ function KpiCard({ label, value, sublabel, - sparkline, + icon, + progress = 0, }: { label: string; value: string; sublabel?: string; - sparkline?: { values: number[]; labels: string[] }; + icon: React.ReactNode; + progress?: number; }) { return ( -
-
- {label} -
-
{value}
- {sublabel ? ( -
{sublabel}
- ) : null} - {sparkline ? ( -
- +
+
+

+ {label} +

+
+ {icon}
+
+
+

{value}

+
+ {sublabel ? ( +

{sublabel}

) : null} +
+
+
); } @@ -205,24 +219,30 @@ function ChartCard({ sparkline?: { values: number[]; labels: string[] }; }) { return ( -
-
+
+
-
- {subtitle} -
-
{title}
+

+ {title} +

+

{subtitle}

-
24h
+ + 24h +
-
{children}
+
{children}
{sparkline ? ( -
-
7d trend
+
+
+ + 7d trend +
) : null} @@ -301,6 +321,23 @@ export default function DashboardPage() { [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); @@ -311,221 +348,242 @@ export default function DashboardPage() { return ( -
-

- Sign in to access the dashboard. -

- - - +
+
+

+ Sign in to access the dashboard. +

+ + + +
-
-
-

Dashboard

- {updatedAtLabel ? ( -
Updated {updatedAtLabel}
+
+
+
+
+

+ Dashboard +

+

+ Monitor your mission control operations +

+
+ {updatedAtLabel ? ( +
+ + Updated {updatedAtLabel} +
+ ) : null} +
+
+
+ + {error ? ( +
+ {error} +
+ ) : null} + + {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}
- - {error ? ( -
- {error} -
- ) : null} - - {isLoading && !metrics ? ( -
- Loading dashboard metrics… -
- ) : null} - - {metrics ? ( - <> -
- - - - -
- -
- - - - - - - formatNumber(v)} />} /> - - - - - - - - - - - - `${v.toFixed(1)}h`} />} - /> - - - - - - - - - - - - formatPercent(v)} />} - /> - - - - - - - - - - - - formatNumber(v)} />} /> - - - - - - -
- - ) : null} -
+ ); diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index cb1184a..d34394b 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -4,23 +4,27 @@ :root { color-scheme: light; - --bg: #f4f6fa; + --bg: #f8fafc; --surface: #ffffff; - --surface-muted: #eef2f7; - --surface-strong: #e6ecf6; - --border: #d6dce6; - --border-strong: #c2cad9; - --text: #0b1220; - --text-muted: #4b5a73; - --text-quiet: #7c8aa0; - --accent: #1d4ed8; - --accent-strong: #1e40af; - --accent-soft: rgba(29, 78, 216, 0.12); - --success: #0f766e; - --warning: #b45309; - --danger: #b42318; - --shadow-panel: 0 18px 40px rgba(12, 17, 29, 0.08); - --shadow-card: 0 12px 24px rgba(12, 17, 29, 0.06); + --surface-muted: #f1f5f9; + --surface-strong: #e2e8f0; + --border: #e2e8f0; + --border-strong: #cbd5e1; + --text: #0f172a; + --text-muted: #64748b; + --text-quiet: #94a3b8; + --accent: #2563eb; + --accent-strong: #1d4ed8; + --accent-soft: rgba(37, 99, 235, 0.12); + --success: #16a34a; + --warning: #d97706; + --danger: #dc2626; + --shadow-panel: + 0 1px 3px rgba(15, 23, 42, 0.12), + 0 1px 2px rgba(15, 23, 42, 0.08); + --shadow-card: + 0 1px 2px rgba(15, 23, 42, 0.08), + 0 2px 6px rgba(15, 23, 42, 0.06); } body { diff --git a/frontend/src/components/atoms/BrandMark.tsx b/frontend/src/components/atoms/BrandMark.tsx index 8cb444b..5487176 100644 --- a/frontend/src/components/atoms/BrandMark.tsx +++ b/frontend/src/components/atoms/BrandMark.tsx @@ -1,13 +1,12 @@ export function BrandMark() { return (
-
- OC - +
+ OC
-
- OpenClaw +
+ OPENCLAW
Mission Control diff --git a/frontend/src/components/organisms/DashboardSidebar.tsx b/frontend/src/components/organisms/DashboardSidebar.tsx index 5399a0d..9731d1d 100644 --- a/frontend/src/components/organisms/DashboardSidebar.tsx +++ b/frontend/src/components/organisms/DashboardSidebar.tsx @@ -10,18 +10,19 @@ export function DashboardSidebar() { const pathname = usePathname(); return ( -