@jigx/core-sdk 1.1.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.
- package/dist/action/ja.generate-pdf.d.ts +17 -1
- package/dist/action/ja.generate-pdf.d.ts.map +1 -1
- package/dist/action/ja.generate-pdf.js +4 -1
- package/dist/action/ja.in-background.d.ts +3 -2
- package/dist/action/ja.in-background.d.ts.map +1 -1
- package/dist/action/ja.in-background.js +1 -1
- package/dist/assets/example-extraction-cache.json +3 -3
- package/dist/assets/extracted-core-sdk-examples.yaml +18 -0
- package/dist/assets/extracted-core-sdk-types.yaml +58 -0
- package/dist/assets/type-extraction-cache.json +3 -3
- package/docs/array-fields.md +371 -0
- package/docs/conditional-logic.md +178 -0
- package/docs/convention-naming.md +102 -0
- package/docs/date-field.md +92 -0
- package/docs/dropdown-fields.md +879 -0
- package/docs/field-state.md +131 -0
- package/docs/field-types-overview.md +132 -0
- package/docs/formatting.md +421 -0
- package/docs/icons.md +142 -0
- package/docs/index.md +23 -0
- package/docs/jsonata-expressions.md +200 -0
- package/docs/media-fields.md +107 -0
- package/docs/overview.md +467 -0
- package/docs/pattern-build-deploy.md +91 -0
- package/docs/pattern-datasources.md +459 -0
- package/docs/pattern-forms.md +528 -0
- package/docs/pattern-global-actions.md +92 -0
- package/docs/pattern-javascript-functions.md +452 -0
- package/docs/pattern-navigation.md +304 -0
- package/docs/pattern-pdf-generation.md +391 -0
- package/docs/pattern-rest-acumatica.md +660 -0
- package/docs/pattern-sync-progress.md +96 -0
- package/docs/pattern-sync.md +653 -0
- package/docs/pattern-tabs-form.md +293 -0
- package/docs/recipe-index.md +64 -0
- package/docs/runtime-variables.md +127 -0
- package/docs/sections.md +81 -0
- package/docs/validation-patterns.md +150 -0
- package/package.json +3 -2
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
|
+
```
|