"use client"; import type { DashboardWidgetConfig, DashboardWidgetType } from "@capakraken/shared/types"; import dynamic from "next/dynamic"; import { Suspense, useState, useRef, useEffect } from "react"; import { useDashboardLayout } from "~/hooks/useDashboardLayout.js"; import { WidgetContainer } from "./WidgetContainer.js"; import { AddWidgetModal } from "./AddWidgetModal.js"; import { getWidget } from "./widget-registry.js"; // Import CSS for react-grid-layout import "react-grid-layout/css/styles.css"; import "react-resizable/css/styles.css"; function WidgetFallback() { return (
); } // Dynamic import — no WidthProvider (uses findDOMNode, broken in React 18 strict mode). // We measure container width ourselves via ResizeObserver and pass it as a prop. const GridLayout = dynamic(() => import("react-grid-layout").then((m) => m.Responsive), { ssr: false, }); function renderWidget( type: DashboardWidgetType, config: DashboardWidgetConfig, onConfigChange: (u: Record) => void, ) { const widget = getWidget(type); const Component = widget.component; return ( }> } onConfigChange={onConfigChange} /> ); } export function DashboardClient() { const [addModalOpen, setAddModalOpen] = useState(false); const { config, addWidget, removeWidget, updateWidgetConfig, onLayoutChange, resetLayout } = useDashboardLayout(); // Measure grid container width so Responsive knows the column size. // We can't use WidthProvider (uses findDOMNode, deprecated in React 18). const containerRef = useRef(null); const [gridWidth, setGridWidth] = useState(1200); useEffect(() => { const el = containerRef.current; if (!el) return; const ro = new ResizeObserver(([entry]) => { if (entry) setGridWidth(entry.contentRect.width); }); ro.observe(el); setGridWidth(el.getBoundingClientRect().width); return () => ro.disconnect(); }, []); const layouts = { lg: config.widgets.map((w) => ({ i: w.id, x: w.x, y: w.y, w: w.w, h: w.h, minW: w.minW ?? 2, minH: w.minH ?? 2, })), }; return (

Dashboard

Drag widgets to rearrange them and resize from the corners.

{config.widgets.length === 0 ? (

No widgets on this dashboard yet.

Start with a widget and build a view that matches how your team actually plans and monitors work.

) : (
{(() => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const AnyGridLayout = GridLayout as any; return ( , ) => onLayoutChange(allLayouts["lg"] ?? [])} draggableHandle=".widget-drag-handle" margin={[12, 12]} > {config.widgets.map((widget) => (
updateWidgetConfig(widget.id, { showDetails: widget.config.showDetails !== true, }) } onRemove={() => removeWidget(widget.id)} > {renderWidget(widget.type, widget.config, (update) => updateWidgetConfig(widget.id, update), )}
))}
); })()}
)} {addModalOpen && setAddModalOpen(false)} />}
); }