web-mojo 2.1.827 → 2.1.867

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.
Files changed (64) hide show
  1. package/dist/admin.cjs.js +1 -1
  2. package/dist/admin.cjs.js.map +1 -1
  3. package/dist/admin.es.js +303 -38
  4. package/dist/admin.es.js.map +1 -1
  5. package/dist/auth.cjs.js +1 -1
  6. package/dist/auth.cjs.js.map +1 -1
  7. package/dist/auth.es.js +3 -3
  8. package/dist/auth.es.js.map +1 -1
  9. package/dist/charts.cjs.js +1 -1
  10. package/dist/charts.es.js +2 -2
  11. package/dist/chunks/{ChatView-DJK49g8O.js → ChatView-8wuIN1zH.js} +327 -53
  12. package/dist/chunks/ChatView-8wuIN1zH.js.map +1 -0
  13. package/dist/chunks/ChatView-BtoAS3jU.js +2 -0
  14. package/dist/chunks/ChatView-BtoAS3jU.js.map +1 -0
  15. package/dist/chunks/{ContextMenu-K8sVV2Sf.js → ContextMenu-Bem9gIL6.js} +2 -2
  16. package/dist/chunks/{ContextMenu-K8sVV2Sf.js.map → ContextMenu-Bem9gIL6.js.map} +1 -1
  17. package/dist/chunks/{ContextMenu-CRwv6-gc.js → ContextMenu-DPxZgf69.js} +3 -2
  18. package/dist/chunks/{ContextMenu-CRwv6-gc.js.map → ContextMenu-DPxZgf69.js.map} +1 -1
  19. package/dist/chunks/{DataView-DoKRJHii.js → DataView-C9O8uelM.js} +2 -2
  20. package/dist/chunks/{DataView-DoKRJHii.js.map → DataView-C9O8uelM.js.map} +1 -1
  21. package/dist/chunks/{DataView-3iqbZ_4y.js → DataView-DhbOr4Wm.js} +2 -2
  22. package/dist/chunks/{DataView-3iqbZ_4y.js.map → DataView-DhbOr4Wm.js.map} +1 -1
  23. package/dist/chunks/{Dialog-Bup1WdQn.js → Dialog-CQA_WzmY.js} +5 -5
  24. package/dist/chunks/{Dialog-Bup1WdQn.js.map → Dialog-CQA_WzmY.js.map} +1 -1
  25. package/dist/chunks/{Dialog-CEEzQOVl.js → Dialog-gO9ln-ej.js} +2 -2
  26. package/dist/chunks/{Dialog-CEEzQOVl.js.map → Dialog-gO9ln-ej.js.map} +1 -1
  27. package/dist/chunks/{FormView-C4Po3Q3z.js → FormView-DbPm-jDU.js} +4 -4
  28. package/dist/chunks/FormView-DbPm-jDU.js.map +1 -0
  29. package/dist/chunks/FormView-DkxYCmes.js +3 -0
  30. package/dist/chunks/FormView-DkxYCmes.js.map +1 -0
  31. package/dist/chunks/{MetricsMiniChartWidget-CxTnMgp6.js → MetricsMiniChartWidget-C41dYV1K.js} +2 -2
  32. package/dist/chunks/{MetricsMiniChartWidget-CxTnMgp6.js.map → MetricsMiniChartWidget-C41dYV1K.js.map} +1 -1
  33. package/dist/chunks/{MetricsMiniChartWidget-CK6K9Kh6.js → MetricsMiniChartWidget-Dz2d0GG2.js} +3 -3
  34. package/dist/chunks/{MetricsMiniChartWidget-CK6K9Kh6.js.map → MetricsMiniChartWidget-Dz2d0GG2.js.map} +1 -1
  35. package/dist/chunks/{PDFViewer-DgXB4rR9.js → PDFViewer-BJT0-zQL.js} +3 -3
  36. package/dist/chunks/{PDFViewer-DgXB4rR9.js.map → PDFViewer-BJT0-zQL.js.map} +1 -1
  37. package/dist/chunks/{PDFViewer-BvBw9BNk.js → PDFViewer-DvTxpaXI.js} +2 -2
  38. package/dist/chunks/{PDFViewer-BvBw9BNk.js.map → PDFViewer-DvTxpaXI.js.map} +1 -1
  39. package/dist/chunks/{Page-DaptCfWp.js → Page-CeYPFh_j.js} +2 -2
  40. package/dist/chunks/{Page-DaptCfWp.js.map → Page-CeYPFh_j.js.map} +1 -1
  41. package/dist/chunks/{Page-BJxsE5Os.js → Page-xjtBxzqE.js} +2 -2
  42. package/dist/chunks/{Page-BJxsE5Os.js.map → Page-xjtBxzqE.js.map} +1 -1
  43. package/dist/chunks/{TopNav-BkpUjJB2.js → TopNav-CcS_qMvn.js} +2 -2
  44. package/dist/chunks/{TopNav-BkpUjJB2.js.map → TopNav-CcS_qMvn.js.map} +1 -1
  45. package/dist/chunks/{TopNav-BjJojcF7.js → TopNav-baMWNGG2.js} +5 -5
  46. package/dist/chunks/{TopNav-BjJojcF7.js.map → TopNav-baMWNGG2.js.map} +1 -1
  47. package/dist/chunks/{WebApp-Cd21W4Hv.js → WebApp-DLAySO90.js} +13 -13
  48. package/dist/chunks/{WebApp-Cd21W4Hv.js.map → WebApp-DLAySO90.js.map} +1 -1
  49. package/dist/chunks/{WebApp-9L0mkLAO.js → WebApp-DkJV3BmZ.js} +2 -2
  50. package/dist/chunks/{WebApp-9L0mkLAO.js.map → WebApp-DkJV3BmZ.js.map} +1 -1
  51. package/dist/docit.cjs.js +1 -1
  52. package/dist/docit.es.js +5 -5
  53. package/dist/index.cjs.js +1 -1
  54. package/dist/index.cjs.js.map +1 -1
  55. package/dist/index.es.js +58 -52
  56. package/dist/lightbox.cjs.js +1 -1
  57. package/dist/lightbox.es.js +4 -4
  58. package/package.json +1 -1
  59. package/dist/chunks/ChatView-CM0ATZID.js +0 -2
  60. package/dist/chunks/ChatView-CM0ATZID.js.map +0 -1
  61. package/dist/chunks/ChatView-DJK49g8O.js.map +0 -1
  62. package/dist/chunks/FormView-BEIw_LH9.js +0 -3
  63. package/dist/chunks/FormView-BEIw_LH9.js.map +0 -1
  64. package/dist/chunks/FormView-C4Po3Q3z.js.map +0 -1
