import React, { useState, useEffect } from 'react'; import { useUserStore } from '../store/userStore'; import { auth } from '../lib/auth-client'; import { Bell, Mail, MessageSquare, TrendingUp, Moon } from 'lucide-react'; import { useToast } from '@/contexts/ToastContext'; interface NotificationSettings { email_notifications: boolean; push_notifications: boolean; weekly_reports: boolean; ai_insights: boolean; severity_threshold: 'low' | 'medium' | 'high'; dnd_start: string | null; dnd_end: string | null; } export default function NotificationSettings() { const { user } = useUserStore(); const toast = useToast(); const [settings, setSettings] = useState({ email_notifications: true, push_notifications: false, weekly_reports: true, ai_insights: true, severity_threshold: 'medium', dnd_start: null, dnd_end: null, }); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); useEffect(() => { loadSettings(); }, [user]); async function loadSettings() { if (!user) return; try { // FIRST: Try to load from localStorage (fastest and most reliable) const localStorageKey = `notification_settings_${user.id}`; const savedSettings = localStorage.getItem(localStorageKey); if (savedSettings) { const parsed = JSON.parse(savedSettings); setSettings(parsed); console.log('✓ Loaded settings from localStorage:', parsed); setLoading(false); return; } // SECOND: Try to load from API const workspaceId = user.id; const session = await auth.getSession(); const token = session.data.session?.access_token; if (!token) { console.warn('No auth token, using default settings'); setLoading(false); return; } const response = await fetch( `/api/settings/${workspaceId}/${user.id}`, { headers: { Authorization: `Bearer ${token}`, }, } ); if (response.ok) { const data = await response.json(); if (data) { setSettings(data); // Save to localStorage as backup localStorage.setItem(localStorageKey, JSON.stringify(data)); console.log('✓ Loaded settings from API and saved to localStorage'); } } else if (response.status === 404) { console.log('No saved settings found, using defaults'); } else { console.error('Failed to load settings:', response.statusText); } } catch (error) { console.error('Failed to load settings:', error); } finally { setLoading(false); } } async function updateSetting(key: keyof NotificationSettings, value: any) { if (!user) return; // Optimistic update const newSettings = { ...settings, [key]: value }; setSettings(newSettings); // IMMEDIATELY save to localStorage (guaranteed to work) const localStorageKey = `notification_settings_${user.id}`; localStorage.setItem(localStorageKey, JSON.stringify(newSettings)); console.log(`✓ Setting saved to localStorage: ${key} = ${value}`); setSaving(true); try { // Also try to save to API (best effort) const workspaceId = user.id; const session = await auth.getSession(); const token = session.data.session?.access_token; if (!token) { console.warn('No auth token, settings saved locally only'); setSaving(false); return; } const response = await fetch(`/api/settings/${workspaceId}/${user.id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}`, }, body: JSON.stringify({ [key]: value }), }); if (response.ok) { console.log(`✓ Setting also saved to API: ${key} = ${value}`); } else { console.warn('API save failed, but localStorage save succeeded'); } // Handle push notification registration if (key === 'push_notifications' && value === true) { await registerPushNotifications(workspaceId); } } catch (error) { console.error('Failed to update setting in API:', error); console.log('Settings still saved in localStorage'); } finally { setSaving(false); } } async function registerPushNotifications(workspaceId: string) { if (!('serviceWorker' in navigator) || !('PushManager' in window)) { toast.warning('Push notifications are not supported in this browser'); setSettings((prev) => ({ ...prev, push_notifications: false })); return; } try { // Register service worker const registration = await navigator.serviceWorker.register('/sw.js'); // Wait for service worker to be ready await navigator.serviceWorker.ready; // Subscribe to push notifications const subscription = await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array( import.meta.env.VITE_VAPID_PUBLIC_KEY || '' ), }); // Send subscription to backend const session = await auth.getSession(); const token = session.data.session?.access_token; await fetch('/api/push/subscribe', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}`, }, body: JSON.stringify({ workspace_id: workspaceId, user_id: user?.id, token: JSON.stringify(subscription), platform: 'web', }), }); toast.success('Push notifications enabled successfully'); console.log('Push notifications registered successfully'); } catch (error) { console.error('Failed to register push notifications:', error); toast.error('Failed to enable push notifications. Please try again.'); setSettings((prev) => ({ ...prev, push_notifications: false })); } } function urlBase64ToUint8Array(base64String: string) { const padding = '='.repeat((4 - (base64String.length % 4)) % 4); const base64 = (base64String + padding) .replace(/-/g, '+') .replace(/_/g, '/'); const rawData = window.atob(base64); const outputArray = new Uint8Array(rawData.length); for (let i = 0; i < rawData.length; ++i) { outputArray[i] = rawData.charCodeAt(i); } return outputArray; } if (loading) { return (
); } return (
{/* Header */}

Notifications

Manage how you receive updates from DataVision

{/* Settings List */}
{/* Email Notifications */} updateSetting('email_notifications', checked)} disabled={saving} /> {/* Push Notifications */} updateSetting('push_notifications', checked)} disabled={saving} /> {/* Weekly Reports */} updateSetting('weekly_reports', checked)} disabled={saving} /> {/* AI Insights */} updateSetting('ai_insights', checked)} disabled={saving} />
{/* Advanced Settings */}

Advanced Settings

{/* Severity Threshold */}

Only receive notifications for insights at or above this severity level

{/* Do Not Disturb */}
updateSetting('dnd_start', e.target.value)} className="w-full px-4 py-3 sm:py-2 text-base bg-white dark:bg-dark-bg border border-gray-300 dark:border-dark-border rounded-lg focus:ring-2 focus:ring-orange-500 focus:border-transparent" placeholder="Start time" disabled={saving} />

Start time

updateSetting('dnd_end', e.target.value)} className="w-full px-4 py-3 sm:py-2 text-base bg-white dark:bg-dark-bg border border-gray-300 dark:border-dark-border rounded-lg focus:ring-2 focus:ring-orange-500 focus:border-transparent" placeholder="End time" disabled={saving} />

End time

No notifications will be sent during this time window

{/* Save Indicator */} {saving && (
Saving...
)}
); } interface SettingCardProps { icon: React.ElementType; title: string; description: string; checked: boolean; onChange: (checked: boolean) => void; disabled?: boolean; } function SettingCard({ icon: Icon, title, description, checked, onChange, disabled, }: SettingCardProps) { return (

{title}

{description}

{/* Toggle Switch */}
); }