import { useEffect, useRef, useState } from 'react'
import { api } from '../api.js'
import { THEMES, useAuth } from '../auth.js'
import { dateTime } from '../format.js'
import { useT } from '../i18n.js'
import { Avatar, ErrorNote, Field, Modal } from '../components/ui.jsx'
export default function Profile() {
const t = useT()
const { user, setUser, setLang, theme, setTheme } = useAuth()
const [name, setName] = useState(user.name)
const [current, setCurrent] = useState('')
const [next, setNext] = useState('')
const [error, setError] = useState(null)
const [done, setDone] = useState(false)
const submit = async (e) => {
e.preventDefault()
try {
const body = { name }
if (next) Object.assign(body, { current_password: current, new_password: next })
setUser(await api.put('/api/auth/me', body))
setCurrent('')
setNext('')
setError(null)
setDone(true)
} catch (err) { setError(err); setDone(false) }
}
return (
)
}
function AvatarPicker() {
const t = useT()
const { user, setUser } = useAuth()
const input = useRef(null)
const [error, setError] = useState(null)
const upload = async (e) => {
const file = e.target.files[0]
e.target.value = ''
if (!file) return
const form = new FormData()
form.append('file', file)
try { setUser(await api.put('/api/auth/me/avatar', form)); setError(null) } catch (err) { setError(err) }
}
const remove = async () => {
try { setUser(await api.del('/api/auth/me/avatar')); setError(null) } catch (err) { setError(err) }
}
return (
)
}
function MfaSection() {
const t = useT()
const { user, lang } = useAuth()
const [dialog, setDialog] = useState(null) // setup | disable | codes
return (
{t('mfa')} {user.mfa_enabled ? t('mfa_on') : t('mfa_off')}
{user.mfa_enabled ? <>
{t('mfa_on_since', { when: dateTime(user.mfa_enabled_at, lang), n: user.mfa_recovery_left })}
> : <>
{t('mfa_off_text')}
>}
{dialog === 'setup' &&
setDialog(null)} />}
{dialog === 'disable' && setDialog(null)} />}
{dialog === 'codes' && setDialog(null)} />}
)
}
function RecoveryCodes({ codes, onDone }) {
const t = useT()
const [copied, setCopied] = useState(false)
const copy = () => navigator.clipboard?.writeText(codes.join('\n')).then(() => setCopied(true)).catch(() => {})
return <>
{t('mfa_codes_text')}
{codes.map((c) => {c})}
>
}
function MfaSetup({ onClose }) {
const t = useT()
const { setUser } = useAuth()
const [setup, setSetup] = useState(null)
const [code, setCode] = useState('')
const [codes, setCodes] = useState(null)
const [error, setError] = useState(null)
useEffect(() => { api.post('/api/auth/mfa/setup').then(setSetup).catch(setError) }, [])
const confirm = async (e) => {
e.preventDefault()
try {
const r = await api.post('/api/auth/mfa/enable', { code })
setCodes(r.recovery_codes)
setUser(r.user)
} catch (err) { setError(err); setCode('') }
}
if (codes) return
return (
{setup && }
{!setup && }
)
}
function MfaDisable({ onClose }) {
const t = useT()
const { setUser } = useAuth()
const [password, setPassword] = useState('')
const [code, setCode] = useState('')
const [error, setError] = useState(null)
const submit = async (e) => {
e.preventDefault()
try { setUser(await api.post('/api/auth/mfa/disable', { password, code })); onClose() } catch (err) { setError(err) }
}
return (
)
}
function MfaNewCodes({ onClose }) {
const t = useT()
const { setUser } = useAuth()
const [code, setCode] = useState('')
const [codes, setCodes] = useState(null)
const [error, setError] = useState(null)
const submit = async (e) => {
e.preventDefault()
try {
const r = await api.post('/api/auth/mfa/recovery-codes', { code })
setCodes(r.recovery_codes)
setUser(r.user)
} catch (err) { setError(err); setCode('') }
}
return (
{codes ? : (
)}
)
}