21 KiB
Boilerplate-Abgleich — M05 Form-Builder
| Feld | Wert |
|---|---|
| Modul-ID | M05 |
| Name | Form-Builder (Formular-Werkstatt) |
| Milestones | M121 (Editor), M122 (Veröffentlichung/Sharing/Embed), M123 (Submission-Eingang/CRM-Aktionen), M124 (Conditional-Display), M125 (Rate-Limit/Captcha/DSGVO/SHK-Vorlagen) |
| Pflichtenheft-Block | Block 6, Abschnitt „Modul 5: Form-Builder" (5.1–5.13) |
| Spec | _bmad-output/specs/M05-form-builder.md (Size XL, „Voll Google-Forms-Niveau zu Phase 1") |
| Reuse-Level | medium |
Reuse-Zusammenfassung
Die Boilerplate enthält ein fertiges, aber bewusst minimales Form-Widget (packages/form-widget + prisma/schema/formWidget.prisma + apps/web/app/modules/formWidget/builder/* + Submit-Route + 3 Worker-Jobs). Es ist ein embeddable Lead-Capture-Widget, kein Google-Forms-Klon: 8 Feldtypen, 3-Tab-Builder (Palette/Canvas/Properties/Settings/Preview), Drag-&-Drop (@dnd-kit), Origin-Allowlist, Honeypot, Sliding-Window-Rate-Limit, Turnstile (feature-gegated), und Routing auf generic | crm-contact | entity-row plus Webhook-/Notify-Email-Follow-ups über Outbox.
Das deckt das Architektur-Skelett von M121–M123 und den Spam-Schutz-Kern von M125 ab. Es fehlt jedoch der Großteil des fachlichen Funktionsumfangs: 9 von 17 Feldtypen (u.a. Datei, Foto, Signatur, Adresse, Datum/Zeit, Telefon-E.164, hidden), das gesamte explizite Datenmodell der Spec (FormField/FormSubmissionAnswer/FormFile/FormLink/FormAction/FormActionLog/FormLog/FormTag), die 11 Submission-Aktionen (Lead/Deal/Projekt/Task/KWP/Automation/...), Public-URL/QR/iFrame-Auto-Resize, Submissions-Verwaltung (Liste/Filter/CSV/PDF/Re-Run), Conditional-Display (M124), und die 8 SHK-Vorlagen. Querschnitt (DB-Client/Outbox/Audit/Notifications/Feature-Flags/File-Storage/i18n/View-Models/App-Shell) ist solide vorhanden und direkt nutzbar.
Übernehmbar aus Boilerplate
| Boilerplate-Pfad | Was | Wie nutzbar |
|---|---|---|
packages/form-widget/src/schema.ts |
Zod-Schemas FormDefinitionSchema/FormFieldSchema/FormThemeSchema/FormSubmissionPayloadSchema/FieldTypeSchema; Feldtypen-Enum, Length-Validation, Duplicate-Key-Check, HONEYPOT_FIELD_KEY |
als-vorlage — Struktur 1:1, aber Feldtyp-Enum von 8 auf 17 erweitern + field_config/validation_rules (min/max_value, regex, accepted_file_types, max_file_size, custom_error_message) ergänzen |
packages/form-widget/src/origin.ts |
matchAllowedOrigin/normalizeOrigin (Wildcard-Subdomain, leere Allowlist = nicht submittbar) |
1:1 — direkt für Embed-/CORS-Schutz |
packages/form-widget/src/honeypot.ts |
isHoneypotTriggered (still 204/Silent-Drop) |
1:1 — deckt 5.9.1 ab |
packages/form-widget/src/routing.ts |
resolveSubmissionTarget/applyFieldMapping (Field-Mapping-Helper, worker-hot-path, sync) |
anpassen — Mapping-Mechanik 1:1; Target-Set von 3 (generic/crm-contact/entity-row) auf die 11 Action-Typen der Spec erweitern |
prisma/schema/formWidget.prisma (Form, FormSubmission) |
Tenant-scoped Form-Definition (JSON) + Submission-Inbox (payload JSON, ipHash SHA-256, status, result, allowedOrigins[], notifyEmails[], webhookUrl, config, key @unique) |
als-vorlage — gutes Fundament für Form+FormSubmission, aber Spec verlangt zusätzlich slug/subtype/status(draft/published/archived)/is_public/requires_login/submission_limit/submission_close_at/success_message/redirect_url und die normalisierten Tabellen (FormField, FormSubmissionAnswer, FormFile, FormLink, FormAction, FormActionLog, FormLog, Tags) |
apps/web/app/modules/formWidget/builder/FormBuilder.tsx + Canvas.tsx/SortableField.tsx/FieldPalette.tsx/FieldProperties.tsx/PreviewTab.tsx/SettingsTab.tsx/palette.ts/types.ts |
Kompletter Tab-Builder mit Drag-&-Drop-Canvas, Feld-Eigenschaften-Editor, Live-Preview, Settings (Origins/Notify/Webhook/Target/Field-Mapping), i18n-Keys durchgängig | als-vorlage — Editor-Grundgerüst & Drag-&-Drop-Pattern direkt übernehmbar; Palette von 8 auf 17 Feldtypen erweitern, FieldProperties um typ-spezifische Configs (Datei-Typen, Datums-Format, Adresse, Signatur, hidden-Prefill) ausbauen. ACHTUNG: liegt in apps/web (nicht in packages/ui-hivegrid) — für HiveGrid in den View-Model/Adapter-Layer umziehen (AGENTS Layer-Disziplin, M013/M015) |
apps/web/app/routes/api.embed.forms.$formKey.submit.tsx |
Public-Submit-Endpoint: Origin-Check → Honeypot-Silent-Drop → Sliding-Window-Rate-Limit (pro IP und pro Origin) → Turnstile (feature-flagged via evaluateFlagForContext) → ipHash → Payload-Validation gegen gespeicherte Definition → Outbox-Publish |
anpassen — deckt M125 (5.9.1–5.9.4) fast vollständig ab; Rate-Limit-/Honeypot-/Captcha-Pipeline 1:1, fehlende Stücke: Datei-Upload-Annahme, Signatur+IP/Zeitstempel-Persistenz (5.5), Anomalie-Drosselung (5.9.4) |
apps/web/app/routes/embed.forms.$formKey.tsx |
iFrame-gerenderte Public-Form-Mini-App | als-vorlage — Basis für Embed; Auto-Resize via postMessage (M122) + Public-URL-Pattern f/<tenant>/<slug> (INTEGRATION-CONTRACTS §3) ergänzen |
apps/web/app/routes/app.$tenant/forms.tsx + forms.$id.tsx |
Tenant-Admin: Form-Liste + Create (mit Submission-Count + Embed-Snippet) und Detail/Edit-Route (Loader baut VM, Action speichert via Prisma, Permission-Gate, CSRF) | als-vorlage — Route-Topologie & Loader/Action-Muster übernehmbar; Permission ist aktuell app.settings.account.update (admin-only) statt der M05-forms.*-Permissions |
apps/worker/src/jobs/form-submission-route.ts |
Outbox-getriebener Routing-Worker (generic/crm-contact/entity-row, Idempotenz via dispatch-key form-submission:<id>, Status-Rückschreibung, Fehler nicht verschluckt → BullMQ-Retry + Sentry) |
anpassen — Worker-Gerüst + Idempotenz-Muster 1:1; Target-Handler um Lead/Deal/Projekt/Task/KWP/Automation erweitern (M123, 5.7) |
apps/worker/src/jobs/form-submission-followups.ts + form-submission-webhook-delivery.ts |
Pure Follow-up-Planner (Webhook + Notify-Email als Outbox-Events) + Webhook-Delivery-Worker | 1:1 für webhook_send + send_email_to_team/send_email_to_submitter (5.7.1/5.7.2/5.7.8) |
apps/web/app/utils/turnstile.server.ts (verifyTurnstileToken) + Feature-Flag form-widget-turnstile |
Captcha-Verifikation | anpassen — vorhanden, aber Cloudflare Turnstile; M125 fordert explizit hCaptcha (EU-konform). Adapter-Tausch oder zweiter Provider nötig |
packages/db (getDbClient/runDbTransaction/outbox.ts/audit-hash.ts/dsgvo/user-fk-map.ts) |
DB-Client, Transaktionen, transaktionale Outbox (publishOutboxEvent), Audit-Hash-Kette, DSGVO-FK-Karte |
1:1 — Querschnitt-Fundament; user-fk-map.ts ist Pflicht-Ergänzung bei neuen User-FKs (Submission submitter_user_id, FormLog.created_by_user_id) |
prisma/schema/core.prisma (AuditEvent, FileObject/FileUploadProgress/FileChunk, IdempotencyKey, Event/EventWebhookAttempt) |
Audit-Trail (Hash-Kette, dataSensitivity/regulatoryScope), File-Storage-Metadaten, Idempotenz, Webhook-Attempts |
1:1 — AuditEvent deckt FormLog/AV#6; FileObject deckt M052-Anbindung für Datei-/Foto-Upload (5.4); IdempotencyKey für Action-Re-Run (5.8.6) |
prisma/schema/notifications.prisma + apps/web/app/modules/notifications/services/.server/NotificationService.ts |
NotificationDelivery/UserNotificationPreference/DeviceToken + Service-Layer (In-App/Email/Push) |
1:1 — notify()-Querschnitt (M011) für „neue Submission"-Notification (5.7.2, M22-Integration) |
prisma/schema/featureFlags.prisma + evaluateFlagForContext |
Feature-Flag-Engine (scope/rules/variants, append-only Audit) | 1:1 — DoD-Pflicht „Feature-Flag-fähig" (M006); bereits im Submit-Endpoint genutzt |
apps/web/app/utils/integrations/storageService.ts + apps/web/app/utils/helpers/.server/MediaService.ts |
S3/Scaleway-Storage-Service + Media-Handling | anpassen — Backend für Datei-/Foto-Upload aus Submissions (5.4.1 „landen im Tenant-Datei-Speicher", M052/M053) |
packages/exports (./csv, ./pdf, audit-pdf, EXPORT_PLAN_ROW_CAPS, S3-Multipart-Sink) + Worker export.ts/dsgvo-export.ts |
Streaming-Export CSV/PDF, Spreadsheet-Injection-Safety, DSGVO-Export | anpassen — CSV-Export der Submissions (5.8.4) + PDF-Export einer Submission (5.8.7) + DSGVO-Löschung (5.8.8) |
packages/view-models (VMState, ActionCallbacks, I18nLabel, assertI18nLabel) |
View-Model-Statusmodell + i18n-Key-Zwang | 1:1 — Konvention M013; Loader baut VM, UI empfängt nur VM+Actions (DoD Layer-Disziplin) |
apps/web/app/application/sidebar/{AppSidebar.tsx,SidebarItem.ts} + packages/ui-app Registry-Factories |
Datengetriebene Sidebar mit Permission-/Feature-Gating | als-vorlage — Sidebar-Eintrag „Forms" (Gruppe gem. INTEGRATION-CONTRACTS §1, Zeile 53 verweist bereits auf M121–M125) |
apps/web/app/components/ui/* (shadcn/Radix: tables, forms, modals, tabs, multi-select, empty/loading states, calendar) |
Komponenten-Bibliothek | 1:1 — Submissions-Liste (TableNew/ColumnSelector/TablePagination → 5.8.1–5.8.3), Filter, Empty-/Loading-States, Datums-Picker (calendar.tsx für date-Feldtyp) |
packages/db/src/notifications.server.ts + OutboxEvent (outbox.prisma) |
Transaktionales Side-Effect-Muster | 1:1 — form.submitted-Event (M17-Integration 5.7.9) + alle Actions als Outbox-Events |
qrcode.react (Root-Dependency ^4.2.0) |
QR-Code-Generator-Lib | 1:1 — QR-Code für Print-Material (M122, 5.6.5) — Lib bereits vorhanden, kein neuer Dep |
@dnd-kit/{core,sortable,utilities} (Root-Dependencies) |
Drag-&-Drop | 1:1 — Editor-Sortierung (M121, 5.3.1); bereits in Builder verwendet |
Vorhandene Daten-Modelle
| Modell | Pfad | Deckt Spec-Entität | Lücke |
|---|---|---|---|
Form |
prisma/schema/formWidget.prisma:8 |
Form (teilweise) |
Fehlen: slug, subtype, status(draft/published/archived) + published_at/archived_at, is_public, requires_login, submission_limit, submission_close_at, success_message, redirect_url, description, created_by_user_id. Vorhanden statt Felder: definition JSON, theme, targetType/fieldMapping (1 Target), allowedOrigins[], notifyEmails[], webhookUrl, config, key |
FormSubmission |
prisma/schema/formWidget.prisma:40 |
FormSubmission (teilweise) |
Vorhanden: payload JSON, sourceOrigin, ipHash, userAgent, status, result. Fehlen: submitter_user_id/submitter_email/submitter_phone, linked_entity_type/linked_entity_id, is_complete, archived_at, submitted_at (nur createdAt) |
AuditEvent |
prisma/schema/core.prisma |
FormLog (AV#6) |
Generisches Audit mit Hash-Kette; reicht als FormLog oder als Vorlage für dediziertes FormLog-Modell |
FileObject/FileUploadProgress/FileChunk |
prisma/schema/core.prisma |
FormFile (Storage-Seite) |
FormFile-Join (submission_id/field_key/file_id) selbst fehlt; Storage-Metadaten vorhanden |
OutboxEvent |
prisma/schema/outbox.prisma |
Action-/Event-Dispatch | Trägt form.submission.*-Events; FormAction/FormActionLog als eigene Modelle fehlen |
NotificationDelivery etc. |
prisma/schema/notifications.prisma |
M22-Notification bei Submission | 1:1 |
FeatureFlag |
prisma/schema/featureFlags.prisma |
Feature-Flag-Querschnitt | 1:1 (form-widget-turnstile existiert bereits) |
app.prisma (leerer Platzhalter) |
prisma/schema/app.prisma |
— | Vorgesehener Ort für die neuen HiveGrid-Form-Modelle (FormField, FormSubmissionAnswer, FormFile, FormLink, FormAction, FormActionLog, FormLog, FormTag) |
Nicht vorhandene Spec-Modelle (komplett neu): FormField (normalisiert — BP speichert Felder als JSON in Form.definition), FormSubmissionAnswer (pro Feld/Submission — BP speichert alles als payload JSON), FormFile, FormLink (personalisierte Tracking-Links mit Token/expires/usage_limit), FormAction, FormActionLog, FormLog (dediziert), FormsTag/FormTag.
Lücken / komplett neu zu bauen
Feldtypen (M121, 5.1): 9 von 17 fehlen — number ist da, aber nicht date/datetime/time, multiselect, boolean(toggle), url, phone (E.164-Norm), file, photo (Mobile-Kamera-Direktzugriff), signature (Touch — kein signature_pad/Signatur-Komponente in der Boilerplate vorhanden), address (Auto-Complete via Maps-Adapter M25), section_header, description_block (Markdown), hidden (URL-Param-Prefill). BP hat nur text/email/number/textarea/select/radio/checkbox/honeypot.
Validation (M121, 5.2): BP hat min/max_length + required. Fehlen: min_value/max_value, regex_pattern, accepted_file_types[]/max_file_size_mb, custom_error_message (Klartext pro Feld, 5.13).
Veröffentlichung/Sharing/Embed (M122, 5.6): Public-URL-Pattern app.hivegrid.de/f/<tenant>/<slug> (INTEGRATION-CONTRACTS §3) — BP nutzt embed.forms.$formKey (Key, kein slug/tenant-Pfad). is_public/requires_login-Gating fehlt. iFrame-Auto-Resize (postMessage) fehlt. QR-Code-Generator fehlt (Lib qrcode.react vorhanden, Integration neu). FormLink (personalisierte Tracking-Links mit Token/expires/usage_limit, 5.6.2) komplett neu. JS-Snippet/embed.js (5.6.4) — BP hat Embed-Script-Base, aber CORS-konformes Inline-Snippet neu zu verifizieren.
Submission-Aktionen (M123, 5.7): BP routet nur auf crm-contact/entity-row + Webhook + Notify-Email. Fehlen: create_lead (M2), create_deal mit Pipeline-Wahl (M3), create_project (M4), create_task (M26), link_to_existing_entity (Auto-Match Email/Phone), trigger_automation (M17), kwp_push (M19). Mehrere Actions in Reihenfolge pro Form + Conditional Actions (filter_rules) + per-Action-Field-Mapping als Konfig — BP kennt nur 1 Target pro Form.
Datei-/Foto-/Signatur-Submission (M123, 5.4/5.5): Datei-Upload-Annahme im Submit-Endpoint fehlt (Schema schließt File explizit aus). Foto-Upload mit Kamera (mobile). Touch-Signatur mit IP+Zeitstempel-Persistenz (rechtliche Beweiskette, 5.5.2). File-→-Tenant-Storage-Verknüpfung (FormFile).
Submissions-Verwaltung (M123, 5.8): BP zeigt nur Submission-Count auf der Liste. Komplett neu: Submission-Liste mit Spalten-Konfiguration (5.8.3), Filter nach Datum/Submitter/Entität, Detail-Ansicht mit allen Antworten + Metadaten + Action-Status + File-Vorschau, Action-Re-Run (5.8.6), CSV-Export (5.8.4 — packages/exports als Basis), PDF-Export (5.8.7 — packages/exports/pdf), Archivieren/DSGVO-Löschen (5.8.8).
Conditional-Display (M124, 5.12.1-as-Phase-1-Variante): Show/Hide-Logik basierend auf anderem Feldwert (ODER/UND), im Editor setzbar + im Renderer ausgewertet — komplett neu (BP-Schema kennt kein conditional_visibility). KEIN Branching (Anti-Liste).
Spam-Schutz (M125, 5.9): Honeypot ✅, Rate-Limit pro IP ✅, Origin-Check ✅, Captcha ✅ (aber Turnstile statt hCaptcha — Provider-Tausch nötig, 5.9.2). Fehlt: Anomalie-Drosselung bei verdächtigen Mustern (sehr schnelles Ausfüllen / mehrfach gleicher Inhalt, 5.9.4).
SHK-Branchen-Vorlagen (M125, 5.10): Alle 8 Vorlagen (Kontakt-Anfrage, Sales-Aufnahme, Foto-Checkliste, Acceptance-Protokoll, Wartungs-Buchung, Krankmeldung, Urlaubsantrag, Customer-Feedback) als deklarative Seed-Konfiguration (LP-3/AV#3) — komplett neu, an M158-Provisionierung gekoppelt.
Permissions (M05 Block J): Die 8 atomaren forms.*-Permissions + 4 Bundles + Default-Rollen-Mapping (SHK-Rollen aus M023) — neu zu seeden. BP-Forms nutzen ein einzelnes app.settings.account.update.
i18n: BP-Builder nutzt settings.forms.builder.*-Keys (Namespace settings/translations). HiveGrid-eigene Keys (de/en) für alle neuen Felder/Texte (M003-Build-Lint blockt sonst).
Layer-Disziplin: Boilerplate-Builder + Routen liegen in apps/web/app (gemischter Loader/UI-Layer). HiveGrid verlangt UI in packages/ui-hivegrid (kein Prisma-Import), Loader baut VM, Subframe via Adapter (M013/M015, DoD). Beachte MEMORY: jede Route MUSS mcp__subframe__design_page nutzen; .server.ts-Re-Export-Blocker → nur Component-Extraktion.
Empfohlenes Vorgehen
-
Datenmodell zuerst (M121-Basis):
formWidget.prismaals Vorlage nehmen, aber das volle Spec-Schema inprisma/schema/app.prismabauen —Form(alle Stammdaten-Felder +slugunique pro Tenant +subtype/status), normalisierteFormField,FormSubmission+FormSubmissionAnswer,FormFile,FormLink,FormAction,FormActionLog,FormLog,FormTag. Entscheidung normalisiert-vs-JSON bewusst treffen (Spec listet beide normalisierten Tabellen UNDsubmission_dataJSON — Hybrid wie BP ist legitim, aberFormField-Normalisierung ist für Editor/Spalten-Konfig/Conditional-Display sinnvoll). User-FKs indsgvo/user-fk-map.tsergänzen. -
Zod-Schema erweitern (M121):
packages/form-widget/src/schema.tsals Startpunkt kopieren/erweitern (eigenes HiveGrid-Paket oder erweitern), Feldtyp-Enum auf 17 erweitern, typ-spezifischefield_config+ vollevalidation_rules+ Klartext-custom_error_message. -
Editor übernehmen + ausbauen (M121):
FormBuilder.tsx+ Canvas/@dnd-kit-Drag-&-Drop + FieldProperties + PreviewTab als Vorlage in den View-Model/Adapter-Layer ziehen (Layer-Disziplin!). Palette auf 17 Feldtypen, FieldProperties um typ-spezifische Configs, Live-Preview + Klartext-Validation (5.13.1/5.13.2). Neue Components: Signatur-Pad (neuer Dep, z.B.signature_pad), Foto/Kamera, Adress-Autocomplete (M25), Datums-Picker (calendar.tsxvorhanden). -
Submit-Endpoint + Spam-Schutz (M125 vorziehen, da BP es fast fertig hat):
api.embed.forms.$formKey.submit.tsxals Vorlage — Honeypot/Rate-Limit/Origin/ipHash 1:1, Turnstile → hCaptcha tauschen, Anomalie-Drosselung ergänzen. Public-URL auff/<tenant>/<slug>(INTEGRATION-CONTRACTS) umstellen,is_public/requires_logingaten. -
Sharing/Embed (M122): iFrame-Auto-Resize (postMessage), QR-Code (
qrcode.react),FormLink-Tracking-Links (Token/expires/usage_limit).embed.forms.$formKey.tsxals Render-Basis. -
Submission-Routing/Actions (M123):
form-submission-route.ts-Worker +applyFieldMapping+ Outbox-Idempotenz-Muster als Skelett; Action-Registry auf alle 11 Typen erweitern, jede Action als eigener Outbox-Event-Handler (create_lead/create_deal/create_project/create_task/kwp_push/trigger_automation/...). Webhook + Notify-Email-Follow-ups 1:1.FormActionLog+ Re-Run (5.8.6) überIdempotencyKey. -
Datei/Foto/Signatur-Persistenz (M123): Submit-Endpoint um Multipart-/File-Annahme erweitern, in
FileObject/Storage (storageService.ts/M052) ablegen,FormFile-Join schreiben; Signatur als File + IP/Zeitstempel in Submission. -
Submissions-Verwaltung (M123): Liste (shadcn
TableNew/ColumnSelector/TablePagination) + Filter + Detail-Ansicht; CSV-Export viapackages/exports/csv, PDF viapackages/exports/pdf, DSGVO-Löschen viadsgvo-export/Hard-Delete-Muster. -
Conditional-Display (M124):
conditional_visibility-JSON pro Feld im Editor setzbar, Show/Hide-State im Renderer (kein Routing-Pfad-Wechsel). Komplett neu. -
SHK-Vorlagen + Permissions (M125): 8 Vorlagen als deklarative Seed-Konfig (an M158 gekoppelt).
forms.*-Permissions + Bundles + Rollen-Mapping seeden. -
Querschnitt-Wiring (alle, DoD): Sidebar-Eintrag „Forms" (INTEGRATION-CONTRACTS §1, Zeile 53), Breadcrumbs,
AuditEvent/FormLog,notify()(M011) bei Submission, Search-Index (M24, falls suchbar), Feature-Flag pro Modul (M006), View-Model-Konvention (M013), Customer-Portal-Sichtbarkeit explizit konfigurieren.
Notizen / Vorbehalte
- „Reif" in der Inventur ist relativ: Die Domain-Packages-Inventur listet Form-Widget als „Reif" — das stimmt für das embeddable Widget, aber gemessen am M05-„Google-Forms-Niveau"-Anspruch ist es ein Bruchteil des Scopes. Daher Reuse-Level medium, nicht high.
- Provider-Diskrepanz Captcha: BP nutzt Cloudflare Turnstile, M125 fordert explizit hCaptcha (EU-konform statt Google-OAuth-Trigger). Architektur ist feature-flagged → Adapter-Tausch machbar, aber bewusste Entscheidung nötig.
- Keine Signatur-Komponente: Trotz Spec-Hinweis „Touch-Signatur via signature_pad (Boilerplate-Deps)" in M121-PLAN ist kein
signature_pad/Signatur-Code in der Boilerplate vorhanden (nur Treffer in unrelated Files). Neuer Dependency erforderlich. - Speicher-Modell-Bruch: BP speichert Felder als
definition-JSON und Antworten alspayload-JSON; Spec listet normalisierteFormField/FormSubmissionAnswer. Für Spalten-Konfiguration (5.8.3), Conditional-Display (M124) und CSV-Export pro Feld ist die normalisierte Variante deutlich tragfähiger — Architektur-Entscheidung früh treffen. - Layer-Umzug Pflicht: BP-Builder/Routen sind nicht layer-getrennt (UI + Prisma im selben
apps/web-Modul). HiveGrid-DoD blockt das — Component-Extraktion inpackages/ui-hivegrid+ VM-Loader nötig (MEMORY:.server.ts-Re-Export-Blocker beachten).