@objectstack/platform-objects 0.1.0 → 4.1.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 +16 -48
- package/dist/apps/index.d.ts +16 -48
- package/dist/apps/index.js +139 -215
- package/dist/apps/index.js.map +1 -1
- package/dist/apps/index.mjs +140 -210
- package/dist/apps/index.mjs.map +1 -1
- package/dist/audit/index.d.mts +40249 -150
- package/dist/audit/index.d.ts +40249 -150
- package/dist/audit/index.js +1428 -0
- package/dist/audit/index.js.map +1 -1
- package/dist/audit/index.mjs +1417 -1
- package/dist/audit/index.mjs.map +1 -1
- package/dist/identity/index.d.mts +18792 -2520
- package/dist/identity/index.d.ts +18792 -2520
- package/dist/identity/index.js +1107 -6
- package/dist/identity/index.js.map +1 -1
- package/dist/identity/index.mjs +1106 -7
- package/dist/identity/index.mjs.map +1 -1
- package/dist/index.d.mts +9 -7
- package/dist/index.d.ts +9 -7
- package/dist/index.js +3939 -1504
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3917 -1487
- package/dist/index.mjs.map +1 -1
- package/dist/integration/index.d.mts +2905 -0
- package/dist/integration/index.d.ts +2905 -0
- package/dist/integration/index.js +140 -0
- package/dist/integration/index.js.map +1 -0
- package/dist/integration/index.mjs +138 -0
- package/dist/integration/index.mjs.map +1 -0
- package/dist/metadata/index.d.mts +1426 -19092
- package/dist/metadata/index.d.ts +1426 -19092
- package/dist/metadata/index.js +29 -619
- package/dist/metadata/index.js.map +1 -1
- package/dist/metadata/index.mjs +30 -615
- package/dist/metadata/index.mjs.map +1 -1
- package/dist/security/index.d.mts +10759 -60
- package/dist/security/index.d.ts +10759 -60
- package/dist/security/index.js +786 -0
- package/dist/security/index.js.map +1 -1
- package/dist/security/index.mjs +782 -1
- package/dist/security/index.mjs.map +1 -1
- package/dist/system/index.d.mts +8409 -0
- package/dist/system/index.d.ts +8409 -0
- package/dist/system/index.js +395 -0
- package/dist/system/index.js.map +1 -0
- package/dist/system/index.mjs +391 -0
- package/dist/system/index.mjs.map +1 -0
- package/package.json +13 -8
- package/dist/tenant/index.d.mts +0 -16454
- package/dist/tenant/index.d.ts +0 -16454
- package/dist/tenant/index.js +0 -741
- package/dist/tenant/index.js.map +0 -1
- package/dist/tenant/index.mjs +0 -733
- package/dist/tenant/index.mjs.map +0 -1
- /package/dist/{state-machine.zod-BFg-VE0M.d-Ek3_yo9P.d.mts → state-machine.zod-BNanU03M.d-Ek3_yo9P.d.mts} +0 -0
- /package/dist/{state-machine.zod-BFg-VE0M.d-Ek3_yo9P.d.ts → state-machine.zod-BNanU03M.d-Ek3_yo9P.d.ts} +0 -0
package/dist/audit/index.js
CHANGED
|
@@ -9,10 +9,62 @@ var SysAuditLog = data.ObjectSchema.create({
|
|
|
9
9
|
pluralLabel: "Audit Logs",
|
|
10
10
|
icon: "scroll-text",
|
|
11
11
|
isSystem: true,
|
|
12
|
+
managedBy: "append-only",
|
|
12
13
|
description: "Immutable audit trail for platform events",
|
|
13
14
|
displayNameField: "action",
|
|
14
15
|
titleFormat: "{action} \xB7 {object_name}",
|
|
15
16
|
compactLayout: ["created_at", "action", "object_name", "record_id", "user_id"],
|
|
17
|
+
listViews: {
|
|
18
|
+
recent: {
|
|
19
|
+
type: "grid",
|
|
20
|
+
name: "recent",
|
|
21
|
+
label: "Recent",
|
|
22
|
+
data: { provider: "object", object: "sys_audit_log" },
|
|
23
|
+
columns: ["created_at", "action", "object_name", "record_id", "user_id"],
|
|
24
|
+
sort: [{ field: "created_at", order: "desc" }],
|
|
25
|
+
pagination: { pageSize: 50 },
|
|
26
|
+
emptyState: { title: "No audit events", message: "Activity will appear here as users interact with the platform." }
|
|
27
|
+
},
|
|
28
|
+
writes_only: {
|
|
29
|
+
type: "grid",
|
|
30
|
+
name: "writes_only",
|
|
31
|
+
label: "Writes",
|
|
32
|
+
data: { provider: "object", object: "sys_audit_log" },
|
|
33
|
+
columns: ["created_at", "action", "object_name", "record_id", "user_id"],
|
|
34
|
+
filter: [{ field: "action", operator: "in", value: ["create", "update", "delete", "restore"] }],
|
|
35
|
+
sort: [{ field: "created_at", order: "desc" }],
|
|
36
|
+
pagination: { pageSize: 50 }
|
|
37
|
+
},
|
|
38
|
+
auth_events: {
|
|
39
|
+
type: "grid",
|
|
40
|
+
name: "auth_events",
|
|
41
|
+
label: "Auth",
|
|
42
|
+
data: { provider: "object", object: "sys_audit_log" },
|
|
43
|
+
columns: ["created_at", "action", "user_id"],
|
|
44
|
+
filter: [{ field: "action", operator: "in", value: ["login", "logout", "permission_change"] }],
|
|
45
|
+
sort: [{ field: "created_at", order: "desc" }],
|
|
46
|
+
pagination: { pageSize: 50 }
|
|
47
|
+
},
|
|
48
|
+
config_changes: {
|
|
49
|
+
type: "grid",
|
|
50
|
+
name: "config_changes",
|
|
51
|
+
label: "Config",
|
|
52
|
+
data: { provider: "object", object: "sys_audit_log" },
|
|
53
|
+
columns: ["created_at", "action", "object_name", "user_id"],
|
|
54
|
+
filter: [{ field: "action", operator: "in", value: ["config_change", "export", "import"] }],
|
|
55
|
+
sort: [{ field: "created_at", order: "desc" }],
|
|
56
|
+
pagination: { pageSize: 50 }
|
|
57
|
+
},
|
|
58
|
+
all_events: {
|
|
59
|
+
type: "grid",
|
|
60
|
+
name: "all_events",
|
|
61
|
+
label: "All",
|
|
62
|
+
data: { provider: "object", object: "sys_audit_log" },
|
|
63
|
+
columns: ["created_at", "action", "object_name", "record_id", "user_id"],
|
|
64
|
+
sort: [{ field: "created_at", order: "desc" }],
|
|
65
|
+
pagination: { pageSize: 100 }
|
|
66
|
+
}
|
|
67
|
+
},
|
|
16
68
|
fields: {
|
|
17
69
|
// ── Event ────────────────────────────────────────────────────
|
|
18
70
|
created_at: data.Field.datetime({
|
|
@@ -137,6 +189,7 @@ var SysPresence = data.ObjectSchema.create({
|
|
|
137
189
|
pluralLabel: "Presences",
|
|
138
190
|
icon: "wifi",
|
|
139
191
|
isSystem: true,
|
|
192
|
+
managedBy: "append-only",
|
|
140
193
|
description: "Real-time user presence and activity tracking",
|
|
141
194
|
titleFormat: "{user_id} ({status})",
|
|
142
195
|
compactLayout: ["user_id", "status", "last_seen"],
|
|
@@ -227,6 +280,7 @@ var SysActivity = data.ObjectSchema.create({
|
|
|
227
280
|
pluralLabel: "Activities",
|
|
228
281
|
icon: "activity",
|
|
229
282
|
isSystem: true,
|
|
283
|
+
managedBy: "append-only",
|
|
230
284
|
description: "Recent activity stream entries (lightweight, denormalized)",
|
|
231
285
|
displayNameField: "summary",
|
|
232
286
|
titleFormat: "{type} \xB7 {summary}",
|
|
@@ -368,6 +422,7 @@ var SysComment = data.ObjectSchema.create({
|
|
|
368
422
|
pluralLabel: "Comments",
|
|
369
423
|
icon: "message-square",
|
|
370
424
|
isSystem: true,
|
|
425
|
+
managedBy: "platform",
|
|
371
426
|
description: "Threaded comments attached to records via thread_id",
|
|
372
427
|
displayNameField: "body",
|
|
373
428
|
titleFormat: "{author_name}: {body}",
|
|
@@ -483,10 +538,1383 @@ var SysComment = data.ObjectSchema.create({
|
|
|
483
538
|
clone: false
|
|
484
539
|
}
|
|
485
540
|
});
|
|
541
|
+
var SysAttachment = data.ObjectSchema.create({
|
|
542
|
+
name: "sys_attachment",
|
|
543
|
+
label: "Attachment",
|
|
544
|
+
pluralLabel: "Attachments",
|
|
545
|
+
icon: "paperclip",
|
|
546
|
+
isSystem: true,
|
|
547
|
+
managedBy: "platform",
|
|
548
|
+
description: "Polymorphic link between a sys_file and any other record",
|
|
549
|
+
titleFormat: "{file_name} \u2192 {parent_object}/{parent_id}",
|
|
550
|
+
compactLayout: ["created_at", "parent_object", "file_name", "mime_type", "size"],
|
|
551
|
+
fields: {
|
|
552
|
+
id: data.Field.text({
|
|
553
|
+
label: "Attachment ID",
|
|
554
|
+
required: true,
|
|
555
|
+
readonly: true,
|
|
556
|
+
group: "System"
|
|
557
|
+
}),
|
|
558
|
+
// ── Parent (polymorphic) ────────────────────────────────────
|
|
559
|
+
parent_object: data.Field.text({
|
|
560
|
+
label: "Parent Object",
|
|
561
|
+
required: true,
|
|
562
|
+
searchable: true,
|
|
563
|
+
maxLength: 128,
|
|
564
|
+
description: "Short object name of the attached-to record (e.g. account, lead)",
|
|
565
|
+
group: "Parent"
|
|
566
|
+
}),
|
|
567
|
+
parent_id: data.Field.text({
|
|
568
|
+
label: "Parent Record",
|
|
569
|
+
required: true,
|
|
570
|
+
searchable: true,
|
|
571
|
+
maxLength: 64,
|
|
572
|
+
description: "Primary key of the attached-to record",
|
|
573
|
+
group: "Parent"
|
|
574
|
+
}),
|
|
575
|
+
// ── File reference ──────────────────────────────────────────
|
|
576
|
+
file_id: data.Field.lookup("sys_file", {
|
|
577
|
+
label: "File",
|
|
578
|
+
required: true,
|
|
579
|
+
description: "The sys_file storage entry being attached",
|
|
580
|
+
group: "File"
|
|
581
|
+
}),
|
|
582
|
+
file_name: data.Field.text({
|
|
583
|
+
label: "File Name",
|
|
584
|
+
required: false,
|
|
585
|
+
searchable: true,
|
|
586
|
+
maxLength: 255,
|
|
587
|
+
description: "Denormalised copy of sys_file.name for fast list rendering",
|
|
588
|
+
group: "File"
|
|
589
|
+
}),
|
|
590
|
+
mime_type: data.Field.text({
|
|
591
|
+
label: "MIME Type",
|
|
592
|
+
required: false,
|
|
593
|
+
maxLength: 128,
|
|
594
|
+
group: "File"
|
|
595
|
+
}),
|
|
596
|
+
size: data.Field.number({
|
|
597
|
+
label: "Size (bytes)",
|
|
598
|
+
required: false,
|
|
599
|
+
group: "File"
|
|
600
|
+
}),
|
|
601
|
+
// ── Sharing ────────────────────────────────────────────────
|
|
602
|
+
share_type: data.Field.select(
|
|
603
|
+
["viewer", "collaborator", "inferred"],
|
|
604
|
+
{
|
|
605
|
+
label: "Share Type",
|
|
606
|
+
defaultValue: "viewer",
|
|
607
|
+
description: "viewer | collaborator | inferred (inherited from parent record)",
|
|
608
|
+
group: "Sharing"
|
|
609
|
+
}
|
|
610
|
+
),
|
|
611
|
+
visibility: data.Field.select(
|
|
612
|
+
["internal", "all_users", "shared_users"],
|
|
613
|
+
{
|
|
614
|
+
label: "Visibility",
|
|
615
|
+
defaultValue: "internal",
|
|
616
|
+
group: "Sharing"
|
|
617
|
+
}
|
|
618
|
+
),
|
|
619
|
+
// ── Authoring ──────────────────────────────────────────────
|
|
620
|
+
uploaded_by: data.Field.lookup("sys_user", {
|
|
621
|
+
label: "Uploaded By",
|
|
622
|
+
required: false,
|
|
623
|
+
group: "System"
|
|
624
|
+
}),
|
|
625
|
+
description: data.Field.textarea({
|
|
626
|
+
label: "Description",
|
|
627
|
+
required: false,
|
|
628
|
+
maxLength: 1024,
|
|
629
|
+
group: "File"
|
|
630
|
+
}),
|
|
631
|
+
created_at: data.Field.datetime({
|
|
632
|
+
label: "Created At",
|
|
633
|
+
required: true,
|
|
634
|
+
defaultValue: "NOW()",
|
|
635
|
+
readonly: true,
|
|
636
|
+
group: "System"
|
|
637
|
+
}),
|
|
638
|
+
updated_at: data.Field.datetime({
|
|
639
|
+
label: "Updated At",
|
|
640
|
+
required: false,
|
|
641
|
+
group: "System"
|
|
642
|
+
})
|
|
643
|
+
},
|
|
644
|
+
indexes: [
|
|
645
|
+
{ fields: ["parent_object", "parent_id", "created_at"] },
|
|
646
|
+
{ fields: ["file_id"] },
|
|
647
|
+
{ fields: ["uploaded_by"] }
|
|
648
|
+
],
|
|
649
|
+
enable: {
|
|
650
|
+
trackHistory: false,
|
|
651
|
+
searchable: true,
|
|
652
|
+
apiEnabled: true,
|
|
653
|
+
trash: true,
|
|
654
|
+
mru: false,
|
|
655
|
+
clone: false
|
|
656
|
+
}
|
|
657
|
+
});
|
|
658
|
+
var SysNotification = data.ObjectSchema.create({
|
|
659
|
+
name: "sys_notification",
|
|
660
|
+
label: "Notification",
|
|
661
|
+
pluralLabel: "Notifications",
|
|
662
|
+
icon: "bell",
|
|
663
|
+
isSystem: true,
|
|
664
|
+
managedBy: "system",
|
|
665
|
+
description: "Per-user notification inbox entries",
|
|
666
|
+
displayNameField: "title",
|
|
667
|
+
titleFormat: "{title}",
|
|
668
|
+
compactLayout: ["title", "type", "is_read", "created_at"],
|
|
669
|
+
listViews: {
|
|
670
|
+
unread: {
|
|
671
|
+
type: "grid",
|
|
672
|
+
name: "unread",
|
|
673
|
+
label: "Unread",
|
|
674
|
+
data: { provider: "object", object: "sys_notification" },
|
|
675
|
+
columns: ["type", "title", "recipient_id", "created_at"],
|
|
676
|
+
filter: [
|
|
677
|
+
{ field: "recipient_id", operator: "equals", value: "{current_user_id}" },
|
|
678
|
+
{ field: "is_read", operator: "equals", value: false }
|
|
679
|
+
],
|
|
680
|
+
sort: [{ field: "created_at", order: "desc" }],
|
|
681
|
+
pagination: { pageSize: 50 },
|
|
682
|
+
emptyState: { title: "Inbox zero", message: "No unread notifications." }
|
|
683
|
+
},
|
|
684
|
+
mine: {
|
|
685
|
+
type: "grid",
|
|
686
|
+
name: "mine",
|
|
687
|
+
label: "Mine",
|
|
688
|
+
data: { provider: "object", object: "sys_notification" },
|
|
689
|
+
columns: ["type", "title", "is_read", "created_at"],
|
|
690
|
+
filter: [{ field: "recipient_id", operator: "equals", value: "{current_user_id}" }],
|
|
691
|
+
sort: [{ field: "created_at", order: "desc" }],
|
|
692
|
+
pagination: { pageSize: 50 }
|
|
693
|
+
},
|
|
694
|
+
all_notifications: {
|
|
695
|
+
type: "grid",
|
|
696
|
+
name: "all_notifications",
|
|
697
|
+
label: "All",
|
|
698
|
+
data: { provider: "object", object: "sys_notification" },
|
|
699
|
+
columns: ["type", "title", "recipient_id", "is_read", "created_at"],
|
|
700
|
+
sort: [{ field: "created_at", order: "desc" }],
|
|
701
|
+
pagination: { pageSize: 100 }
|
|
702
|
+
}
|
|
703
|
+
},
|
|
704
|
+
fields: {
|
|
705
|
+
id: data.Field.text({
|
|
706
|
+
label: "Notification ID",
|
|
707
|
+
required: true,
|
|
708
|
+
readonly: true,
|
|
709
|
+
group: "System"
|
|
710
|
+
}),
|
|
711
|
+
// ── Routing ──────────────────────────────────────────────────
|
|
712
|
+
recipient_id: data.Field.lookup("sys_user", {
|
|
713
|
+
label: "Recipient",
|
|
714
|
+
required: true,
|
|
715
|
+
searchable: true,
|
|
716
|
+
description: "User the notification is delivered to",
|
|
717
|
+
group: "Routing"
|
|
718
|
+
}),
|
|
719
|
+
// ── Content ──────────────────────────────────────────────────
|
|
720
|
+
type: data.Field.select(
|
|
721
|
+
["mention", "assignment", "comment_reply", "lead_converted", "task_due", "system"],
|
|
722
|
+
{
|
|
723
|
+
label: "Type",
|
|
724
|
+
required: true,
|
|
725
|
+
defaultValue: "system",
|
|
726
|
+
description: "Notification category \u2014 drives icon + sort priority",
|
|
727
|
+
group: "Content"
|
|
728
|
+
}
|
|
729
|
+
),
|
|
730
|
+
title: data.Field.text({
|
|
731
|
+
label: "Title",
|
|
732
|
+
required: true,
|
|
733
|
+
maxLength: 255,
|
|
734
|
+
searchable: true,
|
|
735
|
+
group: "Content"
|
|
736
|
+
}),
|
|
737
|
+
body: data.Field.textarea({
|
|
738
|
+
label: "Body",
|
|
739
|
+
required: false,
|
|
740
|
+
description: "Optional secondary text (one-line summary)",
|
|
741
|
+
group: "Content"
|
|
742
|
+
}),
|
|
743
|
+
// ── Source linkage ───────────────────────────────────────────
|
|
744
|
+
source_object: data.Field.text({
|
|
745
|
+
label: "Source Object",
|
|
746
|
+
required: false,
|
|
747
|
+
maxLength: 100,
|
|
748
|
+
description: "Object name of the related record (e.g. lead, opportunity)",
|
|
749
|
+
group: "Source"
|
|
750
|
+
}),
|
|
751
|
+
source_id: data.Field.text({
|
|
752
|
+
label: "Source Record",
|
|
753
|
+
required: false,
|
|
754
|
+
maxLength: 100,
|
|
755
|
+
description: "Record id within source_object",
|
|
756
|
+
group: "Source"
|
|
757
|
+
}),
|
|
758
|
+
url: data.Field.url({
|
|
759
|
+
label: "Deep Link",
|
|
760
|
+
required: false,
|
|
761
|
+
description: "Optional URL to navigate to when clicked",
|
|
762
|
+
group: "Source"
|
|
763
|
+
}),
|
|
764
|
+
actor_id: data.Field.lookup("sys_user", {
|
|
765
|
+
label: "Actor",
|
|
766
|
+
required: false,
|
|
767
|
+
description: "User who caused the notification (mentioner, assigner)",
|
|
768
|
+
group: "Source"
|
|
769
|
+
}),
|
|
770
|
+
actor_name: data.Field.text({
|
|
771
|
+
label: "Actor Name",
|
|
772
|
+
required: false,
|
|
773
|
+
group: "Source"
|
|
774
|
+
}),
|
|
775
|
+
// ── Read state ───────────────────────────────────────────────
|
|
776
|
+
is_read: data.Field.boolean({
|
|
777
|
+
label: "Read",
|
|
778
|
+
defaultValue: false,
|
|
779
|
+
description: "True once recipient acknowledges",
|
|
780
|
+
group: "State"
|
|
781
|
+
}),
|
|
782
|
+
read_at: data.Field.datetime({
|
|
783
|
+
label: "Read At",
|
|
784
|
+
required: false,
|
|
785
|
+
group: "State"
|
|
786
|
+
}),
|
|
787
|
+
// ── Lifecycle ────────────────────────────────────────────────
|
|
788
|
+
created_at: data.Field.datetime({
|
|
789
|
+
label: "Created At",
|
|
790
|
+
required: true,
|
|
791
|
+
defaultValue: "NOW()",
|
|
792
|
+
readonly: true,
|
|
793
|
+
group: "System"
|
|
794
|
+
}),
|
|
795
|
+
updated_at: data.Field.datetime({
|
|
796
|
+
label: "Updated At",
|
|
797
|
+
required: false,
|
|
798
|
+
group: "System"
|
|
799
|
+
})
|
|
800
|
+
},
|
|
801
|
+
indexes: [
|
|
802
|
+
{ fields: ["recipient_id", "is_read", "created_at"] },
|
|
803
|
+
{ fields: ["recipient_id", "created_at"] },
|
|
804
|
+
{ fields: ["source_object", "source_id"] }
|
|
805
|
+
]
|
|
806
|
+
});
|
|
807
|
+
var SysEmail = data.ObjectSchema.create({
|
|
808
|
+
name: "sys_email",
|
|
809
|
+
label: "Email",
|
|
810
|
+
pluralLabel: "Emails",
|
|
811
|
+
icon: "mail",
|
|
812
|
+
isSystem: true,
|
|
813
|
+
managedBy: "append-only",
|
|
814
|
+
description: "Outbound email delivery log",
|
|
815
|
+
displayNameField: "subject",
|
|
816
|
+
titleFormat: "{subject}",
|
|
817
|
+
compactLayout: ["subject", "to", "status", "sent_at"],
|
|
818
|
+
fields: {
|
|
819
|
+
id: data.Field.text({
|
|
820
|
+
label: "Email ID",
|
|
821
|
+
required: true,
|
|
822
|
+
readonly: true,
|
|
823
|
+
group: "System"
|
|
824
|
+
}),
|
|
825
|
+
// ── Envelope ─────────────────────────────────────────────────
|
|
826
|
+
message_id: data.Field.text({
|
|
827
|
+
label: "Message-ID",
|
|
828
|
+
required: false,
|
|
829
|
+
maxLength: 255,
|
|
830
|
+
description: "RFC-5322 Message-ID assigned by the transport",
|
|
831
|
+
group: "Envelope"
|
|
832
|
+
}),
|
|
833
|
+
from_address: data.Field.text({
|
|
834
|
+
label: "From",
|
|
835
|
+
required: true,
|
|
836
|
+
maxLength: 320,
|
|
837
|
+
searchable: true,
|
|
838
|
+
group: "Envelope"
|
|
839
|
+
}),
|
|
840
|
+
to_addresses: data.Field.text({
|
|
841
|
+
label: "To",
|
|
842
|
+
required: true,
|
|
843
|
+
maxLength: 4e3,
|
|
844
|
+
searchable: true,
|
|
845
|
+
description: "Comma-separated recipient addresses",
|
|
846
|
+
group: "Envelope"
|
|
847
|
+
}),
|
|
848
|
+
cc_addresses: data.Field.text({
|
|
849
|
+
label: "Cc",
|
|
850
|
+
required: false,
|
|
851
|
+
maxLength: 4e3,
|
|
852
|
+
group: "Envelope"
|
|
853
|
+
}),
|
|
854
|
+
bcc_addresses: data.Field.text({
|
|
855
|
+
label: "Bcc",
|
|
856
|
+
required: false,
|
|
857
|
+
maxLength: 4e3,
|
|
858
|
+
group: "Envelope"
|
|
859
|
+
}),
|
|
860
|
+
reply_to: data.Field.text({
|
|
861
|
+
label: "Reply-To",
|
|
862
|
+
required: false,
|
|
863
|
+
maxLength: 320,
|
|
864
|
+
group: "Envelope"
|
|
865
|
+
}),
|
|
866
|
+
// ── Content ──────────────────────────────────────────────────
|
|
867
|
+
subject: data.Field.text({
|
|
868
|
+
label: "Subject",
|
|
869
|
+
required: true,
|
|
870
|
+
maxLength: 998,
|
|
871
|
+
searchable: true,
|
|
872
|
+
group: "Content"
|
|
873
|
+
}),
|
|
874
|
+
body_text: data.Field.textarea({
|
|
875
|
+
label: "Body (text)",
|
|
876
|
+
required: false,
|
|
877
|
+
searchable: true,
|
|
878
|
+
group: "Content"
|
|
879
|
+
}),
|
|
880
|
+
body_html: data.Field.textarea({
|
|
881
|
+
label: "Body (HTML)",
|
|
882
|
+
required: false,
|
|
883
|
+
group: "Content"
|
|
884
|
+
}),
|
|
885
|
+
// ── Delivery state ───────────────────────────────────────────
|
|
886
|
+
status: data.Field.select(
|
|
887
|
+
["queued", "sent", "failed"],
|
|
888
|
+
{
|
|
889
|
+
label: "Status",
|
|
890
|
+
required: true,
|
|
891
|
+
defaultValue: "queued",
|
|
892
|
+
description: "Lifecycle state \u2014 queued by IEmailService.send before transport call",
|
|
893
|
+
group: "State"
|
|
894
|
+
}
|
|
895
|
+
),
|
|
896
|
+
error: data.Field.textarea({
|
|
897
|
+
label: "Error",
|
|
898
|
+
required: false,
|
|
899
|
+
description: "Transport error message when status=failed",
|
|
900
|
+
group: "State"
|
|
901
|
+
}),
|
|
902
|
+
attempt_count: data.Field.number({
|
|
903
|
+
label: "Attempts",
|
|
904
|
+
required: false,
|
|
905
|
+
defaultValue: 0,
|
|
906
|
+
description: "Number of delivery attempts performed by the service",
|
|
907
|
+
group: "State"
|
|
908
|
+
}),
|
|
909
|
+
sent_at: data.Field.datetime({
|
|
910
|
+
label: "Sent At",
|
|
911
|
+
required: false,
|
|
912
|
+
description: 'Set when status transitions to "sent"',
|
|
913
|
+
group: "State"
|
|
914
|
+
}),
|
|
915
|
+
// ── Source linkage ───────────────────────────────────────────
|
|
916
|
+
related_object: data.Field.text({
|
|
917
|
+
label: "Related Object",
|
|
918
|
+
required: false,
|
|
919
|
+
maxLength: 100,
|
|
920
|
+
description: "Object name of the related record (e.g. lead, opportunity)",
|
|
921
|
+
group: "Source"
|
|
922
|
+
}),
|
|
923
|
+
related_id: data.Field.text({
|
|
924
|
+
label: "Related Record",
|
|
925
|
+
required: false,
|
|
926
|
+
maxLength: 100,
|
|
927
|
+
description: "Record id within related_object",
|
|
928
|
+
group: "Source"
|
|
929
|
+
}),
|
|
930
|
+
sent_by: data.Field.lookup("sys_user", {
|
|
931
|
+
label: "Sent By",
|
|
932
|
+
required: false,
|
|
933
|
+
group: "Source"
|
|
934
|
+
}),
|
|
935
|
+
// ── Lifecycle ────────────────────────────────────────────────
|
|
936
|
+
created_at: data.Field.datetime({
|
|
937
|
+
label: "Created At",
|
|
938
|
+
required: true,
|
|
939
|
+
defaultValue: "NOW()",
|
|
940
|
+
readonly: true,
|
|
941
|
+
group: "System"
|
|
942
|
+
}),
|
|
943
|
+
updated_at: data.Field.datetime({
|
|
944
|
+
label: "Updated At",
|
|
945
|
+
required: false,
|
|
946
|
+
group: "System"
|
|
947
|
+
})
|
|
948
|
+
},
|
|
949
|
+
indexes: [
|
|
950
|
+
{ fields: ["status", "created_at"] },
|
|
951
|
+
{ fields: ["related_object", "related_id"] },
|
|
952
|
+
{ fields: ["sent_by"] }
|
|
953
|
+
]
|
|
954
|
+
});
|
|
955
|
+
var SysEmailTemplate = data.ObjectSchema.create({
|
|
956
|
+
name: "sys_email_template",
|
|
957
|
+
label: "Email Template",
|
|
958
|
+
pluralLabel: "Email Templates",
|
|
959
|
+
icon: "mail",
|
|
960
|
+
isSystem: true,
|
|
961
|
+
managedBy: "config",
|
|
962
|
+
description: "Outbound email template (subject + body + variables) resolved by name+locale",
|
|
963
|
+
displayNameField: "label",
|
|
964
|
+
titleFormat: "{label}",
|
|
965
|
+
compactLayout: ["name", "label", "category", "locale", "active"],
|
|
966
|
+
fields: {
|
|
967
|
+
id: data.Field.text({
|
|
968
|
+
label: "ID",
|
|
969
|
+
required: true,
|
|
970
|
+
readonly: true,
|
|
971
|
+
group: "System"
|
|
972
|
+
}),
|
|
973
|
+
// ── Identity ─────────────────────────────────────────────────
|
|
974
|
+
name: data.Field.text({
|
|
975
|
+
label: "Name",
|
|
976
|
+
required: true,
|
|
977
|
+
maxLength: 128,
|
|
978
|
+
description: "Dotted snake_case identifier (e.g. auth.password_reset)",
|
|
979
|
+
group: "Identity"
|
|
980
|
+
}),
|
|
981
|
+
label: data.Field.text({
|
|
982
|
+
label: "Label",
|
|
983
|
+
required: true,
|
|
984
|
+
maxLength: 200,
|
|
985
|
+
description: "Human-readable name shown in Studio",
|
|
986
|
+
group: "Identity"
|
|
987
|
+
}),
|
|
988
|
+
category: data.Field.select(
|
|
989
|
+
["auth", "notification", "workflow", "marketing", "custom"],
|
|
990
|
+
{
|
|
991
|
+
label: "Category",
|
|
992
|
+
required: true,
|
|
993
|
+
defaultValue: "custom",
|
|
994
|
+
group: "Identity"
|
|
995
|
+
}
|
|
996
|
+
),
|
|
997
|
+
locale: data.Field.text({
|
|
998
|
+
label: "Locale",
|
|
999
|
+
required: true,
|
|
1000
|
+
defaultValue: "en-US",
|
|
1001
|
+
maxLength: 16,
|
|
1002
|
+
description: "BCP-47 locale tag (en-US, zh-CN, \u2026)",
|
|
1003
|
+
group: "Identity"
|
|
1004
|
+
}),
|
|
1005
|
+
description: data.Field.textarea({
|
|
1006
|
+
label: "Description",
|
|
1007
|
+
required: false,
|
|
1008
|
+
group: "Identity"
|
|
1009
|
+
}),
|
|
1010
|
+
// ── Content ──────────────────────────────────────────────────
|
|
1011
|
+
subject: data.Field.text({
|
|
1012
|
+
label: "Subject",
|
|
1013
|
+
required: true,
|
|
1014
|
+
maxLength: 500,
|
|
1015
|
+
description: "Subject template; supports {{var.path}} placeholders",
|
|
1016
|
+
group: "Content"
|
|
1017
|
+
}),
|
|
1018
|
+
body_html: data.Field.textarea({
|
|
1019
|
+
label: "HTML Body",
|
|
1020
|
+
required: true,
|
|
1021
|
+
description: "HTML body template; supports {{var.path}} placeholders",
|
|
1022
|
+
group: "Content"
|
|
1023
|
+
}),
|
|
1024
|
+
body_text: data.Field.textarea({
|
|
1025
|
+
label: "Plain Text Body",
|
|
1026
|
+
required: false,
|
|
1027
|
+
description: "Optional plain-text alternative (auto-derived from HTML when blank)",
|
|
1028
|
+
group: "Content"
|
|
1029
|
+
}),
|
|
1030
|
+
// ── Envelope overrides ───────────────────────────────────────
|
|
1031
|
+
from_name: data.Field.text({
|
|
1032
|
+
label: "From Name",
|
|
1033
|
+
required: false,
|
|
1034
|
+
maxLength: 200,
|
|
1035
|
+
group: "Envelope"
|
|
1036
|
+
}),
|
|
1037
|
+
from_address: data.Field.text({
|
|
1038
|
+
label: "From Address",
|
|
1039
|
+
required: false,
|
|
1040
|
+
maxLength: 255,
|
|
1041
|
+
group: "Envelope"
|
|
1042
|
+
}),
|
|
1043
|
+
reply_to: data.Field.text({
|
|
1044
|
+
label: "Reply-To",
|
|
1045
|
+
required: false,
|
|
1046
|
+
maxLength: 255,
|
|
1047
|
+
group: "Envelope"
|
|
1048
|
+
}),
|
|
1049
|
+
// ── Lifecycle ────────────────────────────────────────────────
|
|
1050
|
+
active: data.Field.boolean({
|
|
1051
|
+
label: "Active",
|
|
1052
|
+
required: true,
|
|
1053
|
+
defaultValue: true,
|
|
1054
|
+
group: "Lifecycle"
|
|
1055
|
+
}),
|
|
1056
|
+
is_system: data.Field.boolean({
|
|
1057
|
+
label: "System Template",
|
|
1058
|
+
required: false,
|
|
1059
|
+
defaultValue: false,
|
|
1060
|
+
readonly: true,
|
|
1061
|
+
description: "Provided by a plugin / platform; tenants may edit but should not delete",
|
|
1062
|
+
group: "Lifecycle"
|
|
1063
|
+
}),
|
|
1064
|
+
/**
|
|
1065
|
+
* Variables declared by the template author. Stored as JSON text
|
|
1066
|
+
* (array of {name,type,required,description}). Used by the Studio
|
|
1067
|
+
* authoring UI to render form hints and by sendTemplate() to
|
|
1068
|
+
* validate required vars at runtime.
|
|
1069
|
+
*/
|
|
1070
|
+
variables_json: data.Field.textarea({
|
|
1071
|
+
label: "Variables (JSON)",
|
|
1072
|
+
required: false,
|
|
1073
|
+
description: "JSON array of {name,type,required,description}",
|
|
1074
|
+
group: "Lifecycle"
|
|
1075
|
+
}),
|
|
1076
|
+
created_at: data.Field.datetime({
|
|
1077
|
+
label: "Created At",
|
|
1078
|
+
required: true,
|
|
1079
|
+
defaultValue: "NOW()",
|
|
1080
|
+
readonly: true,
|
|
1081
|
+
group: "System"
|
|
1082
|
+
}),
|
|
1083
|
+
updated_at: data.Field.datetime({ label: "Updated At", required: false, group: "System" })
|
|
1084
|
+
},
|
|
1085
|
+
indexes: [
|
|
1086
|
+
{ fields: ["name", "locale"], unique: true },
|
|
1087
|
+
{ fields: ["category"] },
|
|
1088
|
+
{ fields: ["active"] }
|
|
1089
|
+
]
|
|
1090
|
+
});
|
|
1091
|
+
var SysSavedReport = data.ObjectSchema.create({
|
|
1092
|
+
name: "sys_saved_report",
|
|
1093
|
+
label: "Saved Report",
|
|
1094
|
+
pluralLabel: "Saved Reports",
|
|
1095
|
+
icon: "bar-chart",
|
|
1096
|
+
isSystem: true,
|
|
1097
|
+
managedBy: "platform",
|
|
1098
|
+
description: "Persisted ObjectQL report definition \u2014 re-runnable and schedulable",
|
|
1099
|
+
displayNameField: "name",
|
|
1100
|
+
titleFormat: "{name}",
|
|
1101
|
+
compactLayout: ["name", "object_name", "format", "owner_id", "updated_at"],
|
|
1102
|
+
fields: {
|
|
1103
|
+
id: data.Field.text({
|
|
1104
|
+
label: "Report ID",
|
|
1105
|
+
required: true,
|
|
1106
|
+
readonly: true,
|
|
1107
|
+
group: "System"
|
|
1108
|
+
}),
|
|
1109
|
+
name: data.Field.text({
|
|
1110
|
+
label: "Name",
|
|
1111
|
+
required: true,
|
|
1112
|
+
maxLength: 200,
|
|
1113
|
+
searchable: true,
|
|
1114
|
+
group: "Definition"
|
|
1115
|
+
}),
|
|
1116
|
+
description: data.Field.textarea({
|
|
1117
|
+
label: "Description",
|
|
1118
|
+
required: false,
|
|
1119
|
+
group: "Definition"
|
|
1120
|
+
}),
|
|
1121
|
+
object_name: data.Field.text({
|
|
1122
|
+
label: "Object",
|
|
1123
|
+
required: true,
|
|
1124
|
+
maxLength: 100,
|
|
1125
|
+
description: "Short object name the report queries",
|
|
1126
|
+
group: "Definition"
|
|
1127
|
+
}),
|
|
1128
|
+
query_json: data.Field.textarea({
|
|
1129
|
+
label: "Query",
|
|
1130
|
+
required: true,
|
|
1131
|
+
description: "ObjectQL query envelope \u2014 { filter, fields, orderBy, limit, groupBy }",
|
|
1132
|
+
group: "Definition"
|
|
1133
|
+
}),
|
|
1134
|
+
format: data.Field.select(
|
|
1135
|
+
["csv", "json", "html_table"],
|
|
1136
|
+
{
|
|
1137
|
+
label: "Format",
|
|
1138
|
+
required: true,
|
|
1139
|
+
defaultValue: "csv",
|
|
1140
|
+
description: "Rendering used by IReportService.run() and email digests",
|
|
1141
|
+
group: "Definition"
|
|
1142
|
+
}
|
|
1143
|
+
),
|
|
1144
|
+
owner_id: data.Field.lookup("sys_user", {
|
|
1145
|
+
label: "Owner",
|
|
1146
|
+
required: false,
|
|
1147
|
+
description: "User that owns the report definition (drives sharing)",
|
|
1148
|
+
group: "Provenance"
|
|
1149
|
+
}),
|
|
1150
|
+
last_run_at: data.Field.datetime({
|
|
1151
|
+
label: "Last Run",
|
|
1152
|
+
required: false,
|
|
1153
|
+
description: "Stamped by IReportService.run() on successful execution",
|
|
1154
|
+
group: "State"
|
|
1155
|
+
}),
|
|
1156
|
+
last_row_count: data.Field.number({
|
|
1157
|
+
label: "Last Row Count",
|
|
1158
|
+
required: false,
|
|
1159
|
+
group: "State"
|
|
1160
|
+
}),
|
|
1161
|
+
created_at: data.Field.datetime({
|
|
1162
|
+
label: "Created At",
|
|
1163
|
+
required: true,
|
|
1164
|
+
defaultValue: "NOW()",
|
|
1165
|
+
readonly: true,
|
|
1166
|
+
group: "System"
|
|
1167
|
+
}),
|
|
1168
|
+
updated_at: data.Field.datetime({
|
|
1169
|
+
label: "Updated At",
|
|
1170
|
+
required: false,
|
|
1171
|
+
group: "System"
|
|
1172
|
+
})
|
|
1173
|
+
},
|
|
1174
|
+
indexes: [
|
|
1175
|
+
{ fields: ["object_name"] },
|
|
1176
|
+
{ fields: ["owner_id"] },
|
|
1177
|
+
{ fields: ["name"] }
|
|
1178
|
+
]
|
|
1179
|
+
});
|
|
1180
|
+
var SysReportSchedule = data.ObjectSchema.create({
|
|
1181
|
+
name: "sys_report_schedule",
|
|
1182
|
+
label: "Report Schedule",
|
|
1183
|
+
pluralLabel: "Report Schedules",
|
|
1184
|
+
icon: "clock",
|
|
1185
|
+
isSystem: true,
|
|
1186
|
+
managedBy: "platform",
|
|
1187
|
+
description: "Recurring delivery of a sys_saved_report via email",
|
|
1188
|
+
titleFormat: "{report_id} \u2192 {recipients}",
|
|
1189
|
+
compactLayout: ["report_id", "recipients", "interval_minutes", "active", "next_run_at"],
|
|
1190
|
+
fields: {
|
|
1191
|
+
id: data.Field.text({
|
|
1192
|
+
label: "Schedule ID",
|
|
1193
|
+
required: true,
|
|
1194
|
+
readonly: true,
|
|
1195
|
+
group: "System"
|
|
1196
|
+
}),
|
|
1197
|
+
report_id: data.Field.lookup("sys_saved_report", {
|
|
1198
|
+
label: "Report",
|
|
1199
|
+
required: true,
|
|
1200
|
+
group: "Schedule"
|
|
1201
|
+
}),
|
|
1202
|
+
name: data.Field.text({
|
|
1203
|
+
label: "Name",
|
|
1204
|
+
required: false,
|
|
1205
|
+
maxLength: 200,
|
|
1206
|
+
description: "Optional label for the digest \u2014 used in the email subject",
|
|
1207
|
+
group: "Schedule"
|
|
1208
|
+
}),
|
|
1209
|
+
interval_minutes: data.Field.number({
|
|
1210
|
+
label: "Interval (minutes)",
|
|
1211
|
+
required: false,
|
|
1212
|
+
defaultValue: 1440,
|
|
1213
|
+
description: "How often to send (1440 = daily, 10080 = weekly)",
|
|
1214
|
+
group: "Schedule"
|
|
1215
|
+
}),
|
|
1216
|
+
cron_expression: data.Field.text({
|
|
1217
|
+
label: "Cron Expression",
|
|
1218
|
+
required: false,
|
|
1219
|
+
maxLength: 100,
|
|
1220
|
+
description: "Optional 5/6-field cron \u2014 overrides interval_minutes when present",
|
|
1221
|
+
group: "Schedule"
|
|
1222
|
+
}),
|
|
1223
|
+
timezone: data.Field.text({
|
|
1224
|
+
label: "Timezone",
|
|
1225
|
+
required: false,
|
|
1226
|
+
maxLength: 64,
|
|
1227
|
+
defaultValue: "UTC",
|
|
1228
|
+
group: "Schedule"
|
|
1229
|
+
}),
|
|
1230
|
+
active: data.Field.boolean({
|
|
1231
|
+
label: "Active",
|
|
1232
|
+
required: true,
|
|
1233
|
+
defaultValue: true,
|
|
1234
|
+
group: "Schedule"
|
|
1235
|
+
}),
|
|
1236
|
+
recipients: data.Field.text({
|
|
1237
|
+
label: "Recipients",
|
|
1238
|
+
required: true,
|
|
1239
|
+
maxLength: 4e3,
|
|
1240
|
+
description: "Comma-separated email addresses",
|
|
1241
|
+
group: "Delivery"
|
|
1242
|
+
}),
|
|
1243
|
+
format: data.Field.select(
|
|
1244
|
+
["csv", "html_table"],
|
|
1245
|
+
{
|
|
1246
|
+
label: "Format",
|
|
1247
|
+
required: false,
|
|
1248
|
+
defaultValue: "html_table",
|
|
1249
|
+
description: "Render format \u2014 csv is attached, html_table is inlined",
|
|
1250
|
+
group: "Delivery"
|
|
1251
|
+
}
|
|
1252
|
+
),
|
|
1253
|
+
subject_template: data.Field.text({
|
|
1254
|
+
label: "Subject Template",
|
|
1255
|
+
required: false,
|
|
1256
|
+
maxLength: 200,
|
|
1257
|
+
description: "Email subject; {{name}} / {{date}} / {{rows}} are substituted",
|
|
1258
|
+
group: "Delivery"
|
|
1259
|
+
}),
|
|
1260
|
+
owner_id: data.Field.lookup("sys_user", {
|
|
1261
|
+
label: "Owner",
|
|
1262
|
+
required: false,
|
|
1263
|
+
group: "Provenance"
|
|
1264
|
+
}),
|
|
1265
|
+
next_run_at: data.Field.datetime({
|
|
1266
|
+
label: "Next Run",
|
|
1267
|
+
required: false,
|
|
1268
|
+
description: "Dispatcher loads schedules where next_run_at <= now",
|
|
1269
|
+
group: "State"
|
|
1270
|
+
}),
|
|
1271
|
+
last_sent_at: data.Field.datetime({
|
|
1272
|
+
label: "Last Sent",
|
|
1273
|
+
required: false,
|
|
1274
|
+
group: "State"
|
|
1275
|
+
}),
|
|
1276
|
+
last_status: data.Field.select(
|
|
1277
|
+
["ok", "failed", "skipped"],
|
|
1278
|
+
{
|
|
1279
|
+
label: "Last Status",
|
|
1280
|
+
required: false,
|
|
1281
|
+
group: "State"
|
|
1282
|
+
}
|
|
1283
|
+
),
|
|
1284
|
+
last_error: data.Field.textarea({
|
|
1285
|
+
label: "Last Error",
|
|
1286
|
+
required: false,
|
|
1287
|
+
group: "State"
|
|
1288
|
+
}),
|
|
1289
|
+
created_at: data.Field.datetime({
|
|
1290
|
+
label: "Created At",
|
|
1291
|
+
required: true,
|
|
1292
|
+
defaultValue: "NOW()",
|
|
1293
|
+
readonly: true,
|
|
1294
|
+
group: "System"
|
|
1295
|
+
}),
|
|
1296
|
+
updated_at: data.Field.datetime({
|
|
1297
|
+
label: "Updated At",
|
|
1298
|
+
required: false,
|
|
1299
|
+
group: "System"
|
|
1300
|
+
})
|
|
1301
|
+
},
|
|
1302
|
+
indexes: [
|
|
1303
|
+
// Hot path for the dispatch loop.
|
|
1304
|
+
{ fields: ["active", "next_run_at"] },
|
|
1305
|
+
{ fields: ["report_id"] },
|
|
1306
|
+
{ fields: ["owner_id"] }
|
|
1307
|
+
]
|
|
1308
|
+
});
|
|
1309
|
+
var SysApprovalProcess = data.ObjectSchema.create({
|
|
1310
|
+
name: "sys_approval_process",
|
|
1311
|
+
label: "Approval Process",
|
|
1312
|
+
pluralLabel: "Approval Processes",
|
|
1313
|
+
icon: "check-square",
|
|
1314
|
+
isSystem: true,
|
|
1315
|
+
managedBy: "config",
|
|
1316
|
+
// Authoring an approval process requires a visual step designer that
|
|
1317
|
+
// doesn't yet exist — the embedded `definition_json` textarea would
|
|
1318
|
+
// force admins to hand-write a multi-page ApprovalProcess envelope.
|
|
1319
|
+
// Suppress generic CRUD until the designer lands. Real authoring path:
|
|
1320
|
+
// call `defineApprovalProcess({...})` in code and seed via the
|
|
1321
|
+
// approvals service (`POST /api/v1/approvals/processes`) or commit the
|
|
1322
|
+
// definition as a fixture. Editing existing rows (e.g. toggling
|
|
1323
|
+
// `active`) is also suppressed for now because the same textarea would
|
|
1324
|
+
// appear; use the service API or a future designer instead.
|
|
1325
|
+
userActions: { create: false, edit: false, delete: false, import: false },
|
|
1326
|
+
description: "Persisted approval process definition. Authored via defineApprovalProcess() in code; visual designer is on the roadmap.",
|
|
1327
|
+
displayNameField: "name",
|
|
1328
|
+
titleFormat: "{label}",
|
|
1329
|
+
compactLayout: ["name", "object_name", "active", "updated_at"],
|
|
1330
|
+
listViews: {
|
|
1331
|
+
active: {
|
|
1332
|
+
type: "grid",
|
|
1333
|
+
name: "active",
|
|
1334
|
+
label: "Active",
|
|
1335
|
+
data: { provider: "object", object: "sys_approval_process" },
|
|
1336
|
+
columns: ["label", "object_name", "active", "updated_at"],
|
|
1337
|
+
filter: [{ field: "active", operator: "equals", value: true }],
|
|
1338
|
+
sort: [{ field: "label", order: "asc" }],
|
|
1339
|
+
pagination: { pageSize: 50 }
|
|
1340
|
+
},
|
|
1341
|
+
inactive: {
|
|
1342
|
+
type: "grid",
|
|
1343
|
+
name: "inactive",
|
|
1344
|
+
label: "Inactive",
|
|
1345
|
+
data: { provider: "object", object: "sys_approval_process" },
|
|
1346
|
+
columns: ["label", "object_name", "active", "updated_at"],
|
|
1347
|
+
filter: [{ field: "active", operator: "equals", value: false }],
|
|
1348
|
+
sort: [{ field: "label", order: "asc" }],
|
|
1349
|
+
pagination: { pageSize: 50 }
|
|
1350
|
+
},
|
|
1351
|
+
by_object: {
|
|
1352
|
+
type: "grid",
|
|
1353
|
+
name: "by_object",
|
|
1354
|
+
label: "By Object",
|
|
1355
|
+
data: { provider: "object", object: "sys_approval_process" },
|
|
1356
|
+
columns: ["object_name", "label", "active", "updated_at"],
|
|
1357
|
+
sort: [{ field: "object_name", order: "asc" }, { field: "label", order: "asc" }],
|
|
1358
|
+
grouping: { fields: [{ field: "object_name", order: "asc", collapsed: false }] },
|
|
1359
|
+
pagination: { pageSize: 100 }
|
|
1360
|
+
},
|
|
1361
|
+
all_processes: {
|
|
1362
|
+
type: "grid",
|
|
1363
|
+
name: "all_processes",
|
|
1364
|
+
label: "All",
|
|
1365
|
+
data: { provider: "object", object: "sys_approval_process" },
|
|
1366
|
+
columns: ["label", "object_name", "active", "updated_at"],
|
|
1367
|
+
sort: [{ field: "label", order: "asc" }],
|
|
1368
|
+
pagination: { pageSize: 50 }
|
|
1369
|
+
}
|
|
1370
|
+
},
|
|
1371
|
+
fields: {
|
|
1372
|
+
id: data.Field.text({ label: "Process ID", required: true, readonly: true, group: "System" }),
|
|
1373
|
+
name: data.Field.text({
|
|
1374
|
+
label: "Name",
|
|
1375
|
+
required: true,
|
|
1376
|
+
maxLength: 100,
|
|
1377
|
+
description: "Unique snake_case name \u2014 referenced by submitters and audit rows",
|
|
1378
|
+
group: "Definition"
|
|
1379
|
+
}),
|
|
1380
|
+
label: data.Field.text({
|
|
1381
|
+
label: "Display Label",
|
|
1382
|
+
required: true,
|
|
1383
|
+
maxLength: 200,
|
|
1384
|
+
group: "Definition"
|
|
1385
|
+
}),
|
|
1386
|
+
object_name: data.Field.text({
|
|
1387
|
+
label: "Object",
|
|
1388
|
+
required: true,
|
|
1389
|
+
maxLength: 100,
|
|
1390
|
+
description: "Short object name this process governs",
|
|
1391
|
+
group: "Definition"
|
|
1392
|
+
}),
|
|
1393
|
+
description: data.Field.textarea({ label: "Description", required: false, group: "Definition" }),
|
|
1394
|
+
active: data.Field.boolean({
|
|
1395
|
+
label: "Active",
|
|
1396
|
+
required: true,
|
|
1397
|
+
defaultValue: false,
|
|
1398
|
+
description: "Only active processes are dispatched on submission",
|
|
1399
|
+
group: "Definition"
|
|
1400
|
+
}),
|
|
1401
|
+
definition_json: data.Field.textarea({
|
|
1402
|
+
label: "Definition",
|
|
1403
|
+
required: true,
|
|
1404
|
+
description: "Serialised ApprovalProcess JSON (see @objectstack/spec/automation/approval)",
|
|
1405
|
+
group: "Definition"
|
|
1406
|
+
}),
|
|
1407
|
+
created_at: data.Field.datetime({
|
|
1408
|
+
label: "Created At",
|
|
1409
|
+
required: true,
|
|
1410
|
+
defaultValue: "NOW()",
|
|
1411
|
+
readonly: true,
|
|
1412
|
+
group: "System"
|
|
1413
|
+
}),
|
|
1414
|
+
updated_at: data.Field.datetime({ label: "Updated At", required: false, group: "System" })
|
|
1415
|
+
},
|
|
1416
|
+
indexes: [
|
|
1417
|
+
{ fields: ["name"], unique: true },
|
|
1418
|
+
{ fields: ["object_name"] },
|
|
1419
|
+
{ fields: ["active", "object_name"] }
|
|
1420
|
+
]
|
|
1421
|
+
});
|
|
1422
|
+
var SysApprovalRequest = data.ObjectSchema.create({
|
|
1423
|
+
name: "sys_approval_request",
|
|
1424
|
+
label: "Approval Request",
|
|
1425
|
+
pluralLabel: "Approval Requests",
|
|
1426
|
+
icon: "inbox",
|
|
1427
|
+
isSystem: true,
|
|
1428
|
+
managedBy: "system",
|
|
1429
|
+
description: "Live approval instance tracked per submission",
|
|
1430
|
+
displayNameField: "id",
|
|
1431
|
+
titleFormat: "{process_name} \xB7 {record_id}",
|
|
1432
|
+
compactLayout: ["process_name", "object_name", "record_id", "status", "current_step", "submitter_id", "updated_at"],
|
|
1433
|
+
// Curated built-in list views — render as segmented tabs in the console.
|
|
1434
|
+
// Filters use {current_user_id} substitution wired by the console.
|
|
1435
|
+
listViews: {
|
|
1436
|
+
my_pending: {
|
|
1437
|
+
type: "grid",
|
|
1438
|
+
name: "my_pending",
|
|
1439
|
+
label: "My Pending",
|
|
1440
|
+
data: { provider: "object", object: "sys_approval_request" },
|
|
1441
|
+
columns: ["process_name", "object_name", "record_id", "current_step", "submitter_id", "updated_at"],
|
|
1442
|
+
filter: [
|
|
1443
|
+
{ field: "status", operator: "equals", value: "pending" },
|
|
1444
|
+
{ field: "pending_approvers", operator: "contains", value: "{current_user_id}" }
|
|
1445
|
+
],
|
|
1446
|
+
sort: [{ field: "updated_at", order: "desc" }],
|
|
1447
|
+
pagination: { pageSize: 25 },
|
|
1448
|
+
emptyState: { title: "No pending approvals", message: "You're all caught up." }
|
|
1449
|
+
},
|
|
1450
|
+
submitted_by_me: {
|
|
1451
|
+
type: "grid",
|
|
1452
|
+
name: "submitted_by_me",
|
|
1453
|
+
label: "I Submitted",
|
|
1454
|
+
data: { provider: "object", object: "sys_approval_request" },
|
|
1455
|
+
columns: ["process_name", "object_name", "record_id", "status", "current_step", "updated_at"],
|
|
1456
|
+
filter: [{ field: "submitter_id", operator: "equals", value: "{current_user_id}" }],
|
|
1457
|
+
sort: [{ field: "updated_at", order: "desc" }],
|
|
1458
|
+
pagination: { pageSize: 25 }
|
|
1459
|
+
},
|
|
1460
|
+
completed: {
|
|
1461
|
+
type: "grid",
|
|
1462
|
+
name: "completed",
|
|
1463
|
+
label: "Completed",
|
|
1464
|
+
data: { provider: "object", object: "sys_approval_request" },
|
|
1465
|
+
columns: ["process_name", "object_name", "record_id", "status", "submitter_id", "completed_at"],
|
|
1466
|
+
filter: [{ field: "status", operator: "in", value: ["approved", "rejected", "recalled"] }],
|
|
1467
|
+
sort: [{ field: "completed_at", order: "desc" }],
|
|
1468
|
+
pagination: { pageSize: 25 }
|
|
1469
|
+
},
|
|
1470
|
+
all_requests: {
|
|
1471
|
+
type: "grid",
|
|
1472
|
+
name: "all_requests",
|
|
1473
|
+
label: "All",
|
|
1474
|
+
data: { provider: "object", object: "sys_approval_request" },
|
|
1475
|
+
columns: ["process_name", "object_name", "record_id", "status", "current_step", "submitter_id", "updated_at"],
|
|
1476
|
+
sort: [{ field: "updated_at", order: "desc" }],
|
|
1477
|
+
pagination: { pageSize: 50 }
|
|
1478
|
+
}
|
|
1479
|
+
},
|
|
1480
|
+
fields: {
|
|
1481
|
+
id: data.Field.text({ label: "Request ID", required: true, readonly: true, group: "System" }),
|
|
1482
|
+
organization_id: data.Field.lookup("sys_organization", {
|
|
1483
|
+
label: "Organization",
|
|
1484
|
+
required: false,
|
|
1485
|
+
group: "System",
|
|
1486
|
+
description: "Tenant that owns this approval request (propagated from submitter context)"
|
|
1487
|
+
}),
|
|
1488
|
+
process_name: data.Field.text({
|
|
1489
|
+
label: "Process",
|
|
1490
|
+
required: true,
|
|
1491
|
+
maxLength: 100,
|
|
1492
|
+
description: "sys_approval_process.name this request was opened against",
|
|
1493
|
+
group: "Target"
|
|
1494
|
+
}),
|
|
1495
|
+
object_name: data.Field.text({
|
|
1496
|
+
label: "Object",
|
|
1497
|
+
required: true,
|
|
1498
|
+
maxLength: 100,
|
|
1499
|
+
group: "Target"
|
|
1500
|
+
}),
|
|
1501
|
+
record_id: data.Field.text({
|
|
1502
|
+
label: "Record ID",
|
|
1503
|
+
required: true,
|
|
1504
|
+
maxLength: 100,
|
|
1505
|
+
group: "Target"
|
|
1506
|
+
}),
|
|
1507
|
+
submitter_id: data.Field.lookup("sys_user", {
|
|
1508
|
+
label: "Submitter",
|
|
1509
|
+
required: false,
|
|
1510
|
+
group: "Target"
|
|
1511
|
+
}),
|
|
1512
|
+
submitter_comment: data.Field.textarea({
|
|
1513
|
+
label: "Submitter Comment",
|
|
1514
|
+
required: false,
|
|
1515
|
+
group: "Target"
|
|
1516
|
+
}),
|
|
1517
|
+
status: data.Field.select(
|
|
1518
|
+
["pending", "approved", "rejected", "recalled"],
|
|
1519
|
+
{
|
|
1520
|
+
label: "Status",
|
|
1521
|
+
required: true,
|
|
1522
|
+
defaultValue: "pending",
|
|
1523
|
+
description: "Lifecycle state of the request",
|
|
1524
|
+
group: "State"
|
|
1525
|
+
}
|
|
1526
|
+
),
|
|
1527
|
+
current_step: data.Field.text({
|
|
1528
|
+
label: "Current Step",
|
|
1529
|
+
required: false,
|
|
1530
|
+
maxLength: 100,
|
|
1531
|
+
description: "Machine name of the step awaiting approval",
|
|
1532
|
+
group: "State"
|
|
1533
|
+
}),
|
|
1534
|
+
current_step_index: data.Field.number({
|
|
1535
|
+
label: "Current Step Index",
|
|
1536
|
+
required: false,
|
|
1537
|
+
defaultValue: 0,
|
|
1538
|
+
group: "State"
|
|
1539
|
+
}),
|
|
1540
|
+
pending_approvers: data.Field.textarea({
|
|
1541
|
+
label: "Pending Approvers",
|
|
1542
|
+
required: false,
|
|
1543
|
+
description: "Comma-separated user ids who can act on the current step",
|
|
1544
|
+
group: "State"
|
|
1545
|
+
}),
|
|
1546
|
+
payload_json: data.Field.textarea({
|
|
1547
|
+
label: "Snapshot",
|
|
1548
|
+
required: false,
|
|
1549
|
+
description: "Record snapshot at submission time",
|
|
1550
|
+
group: "State"
|
|
1551
|
+
}),
|
|
1552
|
+
completed_at: data.Field.datetime({
|
|
1553
|
+
label: "Completed At",
|
|
1554
|
+
required: false,
|
|
1555
|
+
group: "State"
|
|
1556
|
+
}),
|
|
1557
|
+
created_at: data.Field.datetime({
|
|
1558
|
+
label: "Created At",
|
|
1559
|
+
required: true,
|
|
1560
|
+
defaultValue: "NOW()",
|
|
1561
|
+
readonly: true,
|
|
1562
|
+
group: "System"
|
|
1563
|
+
}),
|
|
1564
|
+
updated_at: data.Field.datetime({ label: "Updated At", required: false, group: "System" })
|
|
1565
|
+
},
|
|
1566
|
+
indexes: [
|
|
1567
|
+
// Look up "is there a pending request for this record?" — common
|
|
1568
|
+
// guard on submit and on edit-while-locked checks.
|
|
1569
|
+
{ fields: ["object_name", "record_id"] },
|
|
1570
|
+
{ fields: ["status", "object_name"] },
|
|
1571
|
+
// "My approvals" inbox — pending_approvers is a CSV string so this
|
|
1572
|
+
// index only helps with status pre-filtering; the engine does a
|
|
1573
|
+
// post-filter substring match per row.
|
|
1574
|
+
{ fields: ["status", "updated_at"] },
|
|
1575
|
+
{ fields: ["submitter_id", "status"] }
|
|
1576
|
+
]
|
|
1577
|
+
});
|
|
1578
|
+
var SysApprovalAction = data.ObjectSchema.create({
|
|
1579
|
+
name: "sys_approval_action",
|
|
1580
|
+
label: "Approval Action",
|
|
1581
|
+
pluralLabel: "Approval Actions",
|
|
1582
|
+
icon: "check-circle",
|
|
1583
|
+
isSystem: true,
|
|
1584
|
+
managedBy: "append-only",
|
|
1585
|
+
description: "Append-only audit trail for approval actions",
|
|
1586
|
+
displayNameField: "id",
|
|
1587
|
+
titleFormat: "{action} \xB7 {step_name}",
|
|
1588
|
+
compactLayout: ["request_id", "step_name", "action", "actor_id", "created_at"],
|
|
1589
|
+
listViews: {
|
|
1590
|
+
recent: {
|
|
1591
|
+
type: "grid",
|
|
1592
|
+
name: "recent",
|
|
1593
|
+
label: "Recent",
|
|
1594
|
+
data: { provider: "object", object: "sys_approval_action" },
|
|
1595
|
+
columns: ["created_at", "request_id", "step_name", "action", "actor_id", "comment"],
|
|
1596
|
+
sort: [{ field: "created_at", order: "desc" }],
|
|
1597
|
+
pagination: { pageSize: 50 },
|
|
1598
|
+
emptyState: { title: "No approval actions yet", message: "Actions are logged automatically when approvals progress." }
|
|
1599
|
+
},
|
|
1600
|
+
by_actor: {
|
|
1601
|
+
type: "grid",
|
|
1602
|
+
name: "by_actor",
|
|
1603
|
+
label: "By Actor",
|
|
1604
|
+
data: { provider: "object", object: "sys_approval_action" },
|
|
1605
|
+
columns: ["actor_id", "created_at", "request_id", "step_name", "action"],
|
|
1606
|
+
sort: [{ field: "actor_id", order: "asc" }, { field: "created_at", order: "desc" }],
|
|
1607
|
+
grouping: { fields: [{ field: "actor_id", order: "asc", collapsed: false }] },
|
|
1608
|
+
pagination: { pageSize: 100 }
|
|
1609
|
+
},
|
|
1610
|
+
all_actions: {
|
|
1611
|
+
type: "grid",
|
|
1612
|
+
name: "all_actions",
|
|
1613
|
+
label: "All",
|
|
1614
|
+
data: { provider: "object", object: "sys_approval_action" },
|
|
1615
|
+
columns: ["created_at", "request_id", "step_name", "action", "actor_id", "comment"],
|
|
1616
|
+
sort: [{ field: "created_at", order: "desc" }],
|
|
1617
|
+
pagination: { pageSize: 100 }
|
|
1618
|
+
}
|
|
1619
|
+
},
|
|
1620
|
+
fields: {
|
|
1621
|
+
id: data.Field.text({ label: "Action ID", required: true, readonly: true, group: "System" }),
|
|
1622
|
+
organization_id: data.Field.lookup("sys_organization", {
|
|
1623
|
+
label: "Organization",
|
|
1624
|
+
required: false,
|
|
1625
|
+
group: "System",
|
|
1626
|
+
description: "Tenant that owns this action (mirrors the parent request)"
|
|
1627
|
+
}),
|
|
1628
|
+
request_id: data.Field.lookup("sys_approval_request", {
|
|
1629
|
+
label: "Request",
|
|
1630
|
+
required: true,
|
|
1631
|
+
group: "Target"
|
|
1632
|
+
}),
|
|
1633
|
+
step_name: data.Field.text({
|
|
1634
|
+
label: "Step",
|
|
1635
|
+
required: false,
|
|
1636
|
+
maxLength: 100,
|
|
1637
|
+
description: "Machine name of the step at the time of the action",
|
|
1638
|
+
group: "Target"
|
|
1639
|
+
}),
|
|
1640
|
+
step_index: data.Field.number({
|
|
1641
|
+
label: "Step Index",
|
|
1642
|
+
required: false,
|
|
1643
|
+
group: "Target"
|
|
1644
|
+
}),
|
|
1645
|
+
action: data.Field.select(
|
|
1646
|
+
["submit", "approve", "reject", "recall", "escalate"],
|
|
1647
|
+
{
|
|
1648
|
+
label: "Action",
|
|
1649
|
+
required: true,
|
|
1650
|
+
group: "Action"
|
|
1651
|
+
}
|
|
1652
|
+
),
|
|
1653
|
+
actor_id: data.Field.lookup("sys_user", {
|
|
1654
|
+
label: "Actor",
|
|
1655
|
+
required: false,
|
|
1656
|
+
group: "Action"
|
|
1657
|
+
}),
|
|
1658
|
+
comment: data.Field.textarea({ label: "Comment", required: false, group: "Action" }),
|
|
1659
|
+
created_at: data.Field.datetime({
|
|
1660
|
+
label: "Created At",
|
|
1661
|
+
required: true,
|
|
1662
|
+
defaultValue: "NOW()",
|
|
1663
|
+
readonly: true,
|
|
1664
|
+
group: "System"
|
|
1665
|
+
})
|
|
1666
|
+
},
|
|
1667
|
+
indexes: [
|
|
1668
|
+
{ fields: ["request_id", "created_at"] },
|
|
1669
|
+
{ fields: ["request_id", "step_index", "action"] }
|
|
1670
|
+
]
|
|
1671
|
+
});
|
|
1672
|
+
var SysJob = data.ObjectSchema.create({
|
|
1673
|
+
name: "sys_job",
|
|
1674
|
+
label: "Background Job",
|
|
1675
|
+
pluralLabel: "Background Jobs",
|
|
1676
|
+
icon: "clock",
|
|
1677
|
+
isSystem: true,
|
|
1678
|
+
managedBy: "system",
|
|
1679
|
+
description: "Catalogue of registered background jobs",
|
|
1680
|
+
displayNameField: "name",
|
|
1681
|
+
titleFormat: "{name}",
|
|
1682
|
+
compactLayout: ["name", "schedule_type", "active", "last_run_at", "last_status"],
|
|
1683
|
+
fields: {
|
|
1684
|
+
id: data.Field.text({ label: "Job ID", required: true, readonly: true, group: "System" }),
|
|
1685
|
+
name: data.Field.text({
|
|
1686
|
+
label: "Job Name",
|
|
1687
|
+
required: true,
|
|
1688
|
+
maxLength: 255,
|
|
1689
|
+
searchable: true,
|
|
1690
|
+
description: "Unique job identifier (snake_case)",
|
|
1691
|
+
group: "Identity"
|
|
1692
|
+
}),
|
|
1693
|
+
schedule_type: data.Field.select(["cron", "interval", "once"], {
|
|
1694
|
+
label: "Schedule Type",
|
|
1695
|
+
required: true,
|
|
1696
|
+
group: "Schedule"
|
|
1697
|
+
}),
|
|
1698
|
+
schedule_expression: data.Field.text({
|
|
1699
|
+
label: "Expression",
|
|
1700
|
+
required: false,
|
|
1701
|
+
maxLength: 200,
|
|
1702
|
+
description: "Cron expression / interval ms / ISO datetime",
|
|
1703
|
+
group: "Schedule"
|
|
1704
|
+
}),
|
|
1705
|
+
timezone: data.Field.text({
|
|
1706
|
+
label: "Timezone",
|
|
1707
|
+
required: false,
|
|
1708
|
+
maxLength: 100,
|
|
1709
|
+
group: "Schedule"
|
|
1710
|
+
}),
|
|
1711
|
+
active: data.Field.boolean({
|
|
1712
|
+
label: "Active",
|
|
1713
|
+
required: true,
|
|
1714
|
+
defaultValue: true,
|
|
1715
|
+
description: "Whether the scheduler is currently running this job",
|
|
1716
|
+
group: "State"
|
|
1717
|
+
}),
|
|
1718
|
+
last_run_at: data.Field.datetime({ label: "Last Run At", required: false, group: "State" }),
|
|
1719
|
+
last_status: data.Field.select(
|
|
1720
|
+
["success", "failed", "timeout", "running"],
|
|
1721
|
+
{ label: "Last Status", required: false, group: "State" }
|
|
1722
|
+
),
|
|
1723
|
+
last_error: data.Field.textarea({ label: "Last Error", required: false, group: "State" }),
|
|
1724
|
+
run_count: data.Field.number({ label: "Run Count", required: false, defaultValue: 0, group: "State" }),
|
|
1725
|
+
failure_count: data.Field.number({ label: "Failure Count", required: false, defaultValue: 0, group: "State" }),
|
|
1726
|
+
created_at: data.Field.datetime({
|
|
1727
|
+
label: "Created At",
|
|
1728
|
+
required: true,
|
|
1729
|
+
defaultValue: "NOW()",
|
|
1730
|
+
readonly: true,
|
|
1731
|
+
group: "System"
|
|
1732
|
+
}),
|
|
1733
|
+
updated_at: data.Field.datetime({ label: "Updated At", required: false, group: "System" })
|
|
1734
|
+
},
|
|
1735
|
+
indexes: [
|
|
1736
|
+
{ fields: ["name"], unique: true },
|
|
1737
|
+
{ fields: ["active"] }
|
|
1738
|
+
]
|
|
1739
|
+
});
|
|
1740
|
+
var SysJobRun = data.ObjectSchema.create({
|
|
1741
|
+
name: "sys_job_run",
|
|
1742
|
+
label: "Job Run",
|
|
1743
|
+
pluralLabel: "Job Runs",
|
|
1744
|
+
icon: "play",
|
|
1745
|
+
isSystem: true,
|
|
1746
|
+
managedBy: "append-only",
|
|
1747
|
+
description: "Background job execution audit trail",
|
|
1748
|
+
displayNameField: "job_name",
|
|
1749
|
+
titleFormat: "{job_name} @ {started_at}",
|
|
1750
|
+
compactLayout: ["job_name", "status", "started_at", "duration_ms", "attempt"],
|
|
1751
|
+
fields: {
|
|
1752
|
+
id: data.Field.text({ label: "Run ID", required: true, readonly: true, group: "System" }),
|
|
1753
|
+
job_name: data.Field.text({
|
|
1754
|
+
label: "Job",
|
|
1755
|
+
required: true,
|
|
1756
|
+
maxLength: 255,
|
|
1757
|
+
searchable: true,
|
|
1758
|
+
group: "Identity"
|
|
1759
|
+
}),
|
|
1760
|
+
status: data.Field.select(
|
|
1761
|
+
["running", "success", "failed", "timeout"],
|
|
1762
|
+
{ label: "Status", required: true, defaultValue: "running", group: "State" }
|
|
1763
|
+
),
|
|
1764
|
+
started_at: data.Field.datetime({ label: "Started At", required: true, group: "State" }),
|
|
1765
|
+
completed_at: data.Field.datetime({ label: "Completed At", required: false, group: "State" }),
|
|
1766
|
+
duration_ms: data.Field.number({ label: "Duration (ms)", required: false, group: "State" }),
|
|
1767
|
+
attempt: data.Field.number({
|
|
1768
|
+
label: "Attempt",
|
|
1769
|
+
required: false,
|
|
1770
|
+
defaultValue: 1,
|
|
1771
|
+
description: "1 for first run, >1 for retries/replays",
|
|
1772
|
+
group: "State"
|
|
1773
|
+
}),
|
|
1774
|
+
trigger: data.Field.select(
|
|
1775
|
+
["schedule", "manual", "replay"],
|
|
1776
|
+
{ label: "Trigger", required: false, defaultValue: "schedule", group: "State" }
|
|
1777
|
+
),
|
|
1778
|
+
error: data.Field.textarea({ label: "Error", required: false, group: "State" }),
|
|
1779
|
+
created_at: data.Field.datetime({
|
|
1780
|
+
label: "Created At",
|
|
1781
|
+
required: true,
|
|
1782
|
+
defaultValue: "NOW()",
|
|
1783
|
+
readonly: true,
|
|
1784
|
+
group: "System"
|
|
1785
|
+
})
|
|
1786
|
+
},
|
|
1787
|
+
indexes: [
|
|
1788
|
+
{ fields: ["job_name", "started_at"] },
|
|
1789
|
+
{ fields: ["status", "started_at"] }
|
|
1790
|
+
]
|
|
1791
|
+
});
|
|
1792
|
+
var SysJobQueue = data.ObjectSchema.create({
|
|
1793
|
+
name: "sys_job_queue",
|
|
1794
|
+
label: "Job Queue Message",
|
|
1795
|
+
pluralLabel: "Job Queue Messages",
|
|
1796
|
+
icon: "inbox",
|
|
1797
|
+
isSystem: true,
|
|
1798
|
+
managedBy: "system",
|
|
1799
|
+
description: "Durable job/message queue including dead letters",
|
|
1800
|
+
displayNameField: "queue",
|
|
1801
|
+
titleFormat: "{queue} #{id}",
|
|
1802
|
+
compactLayout: ["queue", "status", "attempts", "scheduled_for", "last_error"],
|
|
1803
|
+
fields: {
|
|
1804
|
+
id: data.Field.text({ label: "Message ID", required: true, readonly: true, group: "System" }),
|
|
1805
|
+
queue: data.Field.text({
|
|
1806
|
+
label: "Queue",
|
|
1807
|
+
required: true,
|
|
1808
|
+
maxLength: 255,
|
|
1809
|
+
searchable: true,
|
|
1810
|
+
description: "Logical queue name (snake_case)",
|
|
1811
|
+
group: "Identity"
|
|
1812
|
+
}),
|
|
1813
|
+
idempotency_key: data.Field.text({
|
|
1814
|
+
label: "Idempotency Key",
|
|
1815
|
+
required: false,
|
|
1816
|
+
maxLength: 255,
|
|
1817
|
+
description: "Deduplication key within (queue, window)",
|
|
1818
|
+
group: "Identity"
|
|
1819
|
+
}),
|
|
1820
|
+
payload_json: data.Field.textarea({
|
|
1821
|
+
label: "Payload (JSON)",
|
|
1822
|
+
required: false,
|
|
1823
|
+
description: "Serialized message body",
|
|
1824
|
+
group: "Content"
|
|
1825
|
+
}),
|
|
1826
|
+
metadata_json: data.Field.textarea({
|
|
1827
|
+
label: "Metadata (JSON)",
|
|
1828
|
+
required: false,
|
|
1829
|
+
description: "Serialized metadata bag (tenant_id, source_record, ...)",
|
|
1830
|
+
group: "Content"
|
|
1831
|
+
}),
|
|
1832
|
+
status: data.Field.select(
|
|
1833
|
+
["pending", "running", "completed", "failed", "dlq"],
|
|
1834
|
+
{
|
|
1835
|
+
label: "Status",
|
|
1836
|
+
required: true,
|
|
1837
|
+
defaultValue: "pending",
|
|
1838
|
+
description: "Lifecycle state",
|
|
1839
|
+
group: "State"
|
|
1840
|
+
}
|
|
1841
|
+
),
|
|
1842
|
+
priority: data.Field.number({
|
|
1843
|
+
label: "Priority",
|
|
1844
|
+
required: false,
|
|
1845
|
+
defaultValue: 100,
|
|
1846
|
+
description: "Lower = higher priority",
|
|
1847
|
+
group: "Schedule"
|
|
1848
|
+
}),
|
|
1849
|
+
attempts: data.Field.number({ label: "Attempts", required: false, defaultValue: 0, group: "State" }),
|
|
1850
|
+
max_attempts: data.Field.number({ label: "Max Attempts", required: false, defaultValue: 3, group: "State" }),
|
|
1851
|
+
backoff_type: data.Field.select(
|
|
1852
|
+
["fixed", "exponential"],
|
|
1853
|
+
{ label: "Backoff", required: false, defaultValue: "exponential", group: "Schedule" }
|
|
1854
|
+
),
|
|
1855
|
+
backoff_delay_ms: data.Field.number({
|
|
1856
|
+
label: "Backoff Base (ms)",
|
|
1857
|
+
required: false,
|
|
1858
|
+
defaultValue: 1e3,
|
|
1859
|
+
group: "Schedule"
|
|
1860
|
+
}),
|
|
1861
|
+
backoff_max_delay_ms: data.Field.number({
|
|
1862
|
+
label: "Backoff Cap (ms)",
|
|
1863
|
+
required: false,
|
|
1864
|
+
group: "Schedule"
|
|
1865
|
+
}),
|
|
1866
|
+
scheduled_for: data.Field.datetime({
|
|
1867
|
+
label: "Scheduled For",
|
|
1868
|
+
required: false,
|
|
1869
|
+
description: "Earliest time a worker may lease this message",
|
|
1870
|
+
group: "Schedule"
|
|
1871
|
+
}),
|
|
1872
|
+
locked_by: data.Field.text({
|
|
1873
|
+
label: "Locked By",
|
|
1874
|
+
required: false,
|
|
1875
|
+
maxLength: 255,
|
|
1876
|
+
description: "Worker id holding the lease",
|
|
1877
|
+
group: "Lease"
|
|
1878
|
+
}),
|
|
1879
|
+
locked_until: data.Field.datetime({
|
|
1880
|
+
label: "Locked Until",
|
|
1881
|
+
required: false,
|
|
1882
|
+
description: "Lease expiry; if past, another worker may claim",
|
|
1883
|
+
group: "Lease"
|
|
1884
|
+
}),
|
|
1885
|
+
last_error: data.Field.textarea({ label: "Last Error", required: false, group: "State" }),
|
|
1886
|
+
completed_at: data.Field.datetime({ label: "Completed At", required: false, group: "State" }),
|
|
1887
|
+
created_at: data.Field.datetime({
|
|
1888
|
+
label: "Created At",
|
|
1889
|
+
required: true,
|
|
1890
|
+
defaultValue: "NOW()",
|
|
1891
|
+
readonly: true,
|
|
1892
|
+
group: "System"
|
|
1893
|
+
}),
|
|
1894
|
+
updated_at: data.Field.datetime({ label: "Updated At", required: false, group: "System" })
|
|
1895
|
+
},
|
|
1896
|
+
indexes: [
|
|
1897
|
+
{ fields: ["queue", "status", "scheduled_for"] },
|
|
1898
|
+
{ fields: ["idempotency_key", "queue"] },
|
|
1899
|
+
{ fields: ["status"] }
|
|
1900
|
+
]
|
|
1901
|
+
});
|
|
486
1902
|
|
|
487
1903
|
exports.SysActivity = SysActivity;
|
|
1904
|
+
exports.SysApprovalAction = SysApprovalAction;
|
|
1905
|
+
exports.SysApprovalProcess = SysApprovalProcess;
|
|
1906
|
+
exports.SysApprovalRequest = SysApprovalRequest;
|
|
1907
|
+
exports.SysAttachment = SysAttachment;
|
|
488
1908
|
exports.SysAuditLog = SysAuditLog;
|
|
489
1909
|
exports.SysComment = SysComment;
|
|
1910
|
+
exports.SysEmail = SysEmail;
|
|
1911
|
+
exports.SysEmailTemplate = SysEmailTemplate;
|
|
1912
|
+
exports.SysJob = SysJob;
|
|
1913
|
+
exports.SysJobQueue = SysJobQueue;
|
|
1914
|
+
exports.SysJobRun = SysJobRun;
|
|
1915
|
+
exports.SysNotification = SysNotification;
|
|
490
1916
|
exports.SysPresence = SysPresence;
|
|
1917
|
+
exports.SysReportSchedule = SysReportSchedule;
|
|
1918
|
+
exports.SysSavedReport = SysSavedReport;
|
|
491
1919
|
//# sourceMappingURL=index.js.map
|
|
492
1920
|
//# sourceMappingURL=index.js.map
|