Files
AfiS-Aktensystem/web/app/(app)/admin/rollen/page.tsx
T
HarlekinandClaude Opus 5 9c966b471e Tests schreiben nicht mehr in die Entwicklungsdatenbank
Die Testläufe hatten dort 70 erfundene Rollen, 112 Nutzer und 40 Akten
hinterlassen. Die Rollenverwaltung zeigte deshalb "Gast" siebzehnmal, echte
Rollen doppelt und in willkürlicher Reihenfolge.

Aufräumen allein reicht nicht: CaseEntry, CaseEntryVersion und AuditLog lassen
sich nach harter Regel 2 nicht löschen. Was ein Test dort anlegt, bleibt für
immer stehen — in Phase 2 hätte das die Fallliste unbrauchbar gemacht.

Tests laufen deshalb gegen <datenbank>_test auf demselben Server. pnpm test legt
sie an, migriert und seedet sie vorher. Über TEST_DATABASE_URL lässt sich ein
anderes Ziel setzen.

Dazu die Abschnitte der Rollenverwaltung in der Reihenfolge, in der die
Abteilung ihre Rollen aufzählt: Ränge, Zugehörigkeit, Zugang. Innerhalb eines
Abschnitts entscheidet die Position auf dem Discord-Server, oben die höchste.
Damit steht die Hierarchie an einer Stelle, die ohnehin gepflegt wird.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 21:11:40 +02:00

79 lines
2.9 KiB
TypeScript

import type { Metadata } from 'next'
import { db } from '@/lib/db'
import { requirePermission } from '@/lib/auth/current'
import { de } from '@/lib/i18n/de'
import { RoleRow } from './RoleRow'
import { ClearancePreview } from './ClearancePreview'
export const metadata: Metadata = { title: de.roleAdmin.heading }
export const dynamic = 'force-dynamic'
/**
* Reihenfolge der Abschnitte: erst die Ränge, dann die Zugehörigkeit, dann die
* Zugangsrollen — so, wie die Abteilung sie selbst aufzählt. Innerhalb eines
* Abschnitts entscheidet die Position der Rolle auf dem Discord-Server, oben
* die höchste. Damit steht die Hierarchie an einer Stelle, die ohnehin gepflegt
* wird, statt in einer zweiten Sortierung in dieser Anwendung.
*/
const ORDER = ['RANK', 'MEMBERSHIP', 'CLEARANCE', 'UNSORTED'] as const
export default async function RoleAdminPage() {
// Ohne roles.manage gibt es 404, nicht 403.
await requirePermission('roles.manage')
const [mappings, levels] = await Promise.all([
db.roleMapping.findMany({ orderBy: [{ sortOrder: 'desc' }, { label: 'asc' }] }),
db.clearanceLevelConfig.findMany({ orderBy: { level: 'asc' } }),
])
return (
<main className="mx-auto max-w-5xl px-6 py-10">
<h1 className="font-condensed">{de.roleAdmin.heading}</h1>
<p className="mt-2 max-w-(--container-measure) text-text-dim">{de.roleAdmin.intro}</p>
{mappings.length === 0 ? (
<p className="mt-8 text-text-dim">{de.roleAdmin.empty}</p>
) : (
<>
<ClearancePreview
levels={levels}
mappings={mappings.map((m) => ({
label: m.label,
clearanceLevel: m.clearanceLevel,
}))}
/>
{ORDER.map((category) => {
const rows = mappings.filter((m) => m.category === category)
if (rows.length === 0) return null
return (
<section key={category} className="mt-10">
<h2 className="font-condensed text-text-dim">
{de.roleAdmin.categories[category]}
</h2>
<div className="mt-2 border-t border-line">
{rows.map((role) => (
<RoleRow
key={role.discordRoleId}
role={{
discordRoleId: role.discordRoleId,
label: role.label,
clearanceLevel: role.clearanceLevel,
grantsMembership: role.grantsMembership,
category: role.category,
permissions: role.permissions,
isPresent: role.isPresent,
}}
levels={levels.map((l) => ({ level: l.level, name: l.name }))}
/>
))}
</div>
</section>
)
})}
</>
)}
</main>
)
}