@jigx/core-sdk 1.0.0 → 1.2.0-rc

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 (41) hide show
  1. package/README.md +2 -0
  2. package/dist/action/ja.generate-pdf.d.ts +17 -1
  3. package/dist/action/ja.generate-pdf.d.ts.map +1 -1
  4. package/dist/action/ja.generate-pdf.js +4 -1
  5. package/dist/action/ja.in-background.d.ts +3 -2
  6. package/dist/action/ja.in-background.d.ts.map +1 -1
  7. package/dist/action/ja.in-background.js +1 -1
  8. package/dist/assets/example-extraction-cache.json +3 -3
  9. package/dist/assets/extracted-core-sdk-examples.yaml +18 -0
  10. package/dist/assets/extracted-core-sdk-types.yaml +58 -0
  11. package/dist/assets/type-extraction-cache.json +3 -3
  12. package/docs/array-fields.md +371 -0
  13. package/docs/conditional-logic.md +178 -0
  14. package/docs/convention-naming.md +102 -0
  15. package/docs/date-field.md +92 -0
  16. package/docs/dropdown-fields.md +879 -0
  17. package/docs/field-state.md +131 -0
  18. package/docs/field-types-overview.md +132 -0
  19. package/docs/formatting.md +421 -0
  20. package/docs/icons.md +142 -0
  21. package/docs/index.md +23 -0
  22. package/docs/jsonata-expressions.md +200 -0
  23. package/docs/media-fields.md +107 -0
  24. package/docs/overview.md +467 -0
  25. package/docs/pattern-build-deploy.md +91 -0
  26. package/docs/pattern-datasources.md +459 -0
  27. package/docs/pattern-forms.md +528 -0
  28. package/docs/pattern-global-actions.md +92 -0
  29. package/docs/pattern-javascript-functions.md +452 -0
  30. package/docs/pattern-navigation.md +304 -0
  31. package/docs/pattern-pdf-generation.md +391 -0
  32. package/docs/pattern-rest-acumatica.md +660 -0
  33. package/docs/pattern-sync-progress.md +96 -0
  34. package/docs/pattern-sync.md +653 -0
  35. package/docs/pattern-tabs-form.md +293 -0
  36. package/docs/recipe-index.md +64 -0
  37. package/docs/runtime-variables.md +127 -0
  38. package/docs/sections.md +81 -0
  39. package/docs/validation-patterns.md +150 -0
  40. package/package.json +5 -4
  41. package/CHANGELOG.md +0 -95
