@proteos/sdk 0.46.0 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-F5DHWOGW.cjs → chunk-OD7GPJAW.cjs} +50 -15
- package/dist/chunk-OD7GPJAW.cjs.map +1 -0
- package/dist/{chunk-Y4CEHIRX.js → chunk-TDG5QFZ5.js} +50 -15
- package/dist/chunk-TDG5QFZ5.js.map +1 -0
- package/dist/index.cjs +144 -99
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +144 -15
- package/dist/index.d.ts +144 -15
- package/dist/index.js +50 -5
- package/dist/index.js.map +1 -1
- package/dist/meta/index.cjs +57 -57
- package/dist/meta/index.d.cts +1 -1
- package/dist/meta/index.d.ts +1 -1
- package/dist/meta/index.js +1 -1
- package/dist/{types-D1PRIsSO.d.cts → types-COxVBT4w.d.cts} +249 -64
- package/dist/{types-D1PRIsSO.d.ts → types-COxVBT4w.d.ts} +249 -64
- package/package.json +1 -1
- package/src/conversation/index.ts +87 -3
- package/src/conversation/types.ts +103 -1
- package/src/data/types.ts +9 -0
- package/src/functions/actions.ts +29 -0
- package/src/functions/types.ts +6 -4
- package/src/index.ts +8 -0
- package/src/meta/layout/control-registry.json +4 -2
- package/src/meta/layout/elements.ts +130 -0
- package/src/meta/layout/index.ts +3 -0
- package/src/meta/types.ts +85 -39
- package/dist/chunk-F5DHWOGW.cjs.map +0 -1
- package/dist/chunk-Y4CEHIRX.js.map +0 -1
|
@@ -26,6 +26,7 @@ export type Channel =
|
|
|
26
26
|
| 'teams-meeting'
|
|
27
27
|
| 'webex-meeting'
|
|
28
28
|
| 'adhoc'
|
|
29
|
+
| 'phone'
|
|
29
30
|
| 'instagram'
|
|
30
31
|
| 'messenger'
|
|
31
32
|
| 'x'
|
|
@@ -52,6 +53,83 @@ export type ConnectorKey =
|
|
|
52
53
|
| 'adhoc-meeting'
|
|
53
54
|
| 'google-calendar-meeting'
|
|
54
55
|
| 'microsoft-calendar-meeting'
|
|
56
|
+
| 'twilio-phone'
|
|
57
|
+
| 'aircall'
|
|
58
|
+
| 'webhook-cti'
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Telephony lifecycle of one phone call, carried in the call conversation's
|
|
62
|
+
* `metadata.call_status` (a call is a conversation plus call facts — no
|
|
63
|
+
* dedicated resource). Status callbacks apply through a monotonic guard, so a
|
|
64
|
+
* terminal value never regresses.
|
|
65
|
+
*/
|
|
66
|
+
/**
|
|
67
|
+
* Body of POST /conversations/v1/calls/token — the softphone's browser-calling
|
|
68
|
+
* grant for one connection. The client identity is derived server-side from
|
|
69
|
+
* the authenticated user.
|
|
70
|
+
*/
|
|
71
|
+
export interface MintCallTokenRequest {
|
|
72
|
+
connection_id: string
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Which system answers a provider phone number: connected to this platform,
|
|
77
|
+
* in use by another integration (connecting takes it over, disconnecting
|
|
78
|
+
* restores it), or configured nowhere (available).
|
|
79
|
+
*/
|
|
80
|
+
export type PhoneNumberStatus = 'connected' | 'available' | 'in_use'
|
|
81
|
+
|
|
82
|
+
/** One dispatch target: a platform user (id) or a team (slug). */
|
|
83
|
+
export interface RoutingTarget {
|
|
84
|
+
type: 'user' | 'team'
|
|
85
|
+
id: string
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* A connected number's user-level dispatch: who a call rings. v1 rings every
|
|
90
|
+
* expanded target in parallel (teams expand to their direct members at ring
|
|
91
|
+
* time); the object shape leaves room for later strategy options. Empty
|
|
92
|
+
* targets fall back to the connection-wide routing, then busy.
|
|
93
|
+
*/
|
|
94
|
+
export interface PhoneNumberRouting {
|
|
95
|
+
targets: RoutingTarget[]
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** One provider phone number on a phone connection. */
|
|
99
|
+
export interface PhoneNumber {
|
|
100
|
+
/** Provider-side number identity (vendor vocabulary stays in the adapter). */
|
|
101
|
+
external_id: string
|
|
102
|
+
phone_number: string
|
|
103
|
+
friendly_name: string
|
|
104
|
+
status: PhoneNumberStatus
|
|
105
|
+
/** Display-only description of what an in_use number is used by. */
|
|
106
|
+
used_by?: string
|
|
107
|
+
routing: PhoneNumberRouting
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Routing update for one phone number (connect/disconnect are actions). */
|
|
111
|
+
export interface UpdatePhoneNumberRequest {
|
|
112
|
+
routing?: PhoneNumberRouting
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** The minted grant the Voice JS SDK consumes (`new Device(token, { edge })`). */
|
|
116
|
+
export interface CallTokenResponse {
|
|
117
|
+
token: string
|
|
118
|
+
identity: string
|
|
119
|
+
/** Vendor edge to connect through ('dublin' for the EU default). */
|
|
120
|
+
edge?: string
|
|
121
|
+
expires_at: string
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export type CallStatus =
|
|
125
|
+
| 'queued'
|
|
126
|
+
| 'ringing'
|
|
127
|
+
| 'in_progress'
|
|
128
|
+
| 'completed'
|
|
129
|
+
| 'no_answer'
|
|
130
|
+
| 'busy'
|
|
131
|
+
| 'failed'
|
|
132
|
+
| 'canceled'
|
|
55
133
|
|
|
56
134
|
/**
|
|
57
135
|
* Who operates the integration mechanics behind a connector: Proteos' own
|
|
@@ -486,8 +564,27 @@ export interface UpdateConnectionRequest {
|
|
|
486
564
|
settings?: Record<string, unknown>
|
|
487
565
|
}
|
|
488
566
|
|
|
567
|
+
/**
|
|
568
|
+
* Optional body of POST /connections/:id/install. `input` carries
|
|
569
|
+
* user-supplied install credentials for direct-install connectors
|
|
570
|
+
* (twilio-phone, aircall, webhook-cti — connector-documented snake_case
|
|
571
|
+
* keys); omit it for OAuth/hosted connectors.
|
|
572
|
+
*/
|
|
573
|
+
export interface InstallConnectionRequest {
|
|
574
|
+
input?: Record<string, string>
|
|
575
|
+
}
|
|
576
|
+
|
|
489
577
|
export interface InstallConnectionResponse {
|
|
490
|
-
|
|
578
|
+
/** OAuth/hosted installs: open this in a popup. Absent on direct installs. */
|
|
579
|
+
authorization_url?: string
|
|
580
|
+
/** Direct installs complete server-side in this request. */
|
|
581
|
+
is_completed?: boolean
|
|
582
|
+
/**
|
|
583
|
+
* A direct-install connector's one-time setup handout (callback URLs to
|
|
584
|
+
* paste provider-side; a minted token shown exactly once — only its hash is
|
|
585
|
+
* stored server-side). Never retrievable again.
|
|
586
|
+
*/
|
|
587
|
+
setup?: Record<string, string>
|
|
491
588
|
}
|
|
492
589
|
|
|
493
590
|
export interface SendMessageRequest {
|
|
@@ -852,6 +949,11 @@ export interface ListConnectionsQuery extends PaginationQuery {
|
|
|
852
949
|
connector_key?: string
|
|
853
950
|
channel?: string
|
|
854
951
|
scope?: string
|
|
952
|
+
/**
|
|
953
|
+
* Narrows to the user-scoped connections one person owns. Org-scoped
|
|
954
|
+
* connections have no owner and never match.
|
|
955
|
+
*/
|
|
956
|
+
owner_id?: string
|
|
855
957
|
status?: string
|
|
856
958
|
}
|
|
857
959
|
|
package/src/data/types.ts
CHANGED
|
@@ -18,6 +18,15 @@ export type RecordData = Record<string, any>
|
|
|
18
18
|
* (via bracket syntax) include `[eq]`, `[ne]`, `[gt]`, `[gte]`, `[lt]`,
|
|
19
19
|
* `[lte]`, `[in]` (pipe-separated), `[not_in]`, `[contains]`, `[starts_with]`,
|
|
20
20
|
* `[ends_with]`, `[empty]`, `[not_empty]`. Missing operator defaults to `[eq]`.
|
|
21
|
+
* A filter key may reach one hop through a relation attribute with a dotted
|
|
22
|
+
* path (`"company_id.name[contains]": "acme"`).
|
|
23
|
+
*
|
|
24
|
+
* For nested AND/OR trees, pass a filter group (the same wire dialect as
|
|
25
|
+
* `List.filters` / `visible_when`: `{logical_operator, elements: [{field,
|
|
26
|
+
* operator, value}], groups}`, string values, pipe-joined for in/not_in)
|
|
27
|
+
* JSON-encoded under the reserved `_filter` key:
|
|
28
|
+
* `{ _filter: JSON.stringify(group) }`. The server ANDs it with any flat
|
|
29
|
+
* filter params.
|
|
21
30
|
*/
|
|
22
31
|
export interface ListRecordsOptions {
|
|
23
32
|
/** Page number (0-indexed). First page is 0. */
|
package/src/functions/actions.ts
CHANGED
|
@@ -61,6 +61,19 @@ export interface ActionService {
|
|
|
61
61
|
slug: string,
|
|
62
62
|
params: Record<string, unknown>,
|
|
63
63
|
): Promise<unknown>
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Invokes an `entity_batch` action against a SET of records. The whole id
|
|
67
|
+
* array reaches the action in ONE invocation, so the result is a single
|
|
68
|
+
* aggregate payload, not one per record. `recordIds` must be non-empty;
|
|
69
|
+
* the server dedupes it, preserving order. Returns the unwrapped `result`.
|
|
70
|
+
*/
|
|
71
|
+
invokeBatch(
|
|
72
|
+
entitySlug: string,
|
|
73
|
+
slug: string,
|
|
74
|
+
recordIds: string[],
|
|
75
|
+
params: Record<string, unknown>,
|
|
76
|
+
): Promise<unknown>
|
|
64
77
|
}
|
|
65
78
|
|
|
66
79
|
/**
|
|
@@ -117,4 +130,20 @@ export class ActionServiceImpl implements ActionService {
|
|
|
117
130
|
)
|
|
118
131
|
return response.result
|
|
119
132
|
}
|
|
133
|
+
|
|
134
|
+
async invokeBatch(
|
|
135
|
+
entitySlug: string,
|
|
136
|
+
slug: string,
|
|
137
|
+
recordIds: string[],
|
|
138
|
+
params: Record<string, unknown>,
|
|
139
|
+
): Promise<unknown> {
|
|
140
|
+
// The one invoke route whose body is an envelope rather than the params
|
|
141
|
+
// object itself — an id array has nowhere to live in the path.
|
|
142
|
+
const response = await this.client.request<InvokeActionResponse>(
|
|
143
|
+
'POST',
|
|
144
|
+
`/functions/v1/entities/${entitySlug}/actions/${slug}/invoke`,
|
|
145
|
+
{ record_ids: recordIds, params },
|
|
146
|
+
)
|
|
147
|
+
return response.result
|
|
148
|
+
}
|
|
120
149
|
}
|
package/src/functions/types.ts
CHANGED
|
@@ -5,11 +5,13 @@ import { type AuditFields, AuditFieldsSchema } from '../types/common.js'
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Scope of an action. `entity`-scoped actions are invoked against a
|
|
8
|
-
* specific record; `
|
|
8
|
+
* specific record; `entity_batch` actions against a SET of records of one
|
|
9
|
+
* entity (one invocation carrying every id, never one per record — they back
|
|
10
|
+
* a list's toolbar buttons); `global` actions take no record context.
|
|
9
11
|
*/
|
|
10
|
-
export type ActionScope = 'entity' | 'global'
|
|
12
|
+
export type ActionScope = 'entity' | 'entity_batch' | 'global'
|
|
11
13
|
|
|
12
|
-
export const ActionScopeSchema = z.enum(['entity', 'global'])
|
|
14
|
+
export const ActionScopeSchema = z.enum(['entity', 'entity_batch', 'global'])
|
|
13
15
|
|
|
14
16
|
/**
|
|
15
17
|
* Deployable, user-invokable operation. Mirrors `functionsmodel.Action`
|
|
@@ -21,7 +23,7 @@ export interface Action extends AuditFields {
|
|
|
21
23
|
org_id: string
|
|
22
24
|
module_slug: string
|
|
23
25
|
scope: ActionScope
|
|
24
|
-
/** Present only when `scope
|
|
26
|
+
/** Present only when `scope` is `entity` or `entity_batch`. */
|
|
25
27
|
entity?: string
|
|
26
28
|
name: string
|
|
27
29
|
is_active: boolean
|
package/src/index.ts
CHANGED
|
@@ -227,6 +227,8 @@ export type {
|
|
|
227
227
|
AutomatedFilterConfig,
|
|
228
228
|
AutomatedSignal,
|
|
229
229
|
BlockContactRequest,
|
|
230
|
+
CallStatus,
|
|
231
|
+
CallTokenResponse,
|
|
230
232
|
Channel,
|
|
231
233
|
Connection,
|
|
232
234
|
ConnectionCredentials,
|
|
@@ -310,7 +312,12 @@ export type {
|
|
|
310
312
|
MessageRecipient,
|
|
311
313
|
MessageService,
|
|
312
314
|
MessageStatus,
|
|
315
|
+
MintCallTokenRequest,
|
|
313
316
|
MistranscribedTerm,
|
|
317
|
+
PhoneNumber,
|
|
318
|
+
PhoneNumberRouting,
|
|
319
|
+
PhoneNumberStatus,
|
|
320
|
+
RoutingTarget,
|
|
314
321
|
MistranscribedTermService,
|
|
315
322
|
MistranscribedTermStatus,
|
|
316
323
|
MistranscriptionSuggestionSource,
|
|
@@ -349,6 +356,7 @@ export type {
|
|
|
349
356
|
UpdateConversationTypeRequest,
|
|
350
357
|
UpdateDraftRequest,
|
|
351
358
|
UpdateGlossaryTermRequest,
|
|
359
|
+
UpdatePhoneNumberRequest,
|
|
352
360
|
VoiceService,
|
|
353
361
|
VoiceTranscriptionStream,
|
|
354
362
|
} from './conversation/index.js'
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"currency",
|
|
26
26
|
"knowledge-text",
|
|
27
27
|
"file",
|
|
28
|
-
"file-viewer"
|
|
28
|
+
"file-viewer",
|
|
29
|
+
"record-filter"
|
|
29
30
|
],
|
|
30
31
|
"controls": {
|
|
31
32
|
"string": {
|
|
@@ -33,7 +34,8 @@
|
|
|
33
34
|
"compatible": [
|
|
34
35
|
"text",
|
|
35
36
|
"textarea",
|
|
36
|
-
"password"
|
|
37
|
+
"password",
|
|
38
|
+
"record-filter"
|
|
37
39
|
],
|
|
38
40
|
"byFormat": {
|
|
39
41
|
"email": {
|
|
@@ -20,6 +20,9 @@ export const LayoutElementType = {
|
|
|
20
20
|
Component: 'component',
|
|
21
21
|
Divider: 'divider',
|
|
22
22
|
Text: 'text',
|
|
23
|
+
RecordFilter: 'record_filter',
|
|
24
|
+
List: 'list',
|
|
25
|
+
WorkflowTrigger: 'workflow_trigger',
|
|
23
26
|
} as const
|
|
24
27
|
export type LayoutElementType = (typeof LayoutElementType)[keyof typeof LayoutElementType]
|
|
25
28
|
|
|
@@ -66,11 +69,19 @@ export type CardElement = CommonProps & {
|
|
|
66
69
|
content: LayoutElement
|
|
67
70
|
}
|
|
68
71
|
|
|
72
|
+
/**
|
|
73
|
+
* One switchable view inside a `tabs` element. `visible_when` hides the tab
|
|
74
|
+
* when it does not match the record; `default_when` makes it the initially
|
|
75
|
+
* shown tab when it matches. Both are evaluated live against the record being
|
|
76
|
+
* viewed or edited. Resolution order: first visible tab (document order) whose
|
|
77
|
+
* `default_when` matches → `TabsElement.default_tab_id` → first visible tab.
|
|
78
|
+
*/
|
|
69
79
|
export type LayoutTab = {
|
|
70
80
|
id: string
|
|
71
81
|
label: string
|
|
72
82
|
icon?: string
|
|
73
83
|
visible_when?: FilterGroup
|
|
84
|
+
default_when?: FilterGroup
|
|
74
85
|
content: LayoutElement
|
|
75
86
|
}
|
|
76
87
|
|
|
@@ -151,10 +162,99 @@ export type ComponentElement = CommonProps & {
|
|
|
151
162
|
reserved_height?: number
|
|
152
163
|
}
|
|
153
164
|
|
|
165
|
+
/**
|
|
166
|
+
* A filter builder placed directly on a page. It owns no data of its own: it
|
|
167
|
+
* publishes the filter (and the chosen subject entity) under its element id,
|
|
168
|
+
* and every `list` element naming that id in `filter_element_id` renders the
|
|
169
|
+
* filtered rows.
|
|
170
|
+
*
|
|
171
|
+
* `subject_entity` pins which entity the filter is authored against. When it is
|
|
172
|
+
* absent the element renders a subject picker over the entities its bound lists
|
|
173
|
+
* offer — one entry per bound list, since a list carries exactly one entity.
|
|
174
|
+
*
|
|
175
|
+
* The filter is in-memory: it resets on reload rather than persisting per user.
|
|
176
|
+
*/
|
|
177
|
+
export type RecordFilterElement = CommonProps & {
|
|
178
|
+
type: 'record_filter'
|
|
179
|
+
subject_entity?: string
|
|
180
|
+
/** `toolbar` (default) is the Filter button + active chips, matching every
|
|
181
|
+
* list view. `panel` is the always-open AND/OR editor, for a page whose
|
|
182
|
+
* point IS the filter. */
|
|
183
|
+
variant?: 'toolbar' | 'panel'
|
|
184
|
+
/** Nested AND/OR groups. Defaults to true — the records query carries the
|
|
185
|
+
* whole tree, so there is no reason to hide the affordance. */
|
|
186
|
+
is_complex_enabled?: boolean
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Renders records of a configured List — the record-agnostic sibling of
|
|
191
|
+
* `related_list`, and the only way to show records on a page that has no
|
|
192
|
+
* record of its own.
|
|
193
|
+
*
|
|
194
|
+
* The List supplies everything about presentation and behaviour: columns,
|
|
195
|
+
* sorting, base filters, toolbar actions, selection mode, and which page a row
|
|
196
|
+
* opens. Naming more than one list makes the element switchable; the active one
|
|
197
|
+
* is chosen by the bound filter's subject picker, or by the element's own
|
|
198
|
+
* switcher when it is unbound.
|
|
199
|
+
*
|
|
200
|
+
* `filter_element_id` binds the element to a `record_filter` on the same page.
|
|
201
|
+
* The bound filter is ANDed with the list's own saved filters — it narrows the
|
|
202
|
+
* list, it never replaces what the list declared.
|
|
203
|
+
*/
|
|
204
|
+
export type ListElement = CommonProps & {
|
|
205
|
+
type: 'list'
|
|
206
|
+
/** Slugs of the lists this element can render. At least one. */
|
|
207
|
+
list_slugs: string[]
|
|
208
|
+
/** Element id of the `record_filter` driving this list. Unbound renders the
|
|
209
|
+
* list with its own filters only. */
|
|
210
|
+
filter_element_id?: string
|
|
211
|
+
/**
|
|
212
|
+
* Record-page alternative to `filter_element_id`: the attribute on THIS
|
|
213
|
+
* page's record holding a saved filter (as written by the `record-filter`
|
|
214
|
+
* control). The list then renders what the record's own filter selects —
|
|
215
|
+
* a saved-segment record showing its matches.
|
|
216
|
+
*
|
|
217
|
+
* Mutually exclusive with `filter_element_id`: two filters driving one list
|
|
218
|
+
* has no defined precedence, so the layout validator rejects both at once.
|
|
219
|
+
* Record pages only — a standalone page has no record to read.
|
|
220
|
+
*/
|
|
221
|
+
filter_attribute?: string
|
|
222
|
+
/**
|
|
223
|
+
* Attribute on this page's record naming the subject entity slug. The
|
|
224
|
+
* element renders whichever of `list_slugs` targets that entity. Without
|
|
225
|
+
* it the first configured list wins. Pairs with `filter_attribute`.
|
|
226
|
+
*/
|
|
227
|
+
subject_entity_attribute?: string
|
|
228
|
+
page_size?: number
|
|
229
|
+
}
|
|
230
|
+
|
|
154
231
|
export type DividerElement = CommonProps & {
|
|
155
232
|
type: 'divider'
|
|
156
233
|
}
|
|
157
234
|
|
|
235
|
+
/**
|
|
236
|
+
* An always-visible button that starts a manual run of `workflow` (by key) —
|
|
237
|
+
* the in-layout counterpart of a `kind: workflow` toolbar action. `inputs`
|
|
238
|
+
* maps the manual trigger's input_schema field names to Liquid templates
|
|
239
|
+
* rendered against the page scope; `skip_confirmation` fires immediately when
|
|
240
|
+
* every required input resolves. While the run is in flight the element shows
|
|
241
|
+
* the step progress inline in place of the button.
|
|
242
|
+
*/
|
|
243
|
+
export type WorkflowTriggerElement = CommonProps & {
|
|
244
|
+
type: 'workflow_trigger'
|
|
245
|
+
workflow: string
|
|
246
|
+
label: string
|
|
247
|
+
icon?: string
|
|
248
|
+
inputs?: Record<string, string>
|
|
249
|
+
skip_confirmation?: boolean
|
|
250
|
+
/**
|
|
251
|
+
* Optional string attribute on the page's record where the element persists
|
|
252
|
+
* the id of the execution it starts — and reads it back, so progress
|
|
253
|
+
* survives reloads and is shared by every viewer. Record pages only.
|
|
254
|
+
*/
|
|
255
|
+
execution_attribute?: string
|
|
256
|
+
}
|
|
257
|
+
|
|
158
258
|
export type TextVariant = 'heading' | 'subheading' | 'body' | 'caption' | 'callout'
|
|
159
259
|
|
|
160
260
|
export type TextElement = CommonProps & {
|
|
@@ -175,6 +275,9 @@ export type LayoutElement =
|
|
|
175
275
|
| ComponentElement
|
|
176
276
|
| DividerElement
|
|
177
277
|
| TextElement
|
|
278
|
+
| RecordFilterElement
|
|
279
|
+
| ListElement
|
|
280
|
+
| WorkflowTriggerElement
|
|
178
281
|
|
|
179
282
|
// ──────────────────────────────────────────────────────────── Schemas ──
|
|
180
283
|
|
|
@@ -224,6 +327,7 @@ export const LayoutElementSchema: z.ZodType<LayoutElement> = z.lazy(() =>
|
|
|
224
327
|
label: z.string().min(1),
|
|
225
328
|
icon: z.string().optional(),
|
|
226
329
|
visible_when: FilterGroupSchema.optional(),
|
|
330
|
+
default_when: FilterGroupSchema.optional(),
|
|
227
331
|
content: LayoutElementSchema,
|
|
228
332
|
}),
|
|
229
333
|
),
|
|
@@ -265,10 +369,36 @@ export const LayoutElementSchema: z.ZodType<LayoutElement> = z.lazy(() =>
|
|
|
265
369
|
props: z.record(z.unknown()).optional(),
|
|
266
370
|
reserved_height: z.number().positive().optional(),
|
|
267
371
|
}),
|
|
372
|
+
z.object({
|
|
373
|
+
type: z.literal('record_filter'),
|
|
374
|
+
...commonPropsShape,
|
|
375
|
+
subject_entity: z.string().min(1).optional(),
|
|
376
|
+
variant: z.enum(['toolbar', 'panel']).optional(),
|
|
377
|
+
is_complex_enabled: z.boolean().optional(),
|
|
378
|
+
}),
|
|
379
|
+
z.object({
|
|
380
|
+
type: z.literal('list'),
|
|
381
|
+
...commonPropsShape,
|
|
382
|
+
list_slugs: z.array(z.string().min(1)).min(1),
|
|
383
|
+
filter_element_id: z.string().min(1).optional(),
|
|
384
|
+
filter_attribute: z.string().min(1).optional(),
|
|
385
|
+
subject_entity_attribute: z.string().min(1).optional(),
|
|
386
|
+
page_size: z.number().int().positive().optional(),
|
|
387
|
+
}),
|
|
268
388
|
z.object({
|
|
269
389
|
type: z.literal('divider'),
|
|
270
390
|
...commonPropsShape,
|
|
271
391
|
}),
|
|
392
|
+
z.object({
|
|
393
|
+
type: z.literal('workflow_trigger'),
|
|
394
|
+
...commonPropsShape,
|
|
395
|
+
workflow: z.string().min(1),
|
|
396
|
+
label: z.string().min(1),
|
|
397
|
+
icon: z.string().optional(),
|
|
398
|
+
inputs: z.record(z.string()).optional(),
|
|
399
|
+
skip_confirmation: z.boolean().optional(),
|
|
400
|
+
execution_attribute: z.string().min(1).optional(),
|
|
401
|
+
}),
|
|
272
402
|
z.object({
|
|
273
403
|
type: z.literal('text'),
|
|
274
404
|
...commonPropsShape,
|
package/src/meta/layout/index.ts
CHANGED
package/src/meta/types.ts
CHANGED
|
@@ -864,6 +864,19 @@ export interface UpdateComponentRequest {
|
|
|
864
864
|
|
|
865
865
|
/**
|
|
866
866
|
* List column definition.
|
|
867
|
+
*
|
|
868
|
+
* `attribute` is an attribute name or a dot path, told apart by the type of
|
|
869
|
+
* the first segment:
|
|
870
|
+
*
|
|
871
|
+
* - `name` — an attribute on the list's own entity.
|
|
872
|
+
* - `address.city` — a leaf inside one of its `object` attributes.
|
|
873
|
+
* - `company_id.name` — a field of the RELATED record, reached through a
|
|
874
|
+
* relation attribute (the first segment is the FK).
|
|
875
|
+
*
|
|
876
|
+
* A bare `object` attribute is not a valid column — it carries no value of
|
|
877
|
+
* its own, only leaves. Sorting follows the same grammar minus the relation
|
|
878
|
+
* hop: an attribute or an object path can be ordered by, a related field
|
|
879
|
+
* cannot (it would need a join).
|
|
867
880
|
*/
|
|
868
881
|
export interface Column {
|
|
869
882
|
attribute: string
|
|
@@ -877,7 +890,9 @@ export interface Column {
|
|
|
877
890
|
export type SortDirection = 'asc' | 'desc'
|
|
878
891
|
|
|
879
892
|
/**
|
|
880
|
-
* Sort configuration.
|
|
893
|
+
* Sort configuration. `attribute` is an attribute name or an object path
|
|
894
|
+
* (`address.city`); relation paths are not sortable — ordering by a related
|
|
895
|
+
* field would need a join the records query doesn't do.
|
|
881
896
|
*/
|
|
882
897
|
export interface SortConfig {
|
|
883
898
|
attribute: string
|
|
@@ -904,6 +919,60 @@ export {
|
|
|
904
919
|
type LogicalOperator,
|
|
905
920
|
}
|
|
906
921
|
|
|
922
|
+
// ============================================================================
|
|
923
|
+
// Toolbar action buttons (shared by pages and lists)
|
|
924
|
+
// ============================================================================
|
|
925
|
+
|
|
926
|
+
/**
|
|
927
|
+
* What a page toolbar button invokes. Absent normalizes to `action` (pages
|
|
928
|
+
* persisted before `kind` existed).
|
|
929
|
+
*/
|
|
930
|
+
export type PageActionKind = 'action' | 'workflow'
|
|
931
|
+
|
|
932
|
+
/**
|
|
933
|
+
* Page action definition — one toolbar button. `kind: action` invokes a
|
|
934
|
+
* function-service Action by slug (`action`) and may prefill its params;
|
|
935
|
+
* `kind: workflow` starts a manual run of a workflow by key (`workflow`) and
|
|
936
|
+
* may prefill its manual-trigger inputs. `params` / `inputs` map target field
|
|
937
|
+
* names to Liquid templates rendered against the page scope
|
|
938
|
+
* `{ record, entity, params, user }`; a resolved field is locked in the invoke
|
|
939
|
+
* dialog. `skip_confirmation` fires the target immediately when every required
|
|
940
|
+
* field resolved from the templates.
|
|
941
|
+
*/
|
|
942
|
+
export interface PageAction {
|
|
943
|
+
label: string
|
|
944
|
+
icon: string
|
|
945
|
+
kind?: PageActionKind
|
|
946
|
+
action?: string
|
|
947
|
+
workflow?: string
|
|
948
|
+
params?: Record<string, string>
|
|
949
|
+
inputs?: Record<string, string>
|
|
950
|
+
skip_confirmation?: boolean
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
export const PageActionSchema = z.object({
|
|
954
|
+
label: z.string(),
|
|
955
|
+
icon: z.string(),
|
|
956
|
+
kind: z.enum(['action', 'workflow']).optional(),
|
|
957
|
+
action: z.string().optional(),
|
|
958
|
+
workflow: z.string().optional(),
|
|
959
|
+
params: z.record(z.string()).optional(),
|
|
960
|
+
inputs: z.record(z.string()).optional(),
|
|
961
|
+
skip_confirmation: z.boolean().optional(),
|
|
962
|
+
})
|
|
963
|
+
|
|
964
|
+
/**
|
|
965
|
+
* Whether a list's rows can be checked, and whether the checkboxes show from
|
|
966
|
+
* the start:
|
|
967
|
+
*
|
|
968
|
+
* - `on_demand` (default) — a Select toggle in the toolbar reveals them.
|
|
969
|
+
* - `always` — checkboxes are showing from the start.
|
|
970
|
+
* - `off` — rows can never be checked, even when the list carries actions.
|
|
971
|
+
*/
|
|
972
|
+
export type SelectionMode = 'on_demand' | 'always' | 'off'
|
|
973
|
+
|
|
974
|
+
export const SelectionModeSchema = z.enum(['on_demand', 'always', 'off'])
|
|
975
|
+
|
|
907
976
|
/**
|
|
908
977
|
* List configuration.
|
|
909
978
|
* Note: List uses `slug` as its primary identifier, not `id`.
|
|
@@ -915,6 +984,15 @@ export interface List extends AuditFields {
|
|
|
915
984
|
name: string
|
|
916
985
|
entity_slug: string
|
|
917
986
|
columns: Column[]
|
|
987
|
+
/**
|
|
988
|
+
* Toolbar buttons on the list, same shape a page carries. They act on the
|
|
989
|
+
* rows SELECTED in the list, so an `action` button names an `entity_batch`
|
|
990
|
+
* action (invoked once with every selected record id) and prefill templates
|
|
991
|
+
* resolve against the list scope `{ selection, entity, user }`.
|
|
992
|
+
*/
|
|
993
|
+
actions?: PageAction[]
|
|
994
|
+
/** Row-selection affordance; absent normalizes to `on_demand`. */
|
|
995
|
+
selection_mode?: SelectionMode
|
|
918
996
|
/** Record page to open from this list; empty/absent = org default for the entity. */
|
|
919
997
|
default_page_slug?: string
|
|
920
998
|
sorting: SortConfig[]
|
|
@@ -938,6 +1016,8 @@ export const ListSchema = AuditFieldsSchema.extend({
|
|
|
938
1016
|
name: z.string(),
|
|
939
1017
|
entity_slug: z.string(),
|
|
940
1018
|
columns: z.array(ColumnSchema),
|
|
1019
|
+
actions: z.array(PageActionSchema).optional(),
|
|
1020
|
+
selection_mode: SelectionModeSchema.optional(),
|
|
941
1021
|
default_page_slug: z.string().optional(),
|
|
942
1022
|
sorting: z.array(SortConfigSchema),
|
|
943
1023
|
filters: z.array(FilterGroupSchema),
|
|
@@ -962,6 +1042,8 @@ export interface CreateListRequest {
|
|
|
962
1042
|
entity_slug: string
|
|
963
1043
|
name: string
|
|
964
1044
|
columns: Column[]
|
|
1045
|
+
actions?: PageAction[]
|
|
1046
|
+
selection_mode?: SelectionMode
|
|
965
1047
|
default_page_slug?: string
|
|
966
1048
|
sorting: SortConfig[]
|
|
967
1049
|
filters: FilterGroup[]
|
|
@@ -974,6 +1056,8 @@ export interface UpdateListRequest {
|
|
|
974
1056
|
name?: string
|
|
975
1057
|
module_slug?: string
|
|
976
1058
|
columns?: Column[]
|
|
1059
|
+
actions?: PageAction[]
|
|
1060
|
+
selection_mode?: SelectionMode
|
|
977
1061
|
/** Set to '' to clear back to the org default. */
|
|
978
1062
|
default_page_slug?: string
|
|
979
1063
|
sorting?: SortConfig[]
|
|
@@ -1047,44 +1131,6 @@ export interface UpdateListViewRequest {
|
|
|
1047
1131
|
// Page Types
|
|
1048
1132
|
// ============================================================================
|
|
1049
1133
|
|
|
1050
|
-
/**
|
|
1051
|
-
* What a page toolbar button invokes. Absent normalizes to `action` (pages
|
|
1052
|
-
* persisted before `kind` existed).
|
|
1053
|
-
*/
|
|
1054
|
-
export type PageActionKind = 'action' | 'workflow'
|
|
1055
|
-
|
|
1056
|
-
/**
|
|
1057
|
-
* Page action definition — one toolbar button. `kind: action` invokes a
|
|
1058
|
-
* function-service Action by slug (`action`) and may prefill its params;
|
|
1059
|
-
* `kind: workflow` starts a manual run of a workflow by key (`workflow`) and
|
|
1060
|
-
* may prefill its manual-trigger inputs. `params` / `inputs` map target field
|
|
1061
|
-
* names to Liquid templates rendered against the page scope
|
|
1062
|
-
* `{ record, entity, params, user }`; a resolved field is locked in the invoke
|
|
1063
|
-
* dialog. `skip_confirmation` fires the target immediately when every required
|
|
1064
|
-
* field resolved from the templates.
|
|
1065
|
-
*/
|
|
1066
|
-
export interface PageAction {
|
|
1067
|
-
label: string
|
|
1068
|
-
icon: string
|
|
1069
|
-
kind?: PageActionKind
|
|
1070
|
-
action?: string
|
|
1071
|
-
workflow?: string
|
|
1072
|
-
params?: Record<string, string>
|
|
1073
|
-
inputs?: Record<string, string>
|
|
1074
|
-
skip_confirmation?: boolean
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
export const PageActionSchema = z.object({
|
|
1078
|
-
label: z.string(),
|
|
1079
|
-
icon: z.string(),
|
|
1080
|
-
kind: z.enum(['action', 'workflow']).optional(),
|
|
1081
|
-
action: z.string().optional(),
|
|
1082
|
-
workflow: z.string().optional(),
|
|
1083
|
-
params: z.record(z.string()).optional(),
|
|
1084
|
-
inputs: z.record(z.string()).optional(),
|
|
1085
|
-
skip_confirmation: z.boolean().optional(),
|
|
1086
|
-
})
|
|
1087
|
-
|
|
1088
1134
|
/**
|
|
1089
1135
|
* Page type — encodes what the page binds to and how it is served (chrome +
|
|
1090
1136
|
* auth posture both follow from it):
|