Zum Inhalt

PORTING.md — operator-ui Scaffolding-Entscheidungen

Dieses Dokument hält die Pre-Flight-Analyse aus Block 7.1b fest: welche Teile des bereits existierenden frontend/tenant-ui/ als Vorlage für frontend/operator-ui/ portiert werden und welche wir neu bauen, weil sie tenant-spezifisch sind. Zweck: spätere Leser (Code-Review, 7.2/7.3/7.4-Autoren) verstehen ohne Git-Archäologie, warum diese Datei oder jenes Muster hier so steht wie es steht.

1 — Portierungstabelle

1:1-portiert (identische Funktion, Env-Werte tauschen reicht)

Datei (tenant-ui) Begründung
src/composables/useApi.ts Kein tenant-spezifischer Code; nutzt useAuth als Token-Quelle und VITE_API_BASE_URL als Base. Eins-zu-eins übernommen.
src/pages/CallbackPage.vue OAuth-Code-Exchange ist realm-agnostisch — der Callback-Handler delegiert an useAuth.handleCallback. Ziel-Redirect nach Login wird zur Laufzeit aus sessionStorage["post_login_redirect"] gezogen, nicht hart verdrahtet.
src/pages/LogoutPage.vue Trigger-Only; nutzt useAuth.logout().
src/App.vue <BaseLayout v-if="useChrome"><router-view/>-Muster mit route.meta.layout === "bare"-Check ist projektneutral.
src/main.ts Vue-Bootstrap; zwei Zeilen Änderung (Router-Import-Pfad, ggf. Title).
src/env.d.ts Vite-Env-Interface.
eslint.config.js Flat-ESLint-Config mit vue + ts-rules.

Portiert mit Parameter-/Konfig-Anpassung

Datei (tenant-ui) Anpassung in operator-ui
src/composables/useAuth.ts Default-Env: VITE_KEYCLOAK_REALM=kora-platform, VITE_KEYCLOAK_CLIENT_ID=operator-ui, redirect=/admin/operator/auth/callback, post-logout=/admin/operator/. Neu: Route-Guard liest zusätzlich das realm_access.roles-Array und redirectet auf /403, wenn operator-admin fehlt (in 7.1a-Backend enforced, Guards liefern die UX im Frontend).
src/router/index.ts Basis /admin/operator/ statt /tenant/. Routen: /tenants, /tenants/new, /tenants/:id, /tenants/:id/edit, /403, /404. Guard erweitert um Operator-Rolle.
src/layouts/BaseLayout.vue Sidebar-Items auf Operator-Scope: "Tenants" aktiv, "Templates" / "Module" / "Audit-Log" als Block-7.x-Placeholder. Topbar-Badge "Operator" in Accent-Farbe.
src/pages/NotFoundPage.vue Zurück-Link auf /tenants.
src/assets/styles.css Token-Datei getrennt (src/styles/tokens.css). Nur --kora-primary und --kora-primary-hover werden überschrieben (Branding-Entscheidung, siehe §2). Alle anderen Tokens (Font, Spacing, Radius, Shadow) bleiben identisch.
vite.config.ts base: "/admin/operator/", server.port: 5174. Proxy /apihttp://localhost:8280 bleibt.
tsconfig.json Identisch mit Ausnahme des Project-Namens/Paths.
vitest.config.ts Identisch.
playwright.config.ts baseURL passt per E2E_BASE_URL-Override oder http://localhost:8280/admin/operator/.
index.html <title>Kora Platform — Operator</title>.
e2e/helpers.ts Nutzt TOKEN_OPERATOR_ADMIN statt TOKEN_TENANT_A_ADMIN.

Neu gebaut (nicht in tenant-ui vorhanden)

