Správa používateľov + notifikačný systém
- Pridaná kompletná správa používateľov (CRUD, reset hesla, zmena roly) pre ROOT/ADMIN - Backend: POST /users endpoint, createUser controller, validácia - Frontend: UserManagement, UserForm, PasswordResetModal komponenty - Settings prístupné pre ROOT aj ADMIN (AdminRoute) - Notifikačný systém s snooze funkcionalitou - Aktualizácia HELPDESK_INIT_V2.md dokumentácie Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
114
frontend/src/store/notificationStore.ts
Normal file
114
frontend/src/store/notificationStore.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { create } from 'zustand';
|
||||
import { notificationApi, type Notification } from '@/services/notification.api';
|
||||
|
||||
interface NotificationState {
|
||||
notifications: Notification[];
|
||||
unreadCount: number;
|
||||
isLoading: boolean;
|
||||
isOpen: boolean;
|
||||
|
||||
// Actions
|
||||
fetchNotifications: () => Promise<void>;
|
||||
fetchUnreadCount: () => Promise<void>;
|
||||
markAsRead: (id: string) => Promise<void>;
|
||||
markAllAsRead: () => Promise<void>;
|
||||
snooze: (id: string, minutes: number) => Promise<void>;
|
||||
deleteNotification: (id: string) => Promise<void>;
|
||||
setIsOpen: (isOpen: boolean) => void;
|
||||
addNotification: (notification: Notification) => void;
|
||||
}
|
||||
|
||||
export const useNotificationStore = create<NotificationState>((set, get) => ({
|
||||
notifications: [],
|
||||
unreadCount: 0,
|
||||
isLoading: false,
|
||||
isOpen: false,
|
||||
|
||||
fetchNotifications: async () => {
|
||||
set({ isLoading: true });
|
||||
try {
|
||||
const response = await notificationApi.getAll({ limit: 50 });
|
||||
set({ notifications: response.data.notifications });
|
||||
} catch (error) {
|
||||
console.error('Error fetching notifications:', error);
|
||||
} finally {
|
||||
set({ isLoading: false });
|
||||
}
|
||||
},
|
||||
|
||||
fetchUnreadCount: async () => {
|
||||
try {
|
||||
const response = await notificationApi.getUnreadCount();
|
||||
set({ unreadCount: response.data.count });
|
||||
} catch (error) {
|
||||
console.error('Error fetching unread count:', error);
|
||||
}
|
||||
},
|
||||
|
||||
markAsRead: async (id: string) => {
|
||||
try {
|
||||
await notificationApi.markAsRead(id);
|
||||
set((state) => ({
|
||||
notifications: state.notifications.map((n) =>
|
||||
n.id === id ? { ...n, isRead: true, readAt: new Date().toISOString() } : n
|
||||
),
|
||||
unreadCount: Math.max(0, state.unreadCount - 1),
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('Error marking notification as read:', error);
|
||||
}
|
||||
},
|
||||
|
||||
markAllAsRead: async () => {
|
||||
try {
|
||||
await notificationApi.markAllAsRead();
|
||||
set((state) => ({
|
||||
notifications: state.notifications.map((n) => ({
|
||||
...n,
|
||||
isRead: true,
|
||||
readAt: new Date().toISOString(),
|
||||
})),
|
||||
unreadCount: 0,
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('Error marking all as read:', error);
|
||||
}
|
||||
},
|
||||
|
||||
snooze: async (id: string, minutes: number) => {
|
||||
try {
|
||||
const response = await notificationApi.snooze(id, minutes);
|
||||
set((state) => ({
|
||||
notifications: state.notifications.filter((n) => n.id !== id),
|
||||
unreadCount: Math.max(0, state.unreadCount - 1),
|
||||
}));
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('Error snoozing notification:', error);
|
||||
}
|
||||
},
|
||||
|
||||
deleteNotification: async (id: string) => {
|
||||
const notification = get().notifications.find((n) => n.id === id);
|
||||
try {
|
||||
await notificationApi.delete(id);
|
||||
set((state) => ({
|
||||
notifications: state.notifications.filter((n) => n.id !== id),
|
||||
unreadCount: notification && !notification.isRead
|
||||
? Math.max(0, state.unreadCount - 1)
|
||||
: state.unreadCount,
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('Error deleting notification:', error);
|
||||
}
|
||||
},
|
||||
|
||||
setIsOpen: (isOpen: boolean) => set({ isOpen }),
|
||||
|
||||
addNotification: (notification: Notification) => {
|
||||
set((state) => ({
|
||||
notifications: [notification, ...state.notifications],
|
||||
unreadCount: notification.isRead ? state.unreadCount : state.unreadCount + 1,
|
||||
}));
|
||||
},
|
||||
}));
|
||||
15
frontend/src/store/sidebarStore.ts
Normal file
15
frontend/src/store/sidebarStore.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
interface SidebarState {
|
||||
isOpen: boolean;
|
||||
toggle: () => void;
|
||||
open: () => void;
|
||||
close: () => void;
|
||||
}
|
||||
|
||||
export const useSidebarStore = create<SidebarState>((set) => ({
|
||||
isOpen: false,
|
||||
toggle: () => set((state) => ({ isOpen: !state.isOpen })),
|
||||
open: () => set({ isOpen: true }),
|
||||
close: () => set({ isOpen: false }),
|
||||
}));
|
||||
Reference in New Issue
Block a user