package/docs/icons.md ADDED
@@ -0,0 +1,142 @@
1
+ ## Searching Icons
2
+
3
+ Use MCP to find valid icon names:
4
+
5
+ ```typescript
6
+ mcp__jigx_common__search_sdk_icons({
7
+ queries: [{ query: "money dollar payment", category: "business-products" }]
8
+ })
9
+ ```
10
+
11
+ ### Search Tips
12
+
13
+ - Use 3-5 descriptive keywords
14
+ - Include category for better results
15
+ - Bad: "vintage" → vintage-tv
16
+ - Good: "vintage car classic" → vintage-car
17
+
18
+ ### Common Categories
19
+
20
+ | Category | Contains |
21
+ | --- | --- |
22
+ | `interface-essential` | UI elements, controls |
23
+ | `users` | People, profiles, teams |
24
+ | `business-products` | Commerce, finance, products |
25
+ | `technology` | Devices, tech symbols |
26
+ | `logos` | Brand logos |
27
+ | `flags` | Country flags |
28
+ | `transportation` | Vehicles, travel |
29
+ | `maps-navigation` | Location, directions |
30
+
31
+ ## Using Icons
32
+
33
+ ### Field Icons
34
+
35
+ Pass `icon` in constructor or use `.with()`:
36
+
37
+ ```typescript
38
+ step.addEmail({
39
+ name: 'email',
40
+ label: 'Email',
41
+ icon: 'send-email-envelope',
42
+ })
43
+ step.addPhone({
44
+ name: 'phone',
45
+ label: 'Phone',
46
+ icon: 'phone',
47
+ })
48
+ ```
49
+
50
+ ### Step Icons
51
+
52
+ ```typescript
53
+ form.addStep(
54
+ {
55
+ instanceId: 'contact',
56
+ title: 'Contact',
57
+ icon: 'single-neutral',
58
+ },
59
+ (step) => {
60
+ step.addText({ name: 'name', label: 'Name' })
61
+ },
62
+ )
63
+ ```
64
+
65
+ ### Icons in Dropdown/Choice Options
66
+
67
+ ```typescript
68
+ step.addDropdown({
69
+ name: 'priority',
70
+ label: 'Priority',
71
+ data: [
72
+ { label: 'High', value: 'high', icon: 'alert-triangle' },
73
+ { label: 'Medium', value: 'medium', icon: 'alert-circle' },
74
+ { label: 'Low', value: 'low', icon: 'information-circle' },
75
+ ],
76
+ })
77
+ ```
78
+
79
+ ## Updating Icons with .with()
80
+
81
+ ```typescript
82
+ const field = step.addText({ name: 'status', label: 'Status' })
83
+ // Update icon after construction
84
+ field.with({ icon: 'check-circle-1' })
85
+ ```
86
+
87
+ ## Validation
88
+
89
+ Icons are validated at build time:
90
+
91
+ ```typescript
92
+ // Valid - builds successfully
93
+ const step = form.addStep({ instanceId: 'billing', icon: 'currency-dollar' })
94
+
95
+ // Invalid - build fails
96
+ const step = form.addStep({ instanceId: 'billing', icon: 'made-up-icon-123' }) // Error: Invalid icon name
97
+ ```
98
+
99
+ ## Common Icons by Use Case
100
+
101
+ | Use Case | Search Query | Example Icons |
102
+ | --- | --- | --- |
103
+ | Contact | "user person profile" | `user`, `user-profile-stacking` |
104
+ | Email | "mail envelope" | `mail`, `envelope` |
105
+ | Phone | "phone call" | `phone`, `phone-call` |
106
+ | Money | "money dollar currency" | `currency-dollar`, `money-bag` |
107
+ | Location | "location pin map" | `location`, `pin`, `map-marker` |
108
+ | Calendar | "calendar date schedule" | `calendar`, `calendar-check` |
109
+ | Settings | "settings gear cog" | `settings`, `cog`, `gear` |
110
+ | Search | "search magnify" | `search`, `magnifying-glass` |
111
+ | Add | "add plus create" | `add`, `plus`, `plus-circle` |
112
+ | Delete | "delete trash remove" | `trash`, `delete`, `x-circle` |
113
+ | Edit | "edit pencil write" | `pencil`, `edit`, `pen` |
114
+ | Save | "save disk check" | `save`, `floppy-disk`, `check` |
115
+ | Warning | "warning alert triangle" | `alert-triangle`, `warning` |
116
+ | Info | "info information circle" | `information-circle`, `info` |
117
+ | Success | "success check complete" | `check-circle`, `check` |
118
+ | Error | "error x close" | `x-circle`, `x`, `close` |
119
+
120
+ ## Dynamic Icons
121
+
122
+ Icons support `JsonataBuilder` for conditional values:
123
+
124
+ ```typescript
125
+ const statusField = step.addDropdown({
126
+ name: 'status',
127
+ label: 'Status',
128
+ data: [
129
+ { label: 'Active', value: 'active' },
130
+ { label: 'Inactive', value: 'inactive' },
131
+ ],
132
+ })
133
+ step.addText({
134
+ name: 'statusDisplay',
135
+ label: 'Status Display',
136
+ icon: new IconBuilder(
137
+ new JsonataBuilder('$status = "active" ? "check-circle-1" : "x-circle"', {
138
+ status: statusField.state.value,
139
+ }),
140
+ ),
141
+ })
142
+ ```
package/docs/index.md ADDED
@@ -0,0 +1,23 @@
1
+ # Expert SDK Documentation
2
+
3
+ Available documentation resources for Expert SDK forms and wizards.
4
+
5
+ | Resource | Description |
6
+ | --- | --- |
7
+ expressions |
8
+ | [JSONata Expressions](./jsonata-expressions.md) | JSONata expression language for dynamic values |
9
+ | [Conditional Logic](./conditional-logic.md) | Show/hide and require fields based on other field values using JsonataBuilder |
10
+ | [Validation Patterns](./validation-patterns.md) | Custom field validation using errorText with JsonataBuilder expressions |
11
+ | [Sections](./sections.md) | Group related fields visually within steps using addSection() |
12
+ | [Dropdown Fields](./dropdown-fields.md) | Single/multi-select dropdowns with static, dynamic, and arrayField data sources |
13
+ | [Choice Fields](./choice-fields.md) | Radio buttons and chips with grid layout for small option sets |
14
+ | [Array Fields](./array-fields.md) | Nested repeating forms for collecting multiple items (invoice line items, team members, checklist tasks) |
15
+ | [Array Initialization](./predefined-array-items.md) | Static arrays (subforms, fixed lists) and dynamic arrays from related fields using ArrayField value property |
16
+ | [Date Field](./date-field.md) | Date, time, and datetime picker fields with mode, minimum, and maximum constraints |
17
+ | [Formatting](./formatting.md) | Number, currency, date, and unit formatting via format property and FormatBuilder class (ECMA-402 standard) |
18
+ | [Media Fields](./media-fields.md) | File upload, signature capture, and avatar selection fields |
19
+ | [Location Field](./location-field.md) | GPS coordinate capture with auto-fill and editable modes |
20
+ | [Icons](./icons.md) | Search and use SDK icons in constructors with validation at build time |
21
+ | [Runtime Variables](./runtime-variables.md) | System and Auth.user variables for device info and authenticated user data in JsonataBuilder |
22
+ | [Internationalization](./i18n.md) | Multi-language support with I18nBuilder class, JsonataBuilder interpolation, and FormatBuilder for currency/date/number display |
23
+ | [Submission Display](./submission-display.md) | Configure submission title and subtitle with JsonataBuilder or I18nBuilder using step.data.getFieldData() |
@@ -0,0 +1,200 @@
1
+ ## Static Values
2
+
3
+ Pass values directly in constructor or via `.with()`:
4
+
5
+ ```typescript
6
+ step.addEmail({
7
+ name: 'email',
8
+ label: 'Email',
9
+ helperText: 'We will never share your email',
10
+ isRequired: true,
11
+ icon: 'send-email-envelope',
12
+ })
13
+ ```
14
+
15
+ **When to use:**
16
+
17
+ - Fixed labels, titles, descriptions
18
+ - Boolean flags (true/false)
19
+ - No dependencies on other data
20
+
21
+ ## Dynamic Expressions with JsonataBuilder
22
+
23
+ Use `new JsonataBuilder()` when value depends on other fields or data:
24
+
25
+ ```typescript
26
+ const age = step.addNumber({ name: 'age', label: 'Age' })
27
+ step.addCheckbox({
28
+ name: 'consent',
29
+ label: 'I agree to terms',
30
+ isVisible: new JsonataBuilder('$age >= 18', { age: age.state.value }),
31
+ })
32
+ ```
33
+
34
+ ## JsonataBuilder Pattern
35
+
36
+ Constructor: `new JsonataBuilder(body, variables)`
37
+ - **body**: JSONata expression using `$` prefix for variables
38
+ - **variables**: Object mapping variable names to references
39
+
40
+ ```typescript
41
+ const statusField = step.addDropdown({
42
+ name: 'status',
43
+ label: 'Status',
44
+ data: [
45
+ { label: 'Employed', value: 'employed' },
46
+ { label: 'Unemployed', value: 'unemployed' },
47
+ ],
48
+ })
49
+ step.addText({
50
+ name: 'company',
51
+ label: 'Company',
52
+ isRequired: new JsonataBuilder('$status = "employed"', { status: statusField.state.value }),
53
+ })
54
+ ```
55
+
56
+ ## Multiple Variables
57
+
58
+ ```typescript
59
+ const age = step.addNumber({ name: 'age', label: 'Age' })
60
+ const country = step.addDropdown({
61
+ name: 'country',
62
+ label: 'Country',
63
+ data: [
64
+ { label: 'USA', value: 'usa' },
65
+ { label: 'Canada', value: 'canada' },
66
+ ],
67
+ })
68
+ step.addCheckbox({
69
+ name: 'consent',
70
+ label: 'I agree',
71
+ isRequired: new JsonataBuilder('$age >= 18 and $country = "usa"', {
72
+ age: age.state.value,
73
+ country: country.state.value,
74
+ }),
75
+ })
76
+ ```
77
+
78
+ ## Variable Sources
79
+
80
+ | Source | Use Case | Example |
81
+ | --- | --- | --- |
82
+ | `field.state.value` | Same-step field value | `age.state.value` |
83
+ | `step.data.getFieldData()` | Cross-step field value | `step1.data.getFieldData('status')` |
84
+ | `Auth.user.*` | Authenticated user data | `Auth.user.displayName` |
85
+ | `System.*` | Device/environment data | `System.isOnline` |
86
+
87
+ ```typescript
88
+ // User data pre-fill
89
+ step.addText({
90
+ name: 'name',
91
+ label: 'Name',
92
+ value: new JsonataBuilder('$userName', { userName: Auth.user.displayName }),
93
+ })
94
+ // System data in helper text
95
+ step.addText({
96
+ name: 'status',
97
+ label: 'Status',
98
+ helperText: new JsonataBuilder('$online ? "Connected" : "Offline mode"', {
99
+ online: System.isOnline,
100
+ }),
101
+ })
102
+ ```
103
+
104
+ ## String Concatenation
105
+
106
+ Use `&` (not `+`) for string concatenation:
107
+
108
+ ```typescript
109
+ const step1 = form.addStep({ instanceId: 'name' }, (step) => {
110
+ step.addText({ name: 'firstName', label: 'First Name' })
111
+ step.addText({ name: 'lastName', label: 'Last Name' })
112
+ })
113
+ step1.with({ icon: 'monitor-user', title: 'Name' })
114
+ form.with({
115
+ submissionItemTitle: new JsonataBuilder('$firstName & " " & $lastName', {
116
+ firstName: step1.data.getFieldData('firstName'),
117
+ lastName: step1.data.getFieldData('lastName'),
118
+ }),
119
+ })
120
+ ```
121
+
122
+ ## Ternary Conditionals
123
+
124
+ ```typescript
125
+ const statusField = step.addDropdown({
126
+ name: 'status',
127
+ label: 'Status',
128
+ data: [
129
+ { label: 'Active', value: 'active' },
130
+ { label: 'Inactive', value: 'inactive' },
131
+ ],
132
+ })
133
+ step.addText({
134
+ name: 'message',
135
+ label: 'Message',
136
+ helperText: new JsonataBuilder(
137
+ '$status = "active" ? "Account is active" : "Account is inactive"',
138
+ { status: statusField.state.value },
139
+ ),
140
+ })
141
+ ```
142
+
143
+ ## Multi-Line Expressions
144
+
145
+ For complex calculations:
146
+
147
+ ```typescript
148
+ const step1 = form.addStep({ instanceId: 'products' }, (step) => {
149
+ step.addNumber({ name: 'quantity', label: 'Quantity' })
150
+ step.addNumber({ name: 'price', label: 'Price' })
151
+ })
152
+ step1.with({ icon: 'shopping-cart', title: 'Products' })
153
+ form.addStep({ instanceId: 'summary', icon: 'summary-organize' }, (step) => {
154
+ step.addText({
155
+ name: 'total',
156
+ label: 'Total',
157
+ value: new JsonataBuilder(
158
+ `
159
+ $subtotal := $qty * $price;
160
+ $tax := $subtotal * 0.1;
161
+ $total := $subtotal + $tax;
162
+ "$" & $formatNumber($total, "#,##0.00")
163
+ `,
164
+ {
165
+ qty: step1.data.getFieldData('quantity'),
166
+ price: step1.data.getFieldData('price'),
167
+ },
168
+ ),
169
+ isDisabled: true,
170
+ })
171
+ })
172
+ ```
173
+
174
+ ## Updating Properties with .with()
175
+
176
+ Use `.with()` to change properties after construction:
177
+
178
+ ```typescript
179
+ const toggleField = step.addCheckbox({ name: 'showNotes', label: 'Show Notes' })
180
+ const field = step.addText({ name: 'notes', label: 'Notes' })
181
+ // Update multiple properties
182
+ field.with({
183
+ helperText: 'Additional details',
184
+ isVisible: new JsonataBuilder('$show = true', { show: toggleField.state.value }),
185
+ })
186
+ ```
187
+
188
+ ## Common Mistakes
189
+
190
+ **Wrong - == for equality:**
191
+
192
+ ```typescript
193
+ new JsonataBuilder('$status == "active"', ...) // Wrong
194
+ ```
195
+
196
+ **Correct - single =:**
197
+
198
+ ```typescript
199
+ new JsonataBuilder('$status = "active"', ...) // Correct
200
+ ```
@@ -0,0 +1,107 @@
1
+ ## Media Upload
2
+
3
+ ```typescript
4
+ step.addMedia({
5
+ name: 'documents',
6
+ label: 'Upload Documents',
7
+ isMultiple: true, // Enable multi-select
8
+ })
9
+ // Single file media field for comparison (default behavior)
10
+ step.addMedia({
11
+ name: 'profile-photo',
12
+ label: 'Profile Photo',
13
+ })
14
+ ```
15
+
16
+ **Properties:**
17
+
18
+ - `isMultiple: true` - Enable multiple file selection
19
+ - Default: Single file upload
20
+
21
+ ## Local photos for offline sync
22
+
23
+ For local-first Acumatica apps, media fields and picked files should usually be stored in a
24
+ separate local child table rather than embedded directly into the parent record.
25
+
26
+ Recommended shape per file row:
27
+
28
+ - `id`
29
+ - `parentId` — stable local parent row id
30
+ - `recordId` — remote parent id / NoteID target used by the upload URL
31
+ - `fileUrl` — local device URI
32
+ - `filename`
33
+ - `contentType`
34
+ - `Remote`
35
+
36
+ Identity rule:
37
+
38
+ - keep `parentId` as the stable local parent row id
39
+ - keep any remote upload target in a separate field such as `recordId`
40
+ - generate each media child row id locally at creation time with `$uuid()`
41
+ - do not repurpose a child screen `@ctx.jig.instanceId` as the media row id if the media flow is part of a parent aggregate
42
+
43
+ Use `openMediaPicker` when you need direct camera/photo-library capture:
44
+
45
+ ```typescript
46
+ const actions = screen.bottomPanel().add.list({
47
+ title: 'Add Photos',
48
+ icon: 'camera',
49
+ concurrency: 'sequential',
50
+ })
51
+
52
+ actions.actions.openMediaPicker({
53
+ instanceId: 'photo-picker',
54
+ mediaType: 'image',
55
+ isMultiple: true,
56
+ imageQuality: 50,
57
+ })
58
+
59
+ actions.actions
60
+ .executeEntities({ instanceId: 'save-photos' })
61
+ .local('ServiceOrderPhotos', 'save')
62
+ .data(`=$map(@ctx.actions.photo-picker.outputs.newItems, function($f){
63
+ {
64
+ "id": $uuid(),
65
+ "parentId": @ctx.jig.inputs.serviceOrderId,
66
+ "fileUrl": $f.uri,
67
+ "filename": $f.fileName,
68
+ "Remote": "new"
69
+ }
70
+ })`)
71
+ ```
72
+
73
+ Notes:
74
+
75
+ - `mediaType: 'image'` gives the native image flow, including camera/photo-library options
76
+ - omit `imageCropping` when cropping should be disabled
77
+ - use `imageQuality` deliberately for mobile upload size control
78
+ - keep remote upload logic separate; picker/save is local only
79
+
80
+ ## Signature Capture
81
+
82
+ ```typescript
83
+ step.addSignature({
84
+ name: 'agreementSignature',
85
+ label: 'Your Signature',
86
+ helperText: 'Please sign using your finger or stylus',
87
+ })
88
+ ```
89
+
90
+ ## Avatar Field
91
+
92
+ For profile picture selection:
93
+
94
+ ```typescript
95
+ step.addAvatar({ name: 'avatar', label: 'Profile Picture' })
96
+ ```
97
+
98
+ ## Duration Picker
99
+
100
+ For time duration input:
101
+
102
+ ```typescript
103
+ step.addDurationPicker({
104
+ name: 'workDuration',
105
+ label: 'Work Duration',
106
+ })
107
+ ```