# Zustand Stores Specification **Version:** 1.0.0 **Fecha:** 2025-12-05 --- ## Global Stores ### AuthStore ```typescript interface AuthState { // State user: User | null; token: string | null; isAuthenticated: boolean; isLoading: boolean; // Actions login: (credentials: LoginDto) => Promise; logout: () => void; refreshToken: () => Promise; updateUser: (data: Partial) => void; } export const useAuthStore = create()( devtools( persist( (set, get) => ({ user: null, token: null, isAuthenticated: false, isLoading: false, login: async (credentials) => { set({ isLoading: true }); try { const response = await authApi.login(credentials); set({ user: response.user, token: response.token, isAuthenticated: true, isLoading: false }); } catch (error) { set({ isLoading: false }); throw error; } }, logout: () => { set({ user: null, token: null, isAuthenticated: false }); }, refreshToken: async () => { const response = await authApi.refresh(); set({ token: response.token }); }, updateUser: (data) => { set((state) => ({ user: state.user ? { ...state.user, ...data } : null })); } }), { name: 'auth-store', partialize: (state) => ({ token: state.token, user: state.user }) } ) ) ); ``` ### TenantStore ```typescript interface TenantState { // State currentTenant: Tenant | null; availableTenants: Tenant[]; // Actions setCurrentTenant: (tenant: Tenant) => void; setAvailableTenants: (tenants: Tenant[]) => void; switchTenant: (tenantId: string) => Promise; } export const useTenantStore = create()( devtools( persist( (set, get) => ({ currentTenant: null, availableTenants: [], setCurrentTenant: (tenant) => set({ currentTenant: tenant }), setAvailableTenants: (tenants) => set({ availableTenants: tenants }), switchTenant: async (tenantId) => { const tenant = get().availableTenants.find(t => t.id === tenantId); if (tenant) { set({ currentTenant: tenant }); // Reload data for new tenant window.location.reload(); } } }), { name: 'tenant-store' } ) ) ); ``` ### UIStore ```typescript interface UIState { // Sidebar sidebarCollapsed: boolean; sidebarPinned: boolean; // Theme theme: 'light' | 'dark' | 'system'; primaryColor: string; // Modal state activeModal: string | null; modalData: any; // Actions toggleSidebar: () => void; setSidebarCollapsed: (collapsed: boolean) => void; setSidebarPinned: (pinned: boolean) => void; setTheme: (theme: 'light' | 'dark' | 'system') => void; openModal: (name: string, data?: any) => void; closeModal: () => void; } export const useUIStore = create()( devtools( persist( (set) => ({ sidebarCollapsed: false, sidebarPinned: true, theme: 'system', primaryColor: '#3b82f6', activeModal: null, modalData: null, toggleSidebar: () => set((state) => ({ sidebarCollapsed: !state.sidebarCollapsed })), setSidebarCollapsed: (collapsed) => set({ sidebarCollapsed: collapsed }), setSidebarPinned: (pinned) => set({ sidebarPinned: pinned }), setTheme: (theme) => set({ theme }), openModal: (name, data) => set({ activeModal: name, modalData: data }), closeModal: () => set({ activeModal: null, modalData: null }) }), { name: 'ui-store', partialize: (state) => ({ sidebarCollapsed: state.sidebarCollapsed, sidebarPinned: state.sidebarPinned, theme: state.theme, primaryColor: state.primaryColor }) } ) ) ); ``` ### NotificationStore ```typescript interface Notification { id: string; type: 'info' | 'success' | 'warning' | 'error'; title: string; message?: string; duration?: number; action?: { label: string; onClick: () => void; }; } interface NotificationState { notifications: Notification[]; unreadCount: number; addNotification: (notification: Omit) => void; removeNotification: (id: string) => void; clearAll: () => void; markAllAsRead: () => void; } export const useNotificationStore = create()( devtools((set) => ({ notifications: [], unreadCount: 0, addNotification: (notification) => set((state) => ({ notifications: [ ...state.notifications, { ...notification, id: crypto.randomUUID() } ], unreadCount: state.unreadCount + 1 })), removeNotification: (id) => set((state) => ({ notifications: state.notifications.filter((n) => n.id !== id) })), clearAll: () => set({ notifications: [], unreadCount: 0 }), markAllAsRead: () => set({ unreadCount: 0 }) })) ); ``` --- ## Feature Stores ### ConstructionStore ```typescript interface ConstructionState { // Filters projectFilters: ProjectFilters; developmentFilters: DevelopmentFilters; // View preferences projectViewMode: 'list' | 'grid' | 'kanban'; budgetViewMode: 'table' | 'chart'; ganttViewMode: 'day' | 'week' | 'month'; // Selected items selectedProjectId: string | null; selectedDevelopmentId: string | null; // Actions setProjectFilters: (filters: Partial) => void; resetProjectFilters: () => void; setDevelopmentFilters: (filters: Partial) => void; setProjectViewMode: (mode: 'list' | 'grid' | 'kanban') => void; setBudgetViewMode: (mode: 'table' | 'chart') => void; setGanttViewMode: (mode: 'day' | 'week' | 'month') => void; setSelectedProject: (id: string | null) => void; setSelectedDevelopment: (id: string | null) => void; } const initialProjectFilters: ProjectFilters = { status: undefined, search: '', clientId: undefined, managerId: undefined, dateRange: undefined, page: 1, limit: 20 }; export const useConstructionStore = create()( devtools( persist( (set) => ({ projectFilters: initialProjectFilters, developmentFilters: {}, projectViewMode: 'list', budgetViewMode: 'table', ganttViewMode: 'week', selectedProjectId: null, selectedDevelopmentId: null, setProjectFilters: (filters) => set((state) => ({ projectFilters: { ...state.projectFilters, ...filters } })), resetProjectFilters: () => set({ projectFilters: initialProjectFilters }), setDevelopmentFilters: (filters) => set((state) => ({ developmentFilters: { ...state.developmentFilters, ...filters } })), setProjectViewMode: (mode) => set({ projectViewMode: mode }), setBudgetViewMode: (mode) => set({ budgetViewMode: mode }), setGanttViewMode: (mode) => set({ ganttViewMode: mode }), setSelectedProject: (id) => set({ selectedProjectId: id }), setSelectedDevelopment: (id) => set({ selectedDevelopmentId: id }) }), { name: 'construction-store', partialize: (state) => ({ projectViewMode: state.projectViewMode, budgetViewMode: state.budgetViewMode, ganttViewMode: state.ganttViewMode }) } ) ) ); ``` ### ComplianceStore ```typescript interface ComplianceState { // Filters complianceFilters: ComplianceFilters; auditFilters: AuditFilters; // View dashboardView: 'summary' | 'detailed'; // Selected selectedProgramId: string | null; selectedAuditId: string | null; // Actions setComplianceFilters: (filters: Partial) => void; setAuditFilters: (filters: Partial) => void; setDashboardView: (view: 'summary' | 'detailed') => void; setSelectedProgram: (id: string | null) => void; setSelectedAudit: (id: string | null) => void; } export const useComplianceStore = create()( devtools( persist( (set) => ({ complianceFilters: {}, auditFilters: {}, dashboardView: 'summary', selectedProgramId: null, selectedAuditId: null, setComplianceFilters: (filters) => set((state) => ({ complianceFilters: { ...state.complianceFilters, ...filters } })), setAuditFilters: (filters) => set((state) => ({ auditFilters: { ...state.auditFilters, ...filters } })), setDashboardView: (view) => set({ dashboardView: view }), setSelectedProgram: (id) => set({ selectedProgramId: id }), setSelectedAudit: (id) => set({ selectedAuditId: id }) }), { name: 'compliance-store' } ) ) ); ``` ### FinanceStore ```typescript interface FinanceState { // Filters transactionFilters: TransactionFilters; reportFilters: ReportFilters; // View dashboardPeriod: 'month' | 'quarter' | 'year'; chartType: 'bar' | 'line' | 'area'; // Selected selectedAccountId: string | null; // Actions setTransactionFilters: (filters: Partial) => void; setReportFilters: (filters: Partial) => void; setDashboardPeriod: (period: 'month' | 'quarter' | 'year') => void; setChartType: (type: 'bar' | 'line' | 'area') => void; setSelectedAccount: (id: string | null) => void; } export const useFinanceStore = create()( devtools( persist( (set) => ({ transactionFilters: {}, reportFilters: {}, dashboardPeriod: 'month', chartType: 'bar', selectedAccountId: null, setTransactionFilters: (filters) => set((state) => ({ transactionFilters: { ...state.transactionFilters, ...filters } })), setReportFilters: (filters) => set((state) => ({ reportFilters: { ...state.reportFilters, ...filters } })), setDashboardPeriod: (period) => set({ dashboardPeriod: period }), setChartType: (type) => set({ chartType: type }), setSelectedAccount: (id) => set({ selectedAccountId: id }) }), { name: 'finance-store' } ) ) ); ``` ### AssetsStore ```typescript interface AssetsState { // Filters assetFilters: AssetFilters; maintenanceFilters: MaintenanceFilters; workOrderFilters: WorkOrderFilters; // View assetViewMode: 'list' | 'grid'; mapCenter: LatLng | null; mapZoom: number; // Selected selectedAssetId: string | null; // Actions setAssetFilters: (filters: Partial) => void; setMaintenanceFilters: (filters: Partial) => void; setWorkOrderFilters: (filters: Partial) => void; setAssetViewMode: (mode: 'list' | 'grid') => void; setMapCenter: (center: LatLng) => void; setMapZoom: (zoom: number) => void; setSelectedAsset: (id: string | null) => void; } export const useAssetsStore = create()( devtools( persist( (set) => ({ assetFilters: {}, maintenanceFilters: {}, workOrderFilters: {}, assetViewMode: 'list', mapCenter: null, mapZoom: 12, selectedAssetId: null, setAssetFilters: (filters) => set((state) => ({ assetFilters: { ...state.assetFilters, ...filters } })), setMaintenanceFilters: (filters) => set((state) => ({ maintenanceFilters: { ...state.maintenanceFilters, ...filters } })), setWorkOrderFilters: (filters) => set((state) => ({ workOrderFilters: { ...state.workOrderFilters, ...filters } })), setAssetViewMode: (mode) => set({ assetViewMode: mode }), setMapCenter: (center) => set({ mapCenter: center }), setMapZoom: (zoom) => set({ mapZoom: zoom }), setSelectedAsset: (id) => set({ selectedAssetId: id }) }), { name: 'assets-store' } ) ) ); ``` ### DocumentsStore ```typescript interface DocumentsState { // Navigation currentFolderId: string | null; folderPath: Folder[]; // Filters documentFilters: DocumentFilters; // View viewMode: 'list' | 'grid'; sortBy: 'name' | 'date' | 'size'; sortOrder: 'asc' | 'desc'; // Selection selectedDocuments: string[]; // Upload uploadQueue: UploadItem[]; // Actions setCurrentFolder: (id: string | null) => void; setFolderPath: (path: Folder[]) => void; navigateToFolder: (folder: Folder) => void; navigateUp: () => void; setDocumentFilters: (filters: Partial) => void; setViewMode: (mode: 'list' | 'grid') => void; setSortBy: (sortBy: 'name' | 'date' | 'size') => void; setSortOrder: (order: 'asc' | 'desc') => void; selectDocument: (id: string) => void; deselectDocument: (id: string) => void; selectAllDocuments: (ids: string[]) => void; clearSelection: () => void; addToUploadQueue: (item: UploadItem) => void; removeFromUploadQueue: (id: string) => void; clearUploadQueue: () => void; } export const useDocumentsStore = create()( devtools( persist( (set, get) => ({ currentFolderId: null, folderPath: [], documentFilters: {}, viewMode: 'list', sortBy: 'name', sortOrder: 'asc', selectedDocuments: [], uploadQueue: [], setCurrentFolder: (id) => set({ currentFolderId: id }), setFolderPath: (path) => set({ folderPath: path }), navigateToFolder: (folder) => { const currentPath = get().folderPath; const index = currentPath.findIndex(f => f.id === folder.id); if (index >= 0) { // Navigate to existing folder in path set({ currentFolderId: folder.id, folderPath: currentPath.slice(0, index + 1) }); } else { // Navigate to new folder set({ currentFolderId: folder.id, folderPath: [...currentPath, folder] }); } }, navigateUp: () => { const path = get().folderPath; if (path.length > 1) { set({ currentFolderId: path[path.length - 2].id, folderPath: path.slice(0, -1) }); } else { set({ currentFolderId: null, folderPath: [] }); } }, setDocumentFilters: (filters) => set((state) => ({ documentFilters: { ...state.documentFilters, ...filters } })), setViewMode: (mode) => set({ viewMode: mode }), setSortBy: (sortBy) => set({ sortBy }), setSortOrder: (order) => set({ sortOrder: order }), selectDocument: (id) => set((state) => ({ selectedDocuments: [...state.selectedDocuments, id] })), deselectDocument: (id) => set((state) => ({ selectedDocuments: state.selectedDocuments.filter(d => d !== id) })), selectAllDocuments: (ids) => set({ selectedDocuments: ids }), clearSelection: () => set({ selectedDocuments: [] }), addToUploadQueue: (item) => set((state) => ({ uploadQueue: [...state.uploadQueue, item] })), removeFromUploadQueue: (id) => set((state) => ({ uploadQueue: state.uploadQueue.filter(i => i.id !== id) })), clearUploadQueue: () => set({ uploadQueue: [] }) }), { name: 'documents-store', partialize: (state) => ({ viewMode: state.viewMode, sortBy: state.sortBy, sortOrder: state.sortOrder }) } ) ) ); ``` --- ## Store Patterns ### Selectors ```typescript // Memoized selectors for performance export const selectActiveProjects = (state: ConstructionState) => state.projectFilters.status === 'active'; export const selectFilteredProjects = createSelector( [(state) => state.projects, (state) => state.projectFilters], (projects, filters) => { return projects.filter(p => { if (filters.status && p.status !== filters.status) return false; if (filters.search && !p.name.includes(filters.search)) return false; return true; }); } ); ``` ### Actions with Side Effects ```typescript // Using middleware for side effects const logMiddleware = (config) => (set, get, api) => config( (...args) => { console.log(' applying', args); set(...args); console.log(' new state', get()); }, get, api ); ``` ### Hydration ```typescript // Hydrate store from server export const hydrateStore = async () => { const userData = await fetchUserData(); useAuthStore.setState({ user: userData }); const tenantData = await fetchTenantData(); useTenantStore.setState({ currentTenant: tenantData }); }; ``` --- *Ultima actualizacion: 2025-12-05*