# HiveGrid Integration-Contracts **Zweck:** Verbindliche Docking-Points, an denen jedes neue Modul anschließt. Wenn ein AI-Coding-Agent eine Funktion baut, ohne sie hier anzudocken, ist die Funktion **nicht erreichbar** und das Milestone gilt als nicht erledigt. Diese Datei ist Quelle der Wahrheit für: Sidebar-Gruppen, Route-Patterns, Breadcrumbs, Permissions, Search-Index, Activity-Stream, Audit, i18n. > **Naming-/Pfad-Realität (Stand 2026-06-04, siehe `docs/ARCHITECTURE-DECISIONS.md` AD-10):** > Das HiveGrid-UI-Paket heißt real **`packages/ui-app`** (nicht `packages/ui-hivegrid`). Die > Sidebar-Konfiguration liegt in **`apps/web/app/application/sidebar/`** (nicht `sidebar.config.ts`). > Der Such-Index / `indexedEntities` gehört nach **`packages/search/`** (nicht > `packages/db/src/search/indexed-entities.ts`). Geteilte Pakete bleiben `@toolctrl/*`; neue > HiveGrid-Querschnittsdienste (z.B. Activity/`logActivity`) werden als neues `@hivegrid/*`-Paket > angelegt. Lies die `ui-hivegrid`/`@hivegrid/*`/`sidebar.config.ts`-Erwähnungen unten entsprechend. ## 1. App-Shell-Sidebar (Mandanten-App `app.hivegrid.de`) Sidebar ist in 6 feste Gruppen geteilt. Jede neue Funktion gehört in **genau eine** Gruppe: ``` 🏢 ARBEITEN ├─ Dashboard → M147-M150 ├─ Globale Suche (Cmd-K) → M058 ├─ Lead-Inbox → M047-M051 ├─ Verkaufsfälle (Sales) → M059-M064 ├─ Aufträge (Projekte) → M065-M070 ├─ Aufgaben → M071-M074 ├─ Termine → M120 └─ Buchungs-Seiten → M126-M129 📇 STAMMDATEN ├─ Kontakte → M042-M046 ├─ Firmen → M042-M046 ├─ Dateien → M052-M055 ├─ Tags / Schlagwörter → M044 └─ Custom-Felder → M044 📨 KOMMUNIKATION ├─ Email-Inbox → M096-M099 ├─ Marketing-Kampagnen → M100-M104 ├─ Telefonie → M105-M109 ├─ Team-Chat → M110-M114 └─ Notification-Center (Glocke) → M115-M117 ⚙️ AUTOMATION & KI ├─ Automation-Studio → M075-M082 ├─ KI-Sprach-Assistent → M086 └─ Vorlagen / Templates → M082 👥 PERSONAL (HR-Hub) ├─ Schwarzes Brett → M137 ├─ Mitarbeiter-Verzeichnis → M133 ├─ Urlaub / Krankheit → M135, M136 ├─ Lohnabrechnungen → M134 ├─ Anträge → M139 └─ Passwort-Safe → M134 🛠️ EINSTELLUNGEN (Mandanten-Admin) ├─ Mandant (Stammdaten) → M018 ├─ Mitarbeiter & Rollen → M023, M024 ├─ Pipelines → M059, M065 ├─ Forms → M121-M125 ├─ Integrationen (Apps) → M008-M010 ├─ KWP-Verbindung → M089-M095 ├─ Audit-Log → M151 ├─ Subscription / Abo → M153-M156 ├─ Branding / Design → M030 └─ DSGVO / Datenschutz → M164-M166 ``` **Plattform-Admin-App `admin.hivegrid.de`** hat eine separate Sidebar-Struktur (siehe M034-M041). **Customer-Portal** hat eigene minimale Navigation (siehe M141-M146). **Mobile-App-Tab-Bar** ist UI-Modus-abhängig — siehe M169. ## 2. Sidebar-Wiring-Pflicht Jeder neue Modul-Bereich muss in `packages/ui-hivegrid/src/app-shell/sidebar.config.ts` registriert werden. Das ist die **einzige** Quelle der Wahrheit für Sidebar-Inhalte. ```ts // packages/ui-hivegrid/src/app-shell/sidebar.config.ts export type SidebarEntry = { id: string; // eindeutig, kebab-case group: 'arbeiten' | 'stammdaten' | 'kommunikation' | 'automation-ki' | 'personal' | 'einstellungen'; label: string; // i18n-Key, kein Hardcoded-String icon: string; // lucide-react Icon-Name route: string; // RR7-Route-Pattern permission: string | string[]; // Permission-Key aus M004; Eintrag nur sichtbar wenn berechtigt badge?: 'count' | 'dot' | null; // Live-Counter (z.B. ungelesene Mails) order: number; // Sortierung innerhalb der Gruppe feature_flag?: string; // optional: Sidebar-Eintrag nur sichtbar wenn Flag aktiv }; ``` **Regel:** Eine neue Funktion ohne Sidebar-Eintrag (oder ohne Inline-Verlinkung aus einem bestehenden Modul) wird **nicht akzeptiert**. ## 3. Route-Patterns Verbindliche URL-Schemata. Abweichungen erfordern Change-Request. | Bereich | Pattern | Beispiel | |---|---|---| | Mandanten-App | `app.hivegrid.de//...` | `app.hivegrid.de/sales/D-2026-0042` | | Mandant-Settings | `app.hivegrid.de/settings//...` | `app.hivegrid.de/settings/integrationen/kwp` | | Plattform-Admin | `admin.hivegrid.de//...` | `admin.hivegrid.de/mandanten/123` | | Customer-Portal | `app.hivegrid.de/portal//...` | `app.hivegrid.de/portal/geerkens/projekte/42` | | Public-Forms | `app.hivegrid.de/f//` | `app.hivegrid.de/f/geerkens/kontaktformular` | | Public-Booking | `app.hivegrid.de/b//` | `app.hivegrid.de/b/geerkens/wartungstermin` | | Public-Help | `app.hivegrid.de/help///` | `app.hivegrid.de/help/de/integrationen/kwp-setup` | | Public-API | `app.hivegrid.de/api/v1/` | `app.hivegrid.de/api/v1/contacts` | | **Connector-OAuth-Callback** | `app.hivegrid.de/integrations//oauth/callback` | `app.hivegrid.de/integrations/google/oauth/callback` | Die Tenant-Slug ist **nicht** im Pfad bei der Mandanten-App — Tenant kommt aus Subdomain oder Session. **Externe OAuth-Connectors (Google-Kalender, KWP, später AirCall/Microsoft/…):** Der Redirect-/Callback-Endpoint liegt **immer** auf `app.` (nicht `api.`) unter `/integrations//oauth/callback` — konsistent zu den Connector-Settings-Screens (`/settings/integrations/`, `/integrations//…`). Begründung: Der OAuth-Rückweg ist browser-basiert und braucht die an `app.` gebundene Nutzer-Session, um den Token dem richtigen Nutzer/Tenant zuzuordnen; `api.hivegrid.de` (token-basiert, ohne Session) ist dafür nicht geeignet. Tenant wird im OAuth-`state` mitgeführt (eine feste Callback-URL je Umgebung, kein Tenant im Pfad). Pro Umgebung registriert: `app.hivegrid.de`, `staging-app.hivegrid.de`, `localhost:3000`. Das frühere `/api/apps//oauth/callback` ist abgelöst (2026-06-21). ## 4. Breadcrumbs-Konvention Jede Detail-Route hat einen Breadcrumb in `packages/ui-hivegrid/src/breadcrumbs/`. Format: ``` [Sidebar-Gruppe] > [Modul] > [Listen-Name] > [Detail-Titel] ``` Beispiel: `Arbeiten > Verkaufsfälle > Anlagen-Verkauf > D-2026-0042 Heizung Schmidt` ## 5. Activity-Stream-Wiring (M045) Jede schreibende Aktion in jedem Modul muss einen Activity-Stream-Eintrag erzeugen, falls die betroffene Entität einen Stream hat (Kontakt, Firma, Lead, Verkaufsfall, Auftrag). **Pflicht-Service-Aufruf:** ```ts import { logActivity } from '@hivegrid/activity'; await logActivity({ tenantId, actorId, actorType: 'user' | 'auto' | 'sync' | 'system', entityType, entityId, type: 'contact.created' | 'deal.stage_changed' | ..., payload: { ... }, // Human-readable Klartext + ggf. Vorher/Nachher }); ``` Der `logActivity`-Aufruf ist NICHT der Audit-Eintrag — der entsteht zusätzlich automatisch via M001 Prisma-Middleware. ## 6. Audit-Wiring (M001) Audit ist **automatisch** via Prisma-Middleware. Agent muss nichts manuell aufrufen. Wenn etwas im Audit fehlt: Bug in M001-Middleware, nicht im Modul-Code. **Ausnahme:** Aktionen außerhalb von Prisma (z.B. Datei-Upload zu S3, externe Webhook-Aufrufe) müssen manuell auditiert werden: ```ts import { auditEvent } from '@hivegrid/audit'; await auditEvent({ ... }); ``` ## 7. Notification-Wiring (M011) Wenn ein Modul Mitarbeiter benachrichtigen soll: NIE direkt Email/Push senden. Immer: ```ts import { notify } from '@hivegrid/notifications'; await notify({ tenantId, recipientId, category: 'lead.received' | 'deal.won' | ..., template: 'lead-received', // i18n-Key in M003 payload: { ... }, priority: 'normal' | 'high' | 'urgent', }); ``` Der Notification-Service in M011 entscheidet Kanal (In-App/Email/Push), Bündelung (M117), Stille-Stunden (M116). ## 8. Search-Index-Wiring (M056) Neue Entität, die in der globalen Suche auftauchen soll, muss in `packages/db/src/search/indexed-entities.ts` registriert sein: ```ts export const indexedEntities = { contact: { table: 'Kontakt', searchable: ['displayName', 'email', 'phone'], permission: 'crm.read' }, deal: { table: 'Verkaufsfall', searchable: ['title', 'description'], permission: 'sales.read' }, // ... }; ``` Permission-aware Search (M057) liest diese Map. ## 9. i18n-Wiring (M003) Alle UI-Texte als i18n-Keys. Locale-Files unter `packages/i18n/src/locales/de/.json`. ESLint-Rule blockt JSX-Text-Nodes ohne `t()`. Build-Lint blockt Hard-Strings im Build. Naming: `..`, z.B. `sales.kanban.empty_state` oder `auth.login.password_too_short`. ## 10. Permission-Keys (M004) Permission-Definition als TypeScript-Konst in `packages/entitlements/src/permissions.ts`: ```ts export const Permissions = { CRM_READ: 'crm.read', CRM_READ_OWN: 'crm.read_own', CRM_WRITE: 'crm.write', SALES_READ: 'sales.read', SALES_VOLUME_READ: 'sales.volume.read', // separater Key für Volumen-Sichtbarkeit (siehe 3.11.2) // ... } as const; ``` Sidebar-Einträge, Routes, View-Models, Actions filtern alle gegen diese Keys. ## 11. View-Model-Pflicht (M013) JEDE Route in `app/routes/**` baut im Loader ein View-Model. UI-Components in `packages/ui-hivegrid/**` empfangen NUR das VM und Action-Callbacks. Siehe AGENTS.md. ## 12. Smoke-Test pro Route (Pflicht) Jede neue Route bekommt einen Smoke-Test in `e2e/smoke/.spec.ts`: ```ts test('contact-detail loads with view-model', async ({ page }) => { await loginAs(page, 'geerkens-admin'); await page.goto('/kontakte/test-kontakt-id'); await expect(page.getByRole('heading', { name: /kontakt/i })).toBeVisible(); }); ``` Smoke-Tests laufen im Hot-Swap-Drill (M017) gegen `packages/ui-mock` — wenn sie da brechen, ist die Hot-Swap-Architektur verletzt. ## 13. Mobile-Tab-Bar-Wiring (M169) Neue Funktionen, die in der Mobile-App relevant sind, müssen in `packages/ui-hivegrid/src/mobile/tab-bar.config.ts` für mindestens einen UI-Modus (Baustelle / Büro) registriert sein. ## 14. Customer-Portal-Wiring (M142, M143) Funktionen, die Endkunden sehen sollen, müssen explizit in `packages/ui-hivegrid/src/customer-portal/portal-shell.config.ts` registriert sein. Default-Sichtbarkeit: **NICHT sichtbar**. Tenant-Admin gibt explizit frei (M142.5).