import { create } from 'zustand'; import { persist, createJSONStorage } from 'zustand/middleware'; export type Theme = 'light' | 'dark' | 'auto'; export type Language = 'es' | 'en' | 'fr' | 'pt' | 'it'; export type ViewMode = 'list' | 'grid' | 'card'; export type SidebarState = 'expanded' | 'collapsed' | 'hidden'; export interface Toast { id: string; type: 'success' | 'error' | 'warning' | 'info'; title: string; message?: string; duration?: number; action?: { label: string; onClick: () => void; }; persistent?: boolean; } export interface Modal { id: string; type: 'dialog' | 'drawer' | 'fullscreen'; title: string; content: React.ReactNode; size?: 'sm' | 'md' | 'lg' | 'xl'; closeable?: boolean; onClose?: () => void; } export interface UIState { // Theme & Appearance theme: Theme; language: Language; sidebarState: SidebarState; compactMode: boolean; reducedMotion: boolean; // Layout & Navigation currentPage: string; breadcrumbs: Array<{ label: string; path: string }>; viewMode: ViewMode; // Loading States globalLoading: boolean; loadingStates: Record; // Toasts & Notifications toasts: Toast[]; // Modals & Dialogs modals: Modal[]; // User Preferences preferences: { showTips: boolean; autoSave: boolean; confirmActions: boolean; defaultPageSize: number; dateFormat: string; numberFormat: string; timezone: string; }; // Actions setTheme: (theme: Theme) => void; setLanguage: (language: Language) => void; setSidebarState: (state: SidebarState) => void; setCompactMode: (compact: boolean) => void; setReducedMotion: (reduced: boolean) => void; setCurrentPage: (page: string) => void; setBreadcrumbs: (breadcrumbs: Array<{ label: string; path: string }>) => void; setViewMode: (mode: ViewMode) => void; setGlobalLoading: (loading: boolean) => void; setLoading: (key: string, loading: boolean) => void; isLoading: (key: string) => boolean; showToast: (toast: Omit) => string; hideToast: (id: string) => void; clearToasts: () => void; showModal: (modal: Omit) => string; hideModal: (id: string) => void; clearModals: () => void; updatePreference: ( key: K, value: UIState['preferences'][K] ) => void; resetPreferences: () => void; } const defaultPreferences = { showTips: true, autoSave: true, confirmActions: true, defaultPageSize: 25, dateFormat: 'DD/MM/YYYY', numberFormat: 'european', // european, american timezone: 'Europe/Madrid', }; export const useUIStore = create()( persist( (set, get) => ({ // Initial state theme: 'light', language: 'es', sidebarState: 'expanded', compactMode: false, reducedMotion: false, currentPage: '', breadcrumbs: [], viewMode: 'list', globalLoading: false, loadingStates: {}, toasts: [], modals: [], preferences: defaultPreferences, // Theme & Appearance actions setTheme: (theme: Theme) => { set({ theme }); // Apply theme to document const root = document.documentElement; if (theme === 'dark') { root.classList.add('dark'); root.classList.remove('light'); } else if (theme === 'light') { root.classList.add('light'); root.classList.remove('dark'); } else { // Auto theme - check system preference const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; if (prefersDark) { root.classList.add('dark'); root.classList.remove('light'); } else { root.classList.add('light'); root.classList.remove('dark'); } } }, setLanguage: (language: Language) => { set({ language }); // You might want to trigger i18n language change here }, setSidebarState: (sidebarState: SidebarState) => { set({ sidebarState }); }, setCompactMode: (compactMode: boolean) => { set({ compactMode }); }, setReducedMotion: (reducedMotion: boolean) => { set({ reducedMotion }); // Apply reduced motion preference const root = document.documentElement; if (reducedMotion) { root.classList.add('reduce-motion'); } else { root.classList.remove('reduce-motion'); } }, // Navigation actions setCurrentPage: (currentPage: string) => { set({ currentPage }); }, setBreadcrumbs: (breadcrumbs: Array<{ label: string; path: string }>) => { set({ breadcrumbs }); }, setViewMode: (viewMode: ViewMode) => { set({ viewMode }); }, // Loading actions setGlobalLoading: (globalLoading: boolean) => { set({ globalLoading }); }, setLoading: (key: string, loading: boolean) => { set((state) => ({ loadingStates: { ...state.loadingStates, [key]: loading, }, })); }, isLoading: (key: string): boolean => { return get().loadingStates[key] ?? false; }, // Toast actions showToast: (toast: Omit): string => { const id = `toast-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; const newToast: Toast = { ...toast, id, duration: toast.duration ?? (toast.type === 'error' ? 0 : 5000), // Error toasts don't auto-dismiss }; set((state) => ({ toasts: [...state.toasts, newToast], })); // Auto-dismiss toast if duration is set if (newToast.duration && newToast.duration > 0) { setTimeout(() => { get().hideToast(id); }, newToast.duration); } return id; }, hideToast: (id: string) => { set((state) => ({ toasts: state.toasts.filter(toast => toast.id !== id), })); }, clearToasts: () => { set({ toasts: [] }); }, // Modal actions showModal: (modal: Omit): string => { const id = `modal-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; const newModal: Modal = { ...modal, id, size: modal.size ?? 'md', closeable: modal.closeable ?? true, }; set((state) => ({ modals: [...state.modals, newModal], })); return id; }, hideModal: (id: string) => { const { modals } = get(); const modal = modals.find(m => m.id === id); // Call onClose callback if provided if (modal?.onClose) { modal.onClose(); } set((state) => ({ modals: state.modals.filter(modal => modal.id !== id), })); }, clearModals: () => { // Call onClose for all modals const { modals } = get(); modals.forEach(modal => { if (modal.onClose) { modal.onClose(); } }); set({ modals: [] }); }, // Preferences actions updatePreference: ( key: K, value: UIState['preferences'][K] ) => { set((state) => ({ preferences: { ...state.preferences, [key]: value, }, })); }, resetPreferences: () => { set({ preferences: defaultPreferences }); }, }), { name: 'ui-storage', storage: createJSONStorage(() => localStorage), partialize: (state) => ({ theme: state.theme, language: state.language, sidebarState: state.sidebarState, compactMode: state.compactMode, reducedMotion: state.reducedMotion, viewMode: state.viewMode, preferences: state.preferences, }), } ) ); // Selectors for common use cases export const useLanguage = () => useUIStore((state) => state.language); export const useSidebar = () => useUIStore((state) => ({ state: state.sidebarState, setState: state.setSidebarState, })); export const useCompactMode = () => useUIStore((state) => state.compactMode); export const useViewMode = () => useUIStore((state) => state.viewMode); export const useLoading = (key?: string) => { if (key) { return useUIStore((state) => state.isLoading(key)); } return useUIStore((state) => state.globalLoading); }; export const useToasts = () => useUIStore((state) => state.toasts); export const useModals = () => useUIStore((state) => state.modals); export const useBreadcrumbs = () => useUIStore((state) => ({ breadcrumbs: state.breadcrumbs, setBreadcrumbs: state.setBreadcrumbs, })); export const usePreferences = () => useUIStore((state) => state.preferences); // Hook for UI actions export const useUIActions = () => useUIStore((state) => ({ setTheme: state.setTheme, setLanguage: state.setLanguage, setSidebarState: state.setSidebarState, setCompactMode: state.setCompactMode, setReducedMotion: state.setReducedMotion, setCurrentPage: state.setCurrentPage, setBreadcrumbs: state.setBreadcrumbs, setViewMode: state.setViewMode, setGlobalLoading: state.setGlobalLoading, setLoading: state.setLoading, showToast: state.showToast, hideToast: state.hideToast, clearToasts: state.clearToasts, showModal: state.showModal, hideModal: state.hideModal, clearModals: state.clearModals, updatePreference: state.updatePreference, resetPreferences: state.resetPreferences, })); // Initialize theme on store creation if (typeof window !== 'undefined') { // Set initial theme based on stored preference or system preference const storedState = localStorage.getItem('ui-storage'); if (storedState) { try { const { state } = JSON.parse(storedState); useUIStore.getState().setTheme(state.theme || 'auto'); } catch (error) { console.warn('Failed to parse stored UI state:', error); useUIStore.getState().setTheme('auto'); } } else { useUIStore.getState().setTheme('auto'); } // Listen for system theme changes window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => { const { theme } = useUIStore.getState(); if (theme === 'auto') { useUIStore.getState().setTheme('auto'); } }); // Listen for reduced motion preference window.matchMedia('(prefers-reduced-motion: reduce)').addEventListener('change', (e) => { useUIStore.getState().setReducedMotion(e.matches); }); }