Files
AfiS-Aktensystem/web/app/(app)/personalakten/page.tsx
T
Harlekin e0b03ea336
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
Gedankenstriche durch Bindestriche ersetzt
375 Vorkommen in 210 Dateien. Betrifft Kommentare, Oberflaechentexte und die
README; an keiner Stelle Bedeutung, nur Zeichensetzung.
2026-09-04 08:54:52 +02:00

111 lines
4.0 KiB
TypeScript

import type { Metadata } from 'next'
import Link from 'next/link'
import { db } from '@/lib/db'
import { requirePermission } from '@/lib/auth/current'
import { listPersonnelFiles, listStatusTags } from '@/lib/personnel/service'
import { de } from '@/lib/i18n/de'
import { StampMark, classificationEdgeStyle } from '@/components/StampMark'
import { StatusTags } from '@/components/StatusTags'
export const metadata: Metadata = { title: de.personnel.listHeading }
export const dynamic = 'force-dynamic'
/**
* Liste der Personalakten.
*
* Wie bei den Fallakten: Suche, Sichtbarkeit und Zählung laufen über dieselbe
* Abfrage. Eine Akte über der Stufe des Lesers taucht weder auf noch in der
* Zahl daneben.
*/
export default async function PersonnelListPage({
searchParams,
}: {
searchParams: Promise<{ q?: string; status?: string }>
}) {
// Ohne das Recht gibt es die Seite nicht - 404, nicht 403.
const user = await requirePermission('personnel.read')
const { q, status } = await searchParams
const [{ files, total }, levels, statusTags] = await Promise.all([
listPersonnelFiles(user.permissions, { query: q, statusTag: status }),
db.clearanceLevelConfig.findMany({ orderBy: { level: 'asc' } }),
listStatusTags(),
])
const levelName = new Map(levels.map((l) => [l.level, l.name]))
const darfAnlegen =
user.permissions.isAdmin || user.permissions.permissions.has('personnel.edit')
return (
<main className="mx-auto max-w-5xl px-6 py-10">
<div className="flex flex-wrap items-baseline justify-between gap-4">
<h1 className="font-condensed">{de.personnel.listHeading}</h1>
<span className="font-mono text-xs text-text-dim">{de.personnel.countLabel(total)}</span>
</div>
<form method="get" className="mt-4 flex flex-wrap items-center gap-3">
<input
type="search"
name="q"
defaultValue={q ?? ''}
placeholder={de.personnel.search}
aria-label={de.personnel.search}
className="w-full max-w-sm field"
/>
{status ? <input type="hidden" name="status" value={status} /> : null}
{darfAnlegen ? (
<Link href="/personalakten/neu" className="ml-auto btn btn-sm btn-primary">
{de.personnel.create}
</Link>
) : null}
</form>
<nav className="mt-4 flex flex-wrap gap-2">
<Link
href={q ? `/personalakten?q=${encodeURIComponent(q)}` : '/personalakten'}
aria-current={status ? undefined : 'page'}
className="btn btn-sm"
>
{de.personnel.filterAll}
</Link>
{statusTags.map((tag) => (
<Link
key={tag.key}
href={`/personalakten?${new URLSearchParams({ ...(q ? { q } : {}), status: tag.key })}`}
aria-current={tag.key === status ? 'page' : undefined}
className="btn btn-sm"
>
{tag.name}
</Link>
))}
</nav>
{files.length === 0 ? (
<p className="mt-10 text-text-dim">
{q ? de.personnel.emptySearch : de.personnel.empty}
</p>
) : (
<ul className="mt-6 border-t border-line">
{files.map((akte) => (
<li key={akte.id} className="border-b border-line">
<Link
href={`/personalakten/${akte.id}`}
prefetch
className="record-row py-3 pl-3 text-text no-underline"
style={classificationEdgeStyle(akte.classification)}
>
<span className="record-rank">{akte.rank ? `[${akte.rank}]` : ''}</span>
<span className="record-title">{akte.displayName}</span>
<StatusTags tags={akte.statusTags} known={statusTags} />
<StampMark
level={akte.classification}
name={levelName.get(akte.classification) ?? de.common.unknown}
/>
</Link>
</li>
))}
</ul>
)}
</main>
)
}