Spaces:
Paused
Paused
| import { useState, useEffect, useCallback } from 'react'; | |
| import { saveSnapshot, getSnapshots, deleteSnapshot } from '@/lib/indexedDB'; | |
| import { DashboardWidget } from '@/pages/Dashboard'; | |
| import { toast } from '@/hooks/use-toast'; | |
| export interface DashboardSnapshot { | |
| id: string; | |
| dashboardId: string; | |
| name: string; | |
| data: { | |
| widgets: DashboardWidget[]; | |
| filters?: any; | |
| timestamp: number; | |
| }; | |
| createdAt: number; | |
| } | |
| export function useDashboardSnapshots(dashboardId: string = 'default') { | |
| const [snapshots, setSnapshots] = useState<DashboardSnapshot[]>([]); | |
| const [isLoading, setIsLoading] = useState(true); | |
| const loadSnapshots = useCallback(async () => { | |
| try { | |
| const loaded = await getSnapshots(dashboardId); | |
| setSnapshots(loaded.sort((a, b) => b.createdAt - a.createdAt)); | |
| } catch (e) { | |
| console.error('Failed to load snapshots:', e); | |
| } finally { | |
| setIsLoading(false); | |
| } | |
| }, [dashboardId]); | |
| useEffect(() => { | |
| loadSnapshots(); | |
| }, [loadSnapshots]); | |
| const createSnapshot = useCallback(async ( | |
| name: string, | |
| widgets: DashboardWidget[], | |
| filters?: any | |
| ): Promise<DashboardSnapshot> => { | |
| const snapshot: DashboardSnapshot = { | |
| id: `snapshot-${Date.now()}`, | |
| dashboardId, | |
| name, | |
| data: { | |
| widgets: widgets.map(w => ({ ...w })), | |
| filters, | |
| timestamp: Date.now(), | |
| }, | |
| createdAt: Date.now(), | |
| }; | |
| await saveSnapshot(snapshot); | |
| setSnapshots(prev => [snapshot, ...prev]); | |
| toast({ | |
| title: "Snapshot Created", | |
| description: `"${name}" has been saved`, | |
| }); | |
| return snapshot; | |
| }, [dashboardId]); | |
| const removeSnapshot = useCallback(async (snapshotId: string) => { | |
| await deleteSnapshot(snapshotId); | |
| setSnapshots(prev => prev.filter(s => s.id !== snapshotId)); | |
| toast({ | |
| title: "Snapshot Deleted", | |
| description: "Snapshot has been removed", | |
| }); | |
| }, []); | |
| const restoreSnapshot = useCallback((snapshotId: string): DashboardWidget[] | null => { | |
| const snapshot = snapshots.find(s => s.id === snapshotId); | |
| if (!snapshot) return null; | |
| toast({ | |
| title: "Snapshot Restored", | |
| description: `Restored "${snapshot.name}"`, | |
| }); | |
| return snapshot.data.widgets.map(w => ({ | |
| ...w, | |
| id: `widget-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, | |
| })); | |
| }, [snapshots]); | |
| const compareSnapshots = useCallback((id1: string, id2: string) => { | |
| const snap1 = snapshots.find(s => s.id === id1); | |
| const snap2 = snapshots.find(s => s.id === id2); | |
| if (!snap1 || !snap2) return null; | |
| const widgets1 = new Set(snap1.data.widgets.map(w => w.type)); | |
| const widgets2 = new Set(snap2.data.widgets.map(w => w.type)); | |
| const added = [...widgets2].filter(w => !widgets1.has(w)); | |
| const removed = [...widgets1].filter(w => !widgets2.has(w)); | |
| const unchanged = [...widgets1].filter(w => widgets2.has(w)); | |
| return { | |
| added, | |
| removed, | |
| unchanged, | |
| countDiff: snap2.data.widgets.length - snap1.data.widgets.length, | |
| }; | |
| }, [snapshots]); | |
| return { | |
| snapshots, | |
| isLoading, | |
| createSnapshot, | |
| removeSnapshot, | |
| restoreSnapshot, | |
| compareSnapshots, | |
| refreshSnapshots: loadSnapshots, | |
| }; | |
| } | |