Prüfung / Lint, Typen, Tests (push) Canceled after 0s
Prüfung / Images bauen (pdf) (push) Canceled after 0s
Prüfung / Images bauen (web) (push) Canceled after 0s
Prüfung / Images bauen (worker) (push) Canceled after 0s
Images / bauen (pdf, docker/pdf.Dockerfile) (push) Canceled after 0s
Images / bauen (web, docker/web.Dockerfile) (push) Canceled after 0s
Images / bauen (worker, docker/worker.Dockerfile) (push) Canceled after 0s
375 Vorkommen in 210 Dateien. Betrifft Kommentare, Oberflaechentexte und die README; an keiner Stelle Bedeutung, nur Zeichensetzung.
79 lines
2.8 KiB
TypeScript
79 lines
2.8 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 (
|
|
<section>
|
|
<h2 className="font-condensed">{de.roleAdmin.heading}</h2>
|
|
<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>
|
|
)
|
|
})}
|
|
</>
|
|
)}
|
|
</section>
|
|
)
|
|
}
|