diff --git a/src/components/settings/NotificationsTab.tsx b/src/components/settings/NotificationsTab.tsx index fee05aa2..ae3b171d 100644 --- a/src/components/settings/NotificationsTab.tsx +++ b/src/components/settings/NotificationsTab.tsx @@ -8,48 +8,32 @@ import { Button } from "@/components/ui/button"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Separator } from "@/components/ui/separator"; import { Badge } from "@/components/ui/badge"; -import { toast } from "sonner"; +import { Skeleton } from "@/components/ui/skeleton"; +import { handleError, handleSuccess, handleInfo } from "@/lib/errorHandler"; +import { logger } from "@/lib/logger"; import { notificationService } from "@/lib/notificationService"; - -interface ChannelPreferences { - in_app: boolean; - email: boolean; - push: boolean; -} - -interface WorkflowPreferences { - [workflowId: string]: boolean; -} - -interface FrequencySettings { - digest: 'realtime' | 'hourly' | 'daily' | 'weekly'; - max_per_hour: number; -} - -interface NotificationTemplate { - id: string; - workflow_id: string; - name: string; - description: string; - category: string; - is_active: boolean; -} +import type { + NotificationPreferences, + NotificationTemplate, + ChannelPreferences, + WorkflowPreferences, + FrequencySettings +} from "@/types/notifications"; +import { DEFAULT_NOTIFICATION_PREFERENCES } from "@/lib/notificationValidation"; export function NotificationsTab() { const { user } = useAuth(); const { isEnabled: isNovuEnabled, isLoading: isNovuLoading } = usePublicNovuSettings(); const [loading, setLoading] = useState(true); + const [saving, setSaving] = useState(false); const [templates, setTemplates] = useState([]); - const [channelPreferences, setChannelPreferences] = useState({ - in_app: true, - email: true, - push: false, - }); + const [channelPreferences, setChannelPreferences] = useState( + DEFAULT_NOTIFICATION_PREFERENCES.channelPreferences + ); const [workflowPreferences, setWorkflowPreferences] = useState({}); - const [frequencySettings, setFrequencySettings] = useState({ - digest: 'daily', - max_per_hour: 10, - }); + const [frequencySettings, setFrequencySettings] = useState( + DEFAULT_NOTIFICATION_PREFERENCES.frequencySettings + ); useEffect(() => { if (user) { @@ -65,71 +49,109 @@ export function NotificationsTab() { const preferences = await notificationService.getPreferences(user.id); if (preferences) { - const { sms, ...channelPrefs } = preferences.channelPreferences; - setChannelPreferences(channelPrefs); + setChannelPreferences(preferences.channelPreferences); setWorkflowPreferences(preferences.workflowPreferences); setFrequencySettings(preferences.frequencySettings); } + + logger.info('Notification preferences loaded', { + action: 'load_notification_preferences', + userId: user.id + }); } catch (error) { - console.error('Error loading preferences:', error); - toast.error("Failed to load notification preferences"); + logger.error('Failed to load notification preferences', { + action: 'load_notification_preferences', + userId: user.id, + error: error instanceof Error ? error.message : String(error) + }); + + handleError(error, { + action: 'Load notification preferences', + userId: user.id + }); } finally { setLoading(false); } }; const loadTemplates = async () => { + if (!user) return; + try { const templateData = await notificationService.getTemplates(); setTemplates(templateData); - // Initialize workflow preferences if not set + // Initialize workflow preferences for new templates const initialPrefs: WorkflowPreferences = {}; templateData.forEach((template) => { if (!(template.workflow_id in workflowPreferences)) { initialPrefs[template.workflow_id] = true; } }); + if (Object.keys(initialPrefs).length > 0) { setWorkflowPreferences((prev) => ({ ...prev, ...initialPrefs })); } + + logger.info('Notification templates loaded', { + action: 'load_notification_templates', + userId: user.id, + count: templateData.length + }); } catch (error) { - console.error('Error loading templates:', error); + logger.error('Failed to load notification templates', { + action: 'load_notification_templates', + userId: user.id, + error: error instanceof Error ? error.message : String(error) + }); + + handleError(error, { + action: 'Load notification templates', + userId: user.id + }); } }; const savePreferences = async () => { if (!user) return; - setLoading(true); + setSaving(true); + try { - const result = await notificationService.updatePreferences(user.id, { - channelPreferences: { - ...channelPreferences, - sms: false, // SMS not supported in UI - }, + const preferences: NotificationPreferences = { + channelPreferences, workflowPreferences, - frequencySettings, + frequencySettings + }; + + const result = await notificationService.updatePreferences(user.id, preferences); + + if (!result.success) { + throw new Error(result.error || 'Failed to save notification preferences'); + } + + logger.info('Notification preferences saved', { + action: 'save_notification_preferences', + userId: user.id }); - if (result.success) { - toast.success("Notification preferences saved successfully"); - - // Also update Novu subscriber profile to sync channel preferences - if (notificationService.isEnabled()) { - await notificationService.updateSubscriber({ - subscriberId: user.id, - email: user.email, - }); - } - } else { - throw new Error(result.error || 'Failed to save preferences'); - } - } catch (error: any) { - console.error('Error saving preferences:', error); - toast.error(error.message || "Failed to save notification preferences"); + handleSuccess( + 'Notification preferences saved', + 'Your notification settings have been updated successfully.' + ); + } catch (error) { + logger.error('Error saving notification preferences', { + action: 'save_notification_preferences', + userId: user.id, + error: error instanceof Error ? error.message : String(error) + }); + + handleError(error, { + action: 'Save notification preferences', + userId: user.id + }); } finally { - setLoading(false); + setSaving(false); } }; @@ -143,21 +165,36 @@ export function NotificationsTab() { const requestPushPermission = async () => { if (!('Notification' in window)) { - toast.error("This browser doesn't support push notifications"); + handleInfo( + 'Push notifications not supported', + 'Your browser does not support push notifications.' + ); return; } try { const permission = await Notification.requestPermission(); + if (permission === 'granted') { updateChannelPreference('push', true); - toast.success("Push notifications enabled"); + handleSuccess('Push notifications enabled', 'You will now receive push notifications.'); } else { - toast.error("Push notification permission denied"); + handleInfo( + 'Permission denied', + 'Push notifications were not enabled. You can change this in your browser settings.' + ); } } catch (error) { - console.error('Error requesting push permission:', error); - toast.error("Failed to enable push notifications"); + logger.error('Error requesting push permission', { + action: 'request_push_permission', + userId: user?.id, + error: error instanceof Error ? error.message : String(error) + }); + + handleError(error, { + action: 'Enable push notifications', + userId: user?.id + }); } }; @@ -169,24 +206,38 @@ export function NotificationsTab() { return acc; }, {} as Record); - return ( -
- {isNovuLoading ? ( + if (loading || isNovuLoading) { + return ( +
- Loading notification settings... + + + + + + + - ) : !isNovuEnabled ? ( +
+ ); + } + + return ( +
+ {!isNovuEnabled && ( - Novu Not Configured + + Novu Not Configured + - Novu notifications are not configured. Contact an administrator to configure the Novu Application Identifier in admin settings. + Notifications are not fully configured. Contact an administrator to enable Novu integration. - ) : null} + )} @@ -247,7 +298,6 @@ export function NotificationsTab() { }} />
- @@ -263,7 +313,7 @@ export function NotificationsTab() {