web-mojo 2.2.69 → 2.2.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin.cjs.js +1 -1
- package/dist/admin.cjs.js.map +1 -1
- package/dist/admin.es.js +1 -1
- package/dist/admin.es.js.map +1 -1
- package/dist/auth.cjs.js +1 -1
- package/dist/auth.es.js +1 -1
- package/dist/charts.cjs.js +1 -1
- package/dist/charts.cjs.js.map +1 -1
- package/dist/charts.es.js +1 -1
- package/dist/charts.es.js.map +1 -1
- package/dist/chunks/ChatView-DH42WXgV.js +2 -0
- package/dist/chunks/{ChatView-Dw-iVmht.js.map → ChatView-DH42WXgV.js.map} +1 -1
- package/dist/chunks/ChatView-_8eQTETQ.js +2 -0
- package/dist/chunks/{ChatView-CZ3Key2k.js.map → ChatView-_8eQTETQ.js.map} +1 -1
- package/dist/chunks/Collection-BUv4E9op.js +2 -0
- package/dist/chunks/Collection-BUv4E9op.js.map +1 -0
- package/dist/chunks/Collection-r1ACzUeh.js +2 -0
- package/dist/chunks/Collection-r1ACzUeh.js.map +1 -0
- package/dist/chunks/ContextMenu-BFxliZ03.js +2 -0
- package/dist/chunks/{ContextMenu-8vTiZZQV.js.map → ContextMenu-BFxliZ03.js.map} +1 -1
- package/dist/chunks/ContextMenu-BwJJ4QJE.js +2 -0
- package/dist/chunks/{ContextMenu-DBw0WMTO.js.map → ContextMenu-BwJJ4QJE.js.map} +1 -1
- package/dist/chunks/DataView-DMpNXerv.js +2 -0
- package/dist/chunks/{DataView-DyJKgOn3.js.map → DataView-DMpNXerv.js.map} +1 -1
- package/dist/chunks/DataView-_CACqzRt.js +2 -0
- package/dist/chunks/{DataView-BEovBggn.js.map → DataView-_CACqzRt.js.map} +1 -1
- package/dist/chunks/Dialog-BVCCpLPw.js +3 -0
- package/dist/chunks/Dialog-BVCCpLPw.js.map +1 -0
- package/dist/chunks/Dialog-BYiynSW-.js +3 -0
- package/dist/chunks/Dialog-BYiynSW-.js.map +1 -0
- package/dist/chunks/FormView-Dw7HDwzy.js +3 -0
- package/dist/chunks/{FormView-BRHAIawp.js.map → FormView-Dw7HDwzy.js.map} +1 -1
- package/dist/chunks/FormView-OgrZ7x0z.js +3 -0
- package/dist/chunks/{FormView-B1CXO2t8.js.map → FormView-OgrZ7x0z.js.map} +1 -1
- package/dist/chunks/ListView-2M4I8KHF.js +2 -0
- package/dist/chunks/{ListView-CMZpwyyC.js.map → ListView-2M4I8KHF.js.map} +1 -1
- package/dist/chunks/ListView-B0QbqSPv.js +2 -0
- package/dist/chunks/{ListView-BLFFK_Ir.js.map → ListView-B0QbqSPv.js.map} +1 -1
- package/dist/chunks/MetricsCountryMapView-DDdDJQFA.js +2 -0
- package/dist/chunks/{MetricsCountryMapView-B0kWK-Js.js.map → MetricsCountryMapView-DDdDJQFA.js.map} +1 -1
- package/dist/chunks/MetricsCountryMapView-DIlezla0.js +2 -0
- package/dist/chunks/{MetricsCountryMapView-DuBKO7gz.js.map → MetricsCountryMapView-DIlezla0.js.map} +1 -1
- package/dist/chunks/MetricsMiniChartWidget-Dt2V0eXP.js +2 -0
- package/dist/chunks/{MetricsMiniChartWidget-Dg1e6EQJ.js.map → MetricsMiniChartWidget-Dt2V0eXP.js.map} +1 -1
- package/dist/chunks/MetricsMiniChartWidget-_N4kzNY_.js +2 -0
- package/dist/chunks/{MetricsMiniChartWidget-D1w608Jy.js.map → MetricsMiniChartWidget-_N4kzNY_.js.map} +1 -1
- package/dist/chunks/PDFViewer-BruR1RFn.js +2 -0
- package/dist/chunks/{PDFViewer-D_3V8QJe.js.map → PDFViewer-BruR1RFn.js.map} +1 -1
- package/dist/chunks/PDFViewer-CyGFVcvX.js +2 -0
- package/dist/chunks/{PDFViewer-CDeV9OBs.js.map → PDFViewer-CyGFVcvX.js.map} +1 -1
- package/dist/chunks/TableView-CxYpxZvr.js +2 -0
- package/dist/chunks/{TableView-CI_7a-kD.js.map → TableView-CxYpxZvr.js.map} +1 -1
- package/dist/chunks/TableView-DemRVhnX.js +2 -0
- package/dist/chunks/{TableView-CWk5k4LQ.js.map → TableView-DemRVhnX.js.map} +1 -1
- package/dist/chunks/TokenManager-BFaxNsXO.js +2 -0
- package/dist/chunks/{TokenManager-sZgt--C9.js.map → TokenManager-BFaxNsXO.js.map} +1 -1
- package/dist/chunks/TokenManager-IlBEFXqZ.js +2 -0
- package/dist/chunks/{TokenManager-ien2XzwO.js.map → TokenManager-IlBEFXqZ.js.map} +1 -1
- package/dist/chunks/UserProfileView-9vkfCPsp.js +2 -0
- package/dist/chunks/{UserProfileView-kupeq2rN.js.map → UserProfileView-9vkfCPsp.js.map} +1 -1
- package/dist/chunks/UserProfileView-tcBT6XcE.js +2 -0
- package/dist/chunks/{UserProfileView-DnVMHcLH.js.map → UserProfileView-tcBT6XcE.js.map} +1 -1
- package/dist/chunks/WebApp-BFR1zozS.js +2 -0
- package/dist/chunks/{WebApp-CcVF73yg.js.map → WebApp-BFR1zozS.js.map} +1 -1
- package/dist/chunks/WebApp-C82womPC.js +2 -0
- package/dist/chunks/{WebApp-Bti0Gqqo.js.map → WebApp-C82womPC.js.map} +1 -1
- package/dist/chunks/WebSocketClient-Ibi7mLQu.js +2 -0
- package/dist/chunks/{WebSocketClient-Bh0Mmtje.js.map → WebSocketClient-Ibi7mLQu.js.map} +1 -1
- package/dist/chunks/WebSocketClient-QaCUN3EQ.js +2 -0
- package/dist/chunks/{WebSocketClient-CLgYPxWX.js.map → WebSocketClient-QaCUN3EQ.js.map} +1 -1
- package/dist/chunks/index-BaPQHxbL.js +2 -0
- package/dist/chunks/{index-Da9sT-tE.js.map → index-BaPQHxbL.js.map} +1 -1
- package/dist/chunks/index-BdfwxVMZ.js +2 -0
- package/dist/chunks/{index-Aq9ke4vg.js.map → index-BdfwxVMZ.js.map} +1 -1
- package/dist/chunks/{version-XmirKYWA.js → version-C2yYRyPn.js} +2 -2
- package/dist/chunks/{version-XmirKYWA.js.map → version-C2yYRyPn.js.map} +1 -1
- package/dist/chunks/{version-D8JjsPW0.js → version-CaiqhdME.js} +2 -2
- package/dist/chunks/{version-D8JjsPW0.js.map → version-CaiqhdME.js.map} +1 -1
- package/dist/docit.cjs.js +1 -1
- package/dist/docit.cjs.js.map +1 -1
- package/dist/docit.es.js +1 -1
- package/dist/docit.es.js.map +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +1 -1
- package/dist/index.es.js.map +1 -1
- package/dist/lightbox.cjs.js +1 -1
- package/dist/lightbox.cjs.js.map +1 -1
- package/dist/lightbox.es.js +1 -1
- package/dist/lightbox.es.js.map +1 -1
- package/dist/map.cjs.js +1 -1
- package/dist/map.cjs.js.map +1 -1
- package/dist/map.es.js +1 -1
- package/dist/map.es.js.map +1 -1
- package/dist/timeline.cjs.js +1 -1
- package/dist/timeline.cjs.js.map +1 -1
- package/dist/timeline.es.js +1 -1
- package/dist/timeline.es.js.map +1 -1
- package/dist/user-profile.cjs.js +1 -1
- package/dist/user-profile.es.js +1 -1
- package/dist/web-mojo.lite.iife.js +5435 -5435
- package/dist/web-mojo.lite.iife.js.map +1 -1
- package/dist/web-mojo.lite.iife.min.js +68 -68
- package/dist/web-mojo.lite.iife.min.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunks/ChatView-CZ3Key2k.js +0 -2
- package/dist/chunks/ChatView-Dw-iVmht.js +0 -2
- package/dist/chunks/Collection-BWKmydl5.js +0 -2
- package/dist/chunks/Collection-BWKmydl5.js.map +0 -1
- package/dist/chunks/Collection-CmjTsmrP.js +0 -2
- package/dist/chunks/Collection-CmjTsmrP.js.map +0 -1
- package/dist/chunks/ContextMenu-8vTiZZQV.js +0 -2
- package/dist/chunks/ContextMenu-DBw0WMTO.js +0 -2
- package/dist/chunks/DataView-BEovBggn.js +0 -2
- package/dist/chunks/DataView-DyJKgOn3.js +0 -2
- package/dist/chunks/Dialog-Dhqtd9Yz.js +0 -2
- package/dist/chunks/Dialog-Dhqtd9Yz.js.map +0 -1
- package/dist/chunks/Dialog-t_9l2Mou.js +0 -2
- package/dist/chunks/Dialog-t_9l2Mou.js.map +0 -1
- package/dist/chunks/Files-6eRT5k3r.js +0 -2
- package/dist/chunks/Files-6eRT5k3r.js.map +0 -1
- package/dist/chunks/Files-Dh_5PFBn.js +0 -2
- package/dist/chunks/Files-Dh_5PFBn.js.map +0 -1
- package/dist/chunks/FormView-B1CXO2t8.js +0 -3
- package/dist/chunks/FormView-BRHAIawp.js +0 -3
- package/dist/chunks/ListView-BLFFK_Ir.js +0 -2
- package/dist/chunks/ListView-CMZpwyyC.js +0 -2
- package/dist/chunks/MetricsCountryMapView-B0kWK-Js.js +0 -2
- package/dist/chunks/MetricsCountryMapView-DuBKO7gz.js +0 -2
- package/dist/chunks/MetricsMiniChartWidget-D1w608Jy.js +0 -2
- package/dist/chunks/MetricsMiniChartWidget-Dg1e6EQJ.js +0 -2
- package/dist/chunks/PDFViewer-CDeV9OBs.js +0 -2
- package/dist/chunks/PDFViewer-D_3V8QJe.js +0 -2
- package/dist/chunks/Rest-B1eUyLX5.js +0 -2
- package/dist/chunks/Rest-B1eUyLX5.js.map +0 -1
- package/dist/chunks/Rest-BJ3Mvx1L.js +0 -2
- package/dist/chunks/Rest-BJ3Mvx1L.js.map +0 -1
- package/dist/chunks/TableView-CI_7a-kD.js +0 -2
- package/dist/chunks/TableView-CWk5k4LQ.js +0 -2
- package/dist/chunks/ToastService-C2tTooFn.js +0 -3
- package/dist/chunks/ToastService-C2tTooFn.js.map +0 -1
- package/dist/chunks/ToastService-nUaGVpSl.js +0 -3
- package/dist/chunks/ToastService-nUaGVpSl.js.map +0 -1
- package/dist/chunks/TokenManager-ien2XzwO.js +0 -2
- package/dist/chunks/TokenManager-sZgt--C9.js +0 -2
- package/dist/chunks/User-BL9M_PWB.js +0 -2
- package/dist/chunks/User-BL9M_PWB.js.map +0 -1
- package/dist/chunks/User-DqHG5Gr1.js +0 -2
- package/dist/chunks/User-DqHG5Gr1.js.map +0 -1
- package/dist/chunks/UserProfileView-DnVMHcLH.js +0 -2
- package/dist/chunks/UserProfileView-kupeq2rN.js +0 -2
- package/dist/chunks/WebApp-Bti0Gqqo.js +0 -2
- package/dist/chunks/WebApp-CcVF73yg.js +0 -2
- package/dist/chunks/WebSocketClient-Bh0Mmtje.js +0 -2
- package/dist/chunks/WebSocketClient-CLgYPxWX.js +0 -2
- package/dist/chunks/index-Aq9ke4vg.js +0 -2
- package/dist/chunks/index-Da9sT-tE.js +0 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Dialog-BYiynSW-.js","sources":["../../src/core/services/ToastService.js","../../src/core/views/feedback/ProgressView.js","../../src/core/services/FileUpload.js","../../src/core/models/Group.js","../../src/core/models/User.js","../../src/core/models/Files.js","../../src/core/views/feedback/Dialog.js"],"sourcesContent":["/**\n * ToastService - Bootstrap 5 Toast Notification Service\n * Provides methods to display toast notifications with different types and options\n *\n * Features:\n * - Bootstrap 5 toast integration\n * - Multiple toast types (success, error, info, warning)\n * - Auto-dismiss with customizable delays\n * - Toast container management\n * - Event integration\n * - Proper cleanup and memory management\n *\n * @example\n * const toastService = new ToastService();\n * toastService.success('Operation completed successfully!');\n * toastService.error('Something went wrong');\n * toastService.info('FYI: This is informational');\n * toastService.warning('Please be careful');\n */\n\nclass ToastService {\n constructor(options = {}) {\n this.options = {\n containerId: 'toast-container',\n position: 'top-end', // top-start, top-center, top-end, middle-start, etc.\n autohide: true,\n defaultDelay: 5000, // 5 seconds\n maxToasts: 5, // Maximum number of toasts to show at once\n ...options\n };\n\n this.toasts = new Map(); // Track active toasts\n this.toastCounter = 0; // For unique IDs\n\n this.init();\n }\n\n /**\n * Initialize the toast service\n */\n init() {\n this.createContainer();\n }\n\n /**\n * Create the toast container if it doesn't exist\n */\n createContainer() {\n let container = document.getElementById(this.options.containerId);\n\n if (!container) {\n container = document.createElement('div');\n container.id = this.options.containerId;\n container.className = `toast-container position-fixed ${this.getPositionClasses()}`;\n container.style.zIndex = '1070'; // Bootstrap toast z-index\n container.setAttribute('aria-live', 'polite');\n container.setAttribute('aria-atomic', 'true');\n\n document.body.appendChild(container);\n }\n\n this.container = container;\n }\n\n /**\n * Get CSS classes for toast positioning\n */\n getPositionClasses() {\n const positionMap = {\n 'top-start': 'top-0 start-0 p-3',\n 'top-center': 'top-0 start-50 translate-middle-x p-3',\n 'top-end': 'top-0 end-0 p-3',\n 'middle-start': 'top-50 start-0 translate-middle-y p-3',\n 'middle-center': 'top-50 start-50 translate-middle p-3',\n 'middle-end': 'top-50 end-0 translate-middle-y p-3',\n 'bottom-start': 'bottom-0 start-0 p-3',\n 'bottom-center': 'bottom-0 start-50 translate-middle-x p-3',\n 'bottom-end': 'bottom-0 end-0 p-3'\n };\n\n return positionMap[this.options.position] || positionMap['top-end'];\n }\n\n\n\n /**\n * Show a success toast\n * @param {string} message - The message to display\n * @param {object} options - Additional options\n */\n success(message, options = {}) {\n return this.show(message, 'success', {\n icon: 'bi-check-circle-fill',\n ...options\n });\n }\n\n /**\n * Show an error toast\n * @param {string} message - The message to display\n * @param {object} options - Additional options\n */\n error(message, options = {}) {\n return this.show(message, 'error', {\n icon: 'bi-exclamation-triangle-fill',\n autohide: true, // Keep error toasts visible until manually dismissed\n ...options\n });\n }\n\n /**\n * Show an info toast\n * @param {string} message - The message to display\n * @param {object} options - Additional options\n */\n info(message, options = {}) {\n return this.show(message, 'info', {\n icon: 'bi-info-circle-fill',\n ...options\n });\n }\n\n /**\n * Show a warning toast\n * @param {string} message - The message to display\n * @param {object} options - Additional options\n */\n warning(message, options = {}) {\n return this.show(message, 'warning', {\n icon: 'bi-exclamation-triangle-fill',\n ...options\n });\n }\n\n /**\n * Show a plain toast without specific styling\n * @param {string} message - The message to display\n * @param {object} options - Additional options\n */\n plain(message, options = {}) {\n return this.show(message, 'plain', {\n ...options\n });\n }\n\n /**\n * Show a toast with specified type and options\n * @param {string} message - The message to display\n * @param {string} type - Toast type (success, error, info, warning)\n * @param {object} options - Additional options\n */\n show(message, type = 'info', options = {}) {\n // Enforce max toasts limit\n this.enforceMaxToasts();\n\n const toastId = `toast-${++this.toastCounter}`;\n const config = {\n title: this.getDefaultTitle(type),\n icon: this.getDefaultIcon(type),\n autohide: this.options.autohide,\n delay: this.options.defaultDelay,\n dismissible: true,\n ...options\n };\n\n const toastElement = this.createToastElement(toastId, message, type, config);\n this.container.appendChild(toastElement);\n\n // Initialize Bootstrap toast\n if (typeof bootstrap === 'undefined') {\n throw new Error('Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.');\n }\n const bsToast = new bootstrap.Toast(toastElement, {\n autohide: config.autohide,\n delay: config.delay\n });\n\n // Store toast reference\n this.toasts.set(toastId, {\n element: toastElement,\n bootstrap: bsToast,\n type: type,\n message: message\n });\n\n // Setup cleanup on hide\n toastElement.addEventListener('hidden.bs.toast', () => {\n this.cleanup(toastId);\n });\n\n // Show the toast\n bsToast.show();\n\n return {\n id: toastId,\n hide: () => {\n try {\n bsToast.hide();\n } catch (error) {\n console.warn('Error hiding toast:', error);\n }\n },\n dispose: () => this.cleanup(toastId),\n updateProgress: options.updateProgress || null\n };\n }\n\n /**\n * Show a toast with a View component in the body\n * @param {View} view - The View component to display\n * @param {string} type - Toast type (success, error, info, warning, plain)\n * @param {object} options - Additional options\n */\n showView(view, type = 'info', options = {}) {\n // Enforce max toasts limit\n this.enforceMaxToasts();\n\n const toastId = `toast-${++this.toastCounter}`;\n const config = {\n title: options.title || this.getDefaultTitle(type),\n icon: options.icon || this.getDefaultIcon(type),\n autohide: this.options.autohide,\n delay: this.options.defaultDelay,\n dismissible: true,\n ...options\n };\n\n const toastElement = this.createViewToastElement(toastId, view, type, config);\n this.container.appendChild(toastElement);\n\n // Initialize Bootstrap toast\n if (typeof bootstrap === 'undefined') {\n throw new Error('Bootstrap is required for ToastService. Make sure Bootstrap 5 is loaded.');\n }\n const bsToast = new bootstrap.Toast(toastElement, {\n autohide: config.autohide,\n delay: config.delay\n });\n\n // Store toast reference with view\n this.toasts.set(toastId, {\n element: toastElement,\n bootstrap: bsToast,\n type: type,\n view: view,\n message: 'View toast'\n });\n\n // Setup cleanup on hide - dispose view properly\n toastElement.addEventListener('hidden.bs.toast', () => {\n this.cleanupView(toastId);\n });\n\n // Mount and render the view\n const bodyContainer = toastElement.querySelector('.toast-view-body');\n if (bodyContainer && view) {\n view.render(true, bodyContainer);\n }\n\n // Show the toast\n bsToast.show();\n\n return {\n id: toastId,\n view: view,\n hide: () => {\n try {\n bsToast.hide();\n } catch (error) {\n console.warn('Error hiding view toast:', error);\n }\n },\n dispose: () => this.cleanupView(toastId),\n updateProgress: (progressInfo) => {\n if (view && typeof view.updateProgress === 'function') {\n view.updateProgress(progressInfo);\n }\n }\n };\n }\n\n /**\n * Create toast DOM element\n */\n createToastElement(id, message, type, config) {\n const toast = document.createElement('div');\n toast.id = id;\n toast.className = `toast toast-service-${type}`;\n toast.setAttribute('role', 'alert');\n toast.setAttribute('aria-live', 'assertive');\n toast.setAttribute('aria-atomic', 'true');\n\n const header = config.title || config.icon ? this.createToastHeader(config, type) : '';\n const body = this.createToastBody(message, config.icon && !config.title);\n\n toast.innerHTML = `\n ${header}\n ${body}\n `;\n\n return toast;\n }\n\n /**\n * Create toast DOM element for View component\n */\n createViewToastElement(id, view, type, config) {\n const toast = document.createElement('div');\n toast.id = id;\n toast.className = `toast toast-service-${type}`;\n toast.setAttribute('role', 'alert');\n toast.setAttribute('aria-live', 'assertive');\n toast.setAttribute('aria-atomic', 'true');\n\n const header = config.title || config.icon ? this.createToastHeader(config, type) : '';\n const body = this.createViewToastBody();\n\n toast.innerHTML = `\n ${header}\n ${body}\n `;\n\n return toast;\n }\n\n /**\n * Create toast body for View component\n */\n createViewToastBody() {\n return `\n <div class=\"toast-body p-0\">\n <div class=\"toast-view-body p-3\"></div>\n </div>\n `;\n }\n\n /**\n * Create toast header with title and icon\n */\n createToastHeader(config, _type) {\n const iconHtml = config.icon ?\n `<i class=\"${config.icon} toast-service-icon me-2\"></i>` : '';\n\n const titleHtml = config.title ?\n `<strong class=\"me-auto\">${iconHtml}${this.escapeHtml(config.title)}</strong>` : '';\n\n const timeHtml = config.showTime ?\n `<small class=\"text-muted\">${this.getTimeString()}</small>` : '';\n\n const closeButton = config.dismissible ?\n `<button type=\"button\" class=\"btn-close toast-service-close\" data-bs-dismiss=\"toast\" aria-label=\"Close\"></button>` : '';\n\n if (!titleHtml && !timeHtml && !closeButton) {\n return '';\n }\n\n return `\n <div class=\"toast-header\">\n ${titleHtml}\n ${timeHtml}\n ${closeButton}\n </div>\n `;\n }\n\n /**\n * Create toast body with message\n */\n createToastBody(message, showIcon = false) {\n const iconHtml = showIcon ?\n `<i class=\"${this.getDefaultIcon('info')} toast-service-icon me-2\"></i>` : '';\n\n return `\n <div class=\"toast-body d-flex align-items-center\">\n ${iconHtml}\n <span>${this.escapeHtml(message)}</span>\n </div>\n `;\n }\n\n /**\n * Get default title for toast type\n */\n getDefaultTitle(type) {\n const titles = {\n success: 'Success',\n error: 'Error',\n warning: 'Warning',\n info: 'Information',\n plain: ''\n };\n return titles[type] || 'Notification';\n }\n\n /**\n * Get default icon for toast type\n */\n getDefaultIcon(type) {\n const icons = {\n success: 'bi-check-circle-fill',\n error: 'bi-exclamation-triangle-fill',\n warning: 'bi-exclamation-triangle-fill',\n info: 'bi-info-circle-fill',\n plain: ''\n };\n return icons[type] || 'bi-info-circle-fill';\n }\n\n /**\n * Enforce maximum number of toasts\n */\n enforceMaxToasts() {\n const activeToasts = Array.from(this.toasts.values());\n\n if (activeToasts.length >= this.options.maxToasts) {\n // Remove oldest toast\n const oldestId = this.toasts.keys().next().value;\n const oldest = this.toasts.get(oldestId);\n\n if (oldest) {\n oldest.bootstrap.hide();\n }\n }\n }\n\n /**\n * Clean up toast resources\n */\n cleanup(toastId) {\n const toast = this.toasts.get(toastId);\n\n if (toast) {\n // Dispose Bootstrap toast\n try {\n toast.bootstrap.dispose();\n } catch (e) {\n console.warn('Error disposing toast:', e);\n }\n\n // Remove from DOM\n if (toast.element && toast.element.parentNode) {\n toast.element.parentNode.removeChild(toast.element);\n }\n\n // Remove from tracking\n this.toasts.delete(toastId);\n }\n }\n\n /**\n * Clean up view toast resources with proper view disposal\n */\n cleanupView(toastId) {\n const toast = this.toasts.get(toastId);\n\n if (toast) {\n // Dispose view first if it exists\n if (toast.view && typeof toast.view.dispose === 'function') {\n try {\n toast.view.dispose();\n } catch (e) {\n console.warn('Error disposing view in toast:', e);\n }\n }\n\n // Dispose Bootstrap toast\n try {\n toast.bootstrap.dispose();\n } catch (e) {\n console.warn('Error disposing toast:', e);\n }\n\n // Remove from DOM\n if (toast.element && toast.element.parentNode) {\n toast.element.parentNode.removeChild(toast.element);\n }\n\n // Remove from tracking\n this.toasts.delete(toastId);\n }\n }\n\n /**\n * Hide all active toasts\n */\n hideAll() {\n this.toasts.forEach((toast, _id) => {\n toast.bootstrap.hide();\n });\n }\n\n /**\n * Clear all toasts immediately\n */\n clearAll() {\n this.toasts.forEach((toast, id) => {\n this.cleanup(id);\n });\n }\n\n /**\n * Get current time string\n */\n getTimeString() {\n return new Date().toLocaleTimeString([], {\n hour: '2-digit',\n minute: '2-digit'\n });\n }\n\n /**\n * Escape HTML to prevent XSS\n */\n escapeHtml(str) {\n const div = document.createElement('div');\n div.textContent = str;\n return div.innerHTML;\n }\n\n /**\n * Dispose of the entire toast service\n */\n dispose() {\n this.clearAll();\n\n if (this.container && this.container.parentNode) {\n this.container.parentNode.removeChild(this.container);\n }\n }\n\n /**\n * Get statistics about active toasts\n */\n getStats() {\n const stats = {\n total: this.toasts.size,\n byType: {}\n };\n\n this.toasts.forEach(toast => {\n stats.byType[toast.type] = (stats.byType[toast.type] || 0) + 1;\n });\n\n return stats;\n }\n\n /**\n * Set global options\n */\n setOptions(newOptions) {\n this.options = { ...this.options, ...newOptions };\n\n // Recreate container if position changed\n if (newOptions.position) {\n if (this.container) {\n this.container.className = `toast-container position-fixed ${this.getPositionClasses()}`;\n }\n }\n }\n}\n\nexport default ToastService;\n","/**\n * ProgressView - File upload progress component\n * \n * Shows upload progress with progress bar, filename, and cancellation option\n * Integrates with FileUpload service for real-time progress updates\n * \n * Features:\n * - Bootstrap progress bar with percentage\n * - File information (name, size)\n * - Bytes uploaded/total display\n * - Cancel button with confirmation\n * - Responsive design\n * \n * Events:\n * - 'cancel' - Emitted when user cancels upload\n * \n * @example\n * const progressView = new ProgressView({\n * filename: 'document.pdf',\n * filesize: 1024000,\n * onCancel: () => fileUpload.cancel()\n * });\n * \n * // Update progress\n * progressView.updateProgress({ progress: 0.5, loaded: 512000, total: 1024000 });\n */\n\nimport View from '@core/View.js';\nimport dataFormatter from '@core/utils/DataFormatter.js';\n\nclass ProgressView extends View {\n constructor(options = {}) {\n super({\n template: 'progress-view-template',\n ...options\n });\n\n // Initialize progress data\n this.filename = options.filename || 'Unknown file';\n this.filesize = options.filesize || 0;\n this.filesizeFormatted = dataFormatter.pipe(this.filesize, 'filesize');\n \n // Progress state\n this.progress = 0;\n this.percentage = 0;\n this.loaded = 0;\n this.total = this.filesize;\n this.loadedFormatted = '0 B';\n this.totalFormatted = this.filesizeFormatted;\n this.status = 'Starting upload...';\n \n // Options\n this.showCancel = options.showCancel !== false;\n this.onCancel = options.onCancel || null;\n \n // State\n this.cancelled = false;\n this.completed = false;\n }\n\n /**\n * Get template for the progress view\n */\n getTemplate() {\n return `\n <div class=\"progress-view\">\n <div class=\"d-flex justify-content-between align-items-start mb-2\">\n <div class=\"flex-grow-1 min-width-0\">\n <div class=\"fw-medium text-truncate\" title=\"{{filename}}\">\n <i class=\"bi bi-file-earmark me-1\"></i>\n {{filename}}\n </div>\n <small class=\"text-muted\">{{status}}</small>\n </div>\n {{#showCancel}}\n <button type=\"button\" \n class=\"btn btn-sm btn-outline-secondary ms-2\" \n data-action=\"cancel\"\n {{#cancelled}}disabled{{/cancelled}}>\n <i class=\"bi bi-x\"></i>\n </button>\n {{/showCancel}}\n </div>\n \n <div class=\"progress mb-2\" style=\"height: 8px;\">\n <div class=\"progress-bar\" \n role=\"progressbar\" \n style=\"width: {{percentage}}%\"\n aria-valuenow=\"{{percentage}}\" \n aria-valuemin=\"0\" \n aria-valuemax=\"100\">\n </div>\n </div>\n \n <div class=\"d-flex justify-content-between\">\n <small class=\"text-muted\">\n {{loadedFormatted}} / {{totalFormatted}}\n </small>\n <small class=\"text-muted\">\n {{percentage}}%\n </small>\n </div>\n </div>\n `;\n }\n\n /**\n * Update progress information\n * @param {Object} progressInfo - Progress data\n * @param {number} progressInfo.progress - Progress as decimal (0-1)\n * @param {number} progressInfo.loaded - Bytes loaded\n * @param {number} progressInfo.total - Total bytes\n * @param {number} progressInfo.percentage - Progress as percentage (0-100)\n */\n updateProgress(progressInfo) {\n if (this.cancelled || this.completed) {\n return;\n }\n\n this.progress = progressInfo.progress;\n this.percentage = progressInfo.percentage;\n this.loaded = progressInfo.loaded;\n this.total = progressInfo.total || this.filesize;\n \n // Format bytes for display\n this.loadedFormatted = dataFormatter.pipe(this.loaded, 'filesize');\n this.totalFormatted = dataFormatter.pipe(this.total, 'filesize');\n \n // Update status message\n if (this.percentage < 100) {\n this.status = `Uploading... ${this.percentage}%`;\n } else {\n this.status = 'Finalizing upload...';\n }\n\n // Re-render to show updated progress\n this.render();\n }\n\n /**\n * Mark upload as completed\n * @param {string} message - Success message\n */\n markCompleted(message = 'Upload completed!') {\n this.completed = true;\n this.progress = 1;\n this.percentage = 100;\n this.status = message;\n this.render();\n }\n\n /**\n * Mark upload as failed\n * @param {string} message - Error message\n */\n markFailed(message = 'Upload failed') {\n this.status = message;\n this.render();\n }\n\n /**\n * Mark upload as cancelled\n */\n markCancelled() {\n this.cancelled = true;\n this.status = 'Upload cancelled';\n this.render();\n }\n\n /**\n * Handle cancel button click\n * @param {string} action - Action name\n * @param {Event} event - Click event\n * @param {Element} element - Button element\n */\n async onActionCancel(action, event, element) {\n if (this.cancelled || this.completed) {\n return;\n }\n\n // Disable button immediately\n element.disabled = true;\n \n // Mark as cancelled\n this.markCancelled();\n \n // Emit cancel event\n this.emit('cancel');\n \n // Call cancel callback if provided\n if (typeof this.onCancel === 'function') {\n try {\n await this.onCancel();\n } catch (error) {\n console.error('Error in cancel callback:', error);\n }\n }\n }\n\n /**\n * Set filename\n * @param {string} filename - New filename\n */\n setFilename(filename) {\n this.filename = filename;\n this.render();\n }\n\n /**\n * Set file size\n * @param {number} size - File size in bytes\n */\n setFilesize(size) {\n this.filesize = size;\n this.filesizeFormatted = dataFormatter.pipe(size, 'filesize');\n this.total = size;\n this.totalFormatted = this.filesizeFormatted;\n this.render();\n }\n\n /**\n * Get current progress as percentage\n * @returns {number} Progress percentage (0-100)\n */\n getPercentage() {\n return this.percentage;\n }\n\n /**\n * Check if upload is completed\n * @returns {boolean} True if completed\n */\n isCompleted() {\n return this.completed;\n }\n\n /**\n * Check if upload is cancelled\n * @returns {boolean} True if cancelled\n */\n isCancelled() {\n return this.cancelled;\n }\n\n /**\n * Get upload statistics\n * @returns {Object} Upload stats\n */\n getStats() {\n return {\n filename: this.filename,\n filesize: this.filesize,\n progress: this.progress,\n percentage: this.percentage,\n loaded: this.loaded,\n total: this.total,\n cancelled: this.cancelled,\n completed: this.completed,\n status: this.status\n };\n }\n}\n\nexport default ProgressView;","/**\n * FileUpload - File upload service with progress tracking and UI integration\n *\n * Features:\n * - Auto-start upload process\n * - Progress tracking with detailed information\n * - Promise interface with cancellation support\n * - Toast integration for user feedback\n * - Three-stage upload process (initiate → upload → complete)\n *\n * @example\n * const file = new File();\n * const upload = file.upload({\n * file: fileObject,\n * name: 'avatar.jpg',\n * group: 'profile-pics',\n * description: 'User avatar',\n * onProgress: ({ progress, loaded, total, percentage }) => {\n * console.log(`${percentage}% complete`);\n * }\n * });\n *\n * upload.then(result => console.log('Success!'))\n * .catch(error => console.error('Failed:', error));\n */\n\nimport ToastService from './ToastService.js';\nimport ProgressView from '../views/feedback/ProgressView.js';\n\nclass FileUpload {\n constructor(fileModel, options = {}) {\n this.fileModel = fileModel;\n this.options = {\n file: null,\n name: null,\n group: null,\n description: null,\n onProgress: null,\n onComplete: null,\n onError: null,\n showToast: true,\n ...options\n };\n\n // Validation\n if (!this.options.file || !(this.options.file instanceof File)) {\n throw new Error('FileUpload requires a valid File object');\n }\n\n // State management\n this.cancelled = false;\n this.uploadRequest = null;\n this.progressToast = null;\n this.progressView = null;\n this.toastService = null;\n\n // Initialize toast service if needed\n if (this.options.showToast) {\n this.toastService = new ToastService();\n }\n\n // Auto-start upload (Option 3 behavior)\n this.promise = this._startUpload();\n }\n\n /**\n * Main upload orchestration\n * @returns {Promise} Upload promise\n * @private\n */\n async _startUpload() {\n try {\n if (this.options.showToast) {\n this._showProgressToast();\n }\n\n // Stage 1: Initiate upload and get signed URL\n let uploadData;\n try {\n uploadData = await this._initiateUpload();\n } catch (error) {\n throw new Error(`Failed to initiate upload: ${error.message}`);\n }\n\n if (this.cancelled) {\n throw new Error('Upload cancelled');\n }\n\n // Validate upload data\n if (!uploadData || !uploadData.upload_url) {\n throw new Error('Invalid upload response: missing upload URL');\n }\n\n // Normalise upload_url — the backend can return either:\n // • a plain string → direct PUT with raw bytes (e.g. S3 signed URL)\n // • a config object → POST multipart/form-data (e.g. filesystem backend)\n // { upload_url: string, method: string, fields: object, headers: object }\n let uploadConfig;\n if (typeof uploadData.upload_url === 'string') {\n uploadConfig = {\n url: uploadData.upload_url,\n method: 'PUT',\n fields: null,\n headers: {}\n };\n } else if (uploadData.upload_url && typeof uploadData.upload_url === 'object'\n && uploadData.upload_url.upload_url) {\n uploadConfig = {\n url: uploadData.upload_url.upload_url,\n method: uploadData.upload_url.method || 'POST',\n fields: uploadData.upload_url.fields || null,\n headers: uploadData.upload_url.headers || {}\n };\n } else {\n throw new Error(\n `Invalid upload response: unrecognised upload_url format. ` +\n `Server returned: ${JSON.stringify(uploadData.upload_url)}`\n );\n }\n\n // Stage 2: Upload file to signed URL / endpoint\n let result;\n try {\n result = await this._performUpload(uploadConfig);\n } catch (error) {\n throw new Error(`File upload failed: ${error.message}`);\n }\n\n if (this.cancelled) {\n throw new Error('Upload cancelled');\n }\n\n // Stage 3: Mark upload as completed\n try {\n await this._completeUpload();\n } catch (error) {\n console.warn('Failed to mark upload as completed:', error);\n // Don't fail the entire upload for completion marking errors\n // The file was successfully uploaded\n }\n\n // Handle success\n this._onComplete(this.fileModel);\n return this.fileModel;\n\n } catch (error) {\n if (error.message !== 'Upload cancelled') {\n this._onError(error);\n }\n throw error;\n }\n }\n\n /**\n * Initiate upload by calling the API to get signed URL\n * @returns {Promise<Object>} Upload initiation data\n * @private\n */\n async _initiateUpload() {\n try {\n const payload = {\n filename: this.options.name || this.options.file.name,\n file_size: this.options.file.size,\n content_type: this.options.file.type,\n };\n\n if (this.options.group) payload.group = this.options.group;\n if (this.options.description) payload.description = this.options.description;\n\n const response = await this.fileModel.rest.POST('/api/fileman/upload/initiate', payload);\n\n if (!response) {\n throw new Error('No response from upload initiation API');\n }\n\n if (!response.data) {\n throw new Error('Upload initiation response missing data');\n }\n\n // Check server response first (prefer server error messages)\n if (!response.data.status) {\n const errorMessage = response.data.error || 'Upload initiation failed';\n throw new Error(errorMessage);\n }\n\n if (!response.data.data) {\n throw new Error('Upload initiation response missing data payload');\n }\n\n // Set model ID for completion step\n if (response.data.data.id) {\n this.fileModel.set('id', response.data.data.id);\n }\n\n return response.data.data; // { id, upload_url }\n\n } catch (error) {\n // Re-throw with more context if it's a generic error\n if (error.message === 'Network Error' || error.name === 'TypeError') {\n throw new Error('Network error during upload initiation. Please check your connection.');\n }\n throw error;\n }\n }\n\n /**\n * Upload file using the normalised upload config from _startUpload.\n *\n * Two dispatch paths:\n * - PUT + raw bytes → legacy plain-string upload_url, or any config with method PUT.\n * Used by backends that issue a direct signed PUT URL (e.g. S3 signed URL).\n * - POST + FormData → config object with method POST and optional fields dict.\n * Used by the local filesystem backend and S3 presigned POST.\n * fields are appended first, then the file as the \"file\" key,\n * matching Django's request.FILES['file'] convention.\n * Content-Type is intentionally NOT set manually so the browser\n * can write the correct multipart boundary.\n *\n * @param {{ url: string, method: string, fields: object|null, headers: object }} uploadConfig\n * @returns {Promise} Upload result\n * @private\n */\n async _performUpload(uploadConfig) {\n return new Promise((resolve, reject) => {\n if (!(this.options.file instanceof File)) {\n reject(new Error('Only single File objects are supported'));\n return;\n }\n\n const { url, method, fields, headers } = uploadConfig;\n const useFormData = method === 'POST' && fields !== null;\n\n const xhr = new XMLHttpRequest();\n this.uploadRequest = xhr;\n\n // Progress tracking\n xhr.upload.onprogress = (event) => {\n if (this.cancelled) return;\n this._onProgress({\n progress: event.loaded / event.total,\n loaded: event.loaded,\n total: event.total,\n percentage: Math.round((event.loaded / event.total) * 100)\n });\n };\n\n xhr.onload = () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve({ data: xhr.response, status: xhr.status, statusText: xhr.statusText, xhr });\n } else {\n reject(new Error(`Upload failed: ${xhr.status} ${xhr.statusText}`));\n }\n };\n\n xhr.onerror = () => reject(new Error('Upload failed: Network error'));\n xhr.ontimeout = () => reject(new Error('Upload timed out — file may be too large or connection too slow'));\n xhr.onabort = () => reject(new Error('Upload cancelled'));\n\n // Relative URLs must be prefixed with /api/ so Django's URL router picks them\n // up correctly, then resolved through the REST service so they target the\n // configured API host rather than the browser's current web host.\n // Absolute URLs (http/https) are returned unchanged — needed for S3 etc.\n let apiUrl = url;\n if (url.startsWith('/') && !url.startsWith('/api/')) {\n apiUrl = '/api' + url;\n }\n const resolvedUrl = this.fileModel.rest.buildUrl(apiUrl);\n xhr.open(method, resolvedUrl);\n xhr.timeout = 30000;\n\n if (useFormData) {\n // POST multipart/form-data — filesystem backend and S3 presigned POST.\n // Any extra headers from the config (e.g. x-amz-* for S3) are set here,\n // but Content-Type is deliberately skipped so the browser sets the boundary.\n for (const [key, value] of Object.entries(headers || {})) {\n if (key.toLowerCase() !== 'content-type') {\n xhr.setRequestHeader(key, value);\n }\n }\n\n const formData = new FormData();\n for (const [key, value] of Object.entries(fields)) {\n formData.append(key, value);\n }\n // File must be last — required by S3 presigned POST policy; harmless for Django.\n formData.append('file', this.options.file);\n xhr.send(formData);\n\n } else {\n // PUT raw bytes — legacy / direct signed URL path.\n xhr.setRequestHeader('Content-Type', this.options.file.type);\n for (const [key, value] of Object.entries(headers || {})) {\n if (key.toLowerCase() !== 'content-type') {\n xhr.setRequestHeader(key, value);\n }\n }\n xhr.send(this.options.file);\n }\n });\n }\n\n /**\n * Mark upload as completed in the API\n * @returns {Promise} Completion result\n * @private\n */\n async _completeUpload() {\n try {\n const response = await this.fileModel.save({ action: 'mark_as_completed' });\n\n if (!response) {\n throw new Error('No response from upload completion API');\n }\n\n // Check server response format (prefer server errors over HTTP errors)\n if (response.data && !response.data.status) {\n const errorMessage = response.data.error || 'Failed to mark upload as completed';\n throw new Error(errorMessage);\n }\n\n return response;\n } catch (error) {\n // Re-throw with more context\n if (error.message === 'Network Error' || error.name === 'TypeError') {\n throw new Error('Network error during upload completion. The file may have uploaded successfully.');\n }\n throw error;\n }\n }\n\n /**\n * Handle progress updates\n * @param {Object} progressInfo - Progress information\n * @private\n */\n _onProgress(progressInfo) {\n // Update progress toast if shown\n if (this.progressToast && this.progressToast.updateProgress) {\n this.progressToast.updateProgress(progressInfo);\n }\n\n // Call user-provided progress callback\n if (typeof this.options.onProgress === 'function') {\n this.options.onProgress(progressInfo);\n }\n }\n\n /**\n * Handle successful upload completion\n * @param {Object} result - Upload result\n * @private\n */\n _onComplete(result) {\n // Mark progress view as completed\n if (this.progressView) {\n this.progressView.markCompleted('Upload completed successfully!');\n }\n\n // Auto-hide progress toast after a delay\n if (this.progressToast) {\n setTimeout(() => {\n try {\n if (this.progressToast && typeof this.progressToast.hide === 'function') {\n this.progressToast.hide();\n }\n } catch (error) {\n console.warn('Error hiding progress toast:', error);\n }\n }, 2000);\n }\n\n // Call user-provided completion callback\n if (typeof this.options.onComplete === 'function') {\n this.options.onComplete(result);\n }\n }\n\n /**\n * Handle upload errors\n * @param {Error} error - Error object\n * @private\n */\n _onError(error) {\n // Hide progress toast immediately and show error toast\n if (this.progressToast) {\n try {\n this.progressToast.hide();\n } catch (error) {\n console.warn('Error hiding progress toast on error:', error);\n }\n }\n\n // Show error toast with immediate feedback\n if (this.toastService) {\n this.toastService.error(`Upload failed: ${error.message}`);\n }\n\n // Call user-provided error callback\n if (typeof this.options.onError === 'function') {\n this.options.onError(error);\n }\n }\n\n /**\n * Show progress toast with ProgressView component\n * @private\n */\n _showProgressToast() {\n // Create progress view with file information\n this.progressView = new ProgressView({\n filename: this.options.name || this.options.file.name,\n filesize: this.options.file.size,\n showCancel: true,\n onCancel: () => this.cancel()\n });\n\n // Show progress view in toast\n this.progressToast = this.toastService.showView(this.progressView, 'info', {\n title: 'File Upload',\n autohide: false,\n dismissible: false\n });\n }\n\n /**\n * Cancel the upload\n * @returns {boolean} True if cancelled, false if already completed\n */\n cancel() {\n if (this.cancelled) {\n return false;\n }\n\n this.cancelled = true;\n\n // Cancel the upload request if in progress\n if (this.uploadRequest && typeof this.uploadRequest.abort === 'function') {\n this.uploadRequest.abort();\n }\n\n // Mark progress view as cancelled\n if (this.progressView) {\n this.progressView.markCancelled();\n }\n\n // Hide progress toast after a delay\n if (this.progressToast) {\n setTimeout(() => {\n try {\n if (this.progressToast && typeof this.progressToast.hide === 'function') {\n this.progressToast.hide();\n }\n } catch (error) {\n console.warn('Error hiding progress toast on cancel:', error);\n }\n }, 1500);\n }\n\n return true;\n }\n\n /**\n * Check if upload is cancelled\n * @returns {boolean} True if cancelled\n */\n isCancelled() {\n return this.cancelled;\n }\n\n /**\n * Promise interface - then\n * @param {function} onSuccess - Success handler\n * @param {function} onError - Error handler\n * @returns {Promise} Promise chain\n */\n then(onSuccess, onError) {\n return this.promise.then(onSuccess, onError);\n }\n\n /**\n * Promise interface - catch\n * @param {function} onError - Error handler\n * @returns {Promise} Promise chain\n */\n catch(onError) {\n return this.promise.catch(onError);\n }\n\n /**\n * Promise interface - finally\n * @param {function} onFinally - Finally handler\n * @returns {Promise} Promise chain\n */\n finally(onFinally) {\n return this.promise.finally(onFinally);\n }\n\n /**\n * Get upload statistics\n * @returns {Object} Upload stats\n */\n getStats() {\n return {\n filename: this.options.file.name,\n size: this.options.file.size,\n type: this.options.file.type,\n cancelled: this.cancelled,\n group: this.options.group,\n description: this.options.description\n };\n }\n}\n\nexport default FileUpload;\n","\nimport Collection from '@core/Collection.js';\nimport Model from '@core/Model.js';\n\n/**\n * Group Model - Represents an organization, team, or group entity\n *\n * Features:\n * - Hierarchical group support (parent/child relationships)\n * - Member management\n * - Search and filtering capabilities\n * - Role-based permissions within groups\n * - Metadata and settings management\n */\nclass Group extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/group'\n });\n }\n}\n\n/**\n * GroupCollection - Enhanced collection for managing groups with advanced search and filtering\n */\nclass GroupList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: Group,\n endpoint: '/api/group',\n size: 10,\n ...options\n });\n }\n}\n\nconst GroupKinds = {\n 'org': 'Organization',\n 'division': 'Division',\n 'department': 'Department',\n 'team': 'Team',\n 'merchant': 'Merchant',\n 'partner': 'Partner',\n 'client': 'Client',\n 'iso': 'ISO',\n 'sales': 'Sales',\n 'reseller': 'Reseller',\n 'location': 'Location',\n 'region': 'Region',\n 'route': 'Route',\n 'project': 'Project',\n \"inventory\": \"Inventory\",\n 'test': 'Testing',\n 'misc': 'Miscellaneous',\n 'qa': 'Quality Assurance'\n};\n\n// Convert GroupKinds to select options\nconst GroupKindOptions = Object.entries(GroupKinds).map(([key, label]) => ({\n value: key,\n label: label\n}));\n\n/**\n * Form configurations for group management\n */\nconst GroupForms = {\n create: {\n title: 'Create Group',\n fields: [\n {\n name: 'name',\n type: 'text',\n label: 'Group Name',\n required: true,\n placeholder: 'Enter group name'\n },\n {\n name: 'kind',\n type: 'select',\n label: 'Group Kind',\n required: true,\n options: GroupKindOptions\n },\n {\n type: 'collection',\n name: 'parent',\n label: 'Parent Group',\n Collection: GroupList, // Collection class\n labelField: 'name', // Field to display in dropdown\n valueField: 'id', // Field to use as value\n maxItems: 10, // Max items to show in dropdown\n placeholder: 'Search groups...',\n emptyFetch: false,\n debounceMs: 300, // Search debounce delay\n }\n ]\n },\n\n edit: {\n title: 'Edit Group',\n fields: [\n {\n name: 'name',\n type: 'text',\n label: 'Group Name',\n required: true,\n placeholder: 'Enter group name',\n },\n {\n name: 'kind',\n type: 'select',\n label: 'Group Kind',\n required: true,\n options: GroupKindOptions\n },\n {\n type: 'collection',\n name: 'parent',\n label: 'Parent Group',\n Collection: GroupList, // Collection class\n labelField: 'name', // Field to display in dropdown\n valueField: 'id', // Field to use as value\n maxItems: 10, // Max items to show in dropdown\n placeholder: 'Search groups...',\n emptyFetch: false,\n debounceMs: 300, // Search debounce delay\n },\n {\n name: 'metadata.domain',\n type: 'text',\n label: 'Default Domain',\n placeholder: 'Enter Domain',\n },\n {\n name: 'metadata.portal',\n type: 'text',\n label: 'Default Portal',\n placeholder: 'Enter Portal URL',\n },\n {\n name: 'is_active',\n type: 'switch',\n label: 'Is Active',\n cols: 4\n },\n ]\n },\n\n detailed: {\n title: 'Group Details',\n fields: [\n // Profile Header\n {\n type: 'header',\n text: 'Profile Information',\n level: 4,\n class: 'text-primary mb-3'\n },\n\n // Avatar and Basic Info\n {\n type: 'group',\n columns: { xs: 12, md: 4 },\n fields: [\n {\n type: 'image',\n name: 'avatar',\n size: 'lg',\n imageSize: { width: 200, height: 200 },\n placeholder: 'Upload your avatar',\n help: 'Square images work best',\n columns: 12\n },\n {\n name: 'is_active',\n type: 'switch',\n label: 'Is Active',\n columns: 12\n },\n ]\n },\n\n // Profile Details\n {\n type: 'group',\n columns: { xs: 12, md: 8 },\n title: 'Details',\n fields: [\n {\n name: 'name',\n type: 'text',\n label: 'Group Name',\n required: true,\n placeholder: 'Enter group name',\n columns: 12\n },\n {\n name: 'kind',\n type: 'select',\n label: 'Group Kind',\n required: true,\n columns: 12,\n options: [\n { value: 'org', label: 'Organization' },\n { value: 'team', label: 'Team' },\n { value: 'department', label: 'Department' },\n { value: 'merchant', label: 'Merchant' },\n { value: 'iso', label: 'ISO' },\n { value: 'group', label: 'Group' }\n ]\n },\n {\n type: 'collection',\n name: 'parent',\n label: 'Parent Group',\n Collection: GroupList, // Collection class\n labelField: 'name', // Field to display in dropdown\n valueField: 'id', // Field to use as value\n maxItems: 10, // Max items to show in dropdown\n placeholder: 'Search groups...',\n emptyFetch: false,\n debounceMs: 300, // Search debounce delay\n columns: 12\n }\n ]\n },\n\n // Account Settings\n {\n type: 'group',\n columns: 12,\n title: 'Account Settings',\n class: \"pt-3\",\n fields: [\n {\n type: 'select',\n name: 'metadata.timezone',\n label: 'Timezone',\n columns: 6,\n value: 'America/Los_Angeles',\n options: [\n { value: 'America/New_York', text: 'Eastern Time' },\n { value: 'America/Chicago', text: 'Central Time' },\n { value: 'America/Denver', text: 'Mountain Time' },\n { value: 'America/Los_Angeles', text: 'Pacific Time' },\n { value: 'UTC', text: 'UTC' }\n ]\n },\n {\n type: 'select',\n name: 'metadata.eod_hour',\n label: 'End of Day Hour',\n columns: 6,\n options: [\n { value: 0, text: 'Midnight' },\n { value: 1, text: '1 AM' },\n { value: 2, text: '2 AM' },\n { value: 3, text: '3 AM' },\n { value: 4, text: '4 AM' },\n { value: 5, text: '5 AM' },\n { value: 6, text: '6 AM' },\n { value: 7, text: '7 AM' },\n { value: 8, text: '8 AM' },\n { value: 9, text: '9 AM' },\n { value: 10, text: '10 AM' },\n { value: 11, text: '11 AM' },\n { value: 12, text: '12 PM' },\n { value: 13, text: '1 PM' },\n { value: 14, text: '2 PM' },\n { value: 15, text: '3 PM' },\n { value: 16, text: '4 PM' },\n { value: 17, text: '5 PM' },\n { value: 18, text: '6 PM' },\n { value: 19, text: '7 PM' },\n { value: 20, text: '8 PM' },\n { value: 21, text: '9 PM' },\n { value: 22, text: '10 PM' },\n { value: 23, text: '11 PM' }\n ]\n }\n ]\n },\n {\n type: \"text\",\n label: \"Email Template (Prefix)\",\n name: \"metadata.email_template\",\n columns: 12\n }\n ]\n },\n};\n\nGroup.EDIT_FORM = GroupForms.edit;\nGroup.ADD_FORM = GroupForms.create;\nGroup.CREATE_FORM = GroupForms.create; // Alias for compatibility\nGroup.GroupKindOptions = GroupKindOptions;\nGroup.GroupKinds = GroupKinds;\nexport { Group, GroupList, GroupForms };\n","import Collection from '@core/Collection.js';\nimport Model from '@core/Model.js';\n\nimport { GroupList } from './Group.js';\n\nclass User extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/user'\n });\n }\n\n hasPermission(permission) {\n if (this.get(\"is_superuser\")) return true;\n if (Array.isArray(permission)) {\n return permission.some(p => this.hasPermission(p));\n }\n\n // Check if permission has \"sys.\" prefix\n const isSysPermission = permission.startsWith('sys.');\n const permissionToCheck = isSysPermission ? permission.substring(4) : permission;\n\n if (this._hasPermission(permissionToCheck)) {\n return true;\n }\n\n // Only check member permissions if it's not a system permission\n if (!isSysPermission && this.member && this.member.hasPermission(permission)) {\n return true;\n }\n\n return false;\n }\n\n _hasPermission(permission) {\n const permissions = this.get(\"permissions\");\n if (!permissions) {\n return false;\n }\n return permissions[permission] == true;\n }\n\n hasPerm(p) {\n return this.hasPermission(p);\n }\n}\n\nclass UserList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: User,\n endpoint: '/api/user',\n ...options\n });\n }\n}\n\nUser.PERMISSIONS = [\n { name: \"manage_users\", label: \"Manage Users\" },\n { name: \"view_users\", label: \"View Users\" },\n { name: \"view_groups\", label: \"View Groups\" },\n { name: \"manage_groups\", label: \"Manage Groups\" },\n { name: \"view_metrics\", label: \"View System Metrics\" },\n { name: \"manage_metrics\", label: \"Manage System Metrics\" },\n { name: \"view_logs\", label: \"View Logs\" },\n { name: \"view_incidents\", label: \"View Incidents\" },\n { name: \"manage_incidents\", label: \"Manage Incidents\" },\n { name: \"view_tickets\", label: \"View Tickets\" },\n { name: \"manage_tickets\", label: \"Manage Tickets\" },\n { name: \"view_admin\", label: \"View Admin\" },\n { name: \"view_jobs\", label: \"View Jobs\" },\n { name: \"manage_jobs\", label: \"Manage Jobs\" },\n { name: \"view_global\", label: \"View Global\" },\n { name: \"manage_notifications\", label: \"Manage Notifications\" },\n { name: \"manage_files\", label: \"Manage Files\" },\n { name: \"force_single_session\", label: \"Force Single Session\" },\n { name: \"file_vault\", label: \"Access File Vault\" },\n { name: \"manage_aws\", label: \"Manage AWS\" },\n { name: \"manage_docit\", label: \"Manage DocIt\" }\n];\n\n\nUser.PERMISSION_FIELDS = [\n ...User.PERMISSIONS.map(permission => ({\n name: `permissions.${permission.name}`,\n type: 'switch',\n label: permission.label,\n columns: 4\n }))\n];\n\nconst UserForms = {\n create: {\n title: 'Create User',\n fields: [\n { name: 'email', type: 'text', label: 'Email', required: true },\n { name: 'phone_number', type: 'text', label: 'Phone number', columns: 12 },\n { name: 'display_name', type: 'text', label: 'Display Name' }\n ]\n },\n edit: {\n title: 'Edit User',\n fields: [\n { name: 'email', type: 'email', label: 'Email', columns: 12 },\n { name: 'display_name', type: 'text', label: 'Display Name', columns: 12},\n { name: 'phone_number', type: 'text', label: 'Phone number', columns: 12 },\n { type: 'collection', name: 'org', label: 'Organization', Collection: GroupList, labelField: 'name', valueField: 'id', columns: 12 },\n ]\n },\n permissions: {\n title: 'Edit Permissions',\n fields: User.PERMISSION_FIELDS\n }\n};\n\n\n// DataView configuration for User model\nconst UserDataView = {\n // Basic user profile view\n profile: {\n title: 'User Profile',\n columns: 2,\n fields: [\n {\n name: 'id',\n label: 'User ID',\n type: 'number',\n columns: 4\n },\n {\n name: 'last_login',\n label: 'Last Login',\n type: 'datetime',\n format: 'relative',\n columns: 4\n },\n {\n name: 'last_activity',\n label: 'Last Activity',\n type: 'datetime',\n format: 'relative',\n columns: 4\n },\n {\n name: 'username',\n label: 'Username',\n type: 'text',\n format: 'lowercase',\n columns: 4\n },\n {\n name: 'display_name',\n label: 'Display Name',\n type: 'text',\n columns: 4\n },\n\n {\n name: 'email',\n label: 'Email',\n type: 'email',\n columns: 12\n },\n\n {\n name: 'org.name',\n label: 'Organization',\n type: 'text',\n columns: 6\n },\n {\n name: 'phone_number',\n label: 'Phone Number',\n type: 'text',\n columns: 6\n }\n ]\n },\n\n // Activity tracking view\n activity: {\n title: 'User Activity',\n columns: 2,\n fields: [\n {\n name: 'last_login',\n label: 'Last Login',\n type: 'datetime',\n format: 'relative',\n colSize: 6\n },\n {\n name: 'last_activity',\n label: 'Last Activity',\n type: 'datetime',\n format: 'relative',\n colSize: 6\n }\n ]\n },\n\n // Comprehensive view with all data\n detailed: {\n title: 'Detailed User Information',\n columns: 2,\n showEmptyValues: true,\n emptyValueText: 'Not set',\n fields: [\n // Basic Info Section\n {\n name: 'id',\n label: 'User ID',\n type: 'number',\n colSize: 3\n },\n {\n name: 'display_name',\n label: 'Display Name',\n type: 'text',\n format: 'capitalize|default(\"Unnamed User\")',\n colSize: 9\n },\n {\n name: 'username',\n label: 'Username',\n type: 'text',\n format: 'lowercase',\n colSize: 6\n },\n {\n name: 'email',\n label: 'Email Address',\n type: 'email',\n colSize: 6\n },\n {\n name: 'phone_number',\n label: 'Phone Number',\n type: 'phone',\n format: 'phone|default(\"Not provided\")',\n colSize: 6\n },\n {\n name: 'is_active',\n label: 'Account Status',\n type: 'boolean',\n colSize: 6\n },\n\n // Activity Info\n {\n name: 'last_login',\n label: 'Last Login',\n type: 'datetime',\n format: 'relative',\n colSize: 6\n },\n {\n name: 'last_activity',\n label: 'Last Activity',\n type: 'datetime',\n format: 'relative',\n colSize: 6\n },\n\n // Avatar Info\n {\n name: 'avatar.url',\n label: 'Avatar',\n type: 'url',\n colSize: 12\n },\n\n // Complex Data (will use full width automatically)\n {\n name: 'permissions',\n label: 'User Permissions',\n type: 'dataview',\n dataViewColumns: 2,\n showEmptyValues: false\n },\n {\n name: 'metadata',\n label: 'User Metadata',\n type: 'dataview',\n dataViewColumns: 1\n },\n {\n name: 'avatar',\n label: 'Avatar Details',\n type: 'dataview',\n dataViewColumns: 1\n }\n ]\n },\n\n // Permissions-focused view\n permissions: {\n title: 'User Permissions',\n columns: 1,\n fields: [\n {\n name: 'display_name',\n label: 'User',\n type: 'text',\n format: 'capitalize',\n columns: 12\n },\n {\n name: 'permissions',\n label: 'Assigned Permissions',\n type: 'dataview',\n dataViewColumns: 3,\n showEmptyValues: false,\n colSize: 12\n }\n ]\n },\n\n // Compact summary view\n summary: {\n title: 'User Summary',\n columns: 3,\n fields: [\n {\n name: 'display_name',\n label: 'Name',\n type: 'text',\n format: 'capitalize|truncate(30)'\n },\n {\n name: 'email',\n label: 'Email',\n type: 'email'\n },\n {\n name: 'is_active',\n label: 'Status',\n type: 'boolean'\n },\n {\n name: 'last_activity',\n label: 'Last Seen',\n type: 'datetime',\n format: 'relative',\n colSize: 12\n }\n ]\n }\n};\n\nUser.DATA_VIEW = UserDataView.detailed;\nUser.EDIT_FORM = UserForms.edit;\nUser.ADD_FORM = UserForms.create;\n\n/* =========================\n * UserDevice\n * ========================= */\nclass UserDevice extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/user/device',\n });\n }\n\n static async getByDuid(duid) {\n const model = new UserDevice();\n const resp = await model.rest.GET('/api/user/device/lookup', { duid: duid });\n if (resp.success && resp.data && resp.data.data) {\n // A direct lookup should return a single object\n return new UserDevice(resp.data.data);\n }\n return null;\n }\n}\n\nclass UserDeviceList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: UserDevice,\n endpoint: '/api/user/device',\n ...options,\n });\n }\n}\n\n/* =========================\n * UserDeviceLocation\n * ========================= */\nclass UserDeviceLocation extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/user/device/location',\n });\n }\n}\n\nclass UserDeviceLocationList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: UserDeviceLocation,\n endpoint: '/api/user/device/location',\n ...options,\n });\n }\n}\n\nexport { User, UserList, UserForms, UserDataView, UserDevice, UserDeviceList, UserDeviceLocation, UserDeviceLocationList };\n","\nimport Collection from '@core/Collection.js';\nimport Model from '@core/Model.js';\nimport FileUpload from '@core/services/FileUpload.js';\nimport {UserList} from '@core/models/User.js';\nimport {GroupList} from '@core/models/Group.js';\n/* =========================\n * FileManager\n * ========================= */\nclass FileManager extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/fileman/manager',\n });\n }\n}\n\nclass FileManagerList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: FileManager,\n endpoint: '/api/fileman/manager',\n size: 10,\n ...options,\n });\n }\n}\n\nconst FileManagerForms = {\n create: {\n title: 'Add Storage Backend',\n fields: [\n {\n name: 'name',\n type: 'text',\n label: 'Display Name',\n placeholder: 'Enter Display Name',\n cols: 12,\n },\n {\n name: 'use',\n type: 'text',\n label: 'Use',\n placeholder: 'Enter User or Leave Blank',\n cols: 12,\n },\n {\n name: 'backend_url',\n type: 'text',\n label: 'Backend URL',\n required: true,\n value: \"s3://BUCKET_NAME/OPTION_FOLDER\",\n placeholder: 's3://bucket_name/optional folder',\n help: 'Format: service://path. Valid services: s3',\n cols: 12,\n },\n {\n name: 'aws_region',\n type: 'select',\n label: 'AWS Region (optional)',\n value: 'us-east-1',\n options: [\n { value: '', text: 'System Default' },\n { value: 'us-east-1', text: 'US East (N. Virginia)' },\n { value: 'us-east-2', text: 'US East (Ohio)' },\n { value: 'us-west-1', text: 'US West (N. California)' },\n { value: 'us-west-2', text: 'US West (Oregon)' },\n { value: 'ca-central-1', text: 'Canada (Central)' },\n { value: 'eu-west-1', text: 'Europe (Ireland)' },\n { value: 'eu-west-2', text: 'Europe (London)' },\n { value: 'eu-west-3', text: 'Europe (Paris)' },\n { value: 'eu-central-1', text: 'Europe (Frankfurt)' },\n { value: 'eu-north-1', text: 'Europe (Stockholm)' },\n { value: 'eu-south-1', text: 'Europe (Milan)' },\n { value: 'ap-southeast-2', text: 'Asia Pacific (Sydney)' }\n ],\n columns: 12,\n help: 'Optional. Defaults to project AWS_REGION if omitted.'\n },\n {\n name: 'aws_key',\n type: 'text',\n label: 'AWS Key (optional)',\n placeholder: 'enter your AWS Key with S3 permissions',\n columns: 12,\n help: 'Optional, AWS Key with S3 permissions'\n },\n {\n name: 'aws_secret',\n type: 'text',\n label: 'AWS Secret (optional)',\n placeholder: 'enter your AWS Secret with S3 permissions',\n columns: 12,\n help: 'Optional, AWS Secret with S3 permissions'\n },\n {\n name: 'is_default',\n type: 'switch',\n label: 'Is Default',\n cols: 6,\n },\n {\n name: 'is_active',\n type: 'switch',\n label: 'Is Active',\n default: true,\n cols: 6,\n },\n ],\n },\n\n edit: {\n title: 'Edit Storage Backend',\n fields: [\n {\n name: 'name',\n type: 'text',\n label: 'Display Name',\n placeholder: 'Enter Display Name',\n cols: 12,\n },\n {\n name: 'use',\n type: 'text',\n label: 'Use',\n placeholder: 'Enter User or Leave Blank',\n cols: 12,\n },\n {\n name: 'backend_url',\n type: 'text',\n label: 'Backend URL',\n required: true,\n placeholder: 's3://bucket_name/optional folder',\n help: 'Format: service://path. Valid services: s3',\n cols: 12,\n },\n {\n name: 'allowed_origins',\n type: 'text',\n label: 'Domains Who Can Upload',\n cols: 12,\n },\n {\n name: 'is_default',\n type: 'switch',\n label: 'Is Default',\n cols: 6,\n },\n {\n name: 'is_active',\n type: 'switch',\n label: 'Is Active',\n default: true,\n cols: 6,\n },\n {\n name: 'is_public',\n type: 'switch',\n label: 'Is Public',\n default: true,\n cols: 6,\n }\n ],\n },\n\n owners: {\n fields: [\n {\n type: 'collection',\n name: 'group',\n label: 'Group (Owner)',\n Collection: GroupList, // Collection class\n labelField: 'name', // Field to display in dropdown\n valueField: 'id', // Field to use as value\n maxItems: 10, // Max items to show in dropdown\n placeholder: 'Search groups...',\n emptyFetch: false,\n debounceMs: 300, // Search debounce delay\n },\n {\n type: 'collection',\n name: 'user',\n label: 'User (Owner)',\n Collection: UserList, // Collection class\n labelField: 'display_name', // Field to display in dropdown\n valueField: 'id', // Field to use as value\n maxItems: 10, // Max items to show in dropdown\n placeholder: 'Search users...',\n emptyFetch: false,\n debounceMs: 300, // Search debounce delay\n },\n ],\n },\n\n credentials: {\n fields: [\n {\n name: 'aws_region',\n type: 'select',\n label: 'AWS Region (optional)',\n value: 'us-east-1',\n options: [\n { value: '', text: 'System Default' },\n { value: 'us-east-1', text: 'US East (N. Virginia)' },\n { value: 'us-east-2', text: 'US East (Ohio)' },\n { value: 'us-west-1', text: 'US West (N. California)' },\n { value: 'us-west-2', text: 'US West (Oregon)' },\n { value: 'ca-central-1', text: 'Canada (Central)' },\n { value: 'eu-west-1', text: 'Europe (Ireland)' },\n { value: 'eu-west-2', text: 'Europe (London)' },\n { value: 'eu-west-3', text: 'Europe (Paris)' },\n { value: 'eu-central-1', text: 'Europe (Frankfurt)' },\n { value: 'eu-north-1', text: 'Europe (Stockholm)' },\n { value: 'eu-south-1', text: 'Europe (Milan)' },\n { value: 'ap-southeast-2', text: 'Asia Pacific (Sydney)' }\n ],\n columns: 12,\n help: 'Optional. Defaults to project AWS_REGION if omitted.'\n },\n {\n name: 'aws_key',\n type: 'text',\n label: 'AWS Key (optional)',\n placeholder: 'enter your AWS Key with S3 permissions',\n columns: 12,\n help: 'Optional, AWS Key with S3 permissions'\n },\n {\n name: 'aws_secret',\n type: 'text',\n label: 'AWS Secret (optional)',\n placeholder: 'enter your AWS Secret with S3 permissions',\n columns: 12,\n help: 'Optional, AWS Secret with S3 permissions'\n },\n ]\n }\n};\n\n/* =========================\n * File\n * ========================= */\nclass File extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/fileman/file',\n });\n }\n\n isImage() {\n return this.get(\"category\") === 'image';\n }\n\n /**\n * Upload file with progress tracking and UI integration\n * Returns a FileUpload instance with promise interface and cancellation support\n *\n * @param {object} options - Upload configuration\n * @param {File} options.file - File object to upload\n * @param {string} options.name - Custom filename (optional)\n * @param {string} options.group - File group/category (optional)\n * @param {string} options.description - File description (optional)\n * @param {function} options.onProgress - Progress callback ({ progress, loaded, total, percentage })\n * @param {function} options.onComplete - Success callback\n * @param {function} options.onError - Error callback\n * @param {boolean} options.showToast - Show progress toast (default: true)\n * @returns {FileUpload} Upload instance with promise interface\n */\n upload(options = {}) {\n return new FileUpload(this, options);\n }\n}\n\nclass FileList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: File,\n endpoint: '/api/fileman/file',\n size: 10,\n ...options,\n });\n }\n}\n\nconst FileForms = {\n create: {\n title: 'Add File',\n fields: [\n\n ],\n },\n\n edit: {\n title: 'Edit File Backend',\n fields: [\n\n ],\n },\n};\n\nexport {\n FileManager,\n FileManagerList,\n FileManagerForms,\n File,\n FileList,\n FileForms,\n};\n","/**\n * Dialog - Complete Bootstrap 5 Modal component for MOJO framework\n * Supports all Bootstrap 5 modal features including sizes, fullscreen, scrollable, etc.\n * Can accept View instances as body content\n */\n\nimport View from '@core/View.js';\nimport { File as FileModel } from '@core/models/Files.js';\n\n\nclass Dialog extends View {\n static _openDialogs = [];\n static _baseZIndex = {\n backdrop: 1050,\n modal: 1055\n };\n\n /**\n * Check if there's a fullscreen table and return appropriate z-index\n */\n static getFullscreenAwareZIndex() {\n const fullscreenTable = document.querySelector('.table-fullscreen');\n if (fullscreenTable) {\n // Fullscreen table uses z-index 9999, so modals should be much higher\n return {\n backdrop: 10040,\n modal: 10050\n };\n }\n return this._baseZIndex;\n }\n\n static _busyIndicator = null;\n static _busyCounter = 0;\n static _busyTimeout = null;\n\n /**\n * Fix all backdrop stacking - ensures proper layering of all open modals\n */\n static fixAllBackdropStacking() {\n const backdrops = document.querySelectorAll('.modal-backdrop');\n const openDialogs = Dialog._openDialogs;\n\n if (backdrops.length === 0 || openDialogs.length === 0) return;\n\n // Sort dialogs by z-index to get correct stacking order\n const sortedDialogs = [...openDialogs].sort((a, b) =>\n (a._dialogZIndex || 0) - (b._dialogZIndex || 0)\n );\n\n // Set backdrop z-indices to create proper stacking\n // Each backdrop should cover all previous modals but stay below its own modal\n backdrops.forEach((backdrop, index) => {\n if (index < sortedDialogs.length) {\n const dialog = sortedDialogs[index];\n const backdropZIndex = dialog._dialogZIndex - 5;\n backdrop.style.zIndex = backdropZIndex;\n\n // Move backdrop to correct container\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n\n if (backdrop.parentNode !== targetContainer) {\n targetContainer.appendChild(backdrop);\n }\n }\n });\n }\n\n /**\n * Update backdrop stacking for all open dialogs\n */\n static updateAllBackdropStacking() {\n Dialog.fixAllBackdropStacking();\n }\n\n /**\n * Shows a full-screen busy indicator.\n * Manages a counter for nested calls, only showing one indicator.\n * @param {object} options - Options { timeout, message }\n */\n static showBusy(options = {}) {\n const { timeout = 30000, message = 'Loading...' } = options;\n\n this._busyCounter++;\n\n if (this._busyCounter === 1) {\n if (this._busyTimeout) {\n clearTimeout(this._busyTimeout);\n }\n\n if (!this._busyIndicator) {\n const zIndexBase = this.getFullscreenAwareZIndex();\n const busyZIndex = zIndexBase.modal + 1000; // Higher than any modal\n\n this._busyIndicator = document.createElement('div');\n this._busyIndicator.className = 'mojo-busy-indicator';\n this._busyIndicator.innerHTML = `\n <div class=\"mojo-busy-spinner\">\n <div class=\"spinner-border text-light\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n <p class=\"mojo-busy-message mt-3 text-light\">${message}</p>\n </div>\n <style>\n .mojo-busy-indicator {\n position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;\n background-color: rgba(0, 0, 0, 0.5); z-index: ${busyZIndex};\n display: flex; align-items: center; justify-content: center;\n opacity: 0; transition: opacity 0.15s linear;\n }\n .mojo-busy-indicator.show { opacity: 1; }\n .mojo-busy-spinner .spinner-border { width: 3rem; height: 3rem; }\n </style>\n `;\n document.body.appendChild(this._busyIndicator);\n }\n\n const msgElement = this._busyIndicator.querySelector('.mojo-busy-message');\n if (msgElement) msgElement.textContent = message;\n\n setTimeout(() => this._busyIndicator.classList.add('show'), 10);\n\n this._busyTimeout = setTimeout(() => {\n console.error('Busy indicator timed out.');\n this.hideBusy(true); // Force hide\n this.alert({\n title: 'Operation Timed Out',\n message: 'The operation took too long. Please check your connection and try again.',\n type: 'danger'\n });\n }, timeout);\n }\n }\n\n /**\n * Hides the full-screen busy indicator.\n * Decrements the counter and only hides when the counter reaches zero.\n * @param {boolean} force - If true, forces the indicator to hide immediately.\n */\n static hideBusy(force = false) {\n if (force) {\n this._busyCounter = 0;\n } else {\n this._busyCounter--;\n }\n\n if (this._busyCounter <= 0) {\n this._busyCounter = 0;\n if (this._busyTimeout) {\n clearTimeout(this._busyTimeout);\n this._busyTimeout = null;\n }\n\n if (this._busyIndicator) {\n this._busyIndicator.classList.remove('show');\n setTimeout(() => {\n if (this._busyIndicator && this._busyCounter === 0) {\n this._busyIndicator.remove();\n this._busyIndicator = null;\n }\n }, 150);\n }\n }\n }\n\n constructor(options = {}) {\n // Generate unique ID if not provided\n const modalId = options.id || `modal-${Date.now()}`;\n\n super({\n ...options,\n id: modalId, // Pass the ID to parent constructor\n tagName: 'div',\n className: `modal ${options.fade !== false ? 'fade' : ''} ${options.className || ''}`,\n attributes: {\n tabindex: '-1',\n 'aria-hidden': 'true',\n 'aria-labelledby': options.labelledBy || `${modalId}-label`,\n 'aria-describedby': options.describedBy || null,\n ...options.attributes\n }\n });\n\n // Store modal ID for internal use\n this.modalId = modalId;\n\n // Dialog configuration\n this.title = options.title || '';\n this.titleId = `${this.modalId}-label`;\n\n // Size options: sm, md (default), lg, xl, xxl, fullscreen, auto\n // Or responsive fullscreen: fullscreen-sm-down, fullscreen-md-down, etc.\n // 'auto' enables dynamic sizing based on content dimensions\n this.size = options.size || '';\n\n // Layout options\n this.centered = options.centered !== undefined ? options.centered : false;\n this.scrollable = options.scrollable !== undefined ? options.scrollable : false;\n // Auto-sizing: dynamically size modal based on content dimensions\n // Can be enabled with autoSize: true or size: 'auto'\n // Waits for modal animation to complete before measuring content\n this.autoSize = options.autoSize || options.size === 'auto'; // Auto-size modal based on content dimensions\n\n // Bootstrap modal options\n this.backdrop = options.backdrop !== undefined ? options.backdrop : true; // true, false, 'static'\n this.keyboard = options.keyboard !== undefined ? options.keyboard : true;\n this.focus = options.focus !== undefined ? options.focus : true;\n\n // Content\n this.header = options.header !== undefined ? options.header : true;\n this.headerContent = options.headerContent || null;\n this.headerView = null; // Will hold View instance if headerContent is a View\n this.closeButton = options.closeButton !== undefined ? options.closeButton : true;\n this.contextMenu = options.contextMenu || null;\n\n // Handle different header content types (support View instances)\n this._processHeaderContent(this.headerContent);\n\n // Enhanced body handling - support View, Promise<View>, or function returning View\n // Aliases supported (common mistakes): `message` and `view`\n // Priority: body > view > message > content\n // - `view` is intended to be a View instance (or function/promise returning one)\n // - `message` is intended to be a string/HTML snippet\n this.body = options.body ?? options.view ?? options.message ?? options.content ?? '';\n this.bodyView = null; // Will hold View instance if body is a View\n this.bodyClass = options.bodyClass || '';\n this.noBodyPadding = options.noBodyPadding || false; // Remove default modal-body padding\n\n // Auto-sizing constraints - only used when autoSize is enabled\n this.minWidth = options.minWidth || 300; // Minimum modal width (px)\n this.minHeight = options.minHeight || 200; // Minimum modal height (px)\n if (options.maxHeight) this.maxHeight = options.maxHeight;\n this.maxWidthPercent = options.maxWidthPercent || 0.9; // Max width as % of viewport\n this.maxHeightPercent = options.maxHeightPercent || 0.8; // Max height as % of viewport\n\n // Handle different body types\n this._processBodyContent(this.body);\n\n this.footer = options.footer || null;\n this.footerView = null; // Will hold View instance if footer is a View\n this.footerClass = options.footerClass || '';\n\n // Handle different footer types\n this._processFooterContent(this.footer);\n\n // Buttons configuration\n this.buttons = options.buttons || null;\n\n // Callbacks for Bootstrap events\n this.onShow = options.onShow || null; // show.bs.modal\n this.onShown = options.onShown || null; // shown.bs.modal\n this.onHide = options.onHide || null; // hide.bs.modal\n this.onHidden = options.onHidden || null; // hidden.bs.modal\n this.onHidePrevented = options.onHidePrevented || null; // hidePrevented.bs.modal\n\n // Auto show on creation\n this.autoShow = options.autoShow !== undefined ? options.autoShow : false;\n\n // Bootstrap modal instance\n this.modal = null;\n\n // Related target (button that triggered the modal)\n this.relatedTarget = options.relatedTarget || null;\n }\n\n /**\n * Process body content to detect and handle View instances\n */\n _processBodyContent(body) {\n // Prefer robust View detection (View instances should end up in `bodyView`)\n if (body instanceof View || (body && typeof body === 'object' && typeof body.render === 'function')) {\n this.bodyView = body;\n this.body = ''; // Clear string body\n this.addChild(this.bodyView); // Add as child for proper lifecycle\n } else if (typeof body === 'function') {\n // Support lazy View creation\n try {\n const result = body();\n if (result instanceof View) {\n this.bodyView = result;\n this.body = '';\n this.addChild(this.bodyView);\n } else if (result instanceof Promise) {\n // Mark for async processing\n this.bodyPromise = result;\n this.body = '<div class=\"text-center\"><div class=\"spinner-border spinner-border-sm\"></div></div>';\n } else {\n this.body = result;\n }\n } catch (error) {\n console.error('Error processing body function:', error);\n this.body = body;\n }\n } else {\n this.body = body;\n }\n }\n\n /**\n * Process header content to detect and handle View instances\n */\n _processHeaderContent(headerContent) {\n if (headerContent instanceof View) {\n this.headerView = headerContent;\n this.headerContent = null;\n this.addChild(this.headerView);\n } else if (typeof headerContent === 'function') {\n // Support lazy View creation\n try {\n const result = headerContent();\n if (result instanceof View) {\n this.headerView = result;\n this.headerContent = null;\n this.addChild(this.headerView);\n } else if (result instanceof Promise) {\n // Mark for async processing\n this.headerPromise = result;\n this.headerContent = '<div class=\"text-center\"><div class=\"spinner-border spinner-border-sm\"></div></div>';\n } else {\n this.headerContent = result;\n }\n } catch (error) {\n console.error('Error processing headerContent function:', error);\n this.headerContent = headerContent;\n }\n } else {\n this.headerContent = headerContent;\n }\n }\n\n /**\n * Process footer content to detect and handle View instances\n */\n _processFooterContent(footer) {\n if (footer instanceof View) {\n this.footerView = footer;\n this.footer = null;\n this.addChild(this.footerView);\n } else if (typeof footer === 'function') {\n // Support lazy View creation\n try {\n const result = footer();\n if (result instanceof View) {\n this.footerView = result;\n this.footer = null;\n this.addChild(this.footerView);\n } else if (result instanceof Promise) {\n // Mark for async processing\n this.footerPromise = result;\n this.footer = '<div class=\"text-center\"><div class=\"spinner-border spinner-border-sm\"></div></div>';\n } else {\n this.footer = result;\n }\n } catch (error) {\n console.error('Error processing footer function:', error);\n this.footer = footer;\n }\n } else {\n this.footer = footer;\n }\n }\n\n /**\n * Get dialog template with all Bootstrap 5 features\n */\n async getTemplate() {\n // Build dialog classes\n const dialogClasses = ['modal-dialog'];\n\n // Add size class (excluding 'auto' which uses default sizing)\n if (this.size && this.size !== 'auto') {\n if (this.size.startsWith('fullscreen')) {\n // Fullscreen or responsive fullscreen\n dialogClasses.push(`modal-${this.size}`);\n } else if (['sm', 'lg', 'xl', 'xxl'].includes(this.size)) {\n // Standard sizes\n dialogClasses.push(`modal-${this.size}`);\n // Auto fullscreen on small screens for large sizes\n if (['lg', 'xl', 'xxl'].includes(this.size)) {\n dialogClasses.push('modal-fullscreen-sm-down');\n }\n }\n }\n\n // Add centered class\n if (this.centered) {\n dialogClasses.push('modal-dialog-centered');\n }\n\n // Add scrollable class\n if (this.scrollable) {\n if (!this.maxHeight) {\n dialogClasses.push('modal-dialog-scrollable');\n } else {\n dialogClasses.push('overflow-hidden');\n }\n }\n\n return `\n <div class=\"${dialogClasses.join(' ')}\">\n <div class=\"modal-content\">\n ${await this.buildHeader()}\n ${await this.buildBody()}\n ${await this.buildFooter()}\n </div>\n </div>\n `;\n }\n\n /**\n * Build modal header\n */\n async buildHeader() {\n if (!this.header) {\n return '';\n }\n\n // If we have a View instance as header content\n if (this.headerView) {\n this.headerView.replaceById = true;\n return `<div class=\"modal-header\" data-view-container=\"header\">\n <!-- View will be mounted here -->\n <div id=\"${this.headerView.id}\"></div>\n </div>`;\n }\n\n if (this.headerContent) {\n return `<div class=\"modal-header\">${this.headerContent}</div>`;\n }\n\n // Build context menu or close button\n let headerActions = '';\n if (this.contextMenu && this.contextMenu.items && this.contextMenu.items.length > 0) {\n headerActions = await this.buildContextMenu();\n } else if (this.closeButton) {\n headerActions = '<button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-label=\"Close\"></button>';\n }\n\n return `\n <div class=\"modal-header\">\n ${this.title ? `<h5 class=\"modal-title\" id=\"${this.titleId}\">${this.title}</h5>` : ''}\n ${headerActions}\n </div>\n `;\n }\n\n async buildContextMenu() {\n const menuItems = await this.filterContextMenuItems();\n if (menuItems.length === 0) {\n // If no items pass permission checks, show regular close button\n return this.closeButton ? '<button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-label=\"Close\"></button>' : '';\n }\n\n const triggerIcon = this.contextMenu.icon || 'bi-three-dots-vertical';\n const buttonClass = this.contextMenu.buttonClass || 'btn btn-link p-1 mojo-modal-context-menu-btn';\n\n const menuItemsHtml = menuItems.map(item => {\n if (item.type === 'divider') {\n return '<li><hr class=\"dropdown-divider\"></li>';\n }\n\n const icon = item.icon ? `<i class=\"${item.icon} me-2\"></i>` : '';\n const label = item.label || '';\n\n if (item.href) {\n return `<li><a class=\"dropdown-item\" href=\"${item.href}\"${item.target ? ` target=\"${item.target}\"` : ''}>${icon}${label}</a></li>`;\n } else if (item.action) {\n const dataAttrs = Object.keys(item)\n .filter(key => key.startsWith('data-'))\n .map(key => `${key}=\"${item[key]}\"`)\n .join(' ');\n return `<li><a class=\"dropdown-item\" data-action=\"${item.action}\" ${dataAttrs}>${icon}${label}</a></li>`;\n }\n\n return '';\n }).join('');\n\n return `\n <div class=\"dropdown\">\n <button class=\"${buttonClass}\" type=\"button\" data-bs-toggle=\"dropdown\" aria-expanded=\"false\">\n <i class=\"${triggerIcon}\"></i>\n </button>\n <ul class=\"dropdown-menu dropdown-menu-end\">\n ${menuItemsHtml}\n </ul>\n </div>\n `;\n }\n\n async filterContextMenuItems() {\n if (!this.contextMenu || !this.contextMenu.items) {\n return [];\n }\n\n const filteredItems = [];\n\n for (const item of this.contextMenu.items) {\n // Always include dividers\n if (item.type === 'divider') {\n filteredItems.push(item);\n continue;\n }\n\n // Check permissions if specified\n if (item.permissions) {\n try {\n const app = this.getApp?.();\n let user = null;\n\n if (app) {\n user = app.activeUser || app.getState?.('activeUser');\n }\n\n // Also check window.getApp as fallback for mock systems\n if (!user && typeof window !== 'undefined' && window.getApp) {\n try {\n const globalApp = window.getApp();\n user = globalApp?.activeUser;\n } catch (e) {\n // Ignore global app errors\n }\n }\n\n if (user && user.hasPermission) {\n if (!user.hasPermission(item.permissions)) {\n continue; // Skip this item\n }\n } else {\n // If no permission system available, skip items with permission requirements\n continue;\n }\n } catch (error) {\n console.warn('Error checking permissions for context menu item:', error);\n continue;\n }\n }\n\n filteredItems.push(item);\n }\n\n return filteredItems;\n }\n\n /**\n * Build modal body\n */\n async buildBody() {\n // If we have a View instance as body\n if (this.bodyView) {\n this.bodyView.replaceById = true;\n const bodyClass = this.noBodyPadding ? `modal-body p-0 ${this.bodyClass}` : `modal-body ${this.bodyClass}`;\n return `<div class=\"${bodyClass}\" data-view-container=\"body\">\n <!-- View will be mounted here -->\n <div id=\"${this.bodyView.id}\"></div>\n </div>`;\n }\n\n // Regular string/HTML body\n if (!this.body && this.body !== '') {\n return '';\n }\n\n const bodyClass = this.noBodyPadding ? `modal-body p-0 ${this.bodyClass}` : `modal-body ${this.bodyClass}`;\n return `\n <div class=\"${bodyClass}\">\n ${this.body}\n </div>\n `;\n }\n\n /**\n * Build modal footer\n */\n async buildFooter() {\n // If we have a View instance as footer\n if (this.footerView) {\n return `<div class=\"modal-footer ${this.footerClass}\" data-view-container=\"footer\"></div>`;\n }\n\n // Custom footer content\n if (this.footer !== null && typeof this.footer === 'string') {\n return `<div class=\"modal-footer ${this.footerClass}\">${this.footer}</div>`;\n }\n\n // Build footer from buttons\n if (this.buttons && this.buttons.length > 0) {\n const buttonsHtml = this.buttons.map(btn => {\n const dismissAttr = btn.dismiss ? 'data-bs-dismiss=\"modal\"' : '';\n const actionAttr = btn.action ? `data-action=\"${btn.action}\"` : '';\n const idAttr = btn.id ? `id=\"${btn.id}\"` : '';\n const disabledAttr = btn.disabled ? 'disabled' : '';\n\n return `\n <button type=\"${btn.type || 'button'}\"\n class=\"btn ${btn.class || 'btn-secondary'}\"\n ${idAttr}\n ${dismissAttr}\n ${actionAttr}\n ${disabledAttr}>\n ${btn.icon ? `<i class=\"bi ${btn.icon} me-1\"></i>` : ''}\n ${btn.text || 'Button'}\n </button>\n `;\n }).join('');\n\n return `<div class=\"modal-footer ${this.footerClass}\">${buttonsHtml}</div>`;\n }\n\n // No footer\n return '';\n }\n\n\n /**\n * Override mount to not require a container for dialogs\n * Dialogs are appended to body or fullscreen element\n */\n async mount(_container = null) {\n if (this.mounted || this.destroyed) {\n return;\n }\n\n // For dialogs, we only need the element, not a container\n if (!this.element) {\n throw new Error('Cannot mount dialog without element');\n }\n\n // Call lifecycle hooks\n await this.onBeforeMount();\n\n // Append to fullscreen element if it exists, otherwise to body\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n targetContainer.appendChild(this.element);\n\n // Bind DOM events\n this.bindEvents();\n\n // Set mounted flag\n this.mounted = true;\n\n // Call after mount (this initializes Bootstrap modal)\n await this.onAfterMount();\n\n // Emit mounted event\n this.emit('mounted', { view: this });\n\n return this;\n }\n\n /**\n * After render - prepare for View instances and apply syntax highlighting\n */\n async onAfterRender() {\n await super.onAfterRender();\n\n // Apply Prism syntax highlighting if available and there are code blocks\n if (window.Prism && this.element) {\n const codeBlocks = this.element.querySelectorAll('pre code');\n if (codeBlocks.length > 0) {\n // Use Prism's highlightAllUnder to highlight code within this dialog\n window.Prism.highlightAllUnder(this.element);\n }\n }\n\n // Child views will be mounted in onAfterMount when element is in DOM\n\n // Apply auto-sizing after rendering if enabled\n if (this.autoSize) {\n this.setupAutoSizing();\n } else if (this.maxHeight) {\n const modalBody = this.element.querySelector('.modal-body');\n if (modalBody) {\n modalBody.style.maxHeight = `${this.maxHeight}px`;\n // modalBody.style.overflowY = 'auto';\n }\n }\n }\n\n /**\n * After mount - initialize Bootstrap modal and mount child views\n */\n async onAfterMount() {\n await super.onAfterMount();\n\n if (typeof window !== 'undefined' && window.bootstrap && window.bootstrap.Modal) {\n // Set data attributes if needed\n if (this.backdrop === 'static') {\n this.element.setAttribute('data-bs-backdrop', 'static');\n }\n if (!this.keyboard) {\n this.element.setAttribute('data-bs-keyboard', 'false');\n }\n\n // Initialize Bootstrap modal with options\n this.modal = new window.bootstrap.Modal(this.element, {\n backdrop: this.backdrop,\n keyboard: this.keyboard,\n focus: this.focus\n });\n\n // Bind Bootstrap events\n this.bindBootstrapEvents();\n\n // Auto show if requested\n if (this.autoShow) {\n this.show(this.relatedTarget);\n }\n }\n }\n\n /**\n * Setup auto-sizing - wait for modal animation to complete\n */\n setupAutoSizing() {\n if (!this.element) return;\n\n // Listen for modal shown event to apply sizing after animation\n this.element.addEventListener('shown.bs.modal', () => {\n this.applyAutoSizing();\n }, { once: true });\n\n // Fallback: apply immediately if modal is already shown or no animation\n setTimeout(() => {\n if (this.isShown()) {\n this.applyAutoSizing();\n }\n }, 100);\n }\n\n /**\n * Apply auto-sizing based on content dimensions\n */\n applyAutoSizing() {\n if (!this.element) return;\n\n try {\n const modalDialog = this.element.querySelector('.modal-dialog');\n const modalContent = this.element.querySelector('.modal-content');\n const modalBody = this.element.querySelector('.modal-body');\n\n if (!modalDialog || !modalContent || !modalBody) {\n console.warn('Dialog auto-sizing: Required elements not found');\n return;\n }\n\n // Wait for child views to fully render\n if (this.bodyView && !this.bodyView.element) {\n setTimeout(() => this.applyAutoSizing(), 50);\n return;\n }\n\n // Store original styles and classes for restoration\n const originalStyles = {\n dialogMaxWidth: modalDialog.style.maxWidth,\n dialogWidth: modalDialog.style.width,\n contentWidth: modalContent.style.width,\n contentMaxHeight: modalContent.style.maxHeight,\n hadScrollableClass: modalDialog.classList.contains('modal-dialog-scrollable')\n };\n\n // Temporarily remove size constraints to measure natural content size\n modalDialog.style.maxWidth = 'none';\n modalDialog.style.width = 'auto';\n modalContent.style.width = 'auto';\n modalContent.style.maxHeight = 'none';\n\n // Force layout recalculation\n modalContent.offsetHeight;\n\n // Measure content dimensions after forced layout\n const contentRect = modalContent.getBoundingClientRect();\n\n // Calculate viewport constraints with margins\n const viewportMargin = 40;\n const maxWidth = Math.min(\n window.innerWidth * this.maxWidthPercent,\n window.innerWidth - viewportMargin\n );\n let maxHeight = Math.min(\n window.innerHeight * this.maxHeightPercent,\n window.innerHeight - viewportMargin\n );\n\n // Calculate optimal dimensions with padding for content\n let optimalWidth = Math.max(this.minWidth, Math.ceil(contentRect.width + 20));\n let optimalHeight = Math.max(this.minHeight, Math.ceil(contentRect.height));\n if (this.maxHeight) {\n maxHeight = Math.min(this.maxHeight, maxHeight);\n modalDialog.style.maxHeight = `${maxHeight}px`;\n }\n // Apply viewport constraints\n optimalWidth = Math.min(optimalWidth, maxWidth);\n const heightExceedsMax = contentRect.height > maxHeight;\n\n // Apply the calculated size\n modalDialog.style.maxWidth = `${optimalWidth}px`;\n\n modalDialog.style.width = `${optimalWidth}px`;\n\n // Handle height overflow with scrolling - use Bootstrap's scrollable class\n if (heightExceedsMax) {\n // Add Bootstrap's modal-dialog-scrollable class for proper max-height behavior\n if (!modalDialog.classList.contains('modal-dialog-scrollable')) {\n modalDialog.classList.add('modal-dialog-scrollable');\n }\n modalContent.style.maxHeight = `${maxHeight}px`;\n optimalHeight = maxHeight;\n }\n\n // Store the applied dimensions\n this.autoSizedWidth = optimalWidth;\n this.autoSizedHeight = optimalHeight;\n this._originalStyles = originalStyles;\n\n } catch (error) {\n console.error('Error in dialog auto-sizing:', error);\n // Fallback: ensure modal is still usable\n this.element.querySelector('.modal-dialog').style.maxWidth = '';\n }\n }\n\n /**\n * Reset auto-sizing and restore original modal dimensions\n */\n resetAutoSizing() {\n if (!this.autoSize || !this._originalStyles || !this.element) return;\n\n try {\n const modalDialog = this.element.querySelector('.modal-dialog');\n const modalContent = this.element.querySelector('.modal-content');\n const modalBody = this.element.querySelector('.modal-body');\n\n if (modalDialog && modalContent && modalBody) {\n // Restore original styles\n modalDialog.style.maxWidth = this._originalStyles.dialogMaxWidth || '';\n modalDialog.style.width = this._originalStyles.dialogWidth || '';\n modalContent.style.width = this._originalStyles.contentWidth || '';\n modalContent.style.maxHeight = this._originalStyles.contentMaxHeight || '';\n\n // Restore scrollable class state\n if (!this._originalStyles.hadScrollableClass && modalDialog.classList.contains('modal-dialog-scrollable')) {\n modalDialog.classList.remove('modal-dialog-scrollable');\n }\n\n // Clear stored dimensions\n delete this.autoSizedWidth;\n delete this.autoSizedHeight;\n delete this._originalStyles;\n }\n } catch (error) {\n console.error('Error resetting dialog auto-sizing:', error);\n }\n }\n\n /**\n * Bind Bootstrap modal events\n */\n bindBootstrapEvents() {\n // show.bs.modal\n this.element.addEventListener('show.bs.modal', (e) => {\n // Manage stacking for multiple dialogs with fullscreen awareness\n const stackIndex = Dialog._openDialogs.length;\n const zIndexBase = Dialog.getFullscreenAwareZIndex();\n const newZIndex = zIndexBase.modal + (stackIndex * 20);\n this.element.style.zIndex = newZIndex;\n\n // Store z-index on this dialog for later reference\n this._dialogZIndex = newZIndex;\n this._backdropZIndex = newZIndex - 10; // Ensure backdrop covers previous modals\n\n Dialog._openDialogs.push(this);\n\n if (this.onShow) this.onShow(e);\n this.emit('show', {\n dialog: this,\n relatedTarget: e.relatedTarget\n });\n });\n\n // shown.bs.modal\n this.element.addEventListener('shown.bs.modal', (e) => {\n // Fix all backdrop stacking after Bootstrap has finished\n setTimeout(() => {\n Dialog.fixAllBackdropStacking();\n }, 50);\n\n if (this.onShown) this.onShown(e);\n this.emit('shown', {\n dialog: this,\n relatedTarget: e.relatedTarget\n });\n\n // Focus first input if exists\n if (this.focus) {\n const firstInput = this.element.querySelector('input:not([type=\"hidden\"]), textarea, select');\n if (firstInput) {\n firstInput.focus();\n }\n }\n });\n\n // hide.bs.modal\n this.element.addEventListener('hide.bs.modal', (e) => {\n // Blur any focused element inside the modal to prevent accessibility warning\n const focusedElement = this.element.querySelector(':focus');\n if (focusedElement) {\n focusedElement.blur();\n }\n\n if (this.onHide) {\n const result = this.onHide(e);\n if (result === false) {\n e.preventDefault();\n return;\n }\n }\n this.emit('hide', { dialog: this });\n });\n\n // hidden.bs.modal\n this.element.addEventListener('hidden.bs.modal', (e) => {\n // Manage stacking\n const index = Dialog._openDialogs.indexOf(this);\n if (index > -1) {\n Dialog._openDialogs.splice(index, 1);\n }\n\n // If there are still modals open, ensure body has modal-open class\n // and properly manage backdrop stacking\n if (Dialog._openDialogs.length > 0) {\n document.body.classList.add('modal-open');\n\n setTimeout(() => { // Let Bootstrap finish its hide animation\n Dialog.fixAllBackdropStacking();\n }, 50);\n }\n\n // Restore focus to the element that had it before modal opened\n if (this.previousFocus && document.body.contains(this.previousFocus)) {\n this.previousFocus.focus();\n }\n\n if (this.onHidden) this.onHidden(e);\n this.emit('hidden', { dialog: this });\n });\n\n // hidePrevented.bs.modal\n this.element.addEventListener('hidePrevented.bs.modal', (e) => {\n if (this.onHidePrevented) this.onHidePrevented(e);\n this.emit('hidePrevented', { dialog: this });\n });\n }\n\n\n\n /**\n * Show the dialog\n * @param {HTMLElement} relatedTarget - Optional element that triggered the modal\n */\n show(relatedTarget = null) {\n // Capture the currently focused element for later restoration\n this.previousFocus = document.activeElement;\n window.lastDialog = this;\n if (this.modal) {\n this.modal.show(relatedTarget);\n }\n }\n\n /**\n * Hide the dialog\n */\n hide() {\n // Blur any focused element inside the modal before hiding\n const focusedElement = this.element?.querySelector(':focus');\n if (focusedElement) {\n focusedElement.blur();\n }\n\n if (this.modal) {\n this.modal.hide();\n }\n }\n\n /**\n * Toggle the dialog\n * @param {HTMLElement} relatedTarget - Optional element that triggered the modal\n */\n toggle(relatedTarget = null) {\n if (this.modal) {\n this.modal.toggle(relatedTarget);\n }\n }\n\n /**\n * Destroy the dialog and clean up resources\n */\n async destroy() {\n // Hide modal if it's showing\n if (this.modal) {\n // Remove focus from any element inside the modal\n const focusedElement = this.element?.querySelector(':focus');\n if (focusedElement) {\n focusedElement.blur();\n }\n\n // Dispose of Bootstrap modal instance\n this.modal.dispose();\n this.modal = null;\n }\n\n // Restore previous focus if available\n if (this.previousFocus && document.body.contains(this.previousFocus)) {\n this.previousFocus.focus();\n this.previousFocus = null;\n }\n\n // Clean up auto-sizing\n if (this.autoSize) {\n this.resetAutoSizing();\n }\n\n // Call parent destroy\n await super.destroy();\n }\n\n /**\n * Handle dynamic height changes\n */\n handleUpdate() {\n if (this.modal) {\n this.modal.handleUpdate();\n }\n }\n\n /**\n * Update dialog content\n * @param {string|View} content - New content (string or View instance)\n */\n async setContent(content) {\n // Handle View instance\n if (content instanceof View) {\n // Clean up old view if exists\n if (this.bodyView) {\n await this.bodyView.destroy();\n this.removeChild(this.bodyView);\n }\n\n this.bodyView = content;\n this.body = '';\n this.addChild(this.bodyView);\n\n const bodyEl = this.element?.querySelector('.modal-body');\n if (bodyEl) {\n bodyEl.innerHTML = '';\n // Pass container to render - it will handle mounting internally\n await this.bodyView.render(bodyEl);\n }\n } else {\n // String content\n this.body = content;\n const bodyEl = this.element?.querySelector('.modal-body');\n if (bodyEl) {\n bodyEl.innerHTML = content;\n }\n }\n\n // Update modal position if needed\n this.handleUpdate();\n }\n\n /**\n * Update dialog title\n */\n setTitle(title) {\n this.title = title;\n const titleEl = this.element?.querySelector('.modal-title');\n if (titleEl) {\n titleEl.textContent = title;\n }\n }\n\n /**\n * Set loading state\n */\n setLoading(loading = true, message = 'Loading...') {\n const bodyEl = this.element?.querySelector('.modal-body');\n if (bodyEl) {\n if (loading) {\n bodyEl.innerHTML = `\n <div class=\"text-center py-4\">\n <div class=\"spinner-border text-primary mb-3\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n <p>${message}</p>\n </div>\n `;\n } else if (this.bodyView) {\n bodyEl.replaceChildren(this.bodyView.element);\n }\n }\n }\n\n /**\n * Clean up\n */\n async onBeforeDestroy() {\n // Clean up child views\n if (this.headerView) {\n await this.headerView.destroy();\n }\n if (this.bodyView) {\n await this.bodyView.destroy();\n }\n if (this.footerView) {\n await this.footerView.destroy();\n }\n\n await super.onBeforeDestroy();\n\n // Dispose Bootstrap modal instance (defensive: destroy() also disposes, but\n // onBeforeDestroy can be called from parent lifecycle paths as well)\n if (this.modal) {\n this.modal.dispose();\n this.modal = null;\n }\n }\n\n /**\n * Static method to show code in a dialog\n */\n static async showCode(options = {}) {\n const dialog = new Dialog({\n title: options.title || 'Source Code',\n size: options.size || 'lg',\n scrollable: true,\n body: Dialog.formatCode(options.code, options.language),\n buttons: [\n {\n text: 'Copy to Clipboard',\n class: 'btn-primary',\n icon: 'bi-clipboard',\n action: 'copy'\n },\n {\n text: 'Close',\n class: 'btn-secondary',\n dismiss: true\n }\n ]\n });\n\n // Handle copy action\n dialog.on('action:copy', async () => {\n if (navigator.clipboard) {\n try {\n await navigator.clipboard.writeText(options.code);\n dialog.showCopySuccess();\n } catch (err) {\n console.error('Failed to copy:', err);\n }\n }\n });\n\n // Mount to fullscreen element if it exists, otherwise body\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n await dialog.render(true, targetContainer);\n\n // Apply syntax highlighting after mounting\n if (window.Prism && dialog.element) {\n window.Prism.highlightAllUnder(dialog.element);\n }\n\n // Show the dialog\n dialog.show();\n\n // Clean up when hidden\n dialog.on('hidden', () => {\n dialog.destroy();\n dialog.element.remove();\n });\n\n return dialog;\n }\n\n /**\n * Format code for display with syntax highlighting support\n */\n static formatCode(code, language = 'javascript') {\n let highlightedCode;\n\n // Check if Prism.js is available and has the language\n if (window.Prism && window.Prism.languages[language]) {\n // Use Prism to highlight the code\n highlightedCode = window.Prism.highlight(code, window.Prism.languages[language], language);\n } else {\n // Fallback: just escape HTML\n highlightedCode = code\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n }\n\n // Add Prism classes for styling even if highlighting wasn't applied\n const prismClass = window.Prism ? `language-${language}` : '';\n\n // Modern VS Code-like dark theme styling\n const codeStyles = `\n max-height: 60vh;\n overflow-y: auto;\n background: #1e1e1e;\n color: #d4d4d4;\n padding: 1.25rem;\n border-radius: 0.5rem;\n margin: 0;\n font-family: 'Cascadia Code', 'Fira Code', 'JetBrains Mono', 'Consolas', 'Monaco', monospace;\n font-size: 0.9rem;\n line-height: 1.6;\n border: 1px solid #2d2d30;\n box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);\n `.replace(/\\s+/g, ' ').trim();\n\n return `\n <style>\n /* Custom Prism theme overrides for Dialog */\n .dialog-code-block .token.comment { color: #6a9955; }\n .dialog-code-block .token.string { color: #ce9178; }\n .dialog-code-block .token.keyword { color: #569cd6; }\n .dialog-code-block .token.function { color: #dcdcaa; }\n .dialog-code-block .token.number { color: #b5cea8; }\n .dialog-code-block .token.operator { color: #d4d4d4; }\n .dialog-code-block .token.class-name { color: #4ec9b0; }\n .dialog-code-block .token.punctuation { color: #d4d4d4; }\n .dialog-code-block .token.boolean { color: #569cd6; }\n .dialog-code-block .token.property { color: #9cdcfe; }\n .dialog-code-block .token.tag { color: #569cd6; }\n .dialog-code-block .token.attr-name { color: #9cdcfe; }\n .dialog-code-block .token.attr-value { color: #ce9178; }\n .dialog-code-block ::selection { background: #264f78; }\n </style>\n <pre class=\"${prismClass} dialog-code-block\" style=\"${codeStyles}\">\n <code class=\"${prismClass}\" style=\"color: inherit; background: transparent; text-shadow: none;\">${highlightedCode}</code>\n </pre>\n `;\n }\n\n /**\n * Trigger Prism highlighting on already rendered code blocks\n * Call this after inserting code into the DOM if not using formatCode\n */\n static highlightCodeBlocks(container = document) {\n if (window.Prism && window.Prism.highlightAllUnder) {\n window.Prism.highlightAllUnder(container);\n }\n }\n\n /**\n * Show copy success feedback\n */\n showCopySuccess() {\n const btn = this.element.querySelector('[data-action=\"copy\"]');\n if (btn) {\n const originalHtml = btn.innerHTML;\n btn.innerHTML = '<i class=\"bi bi-check me-1\"></i>Copied!';\n btn.classList.remove('btn-primary');\n btn.classList.add('btn-success');\n btn.disabled = true;\n\n setTimeout(() => {\n btn.innerHTML = originalHtml;\n btn.classList.remove('btn-success');\n btn.classList.add('btn-primary');\n btn.disabled = false;\n }, 2000);\n }\n }\n\n\n\n\n /**\n * Static method to show HTML preview in a sandboxed iframe\n */\n static async showHtmlPreview(options = {}) {\n const htmlContent = options.html || options.content || '';\n const title = options.title || 'HTML Preview';\n const size = options.size || 'lg';\n const height = options.height || 500;\n\n const previewHtml = `\n <div class=\"html-preview-container\">\n <div class=\"d-flex justify-content-between align-items-center mb-2\">\n <small class=\"text-muted\">Preview (sandboxed)</small>\n <button type=\"button\" class=\"btn btn-sm btn-outline-secondary\" data-action=\"refresh-preview\">\n <i class=\"bi bi-arrow-clockwise\"></i> Refresh\n </button>\n </div>\n <iframe\n id=\"html-preview-frame\"\n class=\"border rounded w-100\"\n style=\"height: ${height}px; background: white;\"\n sandbox=\"allow-same-origin\"\n frameborder=\"0\"\n ></iframe>\n </div>\n `;\n\n const dialog = new Dialog({\n title,\n size,\n scrollable: false,\n body: previewHtml,\n buttons: [\n {\n text: 'Close',\n class: 'btn-secondary',\n dismiss: true\n }\n ]\n });\n\n // Handle refresh action\n dialog.on('action:refresh-preview', async (event) => {\n const iframe = dialog.element.querySelector('#html-preview-frame');\n if (!iframe) return;\n\n const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;\n iframeDoc.open();\n iframeDoc.write(htmlContent);\n iframeDoc.close();\n });\n\n // Mount to fullscreen element if it exists, otherwise body\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n await dialog.render(true, targetContainer);\n\n // Render HTML in iframe after dialog is rendered\n const iframe = dialog.element.querySelector('#html-preview-frame');\n if (iframe) {\n const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;\n iframeDoc.open();\n iframeDoc.write(htmlContent);\n iframeDoc.close();\n }\n\n // Show the dialog\n dialog.show();\n\n // Clean up when hidden\n dialog.on('hidden', () => {\n dialog.destroy();\n dialog.element.remove();\n });\n\n return dialog;\n }\n\n /**\n * Show a dialog with promise-based button handling\n * - If a button has a handler, it will be called. Return semantics:\n * - true or undefined: resolve and close (with button.value || button.action || index)\n * - null or false: keep dialog open (do not resolve)\n * - any other value: resolve with that value and close\n * - If no handler, resolve with action/value/index and close\n * @param {Object} options - Dialog options\n * @returns {Promise} Resolves with value/action/index or null on dismiss\n */\n static async showDialog(options = {}) {\n // Handle legacy signature (message, title, options)\n if (typeof options === 'string') {\n const message = arguments[0];\n const title = arguments[1] || 'Alert';\n const opts = arguments[2] || {};\n options = {\n ...opts,\n body: message,\n title: title\n };\n }\n\n const {\n title = 'Dialog',\n content,\n // Aliases supported (common mistakes): `message` and `view`\n // Priority: body > view > message > content\n body,\n view,\n message,\n size = 'md',\n centered = true,\n buttons = [\n { text: 'OK', class: 'btn-primary', value: true }\n ],\n rejectOnDismiss = false, // Default to return null on dismissal\n ...dialogOptions\n } = options;\n\n const resolvedBody = body ?? view ?? message ?? content ?? '';\n\n // Create the dialog (preserve original button action/dismiss attributes)\n const dialog = new Dialog({\n title,\n body: resolvedBody,\n size,\n centered,\n buttons,\n ...dialogOptions\n });\n\n // Render and mount\n // Mount to fullscreen element if it exists, otherwise body\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n await dialog.render(true, targetContainer);\n\n // Return promise that resolves based on button clicks\n return new Promise((resolve, reject) => {\n let resolved = false;\n\n // Handle button clicks\n const buttonElements = dialog.element.querySelectorAll('.modal-footer button');\n buttonElements.forEach((btnElement, index) => {\n const buttonConfig = buttons[index];\n if (!buttonConfig) return;\n\n btnElement.addEventListener('click', async (e) => {\n if (resolved) return;\n\n const defaultResolveValue = (\n buttonConfig.value !== undefined\n ? buttonConfig.value\n : (buttonConfig.action ?? index)\n );\n\n // If a handler is provided, call it and respect its return semantics\n if (typeof buttonConfig.handler === 'function') {\n try {\n const result = await buttonConfig.handler({\n dialog,\n button: buttonConfig,\n index,\n event: e\n });\n\n // null/false -> keep dialog open\n if (result === null || result === false) {\n return;\n }\n\n // Determine resolve value and close\n const valueToResolve =\n (result === true || result === undefined)\n ? defaultResolveValue\n : result;\n\n resolved = true;\n // Close the dialog (Bootstrap will close if dismiss attribute is present)\n if (!buttonConfig.dismiss) {\n dialog.hide();\n }\n resolve(valueToResolve);\n } catch (err) {\n console.error('Dialog button handler error:', err);\n // Keep dialog open on handler error\n return;\n }\n } else {\n // No handler: resolve with action/value/index and close\n resolved = true;\n if (!buttonConfig.dismiss) {\n dialog.hide();\n }\n resolve(defaultResolveValue);\n }\n });\n });\n\n // Handle backdrop click or ESC key\n dialog.on('hidden', () => {\n // If not already resolved by a button handler, resolve as dismiss\n if (!resolved) {\n resolved = true;\n if (rejectOnDismiss) {\n reject(new Error('Dialog dismissed'));\n } else {\n resolve(null);\n }\n }\n // Always cleanup after hide\n setTimeout(() => {\n dialog.destroy();\n dialog.element.remove();\n }, 100);\n });\n\n // Show the dialog\n dialog.show();\n });\n }\n\n /**\n * Static alert dialog helper\n * @param {Object|string} options - Alert options or message string\n * @returns {Promise} Resolves when OK is clicked\n */\n static async alert(options = {}) {\n // Handle string argument\n if (typeof options === 'string') {\n options = {\n message: options,\n title: 'Alert'\n };\n }\n\n const {\n message = '',\n title = 'Alert',\n type = 'info', // info, success, warning, danger\n ...dialogOptions\n } = options;\n\n // Add icon based on type\n let icon = '';\n let titleClass = '';\n switch(type) {\n case 'success':\n icon = '<i class=\"bi bi-check-circle-fill text-success me-2\"></i>';\n titleClass = 'text-success';\n break;\n case 'warning':\n icon = '<i class=\"bi bi-exclamation-triangle-fill text-warning me-2\"></i>';\n titleClass = 'text-warning';\n break;\n case 'danger':\n case 'error':\n icon = '<i class=\"bi bi-x-circle-fill text-danger me-2\"></i>';\n titleClass = 'text-danger';\n break;\n default:\n icon = '<i class=\"bi bi-info-circle-fill text-info me-2\"></i>';\n titleClass = 'text-info';\n }\n\n return Dialog.showDialog({\n title: `<span class=\"${titleClass}\">${icon}${title}</span>`,\n body: `<p>${message}</p>`,\n size: 'sm',\n centered: true,\n buttons: [\n { text: 'OK', class: 'btn-primary', value: true }\n ],\n ...dialogOptions\n });\n }\n\n /**\n * Static confirm dialog\n */\n static async confirm(message, title = 'Confirm', options = {}) {\n if (typeof message === 'object') {\n options = message;\n message = options.message;\n title = options.title || title;\n }\n const dialog = new Dialog({\n title,\n body: `<p>${message}</p>`,\n size: options.size || 'sm',\n centered: true,\n backdrop: 'static',\n buttons: [\n { text: options.cancelText || 'Cancel', class: 'btn-secondary', dismiss: true, action: 'cancel' },\n { text: options.confirmText || 'Confirm', class: options.confirmClass || 'btn-primary', action: 'confirm' }\n ],\n ...options\n });\n\n // Mount to fullscreen element if it exists, otherwise body\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n await dialog.render(true, targetContainer);\n dialog.show();\n\n return new Promise((resolve) => {\n let result = false;\n\n dialog.on('action:confirm', () => {\n result = true;\n dialog.hide();\n });\n\n dialog.on('hidden', () => {\n dialog.destroy();\n dialog.element.remove();\n resolve(result);\n });\n });\n }\n\n /**\n * Static prompt dialog\n */\n static async prompt(message, title = 'Input', options = {}) {\n const inputId = `prompt-input-${Date.now()}`;\n const defaultValue = options.defaultValue || '';\n const inputType = options.inputType || 'text';\n const placeholder = options.placeholder || '';\n\n const dialog = new Dialog({\n title,\n body: `\n <p>${message}</p>\n <input type=\"${inputType}\"\n class=\"form-control\"\n id=\"${inputId}\"\n value=\"${defaultValue}\"\n placeholder=\"${placeholder}\">\n `,\n size: options.size || 'sm',\n centered: true,\n backdrop: 'static',\n buttons: [\n { text: 'Cancel', class: 'btn-secondary', dismiss: true },\n { text: 'OK', class: 'btn-primary', action: 'ok' }\n ],\n ...options\n });\n\n // Mount to fullscreen element if it exists, otherwise body\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n await dialog.render(true, targetContainer);\n dialog.show();\n\n // Focus the input\n dialog.on('shown', () => {\n const input = dialog.element.querySelector(`#${inputId}`);\n if (input) {\n input.focus();\n input.select();\n }\n });\n\n return new Promise((resolve) => {\n let result = null;\n\n dialog.on('action:ok', () => {\n const input = dialog.element.querySelector(`#${inputId}`);\n result = input ? input.value : null;\n dialog.hide();\n });\n\n dialog.on('hidden', () => {\n dialog.destroy();\n dialog.element.remove();\n resolve(result);\n });\n });\n }\n\n /**\n * Get Bootstrap modal instance\n */\n getModal() {\n return this.modal;\n }\n\n /**\n * Check if modal is shown\n */\n isShown() {\n return this.element?.classList.contains('show') || false;\n }\n\n /**\n * Show form in a dialog for simple data collection (no model saving)\n * @param {object} options - Configuration options\n * @returns {Promise} Promise that resolves with form data or null if cancelled\n */\n static async showForm(options = {}) {\n const {\n title = 'Form',\n formConfig = {},\n size = 'md',\n centered = true,\n submitText = 'Submit',\n cancelText = 'Cancel',\n ...dialogOptions\n } = options;\n\n // Create the FormView (no model for simple form) - lazy loaded\n const FormView = (await import('@core/forms/FormView.js')).default;\n const formView = new FormView({\n fileHandling: options.fileHandling || 'base64',\n data: options.data,\n defaults: options.defaults,\n model: options.model,\n formConfig: {\n fields: formConfig.fields || options.fields,\n ...formConfig,\n submitButton: false,\n resetButton: false\n }\n });\n\n // Create the dialog with the FormView as body\n const dialog = new Dialog({\n title,\n body: formView,\n size,\n centered,\n buttons: [\n {\n text: cancelText,\n class: 'btn-secondary',\n action: 'cancel'\n },\n {\n text: submitText,\n class: 'btn-primary',\n action: 'submit'\n }\n ],\n ...dialogOptions\n });\n\n // Render and mount dialog\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n await dialog.render(true, targetContainer);\n\n // Show the dialog\n dialog.show();\n\n return new Promise((resolve) => {\n let resolved = false;\n\n // Handle dialog actions\n dialog.on('action:submit', async () => {\n if (resolved) return;\n\n // Validate form\n if (!formView.validate()) {\n formView.focusFirstError();\n return;\n }\n\n if (options.autoSave && options.model) {\n dialog.setLoading(true);\n const result = await formView.saveModel()\n if (!result.success) {\n dialog.setLoading(false);\n dialog.render();\n dialog.getApp().toast.error(result.message);\n return;\n }\n resolved = true;\n dialog.hide();\n resolve(result);\n }\n\n // Get form data and resolve\n try {\n const formData = await formView.getFormData();\n resolved = true;\n dialog.hide();\n resolve(formData);\n } catch (error) {\n console.error('Error collecting form data:', error);\n formView.showError('Error collecting form data');\n }\n });\n\n dialog.on('action:cancel', () => {\n if (resolved) return;\n resolved = true;\n dialog.hide();\n resolve(null);\n });\n\n // Handle ESC key or backdrop click\n dialog.on('hidden', () => {\n if (!resolved) {\n resolved = true;\n resolve(null);\n }\n // Clean up\n setTimeout(() => {\n formView.destroy();\n dialog.destroy();\n }, 100);\n });\n });\n }\n\n /**\n * Show form in a dialog for simple data collection (no model saving)\n * @param {object} options - Configuration options\n * @returns {Promise} Promise that resolves with form data or null if cancelled\n */\n static async updateModelImage(options = {}, fieldOptions = {}) {\n const upload = options.upload || false;\n const fieldName = fieldOptions.name || options.field || 'image';\n\n const formOptions = {\n title: \"Upload Your Avatar\",\n model: null,\n autoSave: !upload,\n size: 'sm',\n fields: [\n {\n type: 'image',\n name: fieldName,\n size: 'lg',\n imageSize: { width: 200, height: 200 },\n placeholder: 'Upload your image',\n ...fieldOptions\n }\n ],\n ...options\n };\n\n const result = await Dialog.showForm(formOptions);\n\n if (!upload || !result || !options.model) return result;\n\n // Get the base64 image data from the form result\n const base64Data = result[fieldName];\n if (!base64Data || !base64Data.startsWith('data:')) return result;\n\n // Convert base64 to File object\n const arr = base64Data.split(',');\n const mimeMatch = arr[0]?.match(/:(.*?);/);\n const mime = mimeMatch?.[1] || 'image/png';\n const bstr = atob(arr[1]);\n let n = bstr.length;\n const u8arr = new Uint8Array(n);\n while (n--) { u8arr[n] = bstr.charCodeAt(n); }\n const ext = mime.split('/')[1] || 'png';\n const FileCtor = (typeof window !== 'undefined' && window.File) || globalThis.File;\n if (!FileCtor) {\n throw new Error('File API is not available in this environment');\n }\n const file = new FileCtor([u8arr], `${fieldName}.${ext}`, { type: mime });\n\n // Upload via FileUpload service (3-stage: initiate → upload → complete)\n const fileModel = new FileModel();\n await fileModel.upload({\n file,\n name: `${fieldName}.${ext}`,\n description: options.uploadDescription || `${fieldName} upload`,\n showToast: true\n });\n\n // Save file ID to the model\n const resp = await options.model.save({ [fieldName]: fileModel.id });\n return resp;\n }\n\n static async showModelView(model, options) {\n const modelClass = model.constructor;\n const modelView = modelClass.VIEW_CLASS;\n const viewInstance = new modelView({ model: model });\n options = options || {};\n return await Dialog.showDialog({\n header: false,\n body: viewInstance,\n size: 'lg',\n centered: false,\n ...options\n });\n }\n\n /**\n * Show form in a dialog with automatic model saving\n * @param {object} options - Configuration options (requires model)\n * @returns {Promise} Promise that resolves with save result or null if cancelled\n */\n static async showModelForm(options = {}) {\n const {\n title = 'Edit',\n formConfig = {},\n size = 'md',\n centered = true,\n submitText = 'Save',\n cancelText = 'Cancel',\n model,\n fields,\n ...dialogOptions\n } = options;\n\n if (!model) {\n throw new Error('showModelForm requires a model');\n }\n\n // Create the FormView with model - lazy loaded\n const FormView = (await import('@core/forms/FormView.js')).default;\n const formView = new FormView({\n fileHandling: options.fileHandling || 'base64',\n model: model,\n data: options.data,\n defaults: options.defaults,\n formConfig: {\n // Support both formConfig.fields and direct fields parameter\n fields: fields || formConfig.fields || [],\n ...formConfig,\n submitButton: false,\n resetButton: false\n }\n });\n\n // Create the dialog with the FormView as body\n const dialog = new Dialog({\n title,\n body: formView,\n size,\n centered,\n buttons: [\n {\n text: cancelText,\n class: 'btn-secondary',\n action: 'cancel'\n },\n {\n text: submitText,\n class: 'btn-primary',\n action: 'submit'\n }\n ],\n ...dialogOptions\n });\n\n // Render and mount dialog\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n await dialog.render(true, targetContainer);\n\n // Show the dialog\n dialog.show();\n\n return new Promise((resolve) => {\n let resolved = false;\n\n // Handle dialog actions\n dialog.on('action:submit', async () => {\n if (resolved) return;\n\n // Show loading state\n dialog.setLoading(true, 'Saving...');\n\n try {\n const result = await formView.handleSubmit();\n if (result.success) {\n resolved = true;\n dialog.hide();\n resolve(result);\n } else {\n // Restore form and show error\n dialog.setLoading(false);\n let errmsg = result.error;\n if (result.data && result.data.error) {\n errmsg = result.data.error;\n }\n dialog.getApp().toast.error(errmsg);\n // formView.showError(result.error || 'Save failed. Please try again.');\n }\n } catch (error) {\n console.error('Error saving form:', error);\n // Restore form and show error\n await dialog.setContent(formView);\n formView.showError(error.message || 'An error occurred while saving');\n }\n });\n\n dialog.on('action:cancel', () => {\n if (resolved) return;\n resolved = true;\n dialog.hide();\n resolve(null);\n });\n\n // Handle ESC key or backdrop click\n dialog.on('hidden', () => {\n if (!resolved) {\n resolved = true;\n resolve(null);\n }\n // Clean up\n setTimeout(() => {\n formView.destroy();\n dialog.destroy();\n }, 100);\n });\n });\n }\n\n /**\n * Show data in a dialog using DataView component\n * @param {object} options - Configuration options\n * @returns {Promise} Promise that resolves when dialog is closed\n */\n static async showData(options = {}) {\n const {\n title = 'Data View',\n data = {},\n model = null,\n fields = [],\n columns = 2,\n responsive = true,\n showEmptyValues = false,\n emptyValueText = '—',\n size = 'lg',\n centered = true,\n closeText = 'Close',\n ...dialogOptions\n } = options;\n\n\n // Create the DataView - lazy loaded\n const DataView = (await import('@core/views/data/DataView.js')).default;\n const dataView = new DataView({\n data,\n model,\n fields,\n columns,\n responsive,\n showEmptyValues,\n emptyValueText\n });\n\n // Create the dialog with the DataView as body\n const dialog = new Dialog({\n title,\n body: dataView,\n size,\n centered,\n buttons: [\n {\n text: closeText,\n class: 'btn-secondary',\n value: 'close'\n }\n ],\n ...dialogOptions\n });\n\n // Render and mount dialog\n const fullscreenElement = document.querySelector('.table-fullscreen');\n const targetContainer = fullscreenElement || document.body;\n await dialog.render(true, targetContainer);\n\n // Show the dialog and return promise\n dialog.show();\n\n return new Promise((resolve) => {\n let resolved = false;\n\n // Get close button\n const closeBtn = dialog.element.querySelector('.modal-footer button');\n\n // Handle close\n const handleClose = () => {\n if (resolved) return;\n resolved = true;\n dialog.hide();\n resolve(true);\n };\n\n // Attach event listener\n closeBtn?.addEventListener('click', handleClose);\n\n // Handle ESC key or backdrop click\n dialog.on('hidden', () => {\n if (!resolved) {\n resolved = true;\n resolve(true);\n }\n // Clean up\n setTimeout(() => {\n dataView.destroy();\n dialog.destroy();\n dialog.element.remove();\n }, 100);\n });\n\n // Forward DataView events\n dataView.on('field:click', (data) => {\n dialog.emit('dataview:field:click', data);\n });\n\n dataView.on('error', (data) => {\n dialog.emit('dataview:error', data);\n });\n });\n }\n}\n\nDialog.showConfirm = Dialog.confirm;\nDialog.showError = Dialog.alert;\n\nexport default Dialog;\n"],"names":["ToastService","constructor","options","this","containerId","position","autohide","defaultDelay","maxToasts","toasts","Map","toastCounter","init","createContainer","container","document","getElementById","createElement","id","className","getPositionClasses","style","zIndex","setAttribute","body","appendChild","positionMap","success","message","show","icon","error","info","warning","plain","type","enforceMaxToasts","toastId","config","title","getDefaultTitle","getDefaultIcon","delay","dismissible","toastElement","createToastElement","bootstrap","Error","bsToast","Toast","set","element","addEventListener","cleanup","hide","console","warn","dispose","updateProgress","showView","view","createViewToastElement","cleanupView","bodyContainer","querySelector","render","progressInfo","toast","header","createToastHeader","createToastBody","innerHTML","createViewToastBody","_type","iconHtml","titleHtml","escapeHtml","timeHtml","showTime","getTimeString","closeButton","showIcon","Array","from","values","length","oldestId","keys","next","value","oldest","get","e","parentNode","removeChild","delete","hideAll","forEach","_id","clearAll","Date","toLocaleTimeString","hour","minute","str","div","textContent","getStats","stats","total","size","byType","setOptions","newOptions","ProgressView","View","super","template","filename","filesize","filesizeFormatted","dataFormatter","pipe","progress","percentage","loaded","loadedFormatted","totalFormatted","status","showCancel","onCancel","cancelled","completed","getTemplate","markCompleted","markFailed","markCancelled","onActionCancel","action","event","disabled","emit","setFilename","setFilesize","getPercentage","isCompleted","isCancelled","FileUpload","fileModel","file","name","group","description","onProgress","onComplete","onError","showToast","File","uploadRequest","progressToast","progressView","toastService","promise","_startUpload","uploadData","uploadConfig","result","_showProgressToast","_initiateUpload","upload_url","url","method","fields","headers","JSON","stringify","_performUpload","_completeUpload","_onComplete","_onError","payload","file_size","content_type","response","rest","POST","data","errorMessage","Promise","resolve","reject","useFormData","xhr","XMLHttpRequest","upload","onprogress","_onProgress","Math","round","onload","statusText","onerror","ontimeout","onabort","apiUrl","startsWith","resolvedUrl","buildUrl","open","timeout","key","Object","entries","toLowerCase","setRequestHeader","formData","FormData","append","send","save","setTimeout","cancel","abort","then","onSuccess","catch","onFinally","finally","Group","Model","endpoint","GroupList","Collection","ModelClass","GroupKinds","org","division","department","team","merchant","partner","client","iso","sales","reseller","location","region","route","project","inventory","test","misc","qa","GroupKindOptions","map","label","GroupForms","create","required","placeholder","labelField","valueField","maxItems","emptyFetch","debounceMs","edit","cols","detailed","text","level","class","columns","xs","md","imageSize","width","height","help","EDIT_FORM","ADD_FORM","CREATE_FORM","User","hasPermission","permission","isArray","some","p","isSysPermission","permissionToCheck","substring","_hasPermission","member","permissions","hasPerm","UserList","PERMISSIONS","PERMISSION_FIELDS","UserForms","UserDataView","profile","format","activity","colSize","showEmptyValues","emptyValueText","dataViewColumns","summary","DATA_VIEW","UserDevice","getByDuid","duid","model","resp","GET","UserDeviceList","UserDeviceLocation","UserDeviceLocationList","FileManager","FileManagerList","FileManagerForms","default","owners","credentials","isImage","FileList","Dialog","static","backdrop","modal","getFullscreenAwareZIndex","_baseZIndex","fixAllBackdropStacking","backdrops","querySelectorAll","openDialogs","_openDialogs","sortedDialogs","sort","a","b","_dialogZIndex","index","backdropZIndex","targetContainer","updateAllBackdropStacking","showBusy","_busyCounter","_busyTimeout","clearTimeout","_busyIndicator","busyZIndex","msgElement","classList","add","hideBusy","alert","force","remove","modalId","now","tagName","fade","attributes","tabindex","labelledBy","describedBy","titleId","centered","scrollable","autoSize","keyboard","focus","headerContent","headerView","contextMenu","_processHeaderContent","content","bodyView","bodyClass","noBodyPadding","minWidth","minHeight","maxHeight","maxWidthPercent","maxHeightPercent","_processBodyContent","footer","footerView","footerClass","_processFooterContent","buttons","onShow","onShown","onHide","onHidden","onHidePrevented","autoShow","relatedTarget","addChild","bodyPromise","headerPromise","footerPromise","dialogClasses","push","includes","join","buildHeader","buildBody","buildFooter","replaceById","headerActions","items","buildContextMenu","menuItems","filterContextMenuItems","triggerIcon","buttonClass","item","href","target","dataAttrs","filter","filteredItems","app","getApp","user","activeUser","getState","window","globalApp","buttonsHtml","btn","dismissAttr","dismiss","actionAttr","idAttr","disabledAttr","mount","_container","mounted","destroyed","onBeforeMount","bindEvents","onAfterMount","onAfterRender","Prism","highlightAllUnder","setupAutoSizing","modalBody","Modal","bindBootstrapEvents","applyAutoSizing","once","isShown","modalDialog","modalContent","originalStyles","dialogMaxWidth","maxWidth","dialogWidth","contentWidth","contentMaxHeight","hadScrollableClass","contains","offsetHeight","contentRect","getBoundingClientRect","viewportMargin","min","innerWidth","innerHeight","optimalWidth","max","ceil","optimalHeight","heightExceedsMax","autoSizedWidth","autoSizedHeight","_originalStyles","resetAutoSizing","stackIndex","newZIndex","_backdropZIndex","dialog","firstInput","focusedElement","blur","preventDefault","indexOf","splice","previousFocus","activeElement","lastDialog","toggle","destroy","handleUpdate","setContent","bodyEl","setTitle","titleEl","setLoading","loading","replaceChildren","onBeforeDestroy","showCode","formatCode","code","language","on","async","navigator","clipboard","writeText","showCopySuccess","err","highlightedCode","languages","highlight","replace","prismClass","trim","highlightCodeBlocks","originalHtml","showHtmlPreview","htmlContent","html","iframe","iframeDoc","contentDocument","contentWindow","write","close","showDialog","arguments","rejectOnDismiss","dialogOptions","resolved","btnElement","buttonConfig","defaultResolveValue","handler","button","valueToResolve","titleClass","confirm","cancelText","confirmText","confirmClass","prompt","inputId","defaultValue","inputType","input","select","getModal","showForm","formConfig","submitText","formView","FormView","require","n","FormView$1","fileHandling","defaults","submitButton","resetButton","validate","autoSave","saveModel","getFormData","showError","focusFirstError","updateModelImage","fieldOptions","fieldName","field","formOptions","base64Data","arr","split","mimeMatch","match","mime","bstr","atob","u8arr","Uint8Array","charCodeAt","ext","FileCtor","globalThis","FileModel","uploadDescription","showModelView","viewInstance","modelView","VIEW_CLASS","showModelForm","handleSubmit","errmsg","showData","responsive","closeText","dataView","DataView","closeBtn","showConfirm"],"mappings":"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"}
|