'use client'; import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; import { ResponsiveGridLayout, useContainerWidth, verticalCompactor, type ResponsiveLayouts, type Layout } from 'react-grid-layout'; import { RotateCcw } from 'lucide-react'; const STORAGE_KEY = 'dashboard-layouts'; const BREAKPOINTS = { lg: 1024, md: 768, sm: 0 }; const COLS = { lg: 6, md: 4, sm: 1 }; const ROW_HEIGHT = 200; const MARGIN: [number, number] = [16, 16]; const DEFAULT_LAYOUTS: ResponsiveLayouts = { lg: [ { i: 'uptime', x: 0, y: 0, w: 2, h: 1, minW: 2, minH: 1 }, { i: 'weather', x: 2, y: 0, w: 2, h: 1, minW: 2, minH: 1 }, { i: 'globe', x: 4, y: 0, w: 2, h: 2, minW: 2, minH: 2 }, { i: 'newsfeed', x: 0, y: 1, w: 4, h: 3, minW: 2, minH: 2 }, ], md: [ { i: 'uptime', x: 0, y: 0, w: 2, h: 1, minW: 2, minH: 1 }, { i: 'weather', x: 2, y: 0, w: 2, h: 1, minW: 2, minH: 1 }, { i: 'globe', x: 0, y: 1, w: 2, h: 2, minW: 2, minH: 2 }, { i: 'newsfeed', x: 2, y: 1, w: 2, h: 3, minW: 2, minH: 2 }, ], sm: [ { i: 'uptime', x: 0, y: 0, w: 1, h: 1, minW: 1, minH: 1 }, { i: 'weather', x: 0, y: 1, w: 1, h: 1, minW: 1, minH: 1 }, { i: 'globe', x: 0, y: 2, w: 1, h: 2, minW: 1, minH: 2 }, { i: 'newsfeed', x: 0, y: 4, w: 1, h: 3, minW: 1, minH: 2 }, ], }; const DRAG_CONFIG = { handle: '.widget-drag-handle' }; // --- Layout Context --- interface LayoutContextValue { updateWidgetSize: (key: string, w: number, h: number) => void; } const LayoutContext = createContext({ updateWidgetSize: () => {}, }); export function useLayoutContext() { return useContext(LayoutContext); } // --- GridShell --- interface GridShellProps { children: React.ReactNode; } function loadSavedLayouts(): ResponsiveLayouts | null { if (typeof window === 'undefined') return null; try { const raw = localStorage.getItem(STORAGE_KEY); if (raw) return JSON.parse(raw); } catch {} return null; } function persistLayouts(layouts: ResponsiveLayouts) { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(layouts)); } catch {} } export function GridShell({ children }: GridShellProps) { const [layouts, setLayouts] = useState(DEFAULT_LAYOUTS); const [ready, setReady] = useState(false); const { width, containerRef, mounted } = useContainerWidth({ initialWidth: 1280 }); useEffect(() => { const saved = loadSavedLayouts(); if (saved) setLayouts(saved); setReady(true); }, []); const handleLayoutChange = useCallback((_layout: Layout, allLayouts: ResponsiveLayouts) => { setLayouts(allLayouts); persistLayouts(allLayouts); }, []); const handleReset = useCallback(() => { setLayouts(DEFAULT_LAYOUTS); persistLayouts(DEFAULT_LAYOUTS); }, []); const updateWidgetSize = useCallback((key: string, w: number, h: number) => { setLayouts((prev) => { const next: ResponsiveLayouts = {}; for (const bp of Object.keys(prev)) { const bpLayout = prev[bp]; if (!bpLayout) continue; next[bp] = bpLayout.map((item) => item.i === key ? { ...item, w, h } : item ); } persistLayouts(next); return next; }); }, []); const contextValue = useMemo( () => ({ updateWidgetSize }), [updateWidgetSize] ); const childArray = React.Children.toArray(children); return (

Mission Control

SYSTEM ONLINE
}> {mounted && ready && ( {childArray.map((child) => { if (!React.isValidElement(child)) return null; const key = child.key as string; return (
{child}
); })}
)}
); }