create-restforge-skills 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +137 -0
- package/cli/index.js +235 -0
- package/cli/mcp.js +94 -0
- package/package.json +30 -0
- package/skills/restforge/SKILL.md +421 -0
- package/skills/restforge/references/config-schema.md +173 -0
- package/skills/restforge/references/dbschema-catalog.md +238 -0
- package/skills/restforge/references/design-to-sdf.md +618 -0
- package/skills/restforge/references/field-validation.md +173 -0
- package/skills/restforge/references/rdf-advanced.md +488 -0
- package/skills/restforge/references/udf-catalog.md +489 -0
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
# Reference: UDF Catalog (UI Definition File)
|
|
2
|
+
|
|
3
|
+
> **Offline mirror.** This file mirrors `designer_get_udf_catalog` from the
|
|
4
|
+
> installed RESTForge designer. The live tool is authoritative — when this file
|
|
5
|
+
> and the tool disagree, trust the tool, then update this file. Field types and
|
|
6
|
+
> features depend on the active plugin version; always re-ground with the tool
|
|
7
|
+
> (and `designer_list_plugins`) before defining a UDF payload.
|
|
8
|
+
|
|
9
|
+
Source: `designer_get_udf_catalog` — installed designer version.
|
|
10
|
+
Use as grounding before defining or editing any UDF payload.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
1. [Payload Envelope](#payload-envelope)
|
|
17
|
+
2. [App Config](#app-config)
|
|
18
|
+
3. [Page Anatomy](#page-anatomy)
|
|
19
|
+
4. [Field Types](#field-types)
|
|
20
|
+
5. [Field Attributes](#field-attributes)
|
|
21
|
+
6. [Field Rows (Layout)](#field-rows-layout)
|
|
22
|
+
7. [Field States (Conditional)](#field-states-conditional)
|
|
23
|
+
8. [Features](#features)
|
|
24
|
+
9. [Data Source](#data-source)
|
|
25
|
+
10. [Navigation](#navigation)
|
|
26
|
+
11. [Homepage](#homepage)
|
|
27
|
+
12. [Dashboard Page](#dashboard-page)
|
|
28
|
+
13. [Master-Detail](#master-detail)
|
|
29
|
+
14. [Workflow Actions](#workflow-actions)
|
|
30
|
+
15. [ID Generation](#id-generation)
|
|
31
|
+
16. [Live Sync](#live-sync)
|
|
32
|
+
17. [Naming Conventions](#naming-conventions)
|
|
33
|
+
18. [Plugins](#plugins)
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Payload Envelope
|
|
38
|
+
|
|
39
|
+
Root-level UDF structure. All properties at the top level.
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"appConfig": { ... },
|
|
44
|
+
"pages": [ ... ],
|
|
45
|
+
"navigation": [ ... ],
|
|
46
|
+
"homepage": "page-id"
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Multi-file support:**
|
|
51
|
+
- `extends`: inherit from a base file — `"extends": "./base.json"`
|
|
52
|
+
- `include`: inline another file's pages array — `"include": "./orders-pages.json"`
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## App Config
|
|
57
|
+
|
|
58
|
+
| Property | Type | Required | Notes |
|
|
59
|
+
|---|---|---|---|
|
|
60
|
+
| `appName` | string | yes | Display name shown in page title and header |
|
|
61
|
+
| `appCode` | string | yes | Unique app identifier, kebab-case, used in file naming |
|
|
62
|
+
| `plugin` | string | yes | Output plugin: `vanilla-js-basic`, `vanilla-js-auth`, or custom |
|
|
63
|
+
| `apiBaseUrl` | string | yes | Base URL of the RESTForge backend API |
|
|
64
|
+
| `port` | integer | no | Local dev server port; default: `3000` |
|
|
65
|
+
| `numberFormat` | string | no | Locale for number display; default: `"id-ID"` |
|
|
66
|
+
|
|
67
|
+
Plugin-specific properties (for `vanilla-js-auth`):
|
|
68
|
+
- `authAppCode` — app code used for the auth API path
|
|
69
|
+
- `idleTimeout` — session idle timeout in seconds
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Page Anatomy
|
|
74
|
+
|
|
75
|
+
Each entry in `pages[]` represents one page in the application.
|
|
76
|
+
|
|
77
|
+
| Property | Type | Required | Notes |
|
|
78
|
+
|---|---|---|---|
|
|
79
|
+
| `pageId` | string | yes | Unique identifier; CRUD: `^[a-zA-Z0-9_-]+$`; dashboard: `^dash-[a-z0-9-]+$` |
|
|
80
|
+
| `pageTitle` | string | yes | Display title shown in header and sidebar |
|
|
81
|
+
| `pageSubtitle` | string | no | Subtitle shown below title |
|
|
82
|
+
| `pageIcon` | string | no | Icon class (e.g., Tabler Icons: `"ti ti-home"`) |
|
|
83
|
+
| `pageGroup` | string | no | Sidebar group name (Title Case); auto-derives navigation |
|
|
84
|
+
| `pageType` | string | no | `"crud"` (default) or `"dashboard"` |
|
|
85
|
+
| `apiPath` | string | yes (CRUD) | Backend endpoint path; e.g., `"/customer"` |
|
|
86
|
+
| `primaryKey` | string | yes (CRUD) | Primary key field name; e.g., `"customer_id"` |
|
|
87
|
+
| `displayField` | string | yes (CRUD) | Field used as record label in lookups |
|
|
88
|
+
| `fields` | array | yes (CRUD) | Field definitions for the page |
|
|
89
|
+
| `fieldRows` | array | no | Layout grouping for multi-column forms |
|
|
90
|
+
| `fieldStates` | array | no | Conditional visibility/readonly rules |
|
|
91
|
+
| `details` | array | no | Detail pages for master-detail pattern |
|
|
92
|
+
| `workflow` | object | no | Workflow state machine config |
|
|
93
|
+
| `workflowActions` | array | no | Action buttons for workflow transitions |
|
|
94
|
+
| `features` | object | no | Page-level feature flags |
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Field Types
|
|
99
|
+
|
|
100
|
+
8 field types. Each type maps to a specific HTML element and optional library.
|
|
101
|
+
|
|
102
|
+
| Type | HTML Element | Library | Key attributes |
|
|
103
|
+
|---|---|---|---|
|
|
104
|
+
| `text` | `<input type="text">` | — | `maxlength`, `placeholder` |
|
|
105
|
+
| `textarea` | `<textarea>` | — | `rows`, `maxlength` |
|
|
106
|
+
| `number` | `<input type="number">` | — | `min`, `max`, `step` |
|
|
107
|
+
| `checkbox` | toggle switch | — | `defaultValue` (true/false), `checkboxText` |
|
|
108
|
+
| `select` | `<select>` | Select2 | `dataSource`, `tableField` |
|
|
109
|
+
| `date` | date picker | Flatpickr | `minDate`, `maxDate`, `dateFormat` |
|
|
110
|
+
| `timestamp` | datetime picker | Flatpickr | `minDate`, `maxDate`, `dateFormat` |
|
|
111
|
+
| `time` | time picker | Flatpickr | `minTime`, `maxTime` |
|
|
112
|
+
|
|
113
|
+
**Notes:**
|
|
114
|
+
- `select` requires a `dataSource` to populate options.
|
|
115
|
+
- `date`/`timestamp`/`time` require Flatpickr assets bundled by the plugin.
|
|
116
|
+
- `checkbox` renders as a toggle switch, not a standard checkbox input.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Field Attributes
|
|
121
|
+
|
|
122
|
+
Common attributes for all field types.
|
|
123
|
+
|
|
124
|
+
| Attribute | Type | Notes |
|
|
125
|
+
|---|---|---|
|
|
126
|
+
| `name` | string | Field name matching the RDF payload field; snake_case |
|
|
127
|
+
| `label` | string | Display label shown on form and table header |
|
|
128
|
+
| `type` | string | One of the 8 field types above |
|
|
129
|
+
| `required` | boolean | Frontend validation — marks field as mandatory |
|
|
130
|
+
| `readonly` | boolean | Renders as read-only input |
|
|
131
|
+
| `inTable` | boolean | Show column in the data table; default: `true` |
|
|
132
|
+
| `tableOrder` | integer | Column order in table; lower = leftmost |
|
|
133
|
+
| `tableField` | string | Column value to display in table (for `select` — show label, not id) |
|
|
134
|
+
| `width` | integer | Column width in pixels |
|
|
135
|
+
| `placeholder` | string | Input placeholder text |
|
|
136
|
+
| `defaultValue` | any | Default value on new record form |
|
|
137
|
+
| `editorMode` | string | Form only (`"form"`) or table only (`"table"`) |
|
|
138
|
+
| `maxlength` | integer | Max character length for `text`/`textarea` |
|
|
139
|
+
| `rows` | integer | Row height for `textarea` |
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Field Rows (Layout)
|
|
144
|
+
|
|
145
|
+
`fieldRows[]` groups fields into a CSS Grid row for multi-column forms.
|
|
146
|
+
Without `fieldRows`, fields render in a single column.
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
"fieldRows": [
|
|
150
|
+
{ "fields": ["first_name", "last_name"] },
|
|
151
|
+
{ "fields": ["email", "phone", "status"] }
|
|
152
|
+
]
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
- Each entry in `fields[]` refers to a field `name` defined in `fields[]`.
|
|
156
|
+
- Fields within a row share the available width equally by default.
|
|
157
|
+
- Use `colSpan` on a field attribute to span multiple columns:
|
|
158
|
+
`"colSpan": 2` makes a field take 2 of the row's grid columns.
|
|
159
|
+
- Interacts with `features.fieldLayout`: `"horizontal"` places the label
|
|
160
|
+
to the left of the input; `"vertical"` (default) stacks them.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Field States (Conditional)
|
|
165
|
+
|
|
166
|
+
`fieldStates[]` defines conditional visibility and readonly rules based on
|
|
167
|
+
field values. Applied at runtime when field values change.
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
"fieldStates": [
|
|
171
|
+
{
|
|
172
|
+
"when": { "field": "status", "value": "approved" },
|
|
173
|
+
"fields": ["approved_by", "approved_at"],
|
|
174
|
+
"state": "readonly"
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"when": { "field": "order_type", "value": "internal" },
|
|
178
|
+
"fields": ["customer_id"],
|
|
179
|
+
"state": "hidden"
|
|
180
|
+
}
|
|
181
|
+
]
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
| Property | Notes |
|
|
185
|
+
|---|---|
|
|
186
|
+
| `when.field` | The triggering field name |
|
|
187
|
+
| `when.value` | The value that triggers the state change |
|
|
188
|
+
| `fields[]` | Fields to affect when the condition is met |
|
|
189
|
+
| `state` | `"readonly"` or `"hidden"` |
|
|
190
|
+
|
|
191
|
+
Global scope: `fieldStates` at page level applies to all form instances.
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Features
|
|
196
|
+
|
|
197
|
+
`features` is an object at page level controlling toolbar and layout behavior.
|
|
198
|
+
|
|
199
|
+
| Feature | Type | Notes |
|
|
200
|
+
|---|---|---|
|
|
201
|
+
| `enableSearch` | boolean | Show full-text search input in toolbar |
|
|
202
|
+
| `enableStatusFilter` | object | Dropdown filter for boolean/enum field; requires `field` and `options[]` |
|
|
203
|
+
| `enableDataFilter` | array | Dynamic dropdown filters; each entry requires `field` and `dataSource` |
|
|
204
|
+
| `enableLiveSync` | boolean | Enable WebSocket live data refresh; requires `liveSync` at root level |
|
|
205
|
+
| `fieldLayout` | string | Form field label placement: `"vertical"` (default) or `"horizontal"` |
|
|
206
|
+
|
|
207
|
+
`enableStatusFilter` example:
|
|
208
|
+
```json
|
|
209
|
+
"enableStatusFilter": {
|
|
210
|
+
"field": "is_active",
|
|
211
|
+
"options": [
|
|
212
|
+
{ "label": "Active", "value": "true" },
|
|
213
|
+
{ "label": "Inactive", "value": "false" }
|
|
214
|
+
]
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
`enableDataFilter` example:
|
|
219
|
+
```json
|
|
220
|
+
"enableDataFilter": [
|
|
221
|
+
{ "field": "category_id", "dataSource": { "type": "api", "apiPath": "/category" } }
|
|
222
|
+
]
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Data Source
|
|
228
|
+
|
|
229
|
+
Used by `select` fields and `enableDataFilter` to populate options.
|
|
230
|
+
|
|
231
|
+
**Type: static**
|
|
232
|
+
```json
|
|
233
|
+
"dataSource": {
|
|
234
|
+
"type": "static",
|
|
235
|
+
"options": [
|
|
236
|
+
{ "id": "active", "text": "Active" },
|
|
237
|
+
{ "id": "inactive", "text": "Inactive" }
|
|
238
|
+
]
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
**Type: api**
|
|
243
|
+
```json
|
|
244
|
+
"dataSource": {
|
|
245
|
+
"type": "api",
|
|
246
|
+
"apiPath": "/category",
|
|
247
|
+
"id": "category_id",
|
|
248
|
+
"text": "category_name"
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
- `apiPath` calls the backend `/lookup` endpoint of that resource.
|
|
253
|
+
- `id` maps to the option value; `text` maps to the display label.
|
|
254
|
+
- The `tableField` attribute on the field should reference `text` to show
|
|
255
|
+
the label (not the id) in the data table column.
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## Navigation
|
|
260
|
+
|
|
261
|
+
`navigation[]` defines the sidebar menu. Three item types: `page`, `group`,
|
|
262
|
+
`separator`.
|
|
263
|
+
|
|
264
|
+
```json
|
|
265
|
+
"navigation": [
|
|
266
|
+
{ "type": "page", "pageId": "customer", "icon": "ti ti-users" },
|
|
267
|
+
{ "type": "separator" },
|
|
268
|
+
{
|
|
269
|
+
"type": "group",
|
|
270
|
+
"label": "Master Data",
|
|
271
|
+
"icon": "ti ti-database",
|
|
272
|
+
"items": [
|
|
273
|
+
{ "type": "page", "pageId": "category" },
|
|
274
|
+
{ "type": "page", "pageId": "supplier" }
|
|
275
|
+
]
|
|
276
|
+
}
|
|
277
|
+
]
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
- Maximum nesting depth: 3 levels.
|
|
281
|
+
- When `pageGroup` is set on a page, the sidebar auto-derives navigation —
|
|
282
|
+
manual `navigation[]` is optional.
|
|
283
|
+
- Manual `navigation[]` overrides auto-derivation completely.
|
|
284
|
+
- `icon` uses Tabler Icons class format: `"ti ti-<name>"`.
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Homepage
|
|
289
|
+
|
|
290
|
+
`homepage` sets the default landing page when the app loads.
|
|
291
|
+
|
|
292
|
+
```json
|
|
293
|
+
"homepage": "dashboard-main"
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
- Value must match a `pageId` in `pages[]`.
|
|
297
|
+
- Supports both CRUD and dashboard pages.
|
|
298
|
+
- When absent, the first page in `navigation[]` is loaded.
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## Dashboard Page
|
|
303
|
+
|
|
304
|
+
A page with `pageType: "dashboard"` renders a grid-based analytics view.
|
|
305
|
+
|
|
306
|
+
```json
|
|
307
|
+
{
|
|
308
|
+
"pageId": "dash-overview",
|
|
309
|
+
"pageType": "dashboard",
|
|
310
|
+
"pageTitle": "Overview",
|
|
311
|
+
"dataSources": [
|
|
312
|
+
{ "id": "ds-orders", "apiPath": "/order", "action": "aggregate" }
|
|
313
|
+
],
|
|
314
|
+
"rows": [
|
|
315
|
+
{
|
|
316
|
+
"columns": [
|
|
317
|
+
{ "colSpan": { "xl": 4, "lg": 6, "md": 12 }, "widget": { ... } },
|
|
318
|
+
{ "colSpan": { "xl": 8, "lg": 6, "md": 12 }, "widget": { ... } }
|
|
319
|
+
]
|
|
320
|
+
}
|
|
321
|
+
]
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**7 widget types:**
|
|
326
|
+
|
|
327
|
+
| Widget type | Display | Key properties |
|
|
328
|
+
|---|---|---|
|
|
329
|
+
| `column` | Vertical bar chart | `dataSource`, `labelField`, `valueField`, `color` |
|
|
330
|
+
| `bar` | Horizontal bar chart | `dataSource`, `labelField`, `valueField`, `color` |
|
|
331
|
+
| `pie` | Pie / donut chart | `dataSource`, `labelField`, `valueField` |
|
|
332
|
+
| `area` | Area chart | `dataSource`, `labelField`, `valueField`, `color` |
|
|
333
|
+
| `mini-bar` | Compact bar (KPI card) | `dataSource`, `valueField`, `label`, `icon`, `color` |
|
|
334
|
+
| `progress` | Progress bar list | `dataSource`, `labelField`, `valueField`, `maxValue` |
|
|
335
|
+
| `list` | Simple data list | `dataSource`, `columns[]` |
|
|
336
|
+
|
|
337
|
+
`dataSources[]` at page level defines reusable data pools; widgets reference
|
|
338
|
+
them by `id`. Each data source calls the backend `/aggregate` endpoint.
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## Master-Detail
|
|
343
|
+
|
|
344
|
+
A page with `details[]` renders a master-detail composite form.
|
|
345
|
+
|
|
346
|
+
```json
|
|
347
|
+
{
|
|
348
|
+
"pageId": "sales-order",
|
|
349
|
+
"apiPath": "/order",
|
|
350
|
+
"primaryKey": "order_id",
|
|
351
|
+
"displayField": "order_no",
|
|
352
|
+
"fields": [ ... ],
|
|
353
|
+
"details": [
|
|
354
|
+
{
|
|
355
|
+
"pageId": "order-item",
|
|
356
|
+
"apiPath": "/order-item",
|
|
357
|
+
"foreignKey": "order_id",
|
|
358
|
+
"primaryKey": "item_id",
|
|
359
|
+
"displayField": "product_name",
|
|
360
|
+
"fields": [ ... ]
|
|
361
|
+
}
|
|
362
|
+
]
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
- Each entry in `details[]` is a sub-page bound to the master via `foreignKey`.
|
|
367
|
+
- Multiple detail tabs are supported — one entry per detail entity.
|
|
368
|
+
- Generates composite form: master header + detail items grid.
|
|
369
|
+
- Backend must have composite endpoints (`/create-composite`, etc.) generated
|
|
370
|
+
from the RDF master-detail payload.
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
## Workflow Actions
|
|
375
|
+
|
|
376
|
+
`workflowActions[]` adds status-transition buttons to the form toolbar.
|
|
377
|
+
|
|
378
|
+
```json
|
|
379
|
+
"workflow": {
|
|
380
|
+
"statusField": "status"
|
|
381
|
+
},
|
|
382
|
+
"workflowActions": [
|
|
383
|
+
{
|
|
384
|
+
"action": "approve",
|
|
385
|
+
"label": "Approve",
|
|
386
|
+
"targetStatus": "approved",
|
|
387
|
+
"fromStatus": ["pending"],
|
|
388
|
+
"confirm": {
|
|
389
|
+
"message": "Approve this request?",
|
|
390
|
+
"summary": ["request_no", "requested_by", "amount"]
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
]
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
| Property | Notes |
|
|
397
|
+
|---|---|
|
|
398
|
+
| `workflow.statusField` | Field name that holds the current status |
|
|
399
|
+
| `action` | Action identifier sent to backend `/change-status` |
|
|
400
|
+
| `label` | Button label |
|
|
401
|
+
| `targetStatus` | Status value after transition |
|
|
402
|
+
| `fromStatus[]` | Allowed current statuses for this action to appear |
|
|
403
|
+
| `confirm.message` | Confirmation dialog message |
|
|
404
|
+
| `confirm.summary[]` | Fields to display in the confirmation dialog |
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
## ID Generation
|
|
409
|
+
|
|
410
|
+
Supports auto-generated field values via `defaultValue.source: "idgen"`.
|
|
411
|
+
Requires `IDGEN_ENABLED=true` in backend config.
|
|
412
|
+
|
|
413
|
+
```json
|
|
414
|
+
{
|
|
415
|
+
"name": "order_no",
|
|
416
|
+
"type": "text",
|
|
417
|
+
"defaultValue": {
|
|
418
|
+
"source": "idgen",
|
|
419
|
+
"mode": "serial",
|
|
420
|
+
"prefix": "ORD",
|
|
421
|
+
"format": "XXXX-XXXX"
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
5 modes: `number` (sequential with prefix/format), `pin` (OTP numeric),
|
|
427
|
+
`code` (voucher-style), `serial` (license key pattern), `random`.
|
|
428
|
+
The field renders as readonly on the form; value is reserved on focus and
|
|
429
|
+
confirmed on submit.
|
|
430
|
+
|
|
431
|
+
---
|
|
432
|
+
|
|
433
|
+
## Live Sync
|
|
434
|
+
|
|
435
|
+
WebSocket-based live data refresh. Requires `LIVE_SYNC_ENABLED=true` and
|
|
436
|
+
`LIVE_SYNC_PORT` configured in backend.
|
|
437
|
+
|
|
438
|
+
Root-level config in UDF:
|
|
439
|
+
```json
|
|
440
|
+
"liveSync": {
|
|
441
|
+
"url": "ws://localhost:3033",
|
|
442
|
+
"apiKey": "your-api-key"
|
|
443
|
+
}
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
Per-page activation:
|
|
447
|
+
```json
|
|
448
|
+
"features": {
|
|
449
|
+
"enableLiveSync": true
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
When active, the page's data table auto-refreshes when the backend pushes
|
|
454
|
+
an event over the WebSocket channel.
|
|
455
|
+
|
|
456
|
+
---
|
|
457
|
+
|
|
458
|
+
## Naming Conventions
|
|
459
|
+
|
|
460
|
+
| Element | Pattern | Example |
|
|
461
|
+
|---|---|---|
|
|
462
|
+
| `pageId` (CRUD) | `^[a-zA-Z0-9_-]+$` | `sales-order`, `product_list` |
|
|
463
|
+
| `pageId` (dashboard) | `^dash-[a-z0-9-]+$` | `dash-overview`, `dash-monthly` |
|
|
464
|
+
| `widgetId` | `^[a-z][a-z0-9-]*$` | `chart-revenue`, `kpi-orders` |
|
|
465
|
+
| field `name` | snake_case | `order_no`, `customer_id` |
|
|
466
|
+
| `appCode` | kebab-case | `sales-app`, `inventory` |
|
|
467
|
+
| `apiPath` | kebab-case with leading `/` | `"/sales-order"`, `"/product"` |
|
|
468
|
+
| `pageGroup` | Title Case | `"Master Data"`, `"Reports"` |
|
|
469
|
+
|
|
470
|
+
---
|
|
471
|
+
|
|
472
|
+
## Plugins
|
|
473
|
+
|
|
474
|
+
Two built-in plugins. Run `designer_list_plugins` to confirm available versions.
|
|
475
|
+
|
|
476
|
+
| Plugin | Auth | Notes |
|
|
477
|
+
|---|---|---|
|
|
478
|
+
| `vanilla-js-basic` | None | Standard CRUD app, no login flow |
|
|
479
|
+
| `vanilla-js-auth` | JWT | Includes login page, token refresh, role-based visibility |
|
|
480
|
+
|
|
481
|
+
`vanilla-js-auth` requires additional `appConfig` properties:
|
|
482
|
+
- `authAppCode` — must match the backend auth module's app code
|
|
483
|
+
- `idleTimeout` — auto-logout after inactivity (seconds)
|
|
484
|
+
|
|
485
|
+
**Custom plugins:**
|
|
486
|
+
Use `designer_scaffold_plugin` to generate a plugin template. The plugin
|
|
487
|
+
structure contains `plugin.json` (metadata) and Jinja2 templates for each
|
|
488
|
+
file type (HTML, JS, CSS). Use `designer_inspect_plugin` to verify capabilities
|
|
489
|
+
before referencing a plugin in a UDF payload.
|