web-mojo 2.1.936 → 2.1.955

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 (102) 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 +122 -229
  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 +7 -7
  8. package/dist/auth.es.js.map +1 -1
  9. package/dist/charts.cjs.js +1 -1
  10. package/dist/charts.es.js +7 -7
  11. package/dist/chunks/ChatView-CTtQHvRP.js +2 -0
  12. package/dist/chunks/ChatView-CTtQHvRP.js.map +1 -0
  13. package/dist/chunks/{ChatView-DlSxjxah.js → ChatView-DLEStri1.js} +89 -574
  14. package/dist/chunks/ChatView-DLEStri1.js.map +1 -0
  15. package/dist/chunks/Collection-DD1_31eh.js +2 -0
  16. package/dist/chunks/Collection-DD1_31eh.js.map +1 -0
  17. package/dist/chunks/Collection-YRfGoT73.js +990 -0
  18. package/dist/chunks/Collection-YRfGoT73.js.map +1 -0
  19. package/dist/chunks/ContextMenu-By2g3KYY.js +1171 -0
  20. package/dist/chunks/ContextMenu-By2g3KYY.js.map +1 -0
  21. package/dist/chunks/ContextMenu-Cl0TRsIa.js +3 -0
  22. package/dist/chunks/ContextMenu-Cl0TRsIa.js.map +1 -0
  23. package/dist/chunks/DataView-CdDY9ijM.js +2 -0
  24. package/dist/chunks/{DataView-XJbTQ5q0.js.map → DataView-CdDY9ijM.js.map} +1 -1
  25. package/dist/chunks/{DataView-Vmjx4eCr.js → DataView-OUqaLmGB.js} +2 -2
  26. package/dist/chunks/{DataView-Vmjx4eCr.js.map → DataView-OUqaLmGB.js.map} +1 -1
  27. package/dist/chunks/{Dialog-D_rAf4gQ.js → Dialog-C2mRUxga.js} +8 -6
  28. package/dist/chunks/{Dialog-D_rAf4gQ.js.map → Dialog-C2mRUxga.js.map} +1 -1
  29. package/dist/chunks/Dialog-Cl6MN8if.js +2 -0
  30. package/dist/chunks/{Dialog-BinTQTfO.js.map → Dialog-Cl6MN8if.js.map} +1 -1
  31. package/dist/chunks/FormView-BSWaXDav.js +3 -0
  32. package/dist/chunks/{FormView-TPFsq8ZX.js.map → FormView-BSWaXDav.js.map} +1 -1
  33. package/dist/chunks/{FormView-CIriLDZY.js → FormView-HWvIdFkB.js} +10 -6
  34. package/dist/chunks/FormView-HWvIdFkB.js.map +1 -0
  35. package/dist/chunks/ListView-BMNhd5-B.js +492 -0
  36. package/dist/chunks/ListView-BMNhd5-B.js.map +1 -0
  37. package/dist/chunks/ListView-BRGiITfD.js +2 -0
  38. package/dist/chunks/ListView-BRGiITfD.js.map +1 -0
  39. package/dist/chunks/MetricsMiniChartWidget-BkTEO87S.js +2 -0
  40. package/dist/chunks/{MetricsMiniChartWidget-sONcM0pG.js.map → MetricsMiniChartWidget-BkTEO87S.js.map} +1 -1
  41. package/dist/chunks/{MetricsMiniChartWidget-BolRZ-Ja.js → MetricsMiniChartWidget-Y70IHFIe.js} +3 -3
  42. package/dist/chunks/{MetricsMiniChartWidget-BolRZ-Ja.js.map → MetricsMiniChartWidget-Y70IHFIe.js.map} +1 -1
  43. package/dist/chunks/PDFViewer-C0aMqGJL.js +2 -0
  44. package/dist/chunks/{PDFViewer-UBhinN8A.js.map → PDFViewer-C0aMqGJL.js.map} +1 -1
  45. package/dist/chunks/{PDFViewer-D6SKOl85.js → PDFViewer-DkbYnnoV.js} +3 -3
  46. package/dist/chunks/{PDFViewer-D6SKOl85.js.map → PDFViewer-DkbYnnoV.js.map} +1 -1
  47. package/dist/chunks/Page-CvbwEoLv.js +2 -0
  48. package/dist/chunks/{Page-CnvHhwLZ.js.map → Page-CvbwEoLv.js.map} +1 -1
  49. package/dist/chunks/{Page-B7L25Omb.js → Page-Deq4y2Kq.js} +2 -2
  50. package/dist/chunks/{Page-B7L25Omb.js.map → Page-Deq4y2Kq.js.map} +1 -1
  51. package/dist/chunks/Rest-BNYqGlnP.js +2 -0
  52. package/dist/chunks/Rest-BNYqGlnP.js.map +1 -0
  53. package/dist/chunks/{WebApp-El07OMHH.js → Rest-CS4jRCAs.js} +5 -1389
  54. package/dist/chunks/Rest-CS4jRCAs.js.map +1 -0
  55. package/dist/chunks/TopNav-A7NQ4viq.js +2 -0
  56. package/dist/chunks/{TopNav-Dcmcic-i.js.map → TopNav-A7NQ4viq.js.map} +1 -1
  57. package/dist/chunks/{TopNav-CPA884W7.js → TopNav-Dch6cZFa.js} +5 -5
  58. package/dist/chunks/{TopNav-CPA884W7.js.map → TopNav-Dch6cZFa.js.map} +1 -1
  59. package/dist/chunks/WebApp-CaOPY_k7.js +2 -0
  60. package/dist/chunks/WebApp-CaOPY_k7.js.map +1 -0
  61. package/dist/chunks/WebApp-RHtJ4hFZ.js +1388 -0
  62. package/dist/chunks/WebApp-RHtJ4hFZ.js.map +1 -0
  63. package/dist/css/web-mojo.css +2 -2
  64. package/dist/docit.cjs.js +1 -1
  65. package/dist/docit.cjs.js.map +1 -1
  66. package/dist/docit.es.js +12 -10
  67. package/dist/docit.es.js.map +1 -1
  68. package/dist/index.cjs.js +1 -1
  69. package/dist/index.cjs.js.map +1 -1
  70. package/dist/index.es.js +120 -116
  71. package/dist/index.es.js.map +1 -1
  72. package/dist/lightbox.cjs.js +1 -1
  73. package/dist/lightbox.cjs.js.map +1 -1
  74. package/dist/lightbox.es.js +121 -121
  75. package/dist/lightbox.es.js.map +1 -1
  76. package/dist/map.cjs.js +2 -0
  77. package/dist/map.cjs.js.map +1 -0
  78. package/dist/map.es.js +188 -0
  79. package/dist/map.es.js.map +1 -0
  80. package/dist/timeline.cjs.js +2 -0
  81. package/dist/timeline.cjs.js.map +1 -0
  82. package/dist/timeline.es.js +225 -0
  83. package/dist/timeline.es.js.map +1 -0
  84. package/package.json +9 -1
  85. package/dist/chunks/ChatView-DlSxjxah.js.map +0 -1
  86. package/dist/chunks/ChatView-DnqrGXMC.js +0 -2
  87. package/dist/chunks/ChatView-DnqrGXMC.js.map +0 -1
  88. package/dist/chunks/ContextMenu-CE77rUmn.js +0 -2155
  89. package/dist/chunks/ContextMenu-CE77rUmn.js.map +0 -1
  90. package/dist/chunks/ContextMenu-KVxd0Kgd.js +0 -3
  91. package/dist/chunks/ContextMenu-KVxd0Kgd.js.map +0 -1
  92. package/dist/chunks/DataView-XJbTQ5q0.js +0 -2
  93. package/dist/chunks/Dialog-BinTQTfO.js +0 -2
  94. package/dist/chunks/FormView-CIriLDZY.js.map +0 -1
  95. package/dist/chunks/FormView-TPFsq8ZX.js +0 -3
  96. package/dist/chunks/MetricsMiniChartWidget-sONcM0pG.js +0 -2
  97. package/dist/chunks/PDFViewer-UBhinN8A.js +0 -2
  98. package/dist/chunks/Page-CnvHhwLZ.js +0 -2
  99. package/dist/chunks/TopNav-Dcmcic-i.js +0 -2
  100. package/dist/chunks/WebApp-El07OMHH.js.map +0 -1
  101. package/dist/chunks/WebApp-b9DQWz1d.js +0 -2
  102. package/dist/chunks/WebApp-b9DQWz1d.js.map +0 -1
