Files
planer/src/app/(app)/verwaltung/api/page.tsx
T
Florian WildeandClaude Opus 5 7919149879 fix: Navigation aufteilen, statt sie weiter zu dehnen
Mit dem dreizehnten Eintrag verschluckte die Leiste erneut Punkte am
rechten Rand — nach M6 zum zweiten Mal. Das Problem ist nicht die Breite,
sondern die Vermischung: Einrichtung und Verwaltung sucht man nicht beim
Arbeiten. Alltag bleibt in der Leiste, alles andere liegt hinter "Mehr".

Ein <details> statt eines Menues aus Skript: Es klappt ohne JavaScript
auf und braucht keine Zustandsverwaltung.

Zurueckgezogene Tokens stehen jetzt unten. Was noch Zugang hat, ist das,
wonach man sucht.

Zwei E2E-Tests greifen den neuen Weg ueber "Mehr" auf. Ein dritter wartet
das Neuladen ab, bevor er navigiert — die Meldung kommt frueher als die
fertige Liste, und Chromium bricht die Navigation sonst ab.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 18:52:35 +02:00

80 lines
2.5 KiB
TypeScript

import type { Metadata } from 'next'
import { KeyRound } from 'lucide-react'
import { TokenVerwaltung } from '@/app/(app)/verwaltung/api/token-verwaltung'
import {
Card,
CardBeschreibung,
CardInhalt,
CardKopf,
CardTitel,
} from '@/components/ui/card'
import { rechteLesen } from '@/lib/api'
import { alsDatumUhrzeit } from '@/lib/zeit'
import { verlangeRolle } from '@/server/berechtigung'
import { db } from '@/server/db'
import { umgebung } from '@/server/umgebung'
export const metadata: Metadata = { title: 'API-Tokens' }
export default async function ApiSeite() {
await verlangeRolle('ADMIN', '/verwaltung/api')
const tokens = await db.apiToken.findMany({
// Gueltige zuerst, Zurueckgezogenes ans Ende: Was noch Zugang hat, ist
// das, wonach man sucht.
orderBy: [{ widerrufenAm: { sort: 'asc', nulls: 'first' } }, { erstelltAm: 'desc' }],
select: {
id: true,
name: true,
praefix: true,
rechte: true,
gueltigBis: true,
letzteNutzung: true,
widerrufenAm: true,
erstelltAm: true,
},
})
const adresse = umgebung().APP_URL
return (
<div className="flex flex-col gap-6">
<div className="flex items-center gap-3">
<KeyRound className="text-gedaempft-vordergrund size-6" aria-hidden />
<div>
<h1 className="text-2xl font-semibold tracking-tight">API-Tokens</h1>
<p className="text-gedaempft-vordergrund text-sm">
Zugänge für fremde Anwendungen — mit so wenig Rechten wie möglich.
</p>
</div>
</div>
<Card>
<CardKopf>
<CardTitel className="text-base">Tokens</CardTitel>
<CardBeschreibung>
Ein Token ist ein Passwort. Er wird beim Anlegen ein einziges Mal angezeigt;
danach liegt in der Datenbank nur noch sein Fingerabdruck. Wer ihn verliert,
legt einen neuen an — nachschlagen lässt er sich nicht.
</CardBeschreibung>
</CardKopf>
<CardInhalt>
<TokenVerwaltung
adresse={adresse}
tokens={tokens.map((t) => ({
id: t.id,
name: t.name,
praefix: t.praefix,
rechte: rechteLesen(t.rechte),
gueltigBis: t.gueltigBis ? alsDatumUhrzeit(t.gueltigBis) : null,
letzteNutzung: t.letzteNutzung ? alsDatumUhrzeit(t.letzteNutzung) : null,
widerrufen: t.widerrufenAm !== null,
erstelltAm: alsDatumUhrzeit(t.erstelltAm),
}))}
/>
</CardInhalt>
</Card>
</div>
)
}