@cxtms/cx-schema 1.1.0 → 1.2.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.
- package/.claude/skills/cx-core/SKILL.md +93 -0
- package/.claude/skills/cx-core/ref-entity-accounting.md +173 -0
- package/.claude/skills/cx-core/ref-entity-commodity.md +205 -0
- package/.claude/skills/cx-core/ref-entity-contact.md +153 -0
- package/.claude/skills/cx-core/ref-entity-geography.md +119 -0
- package/.claude/skills/cx-core/ref-entity-job.md +77 -0
- package/.claude/skills/cx-core/ref-entity-order-sub.md +140 -0
- package/.claude/skills/cx-core/ref-entity-order.md +168 -0
- package/.claude/skills/cx-core/ref-entity-rate.md +174 -0
- package/.claude/skills/cx-core/ref-entity-shared.md +147 -0
- package/.claude/skills/cx-core/ref-entity-warehouse.md +110 -0
- package/.claude/skills/cx-module/SKILL.md +402 -0
- package/.claude/skills/cx-module/ref-components-data.md +286 -0
- package/.claude/skills/cx-module/ref-components-display.md +394 -0
- package/.claude/skills/cx-module/ref-components-forms.md +362 -0
- package/.claude/skills/cx-module/ref-components-interactive.md +306 -0
- package/.claude/skills/cx-module/ref-components-layout.md +295 -0
- package/.claude/skills/cx-module/ref-components-specialized.md +427 -0
- package/.claude/skills/cx-workflow/SKILL.md +330 -0
- package/.claude/skills/cx-workflow/ref-accounting.md +66 -0
- package/.claude/skills/cx-workflow/ref-communication.md +161 -0
- package/.claude/skills/cx-workflow/ref-entity.md +162 -0
- package/.claude/skills/cx-workflow/ref-expressions.md +239 -0
- package/.claude/skills/cx-workflow/ref-filetransfer.md +80 -0
- package/.claude/skills/cx-workflow/ref-flow.md +180 -0
- package/.claude/skills/cx-workflow/ref-other.md +120 -0
- package/.claude/skills/cx-workflow/ref-query.md +85 -0
- package/.claude/skills/cx-workflow/ref-utilities.md +192 -0
- package/dist/cli.js +252 -33
- package/dist/cli.js.map +1 -1
- package/package.json +3 -2
- package/schemas/workflows/tasks/action-event.json +65 -0
- package/schemas/workflows/tasks/all.json +126 -26
- package/schemas/workflows/tasks/appmodule.json +56 -0
- package/schemas/workflows/tasks/attachment.json +4 -1
- package/schemas/workflows/tasks/authentication.json +72 -0
- package/schemas/workflows/tasks/caching.json +68 -0
- package/schemas/workflows/tasks/charge.json +3 -1
- package/schemas/workflows/tasks/commodity.json +3 -0
- package/schemas/workflows/tasks/contact-address.json +72 -0
- package/schemas/workflows/tasks/contact-payment-method.json +72 -0
- package/schemas/workflows/tasks/edi.json +65 -0
- package/schemas/workflows/tasks/filetransfer.json +102 -0
- package/schemas/workflows/tasks/flow-transition.json +68 -0
- package/schemas/workflows/tasks/httpRequest.json +23 -0
- package/schemas/workflows/tasks/import.json +64 -0
- package/schemas/workflows/tasks/inventory.json +67 -0
- package/schemas/workflows/tasks/movement.json +54 -0
- package/schemas/workflows/tasks/note.json +59 -0
- package/schemas/workflows/tasks/number.json +65 -0
- package/schemas/workflows/tasks/order.json +8 -1
- package/schemas/workflows/tasks/pdf-document.json +60 -0
- package/schemas/workflows/tasks/user.json +70 -0
- package/scripts/postinstall.js +2 -2
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Shared Entity Reference
|
|
2
|
+
|
|
3
|
+
Tag, Attachment, Division, EquipmentType, PackageType, Note/NoteThread.
|
|
4
|
+
|
|
5
|
+
## Tag
|
|
6
|
+
|
|
7
|
+
Tagging system for orders, commodities, inventory items.
|
|
8
|
+
|
|
9
|
+
| Field | Type | Notes |
|
|
10
|
+
|-------|------|-------|
|
|
11
|
+
| `tagId` | `int` | PK |
|
|
12
|
+
| `organizationId` | `int` | |
|
|
13
|
+
| `name` | `string` | |
|
|
14
|
+
| `description` | `string?` | |
|
|
15
|
+
| `entityName` | `string` | Discriminator: which entity type this tag belongs to |
|
|
16
|
+
| `isDeleted` | `bool` | Soft delete |
|
|
17
|
+
| `customValues` | `Dictionary` | jsonb |
|
|
18
|
+
|
|
19
|
+
**Join entities** (all have own `customValues`):
|
|
20
|
+
- `OrderTag` — `orderId` + `tagId` + `customValues`
|
|
21
|
+
- `CommodityTag` — `commodityId` + `tagId` + `customValues`
|
|
22
|
+
- `InventoryItemTag` — `inventoryItemId` + `tagId` + `customValues`
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Attachment
|
|
27
|
+
|
|
28
|
+
File attachments linked to orders, contacts, jobs, etc.
|
|
29
|
+
|
|
30
|
+
| Field | Type | Notes |
|
|
31
|
+
|-------|------|-------|
|
|
32
|
+
| `attachmentId` | `int` | PK |
|
|
33
|
+
| `attachmentGuid` | `Guid?` | |
|
|
34
|
+
| `fileName` | `string` | |
|
|
35
|
+
| `fileUri` | `string` | Storage path |
|
|
36
|
+
| `fileExtension` | `string` | Computed from fileName |
|
|
37
|
+
| `previewUri` | `string?` | |
|
|
38
|
+
| `thumbnailUri` | `string?` | |
|
|
39
|
+
| `description` | `string?` | |
|
|
40
|
+
| `attachmentType` | `AttachmentType` enum | Picture=1, OtherDocument, Avatar, CustomerDocument |
|
|
41
|
+
| `parentId` | `string?` | Polymorphic FK (entity ID as string) |
|
|
42
|
+
| `parentType` | `AttachmentParentType` enum | None=0, Order=1, Contact=2, AccountingTransaction=3, EquipmentType=4, Job=5, Commodity=6 |
|
|
43
|
+
| `status` | `AttachmentStatus` enum | Active=0, PendingUpload=1, UploadFailed=2 |
|
|
44
|
+
| `category` | `AttachmentCategory` enum | General=0, FieldValue=1 |
|
|
45
|
+
| `organizationId` | `int` | |
|
|
46
|
+
| `customValues` | `Dictionary` | jsonb |
|
|
47
|
+
|
|
48
|
+
### GraphQL Computed
|
|
49
|
+
|
|
50
|
+
- `isImage`, `isPdf` — computed from extension
|
|
51
|
+
- `presignedFileUri`, `presignedPreviewUri`, `presignedThumbnailUri` — signed URLs
|
|
52
|
+
- `getPresignedUri(expiresInDays, uriType)` — custom resolver
|
|
53
|
+
- `getParentOrder` — resolve parent Order
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Division
|
|
58
|
+
|
|
59
|
+
Organization divisions/branches.
|
|
60
|
+
|
|
61
|
+
| Field | Type | Notes |
|
|
62
|
+
|-------|------|-------|
|
|
63
|
+
| `divisionId` | `int` | PK |
|
|
64
|
+
| `organizationId` | `int` | |
|
|
65
|
+
| `divisionName` | `string` | |
|
|
66
|
+
| `email` | `string?` | |
|
|
67
|
+
| `phoneNumber` | `string?` | |
|
|
68
|
+
| `faxNumber` | `string?` | |
|
|
69
|
+
| `streetAndNumber` | `string?` | |
|
|
70
|
+
| `city` | `string?` | |
|
|
71
|
+
| `stateCode` | `string?` | FK to State |
|
|
72
|
+
| `countryCode` | `string?` | FK to Country |
|
|
73
|
+
| `zipCode` | `string?` | |
|
|
74
|
+
| `portId` | `string?` | FK to Port |
|
|
75
|
+
| `comments` | `string?` | |
|
|
76
|
+
| `parentDivisionId` | `int?` | Self-referencing FK |
|
|
77
|
+
| `assignDivisionToEntities` | `bool` | |
|
|
78
|
+
| `useDivisionInDocumentHeaders` | `bool` | |
|
|
79
|
+
| `airAmsOriginatorCode` | `string?` | |
|
|
80
|
+
|
|
81
|
+
**Navigation:** `country`, `state`, `port`, `parentDivision`, `nestedDivisions` (children). No customValues.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## EquipmentType
|
|
86
|
+
|
|
87
|
+
| Field | Type | Notes |
|
|
88
|
+
|-------|------|-------|
|
|
89
|
+
| `equipmentTypeId` | `int` | PK |
|
|
90
|
+
| `organizationId` | `int` | |
|
|
91
|
+
| `name` | `string` | |
|
|
92
|
+
|
|
93
|
+
No customValues. Linked to carriers via `CarrierEquipment` join.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## PackageType
|
|
98
|
+
|
|
99
|
+
| Field | Type | Notes |
|
|
100
|
+
|-------|------|-------|
|
|
101
|
+
| `packageTypeId` | `int` | PK |
|
|
102
|
+
| `organizationId` | `int` | |
|
|
103
|
+
| `name` | `string` | |
|
|
104
|
+
| `height` | `decimal` | |
|
|
105
|
+
| `length` | `decimal` | |
|
|
106
|
+
| `width` | `decimal` | |
|
|
107
|
+
| `weight` | `decimal` | |
|
|
108
|
+
| `maximumWeight` | `decimal` | |
|
|
109
|
+
| `volume` | `decimal` | |
|
|
110
|
+
| `air` | `bool` | Mode applicability |
|
|
111
|
+
| `ground` | `bool` | |
|
|
112
|
+
| `ocean` | `bool` | |
|
|
113
|
+
| `containerDescriptionCode` | `string?` | FK |
|
|
114
|
+
| `containerTypeCode` | `string?` | FK |
|
|
115
|
+
| `packageCategoryCode` | `string` | FK |
|
|
116
|
+
|
|
117
|
+
**Navigation:** `packageCategory`, `containerDescription`, `containerType`. No customValues.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## NoteThread
|
|
122
|
+
|
|
123
|
+
| Field | Type | Notes |
|
|
124
|
+
|-------|------|-------|
|
|
125
|
+
| `id` | `Guid` | PK |
|
|
126
|
+
| `organizationId` | `int` | |
|
|
127
|
+
| `name` | `string` | |
|
|
128
|
+
| `slug` | `string` | Lowercase identifier |
|
|
129
|
+
| `isDeleted` | `bool` | Soft delete |
|
|
130
|
+
| `metadata` | `Dictionary` | jsonb (non-nullable, defaults to {}) |
|
|
131
|
+
|
|
132
|
+
**Collections:** `notes` (one-to-many)
|
|
133
|
+
|
|
134
|
+
## Note
|
|
135
|
+
|
|
136
|
+
| Field | Type | Notes |
|
|
137
|
+
|-------|------|-------|
|
|
138
|
+
| `id` | `Guid` | PK |
|
|
139
|
+
| `threadId` | `Guid` | FK to NoteThread |
|
|
140
|
+
| `threadName` | `string` | Snapshot of thread name |
|
|
141
|
+
| `content` | `Dictionary` | TipTap document format (root type="doc"), max 256KB |
|
|
142
|
+
| `mentions` | `[Dictionary]?` | Mentioned users/entities |
|
|
143
|
+
| `tags` | `[string]` | Max 20 tags, each max 32 chars |
|
|
144
|
+
| `isPinned` | `bool` | |
|
|
145
|
+
| `isDeleted` | `bool` | Soft delete |
|
|
146
|
+
|
|
147
|
+
**Navigation:** `thread` (NoteThread)
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Warehouse & Inventory Entity Reference
|
|
2
|
+
|
|
3
|
+
## InventoryItem
|
|
4
|
+
|
|
5
|
+
SKU-level inventory tracking.
|
|
6
|
+
|
|
7
|
+
| Field | Type | Notes |
|
|
8
|
+
|-------|------|-------|
|
|
9
|
+
| `inventoryItemId` | `int` | PK |
|
|
10
|
+
| `organizationId` | `int` | |
|
|
11
|
+
| `sku` | `string` | Stock keeping unit |
|
|
12
|
+
| `productName` | `string?` | |
|
|
13
|
+
| `modelNumber` | `string?` | |
|
|
14
|
+
| `description` | `string?` | |
|
|
15
|
+
| `availableQuantity` | `int` | |
|
|
16
|
+
| `backOrderQuantity` | `int` | |
|
|
17
|
+
| `height` | `decimal?` | |
|
|
18
|
+
| `length` | `decimal?` | |
|
|
19
|
+
| `width` | `decimal?` | |
|
|
20
|
+
| `weight` | `decimal?` | |
|
|
21
|
+
| `volumePiece` | `decimal?` | |
|
|
22
|
+
| `dimensionsUnit` | `DimensionsUnit` enum | In, Cm, M, Ft |
|
|
23
|
+
| `weightUnit` | `WeightUnit` enum | Lb, Kg |
|
|
24
|
+
| `volumeUnit` | `VolumeUnit` enum | Ft, Vlb, Vkg, M, In, Cm |
|
|
25
|
+
| `useSerialNumbers` | `bool` | |
|
|
26
|
+
| `isInactive` | `bool` | |
|
|
27
|
+
| `customerContactId` | `int?` | FK to Contact |
|
|
28
|
+
| `manufacturerContactId` | `int?` | FK to Contact |
|
|
29
|
+
| `packageTypeId` | `int?` | FK to PackageType |
|
|
30
|
+
| `customValues` | `Dictionary` | jsonb |
|
|
31
|
+
|
|
32
|
+
### Navigation
|
|
33
|
+
|
|
34
|
+
| Field | Type |
|
|
35
|
+
|-------|------|
|
|
36
|
+
| `customerContact` | `Contact?` |
|
|
37
|
+
| `manufacturerContact` | `Contact?` |
|
|
38
|
+
| `packageType` | `PackageType?` |
|
|
39
|
+
| `commodities` | `[Commodity]` |
|
|
40
|
+
| `inventoryItemTags` | `[InventoryItemTag]` |
|
|
41
|
+
| `allTags` | `[InventoryItemAllTagsView]` |
|
|
42
|
+
|
|
43
|
+
### GraphQL Computed
|
|
44
|
+
|
|
45
|
+
- `getContact(idPropertyName)` — resolve contact from customValues
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## WarehouseLocation
|
|
50
|
+
|
|
51
|
+
Physical storage zones/locations in warehouse.
|
|
52
|
+
|
|
53
|
+
| Field | Type | Notes |
|
|
54
|
+
|-------|------|-------|
|
|
55
|
+
| `warehouseLocationId` | `int` | PK |
|
|
56
|
+
| `organizationId` | `int` | |
|
|
57
|
+
| `code` | `string` | Location code |
|
|
58
|
+
| `description` | `string?` | |
|
|
59
|
+
| `locationType` | `LocationType` enum | See below |
|
|
60
|
+
| `height` | `decimal?` | |
|
|
61
|
+
| `length` | `decimal?` | |
|
|
62
|
+
| `width` | `decimal?` | |
|
|
63
|
+
| `maximumWeight` | `decimal?` | |
|
|
64
|
+
| `isInactive` | `bool` | |
|
|
65
|
+
| `customerId` | `int?` | FK to Contact |
|
|
66
|
+
| `parentZoneId` | `int?` | FK to WarehouseZone |
|
|
67
|
+
|
|
68
|
+
### LocationType Enum
|
|
69
|
+
|
|
70
|
+
Receiving, Storage, Replenishment, Picking, QualityControl, Shipping, Mobile, Other, Packing, Service, PutAway
|
|
71
|
+
|
|
72
|
+
### Navigation
|
|
73
|
+
|
|
74
|
+
`customer` (Contact), `parentWarehouseZone` (WarehouseZone), `commodities` ([Commodity]). No customValues.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## CargoMovement
|
|
79
|
+
|
|
80
|
+
**Not a separate entity** — implemented as `Order` with `orderType = CargoMovement` (value 7).
|
|
81
|
+
|
|
82
|
+
Movement-specific data stored in Order's `customValues`:
|
|
83
|
+
|
|
84
|
+
| CustomValues Key | Type | Notes |
|
|
85
|
+
|-----------------|------|-------|
|
|
86
|
+
| `movementStatus` | `string` | e.g., "Created" |
|
|
87
|
+
| `movementType` | `string` | |
|
|
88
|
+
| `destinationLocationId` | `string/int` | Warehouse location ID |
|
|
89
|
+
| `destinationLocationDescription` | `string` | |
|
|
90
|
+
| `transportationMode` | `string` | |
|
|
91
|
+
| `finalMileCarrier` | `string` | |
|
|
92
|
+
|
|
93
|
+
The Order's `trackingNumber` field serves as the pallet number for cargo movements.
|
|
94
|
+
|
|
95
|
+
```yaml
|
|
96
|
+
# Example: Query cargo movement in workflow
|
|
97
|
+
- task: "Query/GraphQL@1"
|
|
98
|
+
name: GetMovement
|
|
99
|
+
inputs:
|
|
100
|
+
query: >-
|
|
101
|
+
query($id: Int!, $orgId: Int!) {
|
|
102
|
+
order(organizationId: $orgId, orderId: $id) {
|
|
103
|
+
orderId orderNumber trackingNumber orderType
|
|
104
|
+
customValues
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
variables:
|
|
108
|
+
id: "{{ int inputs.orderId }}"
|
|
109
|
+
orgId: "{{ int organizationId }}"
|
|
110
|
+
```
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cx-module
|
|
3
|
+
description: Generate schema-valid CargoXplorer app module YAML files (UI screens, forms, grids, routes)
|
|
4
|
+
argument-hint: <description of what to build>
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are a CargoXplorer module YAML builder. You generate schema-valid YAML for CX app modules — UI screens, forms, data grids, routes, and components. All output must conform to the JSON schemas in `.cx-schema/`.
|
|
8
|
+
|
|
9
|
+
**IMPORTANT — use `cx-cli` for all module operations:**
|
|
10
|
+
- **Scaffold**: `npx cx-cli create module <name> --template <template>` — generates a schema-valid YAML file. ALWAYS run this first, then read the generated file, then customize. Do NOT write YAML from scratch or copy templates manually.
|
|
11
|
+
- **Scaffold with fields**: `npx cx-cli create module <name> --template <template> --options '<json>'`
|
|
12
|
+
- **Validate**: `npx cx-cli <file.yaml>` — run after every change
|
|
13
|
+
- **Schema lookup**: `npx cx-cli schema <component>` — e.g., `cx-cli schema form`, `cx-cli schema dataGrid`
|
|
14
|
+
- **Examples**: `npx cx-cli example <component>` — show example YAML
|
|
15
|
+
- **List schemas**: `npx cx-cli list`
|
|
16
|
+
- **Extract**: `npx cx-cli extract <source> <component> --to <target>` — move components between modules
|
|
17
|
+
- **Feature folder**: `npx cx-cli create module <name> --template <template> --feature <feature-name>`
|
|
18
|
+
|
|
19
|
+
## Generation Workflow
|
|
20
|
+
|
|
21
|
+
### Step 1: Scaffold via CLI — MANDATORY
|
|
22
|
+
|
|
23
|
+
**You MUST run `cx-cli create module` to generate the initial file.** Do not skip this step. Do not write YAML from scratch. Do not read template files and copy them manually. The CLI generates correct UUIDs, file paths, and structure.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx cx-cli create module <name> --template <template>
|
|
27
|
+
npx cx-cli create module <name> --template <template> --options '<json>'
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
| Template | Use Case |
|
|
31
|
+
|----------|----------|
|
|
32
|
+
| `default` | Generic module with form |
|
|
33
|
+
| `form` | Entity create/edit form |
|
|
34
|
+
| `configuration` | Settings/config screen |
|
|
35
|
+
| `grid` | List/table view |
|
|
36
|
+
| `select` | Reusable async select |
|
|
37
|
+
|
|
38
|
+
### Step 2: Read the generated file
|
|
39
|
+
|
|
40
|
+
### Step 3: Customize for the use case
|
|
41
|
+
|
|
42
|
+
**All templates** — update module name, component names, entity fields, permissions, GraphQL queries/mutations.
|
|
43
|
+
|
|
44
|
+
**`form`** — update form fields, validationSchema, query/mutation field lists. Add tabs for grouped fields. Customize toolbar buttons. Update dirtyGuard messages.
|
|
45
|
+
|
|
46
|
+
**`configuration`** — update form fields, initialValues.append defaults, validationSchema rules, query/mutation field lists. Add tabs for grouped settings.
|
|
47
|
+
|
|
48
|
+
**`grid`** — update view columns, filters, entity fields. Add/remove views. Customize dotsMenu actions. Configure toolbar with export/import actions.
|
|
49
|
+
|
|
50
|
+
**`select`** — update `valueFieldName`, `itemLabelTemplate`, `itemValueTemplate`. Customize GraphQL query fields and variables. Set `navigateActionPermission`. Configure `dropDownToolbar` create button dialog.
|
|
51
|
+
|
|
52
|
+
### Step 4: Validate
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npx cx-cli <generated-file.yaml>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## --options Flag
|
|
61
|
+
|
|
62
|
+
Customize generated modules at scaffold time with `--options`. Accepts inline JSON or a file path.
|
|
63
|
+
|
|
64
|
+
### Field Array Format (all templates)
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npx cx-cli create module "Tariff" --template grid --options '[
|
|
68
|
+
{"name": "code", "type": "text", "label": "Tariff Code", "required": true},
|
|
69
|
+
{"name": "rate", "type": "number", "label": "Rate %"},
|
|
70
|
+
{"name": "effectiveDate", "type": "date"},
|
|
71
|
+
{"name": "isActive", "type": "checkbox", "label": "Active"}
|
|
72
|
+
]'
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Object Format (with entityName)
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npx cx-cli create module "Country" --template select --options '{
|
|
79
|
+
"entityName": "Country",
|
|
80
|
+
"fields": [
|
|
81
|
+
{"name": "countryCode", "type": "text", "label": "Country Code"},
|
|
82
|
+
{"name": "countryName", "type": "text", "label": "Country Name"}
|
|
83
|
+
]
|
|
84
|
+
}'
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Field Properties
|
|
88
|
+
|
|
89
|
+
| Property | Type | Description |
|
|
90
|
+
|----------|------|-------------|
|
|
91
|
+
| `name` | string | **Required.** Field name (camelCase) |
|
|
92
|
+
| `type` | string | **Required.** text, number, checkbox, date, select, select-async, textarea, email |
|
|
93
|
+
| `label` | string | Display label (auto-generated from name if omitted) |
|
|
94
|
+
| `required` | boolean | Add to validationSchema as required |
|
|
95
|
+
| `default` | any | Default value (form/configuration template: added to initialValues.append) |
|
|
96
|
+
|
|
97
|
+
### What --options Customizes Per Template
|
|
98
|
+
|
|
99
|
+
| Template | Fields | Entity | Queries |
|
|
100
|
+
|----------|--------|--------|---------|
|
|
101
|
+
| `form` | Form children, validationSchema, initialValues.append | Entity field definitions | GraphQL query field lists (preserves `id`) |
|
|
102
|
+
| `configuration` | Form children, validationSchema, initialValues.append | Entity field definitions | GraphQL query field lists |
|
|
103
|
+
| `grid` | View columns (with showAs by type), entity fields | Entity name + rootEntityName | — |
|
|
104
|
+
| `select` | Entity fields, itemLabelTemplate | Entity name | GraphQL query field lists |
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Extract Command
|
|
109
|
+
|
|
110
|
+
Move a component (and its routes) from one module into another. Useful for splitting large modules into smaller, focused ones.
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
cx-cli extract <source-file> <component-name> --to <target-file>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### What Gets Moved
|
|
117
|
+
- The component matching the exact `name` field
|
|
118
|
+
- Any routes whose `component` field matches the component name
|
|
119
|
+
- Permissions and entities are **NOT** moved
|
|
120
|
+
|
|
121
|
+
### Examples
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
# Extract to a new file (creates module scaffold automatically)
|
|
125
|
+
npx cx-cli extract modules/orders.yaml Orders/CreateItem --to modules/order-create.yaml
|
|
126
|
+
|
|
127
|
+
# Extract to an existing module
|
|
128
|
+
npx cx-cli extract modules/main.yaml Dashboard --to modules/dashboard.yaml
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### New Target Scaffold
|
|
132
|
+
When the target file doesn't exist, a new module is created with:
|
|
133
|
+
- `module` name derived from filename (PascalCase)
|
|
134
|
+
- Fresh `appModuleId` (UUID)
|
|
135
|
+
- `application` copied from source
|
|
136
|
+
- Empty `entities` and `permissions` arrays
|
|
137
|
+
|
|
138
|
+
### Workflow
|
|
139
|
+
1. Run `extract` to move the component
|
|
140
|
+
2. Manually move any related permissions/entities if needed
|
|
141
|
+
3. Validate both files: `npx cx-cli <source>` and `npx cx-cli <target>`
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## On-Demand References
|
|
146
|
+
|
|
147
|
+
**Read these files only when needed for the current task.** Do not load all references upfront.
|
|
148
|
+
|
|
149
|
+
### Entity Field Reference (cx-core)
|
|
150
|
+
|
|
151
|
+
!cat .claude/skills/cx-core/SKILL.md
|
|
152
|
+
|
|
153
|
+
### Component Directory
|
|
154
|
+
|
|
155
|
+
Read the relevant category ref file when building specific component types:
|
|
156
|
+
|
|
157
|
+
| Category | Components | File |
|
|
158
|
+
|----------|-----------|------|
|
|
159
|
+
| **Layout & Structure** | `layout`, `row`, `col`, `header`, `tabs`, `toolbar`, `card`, `line` | `.claude/skills/cx-module/ref-components-layout.md` |
|
|
160
|
+
| **Forms & Input** | `form`, `field`, `field-collection`, `barcodeScanner` | `.claude/skills/cx-module/ref-components-forms.md` |
|
|
161
|
+
| **Data Display** | `dataGrid`, `text`, `markup`, `badge`, `icon`, `image`, `photo`, `summary`, `diff`, `viewer`, `embed` | `.claude/skills/cx-module/ref-components-display.md` |
|
|
162
|
+
| **Interactive & Nav** | `button`, `dropdown`, `menuButton`, `link`, `redirect`, `navbar`, `navbarItem`, `navbarLink`, `navDropdown` | `.claude/skills/cx-module/ref-components-interactive.md` |
|
|
163
|
+
| **Data & Collections** | `collection`, `list`, `listItem`, `datasource`, `script` | `.claude/skills/cx-module/ref-components-data.md` |
|
|
164
|
+
| **Specialized** | `calendar`, `notes`, `dashboard`, `dashboard-widget`, `widget`, `timeline`, `timeline-grid`, `oauth2` | `.claude/skills/cx-module/ref-components-specialized.md` |
|
|
165
|
+
|
|
166
|
+
### Templates
|
|
167
|
+
|
|
168
|
+
Read the relevant template after scaffolding to understand the generated structure:
|
|
169
|
+
|
|
170
|
+
| Template | File |
|
|
171
|
+
|----------|------|
|
|
172
|
+
| default | `templates/module.yaml` |
|
|
173
|
+
| form | `templates/module-form.yaml` |
|
|
174
|
+
| configuration | `templates/module-configuration.yaml` |
|
|
175
|
+
| grid | `templates/module-grid.yaml` |
|
|
176
|
+
| select | `templates/module-select.yaml` |
|
|
177
|
+
|
|
178
|
+
### JSON Schemas
|
|
179
|
+
|
|
180
|
+
Read schema files from `.cx-schema/` only when debugging validation errors:
|
|
181
|
+
- `schemas.json` — main schema definitions
|
|
182
|
+
- `components/<type>.json` — component schemas (layout, form, dataGrid, field, button, tabs, card, calendar, collection, appComponent, module)
|
|
183
|
+
- `fields/<type>.json` — field schemas (text, select, select-async)
|
|
184
|
+
- `actions/<type>.json` — action schemas (navigate, mutation, dialog, all)
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
# Module YAML Reference
|
|
189
|
+
|
|
190
|
+
## Top-Level Structure
|
|
191
|
+
|
|
192
|
+
```yaml
|
|
193
|
+
module:
|
|
194
|
+
name: "<ModuleName>" # PascalCase identifier
|
|
195
|
+
appModuleId: "<uuid>" # Generate a new UUID v4
|
|
196
|
+
displayName:
|
|
197
|
+
en-US: "Human Readable Name"
|
|
198
|
+
description:
|
|
199
|
+
en-US: "Module description"
|
|
200
|
+
application: "CargoXplorer" # Required
|
|
201
|
+
fileName: "modules/<name>-module.yaml" # File path in repo
|
|
202
|
+
|
|
203
|
+
entities:
|
|
204
|
+
- name: <EntityName>
|
|
205
|
+
entityKind: Order | Contact | OrderEntity | AccountingTransaction | Calendar | CalendarEvent | Other
|
|
206
|
+
extension: false # true if extending existing entity
|
|
207
|
+
displayName: { en-US: "..." }
|
|
208
|
+
fields:
|
|
209
|
+
- name: fieldName
|
|
210
|
+
fieldType: text | number | date | boolean | ...
|
|
211
|
+
displayName: { en-US: "..." }
|
|
212
|
+
isCustomField: false
|
|
213
|
+
props:
|
|
214
|
+
allowOrderBy: true
|
|
215
|
+
allowFilter: true
|
|
216
|
+
filter: # Optional filter selector
|
|
217
|
+
component: "Contacts/Select"
|
|
218
|
+
props:
|
|
219
|
+
filter: "contactType: Customer"
|
|
220
|
+
options: { baseName: "contactId" }
|
|
221
|
+
|
|
222
|
+
permissions:
|
|
223
|
+
- name: "ModuleName/Read" # PascalCase with slashes
|
|
224
|
+
displayName: { en-US: "..." }
|
|
225
|
+
roles: ["Admin", "Manager"]
|
|
226
|
+
|
|
227
|
+
routes:
|
|
228
|
+
- name: "routeName"
|
|
229
|
+
path: "/module-path" # Supports :params
|
|
230
|
+
component: ComponentName # References component name
|
|
231
|
+
platforms: [web, mobile] # Optional, defaults to both
|
|
232
|
+
props:
|
|
233
|
+
title: { en-US: "..." }
|
|
234
|
+
icon: "icon-name"
|
|
235
|
+
permission: "permission-name"
|
|
236
|
+
|
|
237
|
+
components:
|
|
238
|
+
- name: "ModuleName/ComponentName" # Pattern: Module/Component
|
|
239
|
+
displayName: { en-US: "..." }
|
|
240
|
+
permissions: "permission-name" # String or array
|
|
241
|
+
layout:
|
|
242
|
+
component: layout # Root must be a component
|
|
243
|
+
# ... component tree
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Action Types
|
|
247
|
+
|
|
248
|
+
Actions are used in event handlers (onClick, onSubmit, etc.) as arrays:
|
|
249
|
+
|
|
250
|
+
```yaml
|
|
251
|
+
onClick:
|
|
252
|
+
- navigate: "~/path/{{ id }}" # Navigate to route
|
|
253
|
+
- navigateBack: { fallback: "/home" } # Go back in history
|
|
254
|
+
- navigateBackOrClose: { fallback: "/home" } # Go back or close dialog
|
|
255
|
+
- refresh: "componentName" # Refresh a component
|
|
256
|
+
- notification: { message: { en-US: "Saved!" }, type: success } # success|error|warning|info
|
|
257
|
+
- confirm: { title: { en-US: "Delete?" }, message: { en-US: "Are you sure?" } }
|
|
258
|
+
- mutation:
|
|
259
|
+
command: "mutation M($input: MInput!) { m(input: $input) { result } }"
|
|
260
|
+
variables: { input: "{{ form }}" }
|
|
261
|
+
onSuccess: [...]
|
|
262
|
+
onError: [...]
|
|
263
|
+
- query:
|
|
264
|
+
command: "query Q($id: ID!) { entity(id: $id) { id name } }"
|
|
265
|
+
variables: { id: "{{ entityId }}" }
|
|
266
|
+
onSuccess: [...]
|
|
267
|
+
onError: [...]
|
|
268
|
+
- setFields: { "fieldName": "{{ value }}" } # Set form field values
|
|
269
|
+
- setStore: { "key": "{{ value }}" } # Set store values
|
|
270
|
+
- validateForm: {} # Trigger form validation
|
|
271
|
+
- dialog:
|
|
272
|
+
name: "dialogName"
|
|
273
|
+
props: { title: { en-US: "Title" } }
|
|
274
|
+
component: { component: "Module/Component" }
|
|
275
|
+
onClose: [...]
|
|
276
|
+
- workflow:
|
|
277
|
+
workflowId: "<uuid>"
|
|
278
|
+
inputs: { key: "value" }
|
|
279
|
+
onSuccess: [...]
|
|
280
|
+
onError: [...]
|
|
281
|
+
- fileDownload: { url: "...", fileName: "..." }
|
|
282
|
+
- forEach:
|
|
283
|
+
items: "{{ selectedItems }}"
|
|
284
|
+
item: "currentItem"
|
|
285
|
+
actions: [...]
|
|
286
|
+
- if: "{{ condition }}"
|
|
287
|
+
then: [...]
|
|
288
|
+
else: [...]
|
|
289
|
+
- consoleLog: { message: "debug info" }
|
|
290
|
+
- openBarcodeScanner: { onScan: [...] }
|
|
291
|
+
- resetDirtyState: {}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Common Patterns
|
|
295
|
+
|
|
296
|
+
### Localized strings
|
|
297
|
+
```yaml
|
|
298
|
+
displayName:
|
|
299
|
+
en-US: "English text"
|
|
300
|
+
es-ES: "Spanish text"
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Template expressions
|
|
304
|
+
```yaml
|
|
305
|
+
value: "{{ fieldName }}" # Simple variable
|
|
306
|
+
value: "{{ format date L }}" # Format helper
|
|
307
|
+
value: "{{ number quantity }}" # Type cast
|
|
308
|
+
value: "{{ eval items.length > 0 }}" # JavaScript expression
|
|
309
|
+
isHidden: "{{ eval !canEdit }}" # Conditional visibility
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Permissions
|
|
313
|
+
```yaml
|
|
314
|
+
permission: "ModuleName/Read" # Single string (PascalCase/Action)
|
|
315
|
+
permissions: # Array
|
|
316
|
+
- "ModuleName/Read"
|
|
317
|
+
- "ModuleName/Update"
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Async Select Component Pattern
|
|
321
|
+
|
|
322
|
+
Reusable select components (e.g., `Countries/Select`, `Ports/Select`) follow this structure:
|
|
323
|
+
|
|
324
|
+
```yaml
|
|
325
|
+
- name: Entity/Select
|
|
326
|
+
displayName: { en-US: "Select Entity" }
|
|
327
|
+
platforms: [web, mobile]
|
|
328
|
+
layout:
|
|
329
|
+
component: field
|
|
330
|
+
name: entityId # Value binding field name
|
|
331
|
+
props:
|
|
332
|
+
type: select-async
|
|
333
|
+
label: { en-US: "Entity" }
|
|
334
|
+
options:
|
|
335
|
+
valueFieldName: "entityId" # Which result field holds the value
|
|
336
|
+
itemLabelTemplate: "{{name}}" # Handlebars template for labels
|
|
337
|
+
itemValueTemplate: "{{entityId}}" # Handlebars template for values
|
|
338
|
+
navigateActionPermission: "Entity/Update"
|
|
339
|
+
searchQuery: # References list query below
|
|
340
|
+
name: getEntities
|
|
341
|
+
path: entities.items
|
|
342
|
+
params:
|
|
343
|
+
search: "{{ string search }}"
|
|
344
|
+
take: "{{ number pageSize }}"
|
|
345
|
+
skip: "{{ number skip }}"
|
|
346
|
+
filter: "{{ string filter }}"
|
|
347
|
+
valueQuery: # References single-item query below
|
|
348
|
+
name: getEntity
|
|
349
|
+
path: entity
|
|
350
|
+
params:
|
|
351
|
+
entityId: "{{entityId}}"
|
|
352
|
+
allowSearch: true
|
|
353
|
+
allowClear: true
|
|
354
|
+
dropDownToolbar: # Create button in dropdown
|
|
355
|
+
- component: button
|
|
356
|
+
name: createBtn
|
|
357
|
+
props:
|
|
358
|
+
label: { en-US: "Create Entity" }
|
|
359
|
+
icon: plus
|
|
360
|
+
onClick:
|
|
361
|
+
- dialog:
|
|
362
|
+
component: Entity/CreateEntity
|
|
363
|
+
onClose:
|
|
364
|
+
- selectValue: "{{ result.entityId }}"
|
|
365
|
+
queries:
|
|
366
|
+
- name: getEntities # Paginated search query
|
|
367
|
+
query:
|
|
368
|
+
command: >-
|
|
369
|
+
query($organizationId: Int!, $filter: String!, $search: String!, $take: Int!, $skip: Int!) {
|
|
370
|
+
entities(...) { items { entityId name } totalCount }
|
|
371
|
+
}
|
|
372
|
+
variables: { organizationId: "{{number organizationId}}", ... }
|
|
373
|
+
- name: getEntity # Single-value lookup query
|
|
374
|
+
query:
|
|
375
|
+
command: >-
|
|
376
|
+
query($organizationId: Int!, $entityId: Int!) {
|
|
377
|
+
entity(...) { entityId name }
|
|
378
|
+
}
|
|
379
|
+
variables: { entityId: "{{number entityId}}" }
|
|
380
|
+
onEditClick: # Edit action on selected item
|
|
381
|
+
- dialog:
|
|
382
|
+
component: { layout: { component: layout, children: [{ component: Entity/UpdateEntity }] } }
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
# Generation Rules
|
|
388
|
+
|
|
389
|
+
1. **Always scaffold via `cx-cli create module` first** — never write YAML from scratch, never copy templates manually
|
|
390
|
+
2. **Use localized strings** `{ en-US: "..." }` for all user-visible text
|
|
391
|
+
3. **Follow naming conventions**:
|
|
392
|
+
- Module names: PascalCase (e.g., `WarehouseLocations`)
|
|
393
|
+
- Component names: Module/Component pattern (e.g., `WarehouseLocations/List`)
|
|
394
|
+
- Route paths: kebab-case (e.g., `/warehouse-locations`)
|
|
395
|
+
- Permission names: PascalCase with slashes (e.g., `WarehouseLocations/Read`, `System/Contacts/Update`)
|
|
396
|
+
4. **Template expressions** use `{{ expression }}` syntax (double curly braces)
|
|
397
|
+
5. **Include fileName** property pointing to the YAML file location
|
|
398
|
+
6. **Set proper entityKind** when defining entities (Order, Contact, OrderEntity, AccountingTransaction, Calendar, CalendarEvent, Other)
|
|
399
|
+
7. **DataGrid options** requires ALL properties: query, rootEntityName, entityKeys, navigationType, enableDynamicGrid, enableViews, enableSearch, enablePagination, enableColumns, enableFilter, defaultView, onRowClick
|
|
400
|
+
8. **Form component** requires `validationSchema` in props
|
|
401
|
+
9. **Do not change `appModuleId` or `fileName`** — set correctly by CLI scaffold
|
|
402
|
+
10. **Always validate** the final YAML: `npx cx-cli <file.yaml>`
|