ui: add protected dashboard CRUD screens and assignment docs

This commit is contained in:
Syahdan 2026-04-29 18:10:31 +07:00
parent bf42037619
commit cb0d4d7809
24 changed files with 2662 additions and 101 deletions

View file

@ -0,0 +1,123 @@
import { Button } from "@minmon/ui/components/button";
import { cn } from "@minmon/ui/lib/utils";
import { Link, Outlet, useRouterState } from "@tanstack/react-router";
import { Activity, BookText, Globe, LayoutGrid, Server, Wrench } from "lucide-react";
const dashboardLinks = [
{
to: "/dashboard",
label: "Overview",
description: "Health, alerts, and recent checks",
icon: LayoutGrid,
},
{
to: "/dashboard/servers",
label: "Servers",
description: "Inventory and host details",
icon: Server,
},
{
to: "/dashboard/services",
label: "Services",
description: "Processes and uptime targets",
icon: Wrench,
},
{
to: "/dashboard/domains",
label: "Domains",
description: "DNS, SSL, and expiry status",
icon: Globe,
},
{
to: "/dashboard/notes",
label: "Notes",
description: "Runbooks and operational handover",
icon: BookText,
},
] as const;
export function DashboardShell({
userName,
userEmail,
}: {
userName?: string | null;
userEmail?: string | null;
}) {
const pathname = useRouterState({
select: (state) => state.location.pathname,
});
return (
<div className="min-h-full bg-muted/20">
<div className="mx-auto flex w-full max-w-7xl flex-col gap-6 px-4 py-6 lg:px-6">
<section className="border bg-card">
<div className="grid gap-4 px-5 py-5 lg:grid-cols-[1.4fr_0.6fr] lg:px-6">
<div className="space-y-2">
<p className="text-[11px] uppercase tracking-[0.24em] text-muted-foreground">
Server Management Dashboard
</p>
<h1 className="text-2xl font-semibold tracking-tight">Operations overview for demos and day-to-day admin tasks</h1>
<p className="max-w-2xl text-sm text-muted-foreground">
Use this area to review monitored assets, inspect detail states, and stage CRUD forms that can connect to tRPC routers as they are added.
</p>
</div>
<div className="grid gap-3 border-l-0 border-t pt-4 lg:border-l lg:border-t-0 lg:pl-6 lg:pt-0">
<div>
<p className="text-[11px] uppercase tracking-[0.16em] text-muted-foreground">Signed in as</p>
<p className="mt-1 text-sm font-medium">{userName ?? "Administrator"}</p>
<p className="text-xs text-muted-foreground">{userEmail ?? "admin@minmon.local"}</p>
</div>
<div className="flex flex-wrap gap-2">
<Link to="/dashboard/servers/new">
<Button size="sm">Add server</Button>
</Link>
<Link to="/dashboard/notes/new">
<Button size="sm" variant="outline">
New note
</Button>
</Link>
</div>
</div>
</div>
</section>
<div className="grid gap-6 lg:grid-cols-[240px_minmax(0,1fr)]">
<aside className="h-fit border bg-card">
<div className="border-b px-4 py-3">
<div className="flex items-center gap-2 text-sm font-medium">
<Activity className="size-4" />
Navigation
</div>
</div>
<nav className="grid gap-1 p-2">
{dashboardLinks.map(({ to, label, description, icon: Icon }) => {
const active = pathname === to || pathname.startsWith(`${to}/`);
return (
<Link
key={to}
to={to}
className={cn(
"grid gap-0.5 border px-3 py-2 text-left transition-colors",
active ? "border-foreground/20 bg-muted" : "border-transparent hover:border-border hover:bg-muted/40",
)}
>
<span className="flex items-center gap-2 text-sm font-medium">
<Icon className="size-4" />
{label}
</span>
<span className="text-xs text-muted-foreground">{description}</span>
</Link>
);
})}
</nav>
</aside>
<main className="min-w-0">
<Outlet />
</main>
</div>
</div>
</div>
);
}