Datei (operator-ui) Begründung
src/composables/useTenants.ts + useTenant.ts API-Wrapper für /api/v1/platform/tenants/*; tenant-ui hatte nur ein Template-Update-Composable.
src/components/DataTable.vue Generische Tabellen-Komponente. Bewusst minimal (Slot-basierte Columns, Pagination-Event, Empty/Loading/Error-Slots) — 7.2/7.3/7.4 können sie direkt wiederverwenden. Keine eingebaute Sortier-UI (Feature-Creep).
src/components/FormInput.vue / FormTextarea.vue / FormButton.vue Styling-konforme Form-Primitives mit Fehler-Slot und disabled-Handling. Tenant-ui arbeitet mit inline-<input>; wir brauchen für mehrere Tenant-Formulare wiederverwendbare Teile.
src/components/Toast.vue + useToast.ts Globale Toast-Queue. Tenant-ui hat keinen Toast-Mechanismus. Ein <ToastContainer> im App.vue-Root; Composable-API useToast().success(...) / .error(...) / .info(...).
src/components/ConfirmDialog.vue + useConfirm.ts Imperativer Confirm-Promise (analog zu window.confirm, nur hübsch). Für Soft-Delete + "Verlassen ohne Speichern".
src/components/Breadcrumbs.vue, PageHeader.vue Leere Bereiche im BaseLayout, werden pro Seite gefüllt.
src/pages/ForbiddenPage.vue 403-Seite — tenant-ui hat keine; Operator-Fehlerpfad ist relevanter, weil mehr Route-Classes Role-Checks haben.
src/pages/TenantsListPage.vue, CreatePage.vue, DetailPage.vue, EditPage.vue 7.1a-Scope.
src/types/tenant.ts TenantRead, TenantList-Shape. Gespiegelt aus src/kora_platform/models/tenant.py.

Bewusst nicht portiert

Datei (tenant-ui) Grund
src/pages/DashboardPage.vue, ChatbotDetailPage.vue Tenant-seitige Seiten mit Chatbots-Content, irrelevant für Operator.
src/components/TemplateUpdateBanner.vue + -Dialog.vue Template-Update-UX ist Tenant-Seite; Operator-Templates-Verwaltung ist 7.2-Scope.
src/types/api.ts Enthält chatbot-template-spezifische Types. Operator-UI bekommt eigenes src/types/tenant.ts.

2 — Branding-Entscheidung (Pre-Flight 1d)

Kriterien laut Prompt:

  • Klare Unterscheidung zum Tenant-UI (Kora-Grün #0f6e56)
  • WCAG AA (≥ 4.5:1) auf Weiß
  • Nicht rot (Error-Farbe)
  • Nicht zu nah am Grün
  • Industrie-Konvention für Admin/Meta-Surfaces

Wahl: --operator-primary: #3730a3 (Indigo-800, Tailwind-Referenz).

Begründung:

  • Kontrast auf #ffffff: 10.79:1 (WCAG AAA) — deutlich über der 4.5:1-Bar.
  • Kontrast bei color:#ffffff auf --operator-primary-Background (Topbar, Buttons): 10.79:1.
  • Abstand zu Kora-Grün: Hue 240° vs. 165°, also 75° Rotation — klar unterscheidbar auf einen Blick.
  • Semantik: Indigo ist der traditionelle Admin-Farbkanon (Django Admin Violet, Stripe Dashboard Indigo, GitHub Settings, WordPress Backend "Blue"). Operator-User erwarten das unbewusst.
  • Keine Rot-Anmutung, keine Verwechslung mit Error-States.
  • Hover: #312e81 (Indigo-900) — Kontrast 12.29:1.

Zusätzliches Differenzierungsmerkmal: kleine OPERATOR-Badge in der Topbar neben dem Brand-Text, Accent-Farbe in --operator-accent (#f59e0b, Amber-500, Kontrast auf weißem Badge-Hintergrund 3.13:1, aber Badge-Text steht auf --operator-primary mit weißer Schrift → da gilt der Primary-Kontrast). Das Badge ist optisch dezent (10px, halb- transparent), betont aber den Scope-Unterschied beim ersten Blick.

Alle anderen Tokens (Typo, Spacing, Radius, Shadow, Font-Stack, Dark-Mode-Werte) bleiben identisch zu tenant-ui — das Erkennungs- merkmal ist allein die Primary-Farbe + Badge.

3 — Dev-Workflow (Pre-Flight 1c)

Spiegelt tenant-ui 1:1:

  • make operator-ui-devcd frontend/operator-ui && npm install && npm run dev
  • Vite lauscht auf Port 5174 (nicht 5173, der ist für tenant-ui)
  • Proxy /api/*http://localhost:8280 (Platform-API)
  • Keycloak unter http://localhost:8236 (wie tenant-ui)

Kein Docker-Compose-Service für operator-ui im Dev. Dev läuft auf dem Host, die Build-Stage im zentralen Dockerfile.platform liefert das statische Bundle für Prod — analog zu tenant-ui.

4 — Scope-Boundary-Log

Was könnte generalisiert werden, bleibt aber bewusst auf 7.1b-Scope:

  • useAuth.ts in tenant-ui ist bereits env-parametrisiert. Ein echtes Shared-Paket frontend/kora-auth/ (npm workspace) wäre sauberer, spart ~200 LOC Duplikation. Nicht jetzt — Workspace-Setup + build-chain-Änderungen sprengen 7.1b-Budget. Als TODO für post-7.4.
  • <DataTable> könnte Sort-UI, Filter-Pipes und Virtualisierung mitbringen. Nicht jetzt — 7.1b-Tenants-List braucht keins davon, und ohne Use-Case ist das Premature Generalization.
  • Ein Root-weiter Toast-Singleton-Store statt Composable-Queue ist bei tenant-ui + operator-ui + widget in einem Top-Level-Tab-Setting eventuell angenehmer. Nicht jetzt — operator-ui nutzt eigenen, tenant-ui hat keinen, keine Shared-Surface zwingend.

Diese bewusst ungefangenen Möglichkeiten werden im Abschluss-Report zu 7.1b als offene Fragen für 7.2 und später weitergereicht.