import { useEffect, useState } from 'react'; import { toast } from 'sonner'; import { User, Mail, KeyRound, ShieldCheck, Loader2, } from 'lucide-react'; import { api } from '@/api'; import { useAuth } from '@/hooks/useAuth'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Switch } from '@/components/ui/switch'; import { ImportSpreadsheetSection, DownloadMyDataSection, ImportMyDataSection, ImportHistorySection as DataImportHistorySection, } from '@/pages/DataPage'; function asProfile(data) { return data?.profile || data?.user || data || {}; } function asSettings(data) { return data?.settings || data?.notifications || data || {}; } function formatDateTime(value) { if (!value) return 'Not recorded'; const d = new Date(value); if (Number.isNaN(d.getTime())) return String(value); return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' }) + ' ' + d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); } function SectionCard({ title, icon: Icon, subtitle, children }) { return (

{title}

{subtitle &&

{subtitle}

}
{children}
); } function FieldRow({ label, value }) { return (

{label}

{value || 'Not set'}

); } function CheckRow({ id, label, checked, onChange, disabled }) { return ( ); } function ProfileSummary({ profile, loading }) { if (loading) { return (
Loading profile…
); } return (
); } function EditProfile({ profile, onSaved }) { const [displayName, setDisplayName] = useState(profile.display_name || profile.displayName || ''); const [saving, setSaving] = useState(false); useEffect(() => { setDisplayName(profile.display_name || profile.displayName || ''); }, [profile.display_name, profile.displayName]); const save = async () => { setSaving(true); try { const data = await api.updateProfile({ display_name: displayName.trim() || null }); toast.success('Profile saved.'); onSaved(asProfile(data)); } catch (err) { toast.error(err.message || 'Failed to save profile.'); } finally { setSaving(false); } }; return (
setDisplayName(e.target.value)} placeholder="Display name" />
); } function NotificationPreferences({ settings, onSaved }) { const [form, setForm] = useState(settings); const [saving, setSaving] = useState(false); useEffect(() => setForm(settings), [settings]); const set = (k, v) => setForm(prev => ({ ...prev, [k]: v })); const payload = { email: form.email || '', notifications_enabled: !!(form.notifications_enabled ?? form.enabled), notify_3_day: !!(form.notify_3_day ?? form.notify_3d), notify_1_day: !!(form.notify_1_day ?? form.notify_1d), notify_due: !!(form.notify_due ?? form.notify_day_of), notify_overdue: !!(form.notify_overdue ?? form.notify_daily_overdue), }; payload.enabled = payload.notifications_enabled; payload.notify_3d = payload.notify_3_day; payload.notify_1d = payload.notify_1_day; payload.notify_day_of = payload.notify_due; payload.notify_daily_overdue = payload.notify_overdue; const save = async () => { setSaving(true); try { const data = await api.updateProfileSettings(payload); toast.success('Notification preferences saved.'); onSaved(asSettings(data)); } catch (err) { toast.error(err.message || 'Failed to save notification preferences.'); } finally { setSaving(false); } }; return (
set('email', e.target.value)} placeholder="you@example.com" />
set('notifications_enabled', v)} /> set('notify_3_day', v)} disabled={!payload.notifications_enabled} /> set('notify_1_day', v)} disabled={!payload.notifications_enabled} /> set('notify_due', v)} disabled={!payload.notifications_enabled} /> set('notify_overdue', v)} disabled={!payload.notifications_enabled} />
); } function ChangePassword() { const [currentPassword, setCurrentPassword] = useState(''); const [newPassword, setNewPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); const [saving, setSaving] = useState(false); const reset = () => { setCurrentPassword(''); setNewPassword(''); setConfirmPassword(''); }; const submit = async (e) => { e.preventDefault(); if (!currentPassword || !newPassword || !confirmPassword) { toast.error('All password fields are required.'); return; } if (newPassword !== confirmPassword) { toast.error('New passwords do not match.'); return; } setSaving(true); try { await api.changeProfilePassword({ current_password: currentPassword, new_password: newPassword, confirm_new_password: confirmPassword, }); reset(); toast.success('Password changed.'); } catch (err) { toast.error(err.message || 'Failed to change password.'); } finally { setSaving(false); } }; return (
setCurrentPassword(e.target.value)} />
setNewPassword(e.target.value)} />
setConfirmPassword(e.target.value)} />
); } function ProfileNav() { const items = [ ['#account', 'Account'], ['#security', 'Security'], ['#notifications', 'Notifications'], ['#data', 'My Data'], ]; return (
{items.map(([href, label]) => ( {label} ))}
); } function DataManagement() { const [history, setHistory] = useState(null); const [historyLoading, setHistoryLoading] = useState(true); const loadHistory = async () => { setHistoryLoading(true); try { const { history } = await api.importHistory(); setHistory(history); } catch { setHistory([]); } finally { setHistoryLoading(false); } }; useEffect(() => { loadHistory(); }, []); return (

My Data

Import spreadsheet history, import your SQLite data export, and export your user-owned records.

); } export default function ProfilePage() { const { setUser, refresh } = useAuth(); const [profile, setProfile] = useState({}); const [settings, setSettings] = useState({}); const [loading, setLoading] = useState(true); useEffect(() => { let mounted = true; Promise.all([ api.profile(), api.profileSettings(), ]) .then(([profileData, settingsData]) => { if (!mounted) return; setProfile(asProfile(profileData)); setSettings(asSettings(settingsData)); }) .catch(err => toast.error(err.message || 'Failed to load profile.')) .finally(() => mounted && setLoading(false)); return () => { mounted = false; }; }, []); const handleProfileSaved = (nextProfile) => { setProfile(prev => ({ ...prev, ...nextProfile })); setUser(prev => prev ? { ...prev, ...nextProfile } : prev); refresh(); }; return (

Profile

Manage your account, notifications, password, exports, and import history.

User-owned data only
{!loading && }
{!loading && }
); }