web-mojo 2.2.57 → 2.2.59

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 (119) 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 +1 -10105
  4. package/dist/admin.es.js.map +1 -1
  5. package/dist/auth.cjs.js +1 -1
  6. package/dist/auth.es.js +1 -588
  7. package/dist/auth.es.js.map +1 -1
  8. package/dist/charts.cjs.js +1 -1
  9. package/dist/charts.es.js +1 -571
  10. package/dist/charts.es.js.map +1 -1
  11. package/dist/chunks/ChatView-D4A9rIX3.js +2 -0
  12. package/dist/chunks/ChatView-D4A9rIX3.js.map +1 -0
  13. package/dist/chunks/ChatView-nxaq8aIo.js +2 -0
  14. package/dist/chunks/ChatView-nxaq8aIo.js.map +1 -0
  15. package/dist/chunks/Collection-1sPoIFvQ.js +2 -0
  16. package/dist/chunks/{Collection-DaiL0uGl.js.map → Collection-1sPoIFvQ.js.map} +1 -1
  17. package/dist/chunks/{Collection-CxbNKOas.js → Collection-DSBRXpwK.js} +2 -2
  18. package/dist/chunks/{Collection-CxbNKOas.js.map → Collection-DSBRXpwK.js.map} +1 -1
  19. package/dist/chunks/{ContextMenu-ClwHEbbD.js → ContextMenu-BWy7WqF4.js} +2 -2
  20. package/dist/chunks/{ContextMenu-ClwHEbbD.js.map → ContextMenu-BWy7WqF4.js.map} +1 -1
  21. package/dist/chunks/ContextMenu-BvniQz-N.js +3 -0
  22. package/dist/chunks/{ContextMenu-sgvgSACY.js.map → ContextMenu-BvniQz-N.js.map} +1 -1
  23. package/dist/chunks/DataView--nUWtq6r.js +2 -0
  24. package/dist/chunks/{DataView-Dzo0jbs2.js.map → DataView--nUWtq6r.js.map} +1 -1
  25. package/dist/chunks/{DataView-1xh3GFeC.js → DataView-CK3Z0TJH.js} +2 -2
  26. package/dist/chunks/{DataView-1xh3GFeC.js.map → DataView-CK3Z0TJH.js.map} +1 -1
  27. package/dist/chunks/Dialog-BcgSR01Z.js +2 -0
  28. package/dist/chunks/{Dialog-DOGDalUq.js.map → Dialog-BcgSR01Z.js.map} +1 -1
  29. package/dist/chunks/{Dialog-CQlTDhZS.js → Dialog-DwCTFV6O.js} +2 -2
  30. package/dist/chunks/{Dialog-CQlTDhZS.js.map → Dialog-DwCTFV6O.js.map} +1 -1
  31. package/dist/chunks/FormPlugins-DvQ-G5J5.js +2 -0
  32. package/dist/chunks/{FormPlugins-DY6e88YT.js.map → FormPlugins-DvQ-G5J5.js.map} +1 -1
  33. package/dist/chunks/{FormView-DaKA4Sys.js → FormView-CRmEReTC.js} +3 -3
  34. package/dist/chunks/{FormView-DaKA4Sys.js.map → FormView-CRmEReTC.js.map} +1 -1
  35. package/dist/chunks/FormView-OLA7t-yv.js +3 -0
  36. package/dist/chunks/{FormView-Dz3mYasQ.js.map → FormView-OLA7t-yv.js.map} +1 -1
  37. package/dist/chunks/ListView-6JQ6tRXs.js +2 -0
  38. package/dist/chunks/{ListView-X5w5jf51.js.map → ListView-6JQ6tRXs.js.map} +1 -1
  39. package/dist/chunks/{ListView-CDzKIpd8.js → ListView-DVStKiMi.js} +2 -2
  40. package/dist/chunks/{ListView-CDzKIpd8.js.map → ListView-DVStKiMi.js.map} +1 -1
  41. package/dist/chunks/{MetricsCountryMapView-Dx2cw7ya.js → MetricsCountryMapView-CnAEbUw_.js} +2 -2
  42. package/dist/chunks/{MetricsCountryMapView-Dx2cw7ya.js.map → MetricsCountryMapView-CnAEbUw_.js.map} +1 -1
  43. package/dist/chunks/MetricsCountryMapView-J067qrrt.js +2 -0
  44. package/dist/chunks/{MetricsCountryMapView-B2xz6zUw.js.map → MetricsCountryMapView-J067qrrt.js.map} +1 -1
  45. package/dist/chunks/{MetricsMiniChartWidget-CBuso0OE.js → MetricsMiniChartWidget-BeD1slGs.js} +2 -2
  46. package/dist/chunks/{MetricsMiniChartWidget-CBuso0OE.js.map → MetricsMiniChartWidget-BeD1slGs.js.map} +1 -1
  47. package/dist/chunks/MetricsMiniChartWidget-x2gFjHOU.js +2 -0
  48. package/dist/chunks/{MetricsMiniChartWidget-DvKd7Qrk.js.map → MetricsMiniChartWidget-x2gFjHOU.js.map} +1 -1
  49. package/dist/chunks/PDFViewer-CsyKn-gh.js +2 -0
  50. package/dist/chunks/{PDFViewer-EJ9cOfPF.js.map → PDFViewer-CsyKn-gh.js.map} +1 -1
  51. package/dist/chunks/{PDFViewer-ofMGdSaj.js → PDFViewer-DSa4BZCm.js} +2 -2
  52. package/dist/chunks/{PDFViewer-ofMGdSaj.js.map → PDFViewer-DSa4BZCm.js.map} +1 -1
  53. package/dist/chunks/Rest-DHbszkuP.js +2 -0
  54. package/dist/chunks/Rest-DHbszkuP.js.map +1 -0
  55. package/dist/chunks/Rest-Ds9e8tN8.js +2 -0
  56. package/dist/chunks/Rest-Ds9e8tN8.js.map +1 -0
  57. package/dist/chunks/TokenManager-D6SjKgPZ.js +2 -0
  58. package/dist/chunks/{TokenManager-DoN9e6q6.js.map → TokenManager-D6SjKgPZ.js.map} +1 -1
  59. package/dist/chunks/{TokenManager-Gqvj7SDX.js → TokenManager-REbha1Le.js} +2 -2
  60. package/dist/chunks/{TokenManager-Gqvj7SDX.js.map → TokenManager-REbha1Le.js.map} +1 -1
  61. package/dist/chunks/WebApp-CULZpO_0.js +2 -0
  62. package/dist/chunks/{WebApp-6qvqmOts.js.map → WebApp-CULZpO_0.js.map} +1 -1
  63. package/dist/chunks/{WebApp-_dgpwtFw.js → WebApp-DovLtA60.js} +2 -2
  64. package/dist/chunks/{WebApp-_dgpwtFw.js.map → WebApp-DovLtA60.js.map} +1 -1
  65. package/dist/chunks/WebSocketClient-B-wc3mez.js +2 -0
  66. package/dist/chunks/{WebSocketClient-DG2olXpH.js.map → WebSocketClient-B-wc3mez.js.map} +1 -1
  67. package/dist/chunks/{WebSocketClient-MFkFlSue.js → WebSocketClient-BdZ9QYll.js} +2 -2
  68. package/dist/chunks/{WebSocketClient-MFkFlSue.js.map → WebSocketClient-BdZ9QYll.js.map} +1 -1
  69. package/dist/chunks/version-C3dnl1bg.js +2 -0
  70. package/dist/chunks/version-C3dnl1bg.js.map +1 -0
  71. package/dist/chunks/{version-BVADfTA5.js → version-ioN546cp.js} +2 -2
  72. package/dist/chunks/{version-BVADfTA5.js.map → version-ioN546cp.js.map} +1 -1
  73. package/dist/css/web-mojo.css +1 -1
  74. package/dist/docit.cjs.js +1 -1
  75. package/dist/docit.es.js +1 -957
  76. package/dist/docit.es.js.map +1 -1
  77. package/dist/index.cjs.js +1 -1
  78. package/dist/index.es.js +1 -3252
  79. package/dist/index.es.js.map +1 -1
  80. package/dist/lightbox.cjs.js +1 -1
  81. package/dist/lightbox.es.js +1 -3737
  82. package/dist/lightbox.es.js.map +1 -1
  83. package/dist/loader.umd.js +2 -2
  84. package/dist/map.cjs.js +1 -1
  85. package/dist/map.es.js +1 -1032
  86. package/dist/map.es.js.map +1 -1
  87. package/dist/mojo-auth.es.js +338 -0
  88. package/dist/mojo-auth.umd.js +1 -0
  89. package/dist/timeline.cjs.js +1 -1
  90. package/dist/timeline.es.js +1 -224
  91. package/dist/timeline.es.js.map +1 -1
  92. package/dist/web-mojo.lite.iife.js +14 -3
  93. package/dist/web-mojo.lite.iife.js.map +1 -1
  94. package/dist/web-mojo.lite.iife.min.js +6 -6
  95. package/dist/web-mojo.lite.iife.min.js.map +1 -1
  96. package/package.json +2 -2
  97. package/dist/chunks/ChatView-9k6xBWXk.js +0 -7632
  98. package/dist/chunks/ChatView-9k6xBWXk.js.map +0 -1
  99. package/dist/chunks/ChatView-CdtuCDYm.js +0 -2
  100. package/dist/chunks/ChatView-CdtuCDYm.js.map +0 -1
  101. package/dist/chunks/Collection-DaiL0uGl.js +0 -1014
  102. package/dist/chunks/ContextMenu-sgvgSACY.js +0 -1535
  103. package/dist/chunks/DataView-Dzo0jbs2.js +0 -862
  104. package/dist/chunks/Dialog-DOGDalUq.js +0 -1579
  105. package/dist/chunks/FormPlugins-DY6e88YT.js +0 -124
  106. package/dist/chunks/FormView-Dz3mYasQ.js +0 -8636
  107. package/dist/chunks/ListView-X5w5jf51.js +0 -495
  108. package/dist/chunks/MetricsCountryMapView-B2xz6zUw.js +0 -1054
  109. package/dist/chunks/MetricsMiniChartWidget-DvKd7Qrk.js +0 -3283
  110. package/dist/chunks/PDFViewer-EJ9cOfPF.js +0 -946
  111. package/dist/chunks/Rest-CgSjfMaU.js +0 -2
  112. package/dist/chunks/Rest-CgSjfMaU.js.map +0 -1
  113. package/dist/chunks/Rest-W-sPfGh9.js +0 -4375
  114. package/dist/chunks/Rest-W-sPfGh9.js.map +0 -1
  115. package/dist/chunks/TokenManager-DoN9e6q6.js +0 -1423
  116. package/dist/chunks/WebApp-6qvqmOts.js +0 -1386
  117. package/dist/chunks/WebSocketClient-DG2olXpH.js +0 -209
  118. package/dist/chunks/version-OyPGnx30.js +0 -38
  119. package/dist/chunks/version-OyPGnx30.js.map +0 -1
