Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .changeset/object-config-panels-validations-hooks-actions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
'@object-ui/app-shell': minor
---

Studio Data tab: metadata-driven config panels for Validations, Hooks and Actions (with add).

The object **Validations**, **Hooks** and **Actions** sub-tabs are now no-code config panels driven by the corresponding metadata, each able to **create** new entries:

- **Validations** — the panel covers every spec rule type, not just `script`: `cross_field`, `state_machine`, `format`, `json_schema` and `conditional` are all authorable (previously they were read-only "maintain in code"). The **New** menu adds any type seeded with a valid, never-firing skeleton, and a rule's type can be switched in place; CEL predicates reuse the shared `ConditionBuilder`.
- **Hooks** — a new curated `HookDefaultInspector` replaces the flat generic form: the target object is an **object picker** (multi-select + `*`, not a free-text box), lifecycle events are grouped checkboxes, and the handler is a **dedicated code editor** (language + body). Advanced props fall through to a `SchemaForm` fed the live `hook` JSONSchema from `/meta/types`.
- **Actions** — the `ActionDefaultInspector` now receives the live `action` JSONSchema as `serverSchema`, so its "More fields" section can edit any spec property not curated above (nothing is un-editable).

`DataPillar` resolves the per-type schemas once (via `useMetadataTypes`) and passes them down.
18 changes: 18 additions & 0 deletions content/docs/guide/console.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,24 @@ The console opens at **http://localhost:5175** with MSW (Mock Service Worker) pr
| **App Creation Wizard** | 4-step wizard (Basic Info → Objects → Navigation → Branding) to create or edit apps. |
| **Error Boundary** | Graceful error handling with a retry button. |

### Object design (Studio Data tab)

Selecting an object in Studio's **Data** pillar (`/studio/:packageId/data`) opens a
tab strip over that object — **Records · Form · Validations · Hooks · Actions ·
API · Settings**. Each of Validations, Hooks and Actions is a no-code **config
panel driven by the corresponding metadata**, and each supports **adding** new
entries — no code round-trip required:

| Tab | Edits | Panel |
|-----|-------|-------|
| **Validations** | the object's inline `validations[]` (spec `ValidationRuleSchema`) | Master-detail covering **every** rule type — `script`, `cross_field`, `state_machine`, `format`, `json_schema`, `conditional`. The **New** menu adds any type (seeded with a valid, never-firing skeleton); a rule's type can be switched in place. CEL predicates reuse the shared `ConditionBuilder`, fed the object's draft fields. |
| **Hooks** | the separate `hook` metadata type targeting this object | Master-detail whose editor is the platform `SchemaForm` **driven by the live `hook` JSONSchema from `/meta/types`**, so its fields and enums always match the running server's contract. |
| **Actions** | the object's inline `actions[]` (spec `ActionSchema`) | Master-detail using the type-aware `ActionDefaultInspector`; anything not curated falls through to a **"More fields"** form fed the live `action` JSONSchema, so no spec property is un-editable. |

Validations and Actions persist with the object's own **Save draft**; Hooks (a
distinct metadata type) save per-hook. Nothing goes live until the package is
published from the top-bar **Publish** flow.

## Configuration

