@cxtms/cx-schema 1.0.0 → 1.1.1

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.
Files changed (66) hide show
  1. package/.claude/skills/cx-core/SKILL.md +93 -0
  2. package/.claude/skills/cx-core/ref-entity-accounting.md +173 -0
  3. package/.claude/skills/cx-core/ref-entity-commodity.md +205 -0
  4. package/.claude/skills/cx-core/ref-entity-contact.md +153 -0
  5. package/.claude/skills/cx-core/ref-entity-geography.md +119 -0
  6. package/.claude/skills/cx-core/ref-entity-job.md +77 -0
  7. package/.claude/skills/cx-core/ref-entity-order-sub.md +140 -0
  8. package/.claude/skills/cx-core/ref-entity-order.md +168 -0
  9. package/.claude/skills/cx-core/ref-entity-rate.md +174 -0
  10. package/.claude/skills/cx-core/ref-entity-shared.md +147 -0
  11. package/.claude/skills/cx-core/ref-entity-warehouse.md +110 -0
  12. package/.claude/skills/cx-module/SKILL.md +402 -0
  13. package/.claude/skills/cx-module/ref-components-data.md +286 -0
  14. package/.claude/skills/cx-module/ref-components-display.md +394 -0
  15. package/.claude/skills/cx-module/ref-components-forms.md +362 -0
  16. package/.claude/skills/cx-module/ref-components-interactive.md +306 -0
  17. package/.claude/skills/cx-module/ref-components-layout.md +295 -0
  18. package/.claude/skills/cx-module/ref-components-specialized.md +427 -0
  19. package/.claude/skills/cx-workflow/SKILL.md +330 -0
  20. package/.claude/skills/cx-workflow/ref-accounting.md +66 -0
  21. package/.claude/skills/cx-workflow/ref-communication.md +161 -0
  22. package/.claude/skills/cx-workflow/ref-entity.md +162 -0
  23. package/.claude/skills/cx-workflow/ref-expressions.md +239 -0
  24. package/.claude/skills/cx-workflow/ref-filetransfer.md +80 -0
  25. package/.claude/skills/cx-workflow/ref-flow.md +180 -0
  26. package/.claude/skills/cx-workflow/ref-other.md +120 -0
  27. package/.claude/skills/cx-workflow/ref-query.md +85 -0
  28. package/.claude/skills/cx-workflow/ref-utilities.md +171 -0
  29. package/README.md +34 -34
  30. package/dist/cli.js +545 -35
  31. package/dist/cli.js.map +1 -1
  32. package/dist/types.d.ts +17 -1
  33. package/dist/types.d.ts.map +1 -1
  34. package/dist/workflowValidator.d.ts +31 -0
  35. package/dist/workflowValidator.d.ts.map +1 -1
  36. package/dist/workflowValidator.js +299 -9
  37. package/dist/workflowValidator.js.map +1 -1
  38. package/package.json +4 -3
  39. package/schemas/schema.graphql +2077 -321
  40. package/schemas/workflows/flow/aggregation.json +44 -0
  41. package/schemas/workflows/flow/entity.json +110 -0
  42. package/schemas/workflows/flow/state.json +105 -0
  43. package/schemas/workflows/flow/transition.json +143 -0
  44. package/schemas/workflows/input.json +53 -7
  45. package/schemas/workflows/output.json +20 -0
  46. package/schemas/workflows/trigger.json +4 -0
  47. package/schemas/workflows/variable.json +2 -6
  48. package/schemas/workflows/workflow.json +179 -21
  49. package/scripts/postinstall.js +30 -1
  50. package/templates/module-configuration.yaml +110 -0
  51. package/templates/module-form.yaml +152 -0
  52. package/templates/module-grid.yaml +229 -0
  53. package/templates/module-select.yaml +139 -0
  54. package/templates/module.yaml +3 -3
  55. package/templates/workflow-api-tracking.yaml +189 -0
  56. package/templates/workflow-basic.yaml +76 -0
  57. package/templates/workflow-document.yaml +155 -0
  58. package/templates/workflow-entity-trigger.yaml +90 -0
  59. package/templates/workflow-ftp-edi.yaml +158 -0
  60. package/templates/workflow-ftp-tracking.yaml +161 -0
  61. package/templates/workflow-mcp-tool.yaml +112 -0
  62. package/templates/workflow-public-api.yaml +135 -0
  63. package/templates/workflow-scheduled.yaml +125 -0
  64. package/templates/workflow-utility.yaml +96 -0
  65. package/templates/workflow-webhook.yaml +128 -0
  66. package/templates/workflow.yaml +52 -12
