@objectstack/platform-objects 7.2.1 → 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/dist/apps/index.d.mts +45 -19
- package/dist/apps/index.d.ts +45 -19
- package/dist/apps/index.js +1443 -3403
- package/dist/apps/index.js.map +1 -1
- package/dist/apps/index.mjs +1443 -3404
- package/dist/apps/index.mjs.map +1 -1
- package/dist/audit/index.d.mts +4595 -26176
- package/dist/audit/index.d.ts +4595 -26176
- package/dist/audit/index.js +63 -1063
- package/dist/audit/index.js.map +1 -1
- package/dist/audit/index.mjs +64 -1057
- package/dist/audit/index.mjs.map +1 -1
- package/dist/identity/index.d.mts +839 -1281
- package/dist/identity/index.d.ts +839 -1281
- package/dist/index.d.mts +3 -8
- package/dist/index.d.ts +3 -8
- package/dist/index.js +2350 -6776
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2349 -6761
- package/dist/index.mjs.map +1 -1
- package/dist/integration/index.d.mts +1 -2947
- package/dist/integration/index.d.ts +1 -2947
- package/dist/integration/index.js +0 -136
- package/dist/integration/index.js.map +1 -1
- package/dist/integration/index.mjs +0 -135
- package/dist/integration/index.mjs.map +1 -1
- package/dist/metadata/index.d.mts +3924 -261
- package/dist/metadata/index.d.ts +3924 -261
- package/dist/metadata/index.js +101 -0
- package/dist/metadata/index.js.map +1 -1
- package/dist/metadata/index.mjs +101 -1
- package/dist/metadata/index.mjs.map +1 -1
- package/dist/metadata-translations/index.js +597 -505
- package/dist/metadata-translations/index.js.map +1 -1
- package/dist/metadata-translations/index.mjs +597 -505
- package/dist/metadata-translations/index.mjs.map +1 -1
- package/dist/plugin.js +1790 -3614
- package/dist/plugin.js.map +1 -1
- package/dist/plugin.mjs +1790 -3614
- package/dist/plugin.mjs.map +1 -1
- package/dist/security/index.d.mts +1 -18850
- package/dist/security/index.d.ts +1 -18850
- package/dist/security/index.js +0 -1531
- package/dist/security/index.js.map +1 -1
- package/dist/security/index.mjs +0 -1523
- package/dist/security/index.mjs.map +1 -1
- package/dist/system/index.d.mts +144 -212
- package/dist/system/index.d.ts +144 -212
- package/package.json +2 -2
- package/dist/state-machine.zod-BNanU03M.d-Ek3_yo9P.d.mts +0 -41
- package/dist/state-machine.zod-BNanU03M.d-Ek3_yo9P.d.ts +0 -41
package/dist/audit/index.mjs
CHANGED
|
@@ -1,540 +1,114 @@
|
|
|
1
1
|
import { ObjectSchema, Field } from '@objectstack/spec/data';
|
|
2
2
|
|
|
3
|
-
// src/audit/sys-
|
|
4
|
-
var
|
|
5
|
-
name: "
|
|
6
|
-
label: "
|
|
7
|
-
pluralLabel: "
|
|
8
|
-
icon: "
|
|
3
|
+
// src/audit/sys-notification.object.ts
|
|
4
|
+
var SysNotification = ObjectSchema.create({
|
|
5
|
+
name: "sys_notification",
|
|
6
|
+
label: "Notification Event",
|
|
7
|
+
pluralLabel: "Notification Events",
|
|
8
|
+
icon: "bell",
|
|
9
9
|
isSystem: true,
|
|
10
|
-
managedBy: "
|
|
11
|
-
description: "
|
|
12
|
-
displayNameField: "
|
|
13
|
-
titleFormat: "{
|
|
14
|
-
compactLayout: ["
|
|
10
|
+
managedBy: "system",
|
|
11
|
+
description: "Notification events \u2014 one row per emit() (ADR-0030 Layer 2 ingress)",
|
|
12
|
+
displayNameField: "topic",
|
|
13
|
+
titleFormat: "{topic}",
|
|
14
|
+
compactLayout: ["topic", "severity", "source_object", "created_at"],
|
|
15
15
|
listViews: {
|
|
16
16
|
recent: {
|
|
17
17
|
type: "grid",
|
|
18
18
|
name: "recent",
|
|
19
19
|
label: "Recent",
|
|
20
|
-
data: { provider: "object", object: "
|
|
21
|
-
columns: ["
|
|
20
|
+
data: { provider: "object", object: "sys_notification" },
|
|
21
|
+
columns: ["topic", "severity", "actor_id", "source_object", "created_at"],
|
|
22
22
|
sort: [{ field: "created_at", order: "desc" }],
|
|
23
23
|
pagination: { pageSize: 50 },
|
|
24
|
-
emptyState: { title: "No
|
|
25
|
-
},
|
|
26
|
-
writes_only: {
|
|
27
|
-
type: "grid",
|
|
28
|
-
name: "writes_only",
|
|
29
|
-
label: "Writes",
|
|
30
|
-
data: { provider: "object", object: "sys_audit_log" },
|
|
31
|
-
columns: ["created_at", "action", "object_name", "record_id", "user_id"],
|
|
32
|
-
filter: [{ field: "action", operator: "in", value: ["create", "update", "delete", "restore"] }],
|
|
33
|
-
sort: [{ field: "created_at", order: "desc" }],
|
|
34
|
-
pagination: { pageSize: 50 }
|
|
35
|
-
},
|
|
36
|
-
auth_events: {
|
|
37
|
-
type: "grid",
|
|
38
|
-
name: "auth_events",
|
|
39
|
-
label: "Auth",
|
|
40
|
-
data: { provider: "object", object: "sys_audit_log" },
|
|
41
|
-
columns: ["created_at", "action", "user_id"],
|
|
42
|
-
filter: [{ field: "action", operator: "in", value: ["login", "logout", "permission_change"] }],
|
|
43
|
-
sort: [{ field: "created_at", order: "desc" }],
|
|
44
|
-
pagination: { pageSize: 50 }
|
|
24
|
+
emptyState: { title: "No events", message: "No notification events have been emitted." }
|
|
45
25
|
},
|
|
46
|
-
|
|
26
|
+
by_topic: {
|
|
47
27
|
type: "grid",
|
|
48
|
-
name: "
|
|
49
|
-
label: "
|
|
50
|
-
data: { provider: "object", object: "
|
|
51
|
-
columns: ["
|
|
52
|
-
filter: [{ field: "action", operator: "in", value: ["config_change", "export", "import"] }],
|
|
53
|
-
sort: [{ field: "created_at", order: "desc" }],
|
|
54
|
-
pagination: { pageSize: 50 }
|
|
55
|
-
},
|
|
56
|
-
all_events: {
|
|
57
|
-
type: "grid",
|
|
58
|
-
name: "all_events",
|
|
59
|
-
label: "All",
|
|
60
|
-
data: { provider: "object", object: "sys_audit_log" },
|
|
61
|
-
columns: ["created_at", "action", "object_name", "record_id", "user_id"],
|
|
28
|
+
name: "by_topic",
|
|
29
|
+
label: "By Topic",
|
|
30
|
+
data: { provider: "object", object: "sys_notification" },
|
|
31
|
+
columns: ["topic", "severity", "source_object", "source_id", "created_at"],
|
|
62
32
|
sort: [{ field: "created_at", order: "desc" }],
|
|
63
|
-
pagination: { pageSize: 100 }
|
|
33
|
+
pagination: { pageSize: 100 },
|
|
34
|
+
grouping: { fields: [{ field: "topic", order: "asc", collapsed: false }] }
|
|
64
35
|
}
|
|
65
36
|
},
|
|
66
37
|
fields: {
|
|
67
|
-
// ── Event ────────────────────────────────────────────────────
|
|
68
|
-
created_at: Field.datetime({
|
|
69
|
-
label: "Timestamp",
|
|
70
|
-
required: true,
|
|
71
|
-
defaultValue: "NOW()",
|
|
72
|
-
readonly: true,
|
|
73
|
-
group: "Event"
|
|
74
|
-
}),
|
|
75
|
-
action: Field.select(
|
|
76
|
-
["create", "update", "delete", "restore", "login", "logout", "permission_change", "config_change", "export", "import"],
|
|
77
|
-
{
|
|
78
|
-
label: "Action",
|
|
79
|
-
required: true,
|
|
80
|
-
readonly: true,
|
|
81
|
-
searchable: true,
|
|
82
|
-
description: "Action type (snake_case)",
|
|
83
|
-
group: "Event"
|
|
84
|
-
}
|
|
85
|
-
),
|
|
86
|
-
user_id: Field.lookup("sys_user", {
|
|
87
|
-
label: "Actor",
|
|
88
|
-
required: false,
|
|
89
|
-
readonly: true,
|
|
90
|
-
searchable: true,
|
|
91
|
-
description: "User who performed the action (null for system actions)",
|
|
92
|
-
group: "Event"
|
|
93
|
-
}),
|
|
94
|
-
// ── Target record ────────────────────────────────────────────
|
|
95
|
-
object_name: Field.text({
|
|
96
|
-
label: "Object",
|
|
97
|
-
required: false,
|
|
98
|
-
readonly: true,
|
|
99
|
-
searchable: true,
|
|
100
|
-
maxLength: 255,
|
|
101
|
-
description: "Target object (e.g. sys_user, project_task)",
|
|
102
|
-
group: "Target"
|
|
103
|
-
}),
|
|
104
|
-
record_id: Field.text({
|
|
105
|
-
label: "Record ID",
|
|
106
|
-
required: false,
|
|
107
|
-
readonly: true,
|
|
108
|
-
searchable: true,
|
|
109
|
-
description: "ID of the affected record",
|
|
110
|
-
group: "Target"
|
|
111
|
-
}),
|
|
112
|
-
// ── Change payload ───────────────────────────────────────────
|
|
113
|
-
old_value: Field.textarea({
|
|
114
|
-
label: "Old Value",
|
|
115
|
-
required: false,
|
|
116
|
-
readonly: true,
|
|
117
|
-
description: "JSON-serialized previous state",
|
|
118
|
-
group: "Changes"
|
|
119
|
-
}),
|
|
120
|
-
new_value: Field.textarea({
|
|
121
|
-
label: "New Value",
|
|
122
|
-
required: false,
|
|
123
|
-
readonly: true,
|
|
124
|
-
description: "JSON-serialized new state",
|
|
125
|
-
group: "Changes"
|
|
126
|
-
}),
|
|
127
|
-
// ── Client fingerprint ───────────────────────────────────────
|
|
128
|
-
ip_address: Field.text({
|
|
129
|
-
label: "IP Address",
|
|
130
|
-
required: false,
|
|
131
|
-
readonly: true,
|
|
132
|
-
maxLength: 45,
|
|
133
|
-
group: "Client"
|
|
134
|
-
}),
|
|
135
|
-
user_agent: Field.textarea({
|
|
136
|
-
label: "User Agent",
|
|
137
|
-
required: false,
|
|
138
|
-
readonly: true,
|
|
139
|
-
group: "Client"
|
|
140
|
-
}),
|
|
141
|
-
// ── Context ──────────────────────────────────────────────────
|
|
142
|
-
tenant_id: Field.lookup("sys_organization", {
|
|
143
|
-
label: "Tenant",
|
|
144
|
-
required: false,
|
|
145
|
-
readonly: true,
|
|
146
|
-
description: "Tenant context for multi-tenant isolation",
|
|
147
|
-
group: "Context"
|
|
148
|
-
}),
|
|
149
|
-
metadata: Field.textarea({
|
|
150
|
-
label: "Metadata",
|
|
151
|
-
required: false,
|
|
152
|
-
readonly: true,
|
|
153
|
-
description: "JSON-serialized additional context",
|
|
154
|
-
group: "Context"
|
|
155
|
-
}),
|
|
156
|
-
// ── System ───────────────────────────────────────────────────
|
|
157
38
|
id: Field.text({
|
|
158
|
-
label: "
|
|
159
|
-
required: true,
|
|
160
|
-
readonly: true,
|
|
161
|
-
group: "System"
|
|
162
|
-
})
|
|
163
|
-
},
|
|
164
|
-
indexes: [
|
|
165
|
-
{ fields: ["created_at"] },
|
|
166
|
-
{ fields: ["user_id"] },
|
|
167
|
-
{ fields: ["object_name", "record_id"] },
|
|
168
|
-
{ fields: ["action"] },
|
|
169
|
-
{ fields: ["tenant_id"] }
|
|
170
|
-
],
|
|
171
|
-
enable: {
|
|
172
|
-
trackHistory: false,
|
|
173
|
-
// Audit logs are themselves the audit trail
|
|
174
|
-
searchable: true,
|
|
175
|
-
apiEnabled: true,
|
|
176
|
-
apiMethods: ["get", "list"],
|
|
177
|
-
// Read-only — creation happens via internal system hooks only
|
|
178
|
-
trash: false,
|
|
179
|
-
// Never soft-delete audit logs
|
|
180
|
-
mru: false,
|
|
181
|
-
clone: false
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
var SysPresence = ObjectSchema.create({
|
|
185
|
-
name: "sys_presence",
|
|
186
|
-
label: "Presence",
|
|
187
|
-
pluralLabel: "Presences",
|
|
188
|
-
icon: "wifi",
|
|
189
|
-
isSystem: true,
|
|
190
|
-
managedBy: "append-only",
|
|
191
|
-
description: "Real-time user presence and activity tracking",
|
|
192
|
-
titleFormat: "{user_id} ({status})",
|
|
193
|
-
compactLayout: ["user_id", "status", "last_seen"],
|
|
194
|
-
fields: {
|
|
195
|
-
id: Field.text({
|
|
196
|
-
label: "Presence ID",
|
|
197
|
-
required: true,
|
|
198
|
-
readonly: true
|
|
199
|
-
}),
|
|
200
|
-
created_at: Field.datetime({
|
|
201
|
-
label: "Created At",
|
|
202
|
-
defaultValue: "NOW()",
|
|
203
|
-
readonly: true
|
|
204
|
-
}),
|
|
205
|
-
updated_at: Field.datetime({
|
|
206
|
-
label: "Updated At",
|
|
207
|
-
defaultValue: "NOW()",
|
|
208
|
-
readonly: true
|
|
209
|
-
}),
|
|
210
|
-
user_id: Field.lookup("sys_user", {
|
|
211
|
-
label: "User",
|
|
212
|
-
required: true,
|
|
213
|
-
searchable: true
|
|
214
|
-
}),
|
|
215
|
-
session_id: Field.lookup("sys_session", {
|
|
216
|
-
label: "Session",
|
|
217
|
-
required: true
|
|
218
|
-
}),
|
|
219
|
-
status: Field.select({
|
|
220
|
-
label: "Status",
|
|
221
|
-
required: true,
|
|
222
|
-
defaultValue: "online",
|
|
223
|
-
options: [
|
|
224
|
-
{ value: "online", label: "Online" },
|
|
225
|
-
{ value: "away", label: "Away" },
|
|
226
|
-
{ value: "busy", label: "Busy" },
|
|
227
|
-
{ value: "offline", label: "Offline" }
|
|
228
|
-
]
|
|
229
|
-
}),
|
|
230
|
-
last_seen: Field.datetime({
|
|
231
|
-
label: "Last Seen",
|
|
232
|
-
required: true,
|
|
233
|
-
defaultValue: "NOW()"
|
|
234
|
-
}),
|
|
235
|
-
current_location: Field.text({
|
|
236
|
-
label: "Current Location",
|
|
237
|
-
required: false,
|
|
238
|
-
maxLength: 500
|
|
239
|
-
}),
|
|
240
|
-
device: Field.select({
|
|
241
|
-
label: "Device",
|
|
242
|
-
required: false,
|
|
243
|
-
options: [
|
|
244
|
-
{ value: "desktop", label: "Desktop" },
|
|
245
|
-
{ value: "mobile", label: "Mobile" },
|
|
246
|
-
{ value: "tablet", label: "Tablet" },
|
|
247
|
-
{ value: "other", label: "Other" }
|
|
248
|
-
]
|
|
249
|
-
}),
|
|
250
|
-
custom_status: Field.text({
|
|
251
|
-
label: "Custom Status",
|
|
252
|
-
required: false,
|
|
253
|
-
maxLength: 255
|
|
254
|
-
}),
|
|
255
|
-
metadata: Field.json({
|
|
256
|
-
label: "Metadata",
|
|
257
|
-
required: false,
|
|
258
|
-
description: "Arbitrary JSON metadata associated with the presence state (matches PresenceStateSchema.metadata)."
|
|
259
|
-
})
|
|
260
|
-
},
|
|
261
|
-
indexes: [
|
|
262
|
-
{ fields: ["user_id"], unique: false },
|
|
263
|
-
{ fields: ["session_id"], unique: true },
|
|
264
|
-
{ fields: ["status"], unique: false }
|
|
265
|
-
],
|
|
266
|
-
enable: {
|
|
267
|
-
trackHistory: false,
|
|
268
|
-
searchable: false,
|
|
269
|
-
apiEnabled: true,
|
|
270
|
-
apiMethods: ["get", "list", "create", "update", "delete"],
|
|
271
|
-
trash: false,
|
|
272
|
-
mru: false
|
|
273
|
-
}
|
|
274
|
-
});
|
|
275
|
-
var SysActivity = ObjectSchema.create({
|
|
276
|
-
name: "sys_activity",
|
|
277
|
-
label: "Activity",
|
|
278
|
-
pluralLabel: "Activities",
|
|
279
|
-
icon: "activity",
|
|
280
|
-
isSystem: true,
|
|
281
|
-
managedBy: "append-only",
|
|
282
|
-
description: "Recent activity stream entries (lightweight, denormalized)",
|
|
283
|
-
displayNameField: "summary",
|
|
284
|
-
titleFormat: "{type} \xB7 {summary}",
|
|
285
|
-
compactLayout: ["timestamp", "type", "actor_name", "summary"],
|
|
286
|
-
fields: {
|
|
287
|
-
id: Field.text({
|
|
288
|
-
label: "Activity ID",
|
|
39
|
+
label: "Notification ID",
|
|
289
40
|
required: true,
|
|
290
41
|
readonly: true,
|
|
291
42
|
group: "System"
|
|
292
43
|
}),
|
|
293
|
-
|
|
294
|
-
|
|
44
|
+
// ── Event identity ───────────────────────────────────────────
|
|
45
|
+
topic: Field.text({
|
|
46
|
+
label: "Topic",
|
|
295
47
|
required: true,
|
|
296
|
-
|
|
297
|
-
readonly: true,
|
|
298
|
-
group: "Event"
|
|
299
|
-
}),
|
|
300
|
-
type: Field.select(
|
|
301
|
-
[
|
|
302
|
-
"created",
|
|
303
|
-
"updated",
|
|
304
|
-
"deleted",
|
|
305
|
-
"commented",
|
|
306
|
-
"mentioned",
|
|
307
|
-
"shared",
|
|
308
|
-
"assigned",
|
|
309
|
-
"completed",
|
|
310
|
-
"login",
|
|
311
|
-
"logout",
|
|
312
|
-
"system"
|
|
313
|
-
],
|
|
314
|
-
{
|
|
315
|
-
label: "Type",
|
|
316
|
-
required: true,
|
|
317
|
-
readonly: true,
|
|
318
|
-
searchable: true,
|
|
319
|
-
group: "Event"
|
|
320
|
-
}
|
|
321
|
-
),
|
|
322
|
-
summary: Field.text({
|
|
323
|
-
label: "Summary",
|
|
324
|
-
required: true,
|
|
325
|
-
readonly: true,
|
|
326
|
-
maxLength: 500,
|
|
48
|
+
maxLength: 200,
|
|
327
49
|
searchable: true,
|
|
328
|
-
description: "
|
|
50
|
+
description: "Notification topic, e.g. task.assigned, collab.mention",
|
|
329
51
|
group: "Event"
|
|
330
52
|
}),
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
label: "Actor",
|
|
334
|
-
required: false,
|
|
335
|
-
readonly: true,
|
|
336
|
-
searchable: true,
|
|
337
|
-
group: "Actor"
|
|
338
|
-
}),
|
|
339
|
-
actor_name: Field.text({
|
|
340
|
-
label: "Actor Name",
|
|
341
|
-
required: false,
|
|
342
|
-
readonly: true,
|
|
343
|
-
group: "Actor"
|
|
344
|
-
}),
|
|
345
|
-
actor_avatar_url: Field.url({
|
|
346
|
-
label: "Actor Avatar",
|
|
347
|
-
required: false,
|
|
348
|
-
readonly: true,
|
|
349
|
-
group: "Actor"
|
|
350
|
-
}),
|
|
351
|
-
// ── Target ───────────────────────────────────────────────────
|
|
352
|
-
object_name: Field.text({
|
|
353
|
-
label: "Object",
|
|
354
|
-
required: false,
|
|
355
|
-
readonly: true,
|
|
356
|
-
searchable: true,
|
|
357
|
-
maxLength: 255,
|
|
358
|
-
description: "Target object short name (e.g. account, sys_user)",
|
|
359
|
-
group: "Target"
|
|
360
|
-
}),
|
|
361
|
-
record_id: Field.text({
|
|
362
|
-
label: "Record ID",
|
|
363
|
-
required: false,
|
|
364
|
-
readonly: true,
|
|
365
|
-
searchable: true,
|
|
366
|
-
group: "Target"
|
|
367
|
-
}),
|
|
368
|
-
record_label: Field.text({
|
|
369
|
-
label: "Record Label",
|
|
53
|
+
payload: Field.json({
|
|
54
|
+
label: "Payload",
|
|
370
55
|
required: false,
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
description: "Display label of the target record at write time",
|
|
374
|
-
group: "Target"
|
|
375
|
-
}),
|
|
376
|
-
url: Field.url({
|
|
377
|
-
label: "URL",
|
|
378
|
-
required: false,
|
|
379
|
-
readonly: true,
|
|
380
|
-
description: "Optional deep-link to the activity target",
|
|
381
|
-
group: "Target"
|
|
56
|
+
description: "Template inputs carried to channels (title/body/url/actor/source/\u2026)",
|
|
57
|
+
group: "Event"
|
|
382
58
|
}),
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
label: "Environment",
|
|
59
|
+
severity: Field.select(["info", "warning", "critical"], {
|
|
60
|
+
label: "Severity",
|
|
386
61
|
required: false,
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
group: "Context"
|
|
62
|
+
defaultValue: "info",
|
|
63
|
+
description: "Severity hint for rendering / filtering",
|
|
64
|
+
group: "Event"
|
|
391
65
|
}),
|
|
392
|
-
|
|
393
|
-
label: "
|
|
66
|
+
dedup_key: Field.text({
|
|
67
|
+
label: "Dedup Key",
|
|
394
68
|
required: false,
|
|
395
|
-
readonly: true,
|
|
396
|
-
description: "JSON-serialized additional context",
|
|
397
|
-
group: "Context"
|
|
398
|
-
})
|
|
399
|
-
},
|
|
400
|
-
indexes: [
|
|
401
|
-
{ fields: ["timestamp"] },
|
|
402
|
-
{ fields: ["actor_id"] },
|
|
403
|
-
{ fields: ["object_name", "record_id"] },
|
|
404
|
-
{ fields: ["type"] },
|
|
405
|
-
{ fields: ["environment_id"] }
|
|
406
|
-
],
|
|
407
|
-
enable: {
|
|
408
|
-
trackHistory: false,
|
|
409
|
-
searchable: true,
|
|
410
|
-
apiEnabled: true,
|
|
411
|
-
apiMethods: ["get", "list"],
|
|
412
|
-
trash: false,
|
|
413
|
-
mru: false,
|
|
414
|
-
clone: false
|
|
415
|
-
}
|
|
416
|
-
});
|
|
417
|
-
var SysComment = ObjectSchema.create({
|
|
418
|
-
name: "sys_comment",
|
|
419
|
-
label: "Comment",
|
|
420
|
-
pluralLabel: "Comments",
|
|
421
|
-
icon: "message-square",
|
|
422
|
-
isSystem: true,
|
|
423
|
-
managedBy: "platform",
|
|
424
|
-
description: "Threaded comments attached to records via thread_id",
|
|
425
|
-
displayNameField: "body",
|
|
426
|
-
titleFormat: "{author_name}: {body}",
|
|
427
|
-
compactLayout: ["created_at", "author_name", "body"],
|
|
428
|
-
fields: {
|
|
429
|
-
id: Field.text({
|
|
430
|
-
label: "Comment ID",
|
|
431
|
-
required: true,
|
|
432
|
-
readonly: true,
|
|
433
|
-
group: "System"
|
|
434
|
-
}),
|
|
435
|
-
// ── Thread ───────────────────────────────────────────────────
|
|
436
|
-
thread_id: Field.text({
|
|
437
|
-
label: "Thread",
|
|
438
|
-
required: true,
|
|
439
|
-
searchable: true,
|
|
440
69
|
maxLength: 255,
|
|
441
|
-
description: "
|
|
442
|
-
group: "
|
|
443
|
-
}),
|
|
444
|
-
parent_id: Field.lookup("sys_comment", {
|
|
445
|
-
label: "Parent Comment",
|
|
446
|
-
required: false,
|
|
447
|
-
description: "Optional parent comment for nested replies",
|
|
448
|
-
group: "Thread"
|
|
449
|
-
}),
|
|
450
|
-
reply_count: Field.number({
|
|
451
|
-
label: "Reply Count",
|
|
452
|
-
defaultValue: 0,
|
|
453
|
-
readonly: true,
|
|
454
|
-
group: "Thread"
|
|
455
|
-
}),
|
|
456
|
-
// ── Author ───────────────────────────────────────────────────
|
|
457
|
-
author_id: Field.lookup("sys_user", {
|
|
458
|
-
label: "Author",
|
|
459
|
-
required: true,
|
|
460
|
-
searchable: true,
|
|
461
|
-
group: "Author"
|
|
462
|
-
}),
|
|
463
|
-
author_name: Field.text({
|
|
464
|
-
label: "Author Name",
|
|
465
|
-
required: false,
|
|
466
|
-
group: "Author"
|
|
70
|
+
description: "Idempotency key within a topic window; a repeat emit is a no-op",
|
|
71
|
+
group: "Event"
|
|
467
72
|
}),
|
|
468
|
-
|
|
469
|
-
|
|
73
|
+
// ── Source linkage ───────────────────────────────────────────
|
|
74
|
+
source_object: Field.text({
|
|
75
|
+
label: "Source Object",
|
|
470
76
|
required: false,
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
body: Field.textarea({
|
|
475
|
-
label: "Body",
|
|
476
|
-
required: true,
|
|
477
|
-
searchable: true,
|
|
478
|
-
description: "Comment text (Markdown supported)",
|
|
479
|
-
group: "Body"
|
|
77
|
+
maxLength: 100,
|
|
78
|
+
description: "Object name of the related record (e.g. lead, opportunity)",
|
|
79
|
+
group: "Source"
|
|
480
80
|
}),
|
|
481
|
-
|
|
482
|
-
label: "
|
|
81
|
+
source_id: Field.text({
|
|
82
|
+
label: "Source Record",
|
|
483
83
|
required: false,
|
|
484
|
-
|
|
485
|
-
|
|
84
|
+
maxLength: 100,
|
|
85
|
+
description: "Record id within source_object",
|
|
86
|
+
group: "Source"
|
|
486
87
|
}),
|
|
487
|
-
|
|
488
|
-
label: "
|
|
88
|
+
actor_id: Field.lookup("sys_user", {
|
|
89
|
+
label: "Actor",
|
|
489
90
|
required: false,
|
|
490
|
-
description: "
|
|
491
|
-
group: "
|
|
91
|
+
description: "User who caused the event (mentioner, assigner)",
|
|
92
|
+
group: "Source"
|
|
492
93
|
}),
|
|
493
94
|
// ── Lifecycle ────────────────────────────────────────────────
|
|
494
|
-
is_edited: Field.boolean({
|
|
495
|
-
label: "Edited",
|
|
496
|
-
defaultValue: false,
|
|
497
|
-
group: "Lifecycle"
|
|
498
|
-
}),
|
|
499
|
-
edited_at: Field.datetime({
|
|
500
|
-
label: "Edited At",
|
|
501
|
-
required: false,
|
|
502
|
-
group: "Lifecycle"
|
|
503
|
-
}),
|
|
504
|
-
visibility: Field.select(
|
|
505
|
-
["public", "internal", "private"],
|
|
506
|
-
{
|
|
507
|
-
label: "Visibility",
|
|
508
|
-
defaultValue: "public",
|
|
509
|
-
group: "Lifecycle"
|
|
510
|
-
}
|
|
511
|
-
),
|
|
512
95
|
created_at: Field.datetime({
|
|
513
96
|
label: "Created At",
|
|
514
97
|
required: true,
|
|
515
98
|
defaultValue: "NOW()",
|
|
516
99
|
readonly: true,
|
|
517
100
|
group: "System"
|
|
518
|
-
}),
|
|
519
|
-
updated_at: Field.datetime({
|
|
520
|
-
label: "Updated At",
|
|
521
|
-
required: false,
|
|
522
|
-
group: "System"
|
|
523
101
|
})
|
|
524
102
|
},
|
|
525
103
|
indexes: [
|
|
526
|
-
{ fields: ["
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
trash: true,
|
|
535
|
-
mru: false,
|
|
536
|
-
clone: false
|
|
537
|
-
}
|
|
104
|
+
{ fields: ["topic", "created_at"] },
|
|
105
|
+
// Idempotency spine (ADR-0030). UNIQUE so `emit()` dedup is race-safe: a
|
|
106
|
+
// concurrent emit with the same dedup_key loses the insert and converges to
|
|
107
|
+
// the winner (mirrors the delivery outbox). SQL treats NULLs as distinct, so
|
|
108
|
+
// the (common) events with no dedup_key are unconstrained.
|
|
109
|
+
{ fields: ["dedup_key"], unique: true },
|
|
110
|
+
{ fields: ["source_object", "source_id"] }
|
|
111
|
+
]
|
|
538
112
|
});
|
|
539
113
|
var SysAttachment = ObjectSchema.create({
|
|
540
114
|
name: "sys_attachment",
|
|
@@ -653,202 +227,6 @@ var SysAttachment = ObjectSchema.create({
|
|
|
653
227
|
clone: false
|
|
654
228
|
}
|
|
655
229
|
});
|
|
656
|
-
var SysNotification = ObjectSchema.create({
|
|
657
|
-
name: "sys_notification",
|
|
658
|
-
label: "Notification",
|
|
659
|
-
pluralLabel: "Notifications",
|
|
660
|
-
icon: "bell",
|
|
661
|
-
isSystem: true,
|
|
662
|
-
managedBy: "system",
|
|
663
|
-
description: "Per-user notification inbox entries",
|
|
664
|
-
displayNameField: "title",
|
|
665
|
-
titleFormat: "{title}",
|
|
666
|
-
compactLayout: ["title", "type", "is_read", "created_at"],
|
|
667
|
-
/**
|
|
668
|
-
* Row-level inbox actions. Use `visible` CEL expressions to ensure
|
|
669
|
-
* `mark_read` only shows on unread rows and vice-versa, mirroring the
|
|
670
|
-
* mark-as-read affordances in GitHub / Linear inboxes. The toolbar-level
|
|
671
|
-
* `mark_all_read` is intentionally omitted server-side: it requires a
|
|
672
|
-
* bulk update primitive that doesn't yet exist on the REST surface, and
|
|
673
|
-
* the popover already handles the multi-row case client-side via N
|
|
674
|
-
* single-row PATCHes (see `InboxPopover.tsx` -> AppHeader `markAllRead`).
|
|
675
|
-
*/
|
|
676
|
-
actions: [
|
|
677
|
-
{
|
|
678
|
-
name: "mark_read",
|
|
679
|
-
label: "Mark as Read",
|
|
680
|
-
icon: "check",
|
|
681
|
-
variant: "secondary",
|
|
682
|
-
mode: "custom",
|
|
683
|
-
locations: ["list_item"],
|
|
684
|
-
type: "api",
|
|
685
|
-
method: "PATCH",
|
|
686
|
-
target: "/api/v1/data/sys_notification/{id}",
|
|
687
|
-
bodyExtra: { is_read: true },
|
|
688
|
-
visible: "!record.is_read",
|
|
689
|
-
successMessage: "Notification marked as read",
|
|
690
|
-
refreshAfter: true
|
|
691
|
-
},
|
|
692
|
-
{
|
|
693
|
-
name: "mark_unread",
|
|
694
|
-
label: "Mark as Unread",
|
|
695
|
-
icon: "bell-dot",
|
|
696
|
-
variant: "secondary",
|
|
697
|
-
mode: "custom",
|
|
698
|
-
locations: ["list_item"],
|
|
699
|
-
type: "api",
|
|
700
|
-
method: "PATCH",
|
|
701
|
-
target: "/api/v1/data/sys_notification/{id}",
|
|
702
|
-
bodyExtra: { is_read: false, read_at: null },
|
|
703
|
-
visible: "record.is_read",
|
|
704
|
-
successMessage: "Notification marked as unread",
|
|
705
|
-
refreshAfter: true
|
|
706
|
-
}
|
|
707
|
-
],
|
|
708
|
-
listViews: {
|
|
709
|
-
unread: {
|
|
710
|
-
type: "grid",
|
|
711
|
-
name: "unread",
|
|
712
|
-
label: "Unread",
|
|
713
|
-
data: { provider: "object", object: "sys_notification" },
|
|
714
|
-
// Title + actor first (the "who/what" the user actually scans);
|
|
715
|
-
// type stays as a categorising chip; created_at right-aligned.
|
|
716
|
-
columns: ["title", "actor_name", "type", "created_at"],
|
|
717
|
-
filter: [
|
|
718
|
-
{ field: "recipient_id", operator: "equals", value: "{current_user_id}" },
|
|
719
|
-
{ field: "is_read", operator: "equals", value: false }
|
|
720
|
-
],
|
|
721
|
-
sort: [{ field: "created_at", order: "desc" }],
|
|
722
|
-
pagination: { pageSize: 50 },
|
|
723
|
-
emptyState: { title: "Inbox zero", message: "No unread notifications." }
|
|
724
|
-
},
|
|
725
|
-
mine: {
|
|
726
|
-
type: "grid",
|
|
727
|
-
name: "mine",
|
|
728
|
-
label: "Mine",
|
|
729
|
-
data: { provider: "object", object: "sys_notification" },
|
|
730
|
-
columns: ["title", "actor_name", "type", "is_read", "created_at"],
|
|
731
|
-
filter: [{ field: "recipient_id", operator: "equals", value: "{current_user_id}" }],
|
|
732
|
-
sort: [{ field: "created_at", order: "desc" }],
|
|
733
|
-
pagination: { pageSize: 50 },
|
|
734
|
-
// Group by notification category so mention/assignment storms don't
|
|
735
|
-
// hide system or task_due rows. Users still toggle to flat via the
|
|
736
|
-
// toolbar Group control if they prefer chronology only.
|
|
737
|
-
grouping: { fields: [{ field: "type", order: "asc", collapsed: false }] }
|
|
738
|
-
},
|
|
739
|
-
all_notifications: {
|
|
740
|
-
type: "grid",
|
|
741
|
-
name: "all_notifications",
|
|
742
|
-
label: "All",
|
|
743
|
-
data: { provider: "object", object: "sys_notification" },
|
|
744
|
-
columns: ["title", "recipient_id", "actor_name", "type", "is_read", "created_at"],
|
|
745
|
-
sort: [{ field: "created_at", order: "desc" }],
|
|
746
|
-
pagination: { pageSize: 100 }
|
|
747
|
-
}
|
|
748
|
-
},
|
|
749
|
-
fields: {
|
|
750
|
-
id: Field.text({
|
|
751
|
-
label: "Notification ID",
|
|
752
|
-
required: true,
|
|
753
|
-
readonly: true,
|
|
754
|
-
group: "System"
|
|
755
|
-
}),
|
|
756
|
-
// ── Routing ──────────────────────────────────────────────────
|
|
757
|
-
recipient_id: Field.lookup("sys_user", {
|
|
758
|
-
label: "Recipient",
|
|
759
|
-
required: true,
|
|
760
|
-
searchable: true,
|
|
761
|
-
description: "User the notification is delivered to",
|
|
762
|
-
group: "Routing"
|
|
763
|
-
}),
|
|
764
|
-
// ── Content ──────────────────────────────────────────────────
|
|
765
|
-
type: Field.select(
|
|
766
|
-
["mention", "assignment", "comment_reply", "lead_converted", "task_due", "system"],
|
|
767
|
-
{
|
|
768
|
-
label: "Type",
|
|
769
|
-
required: true,
|
|
770
|
-
defaultValue: "system",
|
|
771
|
-
description: "Notification category \u2014 drives icon + sort priority",
|
|
772
|
-
group: "Content"
|
|
773
|
-
}
|
|
774
|
-
),
|
|
775
|
-
title: Field.text({
|
|
776
|
-
label: "Title",
|
|
777
|
-
required: true,
|
|
778
|
-
maxLength: 255,
|
|
779
|
-
searchable: true,
|
|
780
|
-
group: "Content"
|
|
781
|
-
}),
|
|
782
|
-
body: Field.textarea({
|
|
783
|
-
label: "Body",
|
|
784
|
-
required: false,
|
|
785
|
-
description: "Optional secondary text (one-line summary)",
|
|
786
|
-
group: "Content"
|
|
787
|
-
}),
|
|
788
|
-
// ── Source linkage ───────────────────────────────────────────
|
|
789
|
-
source_object: Field.text({
|
|
790
|
-
label: "Source Object",
|
|
791
|
-
required: false,
|
|
792
|
-
maxLength: 100,
|
|
793
|
-
description: "Object name of the related record (e.g. lead, opportunity)",
|
|
794
|
-
group: "Source"
|
|
795
|
-
}),
|
|
796
|
-
source_id: Field.text({
|
|
797
|
-
label: "Source Record",
|
|
798
|
-
required: false,
|
|
799
|
-
maxLength: 100,
|
|
800
|
-
description: "Record id within source_object",
|
|
801
|
-
group: "Source"
|
|
802
|
-
}),
|
|
803
|
-
url: Field.url({
|
|
804
|
-
label: "Deep Link",
|
|
805
|
-
required: false,
|
|
806
|
-
description: "Optional URL to navigate to when clicked",
|
|
807
|
-
group: "Source"
|
|
808
|
-
}),
|
|
809
|
-
actor_id: Field.lookup("sys_user", {
|
|
810
|
-
label: "Actor",
|
|
811
|
-
required: false,
|
|
812
|
-
description: "User who caused the notification (mentioner, assigner)",
|
|
813
|
-
group: "Source"
|
|
814
|
-
}),
|
|
815
|
-
actor_name: Field.text({
|
|
816
|
-
label: "Actor Name",
|
|
817
|
-
required: false,
|
|
818
|
-
group: "Source"
|
|
819
|
-
}),
|
|
820
|
-
// ── Read state ───────────────────────────────────────────────
|
|
821
|
-
is_read: Field.boolean({
|
|
822
|
-
label: "Read",
|
|
823
|
-
defaultValue: false,
|
|
824
|
-
description: "True once recipient acknowledges",
|
|
825
|
-
group: "State"
|
|
826
|
-
}),
|
|
827
|
-
read_at: Field.datetime({
|
|
828
|
-
label: "Read At",
|
|
829
|
-
required: false,
|
|
830
|
-
group: "State"
|
|
831
|
-
}),
|
|
832
|
-
// ── Lifecycle ────────────────────────────────────────────────
|
|
833
|
-
created_at: Field.datetime({
|
|
834
|
-
label: "Created At",
|
|
835
|
-
required: true,
|
|
836
|
-
defaultValue: "NOW()",
|
|
837
|
-
readonly: true,
|
|
838
|
-
group: "System"
|
|
839
|
-
}),
|
|
840
|
-
updated_at: Field.datetime({
|
|
841
|
-
label: "Updated At",
|
|
842
|
-
required: false,
|
|
843
|
-
group: "System"
|
|
844
|
-
})
|
|
845
|
-
},
|
|
846
|
-
indexes: [
|
|
847
|
-
{ fields: ["recipient_id", "is_read", "created_at"] },
|
|
848
|
-
{ fields: ["recipient_id", "created_at"] },
|
|
849
|
-
{ fields: ["source_object", "source_id"] }
|
|
850
|
-
]
|
|
851
|
-
});
|
|
852
230
|
var SysEmail = ObjectSchema.create({
|
|
853
231
|
name: "sys_email",
|
|
854
232
|
label: "Email",
|
|
@@ -1351,377 +729,6 @@ var SysReportSchedule = ObjectSchema.create({
|
|
|
1351
729
|
{ fields: ["owner_id"] }
|
|
1352
730
|
]
|
|
1353
731
|
});
|
|
1354
|
-
var SysApprovalProcess = ObjectSchema.create({
|
|
1355
|
-
name: "sys_approval_process",
|
|
1356
|
-
label: "Approval Process",
|
|
1357
|
-
pluralLabel: "Approval Processes",
|
|
1358
|
-
icon: "check-square",
|
|
1359
|
-
isSystem: true,
|
|
1360
|
-
managedBy: "config",
|
|
1361
|
-
// Authoring an approval process requires a visual step designer that
|
|
1362
|
-
// doesn't yet exist — the embedded `definition_json` textarea would
|
|
1363
|
-
// force admins to hand-write a multi-page ApprovalProcess envelope.
|
|
1364
|
-
// Suppress generic CRUD until the designer lands. Real authoring path:
|
|
1365
|
-
// call `defineApprovalProcess({...})` in code and seed via the
|
|
1366
|
-
// approvals service (`POST /api/v1/approvals/processes`) or commit the
|
|
1367
|
-
// definition as a fixture. Editing existing rows (e.g. toggling
|
|
1368
|
-
// `active`) is also suppressed for now because the same textarea would
|
|
1369
|
-
// appear; use the service API or a future designer instead.
|
|
1370
|
-
userActions: { create: false, edit: false, delete: false, import: false },
|
|
1371
|
-
description: "Persisted approval process definition. Authored via defineApprovalProcess() in code; visual designer is on the roadmap.",
|
|
1372
|
-
displayNameField: "name",
|
|
1373
|
-
titleFormat: "{label}",
|
|
1374
|
-
compactLayout: ["name", "object_name", "active", "updated_at"],
|
|
1375
|
-
listViews: {
|
|
1376
|
-
active: {
|
|
1377
|
-
type: "grid",
|
|
1378
|
-
name: "active",
|
|
1379
|
-
label: "Active",
|
|
1380
|
-
data: { provider: "object", object: "sys_approval_process" },
|
|
1381
|
-
columns: ["label", "object_name", "active", "updated_at"],
|
|
1382
|
-
filter: [{ field: "active", operator: "equals", value: true }],
|
|
1383
|
-
sort: [{ field: "label", order: "asc" }],
|
|
1384
|
-
pagination: { pageSize: 50 }
|
|
1385
|
-
},
|
|
1386
|
-
inactive: {
|
|
1387
|
-
type: "grid",
|
|
1388
|
-
name: "inactive",
|
|
1389
|
-
label: "Inactive",
|
|
1390
|
-
data: { provider: "object", object: "sys_approval_process" },
|
|
1391
|
-
columns: ["label", "object_name", "active", "updated_at"],
|
|
1392
|
-
filter: [{ field: "active", operator: "equals", value: false }],
|
|
1393
|
-
sort: [{ field: "label", order: "asc" }],
|
|
1394
|
-
pagination: { pageSize: 50 }
|
|
1395
|
-
},
|
|
1396
|
-
by_object: {
|
|
1397
|
-
type: "grid",
|
|
1398
|
-
name: "by_object",
|
|
1399
|
-
label: "By Object",
|
|
1400
|
-
data: { provider: "object", object: "sys_approval_process" },
|
|
1401
|
-
columns: ["object_name", "label", "active", "updated_at"],
|
|
1402
|
-
sort: [{ field: "object_name", order: "asc" }, { field: "label", order: "asc" }],
|
|
1403
|
-
grouping: { fields: [{ field: "object_name", order: "asc", collapsed: false }] },
|
|
1404
|
-
pagination: { pageSize: 100 }
|
|
1405
|
-
},
|
|
1406
|
-
all_processes: {
|
|
1407
|
-
type: "grid",
|
|
1408
|
-
name: "all_processes",
|
|
1409
|
-
label: "All",
|
|
1410
|
-
data: { provider: "object", object: "sys_approval_process" },
|
|
1411
|
-
columns: ["label", "object_name", "active", "updated_at"],
|
|
1412
|
-
sort: [{ field: "label", order: "asc" }],
|
|
1413
|
-
pagination: { pageSize: 50 }
|
|
1414
|
-
}
|
|
1415
|
-
},
|
|
1416
|
-
fields: {
|
|
1417
|
-
id: Field.text({ label: "Process ID", required: true, readonly: true, group: "System" }),
|
|
1418
|
-
name: Field.text({
|
|
1419
|
-
label: "Name",
|
|
1420
|
-
required: true,
|
|
1421
|
-
maxLength: 100,
|
|
1422
|
-
description: "Unique snake_case name \u2014 referenced by submitters and audit rows",
|
|
1423
|
-
group: "Definition"
|
|
1424
|
-
}),
|
|
1425
|
-
label: Field.text({
|
|
1426
|
-
label: "Display Label",
|
|
1427
|
-
required: true,
|
|
1428
|
-
maxLength: 200,
|
|
1429
|
-
group: "Definition"
|
|
1430
|
-
}),
|
|
1431
|
-
object_name: Field.text({
|
|
1432
|
-
label: "Object",
|
|
1433
|
-
required: true,
|
|
1434
|
-
maxLength: 100,
|
|
1435
|
-
description: "Short object name this process governs",
|
|
1436
|
-
group: "Definition"
|
|
1437
|
-
}),
|
|
1438
|
-
description: Field.textarea({ label: "Description", required: false, group: "Definition" }),
|
|
1439
|
-
active: Field.boolean({
|
|
1440
|
-
label: "Active",
|
|
1441
|
-
required: true,
|
|
1442
|
-
defaultValue: false,
|
|
1443
|
-
description: "Only active processes are dispatched on submission",
|
|
1444
|
-
group: "Definition"
|
|
1445
|
-
}),
|
|
1446
|
-
definition_json: Field.textarea({
|
|
1447
|
-
label: "Definition",
|
|
1448
|
-
required: true,
|
|
1449
|
-
description: "Serialised ApprovalProcess JSON (see @objectstack/spec/automation/approval)",
|
|
1450
|
-
group: "Definition"
|
|
1451
|
-
}),
|
|
1452
|
-
created_at: Field.datetime({
|
|
1453
|
-
label: "Created At",
|
|
1454
|
-
required: true,
|
|
1455
|
-
defaultValue: "NOW()",
|
|
1456
|
-
readonly: true,
|
|
1457
|
-
group: "System"
|
|
1458
|
-
}),
|
|
1459
|
-
updated_at: Field.datetime({ label: "Updated At", required: false, group: "System" })
|
|
1460
|
-
},
|
|
1461
|
-
indexes: [
|
|
1462
|
-
{ fields: ["name"], unique: true },
|
|
1463
|
-
{ fields: ["object_name"] },
|
|
1464
|
-
{ fields: ["active", "object_name"] }
|
|
1465
|
-
]
|
|
1466
|
-
});
|
|
1467
|
-
var SysApprovalRequest = ObjectSchema.create({
|
|
1468
|
-
name: "sys_approval_request",
|
|
1469
|
-
label: "Approval Request",
|
|
1470
|
-
pluralLabel: "Approval Requests",
|
|
1471
|
-
icon: "inbox",
|
|
1472
|
-
isSystem: true,
|
|
1473
|
-
managedBy: "system",
|
|
1474
|
-
description: "Live approval instance tracked per submission",
|
|
1475
|
-
displayNameField: "id",
|
|
1476
|
-
titleFormat: "{process_name} \xB7 {record_id}",
|
|
1477
|
-
compactLayout: ["process_name", "object_name", "record_id", "status", "current_step", "submitter_id", "updated_at"],
|
|
1478
|
-
// Curated built-in list views — render as segmented tabs in the console.
|
|
1479
|
-
// Filters use {current_user_id} substitution wired by the console.
|
|
1480
|
-
listViews: {
|
|
1481
|
-
my_pending: {
|
|
1482
|
-
type: "grid",
|
|
1483
|
-
name: "my_pending",
|
|
1484
|
-
label: "My Pending",
|
|
1485
|
-
data: { provider: "object", object: "sys_approval_request" },
|
|
1486
|
-
columns: ["process_name", "object_name", "record_id", "current_step", "submitter_id", "updated_at"],
|
|
1487
|
-
filter: [
|
|
1488
|
-
{ field: "status", operator: "equals", value: "pending" },
|
|
1489
|
-
{ field: "pending_approvers", operator: "contains", value: "{current_user_id}" }
|
|
1490
|
-
],
|
|
1491
|
-
sort: [{ field: "updated_at", order: "desc" }],
|
|
1492
|
-
pagination: { pageSize: 25 },
|
|
1493
|
-
emptyState: { title: "No pending approvals", message: "You're all caught up." }
|
|
1494
|
-
},
|
|
1495
|
-
submitted_by_me: {
|
|
1496
|
-
type: "grid",
|
|
1497
|
-
name: "submitted_by_me",
|
|
1498
|
-
label: "I Submitted",
|
|
1499
|
-
data: { provider: "object", object: "sys_approval_request" },
|
|
1500
|
-
columns: ["process_name", "object_name", "record_id", "status", "current_step", "updated_at"],
|
|
1501
|
-
filter: [{ field: "submitter_id", operator: "equals", value: "{current_user_id}" }],
|
|
1502
|
-
sort: [{ field: "updated_at", order: "desc" }],
|
|
1503
|
-
pagination: { pageSize: 25 }
|
|
1504
|
-
},
|
|
1505
|
-
completed: {
|
|
1506
|
-
type: "grid",
|
|
1507
|
-
name: "completed",
|
|
1508
|
-
label: "Completed",
|
|
1509
|
-
data: { provider: "object", object: "sys_approval_request" },
|
|
1510
|
-
columns: ["process_name", "object_name", "record_id", "status", "submitter_id", "completed_at"],
|
|
1511
|
-
filter: [{ field: "status", operator: "in", value: ["approved", "rejected", "recalled"] }],
|
|
1512
|
-
sort: [{ field: "completed_at", order: "desc" }],
|
|
1513
|
-
pagination: { pageSize: 25 }
|
|
1514
|
-
},
|
|
1515
|
-
all_requests: {
|
|
1516
|
-
type: "grid",
|
|
1517
|
-
name: "all_requests",
|
|
1518
|
-
label: "All",
|
|
1519
|
-
data: { provider: "object", object: "sys_approval_request" },
|
|
1520
|
-
columns: ["process_name", "object_name", "record_id", "status", "current_step", "submitter_id", "updated_at"],
|
|
1521
|
-
sort: [{ field: "updated_at", order: "desc" }],
|
|
1522
|
-
pagination: { pageSize: 50 }
|
|
1523
|
-
}
|
|
1524
|
-
},
|
|
1525
|
-
fields: {
|
|
1526
|
-
id: Field.text({ label: "Request ID", required: true, readonly: true, group: "System" }),
|
|
1527
|
-
organization_id: Field.lookup("sys_organization", {
|
|
1528
|
-
label: "Organization",
|
|
1529
|
-
required: false,
|
|
1530
|
-
group: "System",
|
|
1531
|
-
description: "Tenant that owns this approval request (propagated from submitter context)"
|
|
1532
|
-
}),
|
|
1533
|
-
process_name: Field.text({
|
|
1534
|
-
label: "Process",
|
|
1535
|
-
required: true,
|
|
1536
|
-
maxLength: 100,
|
|
1537
|
-
description: "sys_approval_process.name this request was opened against",
|
|
1538
|
-
group: "Target"
|
|
1539
|
-
}),
|
|
1540
|
-
object_name: Field.text({
|
|
1541
|
-
label: "Object",
|
|
1542
|
-
required: true,
|
|
1543
|
-
maxLength: 100,
|
|
1544
|
-
group: "Target"
|
|
1545
|
-
}),
|
|
1546
|
-
record_id: Field.text({
|
|
1547
|
-
label: "Record ID",
|
|
1548
|
-
required: true,
|
|
1549
|
-
maxLength: 100,
|
|
1550
|
-
group: "Target"
|
|
1551
|
-
}),
|
|
1552
|
-
submitter_id: Field.lookup("sys_user", {
|
|
1553
|
-
label: "Submitter",
|
|
1554
|
-
required: false,
|
|
1555
|
-
group: "Target"
|
|
1556
|
-
}),
|
|
1557
|
-
submitter_comment: Field.textarea({
|
|
1558
|
-
label: "Submitter Comment",
|
|
1559
|
-
required: false,
|
|
1560
|
-
group: "Target"
|
|
1561
|
-
}),
|
|
1562
|
-
status: Field.select(
|
|
1563
|
-
["pending", "approved", "rejected", "recalled"],
|
|
1564
|
-
{
|
|
1565
|
-
label: "Status",
|
|
1566
|
-
required: true,
|
|
1567
|
-
defaultValue: "pending",
|
|
1568
|
-
description: "Lifecycle state of the request",
|
|
1569
|
-
group: "State"
|
|
1570
|
-
}
|
|
1571
|
-
),
|
|
1572
|
-
current_step: Field.text({
|
|
1573
|
-
label: "Current Step",
|
|
1574
|
-
required: false,
|
|
1575
|
-
maxLength: 100,
|
|
1576
|
-
description: "Machine name of the step awaiting approval",
|
|
1577
|
-
group: "State"
|
|
1578
|
-
}),
|
|
1579
|
-
current_step_index: Field.number({
|
|
1580
|
-
label: "Current Step Index",
|
|
1581
|
-
required: false,
|
|
1582
|
-
defaultValue: 0,
|
|
1583
|
-
group: "State"
|
|
1584
|
-
}),
|
|
1585
|
-
pending_approvers: Field.textarea({
|
|
1586
|
-
label: "Pending Approvers",
|
|
1587
|
-
required: false,
|
|
1588
|
-
description: "Comma-separated user ids who can act on the current step",
|
|
1589
|
-
group: "State"
|
|
1590
|
-
}),
|
|
1591
|
-
payload_json: Field.textarea({
|
|
1592
|
-
label: "Snapshot",
|
|
1593
|
-
required: false,
|
|
1594
|
-
description: "Record snapshot at submission time",
|
|
1595
|
-
group: "State"
|
|
1596
|
-
}),
|
|
1597
|
-
process_hash: Field.text({
|
|
1598
|
-
label: "Process Hash",
|
|
1599
|
-
required: false,
|
|
1600
|
-
maxLength: 80,
|
|
1601
|
-
readonly: true,
|
|
1602
|
-
description: "sha256 of the approval process body at submit time (ADR-0009 execution pinning). Resolved through sys_metadata_history so process upgrades do not affect in-flight requests.",
|
|
1603
|
-
group: "State"
|
|
1604
|
-
}),
|
|
1605
|
-
completed_at: Field.datetime({
|
|
1606
|
-
label: "Completed At",
|
|
1607
|
-
required: false,
|
|
1608
|
-
group: "State"
|
|
1609
|
-
}),
|
|
1610
|
-
created_at: Field.datetime({
|
|
1611
|
-
label: "Created At",
|
|
1612
|
-
required: true,
|
|
1613
|
-
defaultValue: "NOW()",
|
|
1614
|
-
readonly: true,
|
|
1615
|
-
group: "System"
|
|
1616
|
-
}),
|
|
1617
|
-
updated_at: Field.datetime({ label: "Updated At", required: false, group: "System" })
|
|
1618
|
-
},
|
|
1619
|
-
indexes: [
|
|
1620
|
-
// Look up "is there a pending request for this record?" — common
|
|
1621
|
-
// guard on submit and on edit-while-locked checks.
|
|
1622
|
-
{ fields: ["object_name", "record_id"] },
|
|
1623
|
-
{ fields: ["status", "object_name"] },
|
|
1624
|
-
// "My approvals" inbox — pending_approvers is a CSV string so this
|
|
1625
|
-
// index only helps with status pre-filtering; the engine does a
|
|
1626
|
-
// post-filter substring match per row.
|
|
1627
|
-
{ fields: ["status", "updated_at"] },
|
|
1628
|
-
{ fields: ["submitter_id", "status"] }
|
|
1629
|
-
]
|
|
1630
|
-
});
|
|
1631
|
-
var SysApprovalAction = ObjectSchema.create({
|
|
1632
|
-
name: "sys_approval_action",
|
|
1633
|
-
label: "Approval Action",
|
|
1634
|
-
pluralLabel: "Approval Actions",
|
|
1635
|
-
icon: "check-circle",
|
|
1636
|
-
isSystem: true,
|
|
1637
|
-
managedBy: "append-only",
|
|
1638
|
-
description: "Append-only audit trail for approval actions",
|
|
1639
|
-
displayNameField: "id",
|
|
1640
|
-
titleFormat: "{action} \xB7 {step_name}",
|
|
1641
|
-
compactLayout: ["request_id", "step_name", "action", "actor_id", "created_at"],
|
|
1642
|
-
listViews: {
|
|
1643
|
-
recent: {
|
|
1644
|
-
type: "grid",
|
|
1645
|
-
name: "recent",
|
|
1646
|
-
label: "Recent",
|
|
1647
|
-
data: { provider: "object", object: "sys_approval_action" },
|
|
1648
|
-
columns: ["created_at", "request_id", "step_name", "action", "actor_id", "comment"],
|
|
1649
|
-
sort: [{ field: "created_at", order: "desc" }],
|
|
1650
|
-
pagination: { pageSize: 50 },
|
|
1651
|
-
emptyState: { title: "No approval actions yet", message: "Actions are logged automatically when approvals progress." }
|
|
1652
|
-
},
|
|
1653
|
-
by_actor: {
|
|
1654
|
-
type: "grid",
|
|
1655
|
-
name: "by_actor",
|
|
1656
|
-
label: "By Actor",
|
|
1657
|
-
data: { provider: "object", object: "sys_approval_action" },
|
|
1658
|
-
columns: ["actor_id", "created_at", "request_id", "step_name", "action"],
|
|
1659
|
-
sort: [{ field: "actor_id", order: "asc" }, { field: "created_at", order: "desc" }],
|
|
1660
|
-
grouping: { fields: [{ field: "actor_id", order: "asc", collapsed: false }] },
|
|
1661
|
-
pagination: { pageSize: 100 }
|
|
1662
|
-
},
|
|
1663
|
-
all_actions: {
|
|
1664
|
-
type: "grid",
|
|
1665
|
-
name: "all_actions",
|
|
1666
|
-
label: "All",
|
|
1667
|
-
data: { provider: "object", object: "sys_approval_action" },
|
|
1668
|
-
columns: ["created_at", "request_id", "step_name", "action", "actor_id", "comment"],
|
|
1669
|
-
sort: [{ field: "created_at", order: "desc" }],
|
|
1670
|
-
pagination: { pageSize: 100 }
|
|
1671
|
-
}
|
|
1672
|
-
},
|
|
1673
|
-
fields: {
|
|
1674
|
-
id: Field.text({ label: "Action ID", required: true, readonly: true, group: "System" }),
|
|
1675
|
-
organization_id: Field.lookup("sys_organization", {
|
|
1676
|
-
label: "Organization",
|
|
1677
|
-
required: false,
|
|
1678
|
-
group: "System",
|
|
1679
|
-
description: "Tenant that owns this action (mirrors the parent request)"
|
|
1680
|
-
}),
|
|
1681
|
-
request_id: Field.lookup("sys_approval_request", {
|
|
1682
|
-
label: "Request",
|
|
1683
|
-
required: true,
|
|
1684
|
-
group: "Target"
|
|
1685
|
-
}),
|
|
1686
|
-
step_name: Field.text({
|
|
1687
|
-
label: "Step",
|
|
1688
|
-
required: false,
|
|
1689
|
-
maxLength: 100,
|
|
1690
|
-
description: "Machine name of the step at the time of the action",
|
|
1691
|
-
group: "Target"
|
|
1692
|
-
}),
|
|
1693
|
-
step_index: Field.number({
|
|
1694
|
-
label: "Step Index",
|
|
1695
|
-
required: false,
|
|
1696
|
-
group: "Target"
|
|
1697
|
-
}),
|
|
1698
|
-
action: Field.select(
|
|
1699
|
-
["submit", "approve", "reject", "recall", "escalate"],
|
|
1700
|
-
{
|
|
1701
|
-
label: "Action",
|
|
1702
|
-
required: true,
|
|
1703
|
-
group: "Action"
|
|
1704
|
-
}
|
|
1705
|
-
),
|
|
1706
|
-
actor_id: Field.lookup("sys_user", {
|
|
1707
|
-
label: "Actor",
|
|
1708
|
-
required: false,
|
|
1709
|
-
group: "Action"
|
|
1710
|
-
}),
|
|
1711
|
-
comment: Field.textarea({ label: "Comment", required: false, group: "Action" }),
|
|
1712
|
-
created_at: Field.datetime({
|
|
1713
|
-
label: "Created At",
|
|
1714
|
-
required: true,
|
|
1715
|
-
defaultValue: "NOW()",
|
|
1716
|
-
readonly: true,
|
|
1717
|
-
group: "System"
|
|
1718
|
-
})
|
|
1719
|
-
},
|
|
1720
|
-
indexes: [
|
|
1721
|
-
{ fields: ["request_id", "created_at"] },
|
|
1722
|
-
{ fields: ["request_id", "step_index", "action"] }
|
|
1723
|
-
]
|
|
1724
|
-
});
|
|
1725
732
|
var SysJob = ObjectSchema.create({
|
|
1726
733
|
name: "sys_job",
|
|
1727
734
|
label: "Background Job",
|
|
@@ -1953,6 +960,6 @@ var SysJobQueue = ObjectSchema.create({
|
|
|
1953
960
|
]
|
|
1954
961
|
});
|
|
1955
962
|
|
|
1956
|
-
export {
|
|
963
|
+
export { SysAttachment, SysEmail, SysEmailTemplate, SysJob, SysJobQueue, SysJobRun, SysNotification, SysReportSchedule, SysSavedReport };
|
|
1957
964
|
//# sourceMappingURL=index.mjs.map
|
|
1958
965
|
//# sourceMappingURL=index.mjs.map
|