The console reads its configuration from `objectstack.config.ts`:
Expand Down
62 changes: 62 additions & 0 deletions packages/app-shell/src/views/metadata-admin/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,7 @@ const ENGINE_STRINGS_EN: Record<string, string> = {
// Action default (home) inspector
'engine.inspector.action.kind': 'Action',
'engine.inspector.action.close': 'Close action',
'engine.inspector.hook.kind': 'Hook',
// Dashboard default (home) inspector
'engine.inspector.dashboard.kind': 'Dashboard',
'engine.inspector.dashboard.close': 'Close dashboard',
Expand Down Expand Up @@ -984,6 +985,36 @@ const ENGINE_STRINGS_EN: Record<string, string> = {
'engine.studio.rules.severityError': 'error (rejects the save)',
'engine.studio.rules.enabled': 'Enabled',
'engine.studio.rules.delete': 'Delete',
'engine.studio.rules.type': 'Type',
'engine.studio.rules.newType': 'New validation type',
'engine.studio.rules.typeScript': 'Script — CEL fail condition',
'engine.studio.rules.typeCrossField': 'Cross-field — CEL over multiple fields',
'engine.studio.rules.typeStateMachine': 'State machine — allowed transitions',
'engine.studio.rules.typeFormat': 'Format — regex / built-in format',
'engine.studio.rules.typeJsonSchema': 'JSON schema — validate a JSON field',
'engine.studio.rules.typeConditional': 'Conditional — apply a rule when a guard holds',
'engine.studio.rules.label': 'Label (optional)',
'engine.studio.rules.field': 'Field',
'engine.studio.rules.statusField': 'Status field',
'engine.studio.rules.fields': 'Participating fields',
'engine.studio.rules.noFields': 'This object has no fields yet.',
'engine.studio.rules.pickField': '— pick a field —',
'engine.studio.rules.format': 'Built-in format',
'engine.studio.rules.formatNone': '— none —',
'engine.studio.rules.regex': 'Regex pattern',
'engine.studio.rules.transitions': 'Allowed transitions (from → next states)',
'engine.studio.rules.transitionFrom': 'From state',
'engine.studio.rules.transitionTo': 'Next states (comma-separated)',
'engine.studio.rules.addTransition': 'Add transition',
'engine.studio.rules.jsonSchema': 'JSON Schema',
'engine.studio.rules.invalidJson': 'Invalid JSON',
'engine.studio.rules.then': 'Then — rule applied when the condition holds (JSON)',
'engine.studio.rules.otherwise': 'Otherwise — rule applied when it does not (JSON, optional)',
'engine.studio.rules.events': 'Runs on',
'engine.studio.rules.event.insert': 'Create',
'engine.studio.rules.event.update': 'Update',
'engine.studio.rules.event.delete': 'Delete',
'engine.studio.rules.priority': 'Priority',
'engine.studio.settings.basics': 'Basics',
'engine.studio.settings.noInspector': 'No default object inspector registered.',
'engine.studio.settings.semanticRoles': 'Semantic roles',
Expand Down Expand Up @@ -1495,6 +1526,7 @@ const ENGINE_STRINGS_ZH: Record<string, string> = {
// Action default (home) inspector
'engine.inspector.action.kind': '动作',
'engine.inspector.action.close': '关闭动作',
'engine.inspector.hook.kind': '钩子',
// Dashboard default (home) inspector
'engine.inspector.dashboard.kind': '仪表盘',
'engine.inspector.dashboard.close': '关闭仪表盘',
Expand Down Expand Up @@ -1988,6 +2020,36 @@ const ENGINE_STRINGS_ZH: Record<string, string> = {
'engine.studio.rules.severityError': 'error(拒绝保存)',
'engine.studio.rules.enabled': '启用',
'engine.studio.rules.delete': '删除',
'engine.studio.rules.type': '类型',
'engine.studio.rules.newType': '新建验证类型',
'engine.studio.rules.typeScript': 'Script —— CEL 失败条件',
'engine.studio.rules.typeCrossField': 'Cross-field —— 跨字段 CEL',
'engine.studio.rules.typeStateMachine': 'State machine —— 允许的状态流转',
'engine.studio.rules.typeFormat': 'Format —— 正则 / 内置格式',
'engine.studio.rules.typeJsonSchema': 'JSON schema —— 校验 JSON 字段',
'engine.studio.rules.typeConditional': 'Conditional —— 条件成立时应用规则',
'engine.studio.rules.label': '标签(可选)',
'engine.studio.rules.field': '字段',
'engine.studio.rules.statusField': '状态字段',
'engine.studio.rules.fields': '参与字段',
'engine.studio.rules.noFields': '该对象暂无字段。',
'engine.studio.rules.pickField': '—— 选择字段 ——',
'engine.studio.rules.format': '内置格式',
'engine.studio.rules.formatNone': '—— 无 ——',
'engine.studio.rules.regex': '正则表达式',
'engine.studio.rules.transitions': '允许的状态流转(从 → 下一状态)',
'engine.studio.rules.transitionFrom': '起始状态',
'engine.studio.rules.transitionTo': '下一状态(逗号分隔)',
'engine.studio.rules.addTransition': '新增流转',
'engine.studio.rules.jsonSchema': 'JSON Schema',
'engine.studio.rules.invalidJson': 'JSON 格式无效',
'engine.studio.rules.then': 'Then —— 条件成立时应用的规则(JSON)',
'engine.studio.rules.otherwise': 'Otherwise —— 条件不成立时应用的规则(JSON,可选)',
'engine.studio.rules.events': '触发时机',
'engine.studio.rules.event.insert': '新建',
'engine.studio.rules.event.update': '更新',
'engine.studio.rules.event.delete': '删除',
'engine.studio.rules.priority': '优先级',
'engine.studio.settings.basics': '基础信息',
'engine.studio.settings.noInspector': '未注册对象默认检查器。',
'engine.studio.settings.semanticRoles': '语义角色',
Expand Down
Loading
Loading