import { useState, useEffect, useCallback } from 'react'; import { channelsApi, templatesApi, preferencesApi, inAppApi, } from '../api'; import type { NotificationChannel, NotificationTemplate, NotificationTemplateCreateInput, NotificationPreference, NotificationPreferenceUpdateInput, InAppNotification, InAppNotificationsFilters, } from '../types'; // ============================================================================ // Channels Hook // ============================================================================ interface UseChannelsReturn { channels: NotificationChannel[]; isLoading: boolean; error: Error | null; refresh: () => Promise; getByCode: (code: string) => Promise; } export function useChannels(): UseChannelsReturn { const [channels, setChannels] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const fetchChannels = useCallback(async () => { setIsLoading(true); setError(null); try { const data = await channelsApi.getAll(); setChannels(data); } catch (err) { setError(err instanceof Error ? err : new Error('Error fetching channels')); } finally { setIsLoading(false); } }, []); useEffect(() => { fetchChannels(); }, [fetchChannels]); const getByCode = useCallback(async (code: string): Promise => { return channelsApi.getByCode(code); }, []); return { channels, isLoading, error, refresh: fetchChannels, getByCode, }; } // ============================================================================ // Templates Hook // ============================================================================ interface UseTemplatesReturn { templates: NotificationTemplate[]; isLoading: boolean; error: Error | null; refresh: () => Promise; getByCode: (code: string) => Promise; create: (data: NotificationTemplateCreateInput) => Promise; update: (id: string, data: Partial) => Promise; remove: (id: string) => Promise; } export function useTemplates(): UseTemplatesReturn { const [templates, setTemplates] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const fetchTemplates = useCallback(async () => { setIsLoading(true); setError(null); try { const data = await templatesApi.getAll(); setTemplates(data); } catch (err) { setError(err instanceof Error ? err : new Error('Error fetching templates')); } finally { setIsLoading(false); } }, []); useEffect(() => { fetchTemplates(); }, [fetchTemplates]); const getByCode = useCallback(async (code: string): Promise => { return templatesApi.getByCode(code); }, []); const create = useCallback(async (data: NotificationTemplateCreateInput): Promise => { const result = await templatesApi.create(data); await fetchTemplates(); return result; }, [fetchTemplates]); const update = useCallback(async (id: string, data: Partial): Promise => { const result = await templatesApi.update(id, data); await fetchTemplates(); return result; }, [fetchTemplates]); const remove = useCallback(async (id: string): Promise => { await templatesApi.delete(id); await fetchTemplates(); }, [fetchTemplates]); return { templates, isLoading, error, refresh: fetchTemplates, getByCode, create, update, remove, }; } // ============================================================================ // Preferences Hook // ============================================================================ interface UseNotificationPreferencesReturn { preferences: NotificationPreference | null; isLoading: boolean; isSaving: boolean; error: Error | null; refresh: () => Promise; update: (data: NotificationPreferenceUpdateInput) => Promise; } export function useNotificationPreferences(): UseNotificationPreferencesReturn { const [preferences, setPreferences] = useState(null); const [isLoading, setIsLoading] = useState(true); const [isSaving, setIsSaving] = useState(false); const [error, setError] = useState(null); const fetchPreferences = useCallback(async () => { setIsLoading(true); setError(null); try { const data = await preferencesApi.get(); setPreferences(data); } catch (err) { setError(err instanceof Error ? err : new Error('Error fetching preferences')); } finally { setIsLoading(false); } }, []); useEffect(() => { fetchPreferences(); }, [fetchPreferences]); const update = useCallback(async (data: NotificationPreferenceUpdateInput): Promise => { setIsSaving(true); setError(null); try { const result = await preferencesApi.update(data); setPreferences(result); return result; } catch (err) { const error = err instanceof Error ? err : new Error('Error updating preferences'); setError(error); throw error; } finally { setIsSaving(false); } }, []); return { preferences, isLoading, isSaving, error, refresh: fetchPreferences, update, }; } // ============================================================================ // In-App Notifications Hook // ============================================================================ interface UseInAppNotificationsOptions { initialFilters?: InAppNotificationsFilters; autoLoad?: boolean; pollInterval?: number; } interface UseInAppNotificationsReturn { notifications: InAppNotification[]; unreadCount: number; total: number; page: number; totalPages: number; isLoading: boolean; error: Error | null; filters: InAppNotificationsFilters; setFilters: (filters: InAppNotificationsFilters) => void; refresh: () => Promise; markAsRead: (id: string) => Promise; markAllAsRead: () => Promise; archive: (id: string) => Promise; remove: (id: string) => Promise; } export function useInAppNotifications( options: UseInAppNotificationsOptions = {} ): UseInAppNotificationsReturn { const { initialFilters = {}, autoLoad = true, pollInterval } = options; const [notifications, setNotifications] = useState([]); const [unreadCount, setUnreadCount] = useState(0); const [total, setTotal] = useState(0); const [page, setPage] = useState(1); const [totalPages, setTotalPages] = useState(0); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [filters, setFilters] = useState(initialFilters); const fetchNotifications = useCallback(async () => { setIsLoading(true); setError(null); try { const response = await inAppApi.getAll(filters); setNotifications(response.data); setUnreadCount(response.unreadCount); setTotal(response.total); setPage(response.page); setTotalPages(response.totalPages); } catch (err) { setError(err instanceof Error ? err : new Error('Error fetching notifications')); } finally { setIsLoading(false); } }, [filters]); const fetchUnreadCount = useCallback(async () => { try { const count = await inAppApi.getUnreadCount(); setUnreadCount(count); } catch (err) { // Silently fail for unread count polling } }, []); useEffect(() => { if (autoLoad) { fetchNotifications(); } }, [autoLoad, fetchNotifications]); // Poll for unread count useEffect(() => { if (!pollInterval) return; const interval = setInterval(fetchUnreadCount, pollInterval); return () => clearInterval(interval); }, [pollInterval, fetchUnreadCount]); const markAsRead = useCallback(async (id: string): Promise => { await inAppApi.markAsRead(id); setNotifications(prev => prev.map(n => n.id === id ? { ...n, isRead: true, readAt: new Date().toISOString() } : n) ); setUnreadCount(prev => Math.max(0, prev - 1)); }, []); const markAllAsRead = useCallback(async (): Promise => { await inAppApi.markAllAsRead(); setNotifications(prev => prev.map(n => ({ ...n, isRead: true, readAt: new Date().toISOString() })) ); setUnreadCount(0); }, []); const archive = useCallback(async (id: string): Promise => { await inAppApi.archive(id); setNotifications(prev => prev.filter(n => n.id !== id)); }, []); const remove = useCallback(async (id: string): Promise => { await inAppApi.delete(id); setNotifications(prev => prev.filter(n => n.id !== id)); }, []); return { notifications, unreadCount, total, page, totalPages, isLoading, error, filters, setFilters, refresh: fetchNotifications, markAsRead, markAllAsRead, archive, remove, }; } // ============================================================================ // Notification Bell Hook (for header/navbar) // ============================================================================ interface UseNotificationBellReturn { unreadCount: number; recentNotifications: InAppNotification[]; isLoading: boolean; refresh: () => Promise; markAsRead: (id: string) => Promise; markAllAsRead: () => Promise; } export function useNotificationBell(pollInterval = 30000): UseNotificationBellReturn { const { notifications: recentNotifications, unreadCount, isLoading, refresh, markAsRead, markAllAsRead, } = useInAppNotifications({ initialFilters: { limit: 5, includeRead: false }, pollInterval, }); return { unreadCount, recentNotifications, isLoading, refresh, markAsRead, markAllAsRead, }; }