import { useState, useEffect, useContext, useCallback } from 'react'
import { AuthContext } from '../App.jsx'
import {
updateProfile, changePassword,
listTokens, createToken, deleteToken,
getInstanceSettings, updateInstanceSettings,
} from '../api.js'
const ROLE_ADMIN = 4
export default function SettingsView() {
const { currentUser, setCurrentUser } = useContext(AuthContext) ?? {}
const isAdmin = currentUser && ((currentUser.role_bits & ROLE_ADMIN) !== 0)
const [tab, setTab] = useState('profile')
const tabs = ['profile', 'tokens', ...(isAdmin ? ['instance'] : [])]
const tabLabels = { profile: 'Profile', tokens: 'API Tokens', instance: 'Instance' }
return (
Settings
{tabs.map(t => (
))}
{tab === 'profile' && }
{tab === 'tokens' && }
{tab === 'instance' && isAdmin && }
)
}
function ProfileTab({ currentUser, setCurrentUser }) {
const [displayName, setDisplayName] = useState(currentUser?.display_name ?? '')
const [saving, setSaving] = useState(false)
const [saveMsg, setSaveMsg] = useState(null)
const [curPw, setCurPw] = useState('')
const [newPw, setNewPw] = useState('')
const [confirmPw, setConfirmPw] = useState('')
const [pwSaving, setPwSaving] = useState(false)
const [pwMsg, setPwMsg] = useState(null)
async function handleSaveProfile(e) {
e.preventDefault()
setSaving(true)
setSaveMsg(null)
try {
await updateProfile(displayName)
setCurrentUser(u => ({ ...u, display_name: displayName || null }))
setSaveMsg({ ok: true, text: 'Saved.' })
} catch (err) {
setSaveMsg({ ok: false, text: err.message })
} finally {
setSaving(false)
}
}
async function handleChangePassword(e) {
e.preventDefault()
if (newPw !== confirmPw) { setPwMsg({ ok: false, text: 'Passwords do not match.' }); return }
setPwSaving(true)
setPwMsg(null)
try {
await changePassword(curPw, newPw)
setCurPw(''); setNewPw(''); setConfirmPw('')
setPwMsg({ ok: true, text: 'Password changed.' })
} catch (err) {
setPwMsg({ ok: false, text: err.message })
} finally {
setPwSaving(false)
}
}
return (
)
}
function TokensTab() {
const [tokens, setTokens] = useState([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
const [newName, setNewName] = useState('')
const [creating, setCreating] = useState(false)
const [newToken, setNewToken] = useState(null)
const refresh = useCallback(async () => {
setLoading(true); setError(null)
try { setTokens(await listTokens()) }
catch (e) { setError(e.message) }
finally { setLoading(false) }
}, [])
useEffect(() => { refresh() }, [refresh])
async function handleCreate(e) {
e.preventDefault()
if (!newName.trim()) return
setCreating(true)
try {
const tok = await createToken(newName.trim())
setNewToken(tok)
setNewName('')
refresh()
} catch (err) {
setError(err.message)
} finally {
setCreating(false)
}
}
async function handleDelete(tokenUid) {
try {
await deleteToken(tokenUid)
setTokens(ts => ts.filter(t => t.token_uid !== tokenUid))
} catch (err) {
setError(err.message)
}
}
return (
API Tokens
{newToken && (
Token created. Copy it now — it won't be shown again.
{newToken.raw_token}
)}
{error &&
{error}
}
{loading ? (
Loading\u2026
) : (
{tokens.length === 0 &&
No tokens yet.
}
{tokens.map(tok => (
{tok.name}
Created {tok.created_at.slice(0, 10)}
{tok.last_used_at && ` \u00b7 Last used ${tok.last_used_at.slice(0, 10)}`}
))}
)}
)
}
function InstanceTab() {
const [settings, setSettings] = useState(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
const [saving, setSaving] = useState(false)
const [saveMsg, setSaveMsg] = useState(null)
useEffect(() => {
(async () => {
try { setSettings(await getInstanceSettings()) }
catch (e) { setError(e.message) }
finally { setLoading(false) }
})()
}, [])
async function handleSave(e) {
e.preventDefault()
setSaving(true); setSaveMsg(null)
try {
await updateInstanceSettings(settings)
setSaveMsg({ ok: true, text: 'Saved.' })
} catch (err) {
setSaveMsg({ ok: false, text: err.message })
} finally {
setSaving(false)
}
}
if (loading) return Loading\u2026
if (error) return {error}
if (!settings) return null
return (
)
}