From 73abcc1b6900f68b483529ee4b588252891784ca Mon Sep 17 00:00:00 2001 From: vince Date: Fri, 19 Jun 2026 23:41:19 +0200 Subject: [PATCH] feat(ui): port dashboard components to react --- src/lib/ui/components/Badge.tsx | 11 + src/lib/ui/components/Button.tsx | 39 ++ src/lib/ui/components/CornerBracketFrame.tsx | 26 ++ src/lib/ui/components/DashboardFrame.tsx | 49 +++ src/lib/ui/components/DashboardHeader.tsx | 30 ++ src/lib/ui/components/DiagonalStripeField.tsx | 24 ++ src/lib/ui/components/FooterCell.tsx | 42 ++ src/lib/ui/components/FooterStatusCell.tsx | 6 + src/lib/ui/components/GridFrame.tsx | 13 + src/lib/ui/components/IconButton.tsx | 33 ++ src/lib/ui/components/IconGlyph.tsx | 21 + src/lib/ui/components/LineChart.tsx | 27 ++ src/lib/ui/components/ModuleCard.tsx | 32 ++ src/lib/ui/components/Panel.tsx | 19 + src/lib/ui/components/ProgressMeter.tsx | 34 ++ src/lib/ui/components/ScanlineField.tsx | 21 + src/lib/ui/components/Separator.tsx | 19 + src/lib/ui/components/ServiceGroupPanel.tsx | 6 + src/lib/ui/components/ServicePanel.tsx | 27 ++ src/lib/ui/components/ServiceRow.tsx | 50 +++ src/lib/ui/components/SignalTrace.tsx | 26 ++ src/lib/ui/components/Sparkline.tsx | 37 ++ src/lib/ui/components/StatusBadge.tsx | 14 + src/lib/ui/components/StatusStrip.tsx | 18 + src/lib/ui/components/SystemState.tsx | 26 ++ src/lib/ui/components/TelemetryCard.tsx | 51 +++ src/lib/ui/components/TelemetryGrid.tsx | 16 + src/lib/ui/components/TelemetryStrip.tsx | 6 + src/lib/ui/components/WeatherModule.tsx | 6 + src/lib/ui/components/render.test.ts | 153 ------- src/lib/ui/components/render.test.tsx | 147 +++++++ src/lib/ui/components/styles.css | 399 ++++++++++++++++++ src/lib/ui/index.ts | 58 +-- 33 files changed, 1304 insertions(+), 182 deletions(-) create mode 100644 src/lib/ui/components/Badge.tsx create mode 100644 src/lib/ui/components/Button.tsx create mode 100644 src/lib/ui/components/CornerBracketFrame.tsx create mode 100644 src/lib/ui/components/DashboardFrame.tsx create mode 100644 src/lib/ui/components/DashboardHeader.tsx create mode 100644 src/lib/ui/components/DiagonalStripeField.tsx create mode 100644 src/lib/ui/components/FooterCell.tsx create mode 100644 src/lib/ui/components/FooterStatusCell.tsx create mode 100644 src/lib/ui/components/GridFrame.tsx create mode 100644 src/lib/ui/components/IconButton.tsx create mode 100644 src/lib/ui/components/IconGlyph.tsx create mode 100644 src/lib/ui/components/LineChart.tsx create mode 100644 src/lib/ui/components/ModuleCard.tsx create mode 100644 src/lib/ui/components/Panel.tsx create mode 100644 src/lib/ui/components/ProgressMeter.tsx create mode 100644 src/lib/ui/components/ScanlineField.tsx create mode 100644 src/lib/ui/components/Separator.tsx create mode 100644 src/lib/ui/components/ServiceGroupPanel.tsx create mode 100644 src/lib/ui/components/ServicePanel.tsx create mode 100644 src/lib/ui/components/ServiceRow.tsx create mode 100644 src/lib/ui/components/SignalTrace.tsx create mode 100644 src/lib/ui/components/Sparkline.tsx create mode 100644 src/lib/ui/components/StatusBadge.tsx create mode 100644 src/lib/ui/components/StatusStrip.tsx create mode 100644 src/lib/ui/components/SystemState.tsx create mode 100644 src/lib/ui/components/TelemetryCard.tsx create mode 100644 src/lib/ui/components/TelemetryGrid.tsx create mode 100644 src/lib/ui/components/TelemetryStrip.tsx create mode 100644 src/lib/ui/components/WeatherModule.tsx delete mode 100644 src/lib/ui/components/render.test.ts create mode 100644 src/lib/ui/components/render.test.tsx create mode 100644 src/lib/ui/components/styles.css diff --git a/src/lib/ui/components/Badge.tsx b/src/lib/ui/components/Badge.tsx new file mode 100644 index 0000000..a21ade8 --- /dev/null +++ b/src/lib/ui/components/Badge.tsx @@ -0,0 +1,11 @@ +import type { UiSeverity } from "../types"; +import { StatusBadge } from "./StatusBadge"; + +export interface BadgeProps { + label: string; + severity?: UiSeverity; +} + +export function Badge({ label, severity = "neutral" }: BadgeProps) { + return ; +} diff --git a/src/lib/ui/components/Button.tsx b/src/lib/ui/components/Button.tsx new file mode 100644 index 0000000..8ae3b4e --- /dev/null +++ b/src/lib/ui/components/Button.tsx @@ -0,0 +1,39 @@ +import type { ButtonHTMLAttributes } from "react"; +import { IconGlyph } from "./IconGlyph"; + +export interface ButtonProps + extends Omit, "type"> { + label: string; + variant?: "primary" | "secondary" | "danger" | "ghost"; + size?: "default" | "compact"; + icon?: string; + loading?: boolean; + type?: "button" | "submit" | "reset"; +} + +export function Button({ + label, + variant = "primary", + size = "default", + icon, + disabled = false, + loading = false, + type = "button", + className = "", + ...buttonProps +}: ButtonProps) { + return ( + + ); +} diff --git a/src/lib/ui/components/CornerBracketFrame.tsx b/src/lib/ui/components/CornerBracketFrame.tsx new file mode 100644 index 0000000..1dfd4f7 --- /dev/null +++ b/src/lib/ui/components/CornerBracketFrame.tsx @@ -0,0 +1,26 @@ +export interface CornerBracketFrameProps { + density?: "regular" | "tight"; + size?: "sm" | "md" | "lg"; + tone?: "neutral" | "accent" | "danger"; +} + +export function CornerBracketFrame({ + density = "regular", + size = "md", + tone = "neutral", +}: CornerBracketFrameProps) { + return ( + + ); +} diff --git a/src/lib/ui/components/DashboardFrame.tsx b/src/lib/ui/components/DashboardFrame.tsx new file mode 100644 index 0000000..f07e452 --- /dev/null +++ b/src/lib/ui/components/DashboardFrame.tsx @@ -0,0 +1,49 @@ +import { useId } from "react"; +import type { UiDashboardPreview } from "../types"; +import { ModuleCard } from "./ModuleCard"; +import { ServicePanel } from "./ServicePanel"; +import { StatusStrip } from "./StatusStrip"; +import { TelemetryGrid } from "./TelemetryGrid"; +import "./styles.css"; + +export interface DashboardFrameProps { + dashboard: UiDashboardPreview; + titleId?: string; +} + +export function DashboardFrame({ dashboard, titleId }: DashboardFrameProps) { + const generatedTitleId = useId(); + const resolvedTitleId = titleId || `${generatedTitleId}-title`; + + return ( +
+
+
+ {dashboard.eyebrow ?

{dashboard.eyebrow}

: null} +

{dashboard.title}

+ {dashboard.subtitle ? {dashboard.subtitle} : null} +
+ {dashboard.modules.length ? ( +
+ {dashboard.modules.map((module) => ( + + ))} +
+ ) : null} +
+ + + +
+ {dashboard.serviceGroups.map((group) => ( + + ))} +
+ + +
+ ); +} diff --git a/src/lib/ui/components/DashboardHeader.tsx b/src/lib/ui/components/DashboardHeader.tsx new file mode 100644 index 0000000..68b4e11 --- /dev/null +++ b/src/lib/ui/components/DashboardHeader.tsx @@ -0,0 +1,30 @@ +import { useId } from "react"; +import type { UiModuleBlock } from "../types"; +import { ModuleCard } from "./ModuleCard"; + +export interface DashboardHeaderProps { + title: string; + subtitle?: string; + eyebrow?: string; + module?: UiModuleBlock; +} + +export function DashboardHeader({ + title, + subtitle, + eyebrow, + module, +}: DashboardHeaderProps) { + const titleId = useId(); + + return ( +
+
+ {eyebrow ?

{eyebrow}

: null} +

{title}

+ {subtitle ? {subtitle} : null} +
+ {module ? : null} +
+ ); +} diff --git a/src/lib/ui/components/DiagonalStripeField.tsx b/src/lib/ui/components/DiagonalStripeField.tsx new file mode 100644 index 0000000..b4c60f8 --- /dev/null +++ b/src/lib/ui/components/DiagonalStripeField.tsx @@ -0,0 +1,24 @@ +export interface DiagonalStripeFieldProps { + density?: "open" | "regular" | "tight"; + direction?: "forward" | "backward"; + size?: "sm" | "md" | "lg"; + tone?: "neutral" | "accent" | "warning" | "danger"; +} + +export function DiagonalStripeField({ + density = "regular", + direction = "forward", + size = "md", + tone = "accent", +}: DiagonalStripeFieldProps) { + return ( +