dynamic plugin system, toast notifications, board delete, gitea-sync plugin rewrite, granular locking fixes
This commit is contained in:
@@ -3,6 +3,9 @@ import { Link } from 'react-router-dom'
|
||||
import { api } from '../../lib/api'
|
||||
import { useDocumentTitle } from '../../hooks/useDocumentTitle'
|
||||
import { useFocusTrap } from '../../hooks/useFocusTrap'
|
||||
import { useAdmin } from '../../hooks/useAdmin'
|
||||
import { useConfirm } from '../../hooks/useConfirm'
|
||||
import { useToast } from '../../hooks/useToast'
|
||||
import Dropdown from '../../components/Dropdown'
|
||||
import NumberInput from '../../components/NumberInput'
|
||||
import IconPicker from '../../components/IconPicker'
|
||||
@@ -26,6 +29,9 @@ interface Board {
|
||||
|
||||
export default function AdminBoards() {
|
||||
useDocumentTitle('Manage Boards')
|
||||
const { isSuperAdmin } = useAdmin()
|
||||
const confirm = useConfirm()
|
||||
const toast = useToast()
|
||||
const [boards, setBoards] = useState<Board[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [editBoard, setEditBoard] = useState<Board | null>(null)
|
||||
@@ -84,12 +90,16 @@ export default function AdminBoards() {
|
||||
try {
|
||||
if (editBoard) {
|
||||
await api.put(`/admin/boards/${editBoard.id}`, form)
|
||||
toast.success('Board updated')
|
||||
} else {
|
||||
await api.post('/admin/boards', form)
|
||||
toast.success('Board created')
|
||||
}
|
||||
resetForm()
|
||||
fetchBoards()
|
||||
} catch {} finally {
|
||||
} catch {
|
||||
toast.error('Failed to save board')
|
||||
} finally {
|
||||
setSaving(false)
|
||||
}
|
||||
}
|
||||
@@ -98,7 +108,24 @@ export default function AdminBoards() {
|
||||
try {
|
||||
await api.put(`/admin/boards/${id}`, { isArchived: !isArchived })
|
||||
fetchBoards()
|
||||
} catch {}
|
||||
toast.info(isArchived ? 'Board restored' : 'Board archived')
|
||||
} catch {
|
||||
toast.error('Failed to update board')
|
||||
}
|
||||
}
|
||||
|
||||
const handleDelete = async (board: Board) => {
|
||||
const ok = await confirm(
|
||||
`Permanently delete "${board.name}" and all its posts, comments, and votes? This cannot be undone.`
|
||||
)
|
||||
if (!ok) return
|
||||
try {
|
||||
await api.delete(`/admin/boards/${board.id}`)
|
||||
fetchBoards()
|
||||
toast.success('Board deleted')
|
||||
} catch {
|
||||
toast.error('Failed to delete board')
|
||||
}
|
||||
}
|
||||
|
||||
const slugify = (s: string) => s.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '')
|
||||
@@ -165,6 +192,15 @@ export default function AdminBoards() {
|
||||
>
|
||||
{board.isArchived ? 'Restore' : 'Archive'}
|
||||
</button>
|
||||
{isSuperAdmin && (
|
||||
<button
|
||||
onClick={() => handleDelete(board)}
|
||||
className="action-btn text-xs px-2"
|
||||
style={{ minHeight: 44, color: 'var(--error)' }}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
@@ -291,7 +327,7 @@ export default function AdminBoards() {
|
||||
onChange={(v) => setForm((f) => ({ ...f, rssFeedCount: v }))}
|
||||
min={1}
|
||||
max={200}
|
||||
style={{ width: 100 }}
|
||||
style={{ minWidth: 120 }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user