/** * React Query hooks for layout management */ import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import type { UseQueryResult, UseMutationResult } from "@tanstack/react-query"; import type { UserLayout, WidgetPlacement } from "@mosaic/shared"; import { apiGet, apiPost, apiPatch, apiDelete } from "@/lib/api/client"; const LAYOUTS_KEY = ["layouts"]; interface CreateLayoutData { name: string; layout: WidgetPlacement[]; isDefault?: boolean; metadata?: Record; } interface UpdateLayoutData { id: string; name?: string; layout?: WidgetPlacement[]; isDefault?: boolean; metadata?: Record; } /** * Fetch all layouts for the current user */ export function useLayouts(): UseQueryResult { return useQuery({ queryKey: LAYOUTS_KEY, queryFn: async (): Promise => { return apiGet("/api/layouts"); }, }); } /** * Fetch a single layout by ID */ export function useLayout(id: string): UseQueryResult { return useQuery({ queryKey: [...LAYOUTS_KEY, id], queryFn: async (): Promise => { return apiGet(`/api/layouts/${id}`); }, enabled: !!id, }); } /** * Fetch the default layout */ export function useDefaultLayout(): UseQueryResult { return useQuery({ queryKey: [...LAYOUTS_KEY, "default"], queryFn: async (): Promise => { return apiGet("/api/layouts/default"); }, }); } /** * Create a new layout (uses API client for CSRF protection) */ export function useCreateLayout(): UseMutationResult { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (data: CreateLayoutData): Promise => { return apiPost("/api/layouts", data); }, onSuccess: (): void => { // Invalidate layouts cache to refetch void queryClient.invalidateQueries({ queryKey: LAYOUTS_KEY }); }, }); } /** * Update an existing layout (uses API client for CSRF protection) */ export function useUpdateLayout(): UseMutationResult { const queryClient = useQueryClient(); return useMutation({ mutationFn: async ({ id, ...data }: UpdateLayoutData): Promise => { return apiPatch(`/api/layouts/${id}`, data); }, onSuccess: (_data, variables): void => { // Invalidate affected queries void queryClient.invalidateQueries({ queryKey: LAYOUTS_KEY }); void queryClient.invalidateQueries({ queryKey: [...LAYOUTS_KEY, variables.id] }); }, }); } /** * Delete a layout (uses API client for CSRF protection) */ export function useDeleteLayout(): UseMutationResult { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (id: string): Promise => { await apiDelete(`/api/layouts/${id}`); }, onSuccess: (): void => { // Invalidate layouts cache to refetch void queryClient.invalidateQueries({ queryKey: LAYOUTS_KEY }); }, }); } interface UseSaveLayoutReturn { saveLayout: (layout: WidgetPlacement[]) => void; isSaving: boolean; error: Error | null; } /** * Helper hook to save layout changes with debouncing */ export function useSaveLayout(layoutId: string): UseSaveLayoutReturn { const updateLayout = useUpdateLayout(); const saveLayout = (layout: WidgetPlacement[]): void => { updateLayout.mutate({ id: layoutId, layout, }); }; return { saveLayout, isSaving: updateLayout.isPending, error: updateLayout.error, }; }