diff --git a/src/app/hooks/__tests__/useDashboardWidgets.test.tsx b/src/app/hooks/__tests__/useDashboardWidgets.test.tsx index 6ed8a2aa..4b52e333 100644 --- a/src/app/hooks/__tests__/useDashboardWidgets.test.tsx +++ b/src/app/hooks/__tests__/useDashboardWidgets.test.tsx @@ -1,6 +1,6 @@ // @vitest-environment jsdom import React, { useEffect } from 'react'; -import { describe, it, expect, beforeEach } from 'vitest'; +import { describe, it, expect, beforeEach, vi } from 'vitest'; import { createRoot } from 'react-dom/client'; import { act } from 'react-dom/test-utils'; import { useDashboardWidgets } from '../useDashboardWidgets'; @@ -38,6 +38,7 @@ describe('useDashboardWidgets', () => { let root: ReturnType; beforeEach(() => { + vi.useFakeTimers(); // @ts-expect-error - LocalStorageMock for testing global.localStorage = new LocalStorageMock(); container = document.createElement('div'); @@ -59,6 +60,12 @@ describe('useDashboardWidgets', () => { // After first mount, default widgets should be set and saved expect(api.widgets.length).toBeGreaterThan(0); + + // Advance past the 500ms debounce in saveWidgetLayout + await act(async () => { + vi.advanceTimersByTime(600); + }); + const saved = JSON.parse(localStorage.getItem('dashboard-widgets') || '[]'); expect(saved.length).toBe(api.widgets.length); }); diff --git a/src/app/hooks/useDashboardWidgets.tsx b/src/app/hooks/useDashboardWidgets.tsx index d8173d90..cef32438 100644 --- a/src/app/hooks/useDashboardWidgets.tsx +++ b/src/app/hooks/useDashboardWidgets.tsx @@ -12,6 +12,77 @@ interface Widget { settings: Record; } +// Default widgets matching Figma design — single source of truth +const DEFAULT_WIDGETS: Widget[] = [ + // 4 Summary Stat Cards (small size) + { + id: 'stat-revenue', + type: 'progress-summary', + title: 'Total Revenue', + size: 'small', + position: 0, + isCollapsed: false, + settings: { statType: 'revenue' }, + }, + { + id: 'stat-students', + type: 'progress-summary', + title: 'Students', + size: 'small', + position: 1, + isCollapsed: false, + settings: { statType: 'students' }, + }, + { + id: 'stat-views', + type: 'progress-summary', + title: 'Course Views', + size: 'small', + position: 2, + isCollapsed: false, + settings: { statType: 'views' }, + }, + { + id: 'stat-courses', + type: 'progress-summary', + title: 'Active Courses', + size: 'small', + position: 3, + isCollapsed: false, + settings: { statType: 'courses' }, + }, + // Recent Sales (medium, left column) + { + id: 'recent-sales', + type: 'recent-sales', + title: 'Recent Sales', + size: 'medium', + position: 4, + isCollapsed: false, + settings: {}, + }, + // Recent Activity (medium, right column) + { + id: 'recent-activity', + type: 'recent-activity', + title: 'Recent Activity', + size: 'medium', + position: 5, + isCollapsed: false, + settings: {}, + }, + // Upcoming Schedule (large, full width) + { + id: 'upcoming-schedule', + type: 'upcoming-deadlines', + title: 'Upcoming Schedule', + size: 'large', + position: 6, + isCollapsed: false, + settings: {}, + }, +]; + export const useDashboardWidgets = () => { const [widgets, setWidgets] = useState([]); const [isLoading, setIsLoading] = useState(true); @@ -70,78 +141,8 @@ export const useDashboardWidgets = () => { if (savedWidgets.length > 0) { setWidgets(savedWidgets); } else { - // Default widgets matching Figma design - const defaultWidgets: Widget[] = [ - // 4 Summary Stat Cards (small size) - { - id: 'stat-revenue', - type: 'progress-summary', - title: 'Total Revenue', - size: 'small', - position: 0, - isCollapsed: false, - settings: { statType: 'revenue' }, - }, - { - id: 'stat-students', - type: 'progress-summary', - title: 'Students', - size: 'small', - position: 1, - isCollapsed: false, - settings: { statType: 'students' }, - }, - { - id: 'stat-views', - type: 'progress-summary', - title: 'Course Views', - size: 'small', - position: 2, - isCollapsed: false, - settings: { statType: 'views' }, - }, - { - id: 'stat-courses', - type: 'progress-summary', - title: 'Active Courses', - size: 'small', - position: 3, - isCollapsed: false, - settings: { statType: 'courses' }, - }, - // Recent Sales (medium, left column) - { - id: 'recent-sales', - type: 'recent-sales', - title: 'Recent Sales', - size: 'medium', - position: 4, - isCollapsed: false, - settings: {}, - }, - // Recent Activity (medium, right column) - { - id: 'recent-activity', - type: 'recent-activity', - title: 'Recent Activity', - size: 'medium', - position: 5, - isCollapsed: false, - settings: {}, - }, - // Upcoming Schedule (large, full width) - { - id: 'upcoming-schedule', - type: 'upcoming-deadlines', - title: 'Upcoming Schedule', - size: 'large', - position: 6, - isCollapsed: false, - settings: {}, - }, - ]; - setWidgets(defaultWidgets); - saveWidgetLayout(defaultWidgets); + setWidgets(DEFAULT_WIDGETS); + saveWidgetLayout(DEFAULT_WIDGETS); } setIsLoading(false); }, [loadWidgetLayout, saveWidgetLayout]); @@ -231,77 +232,8 @@ export const useDashboardWidgets = () => { // Reset to default layout const resetToDefault = useCallback(() => { - const defaultWidgets: Widget[] = [ - // 4 Summary Stat Cards (small size) - { - id: 'stat-revenue', - type: 'progress-summary', - title: 'Total Revenue', - size: 'small', - position: 0, - isCollapsed: false, - settings: { statType: 'revenue' }, - }, - { - id: 'stat-students', - type: 'progress-summary', - title: 'Students', - size: 'small', - position: 1, - isCollapsed: false, - settings: { statType: 'students' }, - }, - { - id: 'stat-views', - type: 'progress-summary', - title: 'Course Views', - size: 'small', - position: 2, - isCollapsed: false, - settings: { statType: 'views' }, - }, - { - id: 'stat-courses', - type: 'progress-summary', - title: 'Active Courses', - size: 'small', - position: 3, - isCollapsed: false, - settings: { statType: 'courses' }, - }, - // Recent Sales (medium, left column) - { - id: 'recent-sales', - type: 'recent-sales', - title: 'Recent Sales', - size: 'medium', - position: 4, - isCollapsed: false, - settings: {}, - }, - // Recent Activity (medium, right column) - { - id: 'recent-activity', - type: 'recent-activity', - title: 'Recent Activity', - size: 'medium', - position: 5, - isCollapsed: false, - settings: {}, - }, - // Upcoming Schedule (large, full width) - { - id: 'upcoming-schedule', - type: 'upcoming-deadlines', - title: 'Upcoming Schedule', - size: 'large', - position: 6, - isCollapsed: false, - settings: {}, - }, - ]; - setWidgets(defaultWidgets); - saveWidgetLayout(defaultWidgets); + setWidgets(DEFAULT_WIDGETS); + saveWidgetLayout(DEFAULT_WIDGETS); }, [saveWidgetLayout]); // Export widget configuration