import { useMemo, useState } from "react"; import { useParams } from "react-router-dom"; import { Boxes, Settings2 } from "lucide-react"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Button } from "@/components/ui/button"; import { Skeleton } from "@/components/ui/skeleton"; import { useDashboardBySlug } from "../hooks/useDashboards"; import { useWidgetReferences } from "../hooks/useWidgets"; import { PinnedServiceLink } from "../components/PinnedServiceLink"; import { WidgetInstanceCard } from "../components/WidgetInstance"; import { WidgetConfigDialog } from "../components/WidgetConfigDialog"; /** * Payload model for named dashboards (design choice: inline items, not widget * instance ids). The payload stores an ordered list of items: * * ``` * { items: DashboardItem[] } * ``` * * Where `DashboardItem` is either a pinned service link (this slice) or a * future widget reference (follow-up). Widget composition on named dashboards * is deferred — the main Dashboard already has the rich widget config dialog. */ interface LinkItem { type: "link"; label: string; target: string; } type DashboardItem = LinkItem; function parseItems(payload: Record): DashboardItem[] { const items = payload.items; if (!Array.isArray(items)) return []; return items.filter( (item): item is LinkItem => typeof item === "object" && item !== null && item.type === "link" && typeof item.label === "string" && typeof item.target === "string", ); } export function NamedDashboardPage() { const { slug = "" } = useParams<{ slug: string }>(); const { data: dashboard, isLoading, isError } = useDashboardBySlug(slug); const dashboardScope = `named:${slug}`; const { data: widgetRefs = [] } = useWidgetReferences(dashboardScope); const [configOpen, setConfigOpen] = useState(false); const [editWidgetId, setEditWidgetId] = useState(); const items = useMemo( () => parseItems(dashboard?.payload ?? {}), [dashboard?.payload], ); const visibleWidgets = useMemo( () => widgetRefs .filter((r) => r.widget.enabled) .map((r) => r.widget) .sort((a, b) => a.sort_order - b.sort_order), [widgetRefs], ); if (isLoading) { return ; } if (isError || !dashboard) { return ( Dashboard not found. It may have been deleted or the link is invalid. ); } return (

{dashboard.label}

{visibleWidgets.length > 0 ? (
{visibleWidgets.map((widget) => ( { setEditWidgetId(id); setConfigOpen(true); }} /> ))}
) : null} {items.length === 0 && visibleWidgets.length === 0 ? ( This dashboard is empty. Add widgets via "Edit widgets" or pinned service links from the dashboard management panel on the Services page. ) : items.length > 0 ? (
{items.map((item, index) => ( ))}
) : null} { setConfigOpen(false); setEditWidgetId(undefined); }} dashboardScope={dashboardScope} editWidgetId={editWidgetId} />
); }