Zum Hauptinhalt springen
Sachbearbeiter-Cockpit

Mandanten (Tenant)

Jeder ActaNova-Aufruf ist mandantenbezogen: die Services setzen den Tenant in den Pfad (/Tenants/Items/<tenant>/…). Damit du ihn nicht durch die halbe App durchreichen musst, hält die Library ihn in einem Context — pro App, nicht cockpit-weit.

Deklaration an der App

Welche Mandanten eine App bedient, steht in ihrer Definition:

const apps: CockpitApp[] = [
{ id: 'akten', title: 'Akten', description: '…', tenant: ['Stadt'], component: AktenApp },
{ id: 'anträge', title: 'Anträge', description: '…', tenant: ['Stadt', 'Bezirk'], component: AntraegeApp },
];

Daraus ergibt sich das Verhalten beim Öffnen der App:

tenantVerhalten
genau ein EintragDieser Mandant ist aktiv. Der User sieht keine Auswahl.
mehrere EinträgeEs erscheint ein Auswahl-Gate vor der App („Mandant wählen"). Die Wahl wird gemerkt und beim nächsten Mal übersprungen.
leeres ArrayFehlkonfiguration: die Library wirft App "<id>" hat keinen Tenant definiert (CockpitApp.tenant ist leer).

Gemerkt wird die Wahl im localStorage unter cockpit:tenant:<appId> — also getrennt pro App. Ein gespeicherter Wert wird nur übernommen, wenn er noch in tenant steht; entfernst du einen Mandanten aus der App-Definition, fragt das Gate erneut.

Den aktiven Mandanten lesen

import { useTenant } from '@gentics/cockpit/core';

function Kopfzeile() {
const tenant = useTenant(); // z. B. "Stadt"
return <span>Mandant: {tenant}</span>;
}

useTenant() muss innerhalb einer App laufen.

Außerhalb von React — in Services, Hilfsfunktionen, Event-Handlern ohne Component-Kontext — gibt es getActiveTenant(). Es liefert denselben Wert, wirft aber nicht, sondern gibt null zurück, wenn gerade keine App läuft:

import { getActiveTenant } from '@gentics/cockpit/core';

const tenant = getActiveTenant(); // "Stadt" oder null

Darauf baut auch makeCall auf: Service-Aufrufe brauchen den Tenant deshalb nicht als Parameter.

Einen Umschalter bauen

Für Apps mit mehreren Mandanten liefert useTenantSelection() alles, was ein eigener Umschalter braucht:

import { useTenantSelection } from '@gentics/cockpit/core';

function MandantWechseln() {
const { tenant, available, setTenant } = useTenantSelection();

if (available.length < 2) return null;

return (
<select value={tenant} onChange={(e) => setTenant(e.target.value)}>
{available.map((t) => (
<option key={t} value={t}>{t}</option>
))}
</select>
);
}

setTenant ignoriert Werte, die nicht in available stehen, und schreibt die neue Wahl in den localStorage.

Der Wechsel wirkt sofort: der Tenant steckt in den Query-Keys aller Store-Hooks (["files", tenant], ["file", tenant, id], …). React Query lädt damit die Daten des neuen Mandanten und hält die des alten weiter im Cache.

Einzelne Aufrufe auf einen anderen Mandanten richten

Jeder Store-Hook nimmt optional einen Tenant. Explizit übergeben schlägt den Context:

useFiles(); // aktiver Mandant aus dem Context
useFiles('Bezirk'); // gezielt dieser Mandant

useFile(id);
useFile(id, 'Bezirk');

usePersons({ enabled: true, tenant: 'Bezirk' });
usePerson(id, { tenant: 'Bezirk' });
useDocumentsByFile(fileId, { enabled: true }, 'Bezirk');

Fehlt beides — kein Parameter und kein Context —, wirft der Hook:

Kein Tenant verfügbar: weder als Parameter übergeben noch aus dem Context.
Hook innerhalb einer App (TenantProvider) aufrufen oder tenant explizit übergeben.

Das ist der typische Fehler, wenn ein Store-Hook außerhalb einer App verwendet wird. Der Weg daraus ist immer einer der beiden im Text genannten.

Nicht mandantenbezogen sind die /Me-Aufrufe: useCurrentUser() nimmt deshalb keinen Tenant-Parameter und funktioniert auch außerhalb einer App.

Was der Mandant nicht ist

  • Kein URL-Bestandteil. Der aktive Mandant steht nicht im Pfad — ein Link auf /akten/<id> zeigt beim Empfänger den Datensatz seines gemerkten Mandanten.
  • Keine Berechtigung. Dass eine App einen Mandanten anbietet, heißt nicht, dass der angemeldete User dort etwas sehen darf; das entscheidet ActaNova. Siehe Auth & Rechte.
  • Nicht cockpit-global. Zwei Apps können gleichzeitig unterschiedliche Mandanten aktiv haben. Beim Wechsel der App wird der Context neu aufgebaut.