'use client' import { useEffect, useState } from 'react' import { useRouter } from 'next/navigation' import { authFetch, getApiBase } from '../lib/auth' import '../email-recaps/recaps.css' type Preference = { automatic_monthly: boolean; state: 'off' | 'pending' | 'expired' | 'enabled'; email: string | null; can_subscribe: boolean; detail: string; schedule_enabled: boolean; next_send_at: number | null; day: number; hour: number; resend_after: number | null } const scheduled = (value: number) => `${new Date(value * 1000).toLocaleString(undefined, { dateStyle: 'long', timeStyle: 'short', timeZone: 'UTC' })} UTC` export default function MonthlyRecapPreference() { const router = useRouter() const [data, setData] = useState(null) const [automatic, setAutomatic] = useState(false) const [busy, setBusy] = useState(false) const [error, setError] = useState('') const [notice, setNotice] = useState('') const [revision, setRevision] = useState(0) const [now, setNow] = useState(Date.now()) useEffect(() => { const abort = new AbortController() setError('') void authFetch(`${getApiBase()}/profile/email-recaps`, { signal: abort.signal }).then(async (response) => { if (response.status === 401) { router.replace('/login?next=%2Fprofile'); return } if (!response.ok) throw new Error('Could not load your email preference. Please try again.') const result = await response.json() as Preference if (!abort.signal.aborted) { setData(result); setAutomatic(result.automatic_monthly) } }).catch((err: Error) => { if (!abort.signal.aborted) setError(err.message) }) return () => abort.abort() }, [revision, router]) useEffect(() => { if (!data?.resend_after || data.state === 'enabled' || data.resend_after * 1000 <= Date.now()) return const timer = window.setInterval(() => setNow(Date.now()), 1000) return () => window.clearInterval(timer) }, [data?.resend_after, data?.state]) const save = async (enabled: boolean, monthly = automatic) => { if (busy) return setBusy(true); setError(''); setNotice('') try { const response = await authFetch(`${getApiBase()}/profile/email-recaps`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ enabled, automatic_monthly: monthly }), }) if (response.status === 401) { router.replace('/login?next=%2Fprofile'); return } const result = await response.json().catch(() => ({})) if (!response.ok) throw new Error(typeof result.detail === 'string' ? result.detail : 'Could not update your email preference.') setData(result); setAutomatic(result.automatic_monthly); setNow(Date.now()) setNotice(result.message || (enabled ? 'Personal report emails are enabled.' : 'Personal report emails are off.')) } catch (err) { setError(err instanceof Error ? err.message : 'Could not update your email preference.') // A confirmation may be pending even if SMTP could not confirm delivery. const response = await authFetch(`${getApiBase()}/profile/email-recaps`).catch(() => null) if (response?.ok) { const fresh = await response.json(); setData(fresh); setAutomatic(fresh.automatic_monthly); setNow(Date.now()) } } finally { setBusy(false) } } const cooldown = data?.resend_after ? Math.max(0, Math.ceil(data.resend_after - now / 1000)) : 0 return
A little look back

Your reports, your choice.

{data && {({ off: 'Off', pending: 'Check your inbox', expired: 'Confirmation expired', enabled: 'Email confirmed' })[data.state]}}

Email yourself your viewing report whenever you want. Choose a previous month or the current month so far, and decide whether you also want automatic monthly emails. Explore your latest report ↗

{!data && !error &&

Loading your email preference…

} {data && <> {data.state === 'enabled' ?

Recaps will go to {data.email}. {!data.automatic_monthly ? 'On demand only: choose a month in Reports and email it whenever you want.' : data.schedule_enabled && data.next_send_at ? `Next scheduled send: ${scheduled(data.next_send_at)}.` : 'The administrator has paused scheduled delivery.'}

:

{data.state === 'pending' ? `Check ${data.email} for a confirmation link. It expires after 24 hours. No viewing history is emailed until you confirm.` : data.state === 'expired' ? 'Request a new confirmation link to turn on your recaps.' : 'Confirm your profile email to turn this on. You can unsubscribe from any recap or here in Profile.'}

} {!data.can_subscribe && data.state !== 'enabled' &&

{data.detail}

} {data.state !== 'enabled' && data.can_subscribe && automatic && !data.schedule_enabled &&

You can subscribe now. Monthly sends will begin when your administrator starts the schedule.

}
{data.state !== 'enabled' && } {data.state !== 'off' && }
{cooldown > 0 && data.state !== 'enabled' &&

Another confirmation can be requested in {Math.ceil(cooldown / 60)} {Math.ceil(cooldown / 60) === 1 ? 'minute' : 'minutes'}.

} } {error &&

{error}{!data && }

} {notice &&

{notice}

}
}