diff --git a/app/config/page.tsx b/app/config/page.tsx new file mode 100644 index 0000000..ad17d83 --- /dev/null +++ b/app/config/page.tsx @@ -0,0 +1,100 @@ +"use client"; + +import { Settings, Cpu, Globe, Wrench, Radio } from "lucide-react"; +import { useConfig, useHealth } from "@/lib/hooks"; + +export default function ConfigPage() { + const { data: config, isLoading } = useConfig(); + const { data: health } = useHealth(); + + const online = health?.status === "ok"; + const cfg = config?.config || {}; + + const terminal = cfg.terminal as Record | undefined; + const memory = cfg.memory as Record | undefined; + const display = cfg.display as Record | undefined; + const tts = cfg.tts as Record | undefined; + + return ( +
+
+ +

Config

+
+
+ {online ? "Connected" : "Offline"} +
+
+ + {isLoading ? ( +
+ {[...Array(4)].map((_, i) => ( +
+
+
+
+ ))} +
+ ) : ( +
+ + + + + + + +
+ )} +
+ ); +} + +function ConfigSection({ + icon: Icon, + label, + color, + items, +}: { + icon: typeof Cpu; + label: string; + color: string; + items: [string, string][]; +}) { + return ( +
+
+ + {label} +
+
+ {items.map(([k, v]) => ( +
+ {k} + {v} +
+ ))} +
+
+ ); +} diff --git a/app/layout.tsx b/app/layout.tsx index c2f9b07..9b55c90 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -2,6 +2,7 @@ import type { Metadata, Viewport } from "next"; import "./globals.css"; import { BottomNav } from "@/components/BottomNav"; import { Providers } from "@/components/Providers"; +import { ErrorBoundary } from "@/components/ErrorBoundary"; export const metadata: Metadata = { title: "Hermes OS", @@ -27,7 +28,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
- {children} + {children}
diff --git a/app/page.tsx b/app/page.tsx index e2f2720..b7d81a4 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,7 +1,7 @@ "use client"; import Link from "next/link"; -import { Puzzle, Timer, Sparkles } from "lucide-react"; +import { Puzzle, Timer, Sparkles, Settings } from "lucide-react"; import { StatusCard } from "@/components/StatusCard"; import { StatsCard } from "@/components/StatsCard"; import { SessionCard } from "@/components/SessionCard"; @@ -12,6 +12,7 @@ const QUICK_LINKS = [ { href: "/skills", label: "Skills", icon: Puzzle, color: "#a78bfa" }, { href: "/cron", label: "Cron", icon: Timer, color: "#f59e0b" }, { href: "/soul", label: "Soul", icon: Sparkles, color: "#ec4899" }, + { href: "/config", label: "Config", icon: Settings, color: "#6b7280" }, ] as const; export default function PulsePage() { @@ -27,7 +28,7 @@ export default function PulsePage() { -
+
{QUICK_LINKS.map(({ href, label, icon: Icon, color }) => (
diff --git a/components/ErrorBoundary.tsx b/components/ErrorBoundary.tsx new file mode 100644 index 0000000..3c8901b --- /dev/null +++ b/components/ErrorBoundary.tsx @@ -0,0 +1,36 @@ +"use client"; + +import { Component, type ReactNode } from "react"; +import { AlertTriangle, RefreshCw } from "lucide-react"; + +interface Props { children: ReactNode; fallbackLabel?: string } +interface State { error: Error | null } + +export class ErrorBoundary extends Component { + state: State = { error: null }; + + static getDerivedStateFromError(error: Error) { + return { error }; + } + + render() { + if (this.state.error) { + return ( +
+ +

+ {this.props.fallbackLabel || "Something went wrong"} +

+

{this.state.error.message}

+ +
+ ); + } + return this.props.children; + } +}