@@ -1,1535 +0,0 @@
1
- import { V as View } from "./Rest-W-sPfGh9.js";
2
- import { C as Collection, M as Model } from "./Collection-DaiL0uGl.js";
3
- class Page extends View {
4
- constructor(options = {}) {
5
- options.tagName = options.tagName || "main";
6
- options.className = options.className || "mojo-page";
7
- const pageName = options.pageName || "";
8
- if (pageName && !options.id) {
9
- options.id = "page_" + pageName.toLowerCase().replace(/\s+/g, "_");
10
- }
11
- super(options);
12
- this.pageName = options.pageName || this.constructor.pageName || "";
13
- this.route = options.route || this.constructor.route || "";
14
- this.title = options.title || this.pageName || "";
15
- if (!this.id && this.constructor.pageName && !options.pageName) {
16
- this.id = "page_" + this.constructor.pageName.toLowerCase().replace(/\s+/g, "_");
17
- }
18
- this.pageIcon = options.icon || options.pageIcon || this.constructor.pageIcon || "bi bi-file-text";
19
- this.displayName = options.displayName || this.constructor.displayName || this.pageName || "";
20
- this.pageDescription = options.pageDescription || this.constructor.pageDescription || "";
21
- this.params = {};
22
- this.query = {};
23
- this.matched = false;
24
- this.isActive = false;
25
- this.pageOptions = {
26
- title: options.title || this.pageName || "Untitled Page",
27
- description: options.description || "",
28
- requiresAuth: options.requiresAuth || false,
29
- ...options.pageOptions
30
- };
31
- this.savedState = null;
32
- console.log(`Page ${this.pageName} constructed with route: ${this.route}`);
33
- }
34
- /**
35
- * Handle route parameters - from design doc
36
- * @param {object} params - Route parameters
37
- * @param {object} query - Query string parameters
38
- */
39
- async onParams(params = {}, query = {}) {
40
- this.params = params;
41
- this.query = query;
42
- }
43
- canEnter() {
44
- if (this.options.permissions) {
45
- const user = this.getApp().activeUser;
46
- if (!user || !user.hasPermission(this.options.permissions)) {
47
- return false;
48
- }
49
- }
50
- if (this.options.requiresGroup && !this.getApp().activeGroup) {
51
- return false;
52
- }
53
- return true;
54
- }
55
- /**
56
- * Called when entering this page (before render)
57
- * Override this method for initialization logic
58
- */
59
- async onEnter() {
60
- this.isActive = true;
61
- await this.onInitView();
62
- if (this.savedState) {
63
- this.restoreState(this.savedState);
64
- this.savedState = null;
65
- }
66
- if (this.pageOptions && this.pageOptions.title && typeof document !== "undefined") {
67
- document.title = this.pageOptions.title;
68
- }
69
- this.emit("activated", {
70
- page: this.getMetadata()
71
- });
72
- console.log(`Page ${this.pageName} entered`);
73
- }
74
- /**
75
- * Called when leaving this page (before cleanup)
76
- * Override this method for cleanup logic like removing listeners, clearing timers, etc.
77
- */
78
- async onExit() {
79
- this.savedState = this.captureState();
80
- this.isActive = false;
81
- this.emit("deactivated", {
82
- page: this.getMetadata()
83
- });
84
- console.log(`Page ${this.pageName} exiting`);
85
- }
86
- /**
87
- * Get page metadata for display and events
88
- * @returns {object} Page metadata
89
- */
90
- getMetadata() {
91
- return {
92
- name: this.pageName,
93
- displayName: this.displayName || this.pageName,
94
- icon: this.pageIcon,
95
- description: this.pageDescription,
96
- route: this.route,
97
- isActive: this.isActive
98
- };
99
- }
100
- /**
101
- * Handle default action - fallback from design doc
102
- */
103
- async onActionDefault(action) {
104
- console.log(`Default action '${action}' triggered on page: ${this.pageName}`);
105
- }
106
- async makeActive() {
107
- this.getApp().showPage(this);
108
- }
109
- async onActionNavigate(event, element) {
110
- event.preventDefault();
111
- const page = element.dataset.page;
112
- this.getApp().showPage(page);
113
- }
114
- /**
115
- * Capture current page state for preservation
116
- * @returns {object|null} Captured state
117
- */
118
- captureState() {
119
- if (!this.element) return null;
120
- return {
121
- scrollTop: this.element.scrollTop,
122
- formData: this.captureFormData(),
123
- custom: this.captureCustomState()
124
- };
125
- }
126
- /**
127
- * Restore saved state
128
- * @param {object} state - State to restore
129
- */
130
- restoreState(state) {
131
- if (!state || !this.element) return;
132
- this.element.scrollTop = state.scrollTop || 0;
133
- this.restoreFormData(state.formData);
134
- if (state.custom) {
135
- this.restoreCustomState(state.custom);
136
- }
137
- }
138
- /**
139
- * Capture form data from page
140
- * @returns {object} Form data
141
- */
142
- captureFormData() {
143
- const data = {};
144
- if (!this.element) return data;
145
- this.element.querySelectorAll("input, select, textarea").forEach((field) => {
146
- if (field.name) {
147
- if (field.type === "checkbox") {
148
- data[field.name] = field.checked;
149
- } else if (field.type === "radio") {
150
- if (field.checked) {
151
- data[field.name] = field.value;
152
- }
153
- } else {
154
- data[field.name] = field.value;
155
- }
156
- }
157
- });
158
- return data;
159
- }
160
- /**
161
- * Restore form data to page
162
- * @param {object} formData - Form data to restore
163
- */
164
- restoreFormData(formData) {
165
- if (!formData || !this.element) return;
166
- Object.entries(formData).forEach(([name, value]) => {
167
- const field = this.element.querySelector(`[name="${name}"]`);
168
- if (field) {
169
- if (field.type === "checkbox") {
170
- field.checked = value;
171
- } else if (field.type === "radio") {
172
- const radio = this.element.querySelector(`[name="${name}"][value="${value}"]`);
173
- if (radio) radio.checked = true;
174
- } else {
175
- field.value = value;
176
- }
177
- }
178
- });
179
- }
180
- /**
181
- * Capture custom state - override in subclasses
182
- * @returns {object} Custom state
183
- */
184
- captureCustomState() {
185
- return {};
186
- }
187
- /**
188
- * Restore custom state - override in subclasses
189
- * @param {object} state - Custom state to restore
190
- */
191
- restoreCustomState(state) {
192
- }
193
- /**
194
- * Set page metadata
195
- * @param {object} meta - Metadata object
196
- */
197
- setMeta(meta = {}) {
198
- if (typeof document === "undefined") {
199
- return;
200
- }
201
- if (meta.title) {
202
- document.title = meta.title;
203
- this.pageOptions.title = meta.title;
204
- }
205
- if (meta.description) {
206
- let descMeta = document.querySelector('meta[name="description"]');
207
- if (!descMeta) {
208
- descMeta = document.createElement("meta");
209
- descMeta.name = "description";
210
- document.head.appendChild(descMeta);
211
- }
212
- descMeta.content = meta.description;
213
- this.pageOptions.description = meta.description;
214
- }
215
- Object.entries(meta).forEach(([key, value]) => {
216
- if (key !== "title" && key !== "description") {
217
- let metaEl = document.querySelector(`meta[name="${key}"]`);
218
- if (!metaEl) {
219
- metaEl = document.createElement("meta");
220
- metaEl.name = key;
221
- document.head.appendChild(metaEl);
222
- }
223
- metaEl.content = value;
224
- }
225
- });
226
- }
227
- /**
228
- * Show error message with page context
229
- * @param {string} message - Error message
230
- */
231
- showError(message) {
232
- super.showError(message);
233
- if (this.element) {
234
- const errorDiv = document.createElement("div");
235
- errorDiv.className = "alert alert-danger alert-dismissible fade show";
236
- errorDiv.innerHTML = `
237
- ${message}
238
- <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
239
- `;
240
- this.element.insertBefore(errorDiv, this.element.firstChild);
241
- setTimeout(() => {
242
- if (errorDiv.parentNode) {
243
- errorDiv.parentNode.removeChild(errorDiv);
244
- }
245
- }, 5e3);
246
- }
247
- }
248
- /**
249
- * Show success message with page context
250
- * @param {string} message - Success message
251
- */
252
- showSuccess(message) {
253
- super.showSuccess(message);
254
- if (this.element) {
255
- const successDiv = document.createElement("div");
256
- successDiv.className = "alert alert-success alert-dismissible fade show";
257
- successDiv.innerHTML = `
258
- ${message}
259
- <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
260
- `;
261
- this.element.insertBefore(successDiv, this.element.firstChild);
262
- setTimeout(() => {
263
- if (successDiv.parentNode) {
264
- successDiv.parentNode.removeChild(successDiv);
265
- }
266
- }, 3e3);
267
- }
268
- }
269
- /**
270
- * Page-specific before render hook
271
- */
272
- async onBeforeRender() {
273
- await super.onBeforeRender();
274
- this.setMeta({
275
- title: this.pageOptions.title,
276
- description: this.pageOptions.description
277
- });
278
- }
279
- /**
280
- * Page-specific after mount hook
281
- */
282
- async onAfterMount() {
283
- await super.onAfterMount();
284
- if (typeof document !== "undefined" && this.pageName) {
285
- document.body.classList.add(`page-${this.pageName.toLowerCase().replace(/\s+/g, "-")}`);
286
- }
287
- }
288
- /**
289
- * Page-specific before destroy hook
290
- */
291
- async onBeforeDestroy() {
292
- await super.onBeforeDestroy();
293
- if (typeof document !== "undefined" && this.pageName) {
294
- document.body.classList.remove(`page-${this.pageName.toLowerCase().replace(/\s+/g, "-")}`);
295
- }
296
- }
297
- /**
298
- * Navigate to another page using the app's router
299
- * @param {string} route - Route to navigate to
300
- * @param {object} params - Route parameters
301
- * @param {object} options - Navigation options
302
- */
303
- navigate(route, params = {}, options = {}) {
304
- if (this.app && this.app.router) {
305
- return this.app.router.navigate(route, options);
306
- }
307
- if (typeof window !== "undefined" && window.MOJO?.router) {
308
- return window.MOJO.router.navigate(route, options);
309
- }
310
- console.error("No router available for navigation");
311
- }
312
- getRoute() {
313
- if (this.route) {
314
- let route = this.route;
315
- if (typeof route === "string" && route.startsWith("/")) {
316
- route = route.substring(1);
317
- }
318
- return route;
319
- }
320
- return this.pageName;
321
- }
322
- syncUrl(force = true) {
323
- this.updateBrowserUrl(this.query, false, false);
324
- }
325
- updateBrowserUrl(query = null, replace = false, trigger = false) {
326
- this.getApp();
327
- this.app.router.updateBrowserUrl(this.getRoute(), query, replace, trigger);
328
- }
329
- /**
330
- * Static method to define a page class with metadata
331
- * @param {object} definition - Page class definition
332
- * @returns {class} Page class
333
- */
334
- static define(definition) {
335
- class DefinedPage extends Page {
336
- constructor(options = {}) {
337
- super({
338
- ...definition,
339
- ...options
340
- });
341
- }
342
- }
343
- DefinedPage.template = definition.template;
344
- DefinedPage.pageName = definition.pageName;
345
- DefinedPage.route = definition.route;
346
- return DefinedPage;
347
- }
348
- }
349
- class ToastService {
350
- constructor(options = {}) {
351
- this.options = {
352
- containerId: "toast-container",
353
- position: "top-end",
354
- // top-start, top-center, top-end, middle-start, etc.
355
- autohide: true,
356
- defaultDelay: 5e3,
357
- // 5 seconds
358
- maxToasts: 5,
359
- // Maximum number of toasts to show at once
360
- ...options
361
- };
362
- this.toasts = /* @__PURE__ */ new Map();
363
- this.toastCounter = 0;
364
- this.init();
365
- }
366
- /**
367
- * Initialize the toast service
368
- */
369
- init() {
370
- this.createContainer();
371
- }
372
- /**
373
- * Create the toast container if it doesn't exist
374
- */
375
- createContainer() {
376
- let container = document.getElementById(this.options.containerId);
377
- if (!container) {
378
- container = document.createElement("div");
379
- container.id = this.options.containerId;
380
- container.className = `toast-container position-fixed ${this.getPositionClasses()}`;
381
- container.style.zIndex = "1070";
382
- container.setAttribute("aria-live", "polite");
383
- container.setAttribute("aria-atomic", "true");
384
- document.body.appendChild(container);
385
- }
386
- this.container = container;
387
- }
388
- /**
389
- * Get CSS classes for toast positioning
390
- */
391
- getPositionClasses() {
392
- const positionMap = {
393
- "top-start": "top-0 start-0 p-3",
394
- "top-center": "top-0 start-50 translate-middle-x p-3",
395
- "top-end": "top-0 end-0 p-3",
396
- "middle-start": "top-50 start-0 translate-middle-y p-3",
397
- "middle-center": "top-50 start-50 translate-middle p-3",
398
- "middle-end": "top-50 end-0 translate-middle-y p-3",
399
- "bottom-start": "bottom-0 start-0 p-3",
400
- "bottom-center": "bottom-0 start-50 translate-middle-x p-3",
401
- "bottom-end": "bottom-0 end-0 p-3"
402
- };
403
- return positionMap[this.options.position] || positionMap["top-end"];
404
- }
405
- /**
406
- * Show a success toast
407
- * @param {string} message - The message to display
408
- * @param {object} options - Additional options
409
- */
410
- success(message, options = {}) {
411
- return this.show(message, "success", {
412
- icon: "bi-check-circle-fill",
413
- ...options
414
- });
415
- }
416
- /**
417
- * Show an error toast
418
- * @param {string} message - The message to display
419
- * @param {object} options - Additional options
420
- */
421
- error(message, options = {}) {
422
- return this.show(message, "error", {
423
- icon: "bi-exclamation-triangle-fill",
424
- autohide: true,
425
- // Keep error toasts visible until manually dismissed
426
- ...options
427
- });
428
- }
429
- /**
430
- * Show an info toast
431
- * @param {string} message - The message to display
432
- * @param {object} options - Additional options
433
- */
434
- info(message, options = {}) {
435
- return this.show(message, "info", {
436
- icon: "bi-info-circle-fill",
437
- ...options
438
- });
439
- }
440
- /**
441
- * Show a warning toast
442
- * @param {string} message - The message to display
443
- * @param {object} options - Additional options
444
- */
445
- warning(message, options = {}) {
446
- return this.show(message, "warning", {
447
- icon: "bi-exclamation-triangle-fill",
448
- ...options
449
- });
450
- }
451
- /**
452
- * Show a plain toast without specific styling
453
- * @param {string} message - The message to display
454
- * @param {object} options - Additional options
455
- */
456
- plain(message, options = {}) {
457
- return this.show(message, "plain", {
458
- ...options
459
- });
460
- }
461
- /**
462
- * Show a toast with specified type and options
463
- * @param {string} message - The message to display
464
- * @param {string} type - Toast type (success, error, info, warning)
465
- * @param {object} options - Additional options
466
- */
467
- show(message, type = "info", options = {}) {
468
- this.enforceMaxToasts();
469
- const toastId = `toast-${++this.toastCounter}`;
470
- const config = {
471
- title: this.getDefaultTitle(type),
472
- icon: this.getDefaultIcon(type),
473
- autohide: this.options.autohide,
474
- delay: this.options.defaultDelay,
475
- dismissible: true,
476
- ...options
477
- };
478
- const toastElement = this.createToastElement(toastId, message, type, config);
479
- this.container.appendChild(toastElement);
480
- if (typeof bootstrap === "undefined") {
481
- throw new Error("Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.");
482
- }
483
- const bsToast = new bootstrap.Toast(toastElement, {
484
- autohide: config.autohide,
485
- delay: config.delay
486
- });
487
- this.toasts.set(toastId, {
488
- element: toastElement,
489
- bootstrap: bsToast,
490
- type,
491
- message
492
- });
493
- toastElement.addEventListener("hidden.bs.toast", () => {
494
- this.cleanup(toastId);
495
- });
496
- bsToast.show();
497
- return {
498
- id: toastId,
499
- hide: () => {
500
- try {
501
- bsToast.hide();
502
- } catch (error) {
503
- console.warn("Error hiding toast:", error);
504
- }
505
- },
506
- dispose: () => this.cleanup(toastId),
507
- updateProgress: options.updateProgress || null
508
- };
509
- }
510
- /**
511
- * Show a toast with a View component in the body
512
- * @param {View} view - The View component to display
513
- * @param {string} type - Toast type (success, error, info, warning, plain)
514
- * @param {object} options - Additional options
515
- */
516
- showView(view, type = "info", options = {}) {
517
- this.enforceMaxToasts();
518
- const toastId = `toast-${++this.toastCounter}`;
519
- const config = {
520
- title: options.title || this.getDefaultTitle(type),
521
- icon: options.icon || this.getDefaultIcon(type),
522
- autohide: this.options.autohide,
523
- delay: this.options.defaultDelay,
524
- dismissible: true,
525
- ...options
526
- };
527
- const toastElement = this.createViewToastElement(toastId, view, type, config);
528
- this.container.appendChild(toastElement);
529
- if (typeof bootstrap === "undefined") {
530
- throw new Error("Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.");
531
- }
532
- const bsToast = new bootstrap.Toast(toastElement, {
533
- autohide: config.autohide,
534
- delay: config.delay
535
- });
536
- this.toasts.set(toastId, {
537
- element: toastElement,
538
- bootstrap: bsToast,
539
- type,
540
- view,
541
- message: "View toast"
542
- });
543
- toastElement.addEventListener("hidden.bs.toast", () => {
544
- this.cleanupView(toastId);
545
- });
546
- const bodyContainer = toastElement.querySelector(".toast-view-body");
547
- if (bodyContainer && view) {
548
- view.render(true, bodyContainer);
549
- }
550
- bsToast.show();
551
- return {
552
- id: toastId,
553
- view,
554
- hide: () => {
555
- try {
556
- bsToast.hide();
557
- } catch (error) {
558
- console.warn("Error hiding view toast:", error);
559
- }
560
- },
561
- dispose: () => this.cleanupView(toastId),
562
- updateProgress: (progressInfo) => {
563
- if (view && typeof view.updateProgress === "function") {
564
- view.updateProgress(progressInfo);
565
- }
566
- }
567
- };
568
- }
569
- /**
570
- * Create toast DOM element
571
- */
572
- createToastElement(id, message, type, config) {
573
- const toast = document.createElement("div");
574
- toast.id = id;
575
- toast.className = `toast toast-service-${type}`;
576
- toast.setAttribute("role", "alert");
577
- toast.setAttribute("aria-live", "assertive");
578
- toast.setAttribute("aria-atomic", "true");
579
- const header = config.title || config.icon ? this.createToastHeader(config, type) : "";
580
- const body = this.createToastBody(message, config.icon && !config.title);
581
- toast.innerHTML = `
582
- ${header}
583
- ${body}
584
- `;
585
- return toast;
586
- }
587
- /**
588
- * Create toast DOM element for View component
589
- */
590
- createViewToastElement(id, view, type, config) {
591
- const toast = document.createElement("div");
592
- toast.id = id;
593
- toast.className = `toast toast-service-${type}`;
594
- toast.setAttribute("role", "alert");
595
- toast.setAttribute("aria-live", "assertive");
596
- toast.setAttribute("aria-atomic", "true");
597
- const header = config.title || config.icon ? this.createToastHeader(config, type) : "";
598
- const body = this.createViewToastBody();
599
- toast.innerHTML = `
600
- ${header}
601
- ${body}
602
- `;
603
- return toast;
604
- }
605
- /**
606
- * Create toast body for View component
607
- */
608
- createViewToastBody() {
609
- return `
610
- <div class="toast-body p-0">
611
- <div class="toast-view-body p-3"></div>
612
- </div>
613
- `;
614
- }
615
- /**
616
- * Create toast header with title and icon
617
- */
618
- createToastHeader(config, _type) {
619
- const iconHtml = config.icon ? `<i class="${config.icon} toast-service-icon me-2"></i>` : "";
620
- const titleHtml = config.title ? `<strong class="me-auto">${iconHtml}${this.escapeHtml(config.title)}</strong>` : "";
621
- const timeHtml = config.showTime ? `<small class="text-muted">${this.getTimeString()}</small>` : "";
622
- const closeButton = config.dismissible ? `<button type="button" class="btn-close toast-service-close" data-bs-dismiss="toast" aria-label="Close"></button>` : "";
623
- if (!titleHtml && !timeHtml && !closeButton) {
624
- return "";
625
- }
626
- return `
627
- <div class="toast-header">
628
- ${titleHtml}
629
- ${timeHtml}
630
- ${closeButton}
631
- </div>
632
- `;
633
- }
634
- /**
635
- * Create toast body with message
636
- */
637
- createToastBody(message, showIcon = false) {
638
- const iconHtml = showIcon ? `<i class="${this.getDefaultIcon("info")} toast-service-icon me-2"></i>` : "";
639
- return `
640
- <div class="toast-body d-flex align-items-center">
641
- ${iconHtml}
642
- <span>${this.escapeHtml(message)}</span>
643
- </div>
644
- `;
645
- }
646
- /**
647
- * Get default title for toast type
648
- */
649
- getDefaultTitle(type) {
650
- const titles = {
651
- success: "Success",
652
- error: "Error",
653
- warning: "Warning",
654
- info: "Information",
655
- plain: ""
656
- };
657
- return titles[type] || "Notification";
658
- }
659
- /**
660
- * Get default icon for toast type
661
- */
662
- getDefaultIcon(type) {
663
- const icons = {
664
- success: "bi-check-circle-fill",
665
- error: "bi-exclamation-triangle-fill",
666
- warning: "bi-exclamation-triangle-fill",
667
- info: "bi-info-circle-fill",
668
- plain: ""
669
- };
670
- return icons[type] || "bi-info-circle-fill";
671
- }
672
- /**
673
- * Enforce maximum number of toasts
674
- */
675
- enforceMaxToasts() {
676
- const activeToasts = Array.from(this.toasts.values());
677
- if (activeToasts.length >= this.options.maxToasts) {
678
- const oldestId = this.toasts.keys().next().value;
679
- const oldest = this.toasts.get(oldestId);
680
- if (oldest) {
681
- oldest.bootstrap.hide();
682
- }
683
- }
684
- }
685
- /**
686
- * Clean up toast resources
687
- */
688
- cleanup(toastId) {
689
- const toast = this.toasts.get(toastId);
690
- if (toast) {
691
- try {
692
- toast.bootstrap.dispose();
693
- } catch (e) {
694
- console.warn("Error disposing toast:", e);
695
- }
696
- if (toast.element && toast.element.parentNode) {
697
- toast.element.parentNode.removeChild(toast.element);
698
- }
699
- this.toasts.delete(toastId);
700
- }
701
- }
702
- /**
703
- * Clean up view toast resources with proper view disposal
704
- */
705
- cleanupView(toastId) {
706
- const toast = this.toasts.get(toastId);
707
- if (toast) {
708
- if (toast.view && typeof toast.view.dispose === "function") {
709
- try {
710
- toast.view.dispose();
711
- } catch (e) {
712
- console.warn("Error disposing view in toast:", e);
713
- }
714
- }
715
- try {
716
- toast.bootstrap.dispose();
717
- } catch (e) {
718
- console.warn("Error disposing toast:", e);
719
- }
720
- if (toast.element && toast.element.parentNode) {
721
- toast.element.parentNode.removeChild(toast.element);
722
- }
723
- this.toasts.delete(toastId);
724
- }
725
- }
726
- /**
727
- * Hide all active toasts
728
- */
729
- hideAll() {
730
- this.toasts.forEach((toast, _id) => {
731
- toast.bootstrap.hide();
732
- });
733
- }
734
- /**
735
- * Clear all toasts immediately
736
- */
737
- clearAll() {
738
- this.toasts.forEach((toast, id) => {
739
- this.cleanup(id);
740
- });
741
- }
742
- /**
743
- * Get current time string
744
- */
745
- getTimeString() {
746
- return (/* @__PURE__ */ new Date()).toLocaleTimeString([], {
747
- hour: "2-digit",
748
- minute: "2-digit"
749
- });
750
- }
751
- /**
752
- * Escape HTML to prevent XSS
753
- */
754
- escapeHtml(str) {
755
- const div = document.createElement("div");
756
- div.textContent = str;
757
- return div.innerHTML;
758
- }
759
- /**
760
- * Dispose of the entire toast service
761
- */
762
- dispose() {
763
- this.clearAll();
764
- if (this.container && this.container.parentNode) {
765
- this.container.parentNode.removeChild(this.container);
766
- }
767
- }
768
- /**
769
- * Get statistics about active toasts
770
- */
771
- getStats() {
772
- const stats = {
773
- total: this.toasts.size,
774
- byType: {}
775
- };
776
- this.toasts.forEach((toast) => {
777
- stats.byType[toast.type] = (stats.byType[toast.type] || 0) + 1;
778
- });
779
- return stats;
780
- }
781
- /**
782
- * Set global options
783
- */
784
- setOptions(newOptions) {
785
- this.options = { ...this.options, ...newOptions };
786
- if (newOptions.position) {
787
- if (this.container) {
788
- this.container.className = `toast-container position-fixed ${this.getPositionClasses()}`;
789
- }
790
- }
791
- }
792
- }
793
- class Group extends Model {
794
- constructor(data = {}) {
795
- super(data, {
796
- endpoint: "/api/group"
797
- });
798
- }
799
- }
800
- class GroupList extends Collection {
801
- constructor(options = {}) {
802
- super({
803
- ModelClass: Group,
804
- endpoint: "/api/group",
805
- size: 10,
806
- ...options
807
- });
808
- }
809
- }
810
- const GroupKinds = {
811
- "org": "Organization",
812
- "division": "Division",
813
- "department": "Department",
814
- "team": "Team",
815
- "merchant": "Merchant",
816
- "partner": "Partner",
817
- "client": "Client",
818
- "iso": "ISO",
819
- "sales": "Sales",
820
- "reseller": "Reseller",
821
- "location": "Location",
822
- "region": "Region",
823
- "route": "Route",
824
- "project": "Project",
825
- "inventory": "Inventory",
826
- "test": "Testing",
827
- "misc": "Miscellaneous",
828
- "qa": "Quality Assurance"
829
- };
830
- const GroupKindOptions = Object.entries(GroupKinds).map(([key, label]) => ({
831
- value: key,
832
- label
833
- }));
834
- const GroupForms = {
835
- create: {
836
- title: "Create Group",
837
- fields: [
838
- {
839
- name: "name",
840
- type: "text",
841
- label: "Group Name",
842
- required: true,
843
- placeholder: "Enter group name"
844
- },
845
- {
846
- name: "kind",
847
- type: "select",
848
- label: "Group Kind",
849
- required: true,
850
- options: GroupKindOptions
851
- },
852
- {
853
- type: "collection",
854
- name: "parent",
855
- label: "Parent Group",
856
- Collection: GroupList,
857
- // Collection class
858
- labelField: "name",
859
- // Field to display in dropdown
860
- valueField: "id",
861
- // Field to use as value
862
- maxItems: 10,
863
- // Max items to show in dropdown
864
- placeholder: "Search groups...",
865
- emptyFetch: false,
866
- debounceMs: 300
867
- // Search debounce delay
868
- }
869
- ]
870
- },
871
- edit: {
872
- title: "Edit Group",
873
- fields: [
874
- {
875
- name: "name",
876
- type: "text",
877
- label: "Group Name",
878
- required: true,
879
- placeholder: "Enter group name"
880
- },
881
- {
882
- name: "kind",
883
- type: "select",
884
- label: "Group Kind",
885
- required: true,
886
- options: GroupKindOptions
887
- },
888
- {
889
- type: "collection",
890
- name: "parent",
891
- label: "Parent Group",
892
- Collection: GroupList,
893
- // Collection class
894
- labelField: "name",
895
- // Field to display in dropdown
896
- valueField: "id",
897
- // Field to use as value
898
- maxItems: 10,
899
- // Max items to show in dropdown
900
- placeholder: "Search groups...",
901
- emptyFetch: false,
902
- debounceMs: 300
903
- // Search debounce delay
904
- },
905
- {
906
- name: "metadata.domain",
907
- type: "text",
908
- label: "Default Domain",
909
- placeholder: "Enter Domain"
910
- },
911
- {
912
- name: "metadata.portal",
913
- type: "text",
914
- label: "Default Portal",
915
- placeholder: "Enter Portal URL"
916
- },
917
- {
918
- name: "is_active",
919
- type: "switch",
920
- label: "Is Active",
921
- cols: 4
922
- }
923
- ]
924
- },
925
- detailed: {
926
- title: "Group Details",
927
- fields: [
928
- // Profile Header
929
- {
930
- type: "header",
931
- text: "Profile Information",
932
- level: 4,
933
- class: "text-primary mb-3"
934
- },
935
- // Avatar and Basic Info
936
- {
937
- type: "group",
938
- columns: { xs: 12, md: 4 },
939
- fields: [
940
- {
941
- type: "image",
942
- name: "avatar",
943
- size: "lg",
944
- imageSize: { width: 200, height: 200 },
945
- placeholder: "Upload your avatar",
946
- help: "Square images work best",
947
- columns: 12
948
- },
949
- {
950
- name: "is_active",
951
- type: "switch",
952
- label: "Is Active",
953
- columns: 12
954
- }
955
- ]
956
- },
957
- // Profile Details
958
- {
959
- type: "group",
960
- columns: { xs: 12, md: 8 },
961
- title: "Details",
962
- fields: [
963
- {
964
- name: "name",
965
- type: "text",
966
- label: "Group Name",
967
- required: true,
968
- placeholder: "Enter group name",
969
- columns: 12
970
- },
971
- {
972
- name: "kind",
973
- type: "select",
974
- label: "Group Kind",
975
- required: true,
976
- columns: 12,
977
- options: [
978
- { value: "org", label: "Organization" },
979
- { value: "team", label: "Team" },
980
- { value: "department", label: "Department" },
981
- { value: "merchant", label: "Merchant" },
982
- { value: "iso", label: "ISO" },
983
- { value: "group", label: "Group" }
984
- ]
985
- },
986
- {
987
- type: "collection",
988
- name: "parent",
989
- label: "Parent Group",
990
- Collection: GroupList,
991
- // Collection class
992
- labelField: "name",
993
- // Field to display in dropdown
994
- valueField: "id",
995
- // Field to use as value
996
- maxItems: 10,
997
- // Max items to show in dropdown
998
- placeholder: "Search groups...",
999
- emptyFetch: false,
1000
- debounceMs: 300,
1001
- // Search debounce delay
1002
- columns: 12
1003
- }
1004
- ]
1005
- },
1006
- // Account Settings
1007
- {
1008
- type: "group",
1009
- columns: 12,
1010
- title: "Account Settings",
1011
- class: "pt-3",
1012
- fields: [
1013
- {
1014
- type: "select",
1015
- name: "metadata.timezone",
1016
- label: "Timezone",
1017
- columns: 6,
1018
- value: "America/Los_Angeles",
1019
- options: [
1020
- { value: "America/New_York", text: "Eastern Time" },
1021
- { value: "America/Chicago", text: "Central Time" },
1022
- { value: "America/Denver", text: "Mountain Time" },
1023
- { value: "America/Los_Angeles", text: "Pacific Time" },
1024
- { value: "UTC", text: "UTC" }
1025
- ]
1026
- },
1027
- {
1028
- type: "select",
1029
- name: "metadata.eod_hour",
1030
- label: "End of Day Hour",
1031
- columns: 6,
1032
- options: [
1033
- { value: 0, text: "Midnight" },
1034
- { value: 1, text: "1 AM" },
1035
- { value: 2, text: "2 AM" },
1036
- { value: 3, text: "3 AM" },
1037
- { value: 4, text: "4 AM" },
1038
- { value: 5, text: "5 AM" },
1039
- { value: 6, text: "6 AM" },
1040
- { value: 7, text: "7 AM" },
1041
- { value: 8, text: "8 AM" },
1042
- { value: 9, text: "9 AM" },
1043
- { value: 10, text: "10 AM" },
1044
- { value: 11, text: "11 AM" },
1045
- { value: 12, text: "12 PM" },
1046
- { value: 13, text: "1 PM" },
1047
- { value: 14, text: "2 PM" },
1048
- { value: 15, text: "3 PM" },
1049
- { value: 16, text: "4 PM" },
1050
- { value: 17, text: "5 PM" },
1051
- { value: 18, text: "6 PM" },
1052
- { value: 19, text: "7 PM" },
1053
- { value: 20, text: "8 PM" },
1054
- { value: 21, text: "9 PM" },
1055
- { value: 22, text: "10 PM" },
1056
- { value: 23, text: "11 PM" }
1057
- ]
1058
- }
1059
- ]
1060
- },
1061
- {
1062
- type: "text",
1063
- label: "Email Template (Prefix)",
1064
- name: "metadata.email_template",
1065
- columns: 12
1066
- }
1067
- ]
1068
- }
1069
- };
1070
- Group.EDIT_FORM = GroupForms.edit;
1071
- Group.ADD_FORM = GroupForms.create;
1072
- Group.CREATE_FORM = GroupForms.create;
1073
- Group.GroupKindOptions = GroupKindOptions;
1074
- Group.GroupKinds = GroupKinds;
1075
- const Group$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1076
- __proto__: null,
1077
- Group,
1078
- GroupForms,
1079
- GroupList
1080
- }, Symbol.toStringTag, { value: "Module" }));
1081
- class User extends Model {
1082
- constructor(data = {}) {
1083
- super(data, {
1084
- endpoint: "/api/user"
1085
- });
1086
- }
1087
- hasPermission(permission) {
1088
- if (this.get("is_superuser")) return true;
1089
- if (Array.isArray(permission)) {
1090
- return permission.some((p) => this.hasPermission(p));
1091
- }
1092
- const isSysPermission = permission.startsWith("sys.");
1093
- const permissionToCheck = isSysPermission ? permission.substring(4) : permission;
1094
- if (this._hasPermission(permissionToCheck)) {
1095
- return true;
1096
- }
1097
- if (!isSysPermission && this.member && this.member.hasPermission(permission)) {
1098
- return true;
1099
- }
1100
- return false;
1101
- }
1102
- _hasPermission(permission) {
1103
- const permissions = this.get("permissions");
1104
- if (!permissions) {
1105
- return false;
1106
- }
1107
- return permissions[permission] == true;
1108
- }
1109
- hasPerm(p) {
1110
- return this.hasPermission(p);
1111
- }
1112
- }
1113
- class UserList extends Collection {
1114
- constructor(options = {}) {
1115
- super({
1116
- ModelClass: User,
1117
- endpoint: "/api/user",
1118
- ...options
1119
- });
1120
- }
1121
- }
1122
- User.PERMISSIONS = [
1123
- { name: "manage_users", label: "Manage Users" },
1124
- { name: "view_users", label: "View Users" },
1125
- { name: "view_groups", label: "View Groups" },
1126
- { name: "manage_groups", label: "Manage Groups" },
1127
- { name: "view_metrics", label: "View System Metrics" },
1128
- { name: "manage_metrics", label: "Manage System Metrics" },
1129
- { name: "view_logs", label: "View Logs" },
1130
- { name: "view_incidents", label: "View Incidents" },
1131
- { name: "manage_incidents", label: "Manage Incidents" },
1132
- { name: "view_tickets", label: "View Tickets" },
1133
- { name: "manage_tickets", label: "Manage Tickets" },
1134
- { name: "view_admin", label: "View Admin" },
1135
- { name: "view_jobs", label: "View Jobs" },
1136
- { name: "manage_jobs", label: "Manage Jobs" },
1137
- { name: "view_global", label: "View Global" },
1138
- { name: "manage_notifications", label: "Manage Notifications" },
1139
- { name: "manage_files", label: "Manage Files" },
1140
- { name: "force_single_session", label: "Force Single Session" },
1141
- { name: "file_vault", label: "Access File Vault" },
1142
- { name: "manage_aws", label: "Manage AWS" },
1143
- { name: "manage_docit", label: "Manage DocIt" }
1144
- ];
1145
- User.PERMISSION_FIELDS = [
1146
- ...User.PERMISSIONS.map((permission) => ({
1147
- name: `permissions.${permission.name}`,
1148
- type: "switch",
1149
- label: permission.label,
1150
- columns: 4
1151
- }))
1152
- ];
1153
- const UserForms = {
1154
- create: {
1155
- title: "Create User",
1156
- fields: [
1157
- { name: "email", type: "text", label: "Email", required: true },
1158
- { name: "phone_number", type: "text", label: "Phone number", columns: 12 },
1159
- { name: "display_name", type: "text", label: "Display Name" }
1160
- ]
1161
- },
1162
- edit: {
1163
- title: "Edit User",
1164
- fields: [
1165
- { name: "email", type: "email", label: "Email", columns: 12 },
1166
- { name: "display_name", type: "text", label: "Display Name", columns: 12 },
1167
- { name: "phone_number", type: "text", label: "Phone number", columns: 12 },
1168
- { type: "collection", name: "org", label: "Organization", Collection: GroupList, labelField: "name", valueField: "id", columns: 12 }
1169
- ]
1170
- },
1171
- permissions: {
1172
- title: "Edit Permissions",
1173
- fields: User.PERMISSION_FIELDS
1174
- }
1175
- };
1176
- const UserDataView = {
1177
- // Basic user profile view
1178
- profile: {
1179
- title: "User Profile",
1180
- columns: 2,
1181
- fields: [
1182
- {
1183
- name: "id",
1184
- label: "User ID",
1185
- type: "number",
1186
- columns: 4
1187
- },
1188
- {
1189
- name: "last_login",
1190
- label: "Last Login",
1191
- type: "datetime",
1192
- format: "relative",
1193
- columns: 4
1194
- },
1195
- {
1196
- name: "last_activity",
1197
- label: "Last Activity",
1198
- type: "datetime",
1199
- format: "relative",
1200
- columns: 4
1201
- },
1202
- {
1203
- name: "username",
1204
- label: "Username",
1205
- type: "text",
1206
- format: "lowercase",
1207
- columns: 4
1208
- },
1209
- {
1210
- name: "display_name",
1211
- label: "Display Name",
1212
- type: "text",
1213
- columns: 4
1214
- },
1215
- {
1216
- name: "email",
1217
- label: "Email",
1218
- type: "email",
1219
- columns: 12
1220
- },
1221
- {
1222
- name: "org.name",
1223
- label: "Organization",
1224
- type: "text",
1225
- columns: 6
1226
- },
1227
- {
1228
- name: "phone_number",
1229
- label: "Phone Number",
1230
- type: "text",
1231
- columns: 6
1232
- }
1233
- ]
1234
- },
1235
- // Activity tracking view
1236
- activity: {
1237
- title: "User Activity",
1238
- columns: 2,
1239
- fields: [
1240
- {
1241
- name: "last_login",
1242
- label: "Last Login",
1243
- type: "datetime",
1244
- format: "relative",
1245
- colSize: 6
1246
- },
1247
- {
1248
- name: "last_activity",
1249
- label: "Last Activity",
1250
- type: "datetime",
1251
- format: "relative",
1252
- colSize: 6
1253
- }
1254
- ]
1255
- },
1256
- // Comprehensive view with all data
1257
- detailed: {
1258
- title: "Detailed User Information",
1259
- columns: 2,
1260
- showEmptyValues: true,
1261
- emptyValueText: "Not set",
1262
- fields: [
1263
- // Basic Info Section
1264
- {
1265
- name: "id",
1266
- label: "User ID",
1267
- type: "number",
1268
- colSize: 3
1269
- },
1270
- {
1271
- name: "display_name",
1272
- label: "Display Name",
1273
- type: "text",
1274
- format: 'capitalize|default("Unnamed User")',
1275
- colSize: 9
1276
- },
1277
- {
1278
- name: "username",
1279
- label: "Username",
1280
- type: "text",
1281
- format: "lowercase",
1282
- colSize: 6
1283
- },
1284
- {
1285
- name: "email",
1286
- label: "Email Address",
1287
- type: "email",
1288
- colSize: 6
1289
- },
1290
- {
1291
- name: "phone_number",
1292
- label: "Phone Number",
1293
- type: "phone",
1294
- format: 'phone|default("Not provided")',
1295
- colSize: 6
1296
- },
1297
- {
1298
- name: "is_active",
1299
- label: "Account Status",
1300
- type: "boolean",
1301
- colSize: 6
1302
- },
1303
- // Activity Info
1304
- {
1305
- name: "last_login",
1306
- label: "Last Login",
1307
- type: "datetime",
1308
- format: "relative",
1309
- colSize: 6
1310
- },
1311
- {
1312
- name: "last_activity",
1313
- label: "Last Activity",
1314
- type: "datetime",
1315
- format: "relative",
1316
- colSize: 6
1317
- },
1318
- // Avatar Info
1319
- {
1320
- name: "avatar.url",
1321
- label: "Avatar",
1322
- type: "url",
1323
- colSize: 12
1324
- },
1325
- // Complex Data (will use full width automatically)
1326
- {
1327
- name: "permissions",
1328
- label: "User Permissions",
1329
- type: "dataview",
1330
- dataViewColumns: 2,
1331
- showEmptyValues: false
1332
- },
1333
- {
1334
- name: "metadata",
1335
- label: "User Metadata",
1336
- type: "dataview",
1337
- dataViewColumns: 1
1338
- },
1339
- {
1340
- name: "avatar",
1341
- label: "Avatar Details",
1342
- type: "dataview",
1343
- dataViewColumns: 1
1344
- }
1345
- ]
1346
- },
1347
- // Permissions-focused view
1348
- permissions: {
1349
- title: "User Permissions",
1350
- columns: 1,
1351
- fields: [
1352
- {
1353
- name: "display_name",
1354
- label: "User",
1355
- type: "text",
1356
- format: "capitalize",
1357
- columns: 12
1358
- },
1359
- {
1360
- name: "permissions",
1361
- label: "Assigned Permissions",
1362
- type: "dataview",
1363
- dataViewColumns: 3,
1364
- showEmptyValues: false,
1365
- colSize: 12
1366
- }
1367
- ]
1368
- },
1369
- // Compact summary view
1370
- summary: {
1371
- title: "User Summary",
1372
- columns: 3,
1373
- fields: [
1374
- {
1375
- name: "display_name",
1376
- label: "Name",
1377
- type: "text",
1378
- format: "capitalize|truncate(30)"
1379
- },
1380
- {
1381
- name: "email",
1382
- label: "Email",
1383
- type: "email"
1384
- },
1385
- {
1386
- name: "is_active",
1387
- label: "Status",
1388
- type: "boolean"
1389
- },
1390
- {
1391
- name: "last_activity",
1392
- label: "Last Seen",
1393
- type: "datetime",
1394
- format: "relative",
1395
- colSize: 12
1396
- }
1397
- ]
1398
- }
1399
- };
1400
- User.DATA_VIEW = UserDataView.detailed;
1401
- User.EDIT_FORM = UserForms.edit;
1402
- User.ADD_FORM = UserForms.create;
1403
- class UserDevice extends Model {
1404
- constructor(data = {}) {
1405
- super(data, {
1406
- endpoint: "/api/user/device"
1407
- });
1408
- }
1409
- static async getByDuid(duid) {
1410
- const model = new UserDevice();
1411
- const resp = await model.rest.GET("/api/user/device/lookup", { duid });
1412
- if (resp.success && resp.data && resp.data.data) {
1413
- return new UserDevice(resp.data.data);
1414
- }
1415
- return null;
1416
- }
1417
- }
1418
- class UserDeviceList extends Collection {
1419
- constructor(options = {}) {
1420
- super({
1421
- ModelClass: UserDevice,
1422
- endpoint: "/api/user/device",
1423
- ...options
1424
- });
1425
- }
1426
- }
1427
- class UserDeviceLocation extends Model {
1428
- constructor(data = {}) {
1429
- super(data, {
1430
- endpoint: "/api/user/device/location"
1431
- });
1432
- }
1433
- }
1434
- class UserDeviceLocationList extends Collection {
1435
- constructor(options = {}) {
1436
- super({
1437
- ModelClass: UserDeviceLocation,
1438
- endpoint: "/api/user/device/location",
1439
- ...options
1440
- });
1441
- }
1442
- }
1443
- class ContextMenu extends View {
1444
- constructor(options = {}) {
1445
- super({
1446
- tagName: "div",
1447
- className: "context-menu-view dropdown",
1448
- ...options
1449
- });
1450
- this.config = options.contextMenu || options.config || {};
1451
- this.context = options.context || {};
1452
- }
1453
- /**
1454
- * Build the dropdown menu HTML from the configuration.
1455
- */
1456
- async renderTemplate() {
1457
- const menuItems = this.config.items || [];
1458
- if (menuItems.length === 0) {
1459
- return "";
1460
- }
1461
- const triggerIcon = this.config.icon || "bi-three-dots-horizontal";
1462
- const buttonClass = this.config.buttonClass || "btn btn-link text-secondary ps-3 pe-0 pt-0 pb-1";
1463
- const dropdownId = `context-menu-${this.id}`;
1464
- const menuItemsHtml = menuItems.map((item) => this.buildMenuItemHTML(item)).join("");
1465
- return `
1466
- <button class="${buttonClass}" type="button" id="${dropdownId}" data-bs-toggle="dropdown" aria-expanded="false">
1467
- <i class="${triggerIcon}"></i>
1468
- </button>
1469
- <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="${dropdownId}">
1470
- ${menuItemsHtml}
1471
- </ul>
1472
- `;
1473
- }
1474
- /**
1475
- * Build the HTML for a single menu item.
1476
- * @param {object} item - The menu item configuration.
1477
- * @returns {string} The HTML string for the list item.
1478
- */
1479
- buildMenuItemHTML(item) {
1480
- if (item.type === "divider" || item.separator) {
1481
- return '<li><hr class="dropdown-divider"></li>';
1482
- }
1483
- const icon = item.icon ? `<i class="${item.icon} me-2"></i>` : "";
1484
- const label = item.label || "";
1485
- const itemClass = `dropdown-item ${item.danger ? "text-danger" : ""} ${item.disabled ? "disabled" : ""}`;
1486
- const action = item.action || "";
1487
- if (item.href) {
1488
- return `<li><a class="${itemClass}" href="${item.href}" target="${item.target || "_self"}">${icon}${label}</a></li>`;
1489
- }
1490
- return `<li><a class="${itemClass}" href="#" data-action="menu-item-click" data-item-action="${action}">${icon}${label}</a></li>`;
1491
- }
1492
- /**
1493
- * Handle clicks on menu items.
1494
- * @param {Event} event - The click event.
1495
- * @param {HTMLElement} element - The clicked anchor element.
1496
- */
1497
- async onActionMenuItemClick(event, element) {
1498
- event.preventDefault();
1499
- const action = element.getAttribute("data-item-action");
1500
- if (!action) return;
1501
- const menuItem = this.config.items.find((item) => item.action === action);
1502
- if (!menuItem || menuItem.disabled) return;
1503
- if (typeof menuItem.handler === "function") {
1504
- menuItem.handler(this.context, event, element);
1505
- } else {
1506
- this.parent.events.dispatch(action, event, element);
1507
- }
1508
- this.closeDropdown();
1509
- }
1510
- closeDropdown() {
1511
- const dropdownTrigger = this.element.querySelector('[data-bs-toggle="dropdown"]');
1512
- if (dropdownTrigger) {
1513
- const dropdownInstance = window.bootstrap?.Dropdown.getInstance(dropdownTrigger);
1514
- dropdownInstance?.hide();
1515
- }
1516
- }
1517
- }
1518
- export {
1519
- ContextMenu as C,
1520
- GroupList as G,
1521
- Page as P,
1522
- ToastService as T,
1523
- User as U,
1524
- Group as a,
1525
- GroupForms as b,
1526
- UserList as c,
1527
- UserForms as d,
1528
- UserDataView as e,
1529
- UserDevice as f,
1530
- UserDeviceList as g,
1531
- UserDeviceLocation as h,
1532
- UserDeviceLocationList as i,
1533
- Group$1 as j
1534
- };
1535
- //# sourceMappingURL=ContextMenu-sgvgSACY.js.map