import { create } from 'zustand'; import { persist, createJSONStorage } from 'zustand/middleware'; export type Theme = 'light' | 'dark' | 'auto'; export type Language = 'es' | 'en' | 'eu'; export type ViewMode = 'list' | 'grid' | 'card'; export type SidebarState = 'expanded' | 'collapsed' | 'hidden'; // Toast interface kept for backward compatibility but toast functionality // has been moved to src/utils/toast.ts using react-hot-toast // This interface is deprecated and will be removed in a future version 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; // 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; 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: 'dark', language: 'es', sidebarState: 'expanded', compactMode: false, reducedMotion: false, currentPage: '', breadcrumbs: [], viewMode: 'list', globalLoading: false, loadingStates: {}, 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 }); // Trigger i18n language change only if different import('../i18n').then(({ default: i18n }) => { if (i18n.language !== language) { i18n.changeLanguage(language); } }); }, 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; }, // 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 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, 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 || 'dark'); } catch (error) { console.warn('Failed to parse stored UI state:', error); useUIStore.getState().setTheme('dark'); } } else { useUIStore.getState().setTheme('dark'); } // 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); }); }