/** * Hook for managing widget layouts */ import { useCallback, useState, useEffect } from "react"; import type { WidgetPlacement, LayoutConfig } from "@mosaic/shared"; const STORAGE_KEY = "mosaic-layout"; const DEFAULT_LAYOUT_NAME = "default"; /** * Local storage key for user's workspace preference */ const WORKSPACE_KEY = "mosaic-workspace-id"; /** * Hook to manage widget layout state */ export function useLayout() { const [layouts, setLayouts] = useState>({}); const [currentLayoutId, setCurrentLayoutId] = useState(DEFAULT_LAYOUT_NAME); const [isLoading, setIsLoading] = useState(true); // Load layouts from localStorage on mount useEffect(() => { try { const stored = localStorage.getItem(STORAGE_KEY); if (stored) { const parsed = JSON.parse(stored); setLayouts(parsed); } // Load current layout ID preference const storedLayoutId = localStorage.getItem(`${STORAGE_KEY}-current`); if (storedLayoutId) { setCurrentLayoutId(storedLayoutId); } } catch (error) { console.error("Failed to load layouts from localStorage:", error); } finally { setIsLoading(false); } }, []); // Save layouts to localStorage whenever they change useEffect(() => { if (!isLoading) { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(layouts)); localStorage.setItem(`${STORAGE_KEY}-current`, currentLayoutId); } catch (error) { console.error("Failed to save layouts to localStorage:", error); } } }, [layouts, currentLayoutId, isLoading]); const currentLayout = layouts[currentLayoutId]; const updateLayout = useCallback( (layoutItems: WidgetPlacement[]) => { setLayouts((prev) => ({ ...prev, [currentLayoutId]: { id: currentLayoutId, name: prev[currentLayoutId]?.name || "My Layout", layout: layoutItems, }, })); }, [currentLayoutId] ); const addWidget = useCallback( (widget: WidgetPlacement) => { setLayouts((prev) => { const currentLayoutData = prev[currentLayoutId]; if (!currentLayoutData) { return prev; } return { ...prev, [currentLayoutId]: { ...currentLayoutData, layout: [...currentLayoutData.layout, widget], }, }; }); }, [currentLayoutId] ); const removeWidget = useCallback( (widgetId: string) => { setLayouts((prev) => { const currentLayoutData = prev[currentLayoutId]; if (!currentLayoutData) { return prev; } return { ...prev, [currentLayoutId]: { ...currentLayoutData, layout: currentLayoutData.layout.filter((w) => w.i !== widgetId), }, }; }); }, [currentLayoutId] ); const updateWidget = useCallback( (widgetId: string, updates: Partial) => { setLayouts((prev) => { const currentLayoutData = prev[currentLayoutId]; if (!currentLayoutData) { return prev; } return { ...prev, [currentLayoutId]: { ...currentLayoutData, layout: currentLayoutData.layout.map((w) => w.i === widgetId ? { ...w, ...updates } : w ), }, }; }); }, [currentLayoutId] ); const createLayout = useCallback( (name: string) => { const id = `layout-${Date.now()}`; setLayouts((prev) => ({ ...prev, [id]: { id, name, layout: [], }, })); setCurrentLayoutId(id); return id; }, [] ); const deleteLayout = useCallback( (layoutId: string) => { setLayouts((prev) => { const { [layoutId]: deleted, ...rest } = prev; // If we deleted the current layout, switch to default if (layoutId === currentLayoutId) { const remainingIds = Object.keys(rest); setCurrentLayoutId(remainingIds[0] || DEFAULT_LAYOUT_NAME); } return rest; }); }, [currentLayoutId] ); const renameLayout = useCallback((layoutId: string, name: string) => { setLayouts((prev) => { const existing = prev[layoutId]; if (!existing) return prev; return { ...prev, [layoutId]: { ...existing, name, }, }; }); }, []); const resetLayout = useCallback(() => { setLayouts({ [DEFAULT_LAYOUT_NAME]: { id: DEFAULT_LAYOUT_NAME, name: "Default Layout", layout: [], }, }); setCurrentLayoutId(DEFAULT_LAYOUT_NAME); }, []); return { layouts, currentLayout, currentLayoutId, isLoading, updateLayout, addWidget, removeWidget, updateWidget, createLayout, deleteLayout, renameLayout, switchLayout: setCurrentLayoutId, resetLayout, }; } /** * Hook to get the current workspace ID */ export function useWorkspaceId(): string | null { const [workspaceId, setWorkspaceId] = useState(null); useEffect(() => { try { const stored = localStorage.getItem(WORKSPACE_KEY); if (stored) { setWorkspaceId(stored); } } catch (error) { console.error("Failed to load workspace ID from localStorage:", error); } }, []); return workspaceId; }