@objectstack/plugin-approvals 7.3.0 → 7.4.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/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +75 -0
- package/dist/index.d.mts +6431 -107
- package/dist/index.d.ts +6431 -107
- package/dist/index.js +1237 -776
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1244 -779
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -7
- package/scripts/i18n-extract.config.ts +32 -0
- package/src/approval-node.test.ts +182 -0
- package/src/approval-node.ts +131 -0
- package/src/approval-service.test.ts +205 -304
- package/src/approval-service.ts +208 -491
- package/src/approvals-plugin.ts +61 -53
- package/src/index.ts +12 -11
- package/src/lifecycle-hooks.ts +67 -202
- package/src/nav-contribution.test.ts +46 -0
- package/src/sys-approval-action.object.ts +120 -0
- package/src/sys-approval-request.object.ts +227 -0
- package/src/translations/en.objects.generated.ts +156 -0
- package/src/translations/es-ES.objects.generated.ts +156 -0
- package/src/translations/index.ts +23 -0
- package/src/translations/ja-JP.objects.generated.ts +156 -0
- package/src/translations/zh-CN.objects.generated.ts +156 -0
- package/src/action-executor.ts +0 -313
- package/src/phase-b.test.ts +0 -263
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
+
|
|
3
|
+
import { ObjectSchema, Field } from '@objectstack/spec/data';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* sys_approval_action — Audit trail row per approval action.
|
|
7
|
+
*
|
|
8
|
+
* Append-only: every `submit`, `approve`, `reject`, `recall`, or
|
|
9
|
+
* `escalate` event lands here. The engine reads back per-step approval
|
|
10
|
+
* rows to evaluate `behavior: 'unanimous'` (all approvers must approve
|
|
11
|
+
* before advancing) versus `first_response` (any single approval
|
|
12
|
+
* advances the step).
|
|
13
|
+
*
|
|
14
|
+
* @namespace sys
|
|
15
|
+
*/
|
|
16
|
+
export const SysApprovalAction = ObjectSchema.create({
|
|
17
|
+
name: 'sys_approval_action',
|
|
18
|
+
label: 'Approval Action',
|
|
19
|
+
pluralLabel: 'Approval Actions',
|
|
20
|
+
icon: 'check-circle',
|
|
21
|
+
isSystem: true,
|
|
22
|
+
managedBy: 'append-only',
|
|
23
|
+
description: 'Append-only audit trail for approval actions',
|
|
24
|
+
displayNameField: 'id',
|
|
25
|
+
titleFormat: '{action} · {step_name}',
|
|
26
|
+
compactLayout: ['request_id', 'step_name', 'action', 'actor_id', 'created_at'],
|
|
27
|
+
|
|
28
|
+
listViews: {
|
|
29
|
+
recent: {
|
|
30
|
+
type: 'grid',
|
|
31
|
+
name: 'recent',
|
|
32
|
+
label: 'Recent',
|
|
33
|
+
data: { provider: 'object', object: 'sys_approval_action' },
|
|
34
|
+
columns: ['created_at', 'request_id', 'step_name', 'action', 'actor_id', 'comment'],
|
|
35
|
+
sort: [{ field: 'created_at', order: 'desc' }],
|
|
36
|
+
pagination: { pageSize: 50 },
|
|
37
|
+
emptyState: { title: 'No approval actions yet', message: 'Actions are logged automatically when approvals progress.' },
|
|
38
|
+
},
|
|
39
|
+
by_actor: {
|
|
40
|
+
type: 'grid',
|
|
41
|
+
name: 'by_actor',
|
|
42
|
+
label: 'By Actor',
|
|
43
|
+
data: { provider: 'object', object: 'sys_approval_action' },
|
|
44
|
+
columns: ['actor_id', 'created_at', 'request_id', 'step_name', 'action'],
|
|
45
|
+
sort: [{ field: 'actor_id', order: 'asc' }, { field: 'created_at', order: 'desc' }],
|
|
46
|
+
grouping: { fields: [{ field: 'actor_id', order: 'asc', collapsed: false }] },
|
|
47
|
+
pagination: { pageSize: 100 },
|
|
48
|
+
},
|
|
49
|
+
all_actions: {
|
|
50
|
+
type: 'grid',
|
|
51
|
+
name: 'all_actions',
|
|
52
|
+
label: 'All',
|
|
53
|
+
data: { provider: 'object', object: 'sys_approval_action' },
|
|
54
|
+
columns: ['created_at', 'request_id', 'step_name', 'action', 'actor_id', 'comment'],
|
|
55
|
+
sort: [{ field: 'created_at', order: 'desc' }],
|
|
56
|
+
pagination: { pageSize: 100 },
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
fields: {
|
|
61
|
+
id: Field.text({ label: 'Action ID', required: true, readonly: true, group: 'System' }),
|
|
62
|
+
|
|
63
|
+
organization_id: Field.lookup('sys_organization', {
|
|
64
|
+
label: 'Organization',
|
|
65
|
+
required: false,
|
|
66
|
+
group: 'System',
|
|
67
|
+
description: 'Tenant that owns this action (mirrors the parent request)',
|
|
68
|
+
}),
|
|
69
|
+
|
|
70
|
+
request_id: Field.lookup('sys_approval_request', {
|
|
71
|
+
label: 'Request',
|
|
72
|
+
required: true,
|
|
73
|
+
group: 'Target',
|
|
74
|
+
}),
|
|
75
|
+
|
|
76
|
+
step_name: Field.text({
|
|
77
|
+
label: 'Step',
|
|
78
|
+
required: false,
|
|
79
|
+
maxLength: 100,
|
|
80
|
+
description: 'Machine name of the step at the time of the action',
|
|
81
|
+
group: 'Target',
|
|
82
|
+
}),
|
|
83
|
+
|
|
84
|
+
step_index: Field.number({
|
|
85
|
+
label: 'Step Index',
|
|
86
|
+
required: false,
|
|
87
|
+
group: 'Target',
|
|
88
|
+
}),
|
|
89
|
+
|
|
90
|
+
action: Field.select(
|
|
91
|
+
['submit', 'approve', 'reject', 'recall', 'escalate'],
|
|
92
|
+
{
|
|
93
|
+
label: 'Action',
|
|
94
|
+
required: true,
|
|
95
|
+
group: 'Action',
|
|
96
|
+
},
|
|
97
|
+
),
|
|
98
|
+
|
|
99
|
+
actor_id: Field.lookup('sys_user', {
|
|
100
|
+
label: 'Actor',
|
|
101
|
+
required: false,
|
|
102
|
+
group: 'Action',
|
|
103
|
+
}),
|
|
104
|
+
|
|
105
|
+
comment: Field.textarea({ label: 'Comment', required: false, group: 'Action' }),
|
|
106
|
+
|
|
107
|
+
created_at: Field.datetime({
|
|
108
|
+
label: 'Created At',
|
|
109
|
+
required: true,
|
|
110
|
+
defaultValue: 'NOW()',
|
|
111
|
+
readonly: true,
|
|
112
|
+
group: 'System',
|
|
113
|
+
}),
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
indexes: [
|
|
117
|
+
{ fields: ['request_id', 'created_at'] },
|
|
118
|
+
{ fields: ['request_id', 'step_index', 'action'] },
|
|
119
|
+
],
|
|
120
|
+
});
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
+
|
|
3
|
+
import { ObjectSchema, Field } from '@objectstack/spec/data';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* sys_approval_request — Live approval instance.
|
|
7
|
+
*
|
|
8
|
+
* ADR-0019: opened by a flow's **Approval node** when the run reaches it; the
|
|
9
|
+
* run suspends until a decision is recorded. The row's lifecycle:
|
|
10
|
+
*
|
|
11
|
+
* `pending` → (per-approver decisions) → `approved` | `rejected`
|
|
12
|
+
* `pending` → recalled by submitter → `recalled`
|
|
13
|
+
*
|
|
14
|
+
* `flow_run_id` / `flow_node_id` tie the request back to the suspended run so a
|
|
15
|
+
* decision can resume it; `current_step` mirrors the node id. `node_config_json`
|
|
16
|
+
* snapshots the Approval node config (approvers / behaviour) the request was
|
|
17
|
+
* opened with.
|
|
18
|
+
*
|
|
19
|
+
* `payload_json` captures a snapshot of the target record at submission
|
|
20
|
+
* time — used by notifications so they can render before the record is
|
|
21
|
+
* locked or changed.
|
|
22
|
+
*
|
|
23
|
+
* @namespace sys
|
|
24
|
+
*/
|
|
25
|
+
export const SysApprovalRequest = ObjectSchema.create({
|
|
26
|
+
name: 'sys_approval_request',
|
|
27
|
+
label: 'Approval Request',
|
|
28
|
+
pluralLabel: 'Approval Requests',
|
|
29
|
+
icon: 'inbox',
|
|
30
|
+
isSystem: true,
|
|
31
|
+
managedBy: 'system',
|
|
32
|
+
description: 'Live approval instance tracked per submission',
|
|
33
|
+
displayNameField: 'id',
|
|
34
|
+
titleFormat: '{process_name} · {record_id}',
|
|
35
|
+
compactLayout: ['process_name', 'object_name', 'record_id', 'status', 'current_step', 'submitter_id', 'updated_at'],
|
|
36
|
+
|
|
37
|
+
// Curated built-in list views — render as segmented tabs in the console.
|
|
38
|
+
// Filters use {current_user_id} substitution wired by the console.
|
|
39
|
+
listViews: {
|
|
40
|
+
my_pending: {
|
|
41
|
+
type: 'grid',
|
|
42
|
+
name: 'my_pending',
|
|
43
|
+
label: 'My Pending',
|
|
44
|
+
data: { provider: 'object', object: 'sys_approval_request' },
|
|
45
|
+
columns: ['process_name', 'object_name', 'record_id', 'current_step', 'submitter_id', 'updated_at'],
|
|
46
|
+
filter: [
|
|
47
|
+
{ field: 'status', operator: 'equals', value: 'pending' },
|
|
48
|
+
{ field: 'pending_approvers', operator: 'contains', value: '{current_user_id}' },
|
|
49
|
+
],
|
|
50
|
+
sort: [{ field: 'updated_at', order: 'desc' }],
|
|
51
|
+
pagination: { pageSize: 25 },
|
|
52
|
+
emptyState: { title: 'No pending approvals', message: 'You\'re all caught up.' },
|
|
53
|
+
},
|
|
54
|
+
submitted_by_me: {
|
|
55
|
+
type: 'grid',
|
|
56
|
+
name: 'submitted_by_me',
|
|
57
|
+
label: 'I Submitted',
|
|
58
|
+
data: { provider: 'object', object: 'sys_approval_request' },
|
|
59
|
+
columns: ['process_name', 'object_name', 'record_id', 'status', 'current_step', 'updated_at'],
|
|
60
|
+
filter: [{ field: 'submitter_id', operator: 'equals', value: '{current_user_id}' }],
|
|
61
|
+
sort: [{ field: 'updated_at', order: 'desc' }],
|
|
62
|
+
pagination: { pageSize: 25 },
|
|
63
|
+
},
|
|
64
|
+
completed: {
|
|
65
|
+
type: 'grid',
|
|
66
|
+
name: 'completed',
|
|
67
|
+
label: 'Completed',
|
|
68
|
+
data: { provider: 'object', object: 'sys_approval_request' },
|
|
69
|
+
columns: ['process_name', 'object_name', 'record_id', 'status', 'submitter_id', 'completed_at'],
|
|
70
|
+
filter: [{ field: 'status', operator: 'in', value: ['approved', 'rejected', 'recalled'] }],
|
|
71
|
+
sort: [{ field: 'completed_at', order: 'desc' }],
|
|
72
|
+
pagination: { pageSize: 25 },
|
|
73
|
+
},
|
|
74
|
+
all_requests: {
|
|
75
|
+
type: 'grid',
|
|
76
|
+
name: 'all_requests',
|
|
77
|
+
label: 'All',
|
|
78
|
+
data: { provider: 'object', object: 'sys_approval_request' },
|
|
79
|
+
columns: ['process_name', 'object_name', 'record_id', 'status', 'current_step', 'submitter_id', 'updated_at'],
|
|
80
|
+
sort: [{ field: 'updated_at', order: 'desc' }],
|
|
81
|
+
pagination: { pageSize: 50 },
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
fields: {
|
|
86
|
+
id: Field.text({ label: 'Request ID', required: true, readonly: true, group: 'System' }),
|
|
87
|
+
|
|
88
|
+
organization_id: Field.lookup('sys_organization', {
|
|
89
|
+
label: 'Organization',
|
|
90
|
+
required: false,
|
|
91
|
+
group: 'System',
|
|
92
|
+
description: 'Tenant that owns this approval request (propagated from submitter context)',
|
|
93
|
+
}),
|
|
94
|
+
|
|
95
|
+
process_name: Field.text({
|
|
96
|
+
label: 'Source',
|
|
97
|
+
required: true,
|
|
98
|
+
maxLength: 100,
|
|
99
|
+
description: 'Origin of the request — `flow:<flowName|nodeId>` for node-driven approvals',
|
|
100
|
+
group: 'Target',
|
|
101
|
+
}),
|
|
102
|
+
|
|
103
|
+
object_name: Field.text({
|
|
104
|
+
label: 'Object',
|
|
105
|
+
required: true,
|
|
106
|
+
maxLength: 100,
|
|
107
|
+
group: 'Target',
|
|
108
|
+
}),
|
|
109
|
+
|
|
110
|
+
record_id: Field.text({
|
|
111
|
+
label: 'Record ID',
|
|
112
|
+
required: true,
|
|
113
|
+
maxLength: 100,
|
|
114
|
+
group: 'Target',
|
|
115
|
+
}),
|
|
116
|
+
|
|
117
|
+
submitter_id: Field.lookup('sys_user', {
|
|
118
|
+
label: 'Submitter',
|
|
119
|
+
required: false,
|
|
120
|
+
group: 'Target',
|
|
121
|
+
}),
|
|
122
|
+
|
|
123
|
+
submitter_comment: Field.textarea({
|
|
124
|
+
label: 'Submitter Comment',
|
|
125
|
+
required: false,
|
|
126
|
+
group: 'Target',
|
|
127
|
+
}),
|
|
128
|
+
|
|
129
|
+
status: Field.select(
|
|
130
|
+
['pending', 'approved', 'rejected', 'recalled'],
|
|
131
|
+
{
|
|
132
|
+
label: 'Status',
|
|
133
|
+
required: true,
|
|
134
|
+
defaultValue: 'pending',
|
|
135
|
+
description: 'Lifecycle state of the request',
|
|
136
|
+
group: 'State',
|
|
137
|
+
},
|
|
138
|
+
),
|
|
139
|
+
|
|
140
|
+
current_step: Field.text({
|
|
141
|
+
label: 'Current Step',
|
|
142
|
+
required: false,
|
|
143
|
+
maxLength: 100,
|
|
144
|
+
description: 'Machine name of the step awaiting approval',
|
|
145
|
+
group: 'State',
|
|
146
|
+
}),
|
|
147
|
+
|
|
148
|
+
current_step_index: Field.number({
|
|
149
|
+
label: 'Current Step Index',
|
|
150
|
+
required: false,
|
|
151
|
+
defaultValue: 0,
|
|
152
|
+
group: 'State',
|
|
153
|
+
}),
|
|
154
|
+
|
|
155
|
+
pending_approvers: Field.textarea({
|
|
156
|
+
label: 'Pending Approvers',
|
|
157
|
+
required: false,
|
|
158
|
+
description: 'Comma-separated user ids who can act on the current step',
|
|
159
|
+
group: 'State',
|
|
160
|
+
}),
|
|
161
|
+
|
|
162
|
+
payload_json: Field.textarea({
|
|
163
|
+
label: 'Snapshot',
|
|
164
|
+
required: false,
|
|
165
|
+
description: 'Record snapshot at submission time',
|
|
166
|
+
group: 'State',
|
|
167
|
+
}),
|
|
168
|
+
|
|
169
|
+
// ── ADR-0019: approval-as-flow-node correlation ──────────────────
|
|
170
|
+
// When a request is opened by an Approval *node* (rather than a standalone
|
|
171
|
+
// process), these tie it back to the suspended flow run so a decision can
|
|
172
|
+
// resume it. Null for legacy process-driven requests.
|
|
173
|
+
flow_run_id: Field.text({
|
|
174
|
+
label: 'Flow Run',
|
|
175
|
+
required: false,
|
|
176
|
+
maxLength: 100,
|
|
177
|
+
readonly: true,
|
|
178
|
+
description: 'Suspended automation run id this request gates (ADR-0019). The decision resumes it.',
|
|
179
|
+
group: 'State',
|
|
180
|
+
}),
|
|
181
|
+
|
|
182
|
+
flow_node_id: Field.text({
|
|
183
|
+
label: 'Flow Node',
|
|
184
|
+
required: false,
|
|
185
|
+
maxLength: 100,
|
|
186
|
+
readonly: true,
|
|
187
|
+
description: 'Approval node id within the flow that opened this request (ADR-0019).',
|
|
188
|
+
group: 'State',
|
|
189
|
+
}),
|
|
190
|
+
|
|
191
|
+
node_config_json: Field.textarea({
|
|
192
|
+
label: 'Node Config',
|
|
193
|
+
required: false,
|
|
194
|
+
readonly: true,
|
|
195
|
+
description: 'Snapshot of the Approval node config (approvers/behavior) for node-driven requests (ADR-0019).',
|
|
196
|
+
group: 'State',
|
|
197
|
+
}),
|
|
198
|
+
|
|
199
|
+
completed_at: Field.datetime({
|
|
200
|
+
label: 'Completed At',
|
|
201
|
+
required: false,
|
|
202
|
+
group: 'State',
|
|
203
|
+
}),
|
|
204
|
+
|
|
205
|
+
created_at: Field.datetime({
|
|
206
|
+
label: 'Created At',
|
|
207
|
+
required: true,
|
|
208
|
+
defaultValue: 'NOW()',
|
|
209
|
+
readonly: true,
|
|
210
|
+
group: 'System',
|
|
211
|
+
}),
|
|
212
|
+
|
|
213
|
+
updated_at: Field.datetime({ label: 'Updated At', required: false, group: 'System' }),
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
indexes: [
|
|
217
|
+
// Look up "is there a pending request for this record?" — common
|
|
218
|
+
// guard on submit and on edit-while-locked checks.
|
|
219
|
+
{ fields: ['object_name', 'record_id'] },
|
|
220
|
+
{ fields: ['status', 'object_name'] },
|
|
221
|
+
// "My approvals" inbox — pending_approvers is a CSV string so this
|
|
222
|
+
// index only helps with status pre-filtering; the engine does a
|
|
223
|
+
// post-filter substring match per row.
|
|
224
|
+
{ fields: ['status', 'updated_at'] },
|
|
225
|
+
{ fields: ['submitter_id', 'status'] },
|
|
226
|
+
],
|
|
227
|
+
});
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Auto-generated by 'os i18n extract' for locale 'en'.
|
|
5
|
+
* Edit translations in place; re-run extract (with --merge) to fill new gaps.
|
|
6
|
+
* Do not hand-edit the structure — only the leaf string values.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { TranslationData } from '@objectstack/spec/system';
|
|
10
|
+
|
|
11
|
+
export const enObjects: NonNullable<TranslationData['objects']> = {
|
|
12
|
+
sys_approval_request: {
|
|
13
|
+
label: "Approval Request",
|
|
14
|
+
pluralLabel: "Approval Requests",
|
|
15
|
+
description: "Live approval instance tracked per submission",
|
|
16
|
+
fields: {
|
|
17
|
+
id: {
|
|
18
|
+
label: "Request ID"
|
|
19
|
+
},
|
|
20
|
+
organization_id: {
|
|
21
|
+
label: "Organization",
|
|
22
|
+
help: "Tenant that owns this approval request (propagated from submitter context)"
|
|
23
|
+
},
|
|
24
|
+
process_name: {
|
|
25
|
+
label: "Source",
|
|
26
|
+
help: "Origin of the request — `flow:<flowName|nodeId>` for node-driven approvals"
|
|
27
|
+
},
|
|
28
|
+
object_name: {
|
|
29
|
+
label: "Object"
|
|
30
|
+
},
|
|
31
|
+
record_id: {
|
|
32
|
+
label: "Record ID"
|
|
33
|
+
},
|
|
34
|
+
submitter_id: {
|
|
35
|
+
label: "Submitter"
|
|
36
|
+
},
|
|
37
|
+
submitter_comment: {
|
|
38
|
+
label: "Submitter Comment"
|
|
39
|
+
},
|
|
40
|
+
status: {
|
|
41
|
+
label: "Status",
|
|
42
|
+
help: "Lifecycle state of the request",
|
|
43
|
+
options: {
|
|
44
|
+
pending: "pending",
|
|
45
|
+
approved: "approved",
|
|
46
|
+
rejected: "rejected",
|
|
47
|
+
recalled: "recalled"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
current_step: {
|
|
51
|
+
label: "Current Step",
|
|
52
|
+
help: "Machine name of the step awaiting approval"
|
|
53
|
+
},
|
|
54
|
+
current_step_index: {
|
|
55
|
+
label: "Current Step Index"
|
|
56
|
+
},
|
|
57
|
+
pending_approvers: {
|
|
58
|
+
label: "Pending Approvers",
|
|
59
|
+
help: "Comma-separated user ids who can act on the current step"
|
|
60
|
+
},
|
|
61
|
+
payload_json: {
|
|
62
|
+
label: "Snapshot",
|
|
63
|
+
help: "Record snapshot at submission time"
|
|
64
|
+
},
|
|
65
|
+
flow_run_id: {
|
|
66
|
+
label: "Flow Run",
|
|
67
|
+
help: "Suspended automation run id this request gates (ADR-0019). The decision resumes it."
|
|
68
|
+
},
|
|
69
|
+
flow_node_id: {
|
|
70
|
+
label: "Flow Node",
|
|
71
|
+
help: "Approval node id within the flow that opened this request (ADR-0019)."
|
|
72
|
+
},
|
|
73
|
+
node_config_json: {
|
|
74
|
+
label: "Node Config",
|
|
75
|
+
help: "Snapshot of the Approval node config (approvers/behavior) for node-driven requests (ADR-0019)."
|
|
76
|
+
},
|
|
77
|
+
completed_at: {
|
|
78
|
+
label: "Completed At"
|
|
79
|
+
},
|
|
80
|
+
created_at: {
|
|
81
|
+
label: "Created At"
|
|
82
|
+
},
|
|
83
|
+
updated_at: {
|
|
84
|
+
label: "Updated At"
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
_views: {
|
|
88
|
+
my_pending: {
|
|
89
|
+
label: "My Pending"
|
|
90
|
+
},
|
|
91
|
+
submitted_by_me: {
|
|
92
|
+
label: "I Submitted"
|
|
93
|
+
},
|
|
94
|
+
completed: {
|
|
95
|
+
label: "Completed"
|
|
96
|
+
},
|
|
97
|
+
all_requests: {
|
|
98
|
+
label: "All"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
sys_approval_action: {
|
|
103
|
+
label: "Approval Action",
|
|
104
|
+
pluralLabel: "Approval Actions",
|
|
105
|
+
description: "Append-only audit trail for approval actions",
|
|
106
|
+
fields: {
|
|
107
|
+
id: {
|
|
108
|
+
label: "Action ID"
|
|
109
|
+
},
|
|
110
|
+
organization_id: {
|
|
111
|
+
label: "Organization",
|
|
112
|
+
help: "Tenant that owns this action (mirrors the parent request)"
|
|
113
|
+
},
|
|
114
|
+
request_id: {
|
|
115
|
+
label: "Request"
|
|
116
|
+
},
|
|
117
|
+
step_name: {
|
|
118
|
+
label: "Step",
|
|
119
|
+
help: "Machine name of the step at the time of the action"
|
|
120
|
+
},
|
|
121
|
+
step_index: {
|
|
122
|
+
label: "Step Index"
|
|
123
|
+
},
|
|
124
|
+
action: {
|
|
125
|
+
label: "Action",
|
|
126
|
+
options: {
|
|
127
|
+
submit: "submit",
|
|
128
|
+
approve: "approve",
|
|
129
|
+
reject: "reject",
|
|
130
|
+
recall: "recall",
|
|
131
|
+
escalate: "escalate"
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
actor_id: {
|
|
135
|
+
label: "Actor"
|
|
136
|
+
},
|
|
137
|
+
comment: {
|
|
138
|
+
label: "Comment"
|
|
139
|
+
},
|
|
140
|
+
created_at: {
|
|
141
|
+
label: "Created At"
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
_views: {
|
|
145
|
+
recent: {
|
|
146
|
+
label: "Recent"
|
|
147
|
+
},
|
|
148
|
+
by_actor: {
|
|
149
|
+
label: "By Actor"
|
|
150
|
+
},
|
|
151
|
+
all_actions: {
|
|
152
|
+
label: "All"
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Auto-generated by 'os i18n extract' for locale 'es-ES'.
|
|
5
|
+
* Edit translations in place; re-run extract (with --merge) to fill new gaps.
|
|
6
|
+
* Do not hand-edit the structure — only the leaf string values.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { TranslationData } from '@objectstack/spec/system';
|
|
10
|
+
|
|
11
|
+
export const esESObjects: NonNullable<TranslationData['objects']> = {
|
|
12
|
+
sys_approval_request: {
|
|
13
|
+
label: "Solicitud de aprobación",
|
|
14
|
+
pluralLabel: "Solicitudes de aprobación",
|
|
15
|
+
description: "Instancia activa de aprobación registrada por envío",
|
|
16
|
+
fields: {
|
|
17
|
+
id: {
|
|
18
|
+
label: "ID de solicitud"
|
|
19
|
+
},
|
|
20
|
+
organization_id: {
|
|
21
|
+
label: "Organización",
|
|
22
|
+
help: "Tenant que posee esta solicitud de aprobación (propagado desde el contexto del solicitante)."
|
|
23
|
+
},
|
|
24
|
+
process_name: {
|
|
25
|
+
label: "Origen",
|
|
26
|
+
help: "Origen de la solicitud — `flow:<flowName|nodeId>` para aprobaciones por nodo"
|
|
27
|
+
},
|
|
28
|
+
object_name: {
|
|
29
|
+
label: "Objeto"
|
|
30
|
+
},
|
|
31
|
+
record_id: {
|
|
32
|
+
label: "ID de registro"
|
|
33
|
+
},
|
|
34
|
+
submitter_id: {
|
|
35
|
+
label: "Solicitante"
|
|
36
|
+
},
|
|
37
|
+
submitter_comment: {
|
|
38
|
+
label: "Comentario del solicitante"
|
|
39
|
+
},
|
|
40
|
+
status: {
|
|
41
|
+
label: "Estado",
|
|
42
|
+
help: "Estado del ciclo de vida de la solicitud.",
|
|
43
|
+
options: {
|
|
44
|
+
pending: "Pendiente",
|
|
45
|
+
approved: "Aprobada",
|
|
46
|
+
rejected: "Rechazada",
|
|
47
|
+
recalled: "Retirada"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
current_step: {
|
|
51
|
+
label: "Paso actual",
|
|
52
|
+
help: "Nombre técnico del paso pendiente de aprobación."
|
|
53
|
+
},
|
|
54
|
+
current_step_index: {
|
|
55
|
+
label: "Índice del paso actual"
|
|
56
|
+
},
|
|
57
|
+
pending_approvers: {
|
|
58
|
+
label: "Aprobadores pendientes",
|
|
59
|
+
help: "ID de usuario separados por comas que pueden actuar en el paso actual."
|
|
60
|
+
},
|
|
61
|
+
payload_json: {
|
|
62
|
+
label: "Instantánea",
|
|
63
|
+
help: "Instantánea del registro en el momento del envío."
|
|
64
|
+
},
|
|
65
|
+
flow_run_id: {
|
|
66
|
+
label: "Flow Run",
|
|
67
|
+
help: "Suspended automation run id this request gates (ADR-0019). The decision resumes it."
|
|
68
|
+
},
|
|
69
|
+
flow_node_id: {
|
|
70
|
+
label: "Flow Node",
|
|
71
|
+
help: "Approval node id within the flow that opened this request (ADR-0019)."
|
|
72
|
+
},
|
|
73
|
+
node_config_json: {
|
|
74
|
+
label: "Node Config",
|
|
75
|
+
help: "Snapshot of the Approval node config (approvers/behavior) for node-driven requests (ADR-0019)."
|
|
76
|
+
},
|
|
77
|
+
completed_at: {
|
|
78
|
+
label: "Completado el"
|
|
79
|
+
},
|
|
80
|
+
created_at: {
|
|
81
|
+
label: "Creado el"
|
|
82
|
+
},
|
|
83
|
+
updated_at: {
|
|
84
|
+
label: "Actualizado el"
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
_views: {
|
|
88
|
+
my_pending: {
|
|
89
|
+
label: "Mis pendientes"
|
|
90
|
+
},
|
|
91
|
+
submitted_by_me: {
|
|
92
|
+
label: "Enviadas por mí"
|
|
93
|
+
},
|
|
94
|
+
completed: {
|
|
95
|
+
label: "Completadas"
|
|
96
|
+
},
|
|
97
|
+
all_requests: {
|
|
98
|
+
label: "Todas"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
sys_approval_action: {
|
|
103
|
+
label: "Acción de aprobación",
|
|
104
|
+
pluralLabel: "Acciones de aprobación",
|
|
105
|
+
description: "Registro de auditoría append-only para acciones de aprobación",
|
|
106
|
+
fields: {
|
|
107
|
+
id: {
|
|
108
|
+
label: "ID de acción"
|
|
109
|
+
},
|
|
110
|
+
organization_id: {
|
|
111
|
+
label: "Organización",
|
|
112
|
+
help: "Tenant que posee esta acción (refleja la solicitud principal)."
|
|
113
|
+
},
|
|
114
|
+
request_id: {
|
|
115
|
+
label: "Solicitud"
|
|
116
|
+
},
|
|
117
|
+
step_name: {
|
|
118
|
+
label: "Paso",
|
|
119
|
+
help: "Nombre técnico del paso en el momento de la acción."
|
|
120
|
+
},
|
|
121
|
+
step_index: {
|
|
122
|
+
label: "Índice del paso"
|
|
123
|
+
},
|
|
124
|
+
action: {
|
|
125
|
+
label: "Acción",
|
|
126
|
+
options: {
|
|
127
|
+
submit: "Enviar",
|
|
128
|
+
approve: "Aprobar",
|
|
129
|
+
reject: "Rechazar",
|
|
130
|
+
recall: "Retirar",
|
|
131
|
+
escalate: "Escalar"
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
actor_id: {
|
|
135
|
+
label: "Actor"
|
|
136
|
+
},
|
|
137
|
+
comment: {
|
|
138
|
+
label: "Comentario"
|
|
139
|
+
},
|
|
140
|
+
created_at: {
|
|
141
|
+
label: "Creado el"
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
_views: {
|
|
145
|
+
recent: {
|
|
146
|
+
label: "Recientes"
|
|
147
|
+
},
|
|
148
|
+
by_actor: {
|
|
149
|
+
label: "Por actor"
|
|
150
|
+
},
|
|
151
|
+
all_actions: {
|
|
152
|
+
label: "Todas"
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ApprovalsTranslations — i18n bundle owned by this plugin (ADR-0029 D8).
|
|
5
|
+
*
|
|
6
|
+
* Object label/field/view/action translations for the sys_* objects this
|
|
7
|
+
* plugin owns. Loaded at runtime via the plugin's `kernel:ready` hook
|
|
8
|
+
* (`i18n.loadTranslations`). Regenerate with `os i18n extract` against
|
|
9
|
+
* `scripts/i18n-extract.config.ts`.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { TranslationBundle } from '@objectstack/spec/system';
|
|
13
|
+
import { enObjects } from './en.objects.generated.js';
|
|
14
|
+
import { zhCNObjects } from './zh-CN.objects.generated.js';
|
|
15
|
+
import { jaJPObjects } from './ja-JP.objects.generated.js';
|
|
16
|
+
import { esESObjects } from './es-ES.objects.generated.js';
|
|
17
|
+
|
|
18
|
+
export const ApprovalsTranslations: TranslationBundle = {
|
|
19
|
+
en: { objects: enObjects },
|
|
20
|
+
'zh-CN': { objects: zhCNObjects },
|
|
21
|
+
'ja-JP': { objects: jaJPObjects },
|
|
22
|
+
'es-ES': { objects: esESObjects },
|
|
23
|
+
};
|