@@ -0,0 +1,1171 @@
1
+ import { C as Collection, M as Model } from "./Collection-YRfGoT73.js";
2
+ import { V as View } from "./Rest-CS4jRCAs.js";
3
+ class ToastService {
4
+ constructor(options = {}) {
5
+ this.options = {
6
+ containerId: "toast-container",
7
+ position: "top-end",
8
+ // top-start, top-center, top-end, middle-start, etc.
9
+ autohide: true,
10
+ defaultDelay: 5e3,
11
+ // 5 seconds
12
+ maxToasts: 5,
13
+ // Maximum number of toasts to show at once
14
+ ...options
15
+ };
16
+ this.toasts = /* @__PURE__ */ new Map();
17
+ this.toastCounter = 0;
18
+ this.init();
19
+ }
20
+ /**
21
+ * Initialize the toast service
22
+ */
23
+ init() {
24
+ this.createContainer();
25
+ }
26
+ /**
27
+ * Create the toast container if it doesn't exist
28
+ */
29
+ createContainer() {
30
+ let container = document.getElementById(this.options.containerId);
31
+ if (!container) {
32
+ container = document.createElement("div");
33
+ container.id = this.options.containerId;
34
+ container.className = `toast-container position-fixed ${this.getPositionClasses()}`;
35
+ container.style.zIndex = "1070";
36
+ container.setAttribute("aria-live", "polite");
37
+ container.setAttribute("aria-atomic", "true");
38
+ document.body.appendChild(container);
39
+ }
40
+ this.container = container;
41
+ }
42
+ /**
43
+ * Get CSS classes for toast positioning
44
+ */
45
+ getPositionClasses() {
46
+ const positionMap = {
47
+ "top-start": "top-0 start-0 p-3",
48
+ "top-center": "top-0 start-50 translate-middle-x p-3",
49
+ "top-end": "top-0 end-0 p-3",
50
+ "middle-start": "top-50 start-0 translate-middle-y p-3",
51
+ "middle-center": "top-50 start-50 translate-middle p-3",
52
+ "middle-end": "top-50 end-0 translate-middle-y p-3",
53
+ "bottom-start": "bottom-0 start-0 p-3",
54
+ "bottom-center": "bottom-0 start-50 translate-middle-x p-3",
55
+ "bottom-end": "bottom-0 end-0 p-3"
56
+ };
57
+ return positionMap[this.options.position] || positionMap["top-end"];
58
+ }
59
+ /**
60
+ * Show a success toast
61
+ * @param {string} message - The message to display
62
+ * @param {object} options - Additional options
63
+ */
64
+ success(message, options = {}) {
65
+ return this.show(message, "success", {
66
+ icon: "bi-check-circle-fill",
67
+ ...options
68
+ });
69
+ }
70
+ /**
71
+ * Show an error toast
72
+ * @param {string} message - The message to display
73
+ * @param {object} options - Additional options
74
+ */
75
+ error(message, options = {}) {
76
+ return this.show(message, "error", {
77
+ icon: "bi-exclamation-triangle-fill",
78
+ autohide: false,
79
+ // Keep error toasts visible until manually dismissed
80
+ ...options
81
+ });
82
+ }
83
+ /**
84
+ * Show an info toast
85
+ * @param {string} message - The message to display
86
+ * @param {object} options - Additional options
87
+ */
88
+ info(message, options = {}) {
89
+ return this.show(message, "info", {
90
+ icon: "bi-info-circle-fill",
91
+ ...options
92
+ });
93
+ }
94
+ /**
95
+ * Show a warning toast
96
+ * @param {string} message - The message to display
97
+ * @param {object} options - Additional options
98
+ */
99
+ warning(message, options = {}) {
100
+ return this.show(message, "warning", {
101
+ icon: "bi-exclamation-triangle-fill",
102
+ ...options
103
+ });
104
+ }
105
+ /**
106
+ * Show a plain toast without specific styling
107
+ * @param {string} message - The message to display
108
+ * @param {object} options - Additional options
109
+ */
110
+ plain(message, options = {}) {
111
+ return this.show(message, "plain", {
112
+ ...options
113
+ });
114
+ }
115
+ /**
116
+ * Show a toast with specified type and options
117
+ * @param {string} message - The message to display
118
+ * @param {string} type - Toast type (success, error, info, warning)
119
+ * @param {object} options - Additional options
120
+ */
121
+ show(message, type = "info", options = {}) {
122
+ this.enforceMaxToasts();
123
+ const toastId = `toast-${++this.toastCounter}`;
124
+ const config = {
125
+ title: this.getDefaultTitle(type),
126
+ icon: this.getDefaultIcon(type),
127
+ autohide: this.options.autohide,
128
+ delay: this.options.defaultDelay,
129
+ dismissible: true,
130
+ ...options
131
+ };
132
+ const toastElement = this.createToastElement(toastId, message, type, config);
133
+ this.container.appendChild(toastElement);
134
+ if (typeof bootstrap === "undefined") {
135
+ throw new Error("Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.");
136
+ }
137
+ const bsToast = new bootstrap.Toast(toastElement, {
138
+ autohide: config.autohide,
139
+ delay: config.delay
140
+ });
141
+ this.toasts.set(toastId, {
142
+ element: toastElement,
143
+ bootstrap: bsToast,
144
+ type,
145
+ message
146
+ });
147
+ toastElement.addEventListener("hidden.bs.toast", () => {
148
+ this.cleanup(toastId);
149
+ });
150
+ bsToast.show();
151
+ return {
152
+ id: toastId,
153
+ hide: () => {
154
+ try {
155
+ bsToast.hide();
156
+ } catch (error) {
157
+ console.warn("Error hiding toast:", error);
158
+ }
159
+ },
160
+ dispose: () => this.cleanup(toastId),
161
+ updateProgress: options.updateProgress || null
162
+ };
163
+ }
164
+ /**
165
+ * Show a toast with a View component in the body
166
+ * @param {View} view - The View component to display
167
+ * @param {string} type - Toast type (success, error, info, warning, plain)
168
+ * @param {object} options - Additional options
169
+ */
170
+ showView(view, type = "info", options = {}) {
171
+ this.enforceMaxToasts();
172
+ const toastId = `toast-${++this.toastCounter}`;
173
+ const config = {
174
+ title: options.title || this.getDefaultTitle(type),
175
+ icon: options.icon || this.getDefaultIcon(type),
176
+ autohide: this.options.autohide,
177
+ delay: this.options.defaultDelay,
178
+ dismissible: true,
179
+ ...options
180
+ };
181
+ const toastElement = this.createViewToastElement(toastId, view, type, config);
182
+ this.container.appendChild(toastElement);
183
+ if (typeof bootstrap === "undefined") {
184
+ throw new Error("Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.");
185
+ }
186
+ const bsToast = new bootstrap.Toast(toastElement, {
187
+ autohide: config.autohide,
188
+ delay: config.delay
189
+ });
190
+ this.toasts.set(toastId, {
191
+ element: toastElement,
192
+ bootstrap: bsToast,
193
+ type,
194
+ view,
195
+ message: "View toast"
196
+ });
197
+ toastElement.addEventListener("hidden.bs.toast", () => {
198
+ this.cleanupView(toastId);
199
+ });
200
+ const bodyContainer = toastElement.querySelector(".toast-view-body");
201
+ if (bodyContainer && view) {
202
+ view.render(true, bodyContainer);
203
+ }
204
+ bsToast.show();
205
+ return {
206
+ id: toastId,
207
+ view,
208
+ hide: () => {
209
+ try {
210
+ bsToast.hide();
211
+ } catch (error) {
212
+ console.warn("Error hiding view toast:", error);
213
+ }
214
+ },
215
+ dispose: () => this.cleanupView(toastId),
216
+ updateProgress: (progressInfo) => {
217
+ if (view && typeof view.updateProgress === "function") {
218
+ view.updateProgress(progressInfo);
219
+ }
220
+ }
221
+ };
222
+ }
223
+ /**
224
+ * Create toast DOM element
225
+ */
226
+ createToastElement(id, message, type, config) {
227
+ const toast = document.createElement("div");
228
+ toast.id = id;
229
+ toast.className = `toast toast-service-${type}`;
230
+ toast.setAttribute("role", "alert");
231
+ toast.setAttribute("aria-live", "assertive");
232
+ toast.setAttribute("aria-atomic", "true");
233
+ const header = config.title || config.icon ? this.createToastHeader(config, type) : "";
234
+ const body = this.createToastBody(message, config.icon && !config.title);
235
+ toast.innerHTML = `
236
+ ${header}
237
+ ${body}
238
+ `;
239
+ return toast;
240
+ }
241
+ /**
242
+ * Create toast DOM element for View component
243
+ */
244
+ createViewToastElement(id, view, type, config) {
245
+ const toast = document.createElement("div");
246
+ toast.id = id;
247
+ toast.className = `toast toast-service-${type}`;
248
+ toast.setAttribute("role", "alert");
249
+ toast.setAttribute("aria-live", "assertive");
250
+ toast.setAttribute("aria-atomic", "true");
251
+ const header = config.title || config.icon ? this.createToastHeader(config, type) : "";
252
+ const body = this.createViewToastBody();
253
+ toast.innerHTML = `
254
+ ${header}
255
+ ${body}
256
+ `;
257
+ return toast;
258
+ }
259
+ /**
260
+ * Create toast body for View component
261
+ */
262
+ createViewToastBody() {
263
+ return `
264
+ <div class="toast-body p-0">
265
+ <div class="toast-view-body p-3"></div>
266
+ </div>
267
+ `;
268
+ }
269
+ /**
270
+ * Create toast header with title and icon
271
+ */
272
+ createToastHeader(config, _type) {
273
+ const iconHtml = config.icon ? `<i class="${config.icon} toast-service-icon me-2"></i>` : "";
274
+ const titleHtml = config.title ? `<strong class="me-auto">${iconHtml}${this.escapeHtml(config.title)}</strong>` : "";
275
+ const timeHtml = config.showTime ? `<small class="text-muted">${this.getTimeString()}</small>` : "";
276
+ const closeButton = config.dismissible ? `<button type="button" class="btn-close toast-service-close" data-bs-dismiss="toast" aria-label="Close"></button>` : "";
277
+ if (!titleHtml && !timeHtml && !closeButton) {
278
+ return "";
279
+ }
280
+ return `
281
+ <div class="toast-header">
282
+ ${titleHtml}
283
+ ${timeHtml}
284
+ ${closeButton}
285
+ </div>
286
+ `;
287
+ }
288
+ /**
289
+ * Create toast body with message
290
+ */
291
+ createToastBody(message, showIcon = false) {
292
+ const iconHtml = showIcon ? `<i class="${this.getDefaultIcon("info")} toast-service-icon me-2"></i>` : "";
293
+ return `
294
+ <div class="toast-body d-flex align-items-center">
295
+ ${iconHtml}
296
+ <span>${this.escapeHtml(message)}</span>
297
+ </div>
298
+ `;
299
+ }
300
+ /**
301
+ * Get default title for toast type
302
+ */
303
+ getDefaultTitle(type) {
304
+ const titles = {
305
+ success: "Success",
306
+ error: "Error",
307
+ warning: "Warning",
308
+ info: "Information",
309
+ plain: ""
310
+ };
311
+ return titles[type] || "Notification";
312
+ }
313
+ /**
314
+ * Get default icon for toast type
315
+ */
316
+ getDefaultIcon(type) {
317
+ const icons = {
318
+ success: "bi-check-circle-fill",
319
+ error: "bi-exclamation-triangle-fill",
320
+ warning: "bi-exclamation-triangle-fill",
321
+ info: "bi-info-circle-fill",
322
+ plain: ""
323
+ };
324
+ return icons[type] || "bi-info-circle-fill";
325
+ }
326
+ /**
327
+ * Enforce maximum number of toasts
328
+ */
329
+ enforceMaxToasts() {
330
+ const activeToasts = Array.from(this.toasts.values());
331
+ if (activeToasts.length >= this.options.maxToasts) {
332
+ const oldestId = this.toasts.keys().next().value;
333
+ const oldest = this.toasts.get(oldestId);
334
+ if (oldest) {
335
+ oldest.bootstrap.hide();
336
+ }
337
+ }
338
+ }
339
+ /**
340
+ * Clean up toast resources
341
+ */
342
+ cleanup(toastId) {
343
+ const toast = this.toasts.get(toastId);
344
+ if (toast) {
345
+ try {
346
+ toast.bootstrap.dispose();
347
+ } catch (e) {
348
+ console.warn("Error disposing toast:", e);
349
+ }
350
+ if (toast.element && toast.element.parentNode) {
351
+ toast.element.parentNode.removeChild(toast.element);
352
+ }
353
+ this.toasts.delete(toastId);
354
+ }
355
+ }
356
+ /**
357
+ * Clean up view toast resources with proper view disposal
358
+ */
359
+ cleanupView(toastId) {
360
+ const toast = this.toasts.get(toastId);
361
+ if (toast) {
362
+ if (toast.view && typeof toast.view.dispose === "function") {
363
+ try {
364
+ toast.view.dispose();
365
+ } catch (e) {
366
+ console.warn("Error disposing view in toast:", e);
367
+ }
368
+ }
369
+ try {
370
+ toast.bootstrap.dispose();
371
+ } catch (e) {
372
+ console.warn("Error disposing toast:", e);
373
+ }
374
+ if (toast.element && toast.element.parentNode) {
375
+ toast.element.parentNode.removeChild(toast.element);
376
+ }
377
+ this.toasts.delete(toastId);
378
+ }
379
+ }
380
+ /**
381
+ * Hide all active toasts
382
+ */
383
+ hideAll() {
384
+ this.toasts.forEach((toast, _id) => {
385
+ toast.bootstrap.hide();
386
+ });
387
+ }
388
+ /**
389
+ * Clear all toasts immediately
390
+ */
391
+ clearAll() {
392
+ this.toasts.forEach((toast, id) => {
393
+ this.cleanup(id);
394
+ });
395
+ }
396
+ /**
397
+ * Get current time string
398
+ */
399
+ getTimeString() {
400
+ return (/* @__PURE__ */ new Date()).toLocaleTimeString([], {
401
+ hour: "2-digit",
402
+ minute: "2-digit"
403
+ });
404
+ }
405
+ /**
406
+ * Escape HTML to prevent XSS
407
+ */
408
+ escapeHtml(str) {
409
+ const div = document.createElement("div");
410
+ div.textContent = str;
411
+ return div.innerHTML;
412
+ }
413
+ /**
414
+ * Dispose of the entire toast service
415
+ */
416
+ dispose() {
417
+ this.clearAll();
418
+ if (this.container && this.container.parentNode) {
419
+ this.container.parentNode.removeChild(this.container);
420
+ }
421
+ }
422
+ /**
423
+ * Get statistics about active toasts
424
+ */
425
+ getStats() {
426
+ const stats = {
427
+ total: this.toasts.size,
428
+ byType: {}
429
+ };
430
+ this.toasts.forEach((toast) => {
431
+ stats.byType[toast.type] = (stats.byType[toast.type] || 0) + 1;
432
+ });
433
+ return stats;
434
+ }
435
+ /**
436
+ * Set global options
437
+ */
438
+ setOptions(newOptions) {
439
+ this.options = { ...this.options, ...newOptions };
440
+ if (newOptions.position) {
441
+ if (this.container) {
442
+ this.container.className = `toast-container position-fixed ${this.getPositionClasses()}`;
443
+ }
444
+ }
445
+ }
446
+ }
447
+ class Group extends Model {
448
+ constructor(data = {}) {
449
+ super(data, {
450
+ endpoint: "/api/group"
451
+ });
452
+ }
453
+ }
454
+ class GroupList extends Collection {
455
+ constructor(options = {}) {
456
+ super({
457
+ ModelClass: Group,
458
+ endpoint: "/api/group",
459
+ size: 10,
460
+ ...options
461
+ });
462
+ }
463
+ }
464
+ const GroupKinds = {
465
+ "org": "Organization",
466
+ "division": "Division",
467
+ "department": "Department",
468
+ "team": "Team",
469
+ "merchant": "Merchant",
470
+ "partner": "Partner",
471
+ "client": "Client",
472
+ "iso": "ISO",
473
+ "sales": "Sales",
474
+ "reseller": "Reseller",
475
+ "location": "Location",
476
+ "region": "Region",
477
+ "route": "Route",
478
+ "project": "Project",
479
+ "inventory": "Inventory",
480
+ "test": "Testing",
481
+ "misc": "Miscellaneous",
482
+ "qa": "Quality Assurance"
483
+ };
484
+ const GroupKindOptions = Object.entries(GroupKinds).map(([key, label]) => ({
485
+ value: key,
486
+ label
487
+ }));
488
+ const GroupForms = {
489
+ create: {
490
+ title: "Create Group",
491
+ fields: [
492
+ {
493
+ name: "name",
494
+ type: "text",
495
+ label: "Group Name",
496
+ required: true,
497
+ placeholder: "Enter group name"
498
+ },
499
+ {
500
+ name: "kind",
501
+ type: "select",
502
+ label: "Group Kind",
503
+ required: true,
504
+ options: GroupKindOptions
505
+ },
506
+ {
507
+ type: "collection",
508
+ name: "parent",
509
+ label: "Parent Group",
510
+ Collection: GroupList,
511
+ // Collection class
512
+ labelField: "name",
513
+ // Field to display in dropdown
514
+ valueField: "id",
515
+ // Field to use as value
516
+ maxItems: 10,
517
+ // Max items to show in dropdown
518
+ placeholder: "Search groups...",
519
+ emptyFetch: false,
520
+ debounceMs: 300
521
+ // Search debounce delay
522
+ }
523
+ ]
524
+ },
525
+ edit: {
526
+ title: "Edit Group",
527
+ fields: [
528
+ {
529
+ name: "name",
530
+ type: "text",
531
+ label: "Group Name",
532
+ required: true,
533
+ placeholder: "Enter group name"
534
+ },
535
+ {
536
+ name: "kind",
537
+ type: "select",
538
+ label: "Group Kind",
539
+ required: true,
540
+ options: GroupKindOptions
541
+ },
542
+ {
543
+ type: "collection",
544
+ name: "parent",
545
+ label: "Parent Group",
546
+ Collection: GroupList,
547
+ // Collection class
548
+ labelField: "name",
549
+ // Field to display in dropdown
550
+ valueField: "id",
551
+ // Field to use as value
552
+ maxItems: 10,
553
+ // Max items to show in dropdown
554
+ placeholder: "Search groups...",
555
+ emptyFetch: false,
556
+ debounceMs: 300
557
+ // Search debounce delay
558
+ },
559
+ {
560
+ name: "metadata.domain",
561
+ type: "text",
562
+ label: "Default Domain",
563
+ placeholder: "Enter Domain"
564
+ },
565
+ {
566
+ name: "metadata.portal",
567
+ type: "text",
568
+ label: "Default Portal",
569
+ placeholder: "Enter Portal URL"
570
+ },
571
+ {
572
+ name: "is_active",
573
+ type: "switch",
574
+ label: "Is Active",
575
+ cols: 4
576
+ }
577
+ ]
578
+ },
579
+ detailed: {
580
+ title: "Group Details",
581
+ fields: [
582
+ // Profile Header
583
+ {
584
+ type: "header",
585
+ text: "Profile Information",
586
+ level: 4,
587
+ class: "text-primary mb-3"
588
+ },
589
+ // Avatar and Basic Info
590
+ {
591
+ type: "group",
592
+ columns: { xs: 12, md: 4 },
593
+ fields: [
594
+ {
595
+ type: "image",
596
+ name: "avatar",
597
+ size: "lg",
598
+ imageSize: { width: 200, height: 200 },
599
+ placeholder: "Upload your avatar",
600
+ help: "Square images work best",
601
+ columns: 12
602
+ },
603
+ {
604
+ name: "is_active",
605
+ type: "switch",
606
+ label: "Is Active",
607
+ columns: 12
608
+ }
609
+ ]
610
+ },
611
+ // Profile Details
612
+ {
613
+ type: "group",
614
+ columns: { xs: 12, md: 8 },
615
+ title: "Details",
616
+ fields: [
617
+ {
618
+ name: "name",
619
+ type: "text",
620
+ label: "Group Name",
621
+ required: true,
622
+ placeholder: "Enter group name",
623
+ columns: 12
624
+ },
625
+ {
626
+ name: "kind",
627
+ type: "select",
628
+ label: "Group Kind",
629
+ required: true,
630
+ columns: 12,
631
+ options: [
632
+ { value: "org", label: "Organization" },
633
+ { value: "team", label: "Team" },
634
+ { value: "department", label: "Department" },
635
+ { value: "merchant", label: "Merchant" },
636
+ { value: "iso", label: "ISO" },
637
+ { value: "group", label: "Group" }
638
+ ]
639
+ },
640
+ {
641
+ type: "collection",
642
+ name: "parent",
643
+ label: "Parent Group",
644
+ Collection: GroupList,
645
+ // Collection class
646
+ labelField: "name",
647
+ // Field to display in dropdown
648
+ valueField: "id",
649
+ // Field to use as value
650
+ maxItems: 10,
651
+ // Max items to show in dropdown
652
+ placeholder: "Search groups...",
653
+ emptyFetch: false,
654
+ debounceMs: 300,
655
+ // Search debounce delay
656
+ columns: 12
657
+ }
658
+ ]
659
+ },
660
+ // Account Settings
661
+ {
662
+ type: "group",
663
+ columns: 12,
664
+ title: "Account Settings",
665
+ class: "pt-3",
666
+ fields: [
667
+ {
668
+ type: "select",
669
+ name: "metadata.timezone",
670
+ label: "Timezone",
671
+ columns: 6,
672
+ options: [
673
+ { value: "America/New_York", text: "Eastern Time" },
674
+ { value: "America/Chicago", text: "Central Time" },
675
+ { value: "America/Denver", text: "Mountain Time" },
676
+ { value: "America/Los_Angeles", text: "Pacific Time" },
677
+ { value: "UTC", text: "UTC" }
678
+ ]
679
+ },
680
+ {
681
+ type: "select",
682
+ name: "metadata.language",
683
+ label: "Language",
684
+ columns: 6,
685
+ options: [
686
+ { value: "en", text: "English" },
687
+ { value: "es", text: "Spanish" },
688
+ { value: "fr", text: "French" },
689
+ { value: "de", text: "German" }
690
+ ]
691
+ },
692
+ {
693
+ type: "switch",
694
+ name: "metadata.notify.email",
695
+ label: "Email Notifications",
696
+ columns: 4
697
+ },
698
+ {
699
+ type: "switch",
700
+ name: "metadata.profile_public",
701
+ label: "Public Profile",
702
+ columns: 4
703
+ }
704
+ ]
705
+ }
706
+ ]
707
+ }
708
+ };
709
+ Group.EDIT_FORM = GroupForms.edit;
710
+ Group.ADD_FORM = GroupForms.create;
711
+ Group.CREATE_FORM = GroupForms.create;
712
+ Group.GroupKindOptions = GroupKindOptions;
713
+ Group.GroupKinds = GroupKinds;
714
+ const Group$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
715
+ __proto__: null,
716
+ Group,
717
+ GroupForms,
718
+ GroupList
719
+ }, Symbol.toStringTag, { value: "Module" }));
720
+ class User extends Model {
721
+ constructor(data = {}) {
722
+ super(data, {
723
+ endpoint: "/api/user"
724
+ });
725
+ }
726
+ hasPermission(permission) {
727
+ if (Array.isArray(permission)) {
728
+ return permission.some((p) => this.hasPermission(p));
729
+ }
730
+ const isSysPermission = permission.startsWith("sys.");
731
+ const permissionToCheck = isSysPermission ? permission.substring(4) : permission;
732
+ if (this._hasPermission(permissionToCheck)) {
733
+ return true;
734
+ }
735
+ if (!isSysPermission && this.member && this.member.hasPermission(permission)) {
736
+ return true;
737
+ }
738
+ return false;
739
+ }
740
+ _hasPermission(permission) {
741
+ const permissions = this.get("permissions");
742
+ if (!permissions) {
743
+ return false;
744
+ }
745
+ return permissions[permission] == true;
746
+ }
747
+ hasPerm(p) {
748
+ return this.hasPermission(p);
749
+ }
750
+ }
751
+ class UserList extends Collection {
752
+ constructor(options = {}) {
753
+ super({
754
+ ModelClass: User,
755
+ endpoint: "/api/user",
756
+ ...options
757
+ });
758
+ }
759
+ }
760
+ User.PERMISSIONS = [
761
+ { name: "manage_users", label: "Manage Users" },
762
+ { name: "view_groups", label: "View Groups" },
763
+ { name: "manage_groups", label: "Manage Groups" },
764
+ { name: "view_metrics", label: "View System Metrics" },
765
+ { name: "manage_metrics", label: "Manage System Metrics" },
766
+ { name: "view_logs", label: "View Logs" },
767
+ { name: "view_incidents", label: "View Incidents" },
768
+ { name: "manage_incidents", label: "Manage Incidents" },
769
+ { name: "view_tickets", label: "View Tickets" },
770
+ { name: "manage_tickets", label: "Manage Tickets" },
771
+ { name: "view_admin", label: "View Admin" },
772
+ { name: "view_jobs", label: "View Jobs" },
773
+ { name: "manage_jobs", label: "Manage Jobs" },
774
+ { name: "view_global", label: "View Global" },
775
+ { name: "manage_notifications", label: "Manage Notifications" },
776
+ { name: "manage_files", label: "Manage Files" },
777
+ { name: "force_single_session", label: "Force Single Session" },
778
+ { name: "file_vault", label: "Access File Vault" },
779
+ { name: "manage_aws", label: "Manage AWS" },
780
+ { name: "manage_docit", label: "Manage DocIt" }
781
+ ];
782
+ User.PERMISSION_FIELDS = [
783
+ ...User.PERMISSIONS.map((permission) => ({
784
+ name: `permissions.${permission.name}`,
785
+ type: "switch",
786
+ label: permission.label,
787
+ columns: 4
788
+ }))
789
+ ];
790
+ const UserForms = {
791
+ create: {
792
+ title: "Create User",
793
+ fields: [
794
+ { name: "email", type: "text", label: "Email", required: true },
795
+ { name: "phone_number", type: "text", label: "Phone number", columns: 12 },
796
+ { name: "display_name", type: "text", label: "Display Name" }
797
+ ]
798
+ },
799
+ edit: {
800
+ title: "Edit User",
801
+ fields: [
802
+ { name: "email", type: "email", label: "Email", columns: 12 },
803
+ { name: "display_name", type: "text", label: "Display Name", columns: 12 },
804
+ { name: "phone_number", type: "text", label: "Phone number", columns: 12 },
805
+ { type: "collection", name: "org", label: "Organization", Collection: GroupList, labelField: "name", valueField: "id", columns: 12 }
806
+ ]
807
+ },
808
+ permissions: {
809
+ title: "Edit Permissions",
810
+ fields: User.PERMISSION_FIELDS
811
+ }
812
+ };
813
+ const UserDataView = {
814
+ // Basic user profile view
815
+ profile: {
816
+ title: "User Profile",
817
+ columns: 2,
818
+ fields: [
819
+ {
820
+ name: "id",
821
+ label: "User ID",
822
+ type: "number",
823
+ columns: 4
824
+ },
825
+ {
826
+ name: "last_login",
827
+ label: "Last Login",
828
+ type: "datetime",
829
+ format: "relative",
830
+ columns: 4
831
+ },
832
+ {
833
+ name: "last_activity",
834
+ label: "Last Activity",
835
+ type: "datetime",
836
+ format: "relative",
837
+ columns: 4
838
+ },
839
+ {
840
+ name: "username",
841
+ label: "Username",
842
+ type: "text",
843
+ format: "lowercase",
844
+ columns: 4
845
+ },
846
+ {
847
+ name: "display_name",
848
+ label: "Display Name",
849
+ type: "text",
850
+ columns: 4
851
+ },
852
+ {
853
+ name: "email",
854
+ label: "Email",
855
+ type: "email",
856
+ columns: 12
857
+ },
858
+ {
859
+ name: "org.name",
860
+ label: "Organization",
861
+ type: "text",
862
+ columns: 6
863
+ },
864
+ {
865
+ name: "phone_number",
866
+ label: "Phone Number",
867
+ type: "text",
868
+ columns: 6
869
+ }
870
+ ]
871
+ },
872
+ // Activity tracking view
873
+ activity: {
874
+ title: "User Activity",
875
+ columns: 2,
876
+ fields: [
877
+ {
878
+ name: "last_login",
879
+ label: "Last Login",
880
+ type: "datetime",
881
+ format: "relative",
882
+ colSize: 6
883
+ },
884
+ {
885
+ name: "last_activity",
886
+ label: "Last Activity",
887
+ type: "datetime",
888
+ format: "relative",
889
+ colSize: 6
890
+ }
891
+ ]
892
+ },
893
+ // Comprehensive view with all data
894
+ detailed: {
895
+ title: "Detailed User Information",
896
+ columns: 2,
897
+ showEmptyValues: true,
898
+ emptyValueText: "Not set",
899
+ fields: [
900
+ // Basic Info Section
901
+ {
902
+ name: "id",
903
+ label: "User ID",
904
+ type: "number",
905
+ colSize: 3
906
+ },
907
+ {
908
+ name: "display_name",
909
+ label: "Display Name",
910
+ type: "text",
911
+ format: 'capitalize|default("Unnamed User")',
912
+ colSize: 9
913
+ },
914
+ {
915
+ name: "username",
916
+ label: "Username",
917
+ type: "text",
918
+ format: "lowercase",
919
+ colSize: 6
920
+ },
921
+ {
922
+ name: "email",
923
+ label: "Email Address",
924
+ type: "email",
925
+ colSize: 6
926
+ },
927
+ {
928
+ name: "phone_number",
929
+ label: "Phone Number",
930
+ type: "phone",
931
+ format: 'phone|default("Not provided")',
932
+ colSize: 6
933
+ },
934
+ {
935
+ name: "is_active",
936
+ label: "Account Status",
937
+ type: "boolean",
938
+ colSize: 6
939
+ },
940
+ // Activity Info
941
+ {
942
+ name: "last_login",
943
+ label: "Last Login",
944
+ type: "datetime",
945
+ format: "relative",
946
+ colSize: 6
947
+ },
948
+ {
949
+ name: "last_activity",
950
+ label: "Last Activity",
951
+ type: "datetime",
952
+ format: "relative",
953
+ colSize: 6
954
+ },
955
+ // Avatar Info
956
+ {
957
+ name: "avatar.url",
958
+ label: "Avatar",
959
+ type: "url",
960
+ colSize: 12
961
+ },
962
+ // Complex Data (will use full width automatically)
963
+ {
964
+ name: "permissions",
965
+ label: "User Permissions",
966
+ type: "dataview",
967
+ dataViewColumns: 2,
968
+ showEmptyValues: false
969
+ },
970
+ {
971
+ name: "metadata",
972
+ label: "User Metadata",
973
+ type: "dataview",
974
+ dataViewColumns: 1
975
+ },
976
+ {
977
+ name: "avatar",
978
+ label: "Avatar Details",
979
+ type: "dataview",
980
+ dataViewColumns: 1
981
+ }
982
+ ]
983
+ },
984
+ // Permissions-focused view
985
+ permissions: {
986
+ title: "User Permissions",
987
+ columns: 1,
988
+ fields: [
989
+ {
990
+ name: "display_name",
991
+ label: "User",
992
+ type: "text",
993
+ format: "capitalize",
994
+ columns: 12
995
+ },
996
+ {
997
+ name: "permissions",
998
+ label: "Assigned Permissions",
999
+ type: "dataview",
1000
+ dataViewColumns: 3,
1001
+ showEmptyValues: false,
1002
+ colSize: 12
1003
+ }
1004
+ ]
1005
+ },
1006
+ // Compact summary view
1007
+ summary: {
1008
+ title: "User Summary",
1009
+ columns: 3,
1010
+ fields: [
1011
+ {
1012
+ name: "display_name",
1013
+ label: "Name",
1014
+ type: "text",
1015
+ format: "capitalize|truncate(30)"
1016
+ },
1017
+ {
1018
+ name: "email",
1019
+ label: "Email",
1020
+ type: "email"
1021
+ },
1022
+ {
1023
+ name: "is_active",
1024
+ label: "Status",
1025
+ type: "boolean"
1026
+ },
1027
+ {
1028
+ name: "last_activity",
1029
+ label: "Last Seen",
1030
+ type: "datetime",
1031
+ format: "relative",
1032
+ colSize: 12
1033
+ }
1034
+ ]
1035
+ }
1036
+ };
1037
+ User.DATA_VIEW = UserDataView.detailed;
1038
+ User.EDIT_FORM = UserForms.edit;
1039
+ User.ADD_FORM = UserForms.create;
1040
+ class UserDevice extends Model {
1041
+ constructor(data = {}) {
1042
+ super(data, {
1043
+ endpoint: "/api/user/device"
1044
+ });
1045
+ }
1046
+ static async getByDuid(duid) {
1047
+ const model = new UserDevice();
1048
+ const resp = await model.rest.GET("/api/user/device/lookup", { duid });
1049
+ if (resp.success && resp.data && resp.data.data) {
1050
+ return new UserDevice(resp.data.data);
1051
+ }
1052
+ return null;
1053
+ }
1054
+ }
1055
+ class UserDeviceList extends Collection {
1056
+ constructor(options = {}) {
1057
+ super({
1058
+ ModelClass: UserDevice,
1059
+ endpoint: "/api/user/device",
1060
+ ...options
1061
+ });
1062
+ }
1063
+ }
1064
+ class UserDeviceLocation extends Model {
1065
+ constructor(data = {}) {
1066
+ super(data, {
1067
+ endpoint: "/api/user/device/location"
1068
+ });
1069
+ }
1070
+ }
1071
+ class UserDeviceLocationList extends Collection {
1072
+ constructor(options = {}) {
1073
+ super({
1074
+ ModelClass: UserDeviceLocation,
1075
+ endpoint: "/api/user/device/location",
1076
+ ...options
1077
+ });
1078
+ }
1079
+ }
1080
+ class ContextMenu extends View {
1081
+ constructor(options = {}) {
1082
+ super({
1083
+ tagName: "div",
1084
+ className: "context-menu-view dropdown",
1085
+ ...options
1086
+ });
1087
+ this.config = options.contextMenu || options.config || {};
1088
+ this.context = options.context || {};
1089
+ }
1090
+ /**
1091
+ * Build the dropdown menu HTML from the configuration.
1092
+ */
1093
+ async renderTemplate() {
1094
+ const menuItems = this.config.items || [];
1095
+ if (menuItems.length === 0) {
1096
+ return "";
1097
+ }
1098
+ const triggerIcon = this.config.icon || "bi-three-dots-horizontal";
1099
+ const buttonClass = this.config.buttonClass || "btn btn-link text-secondary ps-3 pe-0 pt-0 pb-1";
1100
+ const dropdownId = `context-menu-${this.id}`;
1101
+ const menuItemsHtml = menuItems.map((item) => this.buildMenuItemHTML(item)).join("");
1102
+ return `
1103
+ <button class="${buttonClass}" type="button" id="${dropdownId}" data-bs-toggle="dropdown" aria-expanded="false">
1104
+ <i class="${triggerIcon}"></i>
1105
+ </button>
1106
+ <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="${dropdownId}">
1107
+ ${menuItemsHtml}
1108
+ </ul>
1109
+ `;
1110
+ }
1111
+ /**
1112
+ * Build the HTML for a single menu item.
1113
+ * @param {object} item - The menu item configuration.
1114
+ * @returns {string} The HTML string for the list item.
1115
+ */
1116
+ buildMenuItemHTML(item) {
1117
+ if (item.type === "divider" || item.separator) {
1118
+ return '<li><hr class="dropdown-divider"></li>';
1119
+ }
1120
+ const icon = item.icon ? `<i class="${item.icon} me-2"></i>` : "";
1121
+ const label = item.label || "";
1122
+ const itemClass = `dropdown-item ${item.danger ? "text-danger" : ""} ${item.disabled ? "disabled" : ""}`;
1123
+ const action = item.action || "";
1124
+ if (item.href) {
1125
+ return `<li><a class="${itemClass}" href="${item.href}" target="${item.target || "_self"}">${icon}${label}</a></li>`;
1126
+ }
1127
+ return `<li><a class="${itemClass}" href="#" data-action="menu-item-click" data-item-action="${action}">${icon}${label}</a></li>`;
1128
+ }
1129
+ /**
1130
+ * Handle clicks on menu items.
1131
+ * @param {Event} event - The click event.
1132
+ * @param {HTMLElement} element - The clicked anchor element.
1133
+ */
1134
+ async onActionMenuItemClick(event, element) {
1135
+ event.preventDefault();
1136
+ const action = element.getAttribute("data-item-action");
1137
+ if (!action) return;
1138
+ const menuItem = this.config.items.find((item) => item.action === action);
1139
+ if (!menuItem || menuItem.disabled) return;
1140
+ if (typeof menuItem.handler === "function") {
1141
+ menuItem.handler(this.context, event, element);
1142
+ } else {
1143
+ this.parent.events.dispatch(action, event, element);
1144
+ }
1145
+ this.closeDropdown();
1146
+ }
1147
+ closeDropdown() {
1148
+ const dropdownTrigger = this.element.querySelector('[data-bs-toggle="dropdown"]');
1149
+ if (dropdownTrigger) {
1150
+ const dropdownInstance = window.bootstrap?.Dropdown.getInstance(dropdownTrigger);
1151
+ dropdownInstance?.hide();
1152
+ }
1153
+ }
1154
+ }
1155
+ export {
1156
+ ContextMenu as C,
1157
+ GroupList as G,
1158
+ ToastService as T,
1159
+ User as U,
1160
+ Group as a,
1161
+ GroupForms as b,
1162
+ UserList as c,
1163
+ UserForms as d,
1164
+ UserDataView as e,
1165
+ UserDevice as f,
1166
+ UserDeviceList as g,
1167
+ UserDeviceLocation as h,
1168
+ UserDeviceLocationList as i,
1169
+ Group$1 as j
1170
+ };
1171
+ //# sourceMappingURL=ContextMenu-By2g3KYY.js.map