import { useEffect, useState } from 'react' import { useI18n } from '../i18n' import { Callout, Spinner } from './ui' import { ApiRequestError } from '../api/client' import { deleteProfile, profileStatus, setConsent, type ConsentPurpose, type DeleteProfileResult, type ProfileStatus, } from '../api/ai' /** Customer privacy/consent controls (PR C4). Advisory only — these choices never approve, deny or * change a payment. Required security processing is shown as informational (not a consent toggle). * Identity is the authenticated principal (session token); no user id is sent. */ export function ConsentControls() { const { t } = useI18n() const [status, setStatus] = useState(null) const [loading, setLoading] = useState(true) const [err, setErr] = useState(false) const [busy, setBusy] = useState(null) const [notice, setNotice] = useState<{ tone: 'ok' | 'warn'; msg: string } | null>(null) const [deleted, setDeleted] = useState(null) async function refresh(signal?: AbortSignal) { setLoading(true) try { setStatus(await profileStatus(signal)) } catch { setErr(true) } finally { setLoading(false) } } useEffect(() => { const c = new AbortController() refresh(c.signal) return () => c.abort() }, []) async function toggle(purpose: ConsentPurpose, grant: boolean) { if (!status) return setBusy(purpose); setNotice(null) try { const next = await setConsent(purpose, grant, status.record_version) setStatus(next); setNotice({ tone: 'ok', msg: t('consent.saved') }) } catch (e) { if (e instanceof ApiRequestError && e.status === 409) { await refresh(); setNotice({ tone: 'warn', msg: t('consent.conflict') }) } else { setErr(true) } } finally { setBusy(null) } } async function onDelete() { if (!window.confirm(t('consent.delete.confirm'))) return setBusy('delete'); setNotice(null) try { const res = await deleteProfile() setDeleted(res) await refresh() setNotice({ tone: 'ok', msg: t('consent.delete.done') }) } catch { setErr(true) } finally { setBusy(null) } } if (loading && !status) return if (err && !status) return {t('home.security.unavailable')} if (!status) return null const optionalRow = (purpose: ConsentPurpose, titleKey: string, explainKey: string) => { const meta = status.purposes[purpose] const granted = meta.status === 'granted' return (
{t(titleKey as never)}

{t(explainKey as never)}

{granted ? t('consent.on') : t('consent.off')}
) } return (
{/* Required security processing — informational, NOT a consent toggle, not "granted". */}
{t('required.title')} {t('required.badge')}

{t('required.explain')}

{t('required.state')}
{optionalRow('optional_personalization', 'consent.personalization.title', 'consent.personalization.explain')} {optionalRow('optional_federation', 'consent.federation.title', 'consent.federation.explain')} {status.personalization_without_shared_learning && ( {t('consent.pwsl')} )} {notice && {notice.msg}}
{t('consent.delete.title')}

{t('consent.delete.explain')}

{deleted && (

{t('consent.delete.deletedLabel')}: {deleted.deleted_categories.join(', ')}

{t('consent.delete.retainedLabel')}: {deleted.retained_categories.join(', ')}

{t('consent.delete.notUnlearning')}

)}
) }