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 /api → http://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:#ffffffauf--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-dev→cd 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.tsin tenant-ui ist bereits env-parametrisiert. Ein echtes Shared-Paketfrontend/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.