Abteilungen als Ebene über der Personalakte
Punkt 3 der Wünsche. Die Abteilung stand als Freitext in der Personalakte. Damit liess sich nichts fragen: „SHD", „Shd" und „Sicherheitsdienst" waren drei Abteilungen. Sie ist jetzt ein eigenes Modell, im Adminbereich verwaltbar, und die drei vorhandenen Freitexte sind in der Migration zu Abteilungen geworden. **Eine Fallakte trägt keine Abteilung.** Sie gehört zu einer, wenn eine beteiligte Person ihr angehört - abgeleitet über CaseParticipant und die Personalakte. Eine eingetragene Zuordnung wäre nach zwei Wochen falsch: Leute wechseln, Beteiligte kommen dazu, und niemand pflegt das nach. Dasselbe gilt für die Dokumente: ein Dokument unmittelbar an der Abteilung wäre halb verdrahtet, weil die Vorlagen über TemplateUse nur Fall- und Personalakte kennen. Die Abteilungsseite zeigt Mitglieder, abgeleitete Fälle und Dokumente, Vermerke, Chat und ein Kreisdiagramm der Fallarten; die Übersicht eines über alle Abteilungen. Das Diagramm ist serverseitig gerendertes SVG - eine Bibliothek wäre das Teuerste auf der Seite gewesen. Die Segmente sind Abstufungen von --steel und nicht die Stufenfarben: ein Tortenstück in Oxidrot würde als „streng geheim" gelesen. Die Zusammenfassung ist die gefährliche Stelle. Eine Abteilungsseite fasst zusammen, was einzeln längst geschützt ist, und eine Zusammenfassung, die nicht filtert, verrät mehr als jede Einzelansicht. Deshalb läuft hier jede Liste und jede Zahl durch dieselben Fragmente wie überall; ein Test prüft, dass die Summe der Diagrammanteile mit der Länge der Liste übereinstimmt. Wo jemand Personalakten gar nicht sehen darf, steht null und keine Null. Vermerke erben Regel 2 unverändert: kein DELETE, Version bei jeder Änderung, Grabstein bei jeder Zurücknahme, als Regel und Trigger in der Datenbank. Ohne Korrekturkette, Anhänge und Karenzzeit - was Beweiswert hat, gehört in eine Fallakte. Nebenbei: das Zugriffsprotokoll hängt jetzt am dafür vorgesehenen Recht audit.read statt an admin.
This commit is contained in:
@@ -50,6 +50,12 @@ export const auditText = {
|
||||
'attachment.read': 'Datei abgerufen',
|
||||
'attachment.release': 'Datei freigegeben',
|
||||
'chat.adopt': 'Nachricht als Vermerk übernommen',
|
||||
'department.create': 'Abteilung angelegt',
|
||||
'department.update': 'Abteilung geändert',
|
||||
'department.assign': 'Abteilung zugeordnet',
|
||||
'department.entry.create': 'Abteilungsvermerk angelegt',
|
||||
'department.entry.edit': 'Abteilungsvermerk geändert',
|
||||
'department.entry.retract': 'Abteilungsvermerk zurückgezogen',
|
||||
'roles.update': 'Rollenzuordnung geändert',
|
||||
'export.pdf': 'PDF erzeugt',
|
||||
'export.approve': 'Export freigegeben',
|
||||
@@ -70,6 +76,8 @@ export const auditText = {
|
||||
RoleMapping: 'Rollenzuordnung',
|
||||
ChangePolicy: 'Änderungsrichtlinie',
|
||||
Database: 'Datenbank',
|
||||
Department: 'Abteilung',
|
||||
DepartmentEntry: 'Abteilungsvermerk',
|
||||
} as Record<string, string>,
|
||||
} as const
|
||||
|
||||
@@ -78,6 +86,7 @@ export const adminText = {
|
||||
intro: 'Rollen, Vorlagen, Regeln und das Protokoll der Abteilung.',
|
||||
sections: {
|
||||
roles: 'Rollen',
|
||||
departments: 'Abteilungen',
|
||||
templates: 'Vorlagen',
|
||||
policy: 'Änderungsrichtlinie',
|
||||
log: 'Protokoll',
|
||||
@@ -85,6 +94,7 @@ export const adminText = {
|
||||
} as Record<string, string>,
|
||||
descriptions: {
|
||||
roles: 'Welche Discord-Rolle welche Freigabestufe und welche Rechte trägt.',
|
||||
departments: 'Die Abteilungen der Foundation. Zugeordnet wird in der Personalakte.',
|
||||
templates: 'Aufbau der Fallakten und Dokumente, Bausteine und Fassungen.',
|
||||
policy: 'Ob und wie lange Verlaufseinträge nachträglich geändert werden dürfen.',
|
||||
log: 'Wer was wo getan hat. Vollständig und nicht änderbar.',
|
||||
|
||||
@@ -27,6 +27,10 @@ export const commonText = {
|
||||
*/
|
||||
export const actionErrorsText = {
|
||||
'not-found': 'Die Akte gibt es nicht, oder du darfst sie nicht sehen.',
|
||||
'department-key-invalid':
|
||||
'Kürzel: Grossbuchstaben, Ziffern und Bindestrich, höchstens 21 Zeichen.',
|
||||
'department-key-taken': 'Dieses Kürzel gibt es schon.',
|
||||
'department-name-missing': 'Die Abteilung braucht einen Namen.',
|
||||
'clearance-too-low': 'Deine Freigabestufe reicht dafür nicht.',
|
||||
'no-permission': 'Dir fehlt das nötige Recht.',
|
||||
'read-only': 'Der Datensatz ist schreibgeschützt.',
|
||||
@@ -40,6 +44,7 @@ export const actionErrorsText = {
|
||||
'participant-target': 'Entweder ein Discord-Konto oder eine Personalakte, nicht beides.',
|
||||
'link-self': 'Eine Akte lässt sich nicht mit sich selbst verknüpfen.',
|
||||
'message-empty': 'Die Nachricht ist leer.',
|
||||
'entry-empty': 'Der Vermerk ist leer.',
|
||||
'message-too-long': 'Die Nachricht ist länger als 4000 Zeichen.',
|
||||
'message-withdrawn': 'Die Nachricht wurde zurückgezogen.',
|
||||
'adopt-needs-case': 'Nur eine Nachricht aus einem Aktenchat wird zum Vermerk.',
|
||||
|
||||
@@ -41,6 +41,11 @@ import { chatText } from '@/lib/i18n/chat'
|
||||
import { personnelText, assignmentText } from '@/lib/i18n/people'
|
||||
import { documentText, attachmentText } from '@/lib/i18n/documents'
|
||||
import { exportText } from '@/lib/i18n/export'
|
||||
import {
|
||||
departmentText,
|
||||
departmentAdminText,
|
||||
departmentEntryText,
|
||||
} from '@/lib/i18n/departments'
|
||||
import {
|
||||
adminText,
|
||||
auditText,
|
||||
@@ -70,6 +75,9 @@ export const de = {
|
||||
document: documentText,
|
||||
attachment: attachmentText,
|
||||
export: exportText,
|
||||
department: departmentText,
|
||||
departmentAdmin: departmentAdminText,
|
||||
departmentEntry: departmentEntryText,
|
||||
common: commonText,
|
||||
actionErrors: actionErrorsText,
|
||||
errors: errorsText,
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Abteilungen. Die Bezeichnungen der Abteilungen selbst stehen **nicht** hier:
|
||||
* das sind Daten, im Adminbereich änderbar, so wie die Namen der
|
||||
* Freigabestufen. Siehe CLAUDE.md, Sprachkonvention.
|
||||
*/
|
||||
export const departmentText = {
|
||||
heading: 'Abteilungen',
|
||||
one: 'Abteilung',
|
||||
intro: 'Was zu einer Abteilung aktenkundig ist, ergibt sich aus ihren Mitgliedern.',
|
||||
empty: 'Noch keine Abteilung. Die Abteilungsleitung legt sie im Adminbereich an.',
|
||||
members: 'Mitglieder',
|
||||
membersEmpty: 'Dieser Abteilung ist niemand zugeordnet.',
|
||||
memberCount: (n: number) => (n === 1 ? '1 Mitglied' : `${n} Mitglieder`),
|
||||
cases: 'Fallakten',
|
||||
casesEmpty: 'Zu dieser Abteilung ist keine Fallakte offen einsehbar.',
|
||||
documents: 'Dokumente',
|
||||
documentsEmpty: 'Kein Dokument zu dieser Abteilung.',
|
||||
entries: 'Vermerke',
|
||||
entriesEmpty: 'Noch kein Vermerk. Ersten Vermerk schreiben.',
|
||||
chart: 'Fallarten',
|
||||
chartAll: 'Fälle je Abteilung',
|
||||
chartEmpty: 'Noch keine Fallakte, aus der sich etwas ablesen liesse.',
|
||||
chartTotal: (n: number) => (n === 1 ? '1 Fallakte' : `${n} Fallakten`),
|
||||
minClearance: 'Mindeststufe',
|
||||
archived: 'Archiviert',
|
||||
derivedHint:
|
||||
'Eine Fallakte gehört zu dieser Abteilung, wenn eine beteiligte Person ihr angehört. Die Zuordnung wird nicht eingetragen, sondern ergibt sich — damit stimmt sie auch, wenn sich die Beteiligten ändern.',
|
||||
} as const
|
||||
|
||||
export const departmentAdminText = {
|
||||
heading: 'Abteilungen',
|
||||
intro:
|
||||
'Die Abteilungen der Foundation. Personen werden in ihrer Personalakte zugeordnet.',
|
||||
create: 'Abteilung anlegen',
|
||||
createTitle: 'Neue Abteilung',
|
||||
key: 'Kürzel',
|
||||
keyHint: 'Grossbuchstaben, Ziffern und Bindestrich. Steht später in der Adresse.',
|
||||
name: 'Name',
|
||||
description: 'Beschreibung',
|
||||
minClearance: 'Mindeststufe',
|
||||
minClearanceHint: 'Wer diese Stufe nicht hat, sieht die Abteilung nicht.',
|
||||
archived: 'Archiviert',
|
||||
archivedHint: 'Archivierte Abteilungen stehen nicht mehr zur Auswahl.',
|
||||
save: 'Abteilung speichern',
|
||||
edit: 'Ändern',
|
||||
cancel: 'Abbrechen',
|
||||
saved: 'Abteilung gespeichert',
|
||||
created: 'Abteilung angelegt',
|
||||
empty: 'Noch keine Abteilung angelegt.',
|
||||
members: 'Mitglieder',
|
||||
} as const
|
||||
|
||||
export const departmentEntryText = {
|
||||
heading: 'Vermerke',
|
||||
add: 'Vermerk schreiben',
|
||||
body: 'Vermerk',
|
||||
save: 'Vermerk speichern',
|
||||
saving: 'Wird gespeichert',
|
||||
edit: 'Ändern',
|
||||
editSave: 'Änderung speichern',
|
||||
cancel: 'Abbrechen',
|
||||
retract: 'Zurückziehen',
|
||||
retractReason: 'Grund der Zurücknahme',
|
||||
retracted: 'Zurückgezogen',
|
||||
retractedBy: (wer: string, grund: string) => `Zurückgezogen von ${wer}: ${grund}`,
|
||||
edited: (v: number) => `geändert, Fassung ${v}`,
|
||||
minClearance: 'Sichtbar ab Stufe',
|
||||
hint: 'Vermerke lassen sich ändern und zurückziehen, aber nie unsichtbar machen: jede Fassung bleibt, jede Zurücknahme hinterlässt einen Grabstein.',
|
||||
} as const
|
||||
@@ -17,6 +17,7 @@ export const personnelText = {
|
||||
name: 'Name',
|
||||
rank: 'Dienstgrad',
|
||||
department: 'Abteilung',
|
||||
noDepartment: 'Keine Abteilung',
|
||||
status: 'Status',
|
||||
statusHint: 'Mehrere möglich. „Gekündigt" und „Verhaftet" schließen einander nicht aus.',
|
||||
statusNone: 'Kein Status gesetzt',
|
||||
|
||||
@@ -20,6 +20,7 @@ export const navText = {
|
||||
dashboard: 'Übersicht',
|
||||
cases: 'Fallakten',
|
||||
personnel: 'Personalakten',
|
||||
departments: 'Abteilungen',
|
||||
search: 'Suche',
|
||||
admin: 'Admin',
|
||||
skipToContent: 'Zum Inhalt springen',
|
||||
@@ -180,6 +181,7 @@ export const paletteText = {
|
||||
{ href: '/faelle', label: 'Fallakten' },
|
||||
{ href: '/faelle/neu', label: 'Neue Fallakte' },
|
||||
{ href: '/personalakten', label: 'Personalakten' },
|
||||
{ href: '/abteilungen', label: 'Abteilungen' },
|
||||
{ href: '/suche', label: 'Suche' },
|
||||
{ href: '/admin', label: 'Admin' },
|
||||
{ href: '/admin/protokoll', label: 'Protokoll' },
|
||||
|
||||
Reference in New Issue
Block a user