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.
214 lines
8.3 KiB
TypeScript
214 lines
8.3 KiB
TypeScript
export const auditText = {
|
|
heading: 'Protokoll',
|
|
intro:
|
|
'Wer was wo getan hat. Jede schreibende Aktion steht hier, dazu jeder Lesezugriff ab Stufe 3. Einträge lassen sich nicht ändern und nicht entfernen.',
|
|
when: 'Zeitpunkt',
|
|
who: 'Wer',
|
|
what: 'Was',
|
|
where: 'Wo',
|
|
clearance: 'Stufe',
|
|
filterActor: 'Person',
|
|
filterAction: 'Aktion',
|
|
filterTarget: 'Art',
|
|
filterFrom: 'Ab',
|
|
filterTo: 'Bis',
|
|
apply: 'Filtern',
|
|
reset: 'Alles zeigen',
|
|
all: 'Alle',
|
|
empty: 'Für diesen Ausschnitt steht nichts im Protokoll.',
|
|
older: 'Ältere anzeigen',
|
|
gone: 'nicht mehr auffindbar',
|
|
ofUser: 'Nur von dieser Person',
|
|
ofTarget: 'Nur zu diesem Vorgang',
|
|
actions: {
|
|
'case.read': 'Fallakte gelesen',
|
|
'case.create': 'Fallakte angelegt',
|
|
'case.update': 'Fallakte geändert',
|
|
'case.reclassify': 'Fallakte umklassifiziert',
|
|
'case.status': 'Status geändert',
|
|
'entry.create': 'Vermerk angelegt',
|
|
'entry.edit': 'Vermerk geändert',
|
|
'entry.retract': 'Vermerk zurückgezogen',
|
|
'entry.restore': 'Vermerk wiederhergestellt',
|
|
'case.participant.add': 'Beteiligten eingetragen',
|
|
'case.participant.remove': 'Beteiligten ausgetragen',
|
|
'case.link': 'Akten verknüpft',
|
|
'case.unlink': 'Verknüpfung gelöst',
|
|
'personnel.read': 'Personalakte gelesen',
|
|
'personnel.create': 'Personalakte angelegt',
|
|
'personnel.update': 'Personalakte geändert',
|
|
'personnel.reclassify': 'Personalakte umklassifiziert',
|
|
'personnel.assign': 'Person zugeordnet',
|
|
'personnel.unassign': 'Zuordnung aufgehoben',
|
|
'document.read': 'Dokument gelesen',
|
|
'document.create': 'Dokument angelegt',
|
|
'document.update': 'Dokument geändert',
|
|
'document.finalize': 'Dokument abgeschlossen',
|
|
'document.retract': 'Dokument zurückgezogen',
|
|
'document.reclassify': 'Dokument umklassifiziert',
|
|
'attachment.upload': 'Datei hochgeladen',
|
|
'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',
|
|
'export.reject': 'Export abgelehnt',
|
|
'export.download': 'Export heruntergeladen',
|
|
'export.database': 'Datenbankabzug erzeugt',
|
|
} as Record<string, string>,
|
|
targets: {
|
|
Case: 'Fallakte',
|
|
CaseEntry: 'Vermerk',
|
|
CaseSection: 'Abschnitt',
|
|
PersonnelFile: 'Personalakte',
|
|
Document: 'Dokument',
|
|
DocumentSection: 'Dokumentabschnitt',
|
|
Attachment: 'Datei',
|
|
ChatMessage: 'Nachricht',
|
|
ExportJob: 'Export',
|
|
RoleMapping: 'Rollenzuordnung',
|
|
ChangePolicy: 'Änderungsrichtlinie',
|
|
Database: 'Datenbank',
|
|
Department: 'Abteilung',
|
|
DepartmentEntry: 'Abteilungsvermerk',
|
|
} as Record<string, string>,
|
|
} as const
|
|
|
|
export const adminText = {
|
|
heading: 'Admin',
|
|
intro: 'Rollen, Vorlagen, Regeln und das Protokoll der Abteilung.',
|
|
sections: {
|
|
roles: 'Rollen',
|
|
departments: 'Abteilungen',
|
|
templates: 'Vorlagen',
|
|
policy: 'Änderungsrichtlinie',
|
|
log: 'Protokoll',
|
|
system: 'System',
|
|
} 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.',
|
|
system: 'Zustand der Dienste, Ladewerte, Nummernkreise.',
|
|
} as Record<string, string>,
|
|
empty: 'Für die Verwaltung fehlen dir die Rechte. Die Abteilungsleitung vergibt sie.',
|
|
} as const
|
|
|
|
/**
|
|
* Der Adminbereich: Rollen, Vorlagen, Einstellungen.
|
|
*
|
|
* Nach Bereichen getrennt, weil eine **Client-Komponente die ganze Datei ins
|
|
* Bündel zieht**, in der ihr Text steht. Lägen alle Texte in einer, trüge jede
|
|
* Ansicht die der ganzen Anwendung mit sich herum — das Budget aus
|
|
* docs/09-performance-und-ux.md wäre nach wenigen Phasen aufgebraucht.
|
|
*
|
|
* Serverkomponenten nehmen weiter `de` aus `lib/i18n/de.ts`: was dorthin
|
|
* gezogen wird, verlässt den Server nicht.
|
|
*/
|
|
|
|
export const roleAdminText = {
|
|
heading: 'Rollenverwaltung',
|
|
intro:
|
|
'Jede Rolle des Discord-Servers steht hier. Zugangsrollen bestimmen die Freigabestufe, Rangrollen die Rechte, und eine Zugehörigkeitsrolle entscheidet, wer sich überhaupt anmelden darf.',
|
|
empty:
|
|
'Noch keine Rollen eingelesen. Der Worker spiegelt sie, sobald er am Discord-Gateway hängt.',
|
|
role: 'Rolle',
|
|
category: 'Sorte',
|
|
clearance: 'Freigabestufe',
|
|
grantsMembership: 'Darf sich anmelden',
|
|
permissions: 'Rechte',
|
|
missingFromGuild: 'Auf dem Server nicht mehr vorhanden',
|
|
save: 'Zuordnung speichern',
|
|
invalidInput: 'Eingabe nicht gültig',
|
|
roleGone: 'Rolle nicht gefunden, sie wurde zwischenzeitlich gelöscht',
|
|
saving: 'Wird gespeichert',
|
|
saved: 'Zuordnung gespeichert',
|
|
confirm: 'Rollen-Mapping ändern? Das wirkt sofort für alle angemeldeten Personen.',
|
|
categories: {
|
|
MEMBERSHIP: 'Zugehörigkeit',
|
|
RANK: 'Rang',
|
|
CLEARANCE: 'Zugang',
|
|
UNSORTED: 'Nicht eingeordnet',
|
|
} as Record<string, string>,
|
|
optIn:
|
|
'Dieses Recht ist im Auslieferungszustand keiner Rolle zugeordnet. Vergib es bewusst.',
|
|
preview: {
|
|
heading: 'Vorschau',
|
|
intro: 'Was jemand mit dieser Stufe sieht, wenn er keine weiteren Rollen hat.',
|
|
level: 'Stufe',
|
|
sees: 'Sieht Akten bis einschliesslich',
|
|
hidden: 'Höhere Akten sind unsichtbar',
|
|
lockedOnly: 'Höhere Akten sind sichtbar, aber gesperrt',
|
|
nobody: 'Keine Rolle führt zu dieser Stufe',
|
|
viaRoles: 'Erreichbar über',
|
|
},
|
|
} as const
|
|
|
|
export const templateAdminText = {
|
|
heading: 'Vorlagen',
|
|
intro:
|
|
'Eine Vorlage bestimmt, aus welchen Bausteinen eine Fallart besteht. Speichern legt eine neue Version an; bestehende Akten behalten die Version, mit der sie angelegt wurden.',
|
|
empty: 'Noch keine Vorlage.',
|
|
version: (v: number) => `Version ${v}`,
|
|
fieldsLabel: 'Bausteine als JSON',
|
|
fieldsHelp:
|
|
'Liste von Bausteinen. Eingabefelder: shorttext, longtext, select, multiselect, date, person, number, checklist, table. Feste Bausteine: heading, paragraph, signature, pagebreak.',
|
|
numberPrefix: 'Präfix des Aktenzeichens',
|
|
numberPadding: 'Stellen der laufenden Nummer',
|
|
save: 'Als neue Version speichern',
|
|
saved: (v: number) => `Gespeichert als Version ${v}`,
|
|
preview: 'Vorschau',
|
|
previewIntro: 'So sieht eine neue Akte mit dieser Vorlage aus.',
|
|
invalid: 'Die Bausteine sind kein gültiges JSON.',
|
|
fieldsInvalid: 'Die Bausteine sind nicht gültig.',
|
|
inputField: 'Eingabe',
|
|
staticBlock: 'Fester Baustein',
|
|
} as const
|
|
|
|
export const settingsText = {
|
|
heading: 'Änderungsrichtlinie',
|
|
intro:
|
|
'Regelt, ob und wie lange Verlaufseinträge nachträglich geändert werden dürfen. Gilt für die ganze Abteilung.',
|
|
policy: 'Richtlinie',
|
|
graceMinutes: 'Karenzzeit in Minuten',
|
|
markGrace: 'Auch Änderungen in der Karenzzeit sichtbar markieren',
|
|
save: 'Richtlinie speichern',
|
|
saved: 'Richtlinie gespeichert',
|
|
confirm: 'Änderungsrichtlinie ändern? Das gilt sofort für alle Akten.',
|
|
policies: {
|
|
APPEND_ONLY: 'Keine Änderung möglich, nur Korrektureinträge',
|
|
GRACE_WINDOW: 'Eigene Einträge in der Karenzzeit frei änderbar, danach nur mit Recht',
|
|
OPEN: 'Jederzeit änderbar, sofern das Recht vorliegt',
|
|
} as Record<string, string>,
|
|
} as const
|
|
|
|
export const vitalsText = {
|
|
heading: 'Ladewerte',
|
|
intro:
|
|
'Gemessen in den Browsern der Nutzer, letzte sieben Tage. Nur Zahlen und das Routenmuster — wer wann welche Akte geöffnet hat, steht im Zugriffsprotokoll.',
|
|
empty: 'Noch nichts gemessen.',
|
|
metric: 'Wert',
|
|
median: 'Median',
|
|
p75: '75. Perzentil',
|
|
count: 'Messungen',
|
|
target: 'Ziel',
|
|
worstRoutes: 'Langsamste Routen',
|
|
route: 'Route',
|
|
targets: {
|
|
LCP: 'unter 1,5 s',
|
|
INP: 'unter 200 ms',
|
|
CLS: 'unter 0,05',
|
|
TTFB: 'unter 500 ms',
|
|
FCP: 'unter 1,0 s',
|
|
} as Record<string, string>,
|
|
} as const
|