135 lines
3.5 KiB
TypeScript
135 lines
3.5 KiB
TypeScript
import { create } from 'zustand';
|
|
|
|
export interface NotificationItem {
|
|
id: string;
|
|
title: string;
|
|
message: string;
|
|
type: 'info' | 'success' | 'warning' | 'error';
|
|
read: boolean;
|
|
createdAt: Date;
|
|
}
|
|
|
|
export interface OfflineAction {
|
|
id: string;
|
|
url: string;
|
|
method: 'POST' | 'PUT' | 'DELETE';
|
|
payload: any;
|
|
timestamp: number;
|
|
}
|
|
|
|
interface UIState {
|
|
// Sidebar State
|
|
sidebarCollapsed: boolean;
|
|
toggleSidebar: () => void;
|
|
setSidebarCollapsed: (collapsed: boolean) => void;
|
|
|
|
// Command Palette State
|
|
commandPaletteOpen: boolean;
|
|
setCommandPaletteOpen: (open: boolean) => void;
|
|
|
|
// Notifications State
|
|
notifications: NotificationItem[];
|
|
unreadCount: number;
|
|
addNotification: (notification: Omit<NotificationItem, 'id' | 'read' | 'createdAt'>) => void;
|
|
markAsRead: (id: string) => void;
|
|
markAllAsRead: () => void;
|
|
clearNotifications: () => void;
|
|
|
|
// Network/Offline State
|
|
isOnline: boolean;
|
|
setIsOnline: (online: boolean) => void;
|
|
offlineQueue: OfflineAction[];
|
|
enqueueOfflineAction: (action: Omit<OfflineAction, 'id' | 'timestamp'>) => void;
|
|
clearOfflineQueue: () => void;
|
|
}
|
|
|
|
export const useUIStore = create<UIState>((set) => ({
|
|
// Sidebar defaults
|
|
sidebarCollapsed: false,
|
|
toggleSidebar: () => set((state) => ({ sidebarCollapsed: !state.sidebarCollapsed })),
|
|
setSidebarCollapsed: (collapsed) => set({ sidebarCollapsed: collapsed }),
|
|
|
|
// Command palette defaults
|
|
commandPaletteOpen: false,
|
|
setCommandPaletteOpen: (open) => set({ commandPaletteOpen: open }),
|
|
|
|
// Mock Notifications for design alignment
|
|
notifications: [
|
|
{
|
|
id: '1',
|
|
title: 'New Repair Job Assigned',
|
|
message: 'Repair ticket #REP0089 assigned to Technician John Doe',
|
|
type: 'info',
|
|
read: false,
|
|
createdAt: new Date(),
|
|
},
|
|
{
|
|
id: '2',
|
|
title: 'Low Stock Alert',
|
|
message: 'iPhone 15 Screen replacement modules are below safety levels (2 units remaining)',
|
|
type: 'warning',
|
|
read: false,
|
|
createdAt: new Date(Date.now() - 3600000),
|
|
},
|
|
{
|
|
id: '3',
|
|
title: 'Invoice Paid',
|
|
message: 'Payment received for Invoice #INV10452 ($450.00)',
|
|
type: 'success',
|
|
read: true,
|
|
createdAt: new Date(Date.now() - 7200000),
|
|
}
|
|
],
|
|
unreadCount: 2,
|
|
|
|
addNotification: (n) =>
|
|
set((state) => {
|
|
const item: NotificationItem = {
|
|
...n,
|
|
id: Math.random().toString(36).substr(2, 9),
|
|
read: false,
|
|
createdAt: new Date(),
|
|
};
|
|
const updated = [item, ...state.notifications];
|
|
return {
|
|
notifications: updated,
|
|
unreadCount: updated.filter((x) => !x.read).length,
|
|
};
|
|
}),
|
|
|
|
markAsRead: (id) =>
|
|
set((state) => {
|
|
const updated = state.notifications.map((n) =>
|
|
n.id === id ? { ...n, read: true } : n
|
|
);
|
|
return {
|
|
notifications: updated,
|
|
unreadCount: updated.filter((x) => !x.read).length,
|
|
};
|
|
}),
|
|
|
|
markAllAsRead: () =>
|
|
set((state) => ({
|
|
notifications: state.notifications.map((n) => ({ ...n, read: true })),
|
|
unreadCount: 0,
|
|
})),
|
|
|
|
clearNotifications: () => set({ notifications: [], unreadCount: 0 }),
|
|
|
|
// Network states
|
|
isOnline: true,
|
|
setIsOnline: (online) => set({ isOnline: online }),
|
|
offlineQueue: [],
|
|
enqueueOfflineAction: (action) =>
|
|
set((state) => ({
|
|
offlineQueue: [
|
|
...state.offlineQueue,
|
|
{
|
|
...action,
|
|
id: Math.random().toString(36).substr(2, 9),
|
|
timestamp: Date.now(),
|
|
},
|
|
],
|
|
})),
|
|
clearOfflineQueue: () => set({ offlineQueue: [] }),
|
|
}));
|