Frontend update
This commit is contained in:
91
client/src/routes/admin/views/SettingsAdmin.jsx
Normal file
91
client/src/routes/admin/views/SettingsAdmin.jsx
Normal file
@@ -0,0 +1,91 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Loading, ErrorState } from '../../../components/PageState.jsx'
|
||||
import { api } from '../../../api/client.js'
|
||||
import { useSite } from '../../../contexts/SiteContext.jsx'
|
||||
|
||||
// Editable settings shown on this screen (key -> label + control type).
|
||||
const FIELDS = [
|
||||
{ key: 'site_title', label: 'Site title' },
|
||||
{ key: 'homepage_teaser', label: 'Homepage teaser', long: true },
|
||||
{ key: 'maintenance_message', label: 'Maintenance message', long: true },
|
||||
{ key: 'status_message', label: 'Status message' },
|
||||
{ key: 'contact_email', label: 'Contact email' },
|
||||
]
|
||||
|
||||
export default function SettingsAdmin() {
|
||||
const { refresh: refreshSite } = useSite()
|
||||
const [values, setValues] = useState(null)
|
||||
const [initial, setInitial] = useState({})
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState('')
|
||||
const [busy, setBusy] = useState(false)
|
||||
const [saved, setSaved] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
let active = true
|
||||
api.admin
|
||||
.getSettings()
|
||||
.then((all) => {
|
||||
if (!active) return
|
||||
const v = {}
|
||||
FIELDS.forEach((f) => (v[f.key] = all[f.key] ?? ''))
|
||||
setValues(v)
|
||||
setInitial(v)
|
||||
})
|
||||
.catch(() => active && setError('Could not load settings.'))
|
||||
.finally(() => active && setLoading(false))
|
||||
return () => {
|
||||
active = false
|
||||
}
|
||||
}, [])
|
||||
|
||||
if (loading) return <Loading />
|
||||
if (error) return <ErrorState message={error} />
|
||||
|
||||
const set = (k) => (e) => {
|
||||
setValues((v) => ({ ...v, [k]: e.target.value }))
|
||||
setSaved(false)
|
||||
}
|
||||
|
||||
async function save() {
|
||||
setBusy(true)
|
||||
setError('')
|
||||
try {
|
||||
await api.admin.updateSettings(values)
|
||||
setInitial(values)
|
||||
setSaved(true)
|
||||
await refreshSite()
|
||||
} catch (err) {
|
||||
setError(err.message || 'Could not save settings.')
|
||||
} finally {
|
||||
setBusy(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<section style={{ maxWidth: 620 }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
|
||||
{FIELDS.map((f) => (
|
||||
<label key={f.key} style={{ display: 'block' }}>
|
||||
<span className="field-label">{f.label}</span>
|
||||
{f.long ? (
|
||||
<textarea value={values[f.key]} onChange={set(f.key)} className="textarea" style={{ minHeight: 90 }} />
|
||||
) : (
|
||||
<input type="text" value={values[f.key]} onChange={set(f.key)} className="input" />
|
||||
)}
|
||||
</label>
|
||||
))}
|
||||
<div style={{ display: 'flex', gap: 10, marginTop: 6, alignItems: 'center' }}>
|
||||
<button onClick={save} disabled={busy} className="btn btn-primary btn-sq">
|
||||
{busy ? 'Saving…' : 'Save changes'}
|
||||
</button>
|
||||
<button onClick={() => setValues(initial)} disabled={busy} className="pill">
|
||||
Reset
|
||||
</button>
|
||||
{saved && <span className="sans" style={{ color: '#7fd0a4', fontSize: '0.85rem' }}>Saved.</span>}
|
||||
{error && <span className="sans" style={{ color: '#d98b84', fontSize: '0.85rem' }}>{error}</span>}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user