UI: portal/header refinements, board index UX, and user settings

This commit is contained in:
Micha
2026-01-01 19:54:02 +01:00
parent f83748cc76
commit 8604cdf95d
26 changed files with 2065 additions and 227 deletions

View File

@@ -12,6 +12,7 @@ export default function Acp({ isAdmin }) {
const [selectedId, setSelectedId] = useState(null)
const [draggingId, setDraggingId] = useState(null)
const [overId, setOverId] = useState(null)
const pendingOrder = useRef(null)
const [createType, setCreateType] = useState(null)
const [users, setUsers] = useState([])
const [usersLoading, setUsersLoading] = useState(false)
@@ -232,7 +233,7 @@ export default function Acp({ isAdmin }) {
setError('')
try {
const data = await listAllForums()
setForums(data)
setForums(data.filter((forum) => !forum.deleted_at))
} catch (err) {
setError(err.message)
} finally {
@@ -394,6 +395,18 @@ export default function Acp({ isAdmin }) {
})
}
const handleStartCreateChild = (type, parentId) => {
setSelectedId(null)
setShowModal(true)
setCreateType(type)
setForm({
name: '',
description: '',
type,
parentId: parentId ? String(parentId) : '',
})
}
const handleSubmit = async (event) => {
event.preventDefault()
setError('')
@@ -402,6 +415,10 @@ export default function Acp({ isAdmin }) {
setError(t('acp.forums_name_required'))
return
}
if (form.type === 'forum' && !form.parentId) {
setError(t('acp.forums_parent_required'))
return
}
try {
if (selectedId) {
await updateForum(selectedId, {
@@ -448,6 +465,11 @@ export default function Acp({ isAdmin }) {
}
const handleDragEnd = () => {
if (pendingOrder.current) {
const { parentId, ordered } = pendingOrder.current
pendingOrder.current = null
reorderForums(parentId, ordered).catch((err) => setError(err.message))
}
setDraggingId(null)
setOverId(null)
}
@@ -504,6 +526,7 @@ export default function Acp({ isAdmin }) {
ordered.splice(toIndex, 0, ordered.splice(fromIndex, 1)[0])
setOverId(String(targetId))
applyLocalOrder(parentId, ordered)
pendingOrder.current = { parentId, ordered }
}
const handleDragEnter = (forumId) => {
@@ -550,6 +573,7 @@ export default function Acp({ isAdmin }) {
}
ordered.splice(toIndex, 0, ordered.splice(fromIndex, 1)[0])
pendingOrder.current = null
try {
await reorderForums(parentId, ordered)
@@ -622,6 +646,24 @@ export default function Acp({ isAdmin }) {
<i className="bi bi-arrow-down-up" aria-hidden="true" />
</span>
<ButtonGroup size="sm" className="bb-action-group">
{node.type === 'category' && (
<>
<Button
variant="dark"
onClick={() => handleStartCreateChild('category', node.id)}
title={t('acp.add_category')}
>
<i className="bi bi-folder-plus" aria-hidden="true" />
</Button>
<Button
variant="dark"
onClick={() => handleStartCreateChild('forum', node.id)}
title={t('acp.add_forum')}
>
<i className="bi bi-chat-left-text" aria-hidden="true" />
</Button>
</>
)}
<Button variant="dark" onClick={() => handleSelectForum(node)} title={t('acp.edit')}>
<i className="bi bi-pencil" aria-hidden="true" />
</Button>
@@ -793,7 +835,9 @@ export default function Acp({ isAdmin }) {
value={form.parentId}
onChange={(event) => setForm({ ...form, parentId: event.target.value })}
>
<option value="">{t('acp.forums_parent_root')}</option>
<option value="" disabled={form.type === 'forum'}>
{t('acp.forums_parent_root')}
</option>
{categoryOptions
.filter((option) => String(option.id) !== String(selectedId))
.map((option) => (