package/dist/admin.es.js CHANGED
@@ -1,13 +1,13 @@
1
- import { P as Page } from "./chunks/Page-DaptCfWp.js";
2
- import { V as View, h as MOJOUtils } from "./chunks/WebApp-Cd21W4Hv.js";
3
- import { B, b, a, c, e, f, W } from "./chunks/WebApp-Cd21W4Hv.js";
4
- import Dialog$1 from "./chunks/Dialog-Bup1WdQn.js";
5
- import { M as MetricsChart, c as MetricsMiniChartWidget, P as PieChart } from "./chunks/MetricsMiniChartWidget-CK6K9Kh6.js";
6
- import { b as TablePage, k as EmailDomainForms, j as EmailDomainList, E as EmailDomain, n as MailboxForms, m as MailboxList, l as Mailbox, r as EmailTemplate, d as TabView, t as EmailTemplateForms, s as EmailTemplateList, I as IncidentEvent, B as IncidentEventForms, A as IncidentEventList, w as FileManagerForms, v as FileManagerList, x as File, T as TableView, z as FileForms, y as FileList, ap as GeoLocatedIP, aq as GeoLocatedIPList, aa as MemberList, a9 as LogList, as as TicketList, G as IncidentList, Y as IncidentStats, R as IncidentHistoryList, Q as IncidentHistory, D as Incident, C as ChatView, H as IncidentForms, Z as Job, a3 as JobEventList, a1 as JobLogList, $ as JobForms, _ as JobList, a6 as JobRunnerList, a4 as JobsEngineStats, a7 as JobRunnerForms, a5 as JobRunner, a8 as Log, M as Member, ab as MemberForms, ac as MetricsPermission, ae as MetricsForms, ad as MetricsPermissionList, an as PushConfigForms, ak as PushConfigList, am as PushDeliveryList, ag as PushDeviceList, ao as PushTemplateForms, ai as PushTemplateList, U as RuleSet, X as RuleList, V as RuleSetList, i as S3BucketForms, h as S3BucketList, o as SentMessage, p as SentMessageList, au as TicketNoteList, at as TicketNote, ar as Ticket, av as TicketForms, aw as TicketCategories } from "./chunks/ChatView-DJK49g8O.js";
7
- import DataView from "./chunks/DataView-3iqbZ_4y.js";
8
- import { b as ContextMenu, C as Collection, a as Group, G as GroupList, c as GroupForms, g as UserDevice, j as UserDeviceLocationList, h as UserDeviceList, U as User, f as UserDataView, e as UserForms, d as UserList } from "./chunks/ContextMenu-CRwv6-gc.js";
9
- import { L as LightboxGallery, P as PDFViewer } from "./chunks/PDFViewer-DgXB4rR9.js";
10
- import { a as applyFileDropMixin, F as FormView } from "./chunks/FormView-C4Po3Q3z.js";
1
+ import { P as Page } from "./chunks/Page-CeYPFh_j.js";
2
+ import { V as View, h as MOJOUtils } from "./chunks/WebApp-DLAySO90.js";
3
+ import { B, b, a, c, e, f, W } from "./chunks/WebApp-DLAySO90.js";
4
+ import Dialog$1 from "./chunks/Dialog-CQA_WzmY.js";
5
+ import { M as MetricsChart, c as MetricsMiniChartWidget, P as PieChart } from "./chunks/MetricsMiniChartWidget-Dz2d0GG2.js";
6
+ import { b as TablePage, k as EmailDomainForms, j as EmailDomainList, E as EmailDomain, n as MailboxForms, m as MailboxList, l as Mailbox, r as EmailTemplate, d as TabView, t as EmailTemplateForms, s as EmailTemplateList, I as IncidentEvent, B as IncidentEventForms, A as IncidentEventList, w as FileManagerForms, v as FileManagerList, x as File, T as TableView, z as FileForms, y as FileList, av as GeoLocatedIP, aw as GeoLocatedIPList, ag as MemberList, af as LogList, ay as TicketList, G as IncidentList, _ as IncidentStats, R as IncidentHistoryList, Q as IncidentHistory, D as Incident, C as ChatView, H as IncidentForms, a3 as Job, a9 as JobEventList, a7 as JobLogList, a5 as JobForms, a4 as JobList, ac as JobRunnerList, aa as JobsEngineStats, ad as JobRunnerForms, ab as JobRunner, ae as Log, M as Member, ah as MemberForms, ai as MetricsPermission, ak as MetricsForms, aj as MetricsPermissionList, at as PushConfigForms, aq as PushConfigList, as as PushDeliveryList, am as PushDeviceList, au as PushTemplateForms, ao as PushTemplateList, U as RuleSet, a0 as MatchByOptions, $ as BundleByOptions, Y as RuleList, V as RuleSetList, i as S3BucketForms, h as S3BucketList, o as SentMessage, p as SentMessageList, aA as TicketNoteList, az as TicketNote, ax as Ticket, aB as TicketForms, aC as TicketCategories } from "./chunks/ChatView-8wuIN1zH.js";
7
+ import DataView from "./chunks/DataView-DhbOr4Wm.js";
8
+ import { b as ContextMenu, C as Collection, a as Group, G as GroupList, c as GroupForms, g as UserDevice, j as UserDeviceLocationList, h as UserDeviceList, U as User, f as UserDataView, e as UserForms, d as UserList } from "./chunks/ContextMenu-DPxZgf69.js";
9
+ import { L as LightboxGallery, P as PDFViewer } from "./chunks/PDFViewer-BJT0-zQL.js";
10
+ import { a as applyFileDropMixin, F as FormView } from "./chunks/FormView-DbPm-jDU.js";
11
11
  class AdminHeaderView extends View {
12
12
  constructor(options = {}) {
13
13
  super({
@@ -831,6 +831,129 @@ class EmailTemplateTablePage extends TablePage {
831
831
  });
832
832
  }
833
833
  }
834
+ class StackTraceView extends View {
835
+ constructor(options = {}) {
836
+ super({
837
+ className: "stack-trace-view",
838
+ ...options
839
+ });
840
+ this.stackTrace = options.stackTrace || "";
841
+ this.template = `
842
+ <div class="stack-trace-container p-3">
843
+ <style>
844
+ .stack-trace-line {
845
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', monospace;
846
+ font-size: 13px;
847
+ line-height: 1.6;
848
+ padding: 4px 8px;
849
+ margin: 0;
850
+ border-left: 3px solid transparent;
851
+ }
852
+ .stack-trace-line:hover {
853
+ background-color: rgba(0, 0, 0, 0.05);
854
+ }
855
+ .stack-trace-error {
856
+ color: #dc3545;
857
+ font-weight: 600;
858
+ border-left-color: #dc3545;
859
+ background-color: rgba(220, 53, 69, 0.05);
860
+ }
861
+ .stack-trace-file {
862
+ color: #0d6efd;
863
+ font-weight: 500;
864
+ border-left-color: #0d6efd;
865
+ }
866
+ .stack-trace-function {
867
+ color: #6610f2;
868
+ font-weight: 500;
869
+ }
870
+ .stack-trace-location {
871
+ color: #6c757d;
872
+ font-size: 12px;
873
+ }
874
+ .stack-trace-line-number {
875
+ color: #fd7e14;
876
+ font-weight: 600;
877
+ }
878
+ .stack-trace-context {
879
+ color: #495057;
880
+ background-color: rgba(0, 0, 0, 0.02);
881
+ }
882
+ .stack-trace-container {
883
+ background-color: #f8f9fa;
884
+ border: 1px solid #dee2e6;
885
+ border-radius: 0.375rem;
886
+ max-height: 600px;
887
+ overflow-y: auto;
888
+ }
889
+ </style>
890
+ <div class="stack-trace-content">
891
+ {{{formattedStackTrace}}}
892
+ </div>
893
+ </div>
894
+ `;
895
+ }
896
+ async onBeforeRender() {
897
+ this.formattedStackTrace = this.formatStackTrace(this.stackTrace);
898
+ }
899
+ formatStackTrace(stackTrace) {
900
+ if (!stackTrace) {
901
+ return '<div class="text-muted p-3">No stack trace available</div>';
902
+ }
903
+ const traceStr = typeof stackTrace === "string" ? stackTrace : JSON.stringify(stackTrace, null, 2);
904
+ const lines = traceStr.split("\n");
905
+ let html = "";
906
+ lines.forEach((line, index) => {
907
+ if (!line.trim()) {
908
+ html += '<div class="stack-trace-line">&nbsp;</div>';
909
+ return;
910
+ }
911
+ if (index === 0 && (line.includes("Error:") || line.includes("Exception:"))) {
912
+ html += `<div class="stack-trace-line stack-trace-error">${this.escapeHtml(line)}</div>`;
913
+ return;
914
+ }
915
+ const filePattern = /(.+?)\s*\(([^:]+):(\d+):(\d+)\)/;
916
+ const simpleFilePattern = /^\s*at\s+([^:]+):(\d+):(\d+)/;
917
+ let match = line.match(filePattern);
918
+ if (match) {
919
+ const [, funcName, filePath, lineNum, colNum] = match;
920
+ html += `<div class="stack-trace-line stack-trace-file">
921
+ <span class="stack-trace-function">${this.escapeHtml(funcName.trim())}</span>
922
+ <span class="stack-trace-location"> (${this.escapeHtml(filePath)}:<span class="stack-trace-line-number">${lineNum}</span>:${colNum})</span>
923
+ </div>`;
924
+ return;
925
+ }
926
+ match = line.match(simpleFilePattern);
927
+ if (match) {
928
+ const [, filePath, lineNum, colNum] = match;
929
+ html += `<div class="stack-trace-line stack-trace-file">
930
+ <span class="stack-trace-location">at ${this.escapeHtml(filePath)}:<span class="stack-trace-line-number">${lineNum}</span>:${colNum}</span>
931
+ </div>`;
932
+ return;
933
+ }
934
+ const pythonPattern = /File\s+"([^"]+)",\s+line\s+(\d+),\s+in\s+(.+)/;
935
+ match = line.match(pythonPattern);
936
+ if (match) {
937
+ const [, filePath, lineNum, funcName] = match;
938
+ html += `<div class="stack-trace-line stack-trace-file">
939
+ <span class="stack-trace-location">File "${this.escapeHtml(filePath)}", line <span class="stack-trace-line-number">${lineNum}</span>, in </span>
940
+ <span class="stack-trace-function">${this.escapeHtml(funcName)}</span>
941
+ </div>`;
942
+ return;
943
+ }
944
+ if (line.trim().startsWith("at ")) {
945
+ html += `<div class="stack-trace-line stack-trace-file">${this.escapeHtml(line)}</div>`;
946
+ return;
947
+ }
948
+ html += `<div class="stack-trace-line stack-trace-context">${this.escapeHtml(line)}</div>`;
949
+ });
950
+ return html;
951
+ }
952
+ updateStackTrace(newStackTrace) {
953
+ this.stackTrace = newStackTrace;
954
+ this.render();
955
+ }
956
+ }
834
957
  class EventView extends View {
835
958
  constructor(options = {}) {
836
959
  super({
@@ -886,20 +1009,20 @@ class EventView extends View {
886
1009
  { name: "details", label: "Details", columns: 12 }
887
1010
  ]
888
1011
  });
889
- this.metadataView = new View({
890
- model: this.model,
891
- template: `<pre class="bg-light p-3 border rounded"><code>{{{model.metadata|json}}}</code></pre>`
892
- });
893
1012
  const tabs = { "Overview": this.overviewView };
894
- if (this.model.get("metadata") && Object.keys(this.model.get("metadata")).length > 0) {
895
- tabs["Metadata"] = this.metadataView;
1013
+ const metadata = this.model.get("metadata") || {};
1014
+ if (metadata.stack_trace) {
1015
+ this.stackTraceView = new StackTraceView({
1016
+ stackTrace: metadata.stack_trace
1017
+ });
1018
+ tabs["Stack Trace"] = this.stackTraceView;
896
1019
  }
897
- if (this.model.get("metadata.stack_trace")) {
898
- this.stackTraceView = new View({
1020
+ if (Object.keys(metadata).length > 0) {
1021
+ this.metadataView = new View({
899
1022
  model: this.model,
900
- template: `<pre class="bg-dark text-white p-3 border rounded">{{{model.metadata.stack_trace}}}</pre>`
1023
+ template: `<pre class="bg-light p-3 border rounded"><code>{{{model.metadata|json}}}</code></pre>`
901
1024
  });
902
- tabs["Stack Trace"] = this.stackTraceView;
1025
+ tabs["Metadata"] = this.metadataView;
903
1026
  }
904
1027
  this.tabView = new TabView({
905
1028
  containerId: "event-tabs",
@@ -1467,7 +1590,7 @@ class FileTablePage extends TablePage {
1467
1590
  */
1468
1591
  async onActionAdd(event, element) {
1469
1592
  event.preventDefault();
1470
- const Dialog2 = (await import("./chunks/Dialog-Bup1WdQn.js")).default;
1593
+ const Dialog2 = (await import("./chunks/Dialog-CQA_WzmY.js")).default;
1471
1594
  const formData = await Dialog2.showForm({
1472
1595
  title: "Upload File",
1473
1596
  size: "md",
@@ -2365,13 +2488,39 @@ class IncidentView extends View {
2365
2488
  { name: "details", label: "Details", columns: 12, format: "pre" }
2366
2489
  ]
2367
2490
  });
2491
+ const eventsCollection = new IncidentEventList({
2492
+ params: { incident: this.model.get("id") }
2493
+ });
2494
+ this.eventsView = new TableView({
2495
+ collection: eventsCollection,
2496
+ hideActivePillNames: ["incident"],
2497
+ columns: [
2498
+ { key: "id", label: "ID", width: "70px", sortable: true },
2499
+ { key: "created", label: "Date", formatter: "datetime", sortable: true, width: "180px" },
2500
+ { key: "category", label: "Category", formatter: "badge", sortable: true },
2501
+ { key: "title", label: "Title", sortable: true },
2502
+ { key: "level", label: "Level", sortable: true, width: "80px" }
2503
+ ],
2504
+ showAdd: false,
2505
+ actions: ["view"],
2506
+ paginated: true,
2507
+ size: 10
2508
+ });
2368
2509
  const adapter = new IncidentHistoryAdapter(this.model.get("id"));
2369
2510
  this.historyView = new ChatView({ adapter });
2370
2511
  const tabs = {
2371
2512
  "Overview": this.overviewView,
2513
+ "Events": this.eventsView,
2372
2514
  "History & Comments": this.historyView
2373
2515
  };
2374
- if (this.model.get("metadata") && Object.keys(this.model.get("metadata")).length > 0) {
2516
+ const metadata = this.model.get("metadata") || {};
2517
+ if (metadata.stack_trace) {
2518
+ this.stackTraceView = new StackTraceView({
2519
+ stackTrace: metadata.stack_trace
2520
+ });
2521
+ tabs["Stack Trace"] = this.stackTraceView;
2522
+ }
2523
+ if (Object.keys(metadata).length > 0) {
2375
2524
  this.metadataView = new View({
2376
2525
  model: this.model,
2377
2526
  template: `<pre class="bg-light p-3 border rounded"><code>{{{model.metadata|json}}}</code></pre>`
@@ -2507,7 +2656,7 @@ class IncidentTablePage extends TablePage {
2507
2656
  { label: "Resolve", icon: "bi bi-check-circle", action: "resolve" },
2508
2657
  { label: "Pause", icon: "bi bi-pause-circle", action: "pause" },
2509
2658
  { label: "Ignore", icon: "bi bi-x-circle", action: "ignore" },
2510
- { label: "Change Status", icon: "bi bi-arrow-repeat", action: "status" }
2659
+ { label: "Merge", icon: "bi bi-merge", action: "merge" }
2511
2660
  ],
2512
2661
  // Table display options
2513
2662
  tableOptions: {
@@ -2554,6 +2703,29 @@ class IncidentTablePage extends TablePage {
2554
2703
  await Promise.all(selected.map((item) => item.model.save({ status: "ignored" })));
2555
2704
  this.tableView.collection.fetch();
2556
2705
  }
2706
+ async onActionBatchMerge(_event, _element) {
2707
+ const selected = this.tableView.getSelectedItems();
2708
+ if (!selected.length) return;
2709
+ const app = this.getApp();
2710
+ const result = await app.showForm({
2711
+ title: `Merge ${selected.length} incidents`,
2712
+ fields: [
2713
+ {
2714
+ name: "merge",
2715
+ type: "select",
2716
+ label: "Select Parent Incident",
2717
+ options: selected.map((item) => ({ value: item.model.id, label: item.model.id })),
2718
+ required: true
2719
+ }
2720
+ ]
2721
+ });
2722
+ if (!result) return;
2723
+ const parentModel = selected.find((item) => item.model.id == result.merge)?.model;
2724
+ if (!parentModel) return;
2725
+ const mergeIds = selected.map((item) => item.model.id).filter((id) => id != result.merge);
2726
+ await parentModel.save({ merge: mergeIds });
2727
+ this.tableView.collection.fetch();
2728
+ }
2557
2729
  }
2558
2730
  class JobStatsView extends View {
2559
2731
  constructor(options = {}) {
@@ -5013,20 +5185,36 @@ class RuleSetView extends View {
5013
5185
  `;
5014
5186
  }
5015
5187
  async onInit() {
5188
+ const matchByValue = this.model.get("match_by");
5189
+ const matchByOption = MatchByOptions.find((opt) => opt.value === matchByValue);
5190
+ const matchByLabel = matchByOption ? matchByOption.label : String(matchByValue);
5191
+ const bundleByValue = this.model.get("bundle_by");
5192
+ const bundleByOption = BundleByOptions.find((opt) => opt.value === bundleByValue);
5193
+ const bundleByLabel = bundleByOption ? bundleByOption.label : String(bundleByValue);
5016
5194
  this.configView = new DataView({
5017
5195
  model: this.model,
5018
5196
  className: "p-3",
5019
5197
  columns: 2,
5020
5198
  fields: [
5021
- { name: "id", label: "RuleSet ID" },
5022
- { name: "name", label: "Name" },
5023
- { name: "category", label: "Category" },
5024
- { name: "priority", label: "Priority" },
5025
- { name: "match_by", label: "Match Logic", format: (v) => v === 0 ? "ALL" : "ANY" },
5026
- { name: "bundle_by", label: "Bundle By" },
5027
- { name: "bundle_minutes", label: "Bundle Minutes" },
5028
- { name: "handler", label: "Handler" },
5029
- { name: "is_active", label: "Status", format: "boolean" }
5199
+ { name: "id", label: "RuleSet ID", cols: 6 },
5200
+ { name: "priority", label: "Priority", cols: 6 },
5201
+ { name: "name", label: "Name", cols: 12 },
5202
+ { name: "category", label: "Category", formatter: "badge", cols: 6 },
5203
+ { name: "is_active", label: "Status", formatter: "boolean", cols: 6 },
5204
+ {
5205
+ name: "match_by",
5206
+ label: "Match Logic",
5207
+ template: matchByLabel,
5208
+ cols: 12
5209
+ },
5210
+ {
5211
+ name: "bundle_by",
5212
+ label: "Bundle By",
5213
+ template: bundleByLabel,
5214
+ cols: 12
5215
+ },
5216
+ { name: "bundle_minutes", label: "Bundle Minutes", cols: 6 },
5217
+ { name: "handler", label: "Handler", cols: 12 }
5030
5218
  ]
5031
5219
  });
5032
5220
  const rulesCollection = new RuleList({
@@ -5038,12 +5226,23 @@ class RuleSetView extends View {
5038
5226
  { key: "id", label: "ID", width: "70px" },
5039
5227
  { key: "name", label: "Name" },
5040
5228
  { key: "field_name", label: "Field" },
5041
- { key: "comparator", label: "Comparator" },
5229
+ { key: "comparator", label: "Comparator", width: "120px" },
5042
5230
  { key: "value", label: "Value" },
5043
- { key: "value_type", label: "Type" }
5231
+ { key: "value_type", label: "Type", width: "100px" }
5044
5232
  ],
5045
5233
  showAdd: true,
5046
- actions: ["edit", "delete"]
5234
+ clickAction: "edit",
5235
+ actions: ["edit", "delete"],
5236
+ contextMenu: [
5237
+ { label: "Edit Rule", action: "edit", icon: "bi-pencil" },
5238
+ { label: "Duplicate Rule", action: "duplicate", icon: "bi-files" },
5239
+ { divider: true },
5240
+ { label: "Delete Rule", action: "delete", icon: "bi-trash", danger: true }
5241
+ ],
5242
+ // Pass the parent ID so new rules get associated with this ruleset
5243
+ defaultData: {
5244
+ parent: this.model.get("id")
5245
+ }
5047
5246
  });
5048
5247
  this.tabView = new TabView({
5049
5248
  containerId: "ruleset-tabs",
@@ -5069,6 +5268,73 @@ class RuleSetView extends View {
5069
5268
  });
5070
5269
  this.addChild(contextMenu);
5071
5270
  }
5271
+ /**
5272
+ * Action handler: Edit RuleSet
5273
+ */
5274
+ async onActionEditRuleset() {
5275
+ const resp = await Dialog$1.showModelForm({
5276
+ title: `Edit RuleSet - ${this.model.get("name")}`,
5277
+ model: this.model,
5278
+ formConfig: RuleSet.EDIT_FORM
5279
+ });
5280
+ if (resp) {
5281
+ await this.render();
5282
+ }
5283
+ }
5284
+ /**
5285
+ * Action handler: Disable/Enable RuleSet
5286
+ */
5287
+ async onActionDisableRuleset() {
5288
+ const isActive = this.model.get("is_active");
5289
+ const newStatus = !isActive;
5290
+ try {
5291
+ this.model.set("is_active", newStatus);
5292
+ await this.model.save();
5293
+ await this.render();
5294
+ Dialog$1.showToast({
5295
+ message: `RuleSet ${newStatus ? "enabled" : "disabled"} successfully`,
5296
+ type: "success"
5297
+ });
5298
+ } catch (error) {
5299
+ Dialog$1.showToast({
5300
+ message: `Failed to update RuleSet: ${error.message}`,
5301
+ type: "error"
5302
+ });
5303
+ }
5304
+ }
5305
+ /**
5306
+ * Action handler: Delete RuleSet
5307
+ */
5308
+ async onActionDeleteRuleset() {
5309
+ const confirmed = await Dialog$1.confirm({
5310
+ title: "Delete RuleSet",
5311
+ message: `Are you sure you want to delete the ruleset "${this.model.get("name")}"? This action cannot be undone.`,
5312
+ confirmText: "Delete",
5313
+ confirmClass: "btn-danger"
5314
+ });
5315
+ if (confirmed) {
5316
+ try {
5317
+ await this.model.destroy();
5318
+ Dialog$1.showToast({
5319
+ message: "RuleSet deleted successfully",
5320
+ type: "success"
5321
+ });
5322
+ const dialog = this.element?.closest(".modal");
5323
+ if (dialog) {
5324
+ const bsModal = bootstrap.Modal.getInstance(dialog);
5325
+ if (bsModal) {
5326
+ bsModal.hide();
5327
+ }
5328
+ }
5329
+ this.emit("ruleset:deleted", { model: this.model });
5330
+ } catch (error) {
5331
+ Dialog$1.showToast({
5332
+ message: `Failed to delete RuleSet: ${error.message}`,
5333
+ type: "error"
5334
+ });
5335
+ }
5336
+ }
5337
+ }
5072
5338
  }
5073
5339
  RuleSetView.VIEW_CLASS = RuleSetView;
5074
5340
  class RuleSetTablePage extends TablePage {
@@ -5079,7 +5345,7 @@ class RuleSetTablePage extends TablePage {
5079
5345
  pageName: "Rule Engine",
5080
5346
  router: "admin/rulesets",
5081
5347
  Collection: RuleSetList,
5082
- itemViewClass: RuleSetView,
5348
+ itemView: RuleSetView,
5083
5349
  viewDialogOptions: {
5084
5350
  header: false,
5085
5351
  size: "xl"
@@ -5089,8 +5355,7 @@ class RuleSetTablePage extends TablePage {
5089
5355
  { key: "name", label: "Name", sortable: true },
5090
5356
  { key: "category", label: "Category", sortable: true, formatter: "badge" },
5091
5357
  { key: "priority", label: "Priority", sortable: true },
5092
- { key: "match_by", label: "Match Logic", formatter: (v) => v === 0 ? "ALL" : "ANY" },
5093
- { key: "is_active", label: "Status", formatter: "boolean|badge('Active:success,Inactive:secondary')" }
5358
+ { key: "match_by", label: "Match Logic", formatter: (v) => v === 0 ? "ALL" : "ANY" }
5094
5359
  ],
5095
5360
  selectable: true,
5096
5361
  searchable: true,