Mock-Modus
Die Library kann ohne ActaNova-Backend laufen: im Mock-Modus liefern die Services kuratierte Beispieldaten statt echter Requests. Das ist der schnellste Weg, ein Sachbearbeiter-Cockpit lokal zu entwickeln oder ein Widget vorzuführen.
Die Anmeldung ist davon nicht betroffen — Keycloak wird weiterhin gebraucht. Gemockt sind nur die Datenabrufe.
Einschalten
Beim Start der App
import { configureCockpit } from '@gentics/cockpit/core';
configureCockpit({ mock: true });
Einmal, bevor die erste Sachbearbeiter-Cockpit-Komponente rendert bzw. der erste Datenabruf läuft. Üblich ist es, den Wert aus einer Umgebungsvariable zu ziehen — die liest dein Projekt selbst aus, damit die Library bundler-unabhängig bleibt:
configureCockpit({ mock: import.meta.env.VITE_COCKPIT_MOCK === 'true' });
configureCockpit merget den Patch über die aktuelle Config; Teilangaben
genügen. Den aktuellen Stand liest getConfig().
Zur Laufzeit in den DevTools
localStorage.setItem('cockpit:mock', 'true'); // danach Seite neu laden
localStorage.removeItem('cockpit:mock'); // zurück zur App-Config
Der localStorage-Wert überstimmt die App-Config — auch umgekehrt:
'false' erzwingt echte Requests, obwohl die App mock: true konfiguriert hat.
Die Reihenfolge ist:
localStorage "cockpit:mock" → configureCockpit({ mock }) → false (Default)
Gelesen wird bei jedem Service-Aufruf neu, ein Reload genügt also; ein Rebuild ist nicht nötig.
Was gemockt ist — und was nicht
| Aufruf | Im Mock-Modus |
|---|---|
useFiles / useFile | 8 zusammenhängende Beispiel-Akten aus dem Behörden-/eGovernment-Kontext |
useGroups | Beispielgruppen |
useUsers | Beispiel-User |
usePersons / usePerson | Beispiel-Personen |
useDocumentsByFile | 3 Beispieldokumente je Akte |
useUploadDocument | kein Request; die Datei landet in einem In-Memory-„Backend" |
useSaveFile | kein Request, liefert true — die Änderung wird nicht behalten |
useCompleteActivity | wird übersprungen |
useCurrentUser | {} — es gibt keine Mock-Daten für /Me |
useAnnotations | [] — es gibt keine Mock-Daten für Annotations |
Der Mock-Datensatz ist mandantenneutral: jeder Tenant bekommt dieselben
Daten. Die Akten tragen echte ActaNova-Feldnamen (title.de,
formattedNumber, fileType.displayName, fileHandlingState, openingDate,
leadingUser, leadingGroup, $apiId), sodass Spalten und jsonPath-Angaben
sich genauso verhalten wie gegen das echte Backend.
Jeder gemockte Aufruf schreibt eine Zeile mit 🔧 in die Konsole — daran
erkennst du auf einen Blick, ob der Modus aktiv ist.
Uploads sind ein echter Rundlauf
Eine im Mock-Modus hochgeladene Datei wird in eine Liste im Speicher gelegt und erscheint beim nächsten Laden der Dokumentliste tatsächlich — der Ablauf „hochladen → Liste aktualisiert sich" ist also testbar. Beim Reload ist der Stand wieder auf den drei Beispieldokumenten.
Gerechnet wird mit kurzen Verzögerungen (etwa 200 ms je Abruf, rund 1 s für einen Upload), damit Skeletons und Lade-Toasts sichtbar sind.
Der Datensatz im Detail
Damit du weißt, welche jsonPath-Angaben und Spalten im Mock-Modus tatsächlich
etwas anzeigen: hier steht, was in den Mock-Daten drin ist.
Akten — useFiles, useFile
Jede der 8 Akten hat genau diese Felder:
| Feld | Form | Werte im Datensatz |
|---|---|---|
$apiType | String | immer "File" |
$apiId | UUID-String | 8 verschiedene, siehe Tabelle unten |
title.de / title.en | Objekt | deutscher und englischer Titel |
displayName | String | identisch mit title.de |
formattedNumber | String | "2026/0431"-Form |
number | Zahl | der Zähler ohne Jahr (431) |
fileType.displayName | Objekt | "Akte" (3×), "eGovernment-Akte" (5×) |
subjectArea.displayName | Objekt | 8 verschiedene Fachgebiete (Bauwesen, Meldewesen, Abgaben, …) |
fileHandlingState | String | "Work" (5×), "Closed" (3×) |
approvalState | String | "Approved" (4×), "NotApproved" (4×) |
leadingUser | Objekt | displayName, $apiId, referencedApiType: "User" |
leadingGroup | Objekt | displayName, $apiId, referencedApiType: "Group" |
createdAt | ISO-Datum mit Z | 2025 und 2026 |
openingDate | ISO-Datum ohne Z | dito |
closingDate | ISO-Datum oder null | gesetzt bei den 3 geschlossenen Akten |
changedAt | ISO-Datum mit Z | dito |
year | Zahl | 2025 (2×), 2026 (6×) |
isPaperFile | Boolean | true (2×), false (6×) |
archive | immer null | — |
remark | String oder null | Freitext, bei 2 Akten null |
Ein vollständiger Eintrag, so wie ihn useFile() liefert:
{
"$apiType": "File",
"$apiId": "0f9f8caa-1b7c-4170-8592-86e063da5b62",
"title": { "de": "Auskunftsersuchen Steiner (IFG)", "en": "Freedom-of-Information request Steiner" },
"displayName": "Auskunftsersuchen Steiner (IFG)",
"formattedNumber": "2026/0431",
"number": 431,
"fileType": { "displayName": "eGovernment-Akte" },
"subjectArea": { "displayName": "Informationsfreiheit" },
"fileHandlingState": "Work",
"approvalState": "NotApproved",
"leadingUser": {
"displayName": "Maria Huber",
"$apiId": "b2c3d4e5-0001-4a10-9b3e-200000000001",
"referencedApiType": "User"
},
"leadingGroup": {
"displayName": "Rechtsabteilung",
"$apiId": "a1b2c3d4-0001-4a10-9b3e-100000000001",
"referencedApiType": "Group"
},
"createdAt": "2026-02-11T10:38:53.868Z",
"openingDate": "2026-02-11T10:38:53",
"closingDate": null,
"changedAt": "2026-03-02T08:12:00.000Z",
"year": 2026,
"isPaperFile": false,
"archive": null,
"remark": "Frist für Beantwortung: 8 Wochen"
}
Die 8 Akten:
title.de | formattedNumber | fileType.displayName | fileHandlingState |
|---|---|---|---|
| Auskunftsersuchen Steiner (IFG) | 2026/0431 | eGovernment-Akte | Work |
| Bauakte Musterstraße 12 | 2025/1187 | Akte | Work |
| Gewerbeanmeldung Café Central | 2026/0089 | eGovernment-Akte | Closed |
| Meldewesen – Zuzug Familie Novak | 2026/0512 | Akte | Work |
| Förderantrag Photovoltaik-Anlage | 2026/0274 | eGovernment-Akte | Work |
| Bürgeranfrage Straßenbeleuchtung | 2026/0603 | eGovernment-Akte | Closed |
| Personenstandsakte Eheschließung Berger | 2025/0945 | Akte | Closed |
| eZustellung Bescheid Abgabenkonto | 2026/0718 | eGovernment-Akte | Work |
Für Deep-Links und useFile(id) — die $apiId in derselben Reihenfolge:
0f9f8caa-1b7c-4170-8592-86e063da5b62 2026/0431 Auskunftsersuchen Steiner (IFG)
8c2d4e91-77af-4a10-9b3e-1f0a2b6c5d44 2025/1187 Bauakte Musterstraße 12
b41a90fe-2c33-4d8b-8e77-93a5c0117e21 2026/0089 Gewerbeanmeldung Café Central
d7e6c512-4b8a-42f0-9c1d-6a2f8b3e0d19 2026/0512 Meldewesen – Zuzug Familie Novak
1a5b8c02-9d4e-4f31-8271-0e6a4c9b7d33 2026/0274 Förderantrag Photovoltaik-Anlage
6f3e2d81-5a7c-4b09-9e14-2c8b1a6f4e07 2026/0603 Bürgeranfrage Straßenbeleuchtung
9b0c7a34-8e1f-4d52-8a63-5f2d9c0b1e88 2025/0945 Personenstandsakte Eheschließung Berger
3c8f1b60-2a9d-4e73-9146-7b0e5a2c8d55 2026/0718 eZustellung Bescheid Abgabenkonto
useFile() mit einer unbekannten Id liefert im Mock-Modus ein leeres Objekt
{} — die Query ist dann erfolgreich, aber ohne Inhalt. Widgets zeigen dafür
den Entwickler-Hinweis zum fehlenden Pfad, nicht einen Ladefehler.
Kein Eintrag hat fileHandlingState: "Destroyed". Der Filter, den useFiles
gegen das echte Backend anwendet, ist im Mock-Modus also nie sichtbar.
Gruppen und User — useGroups, useUsers
Beide liefern Group-Objekte mit name, $apiId und referencedApiType:
useGroups() — 7 Einträge | useUsers() — 5 Einträge |
|---|---|
| Rechtsabteilung | Maria Huber |
| Baubehörde | Julia Wolf |
| Bürgerservice | Peter Lang |
| Finanzverwaltung | Sabine Mayr |
| Gewerbereferat | Thomas Berger |
| Standesamt | |
| Umweltreferat |
Diese Namen und Ids sind dieselben wie in leadingGroup / leadingUser der
Akten. Deshalb erkennt Assignment im Mock-Modus die
bestehende Zuordnung und markiert sie vor — der Abgleich läuft über $apiId.
Gespeichert wird trotzdem nichts (siehe Grenzen).
Dokumente — useDocumentsByFile
Drei Einträge in der Form DocumentSummary (name, size, url), identisch
für jede File-Id:
name | size (Bytes) | url |
|---|---|---|
Auskunftsersuchen_Steiner.pdf | 84233 | https://example.com/mock/… |
Bescheid_Abgabenkonto.pdf | 152119 | https://example.com/mock/… |
Bauplan_Musterstrasse_12.pdf | 1048576 | https://example.com/mock/… |
Die url zeigt auf example.com — der Download-Link ist also klickbar, führt
aber ins Nichts.
Personen — usePersons, usePerson
Sechs Einträge mit $apiType: "Person", $apiId, firstName, lastName und
fullName: Anna Steiner, Bernhard Gruber, Christina Fischer, Daniel Moser,
Elisabeth Pichler, Florian Reiter. Wie bei den Akten liefert usePerson() mit
unbekannter Id ein leeres Objekt.
Denk daran, dass usePersons nur mit enabled: true läuft — sonst bleibt die
Liste auch im Mock-Modus leer.
Grenzen
- Kein Schreiben.
useSaveFiletäuscht Erfolg vor, ohne etwas zu ändern. Eine gespeicherte Zuweisung ist nach dem Reload wieder weg. - Keine Mock-Daten für
/Me. Das Profil-Menü fällt damit auf die Token-Claims zurück (Name, Benutzername, E-Mail aus Keycloak). - Die Mock-Daten sind nicht öffentlich importierbar. Sie liegen in der
Library und lassen sich nicht aus
@gentics/cockpit/coreimportieren oder erweitern. Wer eigene Testdaten braucht, gibt sie über eine eigene Datenquelle hinein —staticSourcebzw. ein eigenes Store-Bündel.
