Gedankenstriche durch Bindestriche ersetzt
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
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.
This commit is contained in:
@@ -2,7 +2,7 @@ import { Skeleton, SkeletonLine } from '@/components/Skeleton'
|
||||
|
||||
/**
|
||||
* Skelett statt Spinner: die Struktur steht sofort, die Daten fallen hinein.
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist —
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist -
|
||||
* man klickt und es passiert scheinbar nichts. Siehe docs/09-performance-und-ux.md.
|
||||
*/
|
||||
export default function Loading() {
|
||||
|
||||
@@ -39,7 +39,7 @@ export async function generateMetadata({
|
||||
*
|
||||
* Dasselbe Bild wie eine Personalakte, eine Stufe höher: wer gehört dazu, was
|
||||
* ist dazu aktenkundig, was ist dazu vermerkt, und worüber wird geredet. Der
|
||||
* Unterschied ist, dass hier **nichts eingetragen** wird — die Fälle ergeben
|
||||
* Unterschied ist, dass hier **nichts eingetragen** wird - die Fälle ergeben
|
||||
* sich aus den Beteiligten und deren Personalakten.
|
||||
*/
|
||||
export default async function DepartmentPage({ params }: { params: Promise<{ key: string }> }) {
|
||||
|
||||
@@ -14,7 +14,7 @@ export const dynamic = 'force-dynamic'
|
||||
*
|
||||
* Das Diagramm beantwortet die Frage, mit der man auf diese Seite kommt: zu
|
||||
* welcher Abteilung gibt es die meisten Fälle. Gezählt wird nur, was der
|
||||
* Fragende auch sehen darf — eine Zahl, die mehr zählt als seine Liste
|
||||
* Fragende auch sehen darf - eine Zahl, die mehr zählt als seine Liste
|
||||
* hergibt, wäre selbst eine Auskunft.
|
||||
*/
|
||||
export default async function DepartmentsPage() {
|
||||
|
||||
@@ -23,7 +23,7 @@ type Existing = {
|
||||
*
|
||||
* Das Kürzel steht nach dem Anlegen fest: es steckt in der Adresse jeder
|
||||
* Abteilungsseite, und ein wandernder Bezeichner macht geteilte Links wertlos.
|
||||
* Wer sich vertan hat, legt neu an und archiviert die alte — archivieren
|
||||
* Wer sich vertan hat, legt neu an und archiviert die alte - archivieren
|
||||
* statt löschen, weil an einer Abteilung Vermerke und Zuordnungen hängen.
|
||||
*/
|
||||
export function DepartmentForm({
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Skeleton, SkeletonLine } from '@/components/Skeleton'
|
||||
|
||||
/**
|
||||
* Skelett statt Spinner: die Struktur steht sofort, die Daten fallen hinein.
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist —
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist -
|
||||
* man klickt und es passiert scheinbar nichts. Siehe docs/09-performance-und-ux.md.
|
||||
*/
|
||||
export default function Loading() {
|
||||
|
||||
@@ -12,7 +12,7 @@ export const dynamic = 'force-dynamic'
|
||||
*
|
||||
* Wie bei der Fallakte sind die Bereiche **eigene Seiten** unter einem
|
||||
* gemeinsamen Layout, keine umgeschalteten Blöcke. Wer gar keinen Bereich
|
||||
* sehen darf, bekommt 404 — die Verwaltung ist für ihn nicht vorhanden.
|
||||
* sehen darf, bekommt 404 - die Verwaltung ist für ihn nicht vorhanden.
|
||||
*/
|
||||
export default async function AdminLayout({ children }: { children: ReactNode }) {
|
||||
const user = await requireUser()
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Skeleton, SkeletonLine } from '@/components/Skeleton'
|
||||
|
||||
/**
|
||||
* Skelett statt Spinner: die Struktur steht sofort, die Daten fallen hinein.
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist —
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist -
|
||||
* man klickt und es passiert scheinbar nichts. Siehe docs/09-performance-und-ux.md.
|
||||
*/
|
||||
export default function Loading() {
|
||||
|
||||
@@ -11,7 +11,7 @@ export const dynamic = 'force-dynamic'
|
||||
* Die Startseite der Verwaltung: was es gibt und wofür es da ist.
|
||||
*
|
||||
* Kein Weiterleiten auf den ersten Bereich. Wer die Verwaltung öffnet, weiss
|
||||
* nicht immer, wo das Gesuchte liegt — eine Liste mit einem Satz je Bereich
|
||||
* nicht immer, wo das Gesuchte liegt - eine Liste mit einem Satz je Bereich
|
||||
* beantwortet das schneller als das Durchklicken der Leiste.
|
||||
*/
|
||||
export default async function AdminIndexPage() {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Skeleton, SkeletonLine } from '@/components/Skeleton'
|
||||
|
||||
/**
|
||||
* Skelett statt Spinner: die Struktur steht sofort, die Daten fallen hinein.
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist —
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist -
|
||||
* man klickt und es passiert scheinbar nichts. Siehe docs/09-performance-und-ux.md.
|
||||
*/
|
||||
export default function Loading() {
|
||||
|
||||
@@ -18,7 +18,7 @@ export type PolicyResult = { ok: true } | { ok: false; error: string }
|
||||
/**
|
||||
* Die Änderungsrichtlinie gilt für die ganze Abteilung und wird serverseitig
|
||||
* gelesen, nie aus dem Request. Deshalb ist das hier die einzige Stelle, an
|
||||
* der sie sich ändern lässt — mit Recht `admin` und mit Protokolleintrag.
|
||||
* der sie sich ändern lässt - mit Recht `admin` und mit Protokolleintrag.
|
||||
*/
|
||||
export async function savePolicy(input: unknown): Promise<PolicyResult> {
|
||||
const user = await requirePermissionForAction('admin')
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Skeleton, SkeletonLine } from '@/components/Skeleton'
|
||||
|
||||
/**
|
||||
* Skelett statt Spinner: die Struktur steht sofort, die Daten fallen hinein.
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist —
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist -
|
||||
* man klickt und es passiert scheinbar nichts. Siehe docs/09-performance-und-ux.md.
|
||||
*/
|
||||
export default function Loading() {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Skeleton, SkeletonLine } from '@/components/Skeleton'
|
||||
|
||||
/**
|
||||
* Skelett statt Spinner: die Struktur steht sofort, die Daten fallen hinein.
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist —
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist -
|
||||
* man klickt und es passiert scheinbar nichts. Siehe docs/09-performance-und-ux.md.
|
||||
*/
|
||||
export default function Loading() {
|
||||
|
||||
@@ -10,7 +10,7 @@ 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
|
||||
* 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.
|
||||
|
||||
@@ -5,7 +5,7 @@ import { de } from '@/lib/i18n/de'
|
||||
* Die Bereiche der Verwaltung, an einer Stelle.
|
||||
*
|
||||
* Vorher standen Rollenverwaltung, Vorlagen und Änderungsrichtlinie als drei
|
||||
* eigene Einträge in der Kopfzeile — drei Einträge für etwas, das man selten
|
||||
* eigene Einträge in der Kopfzeile - drei Einträge für etwas, das man selten
|
||||
* und dann meist zusammenhängend braucht. Jetzt ist es ein Eintrag mit einer
|
||||
* Leiste darunter.
|
||||
*
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Skeleton, SkeletonLine } from '@/components/Skeleton'
|
||||
|
||||
/**
|
||||
* Skelett statt Spinner: die Struktur steht sofort, die Daten fallen hinein.
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist —
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist -
|
||||
* man klickt und es passiert scheinbar nichts. Siehe docs/09-performance-und-ux.md.
|
||||
*/
|
||||
export default function Loading() {
|
||||
|
||||
@@ -97,7 +97,7 @@ export default async function SystemPage() {
|
||||
|
||||
/**
|
||||
* Die Ladewerte der letzten sieben Tage. Median und 75. Perzentil, kein
|
||||
* Mittelwert — ein Ausreisser auf einem alten Laptop soll die Zahl nicht
|
||||
* Mittelwert - ein Ausreisser auf einem alten Laptop soll die Zahl nicht
|
||||
* bestimmen.
|
||||
*/
|
||||
async function VitalsTable() {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Skeleton, SkeletonLine } from '@/components/Skeleton'
|
||||
|
||||
/**
|
||||
* Skelett statt Spinner: die Struktur steht sofort, die Daten fallen hinein.
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist —
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist -
|
||||
* man klickt und es passiert scheinbar nichts. Siehe docs/09-performance-und-ux.md.
|
||||
*/
|
||||
export default function Loading() {
|
||||
|
||||
@@ -13,7 +13,7 @@ const schema = z.object({ id: z.string().min(1) })
|
||||
|
||||
/**
|
||||
* Freigabe einer Datei, deren Prüfung nicht abgeschlossen werden konnte.
|
||||
* Nur mit Recht `admin`, geprüft im Dienst — nicht hier.
|
||||
* Nur mit Recht `admin`, geprüft im Dienst - nicht hier.
|
||||
*/
|
||||
export async function releaseAttachmentAction(input: unknown): Promise<ActionResult> {
|
||||
const user = await requireUser()
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
* Ausfertigen, Zurückziehen, Umklassifizieren.
|
||||
*
|
||||
* Wie beim Fall bewusst **nicht** optimistisch: alle drei sind folgenreich.
|
||||
* Das Ausfertigen fragt nach, das Zurückziehen verlangt eine Begründung — es
|
||||
* Das Ausfertigen fragt nach, das Zurückziehen verlangt eine Begründung - es
|
||||
* gibt kein Löschen, der Grabstein bleibt stehen.
|
||||
*/
|
||||
export function DocumentActions({
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Skeleton, SkeletonLine } from '@/components/Skeleton'
|
||||
|
||||
/**
|
||||
* Skelett statt Spinner: die Struktur steht sofort, die Daten fallen hinein.
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist —
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist -
|
||||
* man klickt und es passiert scheinbar nichts. Siehe docs/09-performance-und-ux.md.
|
||||
*/
|
||||
export default function Loading() {
|
||||
|
||||
@@ -10,7 +10,7 @@ export type ActionResult = { ok: true } | { ok: false; error: string }
|
||||
|
||||
const schema = z.object({ notifyMode: z.enum(['ALL', 'OWN_CASES', 'MENTIONS', 'OFF']) })
|
||||
|
||||
/** Jeder stellt nur für sich selbst ein — die Kennung kommt aus der Sitzung. */
|
||||
/** Jeder stellt nur für sich selbst ein - die Kennung kommt aus der Sitzung. */
|
||||
export async function saveNotifyModeAction(
|
||||
_prev: ActionResult | null,
|
||||
formData: FormData,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Skeleton } from '@/components/Skeleton'
|
||||
|
||||
/**
|
||||
* Skelett statt Spinner: die Struktur steht sofort, die Daten fallen hinein.
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist —
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist -
|
||||
* man klickt und es passiert scheinbar nichts. Siehe docs/09-performance-und-ux.md.
|
||||
*/
|
||||
export default function Loading() {
|
||||
|
||||
@@ -28,7 +28,7 @@ const requestSchema = z.union([
|
||||
/**
|
||||
* Export anfordern.
|
||||
*
|
||||
* Der Auftrag läuft im Hintergrund weiter, nachdem die Antwort raus ist — das
|
||||
* Der Auftrag läuft im Hintergrund weiter, nachdem die Antwort raus ist - das
|
||||
* Rendern dauert, und niemand soll auf einen Fortschrittsbalken starren, der
|
||||
* auf Playwright wartet. Siehe docs/09-performance-und-ux.md.
|
||||
*/
|
||||
|
||||
@@ -90,7 +90,7 @@ export function CaseActions({
|
||||
</button>
|
||||
</div>
|
||||
{/* Welche Zustände von hier aus erreichbar sind, ergibt sich aus dem
|
||||
Lebenszyklus — aus einem Entwurf führt nur „Offen" heraus. Ohne
|
||||
Lebenszyklus - aus einem Entwurf führt nur „Offen" heraus. Ohne
|
||||
diesen Satz sieht die kurze Auswahl aus wie ein Fehler. */}
|
||||
<p className="mt-1.5 max-w-sm text-2xs text-text-dim">
|
||||
{caseText.statusHints[nextStatus] ?? caseText.statusChangeHint}
|
||||
|
||||
@@ -113,7 +113,7 @@ export function Participants({
|
||||
))}
|
||||
</PanelSelect>
|
||||
<PanelSelect value={target} onChange={setTarget} label={participantText.person}>
|
||||
<option value="">—</option>
|
||||
<option value="">-</option>
|
||||
<optgroup label={participantText.pickUser}>
|
||||
{members.map((member) => (
|
||||
<option key={member.id} value={`user:${member.id}`}>
|
||||
|
||||
@@ -7,7 +7,7 @@ import { toggleWatchAction } from '../actions'
|
||||
/**
|
||||
* Akte beobachten.
|
||||
*
|
||||
* Sofortiges Umschalten, der Server bestätigt nur — misslingt es, springt der
|
||||
* Sofortiges Umschalten, der Server bestätigt nur - misslingt es, springt der
|
||||
* Zustand zurück. Beobachten ist umkehrbar und harmlos, dafür braucht es
|
||||
* keinen Wartezustand. Siehe docs/09-performance-und-ux.md.
|
||||
*/
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Skeleton, SkeletonLine } from '@/components/Skeleton'
|
||||
|
||||
/**
|
||||
* Skelett statt Spinner: die Struktur steht sofort, die Daten fallen hinein.
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist —
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist -
|
||||
* man klickt und es passiert scheinbar nichts. Siehe docs/09-performance-und-ux.md.
|
||||
*/
|
||||
export default function Loading() {
|
||||
|
||||
@@ -15,7 +15,7 @@ export const dynamic = 'force-dynamic'
|
||||
*
|
||||
* Für die Abteilungsleitung ist das die Ansicht, mit der sie im Zweifel prüft,
|
||||
* ob an einer Akte nachträglich gedreht wurde. Sichtbar für alle, die die Akte
|
||||
* lesen dürfen — nicht nur für die Leitung. Das ist der ganze Sinn der Sache.
|
||||
* lesen dürfen - nicht nur für die Leitung. Das ist der ganze Sinn der Sache.
|
||||
*/
|
||||
export default async function ChangesPage({
|
||||
params,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Skeleton, SkeletonLine } from '@/components/Skeleton'
|
||||
|
||||
/**
|
||||
* Skelett statt Spinner: die Struktur steht sofort, die Daten fallen hinein.
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist —
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist -
|
||||
* man klickt und es passiert scheinbar nichts. Siehe docs/09-performance-und-ux.md.
|
||||
*/
|
||||
export default function Loading() {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Skeleton, SkeletonLine } from '@/components/Skeleton'
|
||||
|
||||
/**
|
||||
* Skelett statt Spinner: die Struktur steht sofort, die Daten fallen hinein.
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist —
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist -
|
||||
* man klickt und es passiert scheinbar nichts. Siehe docs/09-performance-und-ux.md.
|
||||
*/
|
||||
export default function Loading() {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Skeleton, SkeletonLine } from '@/components/Skeleton'
|
||||
|
||||
/**
|
||||
* Skelett statt Spinner: die Struktur steht sofort, die Daten fallen hinein.
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist —
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist -
|
||||
* man klickt und es passiert scheinbar nichts. Siehe docs/09-performance-und-ux.md.
|
||||
*/
|
||||
export default function Loading() {
|
||||
|
||||
@@ -20,12 +20,12 @@ export const dynamic = 'force-dynamic'
|
||||
* Die Akte ist in Reiter geteilt, und zwar in **eigene Seiten**, nicht in
|
||||
* umgeschaltete Blöcke: nur so wird der Code getrennt geladen. Die Fallakte
|
||||
* war die dichteste Seite der Anwendung und hatte das Bündelbudget aus
|
||||
* docs/09-performance-und-ux.md aufgebraucht — Beteiligte, Verknüpfungen,
|
||||
* docs/09-performance-und-ux.md aufgebraucht - Beteiligte, Verknüpfungen,
|
||||
* Dokumente und Anhänge lagen mit auf der Seite, obwohl man sie selten
|
||||
* gleichzeitig braucht.
|
||||
*
|
||||
* Fachlich passt der Schnitt ohnehin: die drei Ebenen aus
|
||||
* docs/05-akten-und-vorlagen.md — Kopfdaten, Abschnitte, Verlauf — bleiben
|
||||
* docs/05-akten-und-vorlagen.md - Kopfdaten, Abschnitte, Verlauf - bleiben
|
||||
* zusammen, das Umfeld der Akte bekommt seinen eigenen Platz.
|
||||
*/
|
||||
export default async function CaseLayout({
|
||||
|
||||
@@ -185,7 +185,7 @@ async function ChatBlock({
|
||||
|
||||
/**
|
||||
* Der Reiter „Akte": die drei Ebenen aus docs/05-akten-und-vorlagen.md, die
|
||||
* zusammengehören — Abschnitte als Sachstand, Verlauf als Chronologie, dazu
|
||||
* zusammengehören - Abschnitte als Sachstand, Verlauf als Chronologie, dazu
|
||||
* der formlose Aktenchat. Kopfdaten und Reiterleiste stehen im Layout.
|
||||
*/
|
||||
export default async function CasePage({
|
||||
|
||||
@@ -51,7 +51,7 @@ export async function saveSectionAction(
|
||||
/**
|
||||
* Mehrere Abschnitte in einem Zug.
|
||||
*
|
||||
* Jedes Feld wird einzeln geschrieben und einzeln beantwortet — Abschnitte
|
||||
* Jedes Feld wird einzeln geschrieben und einzeln beantwortet - Abschnitte
|
||||
* sind einzeln versioniert, und ein zwischenzeitlich geändertes Feld soll die
|
||||
* übrigen nicht aufhalten. Wäre das eine Transaktion, blockierte ein einziger
|
||||
* fremder Eingriff das ganze Blatt.
|
||||
|
||||
@@ -66,7 +66,7 @@ export async function createCaseAction(
|
||||
caseNumber = created.caseNumber
|
||||
|
||||
// Die Beteiligten gleich mitverknüpfen, damit sie nicht in einem zweiten
|
||||
// Schritt vergessen werden. Scheitert einer, steht die Akte trotzdem — sie
|
||||
// Schritt vergessen werden. Scheitert einer, steht die Akte trotzdem - sie
|
||||
// ist das Wichtigere, und nachtragen kann man auf der Aktenseite.
|
||||
for (const eintrag of beteiligte) {
|
||||
await addParticipant(user.permissions, {
|
||||
@@ -294,7 +294,7 @@ const watchSchema = z.object({ caseNumber: z.string().min(1) })
|
||||
|
||||
export type WatchResult = { ok: true; watching: boolean } | { ok: false; error: string }
|
||||
|
||||
/** Beobachten verschafft keinen Zugriff — nur was sichtbar ist, lässt sich merken. */
|
||||
/** Beobachten verschafft keinen Zugriff - nur was sichtbar ist, lässt sich merken. */
|
||||
export async function toggleWatchAction(input: unknown): Promise<WatchResult> {
|
||||
const user = await requireUser()
|
||||
const parsed = watchSchema.safeParse(input)
|
||||
|
||||
@@ -25,7 +25,7 @@ export function NewCaseForm({
|
||||
const [state, action, pending] = useActionState<CreateState, FormData>(createCaseAction, {})
|
||||
|
||||
// Beliebig viele Beteiligte, jeder mit seiner Rolle. Vorher gab es genau
|
||||
// eine „betroffene Person" — wer einen Zeugen und einen Täter eintragen
|
||||
// eine „betroffene Person" - wer einen Zeugen und einen Täter eintragen
|
||||
// wollte, musste die Akte erst anlegen und dann noch einmal hinein.
|
||||
const [beteiligte, setBeteiligte] = useState<Entwurf[]>([])
|
||||
const [naechster, setNaechster] = useState(1)
|
||||
@@ -133,7 +133,7 @@ export function NewCaseForm({
|
||||
onChange={(event) => aendern(eintrag.schluessel, { ziel: event.target.value })}
|
||||
className="field grow"
|
||||
>
|
||||
<option value="">—</option>
|
||||
<option value="">-</option>
|
||||
<optgroup label={participantText.pickUser}>
|
||||
{members.map((member) => (
|
||||
<option key={member.id} value={`user:${member.id}`}>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Skeleton } from '@/components/Skeleton'
|
||||
|
||||
/**
|
||||
* Skelett statt Spinner: die Struktur steht sofort, die Daten fallen hinein.
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist —
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist -
|
||||
* man klickt und es passiert scheinbar nichts. Siehe docs/09-performance-und-ux.md.
|
||||
*/
|
||||
export default function Loading() {
|
||||
|
||||
@@ -82,7 +82,7 @@ export default async function CaseListPage({
|
||||
</Link>
|
||||
{/* Alle sieben Zustände, nicht fünf: „Entwurf" und „Versiegelt" fehlten,
|
||||
und das waren ausgerechnet die beiden, die in der Liste am häufigsten
|
||||
standen — man konnte nach dem filtern, was man sah, gerade nicht.
|
||||
standen - man konnte nach dem filtern, was man sah, gerade nicht.
|
||||
Versiegelt nur für den, der Versiegeltes überhaupt lesen darf. */}
|
||||
{CASE_STATUSES.filter(
|
||||
(value) =>
|
||||
|
||||
@@ -13,8 +13,8 @@ import { hasAdminAccess } from './admin/sections'
|
||||
/**
|
||||
* Das Rahmenwerk der angemeldeten Anwendung.
|
||||
*
|
||||
* Die Kopfzeile trägt links die Bereiche der Arbeit — Übersicht, Fallakten,
|
||||
* Personalakten, Verwaltung — und rechts alles, was den Nutzer selbst
|
||||
* Die Kopfzeile trägt links die Bereiche der Arbeit - Übersicht, Fallakten,
|
||||
* Personalakten, Verwaltung - und rechts alles, was den Nutzer selbst
|
||||
* betrifft: Suchbalken und Profilbild. Thema, Benachrichtigungen und Abmelden
|
||||
* standen früher zwischen den Aktenbereichen und nahmen dort den Platz weg,
|
||||
* den die Arbeit braucht; sie sitzen jetzt hinter dem Profilbild.
|
||||
|
||||
@@ -17,7 +17,7 @@ type Zuordnung = {
|
||||
* Mitglieder der Abteilung, die an dieser Personalakte hängen.
|
||||
*
|
||||
* Wie die Beteiligten einer Fallakte ist das zugleich der Weg zum
|
||||
* ausnahmsweisen Zugang — der Hinweis steht deshalb direkt am Formular.
|
||||
* ausnahmsweisen Zugang - der Hinweis steht deshalb direkt am Formular.
|
||||
*/
|
||||
export function Assignments({
|
||||
personnelFileId,
|
||||
@@ -94,7 +94,7 @@ export function Assignments({
|
||||
))}
|
||||
</PanelSelect>
|
||||
<PanelSelect value={userId} onChange={setUserId} label={participantText.pickUser}>
|
||||
<option value="">—</option>
|
||||
<option value="">-</option>
|
||||
{members.map((member) => (
|
||||
<option key={member.id} value={member.id}>
|
||||
{member.displayName}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { linkCaseToPersonAction } from '../actions'
|
||||
*
|
||||
* Derselbe Vorgang wie „Beteiligte eintragen" auf der Fallakte, nur von der
|
||||
* anderen Seite gedacht: dort sucht man die Person, hier den Fall. Deshalb ein
|
||||
* Eingabefeld für das Aktenzeichen und keine Auswahlliste — eine Liste aller
|
||||
* Eingabefeld für das Aktenzeichen und keine Auswahlliste - eine Liste aller
|
||||
* sichtbaren Fälle wäre auf einer Personalakte am falschen Ort.
|
||||
*/
|
||||
export function LinkCase({ personnelFileId }: { personnelFileId: string }) {
|
||||
|
||||
@@ -27,7 +27,7 @@ type File = {
|
||||
* Kopfdaten einer Personalakte: lesen und ändern in derselben Ansicht.
|
||||
*
|
||||
* Die Klassifizierung steht bewusst getrennt. Sie ändert, wer die Akte
|
||||
* überhaupt sieht, und eine Herabstufung braucht eine Begründung — das gehört
|
||||
* überhaupt sieht, und eine Herabstufung braucht eine Begründung - das gehört
|
||||
* nicht in dasselbe Formular wie eine Korrektur der Dienstnummer.
|
||||
*/
|
||||
export function PersonnelHead({
|
||||
@@ -276,7 +276,7 @@ function Zeile({
|
||||
return (
|
||||
<div className="flex gap-3">
|
||||
<dt className="font-condensed text-xs text-text-dim">{label}</dt>
|
||||
<dd className={mono ? 'font-mono text-xs' : ''}>{value || '—'}</dd>
|
||||
<dd className={mono ? 'font-mono text-xs' : ''}>{value || '-'}</dd>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ async function AssignmentBlock({
|
||||
* dass das Gespräch bei ihrer Akte steht, war genau das, was vorher auf
|
||||
* Discord passiert ist. Siehe docs/13-entscheidungen.md.
|
||||
*
|
||||
* Übernehmen als Vermerk gibt es hier nicht — dafür braucht es einen Verlauf,
|
||||
* Übernehmen als Vermerk gibt es hier nicht - dafür braucht es einen Verlauf,
|
||||
* und den führt nur die Fallakte.
|
||||
*/
|
||||
async function ChatBlock({
|
||||
|
||||
@@ -71,7 +71,7 @@ export async function createPersonnelAction(
|
||||
id = created.id
|
||||
|
||||
// Das Aktenblatt ist der erste Eintrag zu einer Person und entsteht
|
||||
// deshalb gleich mit — als Entwurf, mit dem Namen schon eingetragen.
|
||||
// deshalb gleich mit - als Entwurf, mit dem Namen schon eingetragen.
|
||||
// Fehlt das Recht dafür, bleibt es bei der Akte allein.
|
||||
await erstesAktenblatt(user.permissions, id, parsed.data.displayName)
|
||||
} catch (error) {
|
||||
|
||||
@@ -8,8 +8,8 @@ import { createPersonnelAction, type ActionResult } from '../actions'
|
||||
/**
|
||||
* Neue Personalakte.
|
||||
*
|
||||
* Das Discord-Konto ist **optional**. Personen ohne Konto — Zeugen, ehemalige
|
||||
* Mitarbeiter, Externe — brauchen genauso eine Akte, und ihr Name ist dann das
|
||||
* Das Discord-Konto ist **optional**. Personen ohne Konto - Zeugen, ehemalige
|
||||
* Mitarbeiter, Externe - brauchen genauso eine Akte, und ihr Name ist dann das
|
||||
* einzige, was sie identifiziert.
|
||||
*/
|
||||
export function NewPersonnelForm({
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Skeleton } from '@/components/Skeleton'
|
||||
|
||||
/**
|
||||
* Skelett statt Spinner: die Struktur steht sofort, die Daten fallen hinein.
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist —
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist -
|
||||
* man klickt und es passiert scheinbar nichts. Siehe docs/09-performance-und-ux.md.
|
||||
*/
|
||||
export default function Loading() {
|
||||
|
||||
@@ -22,7 +22,7 @@ export default async function PersonnelListPage({
|
||||
}: {
|
||||
searchParams: Promise<{ q?: string; status?: string }>
|
||||
}) {
|
||||
// Ohne das Recht gibt es die Seite nicht — 404, nicht 403.
|
||||
// Ohne das Recht gibt es die Seite nicht - 404, nicht 403.
|
||||
const user = await requirePermission('personnel.read')
|
||||
const { q, status } = await searchParams
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { deleteViewAction, saveViewAction } from './actions'
|
||||
/**
|
||||
* Ansichten: die mitgelieferten und die eigenen.
|
||||
*
|
||||
* Beide sind dasselbe — eine Suchadresse. Die mitgelieferten stehen im
|
||||
* Beide sind dasselbe - eine Suchadresse. Die mitgelieferten stehen im
|
||||
* Programm, weil sie von der Gegenwart abhängen („diese Woche"), die eigenen
|
||||
* in der Datenbank.
|
||||
*/
|
||||
|
||||
@@ -10,7 +10,7 @@ const DEBOUNCE_MS = 250
|
||||
/**
|
||||
* Das Suchfeld.
|
||||
*
|
||||
* Tippen schreibt in die Adresse, mit 250 ms Verzögerung — dadurch ist jeder
|
||||
* Tippen schreibt in die Adresse, mit 250 ms Verzögerung - dadurch ist jeder
|
||||
* Zustand teilbar und der Zurück-Knopf tut das Richtige. Während die neue
|
||||
* Seite lädt, bleiben die alten Treffer stehen und werden gedimmt, statt zu
|
||||
* verschwinden. Siehe docs/09-performance-und-ux.md.
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Skeleton, SkeletonLine } from '@/components/Skeleton'
|
||||
|
||||
/**
|
||||
* Skelett statt Spinner: die Struktur steht sofort, die Daten fallen hinein.
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist —
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist -
|
||||
* man klickt und es passiert scheinbar nichts. Siehe docs/09-performance-und-ux.md.
|
||||
*/
|
||||
export default function Loading() {
|
||||
|
||||
@@ -30,7 +30,7 @@ export const dynamic = 'force-dynamic'
|
||||
* Suchseite.
|
||||
*
|
||||
* Alles läuft über Adressparameter: das macht jeden Zustand teilbar, den
|
||||
* Zurück-Knopf richtig und eine gespeicherte Ansicht zu dem, was sie ist —
|
||||
* Zurück-Knopf richtig und eine gespeicherte Ansicht zu dem, was sie ist -
|
||||
* ein Lesezeichen. Siehe lib/search/query.ts.
|
||||
*/
|
||||
export default async function SearchPage({
|
||||
@@ -76,7 +76,7 @@ export default async function SearchPage({
|
||||
])
|
||||
|
||||
// Bei einer einzelnen Art hängt alles Weitere an ihr: Facetten, Blättern,
|
||||
// die Trefferzahl in der Kopfzeile. Bei „Alle" gibt es das nicht — dort ist
|
||||
// die Trefferzahl in der Kopfzeile. Bei „Alle" gibt es das nicht - dort ist
|
||||
// jede Art ein eigener Block mit eigenem Weg in die Vollansicht.
|
||||
const ergebnis = alle ? null : (faelle ?? dokumente ?? personen)!
|
||||
const gesamt = alle
|
||||
@@ -108,7 +108,7 @@ export default async function SearchPage({
|
||||
{SEARCH_SCOPES.map((kind) => {
|
||||
const anzahl = kind === 'all' ? gesamt : zahlen[kind]
|
||||
// `null` heisst: diese Art gibt es für diesen Nutzer nicht. Dann
|
||||
// fehlt der Reiter, statt eine Null zu zeigen — auch eine Null wäre
|
||||
// fehlt der Reiter, statt eine Null zu zeigen - auch eine Null wäre
|
||||
// eine Auskunft.
|
||||
if (anzahl === null) return null
|
||||
|
||||
@@ -279,7 +279,7 @@ type TrefferView =
|
||||
*
|
||||
* Welche Art es ist, steht an der Form: ein Treffer mit Aktenzeichen ist eine
|
||||
* Fallakte. Das ist kein Trick, sondern der Grund, warum die drei Suchen
|
||||
* getrennte Rückgaben haben — ein gemeinsamer Typ mit lauter optionalen
|
||||
* getrennte Rückgaben haben - ein gemeinsamer Typ mit lauter optionalen
|
||||
* Feldern liesse offen, was wirklich dasteht.
|
||||
*/
|
||||
function Treffer({
|
||||
@@ -371,7 +371,7 @@ async function listStatusTags() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Eine Facette. Die Zahlen stammen aus derselben Abfrage wie die Liste — ein
|
||||
* Eine Facette. Die Zahlen stammen aus derselben Abfrage wie die Liste - ein
|
||||
* Zähler, der mehr zählt als die Liste zeigt, wäre selbst eine Information.
|
||||
*/
|
||||
function Facette({
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Skeleton, SkeletonLine } from '@/components/Skeleton'
|
||||
|
||||
/**
|
||||
* Skelett statt Spinner: die Struktur steht sofort, die Daten fallen hinein.
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist —
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist -
|
||||
* man klickt und es passiert scheinbar nichts. Siehe docs/09-performance-und-ux.md.
|
||||
*/
|
||||
export default function Loading() {
|
||||
|
||||
@@ -16,7 +16,7 @@ export const dynamic = 'force-dynamic'
|
||||
*
|
||||
* Woran arbeite ich, was ist fällig, wo hat jemand geschrieben, was hat sich
|
||||
* getan. Jede Liste läuft durch dieselbe Sichtbarkeitsprüfung wie die
|
||||
* Fallliste — das ist der Ort, an dem eine unachtsame Abfrage am ehesten eine
|
||||
* Fallliste - das ist der Ort, an dem eine unachtsame Abfrage am ehesten eine
|
||||
* Akte verrät, die man nicht sehen darf.
|
||||
*/
|
||||
export default async function DashboardPage() {
|
||||
@@ -206,13 +206,13 @@ function Block({
|
||||
|
||||
/**
|
||||
* Eine Zeile der Sprungliste. Sie kann eine Akte, eine Person oder ein
|
||||
* Dokument sein — deshalb steht die Art nicht dabei, sondern nur das, woran
|
||||
* Dokument sein - deshalb steht die Art nicht dabei, sondern nur das, woran
|
||||
* man sie erkennt: Aktenzeichen, Name oder Dokumentnummer.
|
||||
*
|
||||
* Eine Akte und ein Dokument tragen eine **Kennung**, eine Person nicht: ihr
|
||||
* Name ist der Titel. Stünde er in der Kennungsspalte, stünde er in Mono, wäre
|
||||
* abgeschnitten, und der Dienstgrad rutschte dorthin, wo sonst der Titel
|
||||
* steht — untereinander ergäbe das keine Spalte mehr.
|
||||
* steht - untereinander ergäbe das keine Spalte mehr.
|
||||
*
|
||||
* Ohne Vorabruf, anders als die übrigen Listen: die Seiten dahinter merken
|
||||
* sich, dass sie geöffnet wurden. Der Vorabruf wird zwar serverseitig erkannt
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Skeleton, SkeletonLine } from '@/components/Skeleton'
|
||||
|
||||
/**
|
||||
* Skelett statt Spinner: die Struktur steht sofort, die Daten fallen hinein.
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist —
|
||||
* Ohne diese Grenze wartet die Navigation stumm, bis der Server fertig ist -
|
||||
* man klickt und es passiert scheinbar nichts. Siehe docs/09-performance-und-ux.md.
|
||||
*/
|
||||
export default function Loading() {
|
||||
|
||||
Reference in New Issue
Block a user