'use client'; import { useState } from 'react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { toast } from 'sonner'; import { Button } from '@/components/ui/button'; import { Switch } from '@/components/ui/switch'; import { apiFetch } from '@/lib/api/client'; import { toastError } from '@/lib/api/toast-error'; interface Pref { notificationType: string; inApp: boolean; email: boolean; } const KNOWN_TYPES: Array<{ key: string; label: string; description: string }> = [ { key: 'mention', label: 'Mentions', description: 'When someone @-mentions you in a note.', }, { key: 'reminder_overdue', label: 'Overdue Reminders', description: 'When an interest reminder you own becomes overdue.', }, { key: 'interest_stage_changed', label: 'Interest Stage Changes', description: 'When a pipeline stage changes on an interest assigned to you.', }, { key: 'system_alert', label: 'System Alerts', description: 'Background job failures and maintenance notices.', }, ]; export function NotificationPreferencesForm() { const qc = useQueryClient(); const { data, isLoading } = useQuery({ queryKey: ['notifications', 'preferences'], queryFn: () => apiFetch<{ data: Pref[] }>('/api/v1/notifications/preferences').then((r) => r.data), }); // Key-based remount: body keyed on the server payload signature so its // useState initializer re-runs when prefs land or change. Replaces the // useEffect(setPrefs, [data]) sync the Compiler flagged. const signature = data ? data.map((p) => `${p.notificationType}:${p.inApp ? 1 : 0}:${p.email ? 1 : 0}`).join('|') : 'loading'; return ( ); } function NotificationPreferencesFormBody({ data, isLoading, qc, }: { data: Pref[] | undefined; isLoading: boolean; qc: ReturnType; }) { const [prefs, setPrefs] = useState>(() => { const map = new Map(); for (const t of KNOWN_TYPES) { map.set(t.key, { notificationType: t.key, inApp: true, email: true }); } if (data) { for (const p of data) { map.set(p.notificationType, p); } } return map; }); const mutation = useMutation({ mutationFn: async () => { const payload = { preferences: Array.from(prefs.values()) }; return apiFetch('/api/v1/notifications/preferences', { method: 'PUT', body: payload, }); }, onSuccess: () => { toast.success('Preferences saved'); qc.invalidateQueries({ queryKey: ['notifications', 'preferences'] }); }, onError: (err) => toastError(err), }); function update(type: string, field: 'inApp' | 'email', value: boolean) { setPrefs((prev) => { const next = new Map(prev); const existing = next.get(type) ?? { notificationType: type, inApp: true, email: true }; next.set(type, { ...existing, [field]: value }); return next; }); } if (isLoading) { return
Loading preferences…
; } return (
Type
In-app
Email
{KNOWN_TYPES.map((t) => { const pref = prefs.get(t.key) ?? { notificationType: t.key, inApp: true, email: true, }; return (
{t.label}
{t.description}
update(t.key, 'inApp', v)} />
update(t.key, 'email', v)} />
); })}
); }