From 39827f040f15aa63a087a59071c170565c6881a9 Mon Sep 17 00:00:00 2001 From: zichun Date: Fri, 10 Apr 2026 12:31:42 +0800 Subject: [PATCH] docs(spec): metadata-driven forms & list views design (P3.2/P3.3/P3.6/R3) --- docs/superpowers/specs/2026-04-10-metadata-forms-listviews-design.md | 391 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 391 insertions(+), 0 deletions(-) create mode 100644 docs/superpowers/specs/2026-04-10-metadata-forms-listviews-design.md diff --git a/docs/superpowers/specs/2026-04-10-metadata-forms-listviews-design.md b/docs/superpowers/specs/2026-04-10-metadata-forms-listviews-design.md new file mode 100644 index 0000000..a28f62e --- /dev/null +++ b/docs/superpowers/specs/2026-04-10-metadata-forms-listviews-design.md @@ -0,0 +1,391 @@ +# Metadata-Driven Forms & List Views — Design Spec + +> Sub-project A of the v1.0 remaining work. Covers P3.2 (form renderer), +> P3.3 (form designer), P3.6 (list view designer), and R3 (metadata admin UIs). + +## 1. Context & Motivation + +vibe_erp's Tier 1 extensibility promise is that business analysts can customize +the system through the web UI — no code, no build, no restart. The foundation +is live: custom fields on core entities (P3.1 + P3.4), metadata YAML loader, +`metadata__*` tables, and the `DynamicExtFields` SPA component. + +What's missing is the **designer and renderer layer** — the UIs that let key +users create form layouts, configure list views, and manage all metadata +through the browser. Without these, Tier 1 customization requires editing +YAML files and restarting the server. + +## 2. Scope Decisions (Agreed) + +| Decision | Choice | Rationale | +|---|---|---| +| Form renderer scope | **Hybrid** — core entity forms stay handcrafted; renderer handles user-task forms, custom fields, and future custom entities | Core forms have complex interactions (line items, state transitions) that don't map well to JSON Schema | +| Custom entity creation | **Deferred to v1.1** — no dynamic DDL or runtime endpoint registration in v1.0 | Large, risky feature; custom fields already give key users meaningful extensibility | +| Form renderer library | **@rjsf/core with custom widget registry** — React JSON Schema Form with ERP-specific widgets | FormSchema.kt already stores JSON Schema + UI Schema; @rjsf is the standard renderer | +| Form designer UX | **Structured property editor** — table/accordion UI with live preview tab | 90% of the value of drag-and-drop at 30% of the cost; upgradeable later | + +## 3. Architecture + +``` +metadata__form / metadata__list_view (Postgres JSONB rows) + │ + │ source = 'core' | 'plugin:' | 'user' + │ + MetadataController (REST API) + │ + ├── GET /api/v1/_meta/metadata/forms + ├── GET /api/v1/_meta/metadata/forms/{slug} + ├── PUT /api/v1/_meta/metadata/forms/{slug} ← designer writes + ├── DELETE /api/v1/_meta/metadata/forms/{slug} ← user-created only + ├── GET /api/v1/_meta/metadata/list-views + ├── GET /api/v1/_meta/metadata/list-views/{slug} + ├── PUT /api/v1/_meta/metadata/list-views/{slug} + └── DELETE /api/v1/_meta/metadata/list-views/{slug} + │ + SPA (React + TypeScript) + │ + ├── MetadataFormRenderer (P3.2) — @rjsf/core, renders form from definition + ├── FormDesigner (P3.3) — structured editor, writes definitions back + ├── ListViewDesigner (P3.6) — column/filter/sort editor + └── MetadataAdmin (R3) — tabs: entities, permissions, menus, + custom fields, forms, list views +``` + +### Source tagging + +All form and list-view definitions follow the existing `source` convention: +- `core` — shipped in metadata YAML, loaded by MetadataLoader at boot +- `plugin:` — from plug-in JAR, loaded at plug-in start +- `user` — created through the designer UI, never touched by the loader + +The `PUT` and `DELETE` endpoints only accept `source='user'` rows. Core and +plugin definitions are read-only in the UI (shown grayed out with a lock icon). + +## 4. P3.2 — Form Renderer (`MetadataFormRenderer`) + +### Component + +```tsx + { ... }} // called on valid submit + readOnly={false} // true for view / completed tasks +/> +``` + +### Form definition schema (stored in `metadata__form.payload`) + +```typescript +interface FormDefinition { + entityName: string // "WorkOrder", "printing-shop.Plate" + slug: string // unique key: "plate-approval-task" + title: string // display title + purpose: 'create' | 'edit' | 'user-task' | 'view' + jsonSchema: object // JSON Schema draft 2020-12 + uiSchema: object // @rjsf UI Schema (widget, order, sections) + version: number // monotonically increasing +} +``` + +### @rjsf integration + +- **Library:** `@rjsf/core` + `@rjsf/validator-ajv8` +- **Theme:** Custom `VibeErpTheme` wrapping existing Tailwind CSS classes + (slate-700 labels, rounded-md inputs, btn-primary buttons) so rendered + forms look identical to handcrafted pages. +- **Custom widget registry:** + +| Widget ID | Purpose | Data source | +|---|---|---| +| `partner-picker` | Searchable partner dropdown | `GET /api/v1/partners` | +| `item-picker` | Searchable item dropdown | `GET /api/v1/catalog/items` | +| `uom-selector` | UoM dropdown | `GET /api/v1/catalog/uoms` | +| `location-picker` | Location dropdown | `GET /api/v1/inventory/locations` | +| `money-input` | Number + currency display | FieldType.Money | +| `quantity-input` | Number + UoM display | FieldType.Quantity | + +Widgets are registered in a `vibeWidgets` map and passed to `@rjsf/core`'s +`widgets` prop. Each widget is a standard React component that receives +`WidgetProps` from @rjsf. + +### User-task form bridge (P2.3) + +Flowable user-tasks carry a `formKey` in BPMN XML (e.g. `formKey="vibe:plate-approval-task"`). +When the SPA renders a pending user-task: + +1. Strip the `vibe:` prefix → slug `plate-approval-task` +2. Fetch form definition: `GET /api/v1/_meta/metadata/forms/plate-approval-task` +3. Fetch task variables: `GET /api/v1/workflow/tasks/{taskId}` +4. Render: `` +5. On submit: `POST /api/v1/workflow/tasks/{taskId}/complete` with form values + +### Relationship with DynamicExtFields (P3.1) + +DynamicExtFields stays as-is for custom fields on core entity create/edit pages. +MetadataFormRenderer is a separate, more powerful component for full-form rendering. +They share the same Tailwind styling but are independent components — no refactor +of existing create/edit pages needed. + +## 5. P3.3 — Form Designer + +### UX: Structured property editor + +The form designer is a full-page editor with two panels: + +**Left panel — Field list (accordion/table):** +- One row per field in the form +- Each row shows: field key, label, type, required checkbox, width (1/2/3 cols) +- Rows are reorderable via up/down buttons +- "Add field" button at the bottom with a type picker +- "Add section divider" to group fields under headings +- Click a row to expand its property panel inline: + - Label (per locale, with translation inputs) + - Placeholder text + - Help text / description + - Validation rules (min, max, pattern, required) + - Visibility condition (simple: "show when field X equals Y") + - Widget override (dropdown of available widgets) + +**Right panel — Live preview:** +- Renders the current form definition using `` +- Updates in real-time as the user edits fields in the left panel +- "Preview" / "Edit" toggle + +**Top bar:** +- Form title (editable) +- Entity selector (which entity this form is for) +- Purpose selector (create / edit / user-task / view) +- Save button → `PUT /api/v1/_meta/metadata/forms/{slug}` +- Discard button + +### Visibility conditions + +Simple "show when" rules stored in the UI Schema: + +```json +{ + "ui:field:press_id": { + "ui:visible": { "field": "item_type", "equals": "GOOD" } + } +} +``` + +The MetadataFormRenderer evaluates these at render time — no server round-trip. +Only single-field equality conditions in v1.0; complex expressions deferred. + +### Form definition generation + +The designer generates a JSON Schema + UI Schema pair from the field list. +It never exposes raw JSON to the user — the structured editor is the only +interface. Power users who want raw JSON can use the R3 metadata admin UI's +"Raw JSON" tab. + +## 6. P3.6 — List View Designer + +### Definition schema (stored in `metadata__list_view.payload`) + +```typescript +interface ListViewDefinition { + entityName: string + slug: string // "sales-orders-default" + title: string + columns: Array<{ + field: string // entity field key or custom field key + label: string // display header + width?: string // CSS width hint ("200px", "auto") + sortable: boolean + format?: 'date' | 'money' | 'status-badge' | 'link' + }> + defaultSort?: { field: string; direction: 'asc' | 'desc' } + filters?: Array<{ + field: string + operator: 'eq' | 'contains' | 'gt' | 'lt' | 'in' + label: string + }> + pageSize: number // default 25 + version: number +} +``` + +### Designer UX + +Simple two-section editor: + +**Columns section:** +- Table of columns with checkboxes (show/hide), drag-to-reorder, label editing +- Available fields populated from entity's JSON Schema + custom fields +- Format selector per column (plain text, date, money, status badge, link) + +**Filters & sorting section:** +- Add filterable fields (creates a filter bar above the list) +- Set default sort column and direction +- Page size selector + +**Preview:** Shows a mock table with sample data using the current config. + +### Integration with existing list pages + +v1.0 list pages (ItemsPage, PartnersPage, etc.) continue to use the existing +`` component with hardcoded columns. The `ListViewDesigner` creates +definitions for future use (custom entities in v1.1, and opt-in override of +core list views). A `` component renders +a list from a `ListViewDefinition`, reusing `DataTable` internally. + +## 7. R3 — Metadata Admin UIs + +### Route: `/admin/metadata` + +A tabbed admin page with sections: + +| Tab | Content | Editable? | +|---|---|---| +| **Entities** | List of all registered entities (core + plugin + user) | Read-only (v1.0 has no custom entities) | +| **Custom Fields** | List/create/edit custom fields for any entity | Full CRUD for `source='user'` fields | +| **Permissions** | List of all permissions | Read-only (permissions come from YAML) | +| **Menus** | List of all menu entries with section/order | Read-only for core/plugin; editable for user | +| **Forms** | List of form definitions; click to open FormDesigner | Full CRUD for `source='user'` forms | +| **List Views** | List of list-view definitions; click to open ListViewDesigner | Full CRUD for `source='user'` views | + +Each tab shows a `source` badge (core / plugin:name / user) and a lock icon +for non-editable rows. + +### Custom field editor (inline in the admin) + +The custom field tab lets key users: +- Add a new custom field to any entity (creates a `source='user'` row) +- Choose: target entity, field key, type (from FieldType sealed set), required, PII flag +- Add label translations (en, zh-CN, etc.) +- Delete user-created custom fields (with confirmation) + +This replaces the need to edit YAML for Tier 1 custom fields — the full +key-user story is now possible through the browser. + +### Permissions + +- `admin.metadata.read` — view all metadata tabs +- `admin.metadata.write` — create/edit/delete user metadata (custom fields, forms, list views, menus) + +These are added to the core `identity.yml` metadata. + +## 8. Backend Changes + +### New REST endpoints (MetadataController) + +``` +# Forms +GET /api/v1/_meta/metadata/forms → List +GET /api/v1/_meta/metadata/forms/{slug} → FormDefinition +PUT /api/v1/_meta/metadata/forms/{slug} → FormDefinition (upsert, source='user') +DELETE /api/v1/_meta/metadata/forms/{slug} → 204 (source='user' only) + +# List views +GET /api/v1/_meta/metadata/list-views → List +GET /api/v1/_meta/metadata/list-views/{slug} → ListViewDefinition +PUT /api/v1/_meta/metadata/list-views/{slug} → ListViewDefinition (upsert, source='user') +DELETE /api/v1/_meta/metadata/list-views/{slug} → 204 (source='user' only) + +# Custom fields (new write endpoints) +POST /api/v1/_meta/metadata/custom-fields → CustomField (source='user') +PUT /api/v1/_meta/metadata/custom-fields/{key} → CustomField (source='user') +DELETE /api/v1/_meta/metadata/custom-fields/{key} → 204 (source='user' only) +``` + +The existing `GET` endpoints remain unchanged. Write endpoints require +`admin.metadata.write` permission. + +### Database + +The `metadata__form` and `metadata__list_view` tables already exist in +`000-platform-init.xml`. No new migrations needed — just new code that +reads/writes these tables. + +### MetadataLoader changes + +Add `forms:` and `listViews:` sections to the MetadataYaml schema so core +PBCs and plug-ins can ship default form and list-view definitions in their +metadata YAML. The loader processes them with the same delete-by-source +idempotency as entities, permissions, menus, and custom fields. + +### CustomFieldRegistry refresh + +After a custom field is created/updated/deleted through the REST API, the +registry must refresh. Add a `refresh()` call in the write endpoint handler +(same pattern as MetadataLoader already uses). + +## 9. SPA Dependencies + +New npm packages: +- `@rjsf/core` — React JSON Schema Form renderer +- `@rjsf/utils` — shared utilities +- `@rjsf/validator-ajv8` — JSON Schema validation via Ajv + +Estimated bundle impact: ~60KB gzipped (acceptable for an ERP SPA). + +## 10. File Inventory (New & Modified) + +### New files + +**Backend (Kotlin):** +- `platform/platform-metadata/src/.../web/FormDefinitionController.kt` — CRUD for form defs +- `platform/platform-metadata/src/.../web/ListViewDefinitionController.kt` — CRUD for list-view defs +- `platform/platform-metadata/src/.../web/CustomFieldWriteController.kt` — write endpoints for custom fields +- `platform/platform-metadata/src/.../yaml/FormYaml.kt` — YAML deserialization for forms +- `platform/platform-metadata/src/.../yaml/ListViewYaml.kt` — YAML deserialization for list views + +**Frontend (TypeScript/React):** +- `web/src/components/MetadataFormRenderer.tsx` — @rjsf wrapper with VibeErp theme +- `web/src/components/form-widgets/PartnerPicker.tsx` — partner search widget +- `web/src/components/form-widgets/ItemPicker.tsx` — item search widget +- `web/src/components/form-widgets/UomSelector.tsx` — UoM dropdown widget +- `web/src/components/form-widgets/LocationPicker.tsx` — location dropdown widget +- `web/src/components/form-widgets/MoneyInput.tsx` — money input widget +- `web/src/components/form-widgets/QuantityInput.tsx` — quantity input widget +- `web/src/components/form-widgets/index.ts` — widget registry +- `web/src/pages/FormDesignerPage.tsx` — structured property editor +- `web/src/pages/ListViewDesignerPage.tsx` — column/filter/sort editor +- `web/src/pages/MetadataAdminPage.tsx` — tabbed admin with sub-sections + +**Metadata YAML (reference):** +- Core PBCs ship default form definitions for user-task forms (if any) +- Printing-shop plug-in ships a `plate-approval-task` form definition + +### Modified files + +- `platform/platform-metadata/src/.../yaml/MetadataYaml.kt` — add `forms` + `listViews` sections +- `platform/platform-metadata/src/.../MetadataLoader.kt` — process forms + list views +- `platform/platform-metadata/src/.../web/MetadataController.kt` — add form/list-view GET endpoints +- `web/src/App.tsx` — add routes for designer + admin pages +- `web/src/api/client.ts` — add API functions for form/list-view/custom-field CRUD +- `web/src/i18n/messages.ts` — add message keys for new pages +- `web/src/types/api.ts` — add FormDefinition + ListViewDefinition types +- `web/package.json` — add @rjsf dependencies + +## 11. Testing Strategy + +**Backend unit tests:** +- FormDefinition CRUD (create, read, update, delete with source enforcement) +- ListViewDefinition CRUD (same) +- CustomField write endpoints (create, update, delete, registry refresh) +- MetadataLoader with forms + list views sections +- Source-tag enforcement (reject writes to core/plugin rows) + +**Frontend:** +- Manual smoke test: open form designer, create a form, preview it, save it +- Manual smoke test: open list view designer, configure columns, save +- Manual smoke test: open metadata admin, browse all tabs + +**Integration:** +- Printing-shop plug-in ships a `plate-approval-task` form definition +- Boot the framework, start the plate-approval BPMN process, verify the + user-task renders the form from metadata, complete it, verify values flow + through to the workflow + +## 12. Out of Scope (Deferred) + +- **Custom entities / dynamic DDL** — v1.1 +- **Drag-and-drop form designer** — future upgrade of the structured editor +- **Complex visibility conditions** — v1.0 supports "show when X equals Y" only +- **Form versioning / migration** — v1.1 (for now, bumping version is manual) +- **List view as override for core pages** — v1.1 (core pages keep hardcoded columns) +- **Import/export of form/list-view definitions** — v1.1 -- libgit2 0.22.2