@@ -0,0 +1,295 @@
1
+ # Layout & Structure Components
2
+
3
+ ## layout
4
+
5
+ General-purpose container. Renders MUI Grid or Box with flexbox.
6
+
7
+ **Props:**
8
+ | Prop | Type | Default | Description |
9
+ |------|------|---------|-------------|
10
+ | `orientation` | `horizontal \| vertical \| flex` | — | Grid direction |
11
+ | `cols` | `number` | — | Equal-width columns (`12/cols` per child) |
12
+ | `columns` | `number \| {xs,sm,md,lg,xl}` | — | Responsive column count |
13
+ | `spacing` | `number` | `3` | Grid gap spacing |
14
+ | `containerTag` | `grid \| box` | `grid` | Grid container vs flexbox Box |
15
+ | `containerSx` | `SxProps` | — | MUI sx styles on container |
16
+ | `className` | `string` | — | CSS class (template-parsed) |
17
+ | `id` | `string` | — | Element ID (template-parsed) |
18
+ | `direction` | `row \| column` | — | Explicit flex direction |
19
+ | `justifyContent` | `string` | — | Flexbox main-axis alignment |
20
+ | `alignItems` | `string` | — | Flexbox cross-axis alignment |
21
+ | `refreshHandler` | `string` | — | Remounts on refresh event |
22
+ | `permission` | `string` | — | Permission gate |
23
+ | `title` | `ILocalizeString` | — | Layout title |
24
+ | `icon` | `string` | — | Layout icon |
25
+ | `toolbar` | `component[]` | — | Toolbar components |
26
+ | `itemDefaults` | `{size?,offset?,order?,sx?}` | — | Default Grid item props for all children |
27
+
28
+ **Events:** `onClick`
29
+
30
+ **Children:** Yes — each child rendered via ComponentRender, wrapped in Grid item.
31
+
32
+ ```yaml
33
+ # Basic 2-column layout
34
+ component: layout
35
+ name: detailLayout
36
+ props:
37
+ cols: 2
38
+ spacing: 2
39
+ title:
40
+ en-US: "Detail View"
41
+ icon: file-text
42
+ permission: "Module/Read"
43
+ children:
44
+ - component: field
45
+ name: firstName
46
+ props: { type: text, label: { en-US: "First Name" } }
47
+ - component: field
48
+ name: lastName
49
+ props: { type: text, label: { en-US: "Last Name" } }
50
+ ```
51
+
52
+ ```yaml
53
+ # Horizontal flex layout with toolbar
54
+ component: layout
55
+ name: pageLayout
56
+ props:
57
+ orientation: vertical
58
+ toolbar:
59
+ - component: button
60
+ name: saveBtn
61
+ props:
62
+ label: { en-US: "Save" }
63
+ icon: check
64
+ options: { type: submit, variant: primary }
65
+ children:
66
+ - component: form
67
+ name: myForm
68
+ # ...
69
+ ```
70
+
71
+ ---
72
+
73
+ ## row
74
+
75
+ Horizontal MUI Grid row. Simpler alternative to layout for single rows.
76
+
77
+ **Props:**
78
+ | Prop | Type | Default | Description |
79
+ |------|------|---------|-------------|
80
+ | `spacing` | `number` | `3` | Grid spacing |
81
+ | `columns` | `number` | — | Grid columns |
82
+ | `direction` | `row \| column` | `row` | Grid direction |
83
+ | `sx` | `SxProps` | — | MUI sx styles |
84
+ | `className` | `string` | — | CSS class |
85
+ | `alignItems` | `string` | — | Cross-axis alignment |
86
+ | `justifyContent` | `string` | — | Main-axis alignment |
87
+
88
+ **Children:** Yes — rendered without Grid item wrapper (use `col` children).
89
+
90
+ ```yaml
91
+ component: row
92
+ name: headerRow
93
+ props:
94
+ spacing: 2
95
+ alignItems: center
96
+ children:
97
+ - component: col
98
+ name: leftCol
99
+ props: { size: 6 }
100
+ children:
101
+ - component: text
102
+ name: title
103
+ props: { value: "Header", type: h3 }
104
+ - component: col
105
+ name: rightCol
106
+ props: { size: 6 }
107
+ children:
108
+ - component: button
109
+ name: actionBtn
110
+ props: { label: { en-US: "Action" } }
111
+ ```
112
+
113
+ ---
114
+
115
+ ## col
116
+
117
+ Grid column item. Child of `row` or `layout`.
118
+
119
+ **Props:**
120
+ | Prop | Type | Description |
121
+ |------|------|-------------|
122
+ | `size` | `number \| {xs,sm,md,lg,xl}` | Responsive column width (plain number = xs) |
123
+ | `offset` | `number \| {xs,sm,md,lg,xl}` | Responsive column offset |
124
+ | `order` | `number \| {xs,sm,md,lg,xl}` | CSS order for reordering |
125
+ | `sx` | `SxProps` | MUI sx styles |
126
+ | `className` | `string` | CSS class |
127
+ | `alignSelf` | `string` | CSS align-self |
128
+
129
+ **Children:** Yes.
130
+
131
+ ```yaml
132
+ component: col
133
+ name: mainContent
134
+ props:
135
+ size: { xs: 12, md: 8 }
136
+ offset: { md: 2 }
137
+ ```
138
+
139
+ ---
140
+
141
+ ## header
142
+
143
+ Section header with title and optional subtitle.
144
+
145
+ **Props:**
146
+ | Prop | Type | Description |
147
+ |------|------|-------------|
148
+ | `title` | `string` | Rendered as `<h3>` |
149
+ | `subtitle` | `string` | Rendered as `<h4>` with divider |
150
+ | `className` | `string` | Additional CSS class |
151
+
152
+ **Children:** Yes.
153
+
154
+ ```yaml
155
+ component: header
156
+ name: sectionHeader
157
+ props:
158
+ title: "Contact Information"
159
+ subtitle: "Primary contact details"
160
+ ```
161
+
162
+ ---
163
+
164
+ ## tabs
165
+
166
+ Tabbed interface with MUI TabContext. Tab state stored in URL params.
167
+
168
+ **Props:**
169
+ | Prop | Type | Description |
170
+ |------|------|-------------|
171
+ | `options` | `object` | Additional props spread to Tab elements |
172
+ | `toolbar` | `component[]` | Action components next to tab list |
173
+ | `useNavigationForTabs` | `boolean` | Push to history instead of replace |
174
+
175
+ **Tab children props:**
176
+ | Prop | Type | Description |
177
+ |------|------|-------------|
178
+ | `label` | `ILocalizeString` | Tab label (localized, template-parsed) |
179
+ | `isVisible` | `string` | Template expression — show when truthy |
180
+ | `isHidden` | `string` | Template expression — hide when truthy |
181
+ | `options` | `object` | Additional Tab element props |
182
+
183
+ **Children:** Each child = one tab. Content rendered inside TabPanel via LayoutComponent.
184
+
185
+ ```yaml
186
+ component: tabs
187
+ name: detailTabs
188
+ props:
189
+ toolbar:
190
+ - component: button
191
+ name: refreshBtn
192
+ props: { label: { en-US: "Refresh" }, icon: refresh-cw }
193
+ children:
194
+ - name: general
195
+ props:
196
+ label: { en-US: "General" }
197
+ children:
198
+ - component: field
199
+ name: name
200
+ props: { type: text, label: { en-US: "Name" } }
201
+ - name: advanced
202
+ props:
203
+ label: { en-US: "Advanced" }
204
+ isHidden: "{{ eval !isAdmin }}"
205
+ children:
206
+ - component: field
207
+ name: config
208
+ props: { type: textarea, label: { en-US: "Config" } }
209
+ ```
210
+
211
+ ---
212
+
213
+ ## toolbar
214
+
215
+ Nav-style toolbar with title and action buttons.
216
+
217
+ **Props:**
218
+ | Prop | Type | Description |
219
+ |------|------|-------------|
220
+ | `title` | `ILocalizeString` | Toolbar title (navbar brand) |
221
+ | `buttons` | `component[]` | Action components (right-aligned) |
222
+
223
+ **Children:** No — uses `buttons` prop.
224
+
225
+ ```yaml
226
+ component: toolbar
227
+ name: pageToolbar
228
+ props:
229
+ title: { en-US: "Order Management" }
230
+ buttons:
231
+ - component: button
232
+ name: exportBtn
233
+ props: { label: { en-US: "Export" }, icon: download }
234
+ - component: button
235
+ name: createBtn
236
+ props: { label: { en-US: "Create" }, icon: plus, options: { variant: primary } }
237
+ ```
238
+
239
+ ---
240
+
241
+ ## card
242
+
243
+ MUI Card container with optional header, content, and actions.
244
+
245
+ **Props (under `options`):**
246
+ | Prop | Type | Default | Description |
247
+ |------|------|---------|-------------|
248
+ | `variant` | `elevation \| outlined` | — | MUI Card variant |
249
+ | `elevation` | `number` | — | Shadow depth |
250
+ | `sx` | `SxProps` | — | Card sx styles |
251
+ | `className` | `string` | — | Additional CSS class |
252
+ | `bgcolor` | `string` | — | Background color |
253
+ | `color` | `string` | — | Text color |
254
+ | `header` | `{title?, subheader?, sx?}` | — | Renders MUI CardHeader |
255
+ | `disableContentWrapper` | `boolean` | `false` | Skip CardContent wrapper |
256
+ | `contentSx` | `SxProps` | — | CardContent styles |
257
+ | `contentClassName` | `string` | `card-content` | CardContent CSS class |
258
+
259
+ **Children:** Yes — wrapped in CardContent (unless `disableContentWrapper`).
260
+
261
+ ```yaml
262
+ component: card
263
+ name: summaryCard
264
+ props:
265
+ options:
266
+ variant: outlined
267
+ header:
268
+ title: "Summary"
269
+ subheader: "Last updated today"
270
+ contentSx:
271
+ padding: 2
272
+ children:
273
+ - component: text
274
+ name: total
275
+ props: { value: "Total: {{ totalAmount }}", type: h4 }
276
+ ```
277
+
278
+ ---
279
+
280
+ ## line
281
+
282
+ Simple `<hr>` horizontal divider.
283
+
284
+ **Props:**
285
+ | Prop | Type | Default | Description |
286
+ |------|------|---------|-------------|
287
+ | `options.style` | `CSSProperties` | `margin: 0.5rem 0` | Inline styles |
288
+
289
+ ```yaml
290
+ component: line
291
+ name: divider
292
+ props:
293
+ options:
294
+ style: { margin: "1rem 0" }
295
+ ```
@@ -0,0 +1,427 @@
1
+ # Specialized Components
2
+
3
+ ## calendar
4
+
5
+ FullCalendar integration with GraphQL event sources, timezone support, and programmatic control.
6
+
7
+ **Props:**
8
+ | Prop | Type | Default | Description |
9
+ |------|------|---------|-------------|
10
+ | `calendarId` | `string` | — | Calendar ID (template-parsed), used for timezone fetch |
11
+ | `initialView` | `string` | `dayGridMonth` | View: `dayGridMonth`, `timeGridWeek`, `timeGridDay`, `listMonth` |
12
+ | `height` | `number` | `600` | Calendar height |
13
+ | `aspectRatio` | `number` | `1.35` | Calendar aspect ratio |
14
+ | `options.headerToolbar` | `object` | — | FullCalendar header toolbar config |
15
+ | `options.selectable` | `boolean` | — | Enable date selection |
16
+ | `options.editable` | `boolean` | — | Enable drag/resize events |
17
+ | `options.weekends` | `boolean` | — | Show weekends |
18
+ | `options.nowIndicator` | `boolean` | — | Show current time line |
19
+ | `options.eventDisplay` | `string` | — | Event display style |
20
+ | `options.eventSources` | `EventSource[]` | — | Event data sources |
21
+
22
+ **EventSource definition:**
23
+ | Prop | Type | Description |
24
+ |------|------|-------------|
25
+ | `query.command` | `string` | GraphQL query |
26
+ | `query.variables` | `object` | Query variables |
27
+ | `query.path` | `string` | Path to events in response |
28
+ | `query.mapping` | `object` | Field mapping for events |
29
+ | `color` | `string` | Event background color |
30
+ | `textColor` | `string` | Event text color |
31
+
32
+ **Events:**
33
+ | Event | Data | Description |
34
+ |-------|------|-------------|
35
+ | `onDateClick` | `date, dateStr, allDay, view` | Date cell clicked |
36
+ | `onEventClick` | `event{id,title,start,end,allDay,extendedProps}, view` | Event clicked |
37
+ | `onSelect` | `start, end, startStr, endStr, allDay, view` | Date range selected |
38
+ | `onEventDrop` | `event, oldEvent, delta, revert` | Event drag-dropped |
39
+ | `onEventResize` | `event, oldEvent, revert` | Event resized |
40
+ | `onDatesSet` | `start, end, startStr, endStr, view` | Visible range changed |
41
+
42
+ **Store API:** Stores `calendar_{calendarId}` in context store with: `refresh()`, `changeView()`, `gotoDate()`, `prev()`, `next()`, `today()`.
43
+
44
+ ```yaml
45
+ component: calendar
46
+ name: shipmentCalendar
47
+ props:
48
+ calendarId: "{{ calendarId }}"
49
+ initialView: dayGridMonth
50
+ height: 700
51
+ options:
52
+ selectable: true
53
+ editable: true
54
+ weekends: true
55
+ nowIndicator: true
56
+ headerToolbar:
57
+ left: prev,next,today
58
+ center: title
59
+ right: dayGridMonth,timeGridWeek,timeGridDay
60
+ eventSources:
61
+ - query:
62
+ command: |
63
+ query($start: DateTime!, $end: DateTime!) {
64
+ shipments(startDate: $start, endDate: $end) {
65
+ id title startDate endDate status
66
+ }
67
+ }
68
+ variables:
69
+ start: "{{ startStr }}"
70
+ end: "{{ endStr }}"
71
+ path: shipments
72
+ mapping:
73
+ id: id
74
+ title: title
75
+ start: startDate
76
+ end: endDate
77
+ color: "#1976d2"
78
+ events:
79
+ onEventClick:
80
+ - navigate: "shipments/{{ event.id }}"
81
+ onDateClick:
82
+ - dialog:
83
+ component: Shipments/CreateShipment
84
+ props:
85
+ date: "{{ dateStr }}"
86
+ onSelect:
87
+ - dialog:
88
+ component: Shipments/CreateShipment
89
+ props:
90
+ startDate: "{{ startStr }}"
91
+ endDate: "{{ endStr }}"
92
+ ```
93
+
94
+ ---
95
+
96
+ ## notes
97
+
98
+ Rich-text notes/comments component with TipTap editor, message threading, and pagination.
99
+
100
+ **Props:**
101
+ | Prop | Type | Default | Description |
102
+ |------|------|---------|-------------|
103
+ | `threadName` | `string` | — | Thread identifier (template-parsed) |
104
+ | `title` | `ILocalizeString` | `Notes` | Card title |
105
+ | `height` | `number` | `400` | Card max height |
106
+ | `placeholder` | `string` | — | Editor placeholder |
107
+ | `readonly` | `boolean \| string` | — | Hide input area |
108
+ | `options.allowAttachments` | `boolean` | `true` | Show attach button |
109
+ | `options.showTimestamps` | `boolean` | `true` | Show relative times |
110
+ | `options.showAuthor` | `boolean` | `true` | Show author info |
111
+ | `options.inputPosition` | `top \| bottom` | `top` | Input placement |
112
+ | `options.autoScroll` | `boolean` | `true` | Auto-scroll to latest |
113
+ | `pagination.pageSize` | `number` | `20` | Notes per page |
114
+ | `pagination.orderBy` | `string` | `created` | Sort field |
115
+ | `pagination.orderDirection` | `ASC \| DESC` | `DESC` | Sort direction |
116
+ | `pagination.loadMoreMode` | `button \| auto \| disabled` | `button` | Load more behavior |
117
+ | `permissions.create` | `string` | — | Create permission |
118
+ | `permissions.edit` | `string` | — | Edit permission |
119
+ | `permissions.delete` | `string` | — | Delete permission |
120
+
121
+ **Events:**
122
+ | Event | Description |
123
+ |-------|-------------|
124
+ | `onNoteCreated` | After note created |
125
+ | `onNoteUpdated` | After note updated |
126
+ | `onNoteDeleted` | After note deleted |
127
+
128
+ **Features:** Enter to send, Shift+Enter for newline. Message grouping (5 min window). Date separators. Long message collapse (>500 chars). Edit/delete on hover.
129
+
130
+ ```yaml
131
+ component: notes
132
+ name: orderNotes
133
+ props:
134
+ threadName: "order-{{ orderId }}"
135
+ title: { en-US: "Order Notes" }
136
+ height: 500
137
+ placeholder: "Add a note..."
138
+ options:
139
+ allowAttachments: true
140
+ inputPosition: top
141
+ autoScroll: true
142
+ pagination:
143
+ pageSize: 30
144
+ orderDirection: DESC
145
+ loadMoreMode: button
146
+ permissions:
147
+ create: "Orders/CreateNote"
148
+ edit: "Orders/EditNote"
149
+ delete: "Orders/DeleteNote"
150
+ events:
151
+ onNoteCreated:
152
+ - refresh: orderActivity
153
+ ```
154
+
155
+ ---
156
+
157
+ ## dashboard
158
+
159
+ CSS Grid-based dashboard with draggable/resizable widgets.
160
+
161
+ **Props:**
162
+ | Prop | Type | Default | Description |
163
+ |------|------|---------|-------------|
164
+ | `toolbar` | `component[]` | — | Filter/form fields in header |
165
+ | `options.rows` | `number` | `12` | Grid rows |
166
+ | `options.columns` | `number` | `12` | Grid columns |
167
+ | `options.gridGap` | `number` | `16` | Gap between cells (px) |
168
+ | `options.allowEdit` | `boolean` | `false` | Enable edit mode (drag/resize/add/remove) |
169
+ | `options.showGridLines` | `boolean` | `true` | Grid background in edit mode |
170
+ | `options.autoSave` | `boolean` | `false` | Auto-save layout |
171
+ | `options.title` | `string` | — | Dashboard title |
172
+ | `options.height` | `string` | — | Container height |
173
+
174
+ **Children:** `dashboard-widget` components only.
175
+
176
+ ---
177
+
178
+ ## dashboard-widget
179
+
180
+ Positioned widget card inside a dashboard. Supports drag-to-move and resize in edit mode.
181
+
182
+ **Props (under `options`):**
183
+ | Prop | Type | Default | Description |
184
+ |------|------|---------|-------------|
185
+ | `row` | `number` | `1` | Grid row start |
186
+ | `col` | `number` | `1` | Grid column start |
187
+ | `rowSpan` | `number` | `1` | Rows spanned |
188
+ | `colSpan` | `number` | `1` | Columns spanned |
189
+ | `title` | `string` | — | Widget header title |
190
+ | `showHeader` | `boolean` | `true` | Show card header |
191
+ | `minRowSpan` / `maxRowSpan` | `number` | — | Size constraints |
192
+ | `minColSpan` / `maxColSpan` | `number` | — | Size constraints |
193
+ | `allowScroll` | `boolean` | `false` | Allow content scroll |
194
+
195
+ **Children:** Yes — any components.
196
+
197
+ ```yaml
198
+ # Dashboard with widgets
199
+ component: dashboard
200
+ name: operationsDashboard
201
+ props:
202
+ options:
203
+ rows: 8
204
+ columns: 12
205
+ gridGap: 16
206
+ allowEdit: true
207
+ title: "Operations Dashboard"
208
+ toolbar:
209
+ - component: field
210
+ name: dateRange
211
+ props: { type: rangedatetime, label: { en-US: "Date Range" } }
212
+ children:
213
+ - component: dashboard-widget
214
+ name: revenueWidget
215
+ props:
216
+ options:
217
+ row: 1
218
+ col: 1
219
+ rowSpan: 3
220
+ colSpan: 6
221
+ title: "Revenue"
222
+ children:
223
+ - component: widget
224
+ name: revenueChart
225
+ props:
226
+ type: chart
227
+ queries:
228
+ - name: getRevenue
229
+ query:
230
+ command: "query { monthlyRevenue { month amount } }"
231
+
232
+ - component: dashboard-widget
233
+ name: statsWidget
234
+ props:
235
+ options:
236
+ row: 1
237
+ col: 7
238
+ rowSpan: 3
239
+ colSpan: 6
240
+ title: "Quick Stats"
241
+ children:
242
+ - component: widget
243
+ name: orderStats
244
+ props:
245
+ type: stats
246
+ queries:
247
+ - name: getStats
248
+ query:
249
+ command: "query { orderStats { total pending completed } }"
250
+ ```
251
+
252
+ ---
253
+
254
+ ## widget
255
+
256
+ Data-driven widget that delegates to sub-components by type.
257
+
258
+ **Props:**
259
+ | Prop | Type | Description |
260
+ |------|------|-------------|
261
+ | `type` | `stats \| chart \| kpi \| metric \| table` | **Required.** Widget type |
262
+ | `initialValues` | `object` | Data loading config (like form) |
263
+ | `queries` | `object` | Named GraphQL queries |
264
+ | `data` | `object` | Static data |
265
+ | `refreshHandler` | `string` | Refresh handler |
266
+
267
+ **Events:** `onLoading`, `onSuccess` (data: `loadedData`), `onError` (data: `error`)
268
+
269
+ ---
270
+
271
+ ## timeline
272
+
273
+ MUI Lab Timeline for displaying events chronologically. Horizontal or vertical orientation.
274
+
275
+ **Props:**
276
+ | Prop | Type | Default | Description |
277
+ |------|------|---------|-------------|
278
+ | `orientation` | `horizontal \| vertical` | `horizontal` | Layout mode |
279
+ | `view` | `day \| week \| month \| year` | `week` | Time view |
280
+ | `startDate` / `endDate` | `string` | — | Initial date range |
281
+ | `eventSources` | `EventSource[]` | — | Same pattern as calendar |
282
+ | `eventTemplate` | `ComponentProps` | — | Custom event template |
283
+ | `options.height` | `string \| number` | `400` | Component height |
284
+ | `options.showTodayMarker` | `boolean` | `true` | Today marker |
285
+ | `options.enableZoom` | `boolean` | `true` | View switcher |
286
+ | `options.enableNavigation` | `boolean` | `true` | Prev/next/today buttons |
287
+ | `options.alternating` | `boolean` | `true` | Alternate sides (vertical) |
288
+ | `options.dateFormat` | `string` | `MMM DD` | Date format |
289
+
290
+ **Events:** `onEventClick` (data: `event`)
291
+
292
+ ```yaml
293
+ component: timeline
294
+ name: orderTimeline
295
+ props:
296
+ orientation: vertical
297
+ view: month
298
+ options:
299
+ enableNavigation: true
300
+ alternating: true
301
+ eventSources:
302
+ - query:
303
+ command: "query($id: Int!) { orderHistory(orderId: $id) { id title date type } }"
304
+ variables: { id: "{{ number orderId }}" }
305
+ path: orderHistory
306
+ eventTemplate:
307
+ component: card
308
+ name: eventCard
309
+ props:
310
+ options:
311
+ variant: outlined
312
+ header:
313
+ title: "{{ item.title }}"
314
+ subheader: "{{ format item.date LLL }}"
315
+ events:
316
+ onEventClick:
317
+ - dialog:
318
+ component: Orders/EventDetail
319
+ props: { eventId: "{{ event.id }}" }
320
+ ```
321
+
322
+ ---
323
+
324
+ ## timeline-grid
325
+
326
+ CSS Grid-based timeline with swim lanes, drill-down, and virtual scrolling.
327
+
328
+ **Props:**
329
+ | Prop | Type | Default | Description |
330
+ |------|------|---------|-------------|
331
+ | `view` | `day \| week \| month \| year` | `week` | Time view |
332
+ | `startDate` / `endDate` | `string` | — | Date range |
333
+ | `eventSources` | `EventSource[]` | — | Event data sources |
334
+ | `eventTemplate` | `ComponentProps` | — | Custom event template |
335
+ | `options.height` | `string \| number` | `600` | Container height |
336
+ | `options.cellHeight` | `number` | `60/100` | Cell height (px) |
337
+ | `options.groupBy` | `string` | — | Field for swim lane grouping |
338
+ | `options.showTodayMarker` | `boolean` | `true` | Today marker |
339
+ | `options.enableNavigation` | `boolean` | `true` | Nav controls |
340
+ | `options.virtualScroll` | `boolean` | `true` | Virtual scrolling |
341
+ | `options.hourInterval` | `15 \| 30 \| 60` | `60` | Time intervals (day/week) |
342
+ | `options.showWeekends` | `boolean` | `true` | Show weekends |
343
+ | `options.showTotalCount` | `boolean` | `false` | Show event totals |
344
+
345
+ **Events:**
346
+ | Event | Data | Description |
347
+ |-------|------|-------------|
348
+ | `onEventClick` | `item, view` | Event clicked |
349
+ | `onCellClick` | `column, row, date, view` | Empty cell clicked |
350
+ | `onViewChange` | `previousView, newView, startDate, endDate` | View changed |
351
+ | `onNavigate` | `direction, view, startDate, endDate` | Navigation |
352
+ | `onEventsLoaded` | `events, eventCount, view, dataRange` | Data loaded |
353
+ | `onLoad` | `view, startDate, endDate, options` | Initial mount |
354
+
355
+ **Column drill-down:** Click column header: year->month, month->week, week->day.
356
+
357
+ ```yaml
358
+ component: timeline-grid
359
+ name: scheduleGrid
360
+ props:
361
+ view: week
362
+ options:
363
+ height: 700
364
+ cellHeight: 80
365
+ groupBy: assignee
366
+ showWeekends: false
367
+ hourInterval: 30
368
+ showTotalCount: true
369
+ enableNavigation: true
370
+ eventSources:
371
+ - query:
372
+ command: |
373
+ query($start: DateTime!, $end: DateTime!) {
374
+ scheduleEvents(start: $start, end: $end) {
375
+ id title startDate endDate assignee status
376
+ }
377
+ }
378
+ path: scheduleEvents
379
+ events:
380
+ onEventClick:
381
+ - dialog:
382
+ component: Schedule/EventDetail
383
+ props: { eventId: "{{ item.id }}" }
384
+ onCellClick:
385
+ - dialog:
386
+ component: Schedule/CreateEvent
387
+ props: { date: "{{ date }}", assignee: "{{ row }}" }
388
+ ```
389
+
390
+ ---
391
+
392
+ ## oauth2
393
+
394
+ OAuth2 authorization flow button. Opens popup for auth, exchanges code for token.
395
+
396
+ **Props:**
397
+ | Prop | Type | Description |
398
+ |------|------|-------------|
399
+ | `clientId` | `string` | OAuth2 client ID (template-parsed) |
400
+ | `clientSecret` | `string` | OAuth2 client secret (template-parsed) |
401
+ | `authorizationUrl` | `string` | Authorization endpoint |
402
+ | `tokenUrl` | `string` | Token exchange endpoint |
403
+ | `scopes` | `string[]` | Requested scopes |
404
+ | `additionalParams` | `Record<string, string>` | Extra auth URL params |
405
+ | `additionalHeaders` | `object` | Extra token request headers |
406
+ | `label` | `ILocalizeString` | Button label (default: `Authorize`) |
407
+ | `className` | `string` | Button CSS class |
408
+
409
+ **Events:** `onToken` — fires with `{ token }` when OAuth completes.
410
+
411
+ ```yaml
412
+ component: oauth2
413
+ name: intuitAuth
414
+ props:
415
+ label: { en-US: "Connect QuickBooks" }
416
+ clientId: "{{ quickbooksClientId }}"
417
+ clientSecret: "{{ quickbooksClientSecret }}"
418
+ authorizationUrl: "https://appcenter.intuit.com/connect/oauth2"
419
+ tokenUrl: "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer"
420
+ scopes:
421
+ - com.intuit.quickbooks.accounting
422
+ onToken:
423
+ - mutation:
424
+ command: "mutation($token: String!) { saveOAuthToken(token: $token) { success } }"
425
+ variables: { token: "{{ token }}" }
426
+ - notification: { message: { en-US: "Connected!" }, type: success }
427
+ ```