web-mojo 2.2.10 → 2.2.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/admin.cjs.js +1 -1
  2. package/dist/admin.cjs.js.map +1 -1
  3. package/dist/admin.es.js +480 -498
  4. package/dist/admin.es.js.map +1 -1
  5. package/dist/auth.cjs.js +1 -1
  6. package/dist/auth.es.js +1 -1
  7. package/dist/charts.cjs.js +1 -1
  8. package/dist/charts.es.js +4 -4
  9. package/dist/chunks/{ChatView-BxIeRwBQ.js → ChatView-CQnDGafI.js} +2 -2
  10. package/dist/chunks/{ChatView-BxIeRwBQ.js.map → ChatView-CQnDGafI.js.map} +1 -1
  11. package/dist/chunks/{ChatView-DfhhZKoN.js → ChatView-ppMlENSa.js} +7 -7
  12. package/dist/chunks/{ChatView-DfhhZKoN.js.map → ChatView-ppMlENSa.js.map} +1 -1
  13. package/dist/chunks/{Collection-G5_fJcpB.js → Collection-BQxqHtRi.js} +2 -2
  14. package/dist/chunks/{Collection-G5_fJcpB.js.map → Collection-BQxqHtRi.js.map} +1 -1
  15. package/dist/chunks/{Collection-BgX6OcUC.js → Collection-BlP54kxB.js} +2 -2
  16. package/dist/chunks/{Collection-BgX6OcUC.js.map → Collection-BlP54kxB.js.map} +1 -1
  17. package/dist/chunks/{ContextMenu-C4_MWleT.js → ContextMenu-BH5SaDXX.js} +2 -2
  18. package/dist/chunks/{ContextMenu-C4_MWleT.js.map → ContextMenu-BH5SaDXX.js.map} +1 -1
  19. package/dist/chunks/{ContextMenu-CMoik8q6.js → ContextMenu-BNFU-kG4.js} +3 -3
  20. package/dist/chunks/{ContextMenu-CMoik8q6.js.map → ContextMenu-BNFU-kG4.js.map} +1 -1
  21. package/dist/chunks/{DataView-i7kpJjVQ.js → DataView-BpXdthN2.js} +2 -2
  22. package/dist/chunks/{DataView-i7kpJjVQ.js.map → DataView-BpXdthN2.js.map} +1 -1
  23. package/dist/chunks/{DataView-BYNjIf--.js → DataView-R_LkYBAw.js} +2 -2
  24. package/dist/chunks/{DataView-BYNjIf--.js.map → DataView-R_LkYBAw.js.map} +1 -1
  25. package/dist/chunks/{Dialog-DX5h2QA9.js → Dialog--hl_Uh6X.js} +2 -2
  26. package/dist/chunks/{Dialog-DX5h2QA9.js.map → Dialog--hl_Uh6X.js.map} +1 -1
  27. package/dist/chunks/{Dialog-Cu_Dx46k.js → Dialog-RzLLLfJD.js} +5 -5
  28. package/dist/chunks/{Dialog-Cu_Dx46k.js.map → Dialog-RzLLLfJD.js.map} +1 -1
  29. package/dist/chunks/{FormView-B_90L1RY.js → FormView--WuITh01.js} +2 -2
  30. package/dist/chunks/{FormView-B_90L1RY.js.map → FormView--WuITh01.js.map} +1 -1
  31. package/dist/chunks/{FormView-Bwofbd8S.js → FormView-C1emfj3B.js} +2 -2
  32. package/dist/chunks/{FormView-Bwofbd8S.js.map → FormView-C1emfj3B.js.map} +1 -1
  33. package/dist/chunks/{ListView-DoF-Sr56.js → ListView-B96JeG4g.js} +5 -5
  34. package/dist/chunks/ListView-B96JeG4g.js.map +1 -0
  35. package/dist/chunks/{ListView-OWwlcGGg.js → ListView-O9AO02Rf.js} +2 -2
  36. package/dist/chunks/ListView-O9AO02Rf.js.map +1 -0
  37. package/dist/chunks/{MetricsMiniChartWidget-vXr5pxpm.js → MetricsMiniChartWidget-DoxqoF1X.js} +2 -2
  38. package/dist/chunks/{MetricsMiniChartWidget-vXr5pxpm.js.map → MetricsMiniChartWidget-DoxqoF1X.js.map} +1 -1
  39. package/dist/chunks/{MetricsMiniChartWidget-DL5stA6A.js → MetricsMiniChartWidget-DyVs4Wt0.js} +4 -4
  40. package/dist/chunks/{MetricsMiniChartWidget-DL5stA6A.js.map → MetricsMiniChartWidget-DyVs4Wt0.js.map} +1 -1
  41. package/dist/chunks/{PDFViewer-DSmi78S6.js → PDFViewer-BxFcG82d.js} +2 -2
  42. package/dist/chunks/{PDFViewer-DSmi78S6.js.map → PDFViewer-BxFcG82d.js.map} +1 -1
  43. package/dist/chunks/{PDFViewer--jlqnuVw.js → PDFViewer-CHX2NLkG.js} +3 -3
  44. package/dist/chunks/{PDFViewer--jlqnuVw.js.map → PDFViewer-CHX2NLkG.js.map} +1 -1
  45. package/dist/chunks/{Rest-ChN4Ntac.js → Rest-0oRgqNjX.js} +16 -1
  46. package/dist/chunks/Rest-0oRgqNjX.js.map +1 -0
  47. package/dist/chunks/Rest-P-KCJpjB.js +2 -0
  48. package/dist/chunks/Rest-P-KCJpjB.js.map +1 -0
  49. package/dist/chunks/{TokenManager-CEOPgnsw.js → TokenManager-CCfcK4aA.js} +2 -2
  50. package/dist/chunks/{TokenManager-CEOPgnsw.js.map → TokenManager-CCfcK4aA.js.map} +1 -1
  51. package/dist/chunks/{TokenManager-DiQfilqw.js → TokenManager-TEF4Gmwu.js} +5 -5
  52. package/dist/chunks/{TokenManager-DiQfilqw.js.map → TokenManager-TEF4Gmwu.js.map} +1 -1
  53. package/dist/chunks/{WebSocketClient-JHjYcYbU.js → WebSocketClient-BbPsISrp.js} +2 -2
  54. package/dist/chunks/{WebSocketClient-JHjYcYbU.js.map → WebSocketClient-BbPsISrp.js.map} +1 -1
  55. package/dist/chunks/{WebSocketClient-bLYhu2Wv.js → WebSocketClient-D53hpvM8.js} +2 -2
  56. package/dist/chunks/{WebSocketClient-bLYhu2Wv.js.map → WebSocketClient-D53hpvM8.js.map} +1 -1
  57. package/dist/chunks/{version-DOHckOGK.js → version-C2aAPoA6.js} +4 -4
  58. package/dist/chunks/{version-DOHckOGK.js.map → version-C2aAPoA6.js.map} +1 -1
  59. package/dist/chunks/{version-DUvrBxZl.js → version-CiqJg8U3.js} +2 -2
  60. package/dist/chunks/{version-DUvrBxZl.js.map → version-CiqJg8U3.js.map} +1 -1
  61. package/dist/docit.cjs.js +1 -1
  62. package/dist/docit.es.js +6 -6
  63. package/dist/index.cjs.js +1 -1
  64. package/dist/index.es.js +15 -15
  65. package/dist/lightbox.cjs.js +1 -1
  66. package/dist/lightbox.es.js +5 -5
  67. package/dist/map.cjs.js +1 -1
  68. package/dist/map.es.js +2 -2
  69. package/dist/timeline.cjs.js +1 -1
  70. package/dist/timeline.es.js +4 -4
  71. package/package.json +1 -1
  72. package/dist/chunks/ListView-DoF-Sr56.js.map +0 -1
  73. package/dist/chunks/ListView-OWwlcGGg.js.map +0 -1
  74. package/dist/chunks/Rest-ChN4Ntac.js.map +0 -1
  75. package/dist/chunks/Rest-DhD-U1vp.js +0 -2
  76. package/dist/chunks/Rest-DhD-U1vp.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"admin.cjs.js","sources":["../src/extensions/admin/account/AdminDashboardPage.js","../src/extensions/admin/account/users/UserView.js","../src/extensions/admin/account/users/UserTablePage.js","../src/extensions/admin/account/users/MemberView.js","../src/extensions/admin/account/users/MemberTablePage.js","../src/extensions/admin/account/groups/GroupView.js","../src/extensions/admin/account/groups/GroupTablePage.js","../src/extensions/admin/account/devices/DeviceView.js","../src/extensions/admin/account/devices/UserDeviceTablePage.js","../src/extensions/admin/account/devices/UserDeviceLocationTablePage.js","../src/extensions/admin/account/devices/GeoIPView.js","../src/extensions/admin/account/devices/GeoLocatedIPTablePage.js","../src/extensions/map/MapLibreView.js","../src/extensions/map/countryCentroids.js","../src/extensions/map/MetricsCountryMapView.js","../src/extensions/admin/incidents/IncidentDashboardPage.js","../src/core/views/data/StackTraceView.js","../src/extensions/admin/incidents/adapters/IncidentHistoryAdapter.js","../src/extensions/admin/incidents/IncidentView.js","../src/extensions/admin/incidents/IncidentTablePage.js","../src/extensions/admin/incidents/EventView.js","../src/extensions/admin/incidents/EventTablePage.js","../src/extensions/admin/incidents/adapters/TicketNoteAdapter.js","../src/extensions/admin/incidents/TicketView.js","../src/extensions/admin/incidents/TicketTablePage.js","../src/extensions/admin/incidents/RuleSetView.js","../src/extensions/admin/incidents/RuleSetTablePage.js","../src/extensions/admin/messaging/email/EmailDomainTablePage.js","../src/extensions/admin/messaging/email/EmailMailboxTablePage.js","../src/extensions/admin/messaging/email/EmailTemplateView.js","../src/extensions/admin/messaging/email/EmailTemplateTablePage.js","../src/extensions/admin/messaging/email/EmailView.js","../src/extensions/admin/messaging/email/SentMessageTablePage.js","../src/core/models/Phonehub.js","../src/extensions/admin/messaging/sms/PhoneNumberView.js","../src/extensions/admin/messaging/sms/PhoneNumberTablePage.js","../src/extensions/admin/messaging/sms/SMSView.js","../src/extensions/admin/messaging/sms/SMSTablePage.js","../src/extensions/admin/messaging/push/PushDashboardPage.js","../src/extensions/admin/messaging/push/PushConfigTablePage.js","../src/extensions/admin/messaging/push/PushTemplateTablePage.js","../src/extensions/admin/messaging/push/PushDeliveryView.js","../src/extensions/admin/messaging/push/PushDeliveryTablePage.js","../src/extensions/admin/messaging/push/PushDeviceView.js","../src/extensions/admin/messaging/push/PushDeviceTablePage.js","../src/extensions/admin/jobs/JobStatsView.js","../src/extensions/admin/jobs/JobHealthView.js","../src/extensions/admin/jobs/JobDetailsView.js","../src/extensions/admin/jobs/tables/JobsTable.js","../src/extensions/admin/jobs/tables/RunnersTable.js","../src/extensions/admin/jobs/tables/ScheduledJobsTable.js","../src/extensions/admin/jobs/JobsAdminPage.js","../src/extensions/admin/jobs/TaskDetailsView.js","../src/extensions/admin/jobs/RunnerDetailsView.js","../src/extensions/admin/jobs/TaskManagementPage.js","../src/extensions/admin/monitoring/LogView.js","../src/extensions/admin/monitoring/LogTablePage.js","../src/extensions/admin/monitoring/MetricsPermissionsView.js","../src/extensions/admin/monitoring/MetricsPermissionsTablePage.js","../src/extensions/admin/storage/FileManagerTablePage.js","../src/extensions/admin/storage/FileView.js","../src/extensions/admin/storage/FileTablePage.js","../src/extensions/admin/storage/S3BucketTablePage.js","../src/admin.js"],"sourcesContent":["/**\n * AdminDashboardPage - Administrative dashboard with system metrics and charts\n */\n\nimport Page from '@core/Page.js';\nimport View from '@core/View.js';\nimport { MetricsChart, MetricsMiniChart, MetricsMiniChartWidget } from '@ext/charts/index.js';\n\n// Embedded HeaderView for dashboard statistics\nclass AdminHeaderView extends View {\n constructor(options = {}) {\n super({\n title: 'Dashboard',\n ...options,\n headerActions: [\n {\n label: 'Export',\n icon: 'bi-download',\n action: 'export',\n buttonClass: 'btn-primary'\n }\n ],\n className: 'admin-header-section'\n });\n\n // Mock data - replace with real API calls\n this.stats = {\n user_activity_day: 0,\n total_users: 0,\n group_activity_day: 0,\n total_groups: 0,\n api_calls: 0,\n apiChange: '',\n incidents: 0,\n incidentsChange: ''\n };\n\n // Prepare formatted data for template\n this.prepareStatsForTemplate();\n }\n\n async getTemplate() {\n return `\n <div class=\"admin-stats-header mb-4\">\n <div class=\"row\">\n <div class=\"col-xl-3 col-lg-6 col-12 mb-3\">\n <div data-container=\"user_activity_day\"></div>\n </div>\n\n <div class=\"col-xl-3 col-lg-6 col-12 mb-3\">\n <div data-container=\"group_activity_day\"></div>\n </div>\n\n <div class=\"col-xl-3 col-lg-6 col-12 mb-3\">\n <div data-container=\"api_activity_day\"></div>\n </div>\n\n <div class=\"col-xl-3 col-lg-6 col-12 mb-3\">\n <div data-container=\"incident_activity_day\"></div>\n </div>\n </div>\n </div>\n `;\n }\n\n async onInit() {\n // TODO: Replace with actual API calls to fetch real statistics\n // this.loadStats();\n // slug: user_activity_day, group_activity_day\n this.userActivity = new MetricsMiniChartWidget({\n icon: \"bi bi-people fs-2\",\n title: 'User Activity',\n subtitle: '{{now_value}} <span class=\"subtitle-label\">{{now_label}}</span> {{total_users}} <span class=\"subtitle-label\">Total</span>',\n background: \"#5388D6\",\n textColor: \"#FFFFFF\",\n granularity: 'days',\n trendRange: 4,\n trendOffset: 0,\n slugs: ['user_activity_day'],\n account: 'global',\n chartType: 'bar',\n showTooltip: true,\n showXAxis: true,\n height: 50,\n chartWidth: '100%',\n color: 'rgba(245, 245, 255, 0.8)',\n fill: true,\n fillColor: 'rgba(245, 245, 255, 0.6)',\n smoothing: 0.3,\n showTrending: true,\n showSettings: true,\n showDateRange: true,\n containerId: 'user_activity_day'\n });\n this.addChild(this.userActivity);\n\n this.groupActivity = new MetricsMiniChartWidget({\n icon: \"bi bi-collection fs-2\",\n title: 'Group Activity',\n subtitle: '{{now_value}} <span class=\"subtitle-label\">{{now_label}}</span> {{total_groups}} <span class=\"subtitle-label\">Total</span>',\n background: \"#1f6a7a\",\n textColor: \"#FFFFFF\",\n granularity: 'days',\n trendRange: 4,\n trendOffset: 0,\n slugs: ['group_activity_day'],\n account: 'global',\n chartType: 'bar',\n showTooltip: true,\n showXAxis: true,\n height: 50,\n chartWidth: '100%',\n color: 'rgba(245, 245, 255, 0.8)',\n fill: true,\n fillColor: 'rgba(245, 245, 255, 0.6)',\n smoothing: 0.3,\n showTrending: true,\n containerId: 'group_activity_day'\n });\n this.addChild(this.groupActivity);\n\n this.apiActivity = new MetricsMiniChartWidget({\n icon: \"bi bi-graph-up fs-2\",\n title: 'API Requests',\n subtitle: '{{now_value}} <span class=\"subtitle-label\">{{now_label}}</span> {{total}} <span class=\"subtitle-label\">Total</span>',\n background: \"#50A079\",\n textColor: \"#FFFFFF\",\n endpoint: '/api/metrics/fetch',\n trendRange: 4,\n trendOffset: 0,\n granularity: 'days',\n slugs: ['api_calls'],\n account: 'global',\n chartType: 'line',\n showTooltip: true,\n showXAxis: true,\n height: 50,\n chartWidth: '100%',\n color: 'rgba(245, 245, 255, 0.8)',\n fill: true,\n fillColor: 'rgba(245, 245, 255, 0.6)',\n smoothing: 0.3,\n showTrending: true,\n containerId: 'api_activity_day'\n });\n this.addChild(this.apiActivity);\n\n this.incidentActivity = new MetricsMiniChartWidget({\n icon: \"bi bi-exclamation-triangle fs-2\",\n title: 'Incidents',\n subtitle: '{{now_value}} <span class=\"subtitle-label\">{{now_label}}</span> {{total}} <span class=\"subtitle-label\">Total</span>',\n background: \"#B14545\",\n textColor: \"#FFFFFF\",\n endpoint: '/api/metrics/fetch',\n trendRange: 4,\n trendOffset: 0,\n granularity: 'days',\n slugs: ['incidents'],\n account: 'incident',\n chartType: 'line',\n showTooltip: true,\n showXAxis: true,\n height: 50,\n chartWidth: '100%',\n color: 'rgba(245, 245, 255, 0.8)',\n fill: true,\n fillColor: 'rgba(245, 245, 255, 0.6)',\n smoothing: 0.3,\n showTrending: true,\n containerId: 'incident_activity_day'\n });\n this.addChild(this.incidentActivity);\n }\n\n async onBeforeRender() {\n\n }\n\n prepareStatsForTemplate() {\n // Determine badge and icon classes based on incidents change\n // const isDecreasing = this.stats.incidentsChange.startsWith('-');\n // this.stats.incidentsBadgeClass = isDecreasing ? 'bg-success-subtle text-success' : 'bg-warning-subtle text-warning';\n // this.stats.incidentsIconClass = isDecreasing ? 'arrow-down' : 'arrow-up';\n\n }\n\n async loadValues() {\n try {\n const response = await this.getApp().rest.GET('/api/metrics/value/get', {\n slugs: ['total_users', 'total_groups'],\n account: \"global\"\n });\n if (response.success && response.data.status) {\n Object.assign(this.stats, response.data.data);\n }\n if (this.groupActivity) {\n this.groupActivity.header.total_groups = this.stats.total_groups || 0;\n this.groupActivity.header.render();\n }\n if (this.userActivity) {\n this.userActivity.header.total_users = this.stats.total_users || 0;\n this.userActivity.header.render();\n }\n } catch (error) {\n console.error('Failed to load admin stats:', error);\n }\n }\n\n async loadStats() {\n // Example of how to load real data\n try {\n const response = await this.getApp().rest.GET('/api/metrics/series', {\n slugs: ['user_created', 'user_activity_day', \"incidents\",\n \"api_calls\", \"api_errors\", \"group_activity_day\"],\n account: \"global\",\n granularity: \"days\"\n });\n if (response.success && response.data.status) {\n Object.assign(this.stats, response.data.data);\n this.prepareStatsForTemplate();\n }\n } catch (error) {\n console.error('Failed to load admin stats:', error);\n }\n }\n}\n\nexport default class AdminDashboardPage extends Page {\n constructor(options = {}) {\n super({\n ...options,\n title: 'Admin Dashboard',\n className: 'admin-dashboard-page'\n });\n\n // Page data\n this.pageTitle = 'Admin Dashboard';\n this.pageSubtitle = 'System monitoring and metrics overview';\n }\n\n async getTemplate() {\n return `\n <div class=\"admin-dashboard-container container-lg\">\n <!-- Page Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-2\">\n <div>\n <p class=\"text-muted mb-0\">{{pageSubtitle}}</p>\n <small class=\"text-info\">\n <i class=\"bi bi-shield-check me-1\"></i>\n Real-time system metrics and performance monitoring\n </small>\n </div>\n <div class=\"btn-group\" role=\"group\">\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm\"\n data-action=\"refresh-all\" title=\"Refresh All Charts\">\n <i class=\"bi bi-arrow-clockwise\"></i> Refresh\n </button>\n <button type=\"button\" class=\"btn btn-outline-primary btn-sm\"\n data-action=\"export-metrics\" title=\"Export Metrics Data\">\n <i class=\"bi bi-download\"></i> Export\n </button>\n <button type=\"button\" class=\"btn btn-outline-warning btn-sm\"\n data-action=\"view-alerts\" title=\"View System Alerts\">\n <i class=\"bi bi-bell\"></i> Alerts\n </button>\n </div>\n </div>\n\n <!-- Stats Header -->\n <div data-container=\"admin-header\"></div>\n <div data-container=\"example-chart\"></div>\n <!-- Charts Section -->\n <div class=\"row\">\n <!-- Full Width API Metrics Chart -->\n <div class=\"col-12 mb-4\">\n <div data-container=\"api-metrics-chart\"></div>\n </div>\n </div>\n\n <!-- System Status Footer -->\n <div class=\"row\">\n <div class=\"col-12\">\n <div class=\"alert alert-success border-0\" role=\"alert\">\n <div class=\"d-flex align-items-center\">\n <i class=\"bi bi-check-circle-fill me-2\"></i>\n <div>\n <strong>System Status:</strong> All systems operational.\n Last updated: <span class=\"text-muted\">{{lastUpdated}}</span>\n </div>\n <div class=\"ms-auto\">\n <button class=\"btn btn-sm btn-outline-success\" data-action=\"view-system-status\">\n <i class=\"bi bi-info-circle\"></i> Details\n </button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n `;\n }\n\n async onInit() {\n // Set last updated time\n this.lastUpdated = new Date().toLocaleString();\n\n // Create and add header view\n this.headerView = new AdminHeaderView({\n containerId: 'admin-header'\n });\n this.addChild(this.headerView);\n\n // Create API Metrics Chart\n this.apiMetricsChart = new MetricsChart({\n title: `<i class=\"bi bi-graph-up me-2\"></i> API Metrics`,\n endpoint: '/api/metrics/fetch',\n height: 250,\n granularity: 'hours',\n slugs: ['api_calls', 'api_errors'],\n account: 'global',\n chartType: 'line',\n showDateRange: false,\n yAxis: {\n label: 'Count',\n beginAtZero: true\n },\n tooltip: {\n y: 'number'\n },\n containerId: 'api-metrics-chart'\n });\n this.addChild(this.apiMetricsChart);\n\n }\n\n // Action Handlers\n async onActionRefreshAll(event, element) {\n try {\n // Show loading state\n const button = element || event?.currentTarget || null;\n const icon = button?.querySelector?.('i');\n icon?.classList.add('bi-spin');\n if (button) button.disabled = true;\n\n // Refresh all charts\n const promises = [\n this.headerView?.loadValues(),\n this.apiMetricsChart?.refresh()\n ].filter(Boolean);\n\n await Promise.allSettled(promises);\n\n\n // Update last updated time\n this.lastUpdated = new Date().toLocaleString();\n\n // Emit refresh event\n const eventBus = this.getApp()?.events;\n if (eventBus) {\n eventBus.emit('admin:dashboard-refreshed', {\n page: this,\n timestamp: this.lastUpdated\n });\n }\n\n } catch (error) {\n console.error('Failed to refresh dashboard:', error);\n // Show error feedback\n const alert = this.element.querySelector('.alert-success');\n if (alert) {\n alert.className = 'alert alert-danger border-0';\n alert.innerHTML = `\n <div class=\"d-flex align-items-center\">\n <i class=\"bi bi-exclamation-triangle-fill me-2\"></i>\n <div>\n <strong>Error:</strong> Failed to refresh dashboard data.\n </div>\n </div>\n `;\n\n // Reset after 5 seconds\n setTimeout(() => {\n alert.className = 'alert alert-success border-0';\n alert.innerHTML = `\n <div class=\"d-flex align-items-center\">\n <i class=\"bi bi-check-circle-fill me-2\"></i>\n <div>\n <strong>System Status:</strong> All systems operational.\n Last updated: <span class=\"text-muted\">${this.lastUpdated}</span>\n </div>\n </div>\n `;\n }, 5000);\n }\n } finally {\n // Reset button state\n const icon = element.querySelector('i');\n icon?.classList.remove('bi-spin');\n if (button) button.disabled = false;\n }\n }\n\n async onActionExportMetrics(event, element) {\n try {\n // Export all charts as PNG\n await this.apiMetricsChart?.export('png');\n\n // Show success feedback\n const eventBus = this.getApp()?.events;\n if (eventBus) {\n eventBus.emit('admin:metrics-exported', {\n page: this,\n charts: ['api-metrics']\n });\n }\n\n } catch (error) {\n console.error('Failed to export metrics:', error);\n }\n }\n\n async onActionViewAlerts(event, element) {\n // Navigate to alerts page or show alerts modal\n const router = this.getApp()?.router;\n if (router) {\n router.navigateTo('/admin/alerts');\n }\n }\n\n async onActionViewSystemStatus(event, element) {\n // Navigate to system status page\n const router = this.getApp()?.router;\n if (router) {\n router.navigateTo('/admin/system-status');\n }\n }\n\n // Public API\n async refreshDashboard() {\n return this.onActionRefreshAll(null, null, { disabled: false, querySelector: () => null });\n }\n\n getCharts() {\n return {\n apiMetrics: this.apiMetricsChart\n };\n }\n\n getStats() {\n return this.headerView?.stats || {};\n }\n\n async onAfterRender() {\n this.headerView?.loadValues();\n }\n}\n","/**\n * UserView - Comprehensive user management interface\n *\n * Features:\n * - Two-row header with avatar, name, contact info, and activity\n * - Tabbed interface for Profile, Groups, Events, and Logs\n * - Integrated with DataView and Table components\n * - Clean Bootstrap 5 styling\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport DataView from '@core/views/data/DataView.js';\nimport TableView from '@core/views/table/TableView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport { User, UserDataView, UserForms, UserDeviceList, UserDeviceLocationList } from '@core/models/User.js';\nimport { LogList } from '@core/models/Log.js';\nimport { IncidentEventList } from '@core/models/Incident.js';\nimport { MemberList } from '@core/models/Member.js';\nimport { PushDeviceList } from '@core/models/Push.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport FormView from '@core/forms/FormView.js';\n\n\nclass UserView extends View {\n constructor(options = {}) {\n super({\n className: 'user-view',\n ...options\n });\n\n // User model instance\n this.model = options.model || new User(options.data || {});\n\n // Tab views\n this.tabView = null;\n this.profileView = null;\n this.groupsView = null;\n this.eventsView = null;\n this.logsView = null;\n\n // Set template\n this.template = `\n <div class=\"user-view-container\">\n <!-- User Header -->\n <div data-container=\"user-header\"></div>\n <!-- Tab Container -->\n <div data-container=\"user-tabs\"></div>\n </div>\n `;\n }\n\n async onInit() {\n // Create Profile tab using DataView\n this.header = new View({\n containerId: 'user-header',\n template: `\n <div class=\"d-flex justify-content-between align-items-start mb-4\">\n <!-- Left Side: Primary Identity -->\n <div class=\"d-flex align-items-center gap-3\">\n {{{model.avatar|avatar('md','rounded-circle')}}}\n <div>\n <h3 class=\"mb-0\">{{model.display_name|default('Unnamed User')}}</h3>\n <a href=\"mailto:{{model.email}}\" class=\"text-decoration-none text-body\">{{model.email}}</a>{{{model.email|clipboard}}}\n {{#model.phone_number}}\n <div class=\"text-muted small mt-1\">{{{model.phone_number|phone(false)}}}</div>\n {{/model.phone_number}}\n </div>\n </div>\n\n <!-- Right Side: Status & Actions -->\n <div class=\"d-flex align-items-start gap-4\">\n <div class=\"text-end\">\n <div class=\"d-flex align-items-center gap-2\">\n <i class=\"bi bi-circle-fill fs-8 {{model.is_active|boolean('text-success','text-secondary')}}\"></i>\n <span>{{model.is_active|boolean('Active','Inactive')}}</span>\n </div>\n {{#model.last_activity}}\n <div class=\"text-muted small mt-1\">Last active {{model.last_activity|relative}}</div>\n {{/model.last_activity}}\n </div>\n <div data-container=\"user-context-menu\"></div>\n </div>\n </div>`\n });\n\n this.header.setModel(this.model);\n this.addChild(this.header);\n\n this.profileView = new DataView({\n model: this.model,\n className: \"p-3\",\n showEmptyValues: true,\n fields: UserDataView.profile.fields\n });\n\n this.permsView = new FormView({\n fields: User.PERMISSION_FIELDS,\n model: this.model, // Set model during construction\n autosaveModelField: true // Enable auto-save with status indicators\n });\n\n // Create Groups table with MemberList collection\n const membersCollection = new MemberList({\n params: { user: this.model.get('id'), size: 5 }\n });\n this.groupsView = new TableView({\n collection: membersCollection,\n hideActivePillNames: ['user'],\n columns: [\n {\n key: 'created',\n label: 'Date Joined',\n formatter: 'date',\n sortable: true\n },\n {\n key: 'group.name',\n label: 'Group Name',\n sortable: true\n },\n {\n key: 'permissions|keys|badge',\n label: 'Permissions'\n }\n\n ]\n });\n\n // Create Events table with IncidentEventList collection\n const eventsCollection = new IncidentEventList({\n params: {\n size: 5,\n model_name: \"account.User\",\n model_id: this.model.get('id')\n }\n });\n this.eventsView = new TableView({\n collection: eventsCollection,\n hideActivePillNames: ['model_name', 'model_id'],\n columns: [\n {\n key: 'id',\n label: 'ID',\n sortable: true,\n width: '40px'\n },\n {\n key: 'created',\n label: 'Date',\n formatter: 'datetime',\n sortable: true,\n width: '150px'\n },\n {\n key: 'category|badge',\n label: 'Category'\n },\n {\n key: 'title',\n label: 'Title'\n }\n ]\n });\n\n const userDevices = new UserDeviceList({\n params: {\n size: 5,\n user: this.model.get('id')\n }\n });\n this.devicesView = new TableView({\n collection: userDevices,\n hideActivePillNames: ['user'],\n columns: [\n { key: 'duid|truncate_middle(16)', label: 'Device ID', sortable: true },\n { key: 'device_info.user_agent.family', label: 'Browser', formatter: \"default('—')\" },\n { key: 'device_info.os.family', label: 'OS', formatter: \"default('—')\" },\n { key: 'first_seen', label: 'First Seen', formatter: \"epoch|datetime\" },\n { key: 'last_seen', label: 'Last Seen', formatter: \"epoch|datetime\" }\n ],\n size: 5\n });\n\n\n const userLocations = new UserDeviceLocationList({\n params: {\n size: 5,\n user: this.model.get('id')\n }\n });\n this.locationsView = new TableView({\n collection: userLocations,\n\n hideActivePillNames: ['user'],\n columns: [\n { key: 'user_device', label: 'Device', template: '{{model.user_device.device_info.user_agent.family}} on {{model.user_device.device_info.os.family}}', sortable: true },\n { key: 'geolocation.city', label: 'City', formatter: \"default('—')\" },\n { key: 'geolocation.region', label: 'Region', formatter: \"default('—')\" },\n { key: 'geolocation.country_name', label: 'Country', formatter: \"default('—')\" },\n { key: 'last_seen', label: 'Last Seen', formatter: \"epoch|datetime\" }\n ],\n size: 5\n });\n\n const pushDevices = new PushDeviceList({\n params: {\n size: 5,\n user: this.model.get('id')\n }\n });\n this.pushDevicesView = new TableView({\n collection: pushDevices,\n hideActivePillNames: ['user'],\n columns: [\n { key: 'duid|truncate_middle(16)', label: 'Device ID', sortable: true },\n { key: 'device_info.user_agent.family', label: 'Browser', formatter: \"default('—')\" },\n { key: 'device_info.os.family', label: 'OS', formatter: \"default('—')\" },\n { key: 'first_seen', label: 'First Seen', formatter: \"epoch|datetime\" },\n { key: 'last_seen', label: 'Last Seen', formatter: \"epoch|datetime\" }\n ],\n size: 5\n });\n\n // Create Logs table with LogList collection\n const logsCollection = new LogList({\n params: {\n size: 5,\n model_name: \"account.User\",\n model_id: this.model.get('id')\n }\n });\n this.logsView = new TableView({\n collection: logsCollection,\n permissions: 'view_logs',\n hideActivePillNames: ['model_name', 'model_id'],\n columns: [\n {\n key: 'created',\n label: 'Timestamp',\n sortable: true,\n formatter: \"epoch|datetime\",\n filter: {\n name: \"created\",\n type: 'daterange',\n startName: 'dr_start',\n endName: 'dr_end',\n fieldName: 'dr_field',\n label: 'Date Range',\n format: 'YYYY-MM-DD',\n displayFormat: 'MMM DD, YYYY',\n separator: ' to '\n }\n },\n {\n key: 'level',\n label: 'Level',\n sortable: true,\n filter: {\n type: 'select',\n options: [\n { value: 'info', label: 'Info' },\n { value: 'warning', label: 'Warning' },\n { value: 'error', label: 'Error' }\n ]\n }\n },\n {\n key: 'kind',\n label: 'Kind',\n filter: {\n type: 'text'\n }\n },\n {\n name: 'log',\n label: 'Log',\n }\n ]\n });\n\n // Create Logs table with LogList collection\n const activityCollection = new LogList({\n params: {\n size: 5,\n uid: this.model.get('id')\n }\n });\n this.activityView = new TableView({\n collection: activityCollection,\n hideActivePillNames: ['uid'],\n permissions: 'view_logs',\n columns: [\n {\n key: 'created',\n label: 'Timestamp',\n sortable: true,\n formatter: \"epoch|datetime\",\n filter: {\n name: \"created\",\n type: 'daterange',\n startName: 'dr_start',\n endName: 'dr_end',\n fieldName: 'dr_field',\n label: 'Date Range',\n format: 'YYYY-MM-DD',\n displayFormat: 'MMM DD, YYYY',\n separator: ' to '\n }\n },\n {\n key: 'level',\n label: 'Level',\n sortable: true,\n filter: {\n type: 'select',\n options: [\n { value: 'info', label: 'Info' },\n { value: 'warning', label: 'Warning' },\n { value: 'error', label: 'Error' }\n ]\n }\n },\n {\n key: 'kind',\n label: 'Kind',\n filter: {\n type: 'text'\n }\n },\n {\n name: 'path',\n label: 'Path',\n }\n ]\n });\n\n // Create TabView with all tabs and responsive behavior\n this.tabView = new TabView({\n tabs: {\n 'Profile': this.profileView,\n 'Permissions': this.permsView,\n 'Groups': this.groupsView,\n 'Events': this.eventsView,\n 'Logs': this.logsView,\n 'Activity': this.activityView,\n \"Devices\": this.devicesView,\n \"Locations\": this.locationsView,\n \"Push Devices\": this.pushDevicesView\n },\n activeTab: 'Profile',\n containerId: 'user-tabs',\n enableResponsive: true,\n dropdownStyle: \"select\",\n minWidth: 300, // Switch to dropdown below 600px\n });\n\n // Add TabView as child\n this.addChild(this.tabView);\n\n const userMenu = new ContextMenu({\n containerId: 'user-context-menu',\n className: \"context-menu-view header-menu-absolute\",\n context: this.model, // Pass the user model as context\n config: {\n icon: 'bi-three-dots-vertical',\n items: [\n { label: 'Edit User', action: 'edit-user', icon: 'bi-pencil' },\n { label: 'Reset Password', action: 'reset-password', icon: 'bi-key' },\n { type: 'divider' },\n this.model.get('is_active')\n ? { label: 'Deactivate User', action: 'deactivate-user', icon: 'bi-person-dash' }\n : { label: 'Activate User', action: 'activate-user', icon: 'bi-person-check' },\n ]\n }\n });\n this.addChild(userMenu);\n }\n\n async onActionEditUser() {\n let frmConfig = UserForms.edit;\n const resp = await Dialog.showModelForm({\n title: `EDIT - #${this.model.id} ${this.options.modelName}`,\n model: this.model,\n formConfig: frmConfig,\n });\n if (resp) {\n this.render();\n }\n }\n async onActionResetPassword() { console.log(\"TODO: reset password\") }\n\n async onActionDeactivateUser() {\n const res = await Dialog.confirm(\"Are you sure you want to disable this user?\");\n if (res) {\n await this.model.save({ is_active: false });\n this.getApp().toast.success(\"Member disable\");\n } else {\n this.getApp().toast.error(\"Member disable failed\");\n }\n }\n\n async onActionActivateUser() {\n const res = await Dialog.confirm(\"Are you sure you want to enable this user?\");\n if (res) {\n await this.model.save({ is_active: true });\n this.getApp().toast.success(\"Member enabled\");\n } else {\n this.getApp().toast.error(\"Member enable failed\");\n }\n }\n\n // Action handlers for table interactions\n async onActionViewGroup(action, event, element) {\n const groupId = element.getAttribute('data-id');\n console.log('View group:', groupId);\n // TODO: Implement group view dialog\n }\n\n async onActionRemoveFromGroup(action, event, element) {\n const groupId = element.getAttribute('data-id');\n console.log('Remove user from group:', groupId);\n // TODO: Implement remove from group functionality\n }\n\n async onActionViewEvent(action, event, element) {\n const eventId = element.getAttribute('data-id');\n console.log('View event:', eventId);\n // TODO: Implement event view dialog\n }\n\n async onActionViewLog(action, event, element) {\n const logId = element.getAttribute('data-id');\n console.log('View log:', logId);\n // TODO: Implement log view dialog\n }\n\n async showTab(tabName) {\n if (this.tabView) {\n await this.tabView.showTab(tabName);\n }\n }\n\n getActiveTab() {\n return this.tabView ? this.tabView.getActiveTab() : null;\n }\n\n _onModelChange() {\n // do nothing, we do not want model changes to render this entire view\n }\n\n // Static factory method\n static create(options = {}) {\n return new UserView(options);\n }\n}\n\nUser.VIEW_CLASS = UserView;\n\nexport default UserView;\n","/**\n * UsersPage - Simple Todo List using TablePage component\n * Demonstrates clean usage of TablePage framework features\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport {UserList, UserForms} from '@core/models/User.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport MOJOUtils from '@core/utils/MOJOUtils.js';\nimport UserView from './UserView.js';\n\n\nclass UserTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_users',\n pageName: 'Manage Users',\n router: \"admin/users\",\n Collection: UserList,\n\n viewDialogOptions: { header: false },\n\n defaultQuery: {\n sort: '-last_activity',\n is_active: true\n },\n\n // Column definitions\n columns: [\n {\n key: 'id',\n label: 'ID',\n sortable: true,\n class: 'text-muted'\n },\n // {\n // label: 'Avatar',\n // key: 'avatar|avatar(\"sm\")',\n // sortable: false,\n // visibility: 'md'\n // },\n {\n key: 'display_name|tooltip:model.username',\n label: 'Display Name',\n },\n {\n label: 'Info',\n key: 'permissions.manage_users',\n template: `\n {{^model.is_active}}<span class=\"text-danger\">DISABLED</span> {{/model.is_active}}\n {{#model.permissions.manage_users}}{{{model.permissions.manage_users|yesnoicon('bi bi-person-gear text-danger')|tooltip('Manage Users')}}} {{/model.permissions.manage_users}}\n {{#model.permissions.manage_groups}}{{{model.permissions.manage_groups|yesnoicon('bi bi-building-gear text-primary')|tooltip('Manage Groups')}}} {{/model.permissions.manage_groups}}\n {{#model.permissions.view_global}}{{{model.permissions.view_global|yesnoicon('bi bi-globe text-secondary')|tooltip('View Global Menu')}}} {{/model.permissions.view_global}}\n {{#model.permissions.view_admin}}{{{model.permissions.view_admin|yesnoicon('bi bi-wrench text-secondary')|tooltip('View Admin Menu')}}} {{/model.permissions.view_admin}}\n `,\n sortable: false,\n },\n {\n key: 'email',\n label: 'Email',\n visibility: 'xl',\n className: 'text-muted fs-8',\n },\n // {\n // key: 'username',\n // label: 'Username',\n // visibility: 'xl',\n // className: 'text-muted fs-8',\n // },\n {\n key: 'last_activity',\n label: 'Last Activity',\n formatter: \"relative\",\n className: 'text-muted fs-8',\n }\n ],\n\n filters: [\n {\n key: 'is_active',\n label: 'Active',\n type: 'boolean',\n defaultValue: true,\n },\n {\n key: 'email',\n label: 'Email',\n type: 'text',\n defaultValue: '',\n },\n {\n key: 'username',\n label: 'Username',\n type: 'text',\n defaultValue: '',\n },\n {\n key: 'locations__ip_address',\n label: 'IP Address',\n type: 'text',\n defaultValue: '',\n },\n {\n key: 'last_activity',\n type: 'daterange',\n startName: 'dr_start',\n endName: 'dr_end',\n fieldName: 'dr_field',\n label: 'Date Range',\n format: 'YYYY-MM-DD',\n displayFormat: 'MMM DD, YYYY',\n separator: ' to '\n }\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // TablePage toolbar\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No users found. Click \"Add\" to create a new user.',\n\n // Context menu configuration\n contextMenu: [\n {\n icon: 'bi-pencil',\n action: 'edit',\n label: \"Edit Profile\"\n },\n {\n icon: 'bi-shield-check',\n action: 'edit-permissions',\n label: \"Edit Permissions\"\n },\n {\n icon: 'bi-shield',\n action: 'change-password',\n label: \"Change Password\",\n },\n { separator: true },\n {\n icon: 'bi-envelope',\n action: 'send-invite',\n label: \"Send Invite\"\n }\n ],\n\n // Table display options (for HTML table styling)\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n\n async onActionEditPermissions(event, element) {\n\n event.preventDefault();\n const item = this.collection.get(element.dataset.id);\n const result = await Dialog.showModelForm({\n model: item,\n size: 'lg',\n title: `Edit Permissions for \"${item._.username}\"`,\n fields: UserForms.permissions.fields\n });\n }\n\n async onActionChangePassword(event, element) {\n // Implement password change logic here\n const item = this.collection.get(element.dataset.id);\n const data = await Dialog.showForm({\n title: `Change Password for \"${item._.username}\"`,\n fields: [\n {\n type: 'text', // Change from 'hidden' to 'text'\n name: 'username',\n value: item.get('email') || item.get('username'),\n attributes: {\n autocomplete: 'username',\n readonly: 'readonly',\n tabindex: '-1',\n style: 'position: absolute; left: -9999px; opacity: 0; height: 0; width: 0;'\n }\n },\n {\n name: 'new_password',\n label: 'New Password',\n type: 'password',\n passwordUsage: 'new',\n required: true,\n showToggle: true,\n attributes: {\n autocomplete: 'new-password' // Make sure this isn't being overridden\n }\n }\n ]\n });\n\n if (data && data.new_password) {\n // Basic password validation\n const result = MOJOUtils.checkPasswordStrength(data.new_password);\n if (result.score < 5) {\n this.getApp().toast.error('Password must be at least 6 characters long and contain at least 2 of the following: uppercase letter, lowercase letter, or number');\n await this.onActionChangePassword(event, element);\n return;\n }\n const resp = await item.save({new_password: data.new_password});\n if (!this.onPasswordChange(resp)) {\n await this.onActionChangePassword(event, element);\n }\n }\n }\n\n onPasswordChange(resp) {\n if (resp.success) {\n this.getApp().toast.success('Password changed successfully');\n return true;\n } else {\n if (resp.data && resp.data.error) {\n this.getApp().toast.error(resp.data.error);\n } else {\n this.getApp().toast.error('Failed to change password');\n }\n }\n return false;\n }\n\n async onActionSendInvite(event, element) {\n const item = this.collection.get(element.dataset.id);\n const resp = await item.save({send_invite: true});\n if (resp.success) {\n this.getApp().toast.success('Invite sent successfully');\n return true;\n } else {\n if (resp.data && resp.data.error) {\n this.getApp().toast.error(resp.data.error);\n } else {\n this.getApp().toast.error('Failed to send invite');\n }\n }\n return false;\n }\n\n}\n\nexport default UserTablePage;\n","/**\n * MemberView - Comprehensive view for a group membership record\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport DataView from '@core/views/data/DataView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport { Member, MemberForms } from '@core/models/Member.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\n\nclass MemberView extends View {\n constructor(options = {}) {\n super({\n className: 'member-view',\n ...options\n });\n\n this.model = options.model || new Member(options.data || {});\n\n this.template = `\n <div class=\"member-view-container\">\n <!-- Header -->\n <div class=\"d-flex justify-content-between align-items-start mb-4\">\n <div class=\"d-flex align-items-center gap-3\">\n {{{model.user.avatar|avatar('md','rounded-circle')}}}\n <div>\n <h3 class=\"mb-0\">{{model.user.display_name}}</h3>\n <div class=\"text-muted\">Member of <strong>{{model.group.name}}</strong></div>\n </div>\n </div>\n\n <div class=\"d-flex align-items-start gap-4\">\n <div class=\"text-end\">\n <div>Role: <span class=\"badge bg-primary\">{{model.role|capitalize}}</span></div>\n <div class=\"text-muted small mt-1\">Status: {{model.status|capitalize}}</div>\n </div>\n <div data-container=\"member-context-menu\"></div>\n </div>\n </div>\n\n <!-- Tab Container -->\n <div data-container=\"member-tabs\"></div>\n </div>\n `;\n }\n\n async onInit() {\n // Overview Tab\n this.overviewView = new DataView({\n model: this.model,\n className: \"p-3\",\n columns: 2,\n fields: [\n { name: 'id', label: 'Membership ID' },\n { name: 'user.id', label: 'User ID' },\n { name: 'user.display_name', label: 'User Name' },\n { name: 'user.email', label: 'User Email' },\n { name: 'group.id', label: 'Group ID' },\n { name: 'group.name', label: 'Group Name' },\n { name: 'role', label: 'Role' },\n { name: 'status', label: 'Status' },\n { name: 'created', label: 'Date Joined', format: 'datetime' },\n ]\n });\n\n this.tabView = new TabView({\n containerId: 'member-tabs',\n tabs: {\n 'Overview': this.overviewView,\n },\n activeTab: 'Overview'\n });\n this.addChild(this.tabView);\n\n // ContextMenu\n const memberMenu = new ContextMenu({\n containerId: 'member-context-menu',\n className: \"context-menu-view header-menu-absolute\",\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: [\n { label: 'Edit Membership', action: 'edit-membership', icon: 'bi-pencil' },\n { type: 'divider' },\n { label: 'View User', action: 'view-user', icon: 'bi-person' },\n { label: 'View Group', action: 'view-group', icon: 'bi-diagram-3' },\n { type: 'divider' },\n { label: 'Remove From Group', action: 'remove-member', icon: 'bi-trash', danger: true }\n ]\n }\n });\n this.addChild(memberMenu);\n }\n\n async onActionEditMembership() {\n const resp = await Dialog.showModelForm({\n title: `Edit Membership`,\n model: this.model,\n formConfig: MemberForms.edit,\n });\n if (resp) {\n this.render();\n }\n }\n\n async onActionViewUser() {\n console.log(\"TODO: View user\", this.model.get('user.id'));\n }\n\n async onActionViewGroup() {\n console.log(\"TODO: View group\", this.model.get('group.id'));\n }\n\n async onActionRemoveMember() {\n const confirmed = await Dialog.confirm(\n `Are you sure you want to remove ${this.model.get('user.display_name')} from ${this.model.get('group.name')}?`,\n 'Confirm Removal'\n );\n if (confirmed) {\n const resp = await this.model.destroy();\n if (resp.success) {\n this.emit('member:removed', { model: this.model });\n }\n }\n }\n}\n\nMember.VIEW_CLASS = MemberView;\n\nexport default MemberView;\n","/**\n * MemberTablePage - Member management using TablePage component\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { MemberList, MemberForms } from '@core/models/Member.js';\nimport MemberView from './MemberView.js';\n\nclass MemberTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_members',\n pageName: 'Manage Members',\n router: \"admin/members\",\n Collection: MemberList,\n \n formEdit: MemberForms.edit,\n itemViewClass: MemberView,\n \n viewDialogOptions: {\n header: false,\n size: 'lg'\n },\n \n // Column definitions\n columns: [\n {\n key: 'id',\n label: 'ID',\n width: '60px',\n sortable: true,\n class: 'text-muted'\n },\n {\n key: 'user.display_name',\n label: 'User',\n formatter: \"default('Unknown User')\"\n },\n {\n key: 'user.email',\n label: 'Email',\n formatter: \"default('No Email')\"\n },\n {\n key: 'group.name',\n label: 'Group',\n formatter: \"default('Unknown Group')\"\n },\n {\n key: 'role',\n label: 'Role',\n formatter: \"badge\"\n },\n {\n key: 'status',\n label: 'Status',\n formatter: \"badge\"\n },\n {\n key: 'created',\n label: 'Added',\n formatter: \"epoch|datetime\"\n }\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No members found. Click \"Add Member\" to add users to groups.',\n\n // Batch actions\n batchBarLocation: 'top',\n batchActions: [\n { label: \"Remove\", icon: \"bi bi-person-dash\", action: \"batch-remove\" },\n { label: \"Export\", icon: \"bi bi-download\", action: \"batch-export\" },\n { label: \"Change Role\", icon: \"bi bi-person-gear\", action: \"batch-role\" },\n { label: \"Activate\", icon: \"bi bi-check-circle\", action: \"batch-activate\" },\n { label: \"Deactivate\", icon: \"bi bi-x-circle\", action: \"batch-deactivate\" }\n ],\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n}\n\nexport default MemberTablePage;","/**\n * GroupView - Comprehensive group management interface\n *\n * Features:\n * - Clean header with group name, kind, parent info, and activity\n * - Tabbed interface for Members, Child Groups, and Logs\n * - Integrated with Table and ContextMenu components\n * - Clean Bootstrap 5 styling\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport TableView from '@core/views/table/TableView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport { Group, GroupList, GroupForms } from '@core/models/Group.js';\nimport { MemberList } from '@core/models/Member.js';\nimport { LogList } from '@core/models/Log.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\n\nclass GroupView extends View {\n constructor(options = {}) {\n super({\n className: 'group-view',\n ...options\n });\n\n // Group model instance\n this.model = options.model || new Group(options.data || {});\n\n // Tab views\n this.tabView = null;\n this.membersView = null;\n this.childrenView = null;\n this.logsView = null;\n\n // Set template\n this.template = `\n <div class=\"group-view-container\">\n <!-- Group Header -->\n <div class=\"d-flex justify-content-between align-items-start mb-4\">\n <!-- Left Side: Primary Identity -->\n <div class=\"d-flex align-items-center gap-3\">\n {{#model.avatar}}\n {{{model.avatar|avatar('md','rounded')}}}\n {{/model.avatar}}\n {{^model.avatar}}\n <div class=\"avatar-placeholder rounded-circle bg-light d-flex align-items-center justify-content-center\" style=\"width: 80px; height: 80px;\">\n <i class=\"bi bi-collection text-secondary\" style=\"font-size: 40px;\"></i>\n </div>\n {{/model.avatar}}\n <div>\n <h3 class=\"mb-1\">{{model.name|truncate(32)|default('Unnamed Group')}}</h3>\n <div class=\"text-muted small\">\n <span>ID: {{model.id}}</span>\n <span class=\"mx-2\">|</span>\n <span>Kind: {{model.kind|capitalize}}</span>\n {{#model.metadata.timezone}}\n <span class=\"mx-2\">|</span>\n <span><i class=\"bi bi-clock\"></i> {{model.metadata.timezone}}</span>\n {{/model.metadata.timezone}}\n </div>\n {{#model.parent}}\n <div class=\"text-muted small mt-2\">\n <div>Parent: <a href=\"#\" data-action=\"view-parent\" data-id=\"{{model.parent.id}}\">{{model.parent.name|truncate(32)}}</a></div>\n <div>ID: {{model.parent.id}} | Kind: {{model.parent.kind|capitalize}}</div>\n </div>\n {{/model.parent}}\n </div>\n </div>\n\n <!-- Right Side: Status & Actions -->\n <div class=\"d-flex align-items-start gap-4\">\n <div class=\"text-end\">\n <div class=\"d-flex align-items-center gap-2\">\n <i class=\"bi bi-circle-fill fs-8 {{model.is_active|boolean('text-success','text-secondary')}}\"></i>\n <span>{{model.is_active|boolean('Active','Inactive')}}</span>\n </div>\n {{#model.last_activity}}\n <div class=\"text-muted small mt-1\">Last active {{model.last_activity|relative}}</div>\n {{/model.last_activity}}\n </div>\n <div data-container=\"group-context-menu\"></div>\n </div>\n </div>\n\n <!-- Tab Container -->\n <div data-container=\"group-tabs\"></div>\n </div>\n `;\n }\n\n async onInit() {\n // Create Members table\n const membersCollection = new MemberList({\n params: { group: this.model.get('id'), size: 5 }\n });\n this.membersView = new TableView({\n collection: membersCollection,\n hideActivePillNames: ['group'],\n columns: [\n { key: 'user.display_name', label: 'User', sortable: true },\n { key: 'user.email', label: 'Email', sortable: true },\n { key: 'created', label: 'Date Joined', formatter: 'date', sortable: true }\n ],\n showAdd: true,\n addButtonLabel: \"Invite\",\n onAdd: async (event) => {\n this.onInviteClick(event);\n },\n });\n\n // Create Children (sub-groups) table\n const childrenCollection = new GroupList({\n params: { parent: this.model.get('id'), size: 5 }\n });\n this.childrenView = new TableView({\n collection: childrenCollection,\n hideActivePillNames: ['parent'],\n columns: [\n { key: 'name', label: 'Name', sortable: true },\n { key: 'kind', label: 'Kind', formatter: 'badge' },\n { key: 'created', label: 'Created', formatter: 'date', sortable: true }\n ],\n\n toolbarButtons: [\n {\n label: 'Add Multiple',\n icon: 'bi bi-plus-circle',\n action: 'add-multiple',\n className: 'btn-success'\n }\n ],\n });\n\n // Create Logs table\n const logsCollection = new LogList({\n params: {\n size: 5,\n model_name: \"account.Group\",\n model_id: this.model.get('id')\n }\n });\n this.logsView = new TableView({\n collection: logsCollection,\n permissions: 'view_logs',\n hideActivePillNames: ['model_name', 'model_id'],\n columns: [\n { key: 'created', label: 'Timestamp', sortable: true, formatter: \"epoch|datetime\" },\n { key: 'level', label: 'Level', sortable: true, formatter: 'badge' },\n { key: 'kind', label: 'Kind' },\n { key: 'log', label: 'Log' }\n ]\n });\n\n // Create TabView\n this.tabView = new TabView({\n tabs: {\n 'Members': this.membersView,\n 'Children': this.childrenView,\n 'Logs': this.logsView\n },\n activeTab: 'Members',\n containerId: 'group-tabs'\n });\n this.addChild(this.tabView);\n\n // Create ContextMenu\n const groupMenu = new ContextMenu({\n containerId: 'group-context-menu',\n className: \"context-menu-view header-menu-absolute\",\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: [\n { label: 'Edit Group', action: 'edit-group', icon: 'bi-pencil' },\n { label: 'Add Member', action: 'add-member', icon: 'bi-person-plus' },\n { label: 'Add Child Group', action: 'add-child-group', icon: 'bi-diagram-3' },\n { type: 'divider' },\n this.model.get('is_active')\n ? { label: 'Deactivate Group', action: 'deactivate-group', icon: 'bi-x-circle' }\n : { label: 'Activate Group', action: 'activate-group', icon: 'bi-check-circle' },\n ]\n }\n });\n this.addChild(groupMenu);\n }\n\n async onActionEditGroup() {\n const resp = await Dialog.showModelForm({\n title: `Edit Group - ${this.model.get('name')}`,\n model: this.model,\n size: 'lg',\n formConfig: GroupForms.detailed,\n });\n if (resp) {\n this.render(); // Re-render to show updated data in header\n }\n }\n\n // TODO: Implement other context menu actions\n async onActionAddMember() { console.log(\"TODO: Add member to group\", this.model.id); }\n async onActionAddChildGroup() { console.log(\"TODO: Add child group to\", this.model.id); }\n async onActionDeactivateGroup() { console.log(\"TODO: Deactivate group\", this.model.id); }\n async onActionActivateGroup() { console.log(\"TODO: Activate group\", this.model.id); }\n\n async onActionViewParent(event, element) {\n const parentId = element.dataset.id;\n console.log(\"TODO: View parent group\", parentId);\n // This would typically involve closing the current dialog and opening a new one for the parent.\n // The parent TablePage would need to handle this event.\n this.emit('view-parent-group', { groupId: parentId });\n }\n\n async onInviteClick(event) {\n event.preventDefault();\n event.stopPropagation();\n const app = this.getApp();\n const data = await app.showForm({\n title: \"Invite User To \" + this.model.get(\"name\"),\n fields: [\n {\n type: 'email',\n name: 'email',\n label: 'Email',\n required: true,\n columns: 12\n }\n ]\n });\n\n if (data && data.email) {\n app.showLoading();\n const resp = await app.rest.POST('/api/group/member/invite', {\n group: this.model.id,\n email: data.email\n });\n app.hideLoading();\n if (resp.success) {\n app.toast.success(\"User invited successfully\");\n this.membersView.collection.fetch();\n } else {\n app.toast.error(\"Failed to invite user\");\n }\n }\n }\n\n\n async onActionAddMultiple() {\n const data = await Dialog.showForm({\n title: \"Select Members\",\n fields: [\n {\n name: 'group_ids',\n type: 'collectionmultiselect',\n required: true,\n Collection: GroupList,\n labelField: 'name',\n itemTemplate: `\n <div class=\"ms-2\">\n <div class=\"fs-7\">{{model.name}}</div>\n <div class=\"fs-8 text-muted\">{{model.kind}}</div>\n </div>\n `,\n valueField: 'id',\n enableSearch: true,\n searchPlaceholder: 'Search groups...',\n defaultParams: {\n is_active: true,\n size: 100,\n group: this.model.id\n }\n },\n ]\n });\n if (data) {\n console.warn(data);\n this.getApp().toast.warning(\"This is only for testing\");\n }\n\n }\n\n}\n\nGroup.VIEW_CLASS = GroupView;\n\nexport default GroupView;\n","/**\n * GroupTablePage - Group management using TablePage component\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { GroupList, GroupForms, Group } from '@core/models/Group.js';\nimport GroupView from './GroupView.js';\n\nclass GroupTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_groups',\n pageName: 'Manage Groups',\n router: \"admin/groups\",\n Collection: GroupList,\n\n formCreate: GroupForms.create,\n formEdit: GroupForms.edit,\n itemViewClass: GroupView,\n\n viewDialogOptions: {\n header: false\n },\n\n defaultQuery: {\n sort: '-id',\n is_active: 1,\n },\n\n // Column definitions\n columns: [\n {\n key: 'id',\n label: 'ID',\n sortable: true,\n class: 'text-muted'\n },\n\n {\n key: 'name',\n label: 'Display Name'\n },\n {\n key: 'kind|badge',\n label: 'Kind',\n filter: {\n type: \"select\",\n options: Group.GroupKindOptions\n }\n },\n {\n key: 'is_active|yesnoicon',\n label: 'Enabled',\n visibility: 'lg'\n },\n {\n key: 'parent.name',\n label: 'Parent',\n formatter: \"default('-')\",\n visibility: 'md',\n class: 'text-muted fs-8'\n },\n {\n key: 'created',\n label: 'Created',\n className: 'text-muted fs-8',\n formatter: \"epoch|datetime\",\n visibility: 'lg'\n },\n {\n key: 'last_activity',\n label: 'Activity',\n className: 'text-muted fs-8',\n formatter: \"relative\",\n visibility: 'lg'\n }\n ],\n\n filters: [\n {\n key: 'is_active',\n label: 'Active',\n type: 'select',\n options: [\n { label: 'Active', value: true },\n { label: 'Inactive', value: false }\n ]\n }\n ],\n\n contextMenu: [\n {\n icon: 'bi-pencil',\n action: 'edit',\n label: \"Edit Group\"\n },\n {\n icon: 'bi-bullseye',\n action: 'make-active',\n label: \"Make Active Group\"\n }\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No groups found. Click \"Add Group\" to create your first one.',\n\n // Batch actions\n batchBarLocation: 'top',\n batchActions: [\n { label: \"Delete\", icon: \"bi bi-trash\", action: \"batch-delete\" },\n { label: \"Export\", icon: \"bi bi-download\", action: \"batch-export\" },\n { label: \"Activate\", icon: \"bi bi-check-circle\", action: \"batch-activate\" },\n { label: \"Deactivate\", icon: \"bi bi-x-circle\", action: \"batch-deactivate\" },\n { label: \"Move\", icon: \"bi bi-arrow-right\", action: \"batch-move\" }\n ],\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n },\n });\n }\n\n onActionMakeActive(event, element) {\n const item = this.collection.get(element.dataset.id);\n this.getApp().setActiveGroup(item);\n }\n\n}\n\nexport default GroupTablePage;\n","/**\n * DeviceView - Comprehensive user device management interface\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport TableView from '@core/views/table/TableView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport { UserDevice, UserDeviceLocationList } from '@core/models/User.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\n\nclass DeviceView extends View {\n constructor(options = {}) {\n super({\n className: 'device-view',\n ...options\n });\n\n this.model = options.model || new UserDevice(options.data || {});\n this.deviceInfo = this.model.get('device_info') || {};\n this.deviceIcon = this.getIconForDevice(this.deviceInfo);\n\n this.template = `\n <div class=\"device-view-container\">\n <!-- Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-4\">\n <!-- Left Side: Icon & Info -->\n <div class=\"d-flex align-items-center gap-3\">\n <div class=\"fs-1 text-primary\">\n <i class=\"bi {{deviceIcon}}\"></i>\n </div>\n <div>\n <h3 class=\"mb-1\">\n {{deviceInfo.user_agent.family}} on {{deviceInfo.os.family}}\n </h3>\n <div class=\"text-muted small\">\n DUID: {{model.duid|truncate_middle(32)}}\n </div>\n <div class=\"text-muted small mt-1\">\n User: <a href=\"#\" data-action=\"view-user\">{{model.user.display_name}}</a>\n </div>\n </div>\n </div>\n\n <!-- Right Side: Status & Actions -->\n <div class=\"d-flex align-items-center gap-4\">\n <div class=\"text-end\">\n <div class=\"text-muted small\">Last Seen</div>\n <div>{{model.last_seen|relative}}</div>\n <div class=\"text-muted small\">from {{model.last_ip}}</div>\n </div>\n <div data-container=\"device-context-menu\"></div>\n </div>\n </div>\n\n <!-- Tab Container -->\n <div data-container=\"device-tabs\"></div>\n </div>\n `;\n }\n\n getIconForDevice(deviceInfo) {\n const os = deviceInfo?.os?.family?.toLowerCase() || '';\n const browser = deviceInfo?.user_agent?.family?.toLowerCase() || '';\n const device = deviceInfo?.device?.family?.toLowerCase() || '';\n\n if (browser.includes('chrome')) return 'bi-browser-chrome';\n if (browser.includes('firefox')) return 'bi-browser-firefox';\n if (browser.includes('safari')) return 'bi-browser-safari';\n if (browser.includes('edge')) return 'bi-browser-edge';\n\n if (os.includes('mac') || os.includes('ios')) return 'bi-apple';\n if (os.includes('windows')) return 'bi-windows';\n if (os.includes('android')) return 'bi-android2';\n if (os.includes('linux')) return 'bi-ubuntu';\n\n if (device.includes('iphone')) return 'bi-phone';\n if (device.includes('ipad')) return 'bi-tablet';\n\n return 'bi-laptop'; // Default icon\n }\n\n async onInit() {\n // Info Tab\n this.infoView = new View({\n model: this.model,\n className: \"p-3\",\n template: `\n <div class=\"list-group\">\n <div class=\"list-group-item\">\n <div class=\"d-flex w-100 justify-content-between\">\n <h6 class=\"mb-1 text-muted\">Browser</h6>\n </div>\n <p class=\"mb-1 fs-5\">{{model.device_info.user_agent.family}} {{model.device_info.user_agent.major}}</p>\n </div>\n <div class=\"list-group-item\">\n <div class=\"d-flex w-100 justify-content-between\">\n <h6 class=\"mb-1 text-muted\">Operating System</h6>\n </div>\n <p class=\"mb-1 fs-5\">{{model.device_info.os.family}} {{model.device_info.os.major}}.{{model.device_info.os.minor}}</p>\n </div>\n <div class=\"list-group-item\">\n <div class=\"d-flex w-100 justify-content-between\">\n <h6 class=\"mb-1 text-muted\">Device</h6>\n </div>\n <p class=\"mb-1 fs-5\">{{model.device_info.device.brand}} {{model.device_info.device.model}}</p>\n </div>\n <div class=\"list-group-item\">\n <div class=\"d-flex w-100 justify-content-between\">\n <h6 class=\"mb-1 text-muted\">Full User Agent</h6>\n </div>\n <small class=\"text-muted\" style=\"word-break: break-all;\">{{model.device_info.string}}</small>\n </div>\n </div>\n `\n });\n\n // Locations Tab\n const locationsCollection = new UserDeviceLocationList({\n params: { user_device: this.model.get('id'), size: 10 }\n });\n this.locationsView = new TableView({\n collection: locationsCollection,\n hideActivePillNames: ['user_device'],\n columns: [\n { key: 'ip_address', label: 'IP Address', sortable: true },\n { key: 'geolocation.city', label: 'City', formatter: \"default('—')\" },\n { key: 'geolocation.region', label: 'Region', formatter: \"default('—')\" },\n { key: 'geolocation.country_name', label: 'Country', formatter: \"default('—')\" },\n { key: 'first_seen', label: 'First Seen', formatter: 'datetime' },\n { key: 'last_seen', label: 'Last Seen', formatter: 'datetime' },\n ]\n });\n\n // TabView\n this.tabView = new TabView({\n tabs: {\n 'Info': this.infoView,\n 'Locations': this.locationsView,\n },\n activeTab: 'Info',\n containerId: 'device-tabs'\n });\n this.addChild(this.tabView);\n\n // ContextMenu\n const deviceMenu = new ContextMenu({\n containerId: 'device-context-menu',\n className: \"context-menu-view header-menu-absolute\",\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: [\n { label: 'View User', action: 'view-user', icon: 'bi-person' },\n { label: 'Block Device', action: 'block-device', icon: 'bi-shield-slash', disabled: true },\n { type: 'divider' },\n { label: 'Delete Record', action: 'delete-device', icon: 'bi-trash', danger: true }\n ]\n }\n });\n this.addChild(deviceMenu);\n }\n\n async onActionViewUser() {\n // This would typically navigate to the user view or open it in a new dialog\n console.log(\"View user:\", this.model.get('user'));\n this.emit('view-user', { userId: this.model.get('user')?.id });\n }\n\n async onActionDeleteDevice() {\n // Placeholder for delete logic\n console.log(\"Delete device:\", this.model.id);\n }\n\n static async show(duid) {\n const model = await UserDevice.getByDuid(duid);\n if (model) {\n const view = new DeviceView({ model });\n const dialog = new Dialog({\n header: false,\n size: 'lg',\n body: view,\n buttons: [{ text: 'Close', class: 'btn-secondary', dismiss: true }]\n });\n await dialog.render(true, document.body);\n dialog.show();\n return dialog;\n }\n Dialog.alert({ message: `Could not find device with DUID: ${duid}`, type: 'warning' });\n return null;\n }\n}\n\nUserDevice.VIEW_CLASS = DeviceView;\nexport default DeviceView;\n","/**\n * UserDeviceTablePage - User device management using TablePage component\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { UserDeviceList } from '@core/models/User.js';\nimport DeviceView from './DeviceView.js';\n\nclass UserDeviceTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_user_devices',\n pageName: 'User Devices',\n router: \"admin/user/devices\",\n Collection: UserDeviceList,\n \n itemViewClass: DeviceView,\n viewDialogOptions: {\n header: false,\n size: 'lg'\n },\n\n // Column definitions\n columns: [\n { key: 'id', label: 'ID', width: '70px', sortable: true, class: 'text-muted' },\n { key: 'duid', label: 'Device ID', sortable: true, formatter: 'truncate_middle(16)' },\n { key: 'user.display_name', label: 'User', sortable: true, formatter: \"default('—')\" },\n { key: 'device_info.user_agent.family', label: 'Browser', formatter: \"default('—')\" },\n { key: 'device_info.os.family', label: 'OS', formatter: \"default('—')\" },\n { key: 'last_ip', label: 'Last IP', sortable: true },\n { key: 'first_seen', label: 'First Seen', formatter: \"epoch|datetime\" },\n { key: 'last_seen', label: 'Last Seen', formatter: \"epoch|datetime\" }\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: false,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No user devices found.',\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n}\n\nexport default UserDeviceTablePage;","/**\n * UserDeviceLocationTablePage - Device location tracking using TablePage component\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { UserDeviceLocationList } from '@core/models/User.js';\n\nclass UserDeviceLocationTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_user_device_locations',\n pageName: 'Device Locations',\n router: \"admin/user/device-locations\",\n Collection: UserDeviceLocationList,\n\n // Column definitions\n columns: [\n { key: 'id', label: 'ID', width: '70px', sortable: true, class: 'text-muted' },\n { key: 'user.display_name', label: 'User', sortable: true },\n { key: 'user_device', label: 'Device', template: '{{user_device.device_info.user_agent.family}} on {{user_device.device_info.os.family}}', sortable: true },\n { key: 'ip_address', label: 'IP Address', sortable: true },\n { key: 'geolocation.city', label: 'City', formatter: \"default('—')\" },\n { key: 'geolocation.region', label: 'Region', formatter: \"default('—')\" },\n { key: 'geolocation.country_name', label: 'Country', formatter: \"default('—')\" },\n { key: 'last_seen', label: 'Last Seen', formatter: \"epoch|datetime\" }\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: false,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No device locations found.',\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n}\n\nexport default UserDeviceLocationTablePage;","/**\n * GeoIPView - Detailed view for a GeoLocatedIP record\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport DataView from '@core/views/data/DataView.js';\nimport TableView from '@core/views/table/TableView.js';\nimport MapView from '@ext/map/MapView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport { GeoLocatedIP } from '@core/models/System.js';\nimport { IncidentEventList } from '@core/models/Incident.js';\nimport { LogList } from '@core/models/Log.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\n\nclass GeoIPView extends View {\n constructor(options = {}) {\n super({\n className: 'geoip-view',\n ...options\n });\n\n this.model = options.model || new GeoLocatedIP(options.data || {});\n this.hasCoordinates = this.model.get('latitude') && this.model.get('longitude');\n\n this.template = `\n <div class=\"geoip-view-container\">\n <!-- Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-4\">\n <!-- Left Side: Icon & Info -->\n <div class=\"d-flex align-items-center gap-3\">\n <div class=\"fs-1 text-primary\">\n <i class=\"bi bi-globe-americas\"></i>\n </div>\n <div>\n <h3 class=\"mb-1\">{{model.ip_address}}</h3>\n <div class=\"text-muted small\">\n {{model.city|default('Unknown Location')}}, {{model.country_name|default('Unknown Location')}}\n </div>\n <div class=\"text-muted small mt-1\">\n ISP: {{model.isp|capitalize}}\n </div>\n </div>\n </div>\n\n <!-- Right Side: Risk Summary + Actions -->\n <div class=\"d-flex align-items-start gap-4\">\n <!-- Risk summary -->\n <div class=\"text-end\">\n <div class=\"d-flex align-items-baseline justify-content-end gap-2\">\n <span class=\"text-muted\">Risk:</span>\n <span class=\"fw-bold fs-4\n {{#model.is_threat}} text-danger {{/model.is_threat}}\n {{#model.is_suspicious}} text-warning {{/model.is_suspicious}}\n {{^model.is_threat}}{{^model.is_suspicious}} text-success {{/model.is_suspicious}}{{/model.is_threat}}\n \">{{#model.threat_level}}{{model.threat_level|capitalize}}{{/model.threat_level}}{{^model.threat_level}}Unknown{{/model.threat_level}}</span>\n </div>\n <div class=\"mt-1 small d-flex align-items-center justify-content-end gap-2\">\n <span class=\"text-muted\">Score:</span>\n <span class=\"fw-semibold\">{{model.risk_score|default('—')}}</span>\n </div>\n <div class=\"mt-1 d-flex align-items-center justify-content-end gap-2\">\n <i class=\"bi bi-shield-lock {{#model.is_tor}}fs-4 text-success{{/model.is_tor}}{{^model.is_tor}}text-muted{{/model.is_tor}}\" data-bs-toggle=\"tooltip\" title=\"TOR exit\"></i>\n <i class=\"bi bi-shield {{#model.is_vpn}}fs-4 text-success{{/model.is_vpn}}{{^model.is_vpn}}text-muted{{/model.is_vpn}}\" data-bs-toggle=\"tooltip\" title=\"VPN detected\"></i>\n <i class=\"bi bi-cloud {{#model.is_cloud}}fs-4 text-success{{/model.is_cloud}}{{^model.is_cloud}}text-muted{{/model.is_cloud}}\" data-bs-toggle=\"tooltip\" title=\"Cloud provider\"></i>\n <i class=\"bi bi-hdd-stack {{#model.is_datacenter}}fs-4 text-success{{/model.is_datacenter}}{{^model.is_datacenter}}text-muted{{/model.is_datacenter}}\" data-bs-toggle=\"tooltip\" title=\"Datacenter\"></i>\n <i class=\"bi bi-phone {{#model.is_mobile}}fs-4 text-success{{/model.is_mobile}}{{^model.is_mobile}}text-muted{{/model.is_mobile}}\" data-bs-toggle=\"tooltip\" title=\"Mobile connection\"></i>\n <i class=\"bi bi-diagram-3 {{#model.is_proxy}}fs-4 text-success{{/model.is_proxy}}{{^model.is_proxy}}text-muted{{/model.is_proxy}}\" data-bs-toggle=\"tooltip\" title=\"Proxy\"></i>\n </div>\n </div>\n <!-- Actions: context menu aligned to top (not vertically centered) -->\n <div class=\"d-flex align-items-start\">\n <div data-container=\"geoip-context-menu\"></div>\n </div>\n </div>\n </div>\n\n <!-- Tabs -->\n <div data-container=\"geoip-tabs\"></div>\n </div>\n `;\n }\n\n async onInit() {\n // Location Details Tab\n this.detailsView = new DataView({\n model: this.model,\n className: \"p-3\",\n showEmptyValues: true,\n emptyValueText: '—',\n columns: 2,\n fields: [\n { name: 'ip_address', label: 'IP Address', cols: 4 },\n { name: 'subnet', label: 'Subnet', cols: 4 },\n { name: 'country_name', label: 'Country', cols: 4 },\n { name: 'country_code', label: 'Country Code', cols: 4 },\n { name: 'region', label: 'Region', cols: 4 },\n { name: 'city', label: 'City', cols: 4 },\n { name: 'postal_code', label: 'Postal Code', cols: 4 },\n { name: 'timezone', label: 'Timezone', cols: 4 },\n { name: 'latitude', label: 'Latitude', cols: 4 },\n { name: 'longitude', label: 'Longitude', cols: 4 },\n ]\n });\n\n // Network Tab\n this.networkView = new DataView({\n model: this.model,\n className: \"p-3\",\n showEmptyValues: true,\n emptyValueText: '—',\n columns: 2,\n fields: [\n { name: 'is_tor', label: 'TOR Exit Node', formatter: 'yesnoicon', cols: 4 },\n { name: 'is_vpn', label: 'VPN', formatter: 'yesnoicon', cols: 4 },\n { name: 'is_proxy', label: 'Proxy', formatter: 'yesnoicon', cols: 4 },\n { name: 'is_cloud', label: 'Cloud Provider', formatter: 'yesnoicon', cols: 4 },\n { name: 'is_datacenter', label: 'Datacenter', formatter: 'yesnoicon', cols: 4 },\n { name: 'is_mobile', label: 'Mobile', formatter: 'yesnoicon', cols: 4 },\n { name: 'mobile_carrier', label: 'Mobile Carrier', cols: 8 },\n { name: 'asn', label: 'ASN', cols: 4 },\n { name: 'asn_org', label: 'ASN Organization', cols: 8 },\n { name: 'isp', label: 'ISP', cols: 12 },\n { name: 'connection_type', label: 'Connection Type', cols: 6 }\n ]\n });\n\n // Risk & Reputation Tab\n this.riskView = new DataView({\n model: this.model,\n className: \"p-3\",\n showEmptyValues: true,\n emptyValueText: '—',\n columns: 2,\n fields: [\n { name: 'threat_level', label: 'Threat Level', cols: 6 },\n { name: 'risk_score', label: 'Risk Score', cols: 6 },\n { name: 'is_threat', label: 'Threat', formatter: 'yesnoicon', cols: 6 },\n { name: 'is_suspicious', label: 'Suspicious', formatter: 'yesnoicon', cols: 6 },\n { name: 'is_known_attacker', label: 'Known Attacker', formatter: 'yesnoicon', cols: 6 },\n { name: 'is_known_abuser', label: 'Known Abuser', formatter: 'yesnoicon', cols: 6 }\n ]\n });\n\n // Metadata Tab\n this.metadataView = new DataView({\n model: this.model,\n className: \"p-3\",\n showEmptyValues: true,\n emptyValueText: '—',\n columns: 2,\n fields: [\n { name: 'id', label: 'Record ID', cols: 6 },\n { name: 'provider', label: 'Data Provider', formatter: 'capitalize', cols: 6 },\n { name: 'created', label: 'Created', formatter: 'datetime', cols: 6 },\n { name: 'modified', label: 'Last Modified', formatter: 'datetime', cols: 6 },\n { name: 'last_seen', label: 'Last Seen', formatter: 'datetime', cols: 6 },\n { name: 'expires_at', label: 'Expires', formatter: 'datetime', cols: 6 }\n ]\n });\n\n // Create Events table with IncidentEventList collection\n const eventsCollection = new IncidentEventList({\n params: {\n size: 5,\n source_ip: this.model.get(\"ip_address\")\n }\n });\n this.eventsView = new TableView({\n collection: eventsCollection,\n hideActivePillNames: ['source_ip'],\n columns: [\n { key: 'id', label: 'ID', sortable: true, width: '40px' },\n { key: 'created', label: 'Date', formatter: 'datetime', sortable: true, width: '150px' },\n { key: 'category|badge', label: 'Category' },\n { key: 'title', label: 'Title' }\n ]\n });\n\n // Create Logs table with LogList collection\n const logsCollection = new LogList({\n params: {\n size: 5,\n ip: this.model.get('ip_address')\n }\n });\n this.logsView = new TableView({\n collection: logsCollection,\n permissions: 'view_logs',\n hideActivePillNames: ['ip'],\n columns: [\n {\n key: 'created',\n label: 'Timestamp',\n sortable: true,\n formatter: \"epoch|datetime\",\n filter: {\n name: \"created\",\n type: 'daterange',\n startName: 'dr_start',\n endName: 'dr_end',\n fieldName: 'dr_field',\n label: 'Date Range',\n format: 'YYYY-MM-DD',\n displayFormat: 'MMM DD, YYYY',\n separator: ' to '\n }\n },\n {\n key: 'level',\n label: 'Level',\n sortable: true,\n filter: {\n type: 'select',\n options: [\n { value: 'info', label: 'Info' },\n { value: 'warning', label: 'Warning' },\n { value: 'error', label: 'Error' }\n ]\n }\n },\n { key: 'kind', label: 'Kind', filter: { type: 'text' } },\n { name: 'log', label: 'Log' }\n ]\n });\n\n const tabs = {\n 'Location': this.detailsView,\n 'Network': this.networkView,\n 'Risk & Reputation': this.riskView,\n 'Events': this.eventsView,\n 'Logs': this.logsView,\n 'Metadata': this.metadataView\n };\n\n // Add Map tab if coordinates exist\n if (this.hasCoordinates) {\n const lat = this.model.get('latitude');\n const lng = this.model.get('longitude');\n const city = this.model.get('city') || 'Unknown';\n const region = this.model.get('region') || '';\n const country = this.model.get('country_name') || '';\n\n const locationStr = [city, region, country].filter(Boolean).join(', ');\n\n this.mapView = new MapView({\n markers: [{\n lat: lat,\n lng: lng,\n popup: `<strong>${this.model.get('ip_address')}</strong><br>${locationStr}`\n }],\n tileLayer: \"light\",\n zoom: 4,\n height: 450\n });\n tabs['Map'] = this.mapView;\n }\n\n this.tabView = new TabView({\n containerId: 'geoip-tabs',\n tabs: tabs,\n activeTab: this.hasCoordinates ? 'Map' : 'Location'\n });\n this.addChild(this.tabView);\n\n // ContextMenu\n const menuItems = [\n { label: 'Edit Location', action: 'edit-location', icon: 'bi-geo-alt' },\n { label: 'Edit Security', action: 'edit-security', icon: 'bi-shield-lock' },\n { label: 'Edit Network', action: 'edit-network', icon: 'bi-diagram-3' },\n { type: 'divider' },\n { label: 'Refresh Geolocation', action: 'refresh-geoip', icon: 'bi-arrow-clockwise' },\n ];\n\n if (this.hasCoordinates) {\n menuItems.push({\n label: 'View on Map',\n action: 'view-on-map',\n icon: 'bi-map'\n });\n }\n\n menuItems.push(\n { type: 'divider' },\n { label: 'Delete Record', action: 'delete-geoip', icon: 'bi-trash', danger: true }\n );\n\n const geoIPMenu = new ContextMenu({\n containerId: 'geoip-context-menu',\n className: \"context-menu-view header-menu-absolute\",\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: menuItems\n }\n });\n this.addChild(geoIPMenu);\n }\n\n async onAfterRender() {\n await super.onAfterRender();\n\n // Initialize Bootstrap tooltips for header icons/badges\n if (window.bootstrap && window.bootstrap.Tooltip && this.element) {\n const tooltipTriggerList = this.element.querySelectorAll('[data-bs-toggle=\"tooltip\"]');\n tooltipTriggerList.forEach(el => {\n // Dispose any existing instance (in case of re-render)\n const existing = window.bootstrap.Tooltip.getInstance(el);\n if (existing && typeof existing.dispose === 'function') {\n existing.dispose();\n }\n new window.bootstrap.Tooltip(el);\n });\n }\n }\n\n async onActionEditLocation() {\n const resp = await Dialog.showModelForm({\n title: `Edit Location - ${this.model.get('ip_address')}`,\n model: this.model,\n formConfig: GeoLocatedIP.EDIT_LOCATION_FORM,\n });\n\n if (resp) {\n await this.render();\n this.getApp()?.toast?.success('Location updated successfully');\n }\n }\n\n async onActionEditSecurity() {\n const resp = await Dialog.showModelForm({\n title: `Edit Security - ${this.model.get('ip_address')}`,\n model: this.model,\n formConfig: GeoLocatedIP.EDIT_SECURITY_FORM,\n });\n\n if (resp) {\n await this.render();\n this.getApp()?.toast?.success('Security settings updated successfully');\n }\n }\n\n async onActionEditNetwork() {\n const resp = await Dialog.showModelForm({\n title: `Edit Network - ${this.model.get('ip_address')}`,\n model: this.model,\n formConfig: GeoLocatedIP.EDIT_NETWORK_FORM,\n });\n\n if (resp) {\n await this.render();\n this.getApp()?.toast?.success('Network information updated successfully');\n }\n }\n\n async onActionRefreshGeoip() {\n // Placeholder for refresh logic, e.g., a POST request to a refresh endpoint\n await this.model.save({ refresh: true });\n this.getApp()?.toast?.info('Refresh request sent for ' + this.model.get('ip_address'));\n }\n\n async onActionThreatAnalysis() {\n // Placeholder for refresh logic, e.g., a POST request to a refresh endpoint\n await this.model.save({ threat_analysis: true });\n this.getApp()?.toast?.info('Requesting threat analysis for ' + this.model.get('ip_address'));\n }\n\n async onActionViewOnMap() {\n if (this.hasCoordinates) {\n const lat = this.model.get('latitude');\n const lon = this.model.get('longitude');\n const url = `https://www.google.com/maps/search/?api=1&query=${lat},${lon}`;\n window.open(url, '_blank');\n }\n }\n\n async onActionDeleteGeoip() {\n const confirmed = await Dialog.confirm(\n `Are you sure you want to delete the GeoIP record for \"${this.model.get('ip_address')}\"?`,\n 'Confirm Deletion',\n { confirmClass: 'btn-danger', confirmText: 'Delete' }\n );\n if (confirmed) {\n const resp = await this.model.destroy();\n if (resp.success) {\n this.emit('geoip:deleted', { model: this.model });\n }\n }\n }\n\n static async show(ip) {\n const model = await GeoLocatedIP.lookup(ip);\n if (model) {\n const view = new GeoIPView({ model });\n const dialog = new Dialog({\n header: false,\n size: 'lg',\n body: view,\n buttons: [{ text: 'Close', class: 'btn-secondary', dismiss: true }]\n });\n await dialog.render(true, document.body);\n dialog.show();\n return dialog;\n }\n Dialog.alert({ message: `Could not find geolocation data for IP: ${ip}`, type: 'warning' });\n return null;\n }\n}\n\nGeoLocatedIP.VIEW_CLASS = GeoIPView;\n\nexport default GeoIPView;\n","/**\n * GeoLocatedIPTablePage - GeoIP cache management using TablePage component\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { GeoLocatedIPList, GeoLocatedIP } from '@core/models/System.js';\nimport GeoIPView from './GeoIPView.js';\n\nclass GeoLocatedIPTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_system_geoip',\n pageName: 'GeoIP Cache',\n router: \"admin/system/geoip\",\n Collection: GeoLocatedIPList,\n\n itemView: GeoIPView,\n viewDialogOptions: {\n header: false,\n size: 'xl'\n },\n\n // Column definitions\n columns: [\n { key: 'ip_address', label: 'IP Address', sortable: true },\n { key: 'city', label: 'City', sortable: true, formatter: \"default('—')\" },\n { key: 'region', label: 'Region', sortable: true, formatter: \"default('—')\" },\n { key: 'country_name', label: 'Country', sortable: true, formatter: \"default('—')\" },\n { key: 'isp', label: 'ISP', sortable: true, formatter: \"default('—')\" },\n { key: 'threat_level', label: 'Threat', formatter: \"default('—')\"}\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Actions\n // actions: ['view', 'edit', 'delete'],\n clickAction: 'view',\n\n // Toolbar\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No GeoIP records found.',\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n },\n tableViewOptions: {\n addButtonLabel: \"Lookup IP\",\n onAdd: (evt) => {\n evt.preventDefault();\n // Implement the logic for adding a new record\n this.onLookup();\n }\n }\n });\n }\n\n async onLookup() {\n // Implement the logic for adding a new record\n const data = await this.getApp().showForm({\n title: \"Lookup IP\",\n fields: [\n {\n name: 'ip',\n type: 'text',\n required: true\n }\n ]\n });\n if (data && data.ip) {\n const model = await GeoLocatedIP.lookup(data.ip);\n if (model) {\n this.tableView._onRowView({ model });\n }\n }\n }\n}\n\nexport default GeoLocatedIPTablePage;\n","/**\n * MapLibreView - Interactive vector map component using MapLibre GL JS\n * \n * MapLibre GL is better for:\n * - Vector tiles and custom styling\n * - 3D terrain and buildings\n * - High-performance rendering\n * - Heat maps and data visualizations\n * - Complex animations\n * \n * Use MapView (Leaflet) for simple marker maps.\n * Use MapLibreView for advanced visualizations.\n * \n * @example\n * const mapView = new MapLibreView({\n * markers: [\n * { lng: -122.084, lat: 37.422, popup: 'Mountain View, CA' }\n * ],\n * style: 'streets', // 'streets', 'satellite', 'dark', 'light'\n * zoom: 10,\n * height: 400\n * });\n */\n\nimport View from '@core/View.js';\n\nclass MapLibreView extends View {\n constructor(options = {}) {\n super({\n className: 'maplibre-view',\n ...options\n });\n\n this.markers = options.markers || [];\n this.center = options.center || null;\n this.zoom = options.zoom || 13;\n this.height = options.height || 400;\n this.pitch = options.pitch || 0; // 0-60 degrees (3D tilt)\n this.bearing = options.bearing || 0; // 0-360 degrees (rotation)\n this.mapStyle = options.style || 'streets'; // 'streets', 'satellite', 'dark', 'light', 'terrain'\n this.showNavigationControl = options.showNavigationControl !== false;\n this.autoFitBounds = options.autoFitBounds !== false;\n \n this.map = null;\n this.mapMarkers = [];\n this.pendingMarkers = [...this.markers];\n this.lineSources = options.lineSources || [];\n this.pendingLineData = new Map();\n\n this.template = `\n <div class=\"maplibre-container\">\n <div id=\"maplibre-{{id}}\" style=\"height: {{height}}px; width: 100%; border-radius: 0.375rem; border: 1px solid #dee2e6;\"></div>\n </div>\n `;\n }\n\n async onAfterRender() {\n await this.loadMapLibre();\n await this.initializeMap();\n }\n\n async loadMapLibre() {\n // Check if MapLibre is already loaded\n if (window.maplibregl) return;\n\n // Load MapLibre CSS\n const link = document.createElement('link');\n link.rel = 'stylesheet';\n link.href = 'https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.css';\n document.head.appendChild(link);\n\n // Load MapLibre JS\n return new Promise((resolve, reject) => {\n const script = document.createElement('script');\n script.src = 'https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.js';\n script.onload = resolve;\n script.onerror = reject;\n document.head.appendChild(script);\n });\n }\n\n getMapStyle() {\n const styles = {\n streets: 'https://demotiles.maplibre.org/style.json',\n dark: {\n version: 8,\n sources: {\n osm: {\n type: 'raster',\n tiles: ['https://a.tile.openstreetmap.org/{z}/{x}/{y}.png'],\n tileSize: 256,\n attribution: '© OpenStreetMap contributors'\n }\n },\n layers: [{\n id: 'osm',\n type: 'raster',\n source: 'osm'\n }],\n glyphs: 'https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf'\n },\n light: {\n version: 8,\n sources: {\n osm: {\n type: 'raster',\n tiles: ['https://a.tile.openstreetmap.org/{z}/{x}/{y}.png'],\n tileSize: 256,\n attribution: '© OpenStreetMap contributors'\n }\n },\n layers: [{\n id: 'osm',\n type: 'raster',\n source: 'osm'\n }],\n glyphs: 'https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf'\n },\n satellite: {\n version: 8,\n sources: {\n satellite: {\n type: 'raster',\n tiles: ['https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}'],\n tileSize: 256,\n attribution: '© Esri'\n }\n },\n layers: [{\n id: 'satellite',\n type: 'raster',\n source: 'satellite'\n }],\n glyphs: 'https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf'\n },\n terrain: {\n version: 8,\n sources: {\n terrain: {\n type: 'raster',\n tiles: ['https://a.tile.opentopomap.org/{z}/{x}/{y}.png'],\n tileSize: 256,\n attribution: '© OpenTopoMap contributors'\n }\n },\n layers: [{\n id: 'terrain',\n type: 'raster',\n source: 'terrain'\n }],\n glyphs: 'https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf'\n }\n };\n\n return styles[this.mapStyle] || styles.streets;\n }\n\n async initializeMap() {\n const mapElement = this.element.querySelector(`#maplibre-${this.id}`);\n if (!mapElement || !window.maplibregl) return;\n\n // Determine map center\n let mapCenter = this.center;\n if (!mapCenter && this.markers.length > 0) {\n // Use first marker as center (MapLibre uses [lng, lat])\n mapCenter = [this.markers[0].lng, this.markers[0].lat];\n }\n if (!mapCenter) {\n // Default to world view\n mapCenter = [0, 0];\n }\n\n // Create map\n this.map = new window.maplibregl.Map({\n container: mapElement,\n style: this.getMapStyle(),\n center: mapCenter,\n zoom: this.zoom,\n pitch: this.pitch,\n bearing: this.bearing\n });\n\n // Add navigation control\n if (this.showNavigationControl) {\n this.map.addControl(new window.maplibregl.NavigationControl(), 'top-right');\n }\n\n // Wait for map to load before adding markers\n this.map.on('load', () => {\n const initialMarkers = this.pendingMarkers.length ? this.pendingMarkers : this.markers;\n this.updateMarkers(initialMarkers);\n this.lineSources.forEach(source => this.addLineSource(source));\n this.pendingLineData.forEach(source => this.addLineSource(source));\n this.pendingLineData.clear();\n });\n }\n\n addMarkers(markers) {\n if (!this.map || !Array.isArray(markers)) {\n this.pendingMarkers = Array.isArray(markers) ? markers : [];\n return;\n }\n\n markers.forEach(markerData => {\n const { lng, lat, popup, color, icon, size } = markerData;\n \n if (!lng || !lat) return;\n\n // Create marker element\n const el = document.createElement('div');\n el.className = 'maplibre-marker';\n const markerSize = Number(size) || 30;\n el.style.width = `${markerSize}px`;\n el.style.height = `${markerSize}px`;\n el.style.borderRadius = '50%';\n el.style.backgroundColor = color || '#3b82f6';\n el.style.border = '3px solid white';\n el.style.boxShadow = '0 2px 4px rgba(0,0,0,0.3)';\n el.style.cursor = 'pointer';\n el.style.display = 'flex';\n el.style.alignItems = 'center';\n el.style.justifyContent = 'center';\n\n // Optional icon\n if (icon) {\n el.innerHTML = `<i class=\"${icon}\" style=\"color: white;\"></i>`;\n }\n\n // Create marker\n const marker = new window.maplibregl.Marker({ element: el })\n .setLngLat([lng, lat])\n .addTo(this.map);\n\n // Add popup if provided\n if (popup) {\n const mapPopup = new window.maplibregl.Popup({ offset: 25 })\n .setHTML(popup);\n marker.setPopup(mapPopup);\n }\n\n this.mapMarkers.push(marker);\n });\n }\n\n fitBounds() {\n if (!this.map || this.markers.length === 0) return;\n\n const bounds = new window.maplibregl.LngLatBounds();\n \n this.markers.forEach(marker => {\n bounds.extend([marker.lng, marker.lat]);\n });\n\n this.map.fitBounds(bounds, {\n padding: 50,\n maxZoom: 15\n });\n }\n\n updateMarkers(newMarkers) {\n const markers = Array.isArray(newMarkers) ? newMarkers : [];\n this.markers = markers;\n this.pendingMarkers = markers;\n if (!this.map) return;\n\n this.clearMarkers();\n this.addMarkers(markers);\n\n if (this.autoFitBounds && markers.length > 1) {\n this.fitBounds();\n } else if (markers.length === 1 && this.map) {\n this.map.flyTo({\n center: [markers[0].lng, markers[0].lat],\n zoom: this.zoom\n });\n }\n }\n\n clearMarkers() {\n this.mapMarkers.forEach(marker => {\n marker.remove();\n });\n this.mapMarkers = [];\n }\n\n addLineSource({ id, data, paint, layout }) {\n if (!id || !data) return;\n\n const sourceId = `${id}-source`;\n\n if (!this.map) {\n this.pendingLineData.set(id, { id, data, paint, layout });\n return;\n }\n\n if (this.map.getLayer(id)) {\n this.map.removeLayer(id);\n }\n if (this.map.getSource(sourceId)) {\n this.map.removeSource(sourceId);\n }\n\n this.map.addSource(sourceId, {\n type: 'geojson',\n data\n });\n\n this.map.addLayer({\n id,\n type: 'line',\n source: sourceId,\n paint: {\n 'line-color': '#3b82f6',\n 'line-width': 2,\n 'line-opacity': 0.6,\n ...paint\n },\n layout: {\n 'line-cap': 'round',\n 'line-join': 'round',\n ...layout\n }\n });\n }\n\n updateLineSource(id, { data, paint, layout } = {}) {\n if (!id || !data) return;\n const sourceId = `${id}-source`;\n if (!this.map) {\n this.pendingLineData.set(id, { id, data, paint, layout });\n return;\n }\n if (this.map.getSource(sourceId)) {\n this.map.getSource(sourceId).setData(data);\n if (paint || layout) {\n if (paint) {\n Object.entries(paint).forEach(([key, value]) => {\n this.map.setPaintProperty(id, key, value);\n });\n }\n if (layout) {\n Object.entries(layout).forEach(([key, value]) => {\n this.map.setLayoutProperty(id, key, value);\n });\n }\n }\n } else {\n this.addLineSource({ id, data, paint, layout });\n }\n }\n\n setView(lng, lat, zoom = null) {\n if (!this.map) return;\n this.map.flyTo({\n center: [lng, lat],\n zoom: zoom || this.zoom\n });\n }\n\n setZoom(zoom) {\n if (!this.map) return;\n this.map.setZoom(zoom);\n }\n\n setPitch(pitch) {\n if (!this.map) return;\n this.map.setPitch(pitch);\n }\n\n setBearing(bearing) {\n if (!this.map) return;\n this.map.setBearing(bearing);\n }\n\n async onBeforeDestroy() {\n if (this.map) {\n this.map.remove();\n this.map = null;\n }\n await super.onBeforeDestroy();\n }\n}\n\nexport default MapLibreView;\n","// Auto-generated from countries.geojson - do not edit manually.\nexport const COUNTRY_CENTROIDS = {\n \"AD\": { name: \"Andorra\", lat: 42.54859834854764, lng: 1.5802243611232873 },\n \"AE\": { name: \"United Arab Emirates\", lat: 24.18250292309135, lng: 54.27920525789581 },\n \"AF\": { name: \"Afghanistan\", lat: 34.13402601376932, lng: 66.59216131095278 },\n \"AG\": { name: \"Antigua and Barbuda\", lat: 17.07146759372967, lng: -61.78530823226373 },\n \"AI\": { name: \"Anguilla\", lat: 18.222874004219086, lng: -63.06008343771806 },\n \"AL\": { name: \"Albania\", lat: 41.14165894891656, lng: 20.061082767269493 },\n \"AM\": { name: \"Armenia\", lat: 40.17841274230679, lng: 45.05490831965259 },\n \"AO\": { name: \"Angola\", lat: -12.167424062667942, lng: 17.651768783079 },\n \"AQ\": { name: \"Antarctica\", lat: -77.16987521415838, lng: -177.56451613408842 },\n \"AR\": { name: \"Argentina\", lat: -35.697270518120085, lng: -64.53238503843076 },\n \"AS\": { name: \"American Samoa\", lat: -14.305711987770538, lng: -170.7007316174498 },\n \"AT\": { name: \"Austria\", lat: 47.631858269895794, lng: 13.797778364631036 },\n \"AU\": { name: \"Australia\", lat: -25.697337673983082, lng: 134.02277170916162 },\n \"AW\": { name: \"Aruba\", lat: 12.515625722992898, lng: -69.97564014284046 },\n \"AZ\": { name: \"Azerbaijan\", lat: 40.3920509942049, lng: 48.634592670644324 },\n \"BA\": { name: \"Bosnia and Herzegovina\", lat: 44.14415356126429, lng: 17.83467240787538 },\n \"BB\": { name: \"Barbados\", lat: 13.183219369337529, lng: -59.557383949150285 },\n \"BD\": { name: \"Bangladesh\", lat: 23.673728665121, lng: 90.43212562608613 },\n \"BE\": { name: \"Belgium\", lat: 50.6182138854095, lng: 4.675010154696485 },\n \"BF\": { name: \"Burkina Faso\", lat: 12.108709036312737, lng: -1.6932816211842325 },\n \"BG\": { name: \"Bulgaria\", lat: 42.82043677302438, lng: 25.251739122561908 },\n \"BH\": { name: \"Bahrain\", lat: 26.04798501537066, lng: 50.540695402276775 },\n \"BI\": { name: \"Burundi\", lat: -3.261251993278643, lng: 29.88518227845293 },\n \"BJ\": { name: \"Benin\", lat: 9.503013199615893, lng: 2.305714528830206 },\n \"BL\": { name: \"Saint Barthelemy\", lat: 17.90561691241738, lng: -62.83051610005156 },\n \"BM\": { name: \"Bermuda\", lat: 32.315067430740726, lng: -64.7458500599169 },\n \"BN\": { name: \"Brunei Darussalam\", lat: 4.543205889917609, lng: 114.6430958360464 },\n \"BO\": { name: \"Bolivia\", lat: -16.7312488393574, lng: -64.45209597511206 },\n \"BQ\": { name: \"Bonaire\", lat: 12.180844982440338, lng: -68.29350445958761 },\n \"BQ\": { name: \"Saba\", lat: 17.632512616389718, lng: -63.23739481909494 },\n \"BQ\": { name: \"Saint Eustatius\", lat: 17.4919042294197, lng: -62.978230589445026 },\n \"BR\": { name: \"Brazil\", lat: -11.524630416426652, lng: -54.355206608256424 },\n \"BS\": { name: \"Bahamas\", lat: 24.72162633646784, lng: -78.07275370060313 },\n \"BT\": { name: \"Bhutan\", lat: 27.42163933959824, lng: 90.46716647173861 },\n \"BV\": { name: \"Bouvet Island\", lat: -54.42316679395248, lng: 3.411969465057627 },\n \"BW\": { name: \"Botswana\", lat: -22.236609002062902, lng: 23.85779956995608 },\n \"BY\": { name: \"Belarus\", lat: 53.46791374543163, lng: 27.964252054715104 },\n \"BZ\": { name: \"Belize\", lat: 17.24252476647155, lng: -88.68273510023441 },\n \"CA\": { name: \"Canada\", lat: 57.550480044655636, lng: -98.41680517868062 },\n \"CC\": { name: \"Cocos Islands\", lat: -12.171249450199545, lng: 96.83688767323002 },\n \"CD\": { name: \"Congo DRC\", lat: -3.338629596207896, lng: 23.419827574282188 },\n \"CF\": { name: \"Central African Republic\", lat: 6.331390033944319, lng: 20.520743419397256 },\n \"CG\": { name: \"Congo\", lat: -0.7294391595233845, lng: 14.879732849491393 },\n \"CH\": { name: \"Switzerland\", lat: 46.73678128684938, lng: 8.286928794895285 },\n \"CI\": { name: \"Côte d'Ivoire\", lat: 7.536779279421307, lng: -5.571710194917734 },\n \"CK\": { name: \"Cook Islands\", lat: -21.222613253399842, lng: -159.78768870952257 },\n \"CL\": { name: \"Chile\", lat: -37.82938283049967, lng: -70.76863431739216 },\n \"CM\": { name: \"Cameroon\", lat: 6.294168487480992, lng: 12.948474142398263 },\n \"CN\": { name: \"China\", lat: 38.07325481105728, lng: 104.69113855849604 },\n \"CO\": { name: \"Colombia\", lat: 4.187753877352739, lng: -72.6445066243485 },\n \"CR\": { name: \"Costa Rica\", lat: 9.863467407406214, lng: -84.14673625701816 },\n \"CU\": { name: \"Cuba\", lat: 21.476176522869448, lng: -79.69817857618705 },\n \"CV\": { name: \"Cabo Verde\", lat: 15.076411518651643, lng: -23.63401005900474 },\n \"CW\": { name: \"Curacao\", lat: 12.199996647939832, lng: -68.96939768599042 },\n \"CX\": { name: \"Christmas Island\", lat: -10.446440802183416, lng: 105.70209512200549 },\n \"CY\": { name: \"Cyprus\", lat: 35.11700416345239, lng: 33.375346009199205 },\n \"CZ\": { name: \"Czech Republic\", lat: 49.74917370930982, lng: 15.383273292023533 },\n \"DE\": { name: \"Germany\", lat: 51.08304539800482, lng: 10.426171427430804 },\n \"DJ\": { name: \"Djibouti\", lat: 11.750235727618804, lng: 42.613496898789506 },\n \"DK\": { name: \"Denmark\", lat: 56.00118817971057, lng: 9.378670542409406 },\n \"DM\": { name: \"Dominica\", lat: 15.429269860940513, lng: -61.360471946942994 },\n \"DO\": { name: \"Dominican Republic\", lat: 18.77954818522993, lng: -70.43495198520012 },\n \"DZ\": { name: \"Algeria\", lat: 28.350969744889056, lng: 2.6558464719769135 },\n \"EC\": { name: \"Ecuador\", lat: -1.5642721388853116, lng: -78.4630326109714 },\n \"EE\": { name: \"Estonia\", lat: 58.648108311231034, lng: 25.916870250633806 },\n \"EG\": { name: \"Egypt\", lat: 26.60517034450628, lng: 30.240135435012338 },\n \"ER\": { name: \"Eritrea\", lat: 15.005533147667684, lng: 39.2672401449901 },\n \"ES\": { name: \"Spain\", lat: 40.365008336683836, lng: -3.6516251409956983 },\n \"ES\": { name: \"Canarias\", lat: 28.297665106525546, lng: -16.53799441021647 },\n \"ET\": { name: \"Ethiopia\", lat: 8.729389557048396, lng: 39.914902886544276 },\n \"FI\": { name: \"Finland\", lat: 65.01578959749911, lng: 25.65738433454702 },\n \"FJ\": { name: \"Fiji\", lat: -17.822470952336204, lng: 177.98144613732626 },\n \"FK\": { name: \"Falkland Islands\", lat: -51.75901312766726, lng: -58.746646363799854 },\n \"FM\": { name: \"Micronesia\", lat: 6.8789448129255435, lng: 158.2291899444527 },\n \"FO\": { name: \"Faroe Islands\", lat: 62.130896281495346, lng: -6.9811060913122835 },\n \"FR\": { name: \"France\", lat: 46.6423682169416, lng: 2.1940236627886227 },\n \"GA\": { name: \"Gabon\", lat: -0.628448459921234, lng: 11.839410898545754 },\n \"GB\": { name: \"United Kingdom\", lat: 53.97844735080214, lng: -2.852943909329258 },\n \"GD\": { name: \"Grenada\", lat: 12.112926656338907, lng: -61.67937937204098 },\n \"GE\": { name: \"Georgia\", lat: 42.17986277737226, lng: 43.378866534112234 },\n \"GF\": { name: \"French Guiana\", lat: 3.857429742497078, lng: -53.32232307156624 },\n \"GG\": { name: \"Guernsey\", lat: 49.45870771378872, lng: -2.576392582891568 },\n \"GH\": { name: \"Ghana\", lat: 7.94530467243628, lng: -1.219233362526581 },\n \"GI\": { name: \"Gibraltar\", lat: 36.14022671336082, lng: -5.345549484594358 },\n \"GL\": { name: \"Greenland\", lat: 74.16847218965994, lng: -42.07567788066985 },\n \"GM\": { name: \"Gambia\", lat: 13.428617959189328, lng: -15.383380385869662 },\n \"GN\": { name: \"Guinea\", lat: 10.255986541378112, lng: -10.986948848040218 },\n \"GP\": { name: \"Guadeloupe\", lat: 16.24420002705553, lng: -61.54382262282755 },\n \"GQ\": { name: \"Equatorial Guinea\", lat: 1.5954643936590733, lng: 10.425456672353823 },\n \"GR\": { name: \"Greece\", lat: 39.42012261727978, lng: 23.110368936161876 },\n \"GS\": { name: \"South Georgia and South Sandwich Islands\", lat: -54.37666443862139, lng: -36.77509575898928 },\n \"GT\": { name: \"Guatemala\", lat: 15.820878515352684, lng: -90.31219349119617 },\n \"GU\": { name: \"Guam\", lat: 13.445430479945276, lng: 144.78024458298802 },\n \"GW\": { name: \"Guinea-Bissau\", lat: 11.980075324820504, lng: -14.980186756910847 },\n \"GY\": { name: \"Guyana\", lat: 4.68211981385056, lng: -58.91352612754667 },\n \"HM\": { name: \"Heard Island and McDonald Islands\", lat: -53.084170035513736, lng: 73.49298560844045 },\n \"HN\": { name: \"Honduras\", lat: 14.740370695750006, lng: -86.49251679006962 },\n \"HR\": { name: \"Croatia\", lat: 44.91192100856702, lng: 16.625761129583374 },\n \"HT\": { name: \"Haiti\", lat: 18.883520486983567, lng: -72.89291379842 },\n \"HU\": { name: \"Hungary\", lat: 47.22527332486294, lng: 19.39620048366142 },\n \"ID\": { name: \"Indonesia\", lat: 0.15591979959037652, lng: 113.96538246847302 },\n \"IE\": { name: \"Ireland\", lat: 53.30489539816495, lng: -8.241128545554096 },\n \"IL\": { name: \"Israel\", lat: 31.513542220043195, lng: 35.027923472437024 },\n \"IM\": { name: \"Isle of Man\", lat: 54.22855301008011, lng: -4.532995055468449 },\n \"IN\": { name: \"India\", lat: 23.586300567746722, lng: 81.17300408530181 },\n \"IO\": { name: \"British Indian Ocean Territory\", lat: -7.323548444385743, lng: 72.43501618476016 },\n \"IQ\": { name: \"Iraq\", lat: 33.105075667527906, lng: 43.832529181056884 },\n \"IR\": { name: \"Iran\", lat: 32.906023742890056, lng: 54.237077001065444 },\n \"IS\": { name: \"Iceland\", lat: 65.12360920205514, lng: -19.05682967106099 },\n \"IT\": { name: \"Italy\", lat: 42.98201127614267, lng: 12.763657166123137 },\n \"JE\": { name: \"Jersey\", lat: 49.215396925724306, lng: -2.1291601162653575 },\n \"JM\": { name: \"Jamaica\", lat: 18.12207889341651, lng: -77.30358894542778 },\n \"JO\": { name: \"Jordan\", lat: 31.387064884449156, lng: 36.95728884547246 },\n \"JP\": { name: \"Japan\", lat: 36.76738832597829, lng: 137.46934234351835 },\n \"KE\": { name: \"Kenya\", lat: 0.6899182318376179, lng: 37.95309411262371 },\n \"KG\": { name: \"Kyrgyzstan\", lat: 41.35698905438358, lng: 74.17532903468319 },\n \"KH\": { name: \"Cambodia\", lat: 12.699186865507775, lng: 105.03973078680701 },\n \"KI\": { name: \"Kiribati\", lat: 1.8676673749241066, lng: -157.39024189323504 },\n \"KM\": { name: \"Comoros\", lat: -11.658861474508491, lng: 43.34826587429403 },\n \"KN\": { name: \"Saint Kitts and Nevis\", lat: 17.314736299587768, lng: -62.74560385895787 },\n \"KP\": { name: \"North Korea\", lat: 40.19198091470839, lng: 127.3379805653744 },\n \"KR\": { name: \"South Korea\", lat: 36.402386712544114, lng: 127.76224551357649 },\n \"KW\": { name: \"Kuwait\", lat: 29.281360965443092, lng: 47.56311109320184 },\n \"KY\": { name: \"Cayman Islands\", lat: 19.311231805620288, lng: -81.25203208977878 },\n \"KZ\": { name: \"Kazakhstan\", lat: 47.641465195176835, lng: 66.3759193479301 },\n \"LA\": { name: \"Laos\", lat: 18.117282736873282, lng: 103.76375899026448 },\n \"LB\": { name: \"Lebanon\", lat: 33.91160170722086, lng: 35.89651946324749 },\n \"LC\": { name: \"Saint Lucia\", lat: 13.895749185129906, lng: -60.9689510935251 },\n \"LI\": { name: \"Liechtenstein\", lat: 47.14627562133036, lng: 9.547674672376376 },\n \"LK\": { name: \"Sri Lanka\", lat: 7.696630939329944, lng: 80.66931169770622 },\n \"LR\": { name: \"Liberia\", lat: 6.52012979398834, lng: -9.258988935497618 },\n \"LS\": { name: \"Lesotho\", lat: -29.60168116924817, lng: 28.24475317864227 },\n \"LT\": { name: \"Lithuania\", lat: 55.29437393417175, lng: 23.946021605013534 },\n \"LU\": { name: \"Luxembourg\", lat: 49.77523454542369, lng: 6.103230338458876 },\n \"LV\": { name: \"Latvia\", lat: 56.813853047554154, lng: 24.693671325654403 },\n \"LY\": { name: \"Libya\", lat: 27.202915771690794, lng: 17.91133392454237 },\n \"MA\": { name: \"Morocco\", lat: 28.687598134979325, lng: -8.817212587250811 },\n \"MC\": { name: \"Monaco\", lat: 43.74798224995656, lng: 7.412820873271196 },\n \"MD\": { name: \"Moldova\", lat: 47.0725674580696, lng: 28.391111865941348 },\n \"ME\": { name: \"Montenegro\", lat: 42.73694835210454, lng: 19.29505087156758 },\n \"MF\": { name: \"Saint Martin\", lat: 18.078012113224464, lng: -63.06678525361946 },\n \"MG\": { name: \"Madagascar\", lat: -19.04163612493041, lng: 46.68493466832544 },\n \"MH\": { name: \"Marshall Islands\", lat: 7.307929900994344, lng: 168.72016025351076 },\n \"MK\": { name: \"North Macedonia\", lat: 41.59402890143112, lng: 21.70998923872772 },\n \"ML\": { name: \"Mali\", lat: 17.168146208584837, lng: -4.346399841781153 },\n \"MM\": { name: \"Myanmar\", lat: 19.901227931399873, lng: 97.08892285397344 },\n \"MN\": { name: \"Mongolia\", lat: 47.08644454604851, lng: 103.3987360327455 },\n \"MP\": { name: \"Northern Mariana Islands\", lat: 15.178063516432115, lng: 145.74119737203134 },\n \"MQ\": { name: \"Martinique\", lat: 14.642697353597645, lng: -61.01432380875083 },\n \"MR\": { name: \"Mauritania\", lat: 20.466731212820022, lng: -10.495079045035716 },\n \"MS\": { name: \"Montserrat\", lat: 16.735363391460357, lng: -62.18693281256255 },\n \"MT\": { name: \"Malta\", lat: 35.890522650899314, lng: 14.441922442508494 },\n \"MU\": { name: \"Mauritius\", lat: -20.28142317475198, lng: 57.56415671066546 },\n \"MV\": { name: \"Maldives\", lat: -0.6065577168009924, lng: 73.10076245140479 },\n \"MW\": { name: \"Malawi\", lat: -13.128986464184024, lng: 34.23441182298881 },\n \"MX\": { name: \"Mexico\", lat: 23.87436068093592, lng: -101.55399731630118 },\n \"MY\": { name: \"Malaysia\", lat: 3.6716608556387857, lng: 114.63330303992869 },\n \"MZ\": { name: \"Mozambique\", lat: -17.525230309488748, lng: 35.208577031290176 },\n \"NA\": { name: \"Namibia\", lat: -21.90858163281473, lng: 18.16451345845268 },\n \"NC\": { name: \"New Caledonia\", lat: -21.33003372660827, lng: 165.50767040438612 },\n \"NE\": { name: \"Niger\", lat: 17.08105392407292, lng: 8.86863247002646 },\n \"NF\": { name: \"Norfolk Island\", lat: -29.037654445046282, lng: 167.95259597483337 },\n \"NG\": { name: \"Nigeria\", lat: 9.61029352034213, lng: 8.147714845256194 },\n \"NI\": { name: \"Nicaragua\", lat: 12.893566631930554, lng: -85.016088327669 },\n \"NL\": { name: \"Netherlands\", lat: 52.134054128923886, lng: 5.554136426128487 },\n \"NO\": { name: \"Norway\", lat: 64.97775882947745, lng: 16.670259272390894 },\n \"NP\": { name: \"Nepal\", lat: 28.300920699755657, lng: 84.1338898313567 },\n \"NR\": { name: \"Nauru\", lat: -0.5221021440668993, lng: 166.92937633139178 },\n \"NU\": { name: \"Niue\", lat: -19.05230921680393, lng: -169.86878106699083 },\n \"NZ\": { name: \"New Zealand\", lat: -43.82765432544426, lng: 170.69035541428696 },\n \"OM\": { name: \"Oman\", lat: 20.7242833183209, lng: 55.841088119829 },\n \"PA\": { name: \"Panama\", lat: 8.439536749576892, lng: -80.14428761482796 },\n \"PE\": { name: \"Peru\", lat: -8.522717984240291, lng: -74.11416196781884 },\n \"PF\": { name: \"French Polynesia\", lat: -17.674684080120677, lng: -149.40041671099763 },\n \"PG\": { name: \"Papua New Guinea\", lat: -7.156912819152135, lng: 144.8348942994695 },\n \"PH\": { name: \"Philippines\", lat: 15.586542251094242, lng: 121.82208941416745 },\n \"PK\": { name: \"Pakistan\", lat: 30.116188371410882, lng: 69.08835090769651 },\n \"PL\": { name: \"Poland\", lat: 52.06848055692473, lng: 19.43573279234468 },\n \"PM\": { name: \"Saint Pierre and Miquelon\", lat: 46.95153913615198, lng: -56.32465353437558 },\n \"PN\": { name: \"Pitcairn\", lat: -24.366121747565458, lng: -128.3149848627581 },\n \"PR\": { name: \"Puerto Rico\", lat: 18.216224086610914, lng: -66.49425339593509 },\n \"PS\": { name: \"Palestinian Territory\", lat: 31.930818736453883, lng: 35.24251184154588 },\n \"PT\": { name: \"Portugal\", lat: 39.67529214702138, lng: -7.933662183874006 },\n \"PW\": { name: \"Palau\", lat: 7.534775852547396, lng: 134.57965086721052 },\n \"PY\": { name: \"Paraguay\", lat: -23.42190559259428, lng: -58.38906357428651 },\n \"QA\": { name: \"Qatar\", lat: 25.318528486425386, lng: 51.19794918743203 },\n \"RE\": { name: \"Réunion\", lat: -21.119825460665105, lng: 55.54393506194689 },\n \"RO\": { name: \"Romania\", lat: 45.82454894397586, lng: 25.094158201563292 },\n \"RS\": { name: \"Serbia\", lat: 44.02679870131969, lng: 20.85677444395745 },\n \"RU\": { name: \"Russian Federation\", lat: 59.039434214106194, lng: 98.6704990698032 },\n \"RW\": { name: \"Rwanda\", lat: -2.014687460047154, lng: 29.919439681764082 },\n \"SA\": { name: \"Saudi Arabia\", lat: 24.136038144757897, lng: 44.600958178225596 },\n \"SB\": { name: \"Solomon Islands\", lat: -9.613104734596515, lng: 160.16475795033884 },\n \"SC\": { name: \"Seychelles\", lat: -4.660002318822744, lng: 55.47250789595527 },\n \"SD\": { name: \"Sudan\", lat: 15.67060230984256, lng: 29.951458283594064 },\n \"SE\": { name: \"Sweden\", lat: 62.73420986108448, lng: 17.062431988004956 },\n \"SG\": { name: \"Singapore\", lat: 1.3528251890006349, lng: 103.81025757634053 },\n \"SH\": { name: \"Saint Helena\", lat: -15.962963318340398, lng: -5.717391620813109 },\n \"SI\": { name: \"Slovenia\", lat: 46.13759229564504, lng: 14.890636899973781 },\n \"SJ\": { name: \"Svalbard\", lat: 78.57318936469626, lng: 16.036378851505283 },\n \"SK\": { name: \"Slovakia\", lat: 48.69808390520484, lng: 19.581015362490966 },\n \"SL\": { name: \"Sierra Leone\", lat: 8.561330384750587, lng: -11.78656695731115 },\n \"SM\": { name: \"San Marino\", lat: 43.942820729097896, lng: 12.461278349581722 },\n \"SN\": { name: \"Senegal\", lat: 14.228861491763402, lng: -14.610875368352305 },\n \"SO\": { name: \"Somalia\", lat: 6.524534573103924, lng: 45.40037867243972 },\n \"SR\": { name: \"Suriname\", lat: 4.098723595920171, lng: -55.855514311561286 },\n \"SS\": { name: \"South Sudan\", lat: 7.657782041763295, lng: 30.3851856901788 },\n \"ST\": { name: \"Sao Tome and Principe\", lat: 0.22744704294793774, lng: 6.606158796857607 },\n \"SV\": { name: \"El Salvador\", lat: 13.758041517055418, lng: -88.85911489238985 },\n \"SX\": { name: \"Sint Maarten\", lat: 18.03942608463078, lng: -63.06883135915303 },\n \"SY\": { name: \"Syria\", lat: 35.09751106058316, lng: 38.5117323139514 },\n \"SZ\": { name: \"Eswatini\", lat: -26.562540935608702, lng: 31.510685746082007 },\n \"TC\": { name: \"Turks and Caicos Islands\", lat: 21.799865427483745, lng: -71.74058946811508 },\n \"TD\": { name: \"Chad\", lat: 15.283493546654503, lng: 18.427113900363025 },\n \"TF\": { name: \"Juan De Nova Island\", lat: -17.06449193630804, lng: 42.74374761089645 },\n \"TF\": { name: \"French Southern Territories\", lat: -49.26329687105643, lng: 69.54686984724839 },\n \"TF\": { name: \"Glorioso Islands\", lat: -11.566224871643417, lng: 47.290948081543384 },\n \"TG\": { name: \"Togo\", lat: 8.660743037717811, lng: 0.8990857571109684 },\n \"TH\": { name: \"Thailand\", lat: 13.66222784745538, lng: 101.08675116214552 },\n \"TJ\": { name: \"Tajikistan\", lat: 38.56933138382972, lng: 70.94215281065698 },\n \"TK\": { name: \"Tokelau\", lat: -9.195174767256544, lng: -171.85265950722743 },\n \"TL\": { name: \"Timor-Leste\", lat: -8.809894630601962, lng: 125.95024049562659 },\n \"TM\": { name: \"Turkmenistan\", lat: 39.06069118001429, lng: 58.4577357627054 },\n \"TN\": { name: \"Tunisia\", lat: 34.08636179565723, lng: 9.65587551697984 },\n \"TO\": { name: \"Tonga\", lat: -21.15927212823853, lng: -175.20415878511247 },\n \"TR\": { name: \"Turkey\", lat: 38.93207363123396, lng: 35.56886764076691 },\n \"TT\": { name: \"Trinidad and Tobago\", lat: 10.415515638298093, lng: -61.37236579976247 },\n \"TV\": { name: \"Tuvalu\", lat: -8.514701506447222, lng: 179.217833977593 },\n \"TZ\": { name: \"Tanzania\", lat: -6.355794440041147, lng: 34.81832206060381 },\n \"UA\": { name: \"Ukraine\", lat: 48.657532515563794, lng: 31.27377208442636 },\n \"UG\": { name: \"Uganda\", lat: 1.2821729218416205, lng: 32.34371768463123 },\n \"UM\": { name: \"United States Minor Outlying Islands\", lat: 19.302045812215958, lng: 166.63800339749642 },\n \"US\": { name: \"United States\", lat: 38.8208089190304, lng: -96.33161660829639 },\n \"UY\": { name: \"Uruguay\", lat: -32.78195043831093, lng: -56.01919523458085 },\n \"UZ\": { name: \"Uzbekistan\", lat: 41.4879065244633, lng: 63.8548297593985 },\n \"VA\": { name: \"Vatican City\", lat: 41.90402351316735, lng: 12.451312917026133 },\n \"VC\": { name: \"Saint Vincent and the Grenadines\", lat: 13.254808122970651, lng: -61.193766354393034 },\n \"VE\": { name: \"Venezuela\", lat: 7.148324760507107, lng: -66.36492135985132 },\n \"VG\": { name: \"British Virgin Islands\", lat: 18.42195819619707, lng: -64.62406519257699 },\n \"VI\": { name: \"US Virgin Islands\", lat: 17.738009708772523, lng: -64.76155341409797 },\n \"VN\": { name: \"Vietnam\", lat: 16.517347170839393, lng: 105.91338832758704 },\n \"VU\": { name: \"Vanuatu\", lat: -15.189132121699332, lng: 166.84912735669738 },\n \"WF\": { name: \"Wallis and Futuna\", lat: -14.283442307826677, lng: -178.12735555777184 },\n \"WS\": { name: \"Samoa\", lat: -13.634252953274622, lng: -172.44107655740137 },\n \"YE\": { name: \"Yemen\", lat: 16.001392616307445, lng: 47.46815793206386 },\n \"YT\": { name: \"Mayotte\", lat: -12.824468416301052, lng: 45.128142327031064 },\n \"ZA\": { name: \"South Africa\", lat: -28.55361930679731, lng: 24.75252746489084 },\n \"ZM\": { name: \"Zambia\", lat: -13.162832953186246, lng: 27.75521363430896 },\n \"ZW\": { name: \"Zimbabwe\", lat: -18.92700121981475, lng: 29.717829640720844 },\n};\n\nexport function getCountryCentroid(code) {\n if (!code) return null;\n return COUNTRY_CENTROIDS[code.toUpperCase()] || null;\n}\n\nexport function getCountryCentroids() {\n return COUNTRY_CENTROIDS;\n}","import View from '@core/View.js';\nimport MapLibreView from './MapLibreView.js';\nimport { COUNTRY_CENTROIDS } from './countryCentroids.js';\n\nclass MetricsCountryMapView extends View {\n constructor(options = {}) {\n const mapHeight = options.height || 320;\n super({\n className: 'metrics-country-map-view',\n ...options\n });\n\n this.endpoint = options.endpoint || '/api/metrics/fetch';\n this.account = options.account || 'global';\n this.category = options.category || null;\n this.slugs = options.slugs || null;\n this.granularity = options.granularity || 'days';\n this.maxCountries = options.maxCountries || 12;\n this.metricLabel = options.metricLabel || 'Events';\n this.height = mapHeight;\n this.mapStyle = options.mapStyle || 'dark';\n this.mapOptions = options.mapOptions || {};\n this.showRoutes = options.showRoutes !== false;\n this.routeOrigin = options.routeOrigin || { lng: -77.346, lat: 38.958, name: 'Reston, VA' };\n this._refreshing = false;\n }\n\n async getTemplate() {\n return `\n <div class=\"metrics-country-map\">\n <div class=\"map-container mb-3\" data-container=\"${this.id}-map\" style=\"height:${this.height}px\"></div>\n <div class=\"map-legend small\" data-region=\"legend\"></div>\n </div>\n `;\n }\n\n async onInit() {\n this.statusEl = document.createElement('div');\n this.statusEl.className = 'text-muted small px-3 pb-2';\n this.element.appendChild(this.statusEl);\n\n this.mapView = new MapLibreView({\n containerId: `${this.id}-map`,\n height: this.height,\n style: this.mapStyle,\n zoom: this.mapOptions.zoom ?? 1.3,\n center: this.mapOptions.center || [10, 20],\n pitch: this.mapOptions.pitch ?? 20,\n bearing: this.mapOptions.bearing ?? 0,\n showNavigationControl: this.mapOptions.showNavigationControl ?? true,\n autoFitBounds: this.mapOptions.autoFitBounds ?? false\n });\n this.addChild(this.mapView);\n await this.refresh();\n }\n\n async refresh() {\n if (this._refreshing) return;\n this._refreshing = true;\n this.setStatus('Loading hotspots…');\n try {\n const metrics = await this.fetchMetrics();\n await this.applyMetrics(metrics);\n this.setStatus('');\n } catch (error) {\n console.error('MetricsCountryMapView refresh error', error);\n this.setStatus('Unable to load country metrics.');\n } finally {\n this._refreshing = false;\n }\n }\n\n async fetchMetrics() {\n const rest = this.getApp()?.rest;\n if (!rest) {\n throw new Error('REST client unavailable');\n }\n const params = {\n account: this.account,\n granularity: this.granularity,\n with_labels: true\n };\n if (this.category) params.category = this.category;\n if (this.slugs) {\n const slugsArray = Array.isArray(this.slugs) ? this.slugs : [this.slugs];\n params['slugs[]'] = slugsArray;\n }\n const response = await rest.GET(this.endpoint, params);\n if (!response.success || !response.data?.status) {\n throw new Error(response.data?.error || 'Metrics API error');\n }\n return response.data.data;\n }\n\n async applyMetrics(data) {\n const metrics = data?.data || {};\n const labels = data?.labels || [];\n const entries = [];\n\n Object.entries(metrics).forEach(([iso, series]) => {\n const total = series.reduce((sum, value) => sum + (Number(value) || 0), 0);\n if (!total) return;\n const centroid = COUNTRY_CENTROIDS[iso.toUpperCase()];\n if (!centroid) return;\n entries.push({\n code: iso.toUpperCase(),\n total,\n values: series,\n centroid\n });\n });\n\n if (!entries.length) {\n this.mapView.updateMarkers([]);\n this.renderLegend([]);\n this.setStatus('No country data available for the selected range.');\n return;\n }\n\n entries.sort((a, b) => b.total - a.total);\n const topEntries = entries.slice(0, this.maxCountries);\n const maxValue = topEntries[0]?.total || 1;\n\n const markers = topEntries.map(entry => {\n const intensity = entry.total / maxValue;\n const markerSize = Math.round(18 + intensity * 24);\n return {\n lng: entry.centroid.lng,\n lat: entry.centroid.lat,\n size: markerSize,\n color: this.getMarkerColor(intensity),\n popup: `\n <div class=\"text-center\">\n <strong>${entry.centroid.name}</strong><br/>\n <span class=\"text-muted\">${entry.total.toLocaleString()} ${this.metricLabel}</span>\n </div>\n `\n };\n });\n\n if (this.showRoutes && this.routeOrigin?.lng && this.routeOrigin?.lat) {\n markers.push({\n lng: this.routeOrigin.lng,\n lat: this.routeOrigin.lat,\n size: 26,\n color: '#0d6efd',\n icon: 'bi bi-broadcast-pin',\n popup: `\n <div class=\"text-center\">\n <strong>${this.routeOrigin.name || 'Operations Hub'}</strong><br/>\n <span class=\"text-muted\">Origin</span>\n </div>\n `\n });\n }\n\n this.mapView.updateMarkers(markers);\n this.renderLegend(topEntries, labels);\n if (this.showRoutes) {\n this.renderRoutes(topEntries, maxValue);\n }\n }\n\n getMarkerColor(intensity) {\n const start = [32, 201, 151]; // teal\n const end = [255, 193, 7]; // amber\n const mix = start.map((value, idx) =>\n Math.round(value + (end[idx] - value) * intensity)\n );\n return `rgba(${mix[0]}, ${mix[1]}, ${mix[2]}, 0.9)`;\n }\n\n renderLegend(entries) {\n const legendEl = this.element.querySelector('[data-region=\"legend\"]');\n if (!legendEl) return;\n if (!entries.length) {\n legendEl.innerHTML = '';\n return;\n }\n\n const maxValue = entries[0]?.total || 1;\n const rows = entries.map(entry => {\n const percent = ((entry.total / maxValue) * 100).toFixed(0);\n return `\n <div class=\"d-flex justify-content-between align-items-center py-1 border-top\">\n <div>\n <span class=\"fw-semibold\">${entry.centroid.name}</span>\n <span class=\"text-muted ms-1\">(${entry.code})</span>\n </div>\n <div class=\"text-end\">\n <div class=\"fw-semibold\">${entry.total.toLocaleString()}</div>\n <small class=\"text-muted\">${percent}% of top</small>\n </div>\n </div>\n `;\n }).join('');\n\n legendEl.innerHTML = rows;\n }\n\n renderRoutes(entries, maxValue) {\n const origin = this.routeOrigin || null;\n if (!origin || !origin.lng || !origin.lat || !this.mapView) return;\n\n const features = entries.map(entry => ({\n type: 'Feature',\n geometry: {\n type: 'LineString',\n coordinates: [\n [origin.lng, origin.lat],\n [entry.centroid.lng, entry.centroid.lat]\n ]\n },\n properties: {\n total: entry.total,\n intensity: entry.total / maxValue\n }\n }));\n\n const geojson = {\n type: 'FeatureCollection',\n features\n };\n\n const paint = {\n 'line-color': [\n 'interpolate',\n ['linear'],\n ['get', 'intensity'],\n 0,\n 'rgba(32, 201, 151, 0.2)',\n 1,\n 'rgba(255, 193, 7, 0.85)'\n ],\n 'line-width': [\n 'interpolate',\n ['linear'],\n ['get', 'intensity'],\n 0,\n 1,\n 1,\n 5\n ],\n 'line-opacity': [\n 'interpolate',\n ['linear'],\n ['get', 'intensity'],\n 0,\n 0.2,\n 1,\n 0.9\n ]\n };\n\n this.mapView.lineSources = this.mapView.lineSources.filter(src => src.id !== `${this.id}-routes`);\n this.mapView.updateLineSource(`${this.id}-routes`, {\n data: geojson,\n paint\n });\n }\n\n setStatus(message) {\n if (!this.statusEl) return;\n this.statusEl.textContent = message || '';\n this.statusEl.style.display = message ? 'block' : 'none';\n }\n}\n\nexport default MetricsCountryMapView;\n","import Page from '@core/Page.js';\nimport View from '@core/View.js';\nimport {\n MetricsChart,\n MetricsMiniChartWidget\n} from '@ext/charts/index.js';\nimport TableView from '@core/views/table/TableView.js';\nimport {\n IncidentList,\n IncidentStats\n} from '@core/models/Incident.js';\nimport {\n TicketList\n} from '@core/models/Tickets.js';\nimport { MetricsCountryMapView } from '@ext/map/index.js';\n\nclass IncidentDashboardHeader extends View {\n constructor(options = {}) {\n super({\n ...options,\n className: 'incident-dashboard-header'\n });\n\n this.model = new IncidentStats();\n }\n\n async getTemplate() {\n return `\n <div class=\"row\">\n <div class=\"col-xl-3 col-lg-6 col-12 mb-3\">\n <div class=\"card h-100 border-0 shadow-sm\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div>\n <h6 class=\"card-title text-muted mb-2\">Open Incidents</h6>\n <h3 class=\"mb-1 fw-bold\">{{model.incidents.open}}</h3>\n <span class=\"badge bg-danger-subtle text-danger\">{{model.incidents.new}} New</span>\n </div>\n <div class=\"text-danger\">\n <i class=\"bi bi-exclamation-triangle fs-2\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"col-xl-3 col-lg-6 col-12 mb-3\">\n <div class=\"card h-100 border-0 shadow-sm\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div>\n <h6 class=\"card-title text-muted mb-2\">Open Tickets</h6>\n <h3 class=\"mb-1 fw-bold\">{{model.tickets.open}}</h3>\n <span class=\"badge bg-warning-subtle text-warning\">{{model.tickets.new}} New</span>\n </div>\n <div class=\"text-warning\">\n <i class=\"bi bi-ticket-perforated fs-2\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"col-xl-3 col-lg-6 col-12 mb-3\">\n <div class=\"card h-100 border-0 shadow-sm\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div>\n <h6 class=\"card-title text-muted mb-2\">Recent Events</h6>\n <h3 class=\"mb-1 fw-bold\">{{model.events.recent}}</h3>\n <span class=\"badge bg-info-subtle text-info\">{{model.events.critical}} Critical</span>\n </div>\n <div class=\"text-info\">\n <i class=\"bi bi-activity fs-2\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"col-xl-3 col-lg-6 col-12 mb-3\">\n <div class=\"card h-100 border-0 shadow-sm\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div>\n <h6 class=\"card-title text-muted mb-2\">Recent Incidents</h6>\n <h3 class=\"mb-1 fw-bold\">{{model.incidents.recent}}</h3>\n <span class=\"badge bg-secondary-subtle text-secondary\">Last 24h</span>\n </div>\n <div class=\"text-secondary\">\n <i class=\"bi bi-clock-history fs-2\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n `;\n }\n\n async onBeforeRender() {\n await this.model.fetch();\n }\n}\n\n\nclass IncidentDashboardPage extends Page {\n constructor(options = {}) {\n super({\n ...options,\n title: 'Incidents Dashboard',\n className: 'incident-dashboard-page'\n });\n }\n\n async getTemplate() {\n return `\n <div class=\"container-fluid incident-dashboard\">\n <div class=\"d-flex justify-content-between align-items-center mb-3\">\n <div>\n <p class=\"text-muted mb-0\">Incident Intelligence Hub</p>\n <small class=\"text-info\">\n <i class=\"bi bi-activity me-1\"></i>\n Real-time visibility into incidents, tickets, and correlated events\n </small>\n </div>\n <div class=\"btn-group\" role=\"group\">\n <button type=\"button\"\n class=\"btn btn-outline-secondary btn-sm\"\n data-action=\"refresh-all\"\n title=\"Refresh dashboards\">\n <i class=\"bi bi-arrow-clockwise\"></i> Refresh\n </button>\n </div>\n </div>\n\n <div data-container=\"header\" class=\"mb-4\"></div>\n\n <div class=\"row g-4\">\n <div class=\"col-xl-6 col-lg-12\" data-container=\"events-widget\"></div>\n <div class=\"col-xl-6 col-lg-12\" data-container=\"incidents-widget\"></div>\n </div>\n\n\n <div class=\"row g-4 mt-1\">\n <div class=\"col-12\">\n <div class=\"card shadow-sm h-100\">\n <div class=\"card-header border-0 bg-transparent d-flex align-items-center justify-content-between\">\n <div>\n <h6 class=\"mb-0 text-uppercase small text-muted\">Global Event Hotspots</h6>\n <span class=\"text-muted small\">Clusters sized by total events</span>\n </div>\n <span class=\"badge bg-info-subtle text-info\">Interactive</span>\n </div>\n <div class=\"card-body p-0\" data-container=\"events-country-map\"></div>\n </div>\n </div>\n </div>\n\n\n <div class=\"row g-4 mt-1\">\n <div class=\"col-lg-6\">\n <div class=\"card shadow-sm h-100\">\n <div class=\"card-header border-0 bg-transparent d-flex align-items-center justify-content-between\">\n <div>\n <h6 class=\"mb-0 text-uppercase small text-muted\">Events by Country</h6>\n <span class=\"text-muted small\">Hotspots from the last 24 hours</span>\n </div>\n <span class=\"badge bg-success-subtle text-success\">Live</span>\n </div>\n <div class=\"card-body p-3\">\n <div data-container=\"events-by-country-chart\"></div>\n </div>\n </div>\n </div>\n <div class=\"col-lg-6\">\n <div class=\"card shadow-sm h-100\">\n <div class=\"card-header border-0 bg-transparent d-flex align-items-center justify-content-between\">\n <div>\n <h6 class=\"mb-0 text-uppercase small text-muted\">Incidents by Country</h6>\n <span class=\"text-muted small\">Highest volume regions</span>\n </div>\n <span class=\"badge bg-warning-subtle text-warning\">24h</span>\n </div>\n <div class=\"card-body p-3\">\n <div data-container=\"incidents-by-country-chart\"></div>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"row g-4 mt-1\">\n <div class=\"col-lg-6\">\n <div class=\"card shadow-sm h-100\">\n <div class=\"card-header border-0 bg-transparent d-flex align-items-center justify-content-between\">\n <div>\n <h6 class=\"mb-0 text-uppercase small text-muted\">New Tickets</h6>\n <span class=\"text-muted small\">Fresh tickets awaiting triage</span>\n </div>\n <i class=\"bi bi-ticket-perforated text-muted\"></i>\n </div>\n <div class=\"card-body\" data-container=\"my-tickets-table\"></div>\n </div>\n </div>\n <div class=\"col-lg-6\">\n <div class=\"card shadow-sm h-100\">\n <div class=\"card-header border-0 bg-transparent d-flex align-items-center justify-content-between\">\n <div>\n <h6 class=\"mb-0 text-uppercase small text-muted\">New Incidents</h6>\n <span class=\"text-muted small\">Newest incidents in the queue</span>\n </div>\n <i class=\"bi bi-flag text-warning\"></i>\n </div>\n <div class=\"card-body\" data-container=\"high-priority-incidents-table\"></div>\n </div>\n </div>\n </div>\n </div>\n `;\n }\n\n async onInit() {\n this.header = new IncidentDashboardHeader({\n containerId: 'header'\n });\n this.addChild(this.header);\n\n this.eventsWidget = new MetricsMiniChartWidget({\n containerId: 'events-widget',\n icon: 'bi bi-activity fs-2',\n title: 'System Events',\n subtitle: '{{now_value}} <span class=\"subtitle-label\">{{now_label}}</span>',\n background: '#154360',\n textColor: '#FFFFFF',\n endpoint: '/api/metrics/fetch',\n granularity: 'days',\n slugs: ['incident_events'],\n account: 'incident',\n chartType: 'line',\n showTooltip: true,\n showXAxis: false,\n height: 140,\n color: 'rgba(255,255,255,0.9)',\n fill: true,\n fillColor: 'rgba(255,255,255,0.2)',\n smoothing: 0.3,\n defaultDateRange: '7d',\n valueFormat: 'number',\n showTrending: true,\n showSettings: true,\n settingsKey: 'incident-dashboard-events'\n });\n this.addChild(this.eventsWidget);\n\n this.incidentsWidget = new MetricsMiniChartWidget({\n containerId: 'incidents-widget',\n icon: 'bi bi-exclamation-triangle fs-2',\n title: 'System Incidents',\n subtitle: '{{now_value}} <span class=\"subtitle-label\">{{now_label}}</span>',\n background: '#7D6608',\n textColor: '#FFFFFF',\n endpoint: '/api/metrics/fetch',\n granularity: 'days',\n slugs: ['incidents'],\n account: 'incident',\n chartType: 'line',\n showTooltip: true,\n showXAxis: false,\n height: 140,\n color: 'rgba(255,255,255,0.9)',\n fill: true,\n fillColor: 'rgba(255,255,255,0.25)',\n smoothing: 0.3,\n defaultDateRange: '7d',\n valueFormat: 'number',\n showTrending: true,\n showSettings: true,\n settingsKey: 'incident-dashboard-incidents'\n });\n this.addChild(this.incidentsWidget);\n\n this.eventsByCountryChart = new MetricsChart({\n title: '<i class=\"bi bi-globe-central-south-asia me-2\"></i> Events by Country',\n endpoint: '/api/metrics/fetch',\n account: 'incident',\n category: 'incident_events_by_country',\n granularity: 'days',\n chartType: 'line',\n showDateRange: false,\n showMetricsFilter: false,\n height: 220,\n maxDatasets: 10,\n colors: [\n 'rgba(32, 201, 151, 0.85)'\n ],\n yAxis: {\n label: 'Events',\n beginAtZero: true\n },\n tooltip: { y: 'number:0' },\n containerId: 'events-by-country-chart'\n });\n this.addChild(this.eventsByCountryChart);\n\n this.incidentsByCountryChart = new MetricsChart({\n title: '<i class=\"bi bi-geo-alt me-2\"></i> Incidents by Country',\n endpoint: '/api/metrics/fetch',\n account: 'incident',\n category: 'incident_by_country',\n granularity: 'days',\n chartType: 'line',\n showDateRange: false,\n showMetricsFilter: false,\n height: 220,\n maxDatasets: 10,\n colors: [\n 'rgba(255, 193, 7, 0.85)'\n ],\n yAxis: {\n label: 'Incidents',\n beginAtZero: true\n },\n tooltip: { y: 'number:0' },\n containerId: 'incidents-by-country-chart'\n });\n this.addChild(this.incidentsByCountryChart);\n\n this.eventsCountryMap = new MetricsCountryMapView({\n containerId: 'events-country-map',\n category: 'incident_events_by_country',\n account: 'incident',\n maxCountries: 15,\n metricLabel: 'Events',\n height: 360,\n mapStyle: 'dark'\n });\n this.addChild(this.eventsCountryMap);\n\n const myTicketsCollection = new TicketList({\n params: {\n status: 'new'\n }\n });\n this.myTicketsTable = new TableView({\n containerId: 'my-tickets-table',\n title: 'New Tickets',\n collection: myTicketsCollection,\n columns: [\n { key: 'id', label: 'ID' },\n { key: 'title', label: 'Title' },\n { key: 'priority', label: 'Priority' }\n ]\n });\n this.addChild(this.myTicketsTable);\n\n const newIncidentsCollection = new IncidentList({\n params: {\n status: 'new'\n }\n });\n this.highPriorityIncidentsTable = new TableView({\n containerId: 'high-priority-incidents-table',\n title: 'New Incidents',\n collection: newIncidentsCollection,\n columns: [\n { key: 'id', label: 'ID' },\n { key: 'title', label: 'Title' },\n { key: 'status', label: 'Status', formatter: 'badge' }\n ]\n });\n this.addChild(this.highPriorityIncidentsTable);\n }\n\n async onActionRefreshAll(event, element) {\n const button = element || event?.currentTarget || null;\n const icon = button?.querySelector?.('i');\n icon?.classList.add('bi-spin');\n if (button) button.disabled = true;\n\n const refreshTasks = [\n this.header?.model?.fetch()?.then(() => this.header.render()),\n this.eventsWidget?.refresh(),\n this.incidentsWidget?.refresh(),\n this.eventsByCountryChart?.refresh(),\n this.incidentsByCountryChart?.refresh(),\n this.eventsCountryMap?.refresh(),\n this.myTicketsTable?.collection?.fetch(),\n this.highPriorityIncidentsTable?.collection?.fetch()\n ].filter(Boolean);\n\n await Promise.allSettled(refreshTasks);\n\n icon?.classList.remove('bi-spin');\n if (button) button.disabled = false;\n }\n}\n\nexport default IncidentDashboardPage;\n","/**\n * StackTraceView - Display formatted and color-coded stack traces\n */\n\nimport View from '@core/View.js';\n\nclass StackTraceView extends View {\n constructor(options = {}) {\n super({\n className: 'stack-trace-view',\n ...options\n });\n\n this.stackTrace = options.stackTrace || '';\n \n this.template = `\n <div class=\"stack-trace-container p-3\">\n <style>\n .stack-trace-line {\n font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', monospace;\n font-size: 13px;\n line-height: 1.6;\n padding: 4px 8px;\n margin: 0;\n border-left: 3px solid transparent;\n }\n .stack-trace-line:hover {\n background-color: rgba(0, 0, 0, 0.05);\n }\n .stack-trace-error {\n color: #dc3545;\n font-weight: 600;\n border-left-color: #dc3545;\n background-color: rgba(220, 53, 69, 0.05);\n }\n .stack-trace-file {\n color: #0d6efd;\n font-weight: 500;\n border-left-color: #0d6efd;\n }\n .stack-trace-function {\n color: #6610f2;\n font-weight: 500;\n }\n .stack-trace-location {\n color: #6c757d;\n font-size: 12px;\n }\n .stack-trace-line-number {\n color: #fd7e14;\n font-weight: 600;\n }\n .stack-trace-context {\n color: #495057;\n background-color: rgba(0, 0, 0, 0.02);\n }\n .stack-trace-container {\n background-color: #f8f9fa;\n border: 1px solid #dee2e6;\n border-radius: 0.375rem;\n max-height: 600px;\n overflow-y: auto;\n }\n </style>\n <div class=\"stack-trace-content\">\n {{{formattedStackTrace}}}\n </div>\n </div>\n `;\n }\n\n async onBeforeRender() {\n this.formattedStackTrace = this.formatStackTrace(this.stackTrace);\n }\n\n formatStackTrace(stackTrace) {\n if (!stackTrace) {\n return '<div class=\"text-muted p-3\">No stack trace available</div>';\n }\n\n // Convert to string if it's an object\n const traceStr = typeof stackTrace === 'string' ? stackTrace : JSON.stringify(stackTrace, null, 2);\n \n const lines = traceStr.split('\\n');\n let html = '';\n\n lines.forEach((line, index) => {\n if (!line.trim()) {\n html += '<div class=\"stack-trace-line\">&nbsp;</div>';\n return;\n }\n\n // Detect error message (usually the first line)\n if (index === 0 && (line.includes('Error:') || line.includes('Exception:'))) {\n html += `<div class=\"stack-trace-line stack-trace-error\">${this.escapeHtml(line)}</div>`;\n return;\n }\n\n // Detect file paths with line numbers\n // Pattern: at functionName (file.js:123:45) or file.js:123:45\n const filePattern = /(.+?)\\s*\\(([^:]+):(\\d+):(\\d+)\\)/;\n const simpleFilePattern = /^\\s*at\\s+([^:]+):(\\d+):(\\d+)/;\n \n let match = line.match(filePattern);\n if (match) {\n const [, funcName, filePath, lineNum, colNum] = match;\n html += `<div class=\"stack-trace-line stack-trace-file\">\n <span class=\"stack-trace-function\">${this.escapeHtml(funcName.trim())}</span>\n <span class=\"stack-trace-location\"> (${this.escapeHtml(filePath)}:<span class=\"stack-trace-line-number\">${lineNum}</span>:${colNum})</span>\n </div>`;\n return;\n }\n\n match = line.match(simpleFilePattern);\n if (match) {\n const [, filePath, lineNum, colNum] = match;\n html += `<div class=\"stack-trace-line stack-trace-file\">\n <span class=\"stack-trace-location\">at ${this.escapeHtml(filePath)}:<span class=\"stack-trace-line-number\">${lineNum}</span>:${colNum}</span>\n </div>`;\n return;\n }\n\n // Python-style stack trace: File \"...\", line X, in function\n const pythonPattern = /File\\s+\"([^\"]+)\",\\s+line\\s+(\\d+),\\s+in\\s+(.+)/;\n match = line.match(pythonPattern);\n if (match) {\n const [, filePath, lineNum, funcName] = match;\n html += `<div class=\"stack-trace-line stack-trace-file\">\n <span class=\"stack-trace-location\">File \"${this.escapeHtml(filePath)}\", line <span class=\"stack-trace-line-number\">${lineNum}</span>, in </span>\n <span class=\"stack-trace-function\">${this.escapeHtml(funcName)}</span>\n </div>`;\n return;\n }\n\n // Check if line starts with \"at \" (stack frame indicator)\n if (line.trim().startsWith('at ')) {\n html += `<div class=\"stack-trace-line stack-trace-file\">${this.escapeHtml(line)}</div>`;\n return;\n }\n\n // Default: context line\n html += `<div class=\"stack-trace-line stack-trace-context\">${this.escapeHtml(line)}</div>`;\n });\n\n return html;\n }\n\n updateStackTrace(newStackTrace) {\n this.stackTrace = newStackTrace;\n this.render();\n }\n}\n\nexport default StackTraceView;\n","import { IncidentHistory, IncidentHistoryList } from '@core/models/Incident.js';\n\nclass IncidentHistoryAdapter {\n constructor(incidentId) {\n this.incidentId = incidentId;\n this.collection = new IncidentHistoryList({ params: { incident: this.incidentId } });\n }\n\n async fetch() {\n await this.collection.fetch();\n return this.collection.models.map(item => this.transform(item));\n }\n\n transform(item) {\n return {\n id: item.get('id'),\n type: item.get('kind') === 'comment' ? 'user_comment' : 'system_event',\n author: {\n name: item.get('by.display_name') || 'System',\n avatarUrl: item.get('by.avatar.url')\n },\n timestamp: item.get('created'),\n content: item.get('note'),\n attachments: [] // Incident history doesn't have attachments in this phase\n };\n }\n\n async addNote(data) {\n const history = new IncidentHistory({\n incident: this.incidentId,\n note: data.text,\n kind: 'comment'\n });\n const resp = await history.save();\n if (resp.success) {\n await this.collection.fetch();\n }\n return resp;\n }\n}\n\nexport default IncidentHistoryAdapter;\n","/**\n * IncidentView - Detailed view for an Incident record\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport DataView from '@core/views/data/DataView.js';\nimport TableView from '@core/views/table/TableView.js';\nimport StackTraceView from '@core/views/data/StackTraceView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport { Incident, IncidentForms, IncidentEventList } from '@core/models/Incident.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport IncidentHistoryAdapter from './adapters/IncidentHistoryAdapter.js';\nimport ChatView from '@core/views/chat/ChatView.js';\n\nclass IncidentView extends View {\n constructor(options = {}) {\n super({\n className: 'incident-view',\n ...options\n });\n\n this.model = options.model || new Incident(options.data || {});\n this.incidentIcon = this.getIconForIncident(this.model.get('state'));\n\n this.template = `\n <div class=\"incident-view-container\">\n <!-- Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-4\">\n <div class=\"d-flex align-items-center gap-3\">\n <div class=\"fs-1 {{incidentIcon.color}}\">\n <i class=\"bi {{incidentIcon.icon}}\"></i>\n </div>\n <div>\n <h3 class=\"mb-1\">Incident #{{model.id}}</h3>\n <div class=\"text-muted small\">\n Category: {{model.category|capitalize}}\n </div>\n <div class=\"text-muted small mt-1\">\n Created: {{model.created|datetime}}\n </div>\n </div>\n </div>\n <div class=\"d-flex align-items-center gap-4\">\n <div class=\"text-end\">\n <div>State: <span class=\"badge bg-primary\">{{model.state|capitalize}}</span></div>\n <div class=\"text-muted small mt-1\">Priority: {{model.priority}}</div>\n </div>\n <div data-container=\"incident-context-menu\"></div>\n </div>\n </div>\n\n <!-- Tabs -->\n <div data-container=\"incident-tabs\"></div>\n </div>\n `;\n }\n\n getIconForIncident(state) {\n const s = state?.toLowerCase();\n if (s === 'resolved' || s === 'closed') return { icon: 'bi-check-circle-fill', color: 'text-success' };\n if (s === 'new' || s === 'opened') return { icon: 'bi-exclamation-triangle-fill', color: 'text-danger' };\n if (s === 'paused' || s === 'ignore') return { icon: 'bi-pause-circle-fill', color: 'text-warning' };\n return { icon: 'bi-shield-exclamation', color: 'text-secondary' };\n }\n\n async onInit() {\n // Overview Tab\n this.overviewView = new DataView({\n model: this.model,\n className: \"p-3\",\n columns: 2,\n fields: [\n { name: 'id', label: 'Incident ID' },\n { name: 'state', label: 'State', format: 'badge' },\n { name: 'priority', label: 'Priority' },\n { name: 'category', label: 'Category' },\n { name: 'model_name', label: 'Related Model' },\n { name: 'model_id', label: 'Related Model ID' },\n { name: 'details', label: 'Details', columns: 12, format: 'pre' },\n ]\n });\n\n // Events Tab\n const eventsCollection = new IncidentEventList({\n params: { incident: this.model.get('id') }\n });\n this.eventsView = new TableView({\n collection: eventsCollection,\n hideActivePillNames: ['incident'],\n columns: [\n { key: 'id', label: 'ID', width: '70px', sortable: true },\n { key: 'created', label: 'Date', formatter: 'datetime', sortable: true, width: '180px' },\n { key: 'category', label: 'Category', formatter: 'badge', sortable: true },\n { key: 'title', label: 'Title', sortable: true },\n { key: 'level', label: 'Level', sortable: true, width: '80px' },\n ],\n showAdd: false,\n actions: ['view'],\n paginated: true,\n size: 10\n });\n\n // History & Comments Tab\n const adapter = new IncidentHistoryAdapter(this.model.get('id'));\n this.historyView = new ChatView({ adapter });\n\n const tabs = { \n 'Overview': this.overviewView,\n 'Events': this.eventsView,\n 'History & Comments': this.historyView\n };\n \n const metadata = this.model.get('metadata') || {};\n \n // Add Stack Trace tab if present\n if (metadata.stack_trace) {\n this.stackTraceView = new StackTraceView({\n stackTrace: metadata.stack_trace\n });\n tabs['Stack Trace'] = this.stackTraceView;\n }\n \n // Add Metadata tab if there's metadata\n if (Object.keys(metadata).length > 0) {\n this.metadataView = new View({\n model: this.model,\n template: `<pre class=\"bg-light p-3 border rounded\"><code>{{{model.metadata|json}}}</code></pre>`\n });\n tabs['Metadata'] = this.metadataView;\n }\n\n this.tabView = new TabView({\n containerId: 'incident-tabs',\n tabs: tabs,\n activeTab: 'Overview'\n });\n this.addChild(this.tabView);\n\n // ContextMenu\n const incidentMenu = new ContextMenu({\n containerId: 'incident-context-menu',\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: [\n { label: 'Edit Incident', action: 'edit-incident', icon: 'bi-pencil' },\n { label: 'Resolve', action: 'resolve-incident', icon: 'bi-check-circle' },\n { type: 'divider' },\n { label: 'Delete Incident', action: 'delete-incident', icon: 'bi-trash', danger: true }\n ]\n }\n });\n this.addChild(incidentMenu);\n }\n\n async onActionEditIncident() {\n const resp = await Dialog.showModelForm({\n title: `Edit Incident #${this.model.id}`,\n model: this.model,\n formConfig: IncidentForms.edit,\n });\n if (resp) {\n this.render();\n }\n }\n \n async onActionResolveIncident() {\n await this.model.save({ state: 'resolved' });\n this.render();\n this.emit('incident:updated', { model: this.model });\n }\n\n async onActionDeleteIncident() {\n const confirmed = await Dialog.confirm(\n `Are you sure you want to delete this incident?`,\n 'Confirm Deletion',\n { confirmClass: 'btn-danger', confirmText: 'Delete' }\n );\n if (confirmed) {\n const resp = await this.model.destroy();\n if (resp.success) {\n this.emit('incident:deleted', { model: this.model });\n }\n }\n }\n}\n\nIncident.VIEW_CLASS = IncidentView;\n\nexport default IncidentView;\n","/**\n * IncidentTablePage - Incident management using TablePage component\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { IncidentList, IncidentForms } from '@core/models/Incident.js';\nimport IncidentView from './IncidentView.js';\n\nclass IncidentTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_incidents',\n pageName: 'Manage Incidents',\n router: \"admin/incidents\",\n Collection: IncidentList,\n\n formCreate: IncidentForms.create,\n formEdit: IncidentForms.edit,\n itemViewClass: IncidentView,\n\n viewDialogOptions: {\n header: false,\n size: 'xl'\n },\n\n defaultQuery: {\n sort: '-id',\n status: \"new\",\n },\n\n // Column definitions\n columns: [\n {\n key: 'id',\n label: 'ID',\n width: '60px',\n sortable: true,\n class: 'text-muted'\n },\n {\n key: 'status', label: \"Status\",\n filter: {\n type: 'multiselect',\n options: [\"new\", \"open\", \"paused\", \"resolved\", \"qa\", \"ignored\"],\n }\n },\n {\n key: 'created',\n label: 'Created',\n formatter: \"epoch|datetime\"\n },\n {\n key: 'scope',\n label: 'Scope',\n sortable: true,\n filter: {type:\"text\"}\n },\n {\n key: 'category',\n label: 'Category',\n sortable: true,\n filter: {type:\"text\"}\n },\n {\n key: 'priority',\n label: 'Priority',\n filter: {type:\"text\"}\n },\n {\n key: 'title',\n label: 'title',\n formatter: \"truncate(100)|default('No description')\"\n }\n ],\n\n filters: [\n {\n key: 'category__not',\n label: 'Not Category',\n filter: {type:\"text\"}\n },\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No incidents found. Click \"Add Incident\" to create your first incident.',\n\n // Batch actions\n batchBarLocation: 'top',\n batchActions: [\n { label: \"Open\", icon: \"bi bi-folder2-open\", action: \"open\" },\n { label: \"Resolve\", icon: \"bi bi-check-circle\", action: \"resolve\" },\n { label: \"Pause\", icon: \"bi bi-pause-circle\", action: \"pause\" },\n { label: \"Ignore\", icon: \"bi bi-x-circle\", action: \"ignore\" },\n { label: \"Merge\", icon: \"bi bi-merge\", action: \"merge\" }\n ],\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n\n async onActionBatchResolve(event, element) {\n const selected = this.tableView.getSelectedItems();\n if (!selected.length) return;\n\n const app = this.getApp();\n const result = await app.confirm(`Are you sure you want to close ${selected.length} incidents?`);\n if (!result) return;\n await Promise.all(selected.map(item => item.model.save({status: 'resolved'})));\n this.tableView.collection.fetch();\n }\n\n async onActionBatchOpen(event, element) {\n const selected = this.tableView.getSelectedItems();\n if (!selected.length) return;\n\n const app = this.getApp();\n const result = await app.confirm(`Are you sure you want to open ${selected.length} incidents?`);\n if (!result) return;\n await Promise.all(selected.map(item => item.model.save({status: 'open'})));\n this.tableView.collection.fetch();\n }\n\n async onActionBatchPause(event, element) {\n const selected = this.tableView.getSelectedItems();\n if (!selected.length) return;\n\n const app = this.getApp();\n const result = await app.confirm(`Are you sure you want to pause ${selected.length} incidents?`);\n if (!result) return;\n await Promise.all(selected.map(item => item.model.save({status: 'paused'})));\n this.tableView.collection.fetch();\n }\n\n async onActionBatchIgnore(event, element) {\n const selected = this.tableView.getSelectedItems();\n if (!selected.length) return;\n\n const app = this.getApp();\n const result = await app.confirm(`Are you sure you want to ignore ${selected.length} incidents?`);\n if (!result) return;\n await Promise.all(selected.map(item => item.model.save({status: 'ignored'})));\n this.tableView.collection.fetch();\n }\n\n async onActionBatchMerge(_event, _element) {\n const selected = this.tableView.getSelectedItems();\n if (!selected.length) return;\n\n const app = this.getApp();\n const result = await app.showForm({\n title: `Merge ${selected.length} incidents`,\n fields: [\n {\n name: 'merge',\n type: 'select',\n label: 'Select Parent Incident',\n options: selected.map(item => ({value: item.model.id, label: item.model.id})),\n required: true\n }\n ]\n });\n if (!result) return;\n\n // Find the parent model from selected items\n const parentModel = selected.find(item => item.model.id == result.merge)?.model;\n if (!parentModel) return;\n\n // Get list of all IDs to merge (excluding the parent)\n const mergeIds = selected\n .map(item => item.model.id)\n .filter(id => id != result.merge);\n\n // Save the merge operation to the parent model\n await parentModel.save({ merge: mergeIds });\n this.tableView.collection.fetch();\n }\n}\n\nexport default IncidentTablePage;\n","/**\n * EventView - Detailed view for an IncidentEvent record\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport DataView from '@core/views/data/DataView.js';\nimport StackTraceView from '@core/views/data/StackTraceView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport { IncidentEvent } from '@core/models/Incident.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\n\nclass EventView extends View {\n constructor(options = {}) {\n super({\n className: 'event-view',\n ...options\n });\n\n this.model = options.model || new IncidentEvent(options.data || {});\n this.eventIcon = this.getIconForEvent(this.model.get('level'));\n\n this.template = `\n <div class=\"event-view-container\">\n <!-- Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-4\">\n <div class=\"d-flex align-items-center gap-3\">\n <div class=\"fs-1 {{eventIcon.color}}\">\n <i class=\"bi {{eventIcon.icon}}\"></i>\n </div>\n <div>\n <h3 class=\"mb-1\">{{model.title|default('System Event')}}</h3>\n <div class=\"text-muted small\">\n Category: {{model.category|capitalize}}\n </div>\n <div class=\"text-muted small mt-1\">\n {{model.created|datetime}} from {{model.source_ip|default('Unknown IP')}}\n </div>\n </div>\n </div>\n <div data-container=\"event-context-menu\"></div>\n </div>\n\n <!-- Body -->\n <div data-container=\"event-tabs\"></div>\n </div>\n `;\n }\n\n getIconForEvent(level) {\n if (level >= 40) return { icon: 'bi-exclamation-octagon-fill', color: 'text-danger' }; // Error\n if (level >= 30) return { icon: 'bi-exclamation-triangle-fill', color: 'text-warning' }; // Warning\n if (level >= 20) return { icon: 'bi-info-circle-fill', color: 'text-info' }; // Info\n return { icon: 'bi-bell-fill', color: 'text-secondary' }; // Debug/Default\n }\n\n async onInit() {\n // Overview Tab\n this.overviewView = new DataView({\n model: this.model,\n className: \"p-3\",\n columns: 2,\n fields: [\n { name: 'id', label: 'Event ID' },\n { name: 'level', label: 'Level' },\n { name: 'hostname', label: 'Hostname' },\n { name: 'incident', label: 'Incident ID' },\n { name: 'model_name', label: 'Related Model' },\n { name: 'model_id', label: 'Related Model ID' },\n { name: 'details', label: 'Details', columns: 12 },\n ]\n });\n\n const tabs = { 'Overview': this.overviewView };\n \n const metadata = this.model.get('metadata') || {};\n \n // Add Stack Trace tab if present\n if (metadata.stack_trace) {\n this.stackTraceView = new StackTraceView({\n stackTrace: metadata.stack_trace\n });\n tabs['Stack Trace'] = this.stackTraceView;\n }\n \n // Add Metadata tab if there's metadata\n if (Object.keys(metadata).length > 0) {\n this.metadataView = new View({\n model: this.model,\n template: `<pre class=\"bg-light p-3 border rounded\"><code>{{{model.metadata|json}}}</code></pre>`\n });\n tabs['Metadata'] = this.metadataView;\n }\n\n this.tabView = new TabView({\n containerId: 'event-tabs',\n tabs: tabs,\n activeTab: 'Overview'\n });\n this.addChild(this.tabView);\n\n // ContextMenu\n const menuItems = [\n { label: 'View Incident', action: 'view-incident', icon: 'bi-shield-exclamation', disabled: !this.model.get('incident') },\n { label: 'View Related Model', action: 'view-model', icon: 'bi-box-arrow-up-right', disabled: !this.model.get('model_id') },\n { type: 'divider' },\n { label: 'Delete Event', action: 'delete-event', icon: 'bi-trash', danger: true }\n ];\n\n const eventMenu = new ContextMenu({\n containerId: 'event-context-menu',\n className: \"context-menu-view header-menu-absolute\",\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: menuItems\n }\n });\n this.addChild(eventMenu);\n }\n\n async onActionViewIncident() {\n console.log(\"TODO: View incident\", this.model.get('incident'));\n }\n\n async onActionViewModel() {\n console.log(\"TODO: View model\", this.model.get('model_name'), this.model.get('model_id'));\n }\n\n async onActionDeleteEvent() {\n const confirmed = await Dialog.confirm(\n `Are you sure you want to delete this event? This action cannot be undone.`,\n 'Confirm Deletion',\n { confirmClass: 'btn-danger', confirmText: 'Delete' }\n );\n if (confirmed) {\n const resp = await this.model.destroy();\n if (resp.success) {\n this.emit('event:deleted', { model: this.model });\n }\n }\n }\n}\n\nIncidentEvent.VIEW_CLASS = EventView;\n\nexport default EventView;\n","/**\n * EventTablePage - System events management using TablePage component\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { IncidentEventList, IncidentEventForms } from '@core/models/Incident.js';\nimport EventView from './EventView.js';\n\nclass EventTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_events',\n pageName: 'System Events',\n router: \"admin/events\",\n Collection: IncidentEventList,\n\n formEdit: IncidentEventForms.edit,\n itemViewClass: EventView,\n\n viewDialogOptions: {\n header: false,\n size: 'lg'\n },\n\n defaultQuery: {\n sort: '-id',\n category__not: \"ossec\",\n },\n\n // Column definitions\n columns: [\n { key: 'created', label: 'Timestamp', sortable: true, formatter: 'datetime' },\n {\n key: 'level', label: 'Level',\n sortable: true, formatter: 'badge',\n filter: {\n type: \"select\",\n options: [\n { value: '5', label: 'Critical' },\n { value: '4', label: 'Warning' },\n { value: '3', label: 'Info' },\n { value: '2', label: 'Debug' },\n { value: '1', label: 'Trace' }\n ]\n }\n },\n {\n key: 'scope',\n label: 'Scope',\n sortable: true, formatter: 'badge',\n filter: {\n type: \"combobox\",\n options: [\n { value: 'account', label: 'Account' },\n { value: 'incident', label: 'Incident' },\n { value: 'ossec', label: 'OSSEC' },\n { value: 'fileman', label: 'File Manager' },\n { value: 'metrics', label: 'Metrics' },\n { value: 'jobs', label: 'Jobs' },\n { value: 'aws', label: 'AWS' }\n\n ]\n }\n },\n {\n key: 'category',\n label: 'Category',\n sortable: true, formatter: 'badge',\n filter: {\n type: \"combobox\",\n options: [\n { value: 'rest_error', label: 'Rest Error' },\n { value: 'api_error', label: 'API Error' },\n { value: 'auth', label: 'Auth' },\n { value: 'database', label: 'Database' }\n ]\n }\n },\n { key: 'title', label: 'Title', sortable: true, formatter: 'truncate(50)' },\n {\n key: 'source_ip', label: 'Source IP', sortable: true,\n filter: {\n type: \"text\"\n }\n },\n {\n key: 'metadata.server', label: 'Server',\n sortable: true,\n filter: {\n type: \"text\"\n }\n },\n ],\n\n filters: [\n {\n key: 'category__not',\n label: 'Not Category',\n filter: {type:\"text\"}\n },\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: false,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No events found.',\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n}\n\nexport default EventTablePage;\n","import { TicketNote, TicketNoteList } from '@core/models/Tickets.js';\n\nclass TicketNoteAdapter {\n constructor(ticketId) {\n this.ticketId = ticketId;\n this.collection = new TicketNoteList({ params: { parent: this.ticketId, sort: 'created', size: 100} });\n }\n\n async fetch() {\n await this.collection.fetch();\n return this.collection.models.map(note => this.transform(note));\n }\n\n transform(note) {\n return {\n id: note.get('id'),\n type: 'user_comment', // Ticket notes are always user comments\n author: {\n id: note.get('user.id'),\n name: note.get('user.display_name') || 'System',\n avatarUrl: note.get('user.avatar.url')\n },\n timestamp: note.get('created'),\n content: note.get('note'),\n attachments: note.get('media') ? [note.get('media')] : []\n };\n }\n\n async addNote(data) {\n const note = new TicketNote();\n const resp = await note.save({\n parent: this.ticketId,\n note: data.text,\n media: data.files && data.files.length > 0 ? data.files[0].id : null\n });\n if (resp.success) {\n await this.collection.fetch(); // Refresh the collection\n }\n return resp;\n }\n}\n\nexport default TicketNoteAdapter;\n","import View from '@core/View.js';\nimport DataView from '@core/views/data/DataView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport { Ticket, TicketForms } from '@core/models/Tickets.js';\nimport ChatView from '@core/views/chat/ChatView.js';\nimport TicketNoteAdapter from './adapters/TicketNoteAdapter.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\n\nclass TicketView extends View {\n constructor(options = {}) {\n super({\n className: 'ticket-view',\n ...options\n });\n\n this.model = options.model || new Ticket(options.data || {});\n\n this.template = `\n <div class=\"ticket-view-container d-flex flex-column h-100\">\n <!-- Ticket Header -->\n <div class=\"d-flex justify-content-between align-items-start mb-3 flex-shrink-0\">\n <!-- Left Side: Primary Identity -->\n <div class=\"d-flex align-items-center gap-3\">\n <div class=\"avatar-placeholder rounded-circle bg-light d-flex align-items-center justify-content-center\" style=\"width: 80px; height: 80px;\">\n <i class=\"bi bi-ticket-perforated text-secondary\" style=\"font-size: 40px;\"></i>\n </div>\n <div>\n <h3 class=\"mb-1\">{{model.title|truncate(50)|default('Untitled Ticket')}}</h3>\n <div class=\"text-muted small\">\n <span>Ticket #{{model.id}}</span>\n <span class=\"mx-2\">|</span>\n <span>Priority: {{model.priority|capitalize}}</span>\n {{#model.assignee}}\n <span class=\"mx-2\">|</span>\n <span>Assigned to: {{model.assignee.display_name}}</span>\n {{/model.assignee}}\n </div>\n {{#model.incident}}\n <div class=\"text-muted small mt-1\">\n <i class=\"bi bi-exclamation-triangle\"></i> Related to incident: {{model.incident}}\n </div>\n {{/model.incident}}\n </div>\n </div>\n\n <!-- Right Side: Status & Actions -->\n <div class=\"d-flex align-items-start gap-4\">\n <div class=\"text-end\">\n <div class=\"d-flex align-items-center gap-2\">\n <span class=\"badge {{model.status|badgeClass}}\">{{model.status|capitalize}}</span>\n </div>\n {{#model.created}}\n <div class=\"text-muted small mt-1\">Created {{model.created|relative}}</div>\n {{/model.created}}\n {{#model.modified}}\n <div class=\"text-muted small\">Updated {{model.modified|relative}}</div>\n {{/model.modified}}\n </div>\n <div data-container=\"ticket-context-menu\"></div>\n </div>\n </div>\n\n <!-- Chat View (Full height) -->\n <div class=\"flex-grow-1\" style=\"min-height: 0;\" data-container=\"chat-view\"></div>\n </div>\n `;\n }\n\n async onInit() {\n // Chat View with compact theme (Option 4)\n const adapter = new TicketNoteAdapter(this.model.get('id'));\n this.chatView = new ChatView({\n containerId: 'chat-view',\n adapter: adapter,\n theme: 'compact', // Use compact admin-style theme\n currentUserId: this.getCurrentUserId(),\n inputPlaceholder: 'Add a note...',\n inputButtonText: 'Add Note'\n });\n this.addChild(this.chatView);\n\n // Context Menu\n const ticketMenu = new ContextMenu({\n containerId: 'ticket-context-menu',\n className: \"context-menu-view header-menu-absolute\",\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: [\n { label: 'Edit Ticket', action: 'edit-ticket', icon: 'bi-pencil' },\n { label: 'Change Status', action: 'change-status', icon: 'bi-tag' },\n { label: 'Set Priority', action: 'set-priority', icon: 'bi-flag' },\n { label: 'Assign User', action: 'assign-user', icon: 'bi-person' },\n { type: 'divider' },\n { label: 'Close Ticket', action: 'close-ticket', icon: 'bi-x-circle' },\n ]\n }\n });\n this.addChild(ticketMenu);\n }\n\n /**\n * Get current user ID for chat message positioning\n * @returns {number|null}\n */\n getCurrentUserId() {\n // Get from WebApp state or wherever your app stores current user\n const currentUser = window.app?.state?.user;\n return currentUser?.id || null;\n }\n\n // Context Menu Action Handlers\n async onActionEditTicket() {\n const resp = await Dialog.showModelForm({\n title: `Edit Ticket #${this.model.get('id')} - ${this.model.get('title')}`,\n model: this.model,\n size: 'lg',\n fields: TicketForms.edit.fields\n });\n if (resp) {\n this.render(); // Re-render to show updated data in header\n }\n }\n\n async onActionChangeStatus() {\n const statuses = ['new', 'open', 'in_progress', 'pending', 'resolved', 'closed', 'ignored'];\n const currentStatus = this.model.get('status');\n\n const result = await Dialog.showForm({\n title: 'Change Ticket Status',\n size: 'sm',\n fields: [\n {\n name: 'status',\n label: 'New Status',\n type: 'select',\n options: statuses.map(s => ({ value: s, label: s.replace('_', ' ').toUpperCase() })),\n value: currentStatus,\n required: true\n }\n ]\n });\n\n if (result) {\n try {\n await this.model.save({ status: result.status });\n this.render();\n } catch (error) {\n Dialog.alert({\n type: 'error',\n title: 'Error',\n message: 'Failed to update ticket status: ' + error.message\n });\n }\n }\n }\n\n async onActionSetPriority() {\n const priorities = ['low', 'normal', 'high', 'urgent'];\n const currentPriority = this.model.get('priority');\n\n const result = await Dialog.showForm({\n title: 'Set Ticket Priority',\n size: 'sm',\n fields: [\n {\n name: 'priority',\n label: 'Priority Level',\n type: 'select',\n options: priorities.map(p => ({ value: p, label: p.toUpperCase() })),\n value: currentPriority,\n required: true\n }\n ]\n });\n\n if (result) {\n try {\n await this.model.save({ priority: result.priority });\n this.render();\n } catch (error) {\n Dialog.alert({\n type: 'error',\n title: 'Error',\n message: 'Failed to update ticket priority: ' + error.message\n });\n }\n }\n }\n\n async onActionAssignUser() {\n console.log(\"TODO: Implement assign user dialog with user selector\");\n Dialog.alert({\n title: 'Coming Soon',\n message: 'User assignment feature will be implemented soon.'\n });\n }\n\n async onActionCloseTicket() {\n const confirmed = await Dialog.confirm({\n title: 'Close Ticket',\n message: `Are you sure you want to close ticket #${this.model.get('id')}?`,\n confirmText: 'Close Ticket',\n confirmClass: 'btn-warning'\n });\n\n if (confirmed) {\n try {\n await this.model.save({ status: 'closed' });\n this.render();\n Dialog.alert({\n type: 'success',\n title: 'Success',\n message: 'Ticket has been closed successfully.'\n });\n } catch (error) {\n Dialog.alert({\n type: 'error',\n title: 'Error',\n message: 'Failed to close ticket: ' + error.message\n });\n }\n }\n }\n}\n\nTicket.VIEW_CLASS = TicketView;\n\nexport default TicketView;\n","/**\n * TicketTablePage - Ticket management using TablePage component\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { TicketList, TicketForms, TicketCategories } from '@core/models/Tickets.js';\nimport TicketView from './TicketView.js';\n\nclass TicketTablePage extends TablePage {\n constructor(options = {}) {\n super({\n name: 'admin_tickets',\n pageName: 'Tickets',\n router: \"admin/tickets\",\n Collection: TicketList,\n\n formCreate: TicketForms.create,\n formEdit: TicketForms.edit,\n itemViewClass: TicketView,\n\n viewDialogOptions: {\n header: false\n },\n\n // Column definitions\n columns: [\n { key: 'id', label: 'ID', width: '70px', sortable: true, class: 'text-muted' },\n { key: 'title', label: 'Title', sortable: true},\n {\n key: 'status', label: 'Status', sortable: true,\n editable: true,\n editableOptions: {\n type: \"select\",\n options: [\n \"new\", \"open\", \"paused\", \"resolved\", \"qa\", \"ignored\"\n ]\n },\n filter: {\n type: \"multiselect\",\n placeHolder: \"Select Status\",\n options: [\n \"new\", \"open\", \"paused\", \"resolved\", \"qa\", \"ignored\"\n ]\n }\n },\n { key: 'priority', label: 'Priority', sortable: true },\n {\n key: 'category', label: 'Category', sortable: true,\n editable: true,\n editableOptions: {\n type: \"select\",\n options: [\n ... Object.keys(TicketCategories)\n ]\n },\n filter: {\n type: \"multiselect\",\n placeHolder: \"Select Category\",\n options: [\n ... Object.keys(TicketCategories)\n ]\n }\n },\n { key: 'assignee.display_name', label: 'Assignee', sortable: true, formatter: \"default('Unassigned')\" },\n { key: 'incident.id', label: 'Incident ID', sortable: true },\n { key: 'created', label: 'Created', sortable: true, formatter: 'datetime' }\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No tickets found.',\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n },\n ...options,\n });\n }\n}\n\nexport default TicketTablePage;\n","import View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport DataView from '@core/views/data/DataView.js';\nimport TableView from '@core/views/table/TableView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport { RuleSet, RuleList, BundleByOptions, MatchByOptions } from '@core/models/Incident.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\n\nclass RuleSetView extends View {\n constructor(options = {}) {\n super({\n className: 'ruleset-view',\n ...options\n });\n\n this.model = options.model || new RuleSet(options.data || {});\n\n this.template = `\n <div class=\"ruleset-view-container\">\n <!-- Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-4\">\n <div class=\"d-flex align-items-center gap-3\">\n <div class=\"fs-1 text-primary\"><i class=\"bi bi-gear-wide-connected\"></i></div>\n <div>\n <h3 class=\"mb-1\">{{model.name}}</h3>\n <div class=\"text-muted small\">Category: {{model.category}} | Priority: {{model.priority}}</div>\n </div>\n </div>\n <div data-container=\"ruleset-context-menu\"></div>\n </div>\n\n <!-- Tabs -->\n <div data-container=\"ruleset-tabs\"></div>\n </div>\n `;\n }\n\n async onInit() {\n // Get labels for current values\n const matchByValue = this.model.get('match_by');\n const matchByOption = MatchByOptions.find(opt => opt.value === matchByValue);\n const matchByLabel = matchByOption ? matchByOption.label : String(matchByValue);\n\n const bundleByValue = this.model.get('bundle_by');\n const bundleByOption = BundleByOptions.find(opt => opt.value === bundleByValue);\n const bundleByLabel = bundleByOption ? bundleByOption.label : String(bundleByValue);\n\n // Config Tab\n this.configView = new DataView({\n model: this.model,\n className: \"p-3\",\n columns: 2,\n fields: [\n { name: 'name', label: 'Name', cols: 4 },\n { name: 'category', label: 'Category', formatter: 'badge', cols: 4 },\n { name: 'is_active', label: 'Is Active', formatter: 'yesno_icon', cols: 4 },\n { name: 'priority', label: 'Priority', cols: 4 },\n { name: 'id', label: 'RuleSet ID', cols: 4 },\n\n {\n name: 'match_by',\n label: 'Match Logic',\n template: matchByLabel,\n cols: 4\n },\n {\n name: 'bundle_by',\n label: 'Bundle By',\n template: bundleByLabel,\n cols: 4\n },\n { name: 'bundle_minutes', label: 'Bundle Minutes', cols: 4 },\n { name: 'bundle_by_rule_set', label: 'Bundle By Rule Set', formatter: 'yesno_icon', cols: 4 },\n { name: 'handler', label: 'Handler', cols: 12 },\n ]\n });\n\n // Rules Tab\n const rulesCollection = new RuleList({\n params: { parent: this.model.get('id') }\n });\n this.rulesView = new TableView({\n collection: rulesCollection,\n hideActivePillNames: ['parent'],\n columns: [\n { key: 'id', label: 'ID', width: '70px' },\n { key: 'name', label: 'Name' },\n { key: 'field_name', label: 'Field' },\n { key: 'comparator', label: 'Comparator', width: '120px' },\n { key: 'value', label: 'Value' },\n { key: 'value_type', label: 'Type', width: '100px' },\n ],\n showAdd: true,\n clickAction: 'edit',\n actions: ['edit', 'delete'],\n contextMenu: [\n { label: 'Edit Rule', action: 'edit', icon: 'bi-pencil' },\n { label: 'Duplicate Rule', action: 'duplicate', icon: 'bi-files' },\n { divider: true },\n { label: 'Delete Rule', action: 'delete', icon: 'bi-trash', danger: true }\n ],\n // Pass the parent ID so new rules get associated with this ruleset\n addFormDefaults: {\n parent: this.model.get('id')\n }\n });\n\n this.tabView = new TabView({\n containerId: 'ruleset-tabs',\n tabs: {\n 'Configuration': this.configView,\n 'Rules': this.rulesView\n },\n activeTab: 'Configuration'\n });\n this.addChild(this.tabView);\n\n const contextMenu = new ContextMenu({\n containerId: 'ruleset-context-menu',\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: [\n { label: 'Edit RuleSet', action: 'edit-ruleset', icon: 'bi-pencil' },\n { label: 'Disable', action: 'disable-ruleset', icon: 'bi-toggle-off' },\n { type: 'divider' },\n { label: 'Delete RuleSet', action: 'delete-ruleset', icon: 'bi-trash', danger: true }\n ]\n }\n });\n this.addChild(contextMenu);\n }\n\n /**\n * Action handler: Edit RuleSet\n */\n async onActionEditRuleset() {\n const resp = await Dialog.showModelForm({\n title: `Edit RuleSet - ${this.model.get('name')}`,\n model: this.model,\n formConfig: RuleSet.EDIT_FORM,\n });\n if (resp) {\n await this.render();\n }\n }\n\n /**\n * Action handler: Disable/Enable RuleSet\n */\n async onActionDisableRuleset() {\n const isActive = this.model.get('is_active');\n const newStatus = !isActive;\n\n try {\n this.model.set('is_active', newStatus);\n await this.model.save();\n await this.render();\n\n Dialog.showToast({\n message: `RuleSet ${newStatus ? 'enabled' : 'disabled'} successfully`,\n type: 'success'\n });\n } catch (error) {\n Dialog.showToast({\n message: `Failed to update RuleSet: ${error.message}`,\n type: 'error'\n });\n }\n }\n\n /**\n * Action handler: Delete RuleSet\n */\n async onActionDeleteRuleset() {\n const confirmed = await Dialog.confirm({\n title: 'Delete RuleSet',\n message: `Are you sure you want to delete the ruleset \"${this.model.get('name')}\"? This action cannot be undone.`,\n confirmText: 'Delete',\n confirmClass: 'btn-danger'\n });\n\n if (confirmed) {\n try {\n await this.model.destroy();\n\n Dialog.showToast({\n message: 'RuleSet deleted successfully',\n type: 'success'\n });\n\n // Close the dialog\n const dialog = this.element?.closest('.modal');\n if (dialog) {\n const bsModal = bootstrap.Modal.getInstance(dialog);\n if (bsModal) {\n bsModal.hide();\n }\n }\n\n // Emit event to parent to refresh the table\n this.emit('ruleset:deleted', { model: this.model });\n } catch (error) {\n Dialog.showToast({\n message: `Failed to delete RuleSet: ${error.message}`,\n type: 'error'\n });\n }\n }\n }\n}\n\nRuleSetView.VIEW_CLASS = RuleSetView;\n\nexport default RuleSetView;\n","import TablePage from '@core/pages/TablePage.js';\nimport { RuleSetList } from '@core/models/Incident.js';\nimport RuleSetView from './RuleSetView.js';\n\nclass RuleSetTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_rulesets',\n pageName: 'Rule Engine',\n router: \"admin/rulesets\",\n Collection: RuleSetList,\n itemView: RuleSetView,\n viewDialogOptions: {\n header: false,\n size: 'xl'\n },\n\n columns: [\n { key: 'id', label: 'ID', width: '70px', sortable: true, class: 'text-muted' },\n { key: 'name', label: 'Name', sortable: true },\n { key: 'category', label: 'Category', sortable: true, formatter: 'badge' },\n { key: 'priority', label: 'Priority', sortable: true },\n { key: 'match_by', label: 'Match Logic', formatter: (v) => v === 0 ? 'ALL' : 'ANY' }\n ],\n\n selectable: true,\n searchable: true,\n sortable: true,\n paginated: true,\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n tableOptions: {\n pageSizes: [10, 25, 50],\n defaultPageSize: 25,\n emptyMessage: 'No rule sets found.',\n emptyIcon: 'bi-gear',\n actions: [\"view\", \"edit\", \"delete\"],\n }\n });\n }\n}\n\nexport default RuleSetTablePage;\n","/**\n * EmailDomainTablePage - Admin page for managing SES/SNS Email Domains\n * Clean implementation following simplified TablePage pattern\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport View from '@core/View.js';\nimport { EmailDomain, EmailDomainList, EmailDomainForms } from '@core/models/Email.js';\n\nclass EmailDomainTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_email_domains',\n pageName: 'Email Domains',\n router: 'admin/email/domains',\n Collection: EmailDomainList,\n formCreate: EmailDomainForms.create,\n formEdit: EmailDomainForms.edit,\n\n // Column definitions\n columns: [\n {\n key: 'id',\n label: 'ID',\n width: '70px',\n sortable: true,\n class: 'text-muted'\n },\n {\n key: 'name',\n label: 'Domain',\n sortable: true\n },\n {\n key: 'region',\n label: 'Region',\n sortable: true,\n formatter: \"default('—')\"\n },\n {\n key: 'receiving_enabled',\n label: 'Receiving',\n formatter: \"boolean|badge\"\n },\n {\n key: 'can_send',\n label: 'Send Verified',\n formatter: \"boolean|badge\"\n },\n {\n key: 'can_recv',\n label: 'Recv Verified',\n formatter: \"boolean|badge\"\n },\n {\n key: 'created',\n label: 'Created',\n formatter: \"epoch|datetime\"\n }\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No domains found. Click \"Add Domain\" to get started.',\n\n // Context menu configuration\n contextMenu: [\n {\n icon: 'bi-shield-check',\n action: 'edit-aws-creds',\n label: 'Edit AWS Credentials'\n },\n {\n icon: 'bi-rocket-takeoff',\n action: 'onboard',\n label: 'Onboard'\n },\n {\n icon: 'bi-shield-check',\n action: 'audit',\n label: 'Audit'\n },\n {\n icon: 'bi-arrow-repeat',\n action: 'reconcile',\n label: 'Reconcile'\n }\n ],\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n\n async onActionEditAwsCreds(event, element) {\n const item = this.collection.get(element.dataset.id);\n const result = await Dialog.showModelForm({\n model: item,\n formConfig: EmailDomainForms.credentials,\n }\n );\n return true;\n }\n\n async onActionOnboard(event, element) {\n const item = this.collection.get(element.dataset.id);\n const model = new EmailDomain({ id: item.id });\n\n const formData = await Dialog.showForm(EmailDomainForms.onboard);\n if (!formData) return;\n\n try {\n const resp = await model.onboard(formData);\n if (!resp.success) {\n throw new Error(resp.message || 'Network error during onboarding');\n }\n if (!resp.data?.status) {\n throw new Error(resp.data?.error || 'Onboarding failed');\n }\n\n this.getApp()?.toast?.success('Domain onboarding completed successfully');\n await this.refresh();\n } catch (err) {\n console.error('Onboard error:', err);\n this.showError(err.message || 'Failed to onboard domain');\n }\n }\n\n async onActionAudit(event, element) {\n const item = this.collection.get(element.dataset.id);\n const model = new EmailDomain({ id: item.id });\n\n try {\n const resp = await model.audit();\n if (!resp.success) {\n throw new Error(resp.message || 'Network error during audit');\n }\n if (!resp.data?.status) {\n throw new Error(resp.data?.error || 'Audit failed');\n }\n\n const result = resp.data?.data || {};\n await Dialog.showDialog({\n title: `Audit Report - ${item.name}`,\n body: new View({\n template: `\n <div>\n <p class=\"text-muted\">Drift report and status:</p>\n <pre class=\"bg-light p-3 rounded small\"><code>{{{data.result|json}}}</code></pre>\n </div>\n `,\n data: { result }\n }),\n size: 'lg'\n });\n } catch (err) {\n console.error('Audit error:', err);\n this.showError(err.message || 'Failed to audit domain');\n }\n }\n\n async onActionReconcile(event, element) {\n const item = this.collection.get(element.dataset.id);\n const model = new EmailDomain({ id: item.id });\n\n try {\n const resp = await model.reconcile();\n if (!resp.success) {\n throw new Error(resp.message || 'Network error during reconcile');\n }\n if (!resp.data?.status) {\n throw new Error(resp.data?.error || 'Reconcile failed');\n }\n\n this.getApp()?.toast?.success('Reconcile completed successfully');\n await this.refresh();\n } catch (err) {\n console.error('Reconcile error:', err);\n this.showError(err.message || 'Failed to reconcile domain');\n }\n }\n}\n\nexport default EmailDomainTablePage;\n","/**\n * EmailMailboxTablePage - Admin page for managing Mailboxes\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { MailboxList, MailboxForms, Mailbox } from '@core/models/Email.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\n\nclass EmailMailboxTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_email_mailboxes',\n pageName: 'Mailboxes',\n router: 'admin/email/mailboxes',\n Collection: MailboxList,\n\n formCreate: MailboxForms.create,\n formEdit: MailboxForms.edit,\n clickAction: \"edit\",\n\n // Table columns\n columns: [\n { key: 'id', label: 'ID', width: '70px', sortable: true, class: 'text-muted' },\n { key: 'email', label: 'Email', sortable: true },\n { key: 'domain.name', label: 'Domain', sortable: true, formatter: \"default('—')\" },\n { key: 'allow_inbound', label: 'Inbound', formatter: \"boolean|badge\" },\n { key: 'allow_outbound', label: 'Outbound', formatter: \"boolean|badge\" },\n { key: 'is_system_default', label: 'System Default', formatter: \"boolean|badge\" },\n { key: 'is_domain_default', label: 'Domain Default', formatter: \"boolean|badge\" }\n ],\n\n // Features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No mailboxes found. Click \"Add Mailbox\" to create one.',\n\n // Context menu configuration\n contextMenu: [\n { icon: 'bi-envelope', action: 'send-email', label: 'Send Email' },\n { icon: 'bi-envelope', action: 'send-template-email', label: 'Send Template Email' }\n ],\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n\n async onActionSendEmail(event, element) {\n const item = this.collection.get(element.dataset.id);\n // Implement send email action\n const data = await Dialog.showForm({\n title: 'Send Email',\n fields: [\n { name: 'to', label: 'To', type: 'email', required: true },\n { name: 'subject', label: 'Subject', type: 'text', required: true },\n { name: 'body_html', label: 'Body', type: 'textarea', required: true }\n ]\n });\n data.from_email = item.get('email');\n const result = await Mailbox.sendEmail(data);\n if (result.success) {\n this.getApp().toast.success('Email sent successfully');\n } else {\n let msg = \"Failed to send email\";\n if (result.data.details) {\n msg = result.data.details;\n } else if (result.data.error) {\n msg = result.data.error;\n }\n console.log(result);\n this.getApp().toast.error(msg);\n }\n }\n\n async onActionSendTemplateEmail(event, element) {\n const item = this.collection.get(element.dataset.id);\n // Implement send email action\n const data = await Dialog.showForm({\n title: 'Send Email',\n fields: [\n { name: 'to', label: 'To', type: 'email', required: true },\n { name: 'subject', label: 'Subject', type: 'text', required: true },\n { name: 'template_name', label: 'Template', type: 'text', required: true },\n { name: 'template_context', label: 'Context', type: 'textarea', required: true }\n ]\n });\n data.from_email = item.get('email');\n const result = await Mailbox.sendEmail(data);\n if (result.success) {\n this.getApp().toast.success('Email sent successfully');\n } else {\n let msg = \"Failed to send email\";\n if (result.data.details) {\n msg = result.data.details;\n } else if (result.data.error) {\n msg = result.data.error;\n }\n console.log(result);\n this.getApp().toast.error(msg);\n }\n }\n}\n\nexport default EmailMailboxTablePage;\n","/**\n * EmailTemplateView - A clean view for previewing email templates\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport { EmailTemplate } from '@core/models/Email.js';\n\nclass EmailTemplateView extends View {\n constructor(options = {}) {\n super({\n className: 'email-template-view',\n ...options\n });\n\n this.model = options.model || new EmailTemplate(options.data || {});\n this.hasHtml = !!this.model.get('html_template');\n this.hasText = !!this.model.get('text_template');\n this.hasMetadata = this.model.get('metadata') && Object.keys(this.model.get('metadata')).length > 0;\n\n this.template = `\n <div class=\"email-template-view-container p-3\">\n <!-- Header -->\n <div class=\"template-header border-bottom pb-3 mb-3\">\n <h4 class=\"mb-1\">{{model.name}}</h4>\n <div class=\"text-muted\">\n <strong>Subject:</strong> {{model.subject_template|default('Not set')}}\n </div>\n </div>\n\n <!-- Tabs -->\n <div data-container=\"template-tabs\"></div>\n </div>\n `;\n }\n\n async onInit() {\n const tabs = {};\n\n if (this.hasHtml) {\n tabs['HTML Preview'] = new View({\n model: this.model,\n template: `<div class=\"email-html-content border rounded p-3\" style=\"height: 500px; overflow-y: auto;\">{{model.html_template}}</div>`\n });\n }\n\n if (this.hasText) {\n tabs['Text Version'] = new View({\n model: this.model,\n template: `<pre class=\"email-text-content p-3 bg-light border rounded\" style=\"white-space: pre-wrap; word-wrap: break-word;\">{{model.text_template}}</pre>`\n });\n }\n\n if (this.hasMetadata) {\n tabs['Metadata'] = new View({\n model: this.model,\n template: `<pre class=\"email-metadata-content p-3 bg-light border rounded\"><code>{{model.metadata|json}}</code></pre>`\n });\n }\n\n this.tabView = new TabView({\n containerId: 'template-tabs',\n tabs: tabs,\n activeTab: Object.keys(tabs)[0] || ''\n });\n this.addChild(this.tabView);\n }\n}\n\nexport default EmailTemplateView;\n","/**\n * EmailTemplateTablePage - Admin page for managing Email Templates\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { EmailTemplateList, EmailTemplateForms } from '@core/models/Email.js';\nimport EmailTemplateView from './EmailTemplateView.js';\n\nclass EmailTemplateTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_email_templates',\n pageName: 'Email Templates',\n router: 'admin/email/templates',\n Collection: EmailTemplateList,\n\n formCreate: EmailTemplateForms.create,\n formEdit: EmailTemplateForms.edit,\n itemViewClass: EmailTemplateView,\n clickAction: \"edit\",\n\n viewDialogOptions: {\n header: false,\n size: 'xl',\n scrollable: true\n },\n\n formDialogConfig: { // Dialog options for forms\n size: 'fullscreen',\n },\n\n // Table columns\n columns: [\n { key: 'id', label: 'ID', width: '70px', sortable: true, class: 'text-muted' },\n { key: 'name', label: 'Name', sortable: true },\n { key: 'created', label: 'Created', formatter: 'datetime' },\n { key: 'modified', label: 'Modified', formatter: 'datetime' }\n ],\n\n // Features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No email templates found. Click \"Add Template\" to create your first one.',\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n}\n\nexport default EmailTemplateTablePage;\n","/**\n * EmailView - A slick, email-client-like view for sent messages\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport { SentMessage } from '@core/models/Email.js';\n\nclass EmailView extends View {\n constructor(options = {}) {\n super({\n className: 'email-view',\n ...options\n });\n\n this.model = options.model || new SentMessage(options.data || {});\n this.hasHtml = !!this.model.get('body_html');\n this.hasText = !!this.model.get('body_text');\n this.hasContext = this.model.get('template_context') && Object.keys(this.model.get('template_context')).length > 0;\n\n this.template = `\n <div class=\"email-view-container p-3\">\n <!-- Email Header -->\n <div class=\"email-header border-bottom pb-3 mb-3\">\n <h4 class=\"mb-2\">{{model.subject}}</h4>\n <div class=\"d-flex justify-content-between align-items-center\">\n <div class=\"d-flex align-items-center\">\n <div class=\"flex-shrink-0\">\n <i class=\"bi bi-person-circle fs-2 text-secondary\"></i>\n </div>\n <div class=\"ms-3\">\n <div class=\"fw-bold\">{{model.mailbox.email}}</div>\n <div class=\"text-muted small\">To: {{model.to_addresses|list}}</div>\n </div>\n </div>\n <div class=\"text-end\">\n <div class=\"text-muted small\">{{model.created|datetime}}</div>\n <span class=\"badge {{model.status|badge}} mt-1\">{{model.status|capitalize}}</span>\n </div>\n </div>\n </div>\n\n <!-- Email Body Tabs -->\n <div data-container=\"email-tabs\"></div>\n </div>\n `;\n }\n\n async onInit() {\n const tabs = {};\n\n if (this.hasHtml) {\n tabs['HTML'] = new View({\n model: this.model,\n template: `<div class=\"email-html-content border rounded p-3\" style=\"height: 500px; overflow-y: auto;\">{{{model.body_html}}}</div>`\n });\n }\n\n if (this.hasText) {\n tabs['Text'] = new View({\n model: this.model,\n template: `<pre class=\"email-text-content p-3 bg-light border rounded\" style=\"white-space: pre-wrap; word-wrap: break-word;\">{{model.body_text}}</pre>`\n });\n }\n\n if (this.hasContext) {\n tabs['Context'] = new View({\n model: this.model,\n template: `<pre class=\"email-context-content p-3 bg-light border rounded\"><code>{{model.template_context|json}}</code></pre>`\n });\n }\n\n this.tabView = new TabView({\n containerId: 'email-tabs',\n tabs: tabs,\n activeTab: this.hasHtml ? 'HTML' : (this.hasText ? 'Text' : 'Context')\n });\n this.addChild(this.tabView);\n }\n}\n\nSentMessage.VIEW_CLASS = EmailView;\n\n\nexport default EmailView;\n","/**\n * SentMessageTablePage - Admin page for viewing sent emails\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { SentMessageList } from '@core/models/Email.js';\nimport EmailView from './EmailView.js';\n\nclass SentMessageTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_email_sent',\n pageName: 'Sent Messages',\n router: 'admin/email/sent',\n Collection: SentMessageList,\n \n itemViewClass: EmailView,\n viewDialogOptions: {\n header: false,\n size: 'xl',\n scrollable: true\n },\n\n // Table columns\n columns: [\n { key: 'id', label: 'ID', width: '70px', sortable: true, class: 'text-muted' },\n { key: 'mailbox.email', label: 'From', sortable: true },\n { key: 'to_addresses', label: 'To', sortable: false, formatter: \"list\" },\n { key: 'subject', label: 'Subject', sortable: true },\n { key: 'status', label: 'Status', formatter: 'badge' },\n { key: 'status_reason', label: 'Reason', formatter: \"truncate(80)|default('—')\" },\n { key: 'created', label: 'Created', formatter: 'datetime' }\n ],\n\n // Features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: false,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No sent messages found.',\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n}\n\nexport default SentMessageTablePage;","/**\n * Phonehub.js\n * Models and Collections for PhoneHub phone numbers and SMS with helper actions.\n *\n * Endpoints (all require authentication unless noted):\n * - Phone Number\n * - POST /api/phonehub/number/normalize\n * - POST /api/phonehub/number/lookup\n * - GET /api/phonehub/number\n * - GET /api/phonehub/number/:id\n * - PUT /api/phonehub/number/:id\n * - DELETE /api/phonehub/number/:id\n *\n * - SMS\n * - POST /api/phonehub/sms/send\n * - GET /api/phonehub/sms\n * - GET /api/phonehub/sms/:id\n *\n * Notes on response shapes:\n * - List endpoints typically return { status: true, data: [...], count, size, start }\n * - Single item endpoints typically return { status: true, data: {...} }\n * - Action endpoints (normalize, lookup, send) return { status: true, data: {...} } or { status: false, error }\n */\n\nimport Model from '@core/Model.js';\nimport Collection from '@core/Collection.js';\nimport rest from '@core/Rest.js';\n\n// ======================================================\n// PhoneNumber Model & Collection\n// ======================================================\n\n/**\n * PhoneNumber - Represents a cached phone number lookup result.\n */\nclass PhoneNumber extends Model {\n constructor(data = {}, options = {}) {\n super(data, {\n endpoint: '/api/phonehub/number',\n ...options\n });\n }\n\n /**\n * Normalize an arbitrary phone_number string to E.164 format.\n * @param {string} phoneNumber - Raw phone number input.\n * @param {string} [countryCode='US'] - Optional country code (default US).\n * @returns {Promise<{success: boolean, phone_number?: string, data?: object, error?: string, response?: any}>}\n */\n static async normalize(phoneNumber, countryCode = 'US') {\n const url = '/api/phonehub/number/normalize';\n const payload = {\n phone_number: phoneNumber\n };\n if (countryCode) payload.country_code = countryCode;\n\n const resp = await rest.POST(url, payload);\n const body = resp?.data ?? resp; // rest wrapper or raw\n const ok = body?.status === true || body?.success === true;\n\n if (ok) {\n const normalized = body?.data?.phone_number ?? body?.phone_number;\n return { success: true, phone_number: normalized, data: body?.data ?? body, response: resp };\n }\n return { success: false, error: body?.error || 'Normalization failed', response: resp };\n }\n\n /**\n * Lookup phone number details (carrier, line_type, owner, etc.)\n * @param {string} phoneNumber - E.164 or raw phone number.\n * @param {object} [options] - { force_refresh?: boolean, group?: number }\n * @returns {Promise<{success: boolean, model?: PhoneNumber, data?: object, error?: string, response?: any}>}\n */\n static async lookup(phoneNumber, options = {}) {\n const url = '/api/phonehub/number/lookup';\n const resp = await rest.POST(url, {\n phone_number: phoneNumber,\n ...options\n });\n const body = resp?.data ?? resp;\n const ok = body?.status === true || body?.success === true;\n\n if (ok) {\n const data = body?.data ?? {};\n const model = new PhoneNumber(data, { endpoint: '/api/phonehub/number' });\n return { success: true, model, data, response: resp };\n }\n return { success: false, error: body?.error || 'Phone lookup failed', response: resp };\n }\n\n\n}\n\n/**\n * PhoneNumberList - Paginated collection of PhoneNumber models.\n */\nclass PhoneNumberList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: PhoneNumber,\n endpoint: '/api/phonehub/number',\n size: 10,\n ...options\n });\n }\n\n\n}\n\n// ======================================================\n// SMS Model & Collection\n// ======================================================\n\n/**\n * SMS - Represents an SMS message (sent or received).\n */\nclass SMS extends Model {\n constructor(data = {}, options = {}) {\n super(data, {\n endpoint: '/api/phonehub/sms',\n ...options\n });\n }\n\n /**\n * Send SMS via PhoneHub (Twilio under the hood).\n * @param {object} params - { to_number, body, from_number?, group?, metadata? }\n * @returns {Promise<{success: boolean, model?: SMS, data?: object, error?: string, response?: any}>}\n */\n static async send(params = {}) {\n const url = '/api/phonehub/sms/send';\n const resp = await rest.POST(url, params);\n const body = resp?.data ?? resp;\n const ok = body?.status === true || body?.success === true;\n\n if (ok) {\n const data = body?.data ?? {};\n const model = new SMS(data, { endpoint: '/api/phonehub/sms' });\n return { success: true, model, data, response: resp };\n }\n return { success: false, error: body?.error || 'Failed to send SMS', response: resp };\n }\n\n\n\n\n}\n\n/**\n * SMSList - Paginated collection of SMS models.\n */\nclass SMSList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: SMS,\n endpoint: '/api/phonehub/sms',\n size: 10,\n ...options\n });\n }\n\n\n}\n\n// Exported API\nexport {\n PhoneNumber,\n PhoneNumberList,\n SMS,\n SMSList\n};\n\nexport default {\n PhoneNumber,\n PhoneNumberList,\n SMS,\n SMSList\n};","/**\n * PhoneNumberView - Detailed view for a PhoneHub PhoneNumber record\n *\n * Mirrors GeoIPView structure:\n * - Header with primary identifier and quick info\n * - Tabbed content using TabView and DataView sections\n * - Minimal context menu for refresh and delete\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport DataView from '@core/views/data/DataView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport { PhoneNumber } from '@core/models/Phonehub.js';\n\nclass PhoneNumberView extends View {\n constructor(options = {}) {\n super({\n className: 'phone-number-view',\n ...options\n });\n\n this.model = options.model || new PhoneNumber(options.data || {});\n\n this.template = `\n <div class=\"phone-number-view-container\">\n <!-- Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-4\">\n <!-- Left Side: Icon & Info -->\n <div class=\"d-flex align-items-center gap-3\">\n <div class=\"fs-1 text-primary\">\n <i class=\"bi bi-telephone\"></i>\n </div>\n <div>\n <h3 class=\"mb-1\">{{model.phone_number|default('Unknown Number')}}</h3>\n <div class=\"text-muted small\">\n {{model.carrier|default('—')}} {{#model.line_type}}· {{model.line_type|capitalize}}{{/model.line_type}}\n </div>\n <div class=\"text-muted small mt-1\">\n {{#model.country_code}}Country: {{model.country_code}}{{/model.country_code}}\n {{#model.region}} · Region: {{model.region}}{{/model.region}}\n {{#model.state}} · State: {{model.state}}{{/model.state}}\n </div>\n </div>\n </div>\n\n <!-- Right Side: Actions -->\n <div class=\"d-flex align-items-center gap-4\">\n <div data-container=\"phone-context-menu\"></div>\n </div>\n </div>\n\n <!-- Tabs -->\n <div data-container=\"phone-tabs\"></div>\n </div>\n `;\n }\n\n async onInit() {\n // Overview Tab\n this.overviewView = new DataView({\n model: this.model,\n className: 'p-3',\n showEmptyValues: true,\n emptyValueText: '—',\n columns: 2,\n fields: [\n { name: 'phone_number', label: 'Phone Number', cols: 6 },\n { name: 'country_code', label: 'Country Code', cols: 6 },\n { name: 'region', label: 'Region', cols: 6 },\n { name: 'state', label: 'State', cols: 6 },\n { name: 'registered_owner', label: 'Registered Owner', cols: 6 },\n { name: 'owner_type', label: 'Owner Type', formatter: 'capitalize', cols: 6 },\n { name: 'is_valid', label: 'Valid', formatter: 'yesnoicon', cols: 4 },\n { name: 'is_mobile', label: 'Mobile', formatter: 'yesnoicon', cols: 4 },\n { name: 'is_voip', label: 'VOIP', formatter: 'yesnoicon', cols: 4 },\n ]\n });\n\n // Carrier & Status Tab\n this.carrierView = new DataView({\n model: this.model,\n className: 'p-3',\n showEmptyValues: true,\n emptyValueText: '—',\n columns: 2,\n fields: [\n { name: 'carrier', label: 'Carrier', cols: 6 },\n { name: 'line_type', label: 'Line Type', formatter: 'capitalize', cols: 6 },\n { name: 'lookup_provider', label: 'Lookup Provider', formatter: 'capitalize', cols: 6 },\n { name: 'lookup_count', label: 'Lookup Count', cols: 6 },\n { name: 'last_lookup_at', label: 'Last Lookup', formatter: 'datetime', cols: 6 },\n { name: 'lookup_expires_at', label: 'Cache Expires', formatter: 'datetime', cols: 6 }\n ]\n });\n\n // Address Tab\n this.addressView = new DataView({\n model: this.model,\n className: 'p-3',\n showEmptyValues: true,\n emptyValueText: '—',\n columns: 2,\n fields: [\n { name: 'address_line1', label: 'Address Line 1', cols: 12 },\n { name: 'address_city', label: 'City', cols: 4 },\n { name: 'address_state', label: 'State', cols: 4 },\n { name: 'address_zip', label: 'ZIP', cols: 4 },\n { name: 'address_country', label: 'Country', cols: 6 }\n ]\n });\n\n // Metadata Tab\n this.metadataView = new DataView({\n model: this.model,\n className: 'p-3',\n showEmptyValues: true,\n emptyValueText: '—',\n columns: 2,\n fields: [\n { name: 'id', label: 'Record ID', cols: 6 },\n { name: 'created', label: 'Created', formatter: 'datetime', cols: 6 },\n { name: 'modified', label: 'Last Modified', formatter: 'datetime', cols: 6 }\n ]\n });\n\n const tabs = {\n 'Overview': this.overviewView,\n 'Carrier': this.carrierView,\n 'Address': this.addressView,\n 'Metadata': this.metadataView\n };\n\n this.tabView = new TabView({\n containerId: 'phone-tabs',\n tabs,\n activeTab: 'Overview'\n });\n this.addChild(this.tabView);\n\n // Minimal ContextMenu\n const menuItems = [\n { label: 'Refresh Lookup', action: 'refresh-lookup', icon: 'bi-arrow-repeat' },\n { type: 'divider' },\n { label: 'Delete Record', action: 'delete-phone', icon: 'bi-trash', danger: true }\n ];\n\n const ctxMenu = new ContextMenu({\n containerId: 'phone-context-menu',\n className: 'context-menu-view header-menu-absolute',\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: menuItems\n }\n });\n this.addChild(ctxMenu);\n }\n\n // Actions\n\n async onActionRefreshLookup() {\n const number = this.model.get('phone_number');\n if (!number) {\n this.getApp()?.toast?.warning?.('No phone number to lookup');\n return;\n }\n\n try {\n this.getApp()?.toast?.info?.('Refreshing lookup...');\n // Force refresh lookup and update current model\n const resp = await PhoneNumber.lookup(number, { force_refresh: true });\n if (resp.success && resp.data) {\n this.model.set(resp.data);\n await this.render();\n this.getApp()?.toast?.success?.('Lookup refreshed');\n } else {\n const msg = resp.error || 'Lookup failed';\n this.getApp()?.toast?.error?.(msg);\n }\n } catch (e) {\n this.getApp()?.toast?.error?.(e.message || 'Lookup failed');\n }\n }\n\n async onActionDeletePhone() {\n const confirmed = await Dialog.confirm(\n `Are you sure you want to delete the record for \"${this.model.get('phone_number') || 'this number'}\"?`,\n 'Confirm Deletion',\n { confirmClass: 'btn-danger', confirmText: 'Delete' }\n );\n\n if (!confirmed) return;\n\n try {\n const resp = await this.model.destroy();\n if (resp?.success) {\n this.emit('phone:deleted', { model: this.model });\n } else {\n this.getApp()?.toast?.error?.('Delete failed');\n }\n } catch (e) {\n this.getApp()?.toast?.error?.(e.message || 'Delete failed');\n }\n }\n}\n\nPhoneNumberView.MODEL_CLASS = PhoneNumber;\n\nexport default PhoneNumberView;\n","/**\n * PhoneNumberTablePage - PhoneHub numbers management using TablePage component\n * Mirrors the GeoLocatedIPTablePage pattern with minimal, consistent configuration.\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { PhoneNumberList, PhoneNumber } from '@core/models/Phonehub.js';\nimport PhoneNumberView from './PhoneNumberView.js';\n\nclass PhoneNumberTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n\n // Identity\n name: 'admin_phonehub_numbers',\n pageName: 'Phone Numbers',\n router: 'admin/phonehub/numbers',\n\n // Data source\n Collection: PhoneNumberList,\n\n // Item view configuration\n itemView: PhoneNumberView,\n viewDialogOptions: {\n header: false,\n // size: 'xl'\n },\n\n // Column definitions\n columns: [\n { key: 'phone_number', label: 'Phone Number', sortable: true },\n { key: 'carrier', label: 'Carrier', sortable: true, formatter: \"default('—')\" },\n { key: 'line_type', label: 'Line Type', sortable: true, formatter: \"capitalize\" },\n { key: 'is_mobile', label: 'Mobile', formatter: 'yesnoicon' },\n { key: 'is_voip', label: 'VOIP', formatter: 'yesnoicon' },\n { key: 'is_valid', label: 'Valid', formatter: 'yesnoicon' },\n { key: 'registered_owner', label: 'Owner', sortable: true, formatter: \"default('—')\" },\n { key: 'owner_type', label: 'Owner Type', formatter: \"capitalize\" },\n { key: 'last_lookup_at|relative', label: 'Last Lookup', sortable: true},\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Row action\n clickAction: 'view',\n\n // Toolbar\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No phone numbers found.',\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n },\n\n tableViewOptions: {\n addButtonLabel: \"Lookup\",\n addButtonIcon: 'bi-search',\n onAdd: (evt) => {\n evt.preventDefault();\n // Implement the logic for adding a new record\n this.onLookup();\n }\n }\n });\n }\n\n async onLookup() {\n // Implement the logic for adding a new record\n const data = await this.getApp().showForm({\n title: \"Lookup Phone Number\",\n fields: [\n {\n name: 'number',\n type: 'text',\n required: true\n }\n ]\n });\n if (data && data.number) {\n const resp = await PhoneNumber.lookup(data.number);\n if (resp.model) {\n this.tableView._onRowView(resp);\n }\n }\n }\n}\n\nexport default PhoneNumberTablePage;\n","/**\n * SMSView - Detailed view for a PhoneHub SMS record\n *\n * Mirrors GeoIPView structure:\n * - Header with key info (direction, from/to, status)\n * - Tabbed content using TabView and DataView sections\n * - Minimal context menu for refresh and delete\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport DataView from '@core/views/data/DataView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport { SMS } from '@core/models/Phonehub.js';\n\nclass SMSView extends View {\n constructor(options = {}) {\n super({\n className: 'sms-view',\n ...options\n });\n\n this.model = options.model || new SMS(options.data || {});\n\n this.template = `\n <div class=\"sms-view-container\">\n <!-- Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-4\">\n <!-- Left Side: Icon & Info -->\n <div class=\"d-flex align-items-center gap-3\">\n <div class=\"fs-1 text-primary\">\n <i class=\"bi bi-chat-dots\"></i>\n </div>\n <div>\n <h3 class=\"mb-1\">\n {{#model.direction}}{{model.direction|capitalize}}{{/model.direction}}\n {{^model.direction}}Message{{/model.direction}}\n <small class=\"text-muted ms-2\">\n {{#model.status}}[{{model.status|capitalize}}]{{/model.status}}\n </small>\n </h3>\n <div class=\"text-muted small\">\n {{#model.from_number}}From: {{model.from_number}}{{/model.from_number}}\n {{#model.to_number}} · To: {{model.to_number}}{{/model.to_number}}\n </div>\n <div class=\"text-muted small mt-1\">\n {{#model.provider}}Provider: {{model.provider|capitalize}}{{/model.provider}}\n {{#model.provider_message_id}} · SID: {{model.provider_message_id}}{{/model.provider_message_id}}\n </div>\n </div>\n </div>\n\n <!-- Right Side: Actions -->\n <div class=\"d-flex align-items-center gap-4\">\n <div data-container=\"sms-context-menu\"></div>\n </div>\n </div>\n\n <!-- Tabs -->\n <div data-container=\"sms-tabs\"></div>\n </div>\n `;\n }\n\n async onInit() {\n // Message Tab\n this.messageView = new DataView({\n model: this.model,\n className: 'p-3',\n showEmptyValues: true,\n emptyValueText: '—',\n columns: 2,\n fields: [\n { name: 'direction', label: 'Direction', formatter: 'capitalize', cols: 4 },\n { name: 'status', label: 'Status', formatter: 'capitalize', cols: 4 },\n { name: 'from_number', label: 'From', cols: 6 },\n { name: 'to_number', label: 'To', cols: 6 },\n { name: 'body', label: 'Message Body', cols: 12 }\n ]\n });\n\n // Delivery Tab\n this.deliveryView = new DataView({\n model: this.model,\n className: 'p-3',\n showEmptyValues: true,\n emptyValueText: '—',\n columns: 2,\n fields: [\n { name: 'provider', label: 'Provider', formatter: 'capitalize', cols: 6 },\n { name: 'provider_message_id', label: 'Provider Message ID', cols: 6 },\n { name: 'sent_at', label: 'Sent At', formatter: 'datetime', cols: 6 },\n { name: 'delivered_at', label: 'Delivered At', formatter: 'datetime', cols: 6 },\n { name: 'error_code', label: 'Error Code', cols: 6 },\n { name: 'error_message', label: 'Error Message', cols: 12 }\n ]\n });\n\n // Metadata Tab\n this.metadataView = new DataView({\n model: this.model,\n className: 'p-3',\n showEmptyValues: true,\n emptyValueText: '—',\n columns: 2,\n fields: [\n { name: 'id', label: 'Record ID', cols: 6 },\n { name: 'created', label: 'Created', formatter: 'datetime', cols: 6 },\n { name: 'modified', label: 'Last Modified', formatter: 'datetime', cols: 6 }\n ]\n });\n\n const tabs = {\n 'Message': this.messageView,\n 'Delivery': this.deliveryView,\n 'Metadata': this.metadataView\n };\n\n this.tabView = new TabView({\n containerId: 'sms-tabs',\n tabs,\n activeTab: 'Message'\n });\n this.addChild(this.tabView);\n\n // ContextMenu (minimal)\n const menuItems = [\n { label: 'Refresh', action: 'refresh-sms', icon: 'bi-arrow-repeat' },\n { type: 'divider' },\n { label: 'Delete Message', action: 'delete-sms', icon: 'bi-trash', danger: true }\n ];\n\n const ctxMenu = new ContextMenu({\n containerId: 'sms-context-menu',\n className: 'context-menu-view header-menu-absolute',\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: menuItems\n }\n });\n this.addChild(ctxMenu);\n }\n\n // Actions\n\n async onActionRefreshSms() {\n try {\n this.getApp()?.toast?.info?.('Refreshing message...');\n await this.model.fetch();\n await this.render();\n this.getApp()?.toast?.success?.('Message refreshed');\n } catch (e) {\n this.getApp()?.toast?.error?.(e.message || 'Refresh failed');\n }\n }\n\n async onActionDeleteSms() {\n const title = 'Confirm Deletion';\n const msg = `Are you sure you want to delete this message?`;\n const confirmed = await Dialog.confirm(msg, title, {\n confirmClass: 'btn-danger',\n confirmText: 'Delete'\n });\n\n if (!confirmed) return;\n\n try {\n const resp = await this.model.destroy();\n if (resp?.success) {\n this.emit('sms:deleted', { model: this.model });\n } else {\n this.getApp()?.toast?.error?.('Delete failed');\n }\n } catch (e) {\n this.getApp()?.toast?.error?.(e.message || 'Delete failed');\n }\n }\n}\n\nSMSView.MODEL_CLASS = SMS;\n\nexport default SMSView;","/**\n * SMSTablePage - PhoneHub SMS management using TablePage component\n * Mirrors the GeoLocatedIPTablePage pattern with minimal, consistent configuration.\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { SMSList } from '@core/models/Phonehub.js';\nimport SMSView from './SMSView.js';\n\nclass SMSTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n\n // Identity\n name: 'admin_phonehub_sms',\n pageName: 'SMS Messages',\n router: 'admin/phonehub/sms',\n\n // Data source\n Collection: SMSList,\n\n // Item view configuration\n itemView: SMSView,\n viewDialogOptions: {\n header: false,\n size: 'xl'\n },\n\n // Column definitions\n columns: [\n { key: 'direction', label: 'Direction', sortable: true },\n { key: 'from_number', label: 'From', sortable: true, formatter: \"default('—')\" },\n { key: 'to_number', label: 'To', sortable: true, formatter: \"default('—')\" },\n { key: 'status', label: 'Status', sortable: true },\n { key: 'provider', label: 'Provider', sortable: true, formatter: \"default('—')\" },\n { key: 'body', label: 'Message', formatter: \"default('—')\" },\n { key: 'sent_at', label: 'Sent At', sortable: true, formatter: 'datetime' },\n { key: 'delivered_at', label: 'Delivered At', sortable: true, formatter: 'datetime' },\n { key: 'created', label: 'Created', sortable: true, formatter: 'datetime' }\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Row action\n clickAction: 'view',\n\n // Toolbar\n showRefresh: true,\n showAdd: false,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No SMS messages found.',\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n}\n\nexport default SMSTablePage;","import Page from '@core/Page.js';\nimport { MetricsChart, PieChart } from '@ext/charts/index.js';\nimport TableView from '@core/views/table/TableView.js';\nimport { PushDeliveryList } from '@core/models/Push.js';\n\nclass PushDashboardPage extends Page {\n constructor(options = {}) {\n super({\n ...options,\n title: 'Push Notifications Dashboard',\n className: 'push-dashboard-page'\n });\n }\n\n async getTemplate() {\n return `\n <div class=\"container-fluid\">\n <h1 class=\"h3 mb-4\">Push Notifications</h1>\n <div class=\"row\">\n <!-- Stat cards -->\n </div>\n <div class=\"row\">\n <div class=\"col-xl-8 col-lg-7\">\n <div class=\"card shadow mb-4\">\n <div class=\"card-header\">Notifications Over Time</div>\n <div class=\"card-body\" data-container=\"deliveries-chart\"></div>\n </div>\n </div>\n <div class=\"col-xl-4 col-lg-5\">\n <div class=\"card shadow mb-4\">\n <div class=\"card-header\">Delivery Status</div>\n <div class=\"card-body\" data-container=\"status-chart\"></div>\n </div>\n </div>\n </div>\n <div class=\"row\">\n <div class=\"col-lg-6 mb-4\" data-container=\"recent-deliveries\"></div>\n <div class=\"col-lg-6 mb-4\" data-container=\"failed-deliveries\"></div>\n </div>\n </div>\n `;\n }\n\n async onInit() {\n this.deliveriesChart = new MetricsChart({\n containerId: 'deliveries-chart',\n endpoint: '/api/metrics/fetch',\n slugs: ['push_sent', 'push_failed'],\n chartType: 'line'\n });\n this.addChild(this.deliveriesChart);\n\n this.statusChart = new PieChart({\n containerId: 'status-chart',\n endpoint: '/api/account/devices/push/stats' // Assuming this returns data for pie chart\n });\n this.addChild(this.statusChart);\n\n this.recentDeliveries = new TableView({\n containerId: 'recent-deliveries',\n title: 'Recent Deliveries',\n Collection: new PushDeliveryList({ params: { _sort: '-created', _limit: 5 } }),\n columns: [{ key: 'title', label: 'Title' }, { key: 'status', label: 'Status', formatter: 'badge' }]\n });\n this.addChild(this.recentDeliveries);\n\n this.failedDeliveries = new TableView({\n containerId: 'failed-deliveries',\n title: 'Failed Deliveries',\n Collection: new PushDeliveryList({ params: { status: 'failed', _sort: '-created', _limit: 5 } }),\n columns: [{ key: 'title', label: 'Title' }, { key: 'error_message', label: 'Error' }]\n });\n this.addChild(this.failedDeliveries);\n }\n}\n\nexport default PushDashboardPage;\n","import TablePage from '@core/pages/TablePage.js';\nimport { PushConfigList, PushConfigForms } from '@core/models/Push.js';\n\nclass PushConfigTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_push_configs',\n pageName: 'Push Configurations',\n router: \"admin/push/configs\",\n Collection: PushConfigList,\n formCreate: PushConfigForms.create,\n formEdit: PushConfigForms.edit,\n\n columns: [\n { key: 'id', label: 'ID', width: '70px' },\n { key: 'name', label: 'Name' },\n { key: 'group.name', label: 'Group', formatter: \"default('Default')\" },\n { key: 'fcm_sender_id', label: 'Project ID' },\n { key: 'is_active', label: 'Active', format: 'boolean' },\n ],\n\n searchable: true,\n sortable: true,\n paginated: true,\n showRefresh: true,\n showAdd: true,\n showExport: true,\n actions: [\"edit\", \"delete\"],\n tableOptions: {\n pageSizes: [10, 25, 50],\n defaultPageSize: 25,\n emptyMessage: 'No push configurations found.',\n emptyIcon: 'bi-gear',\n\n }\n });\n }\n}\n\nexport default PushConfigTablePage;\n","import TablePage from '@core/pages/TablePage.js';\nimport { PushTemplateList, PushTemplateForms } from '@core/models/Push.js';\n\nclass PushTemplateTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_push_templates',\n pageName: 'Push Templates',\n router: \"admin/push/templates\",\n Collection: PushTemplateList,\n formCreate: PushTemplateForms.create,\n formEdit: PushTemplateForms.edit,\n\n columns: [\n { key: 'id', label: 'ID', width: '70px' },\n { key: 'name', label: 'Name' },\n { key: 'category', label: 'Category' },\n { key: 'group.name', label: 'Group', formatter: \"default('Default')\" },\n { key: 'priority', label: 'Priority' },\n { key: 'is_active', label: 'Active', format: 'boolean' },\n ],\n\n searchable: true,\n sortable: true,\n paginated: true,\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n tableOptions: {\n pageSizes: [10, 25, 50],\n defaultPageSize: 25,\n emptyMessage: 'No push templates found.',\n emptyIcon: 'bi-file-earmark-text',\n actions: [\"edit\", \"delete\"],\n }\n });\n }\n}\n\nexport default PushTemplateTablePage;\n","import View from '@core/View.js';\n\nclass PushDeliveryView extends View {\n constructor(options = {}) {\n super({\n className: 'push-delivery-view',\n ...options\n });\n this.model = options.model;\n }\n\n getTemplate() {\n return `\n <div class=\"p-3\">\n <div class=\"phone-mockup\">\n <div class=\"phone-screen\">\n <div class=\"notification\">\n <div class=\"notification-header\">\n <i class=\"bi bi-app-indicator\"></i>\n <strong>Your App</strong>\n <span class=\"ms-auto small text-muted\">now</span>\n </div>\n <div class=\"notification-body\">\n <div class=\"fw-bold\">{{model.title}}</div>\n <div>{{model.body}}</div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"mt-3\">\n <h5>Delivery Details</h5>\n <p><strong>Status:</strong> <span class=\"badge {{model.status|badge}}\">{{model.status}}</span></p>\n <p><strong>Error:</strong> {{model.error_message|default('None')}}</p>\n </div>\n </div>\n `;\n }\n}\n\nexport default PushDeliveryView;\n","import TablePage from '@core/pages/TablePage.js';\nimport { PushDeliveryList } from '@core/models/Push.js';\nimport PushDeliveryView from './PushDeliveryView.js';\n\nclass PushDeliveryTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_push_deliveries',\n pageName: 'Push Deliveries',\n router: \"admin/push/deliveries\",\n Collection: PushDeliveryList,\n itemViewClass: PushDeliveryView,\n viewDialogOptions: {\n header: false,\n size: 'md'\n },\n\n columns: [\n { key: 'id', label: 'ID', width: '70px' },\n { key: 'created', label: 'Timestamp', formatter: 'datetime' },\n { key: 'user.display_name', label: 'User' },\n { key: 'device.device_name', label: 'Device' },\n { key: 'title', label: 'Title' },\n { key: 'category', label: 'Category' },\n { key: 'status', label: 'Status', formatter: 'badge' },\n ],\n \n searchable: true,\n sortable: true,\n paginated: true,\n showRefresh: true,\n\n tableOptions: {\n pageSizes: [10, 25, 50],\n defaultPageSize: 25,\n emptyMessage: 'No deliveries found.',\n emptyIcon: 'bi-send',\n actions: [\"view\"],\n }\n });\n }\n}\n\nexport default PushDeliveryTablePage;\n","import View from '@core/View.js';\nimport DataView from '@core/views/data/DataView.js';\n\nclass PushDeviceView extends View {\n constructor(options = {}) {\n super({\n className: 'push-device-view',\n ...options\n });\n this.model = options.model;\n }\n\n getTemplate() {\n return `\n <div class=\"p-3\">\n <h3>{{model.device_name}}</h3>\n <p class=\"text-muted\">{{model.user.display_name}}</p>\n <div data-container=\"data-view\"></div>\n </div>\n `;\n }\n\n onInit() {\n this.dataView = new DataView({\n containerId: 'data-view',\n model: this.model,\n fields: [\n { name: 'platform', label: 'Platform', format: 'badge' },\n { name: 'push_enabled', label: 'Push Enabled', format: 'boolean' },\n { name: 'app_version', label: 'App Version' },\n { name: 'os_version', label: 'OS Version' },\n { name: 'last_seen', label: 'Last Seen', format: 'datetime' },\n { name: 'push_preferences', label: 'Preferences', format: 'json' },\n ]\n });\n this.addChild(this.dataView);\n }\n}\n\nexport default PushDeviceView;\n","import TablePage from '@core/pages/TablePage.js';\nimport { PushDeviceList } from '@core/models/Push.js';\nimport PushDeviceView from './PushDeviceView.js';\n\nclass PushDeviceTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_push_devices',\n pageName: 'Registered Devices',\n router: \"admin/push/devices\",\n Collection: PushDeviceList,\n itemViewClass: PushDeviceView,\n viewDialogOptions: {\n header: false,\n size: 'lg'\n },\n\n columns: [\n { key: 'id', label: 'ID', width: '70px' },\n { key: 'user.display_name', label: 'User' },\n { key: 'device_name', label: 'Device Name' },\n { key: 'platform', label: 'Platform', formatter: 'badge' },\n { key: 'app_version', label: 'App Version' },\n { key: 'push_enabled', label: 'Push Enabled', format: 'boolean' },\n { key: 'last_seen', label: 'Last Seen', formatter: 'datetime' },\n ],\n \n searchable: true,\n sortable: true,\n paginated: true,\n showRefresh: true,\n\n tableOptions: {\n pageSizes: [10, 25, 50],\n defaultPageSize: 25,\n emptyMessage: 'No devices found.',\n emptyIcon: 'bi-phone',\n actions: [\"view\", \"delete\"],\n }\n });\n }\n}\n\nexport default PushDeviceTablePage;\n","/**\n * JobStatsView - Job statistics overview cards\n */\n\nimport View from '@core/View.js';\nimport { Job } from '@core/models/Job.js';\n\nexport default class JobStatsView extends View {\n constructor(options = {}) {\n super({\n className: 'job-stats-section',\n ...options\n });\n\n this.stats = {\n pending: 0,\n running: 0,\n completed: 0,\n failed: 0,\n scheduled: 0\n };\n\n this.template = `\n <div class=\"job-stats-header mb-4\">\n <div class=\"row\">\n <div class=\"col-xl-2 col-lg-4 col-md-6 col-12 mb-3\">\n <div class=\"card h-100 border-0 shadow\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div>\n <h6 class=\"card-title text-muted mb-2\">Pending</h6>\n <h3 class=\"mb-1 fw-bold\">{{stats.pending}}</h3>\n <span class=\"badge bg-primary-subtle text-primary\">\n <i class=\"bi bi-hourglass\"></i> Queued\n </span>\n </div>\n <div class=\"text-primary\">\n <i class=\"bi bi-clock fs-2\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"col-xl-2 col-lg-4 col-md-6 col-12 mb-3\">\n <div class=\"card h-100 border-0 shadow\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div>\n <h6 class=\"card-title text-muted mb-2\">Running</h6>\n <h3 class=\"mb-1 fw-bold\">{{stats.running}}</h3>\n <span class=\"badge bg-success-subtle text-success\">\n <i class=\"bi bi-arrow-repeat\"></i> Active\n </span>\n </div>\n <div class=\"text-success\">\n <i class=\"bi bi-play-circle fs-2\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"col-xl-2 col-lg-4 col-md-6 col-12 mb-3\">\n <div class=\"card h-100 border-0 shadow\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div>\n <h6 class=\"card-title text-muted mb-2\">Scheduled</h6>\n <h3 class=\"mb-1 fw-bold\">{{stats.scheduled}}</h3>\n <span class=\"badge bg-warning-subtle text-warning\">\n <i class=\"bi bi-calendar-event\"></i> Planned\n </span>\n </div>\n <div class=\"text-warning\">\n <i class=\"bi bi-calendar3 fs-2\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"col-xl-3 col-lg-6 col-md-6 col-12 mb-3\">\n <div class=\"card h-100 border-0 shadow\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div>\n <h6 class=\"card-title text-muted mb-2\">Completed</h6>\n <h3 class=\"mb-1 fw-bold\">{{stats.completed}}</h3>\n <span class=\"badge bg-info-subtle text-info\">\n <i class=\"bi bi-check-circle\"></i> Done\n </span>\n </div>\n <div class=\"text-info\">\n <i class=\"bi bi-check-square fs-2\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"col-xl-3 col-lg-6 col-md-6 col-12 mb-3\">\n <div class=\"card h-100 border-0 shadow\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div>\n <h6 class=\"card-title text-muted mb-2\">Failed</h6>\n <h3 class=\"mb-1 fw-bold\">{{stats.failed}}</h3>\n <span class=\"badge bg-danger-subtle text-danger\">\n <i class=\"bi bi-x-octagon\"></i> Errors\n </span>\n </div>\n <div class=\"text-danger\">\n <i class=\"bi bi-exclamation-triangle fs-2\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n `;\n }\n\n _onModelChange() {\n this.loadStats();\n if (this.isMounted()) {\n this.render();\n }\n }\n\n async loadStats() {\n this.stats = this.model.attributes.totals;\n }\n}\n","/**\n * JobHealthView - System health overview\n */\n\nimport View from '@core/View.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport { Job } from '@core/models/Job.js';\n\nexport default class JobHealthView extends View {\n constructor(options = {}) {\n super({\n className: 'job-health-section',\n ...options\n });\n\n this.health = {\n status: 'unknown',\n runners: { active: 0, total: 0 },\n channels: []\n };\n\n this.template = `\n <div class=\"job-health-header mb-4\">\n <div class=\"card border-0 shadow\">\n <div class=\"card-body\">\n <div class=\"row align-items-center\">\n <div class=\"col-md-6\">\n <div class=\"d-flex align-items-center\">\n <div class=\"health-indicator me-3\">\n <i class=\"bi bi-circle-fill fs-4 {{healthStatusClass}}\"></i>\n </div>\n <div>\n <h5 class=\"mb-1\">System Health: {{health.overall_status|capitalize}}</h5>\n <small class=\"text-muted d-block\">\n Workers: {{health.runners.active}}/{{health.runners.total}} active\n </small>\n <small class=\"text-muted d-block\">\n Scheduler:\n <span class=\"{{schedulerStatusClass}} fw-bold\">\n <i class=\"bi {{schedulerIcon}} me-1\"></i>{{schedulerStatusText}}\n </span>\n </small>\n </div>\n </div>\n </div>\n <div class=\"col-md-6\">\n <div class=\"d-flex justify-content-end\">\n <div class=\"btn-group\">\n <button class=\"btn btn-sm btn-outline-primary\" data-action=\"refresh-health\">\n <i class=\"bi bi-arrow-clockwise\"></i> Refresh\n </button>\n <button class=\"btn btn-sm btn-outline-secondary\" data-action=\"system-settings\">\n <i class=\"bi bi-gear\"></i> Settings\n </button>\n </div>\n </div>\n </div>\n </div>\n {{#health.channelsArray.length}}\n <div class=\"row mt-3\">\n <div class=\"col-12\">\n <small class=\"text-muted d-block mb-2\">Channel Status:</small>\n <div class=\"d-flex flex-wrap gap-2\">\n {{#health.channelsArray}}\n <span class=\"badge {{statusBadgeClass}} d-flex align-items-center\">\n <i class=\"bi {{statusIcon}} me-1\"></i>\n {{channel}} ({{queued}} queued, {{inflight}} inflight)\n </span>\n {{/health.channelsArray}}\n </div>\n </div>\n </div>\n {{/health.channelsArray.length}}\n </div>\n </div>\n </div>\n `;\n }\n\n _onModelChange() {\n this.loadHealth();\n if (this.isMounted()) {\n this.render();\n }\n }\n\n async loadHealth() {\n if (!this.model._.totals) return;\n const data = this.model.attributes\n // Determine overall health status\n let overall_status = 'healthy';\n if (data.totals.runners_active === 0) {\n overall_status = 'critical';\n } else if (!data.scheduler.active) {\n overall_status = 'warning';\n }\n this.health = {\n overall_status,\n channels: data.channels,\n runners: {\n active: data.totals.runners_active,\n total: data.runners.length\n },\n totals: data.totals,\n scheduler: data.scheduler\n };\n\n this.healthStatusClass = this.getHealthStatusClass(this.health.overall_status);\n this.setupChannelDisplay();\n this.setupSchedulerDisplay();\n\n }\n\n setupChannelDisplay() {\n if (!this.health.channels) return;\n\n this.health.channelsArray = Object.values(this.health.channels).map(channel => {\n let channelStatus = 'healthy';\n\n // Determine status based on queue backlog and runner availability\n const totalJobs = (channel.queued_count || 0) + (channel.inflight_count || 0);\n if (totalJobs > 50) {\n channelStatus = 'warning';\n }\n if (totalJobs > 100 || channel.runners === 0) {\n channelStatus = 'critical';\n }\n\n return {\n ...channel,\n status: channelStatus,\n statusBadgeClass: this.getChannelBadgeClass(channelStatus),\n statusIcon: this.getChannelIcon(channelStatus),\n queued: channel.queued_count || 0,\n inflight: channel.inflight_count || 0,\n // For backward compatibility, map to old names\n pending: channel.queued_count || 0,\n running: channel.inflight_count || 0\n };\n });\n }\n\n setupSchedulerDisplay() {\n if (!this.health.scheduler) {\n this.schedulerStatusText = 'Unknown';\n this.schedulerStatusClass = 'text-muted';\n this.schedulerIcon = 'bi-question-circle-fill';\n return;\n }\n\n if (this.health.scheduler.active) {\n this.schedulerStatusText = 'Running';\n this.schedulerStatusClass = 'text-success';\n this.schedulerIcon = 'bi-check-circle-fill';\n } else {\n this.schedulerStatusText = 'Stopped';\n this.schedulerStatusClass = 'text-danger';\n this.schedulerIcon = 'bi-x-octagon-fill';\n }\n }\n\n getHealthStatusClass(status) {\n const classes = {\n 'healthy': 'text-success',\n 'warning': 'text-warning',\n 'critical': 'text-danger',\n 'unknown': 'text-muted'\n };\n return classes[status] || 'text-muted';\n }\n\n getChannelBadgeClass(status) {\n const classes = {\n 'healthy': 'bg-success',\n 'warning': 'bg-warning',\n 'critical': 'bg-danger'\n };\n return classes[status] || 'bg-secondary';\n }\n\n getChannelIcon(status) {\n const icons = {\n 'healthy': 'bi-check-circle-fill',\n 'warning': 'bi-exclamation-triangle-fill',\n 'critical': 'bi-x-octagon-fill'\n };\n return icons[status] || 'bi-dash-circle-fill';\n }\n\n async onActionRefreshHealth(event, element) {\n try {\n element.disabled = true;\n await this.model.fetch();\n } catch (error) {\n console.error('Failed to refresh health:', error);\n } finally {\n element.disabled = false;\n }\n }\n\n async onActionSystemSettings() {\n await Dialog.showAlert({\n title: 'System Settings',\n message: 'System settings interface coming soon!',\n type: 'info'\n });\n }\n}\n","/**\n * JobDetailsView - Comprehensive job details interface\n *\n * Features:\n * - Clean header with job status, timing, and function info\n * - Tabbed interface for Overview, Payload, Events, and Logs\n * - Integrated with Table and ContextMenu components\n * - Clean Bootstrap 5 styling\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport TableView from '@core/views/table/TableView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport { Job, JobEventList, JobLogList, JobForms } from '@core/models/Job.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\n\nclass JobDetailsView extends View {\n constructor(options = {}) {\n super({\n className: 'job-details-view',\n ...options\n });\n\n // Job model instance\n this.model = options.model || new Job(options.data || {});\n\n // Tab views\n this.tabView = null;\n this.overviewView = null;\n this.payloadView = null;\n this.eventsView = null;\n this.logsView = null;\n\n // Auto refresh\n this.autoRefreshInterval = null;\n\n // Set template\n this.template = `\n <div class=\"job-details-container\">\n <!-- Job Header -->\n <div class=\"d-flex justify-content-between align-items-start mb-4\">\n <!-- Left Side: Primary Identity -->\n <div class=\"d-flex align-items-center gap-3\">\n <div class=\"avatar-placeholder rounded-circle bg-light d-flex align-items-center justify-content-center\" style=\"width: 80px; height: 80px;\">\n <i class=\"bi {{model.statusIcon}} text-secondary\" style=\"font-size: 40px;\"></i>\n </div>\n <div>\n <h3 class=\"mb-1\">{{model.func|truncate(32)|default('Unknown Function')}}</h3>\n <div class=\"text-muted small\">\n <span>ID: {{model.id}}</span>\n <span class=\"mx-2\">|</span>\n <span>Channel: <span class=\"badge bg-primary\">{{model.channel}}</span></span>\n {{#model.runner_id}}\n <span class=\"mx-2\">|</span>\n <span>Runner: {{model.runner_id|truncate(16)}}</span>\n {{/model.runner_id}}\n </div>\n <div class=\"text-muted small mt-2\">\n <div>Created: {{model.created|datetime}}</div>\n {{#model.started_at}}\n <div>Started: {{model.started_at|datetime}}</div>\n {{/model.started_at}}\n {{#model.finished_at}}\n <div>Finished: {{model.finished_at|datetime}}</div>\n {{/model.finished_at}}\n </div>\n </div>\n </div>\n\n <!-- Right Side: Status & Actions -->\n <div class=\"d-flex align-items-start gap-4\">\n <div class=\"text-end\">\n <div class=\"d-flex align-items-center gap-2\">\n <span class=\"badge {{model.statusBadgeClass}} fs-6\">\n <i class=\"bi {{model.statusIcon}}\"></i> {{model.status|uppercase}}\n </span>\n {{#model.cancel_requested}}\n <span class=\"badge bg-warning ms-1\">\n <i class=\"bi bi-exclamation-triangle\"></i> Cancel Requested\n </span>\n {{/model.cancel_requested}}\n </div>\n {{#model.formattedDuration}}\n <div class=\"text-muted small mt-1\">Duration: {{model.formattedDuration}}</div>\n {{/model.formattedDuration}}\n </div>\n <div data-container=\"job-context-menu\"></div>\n </div>\n </div>\n\n <!-- Tab Container -->\n <div data-container=\"job-details-tabs\"></div>\n </div>\n `;\n }\n\n async onInit() {\n // Create Overview view\n this.overviewView = new View({\n template: `\n <div class=\"job-overview-tab\">\n <div class=\"card border-0 bg-light mb-3\">\n <div class=\"card-body\">\n <div class=\"row\">\n <div class=\"col-md-6\">\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Job ID</label>\n <div class=\"font-monospace\">{{model.id}}</div>\n </div>\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Function</label>\n <div class=\"font-monospace\">{{model.func}}</div>\n </div>\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Channel</label>\n <div>\n <span class=\"badge bg-primary\">{{model.channel}}</span>\n </div>\n </div>\n {{#model.runner_id}}\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Runner</label>\n <div class=\"font-monospace small\">{{model.runner_id}}</div>\n </div>\n {{/model.runner_id}}\n </div>\n <div class=\"col-md-6\">\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Status</label>\n <div>\n <span class=\"badge {{model.statusBadgeClass}} fs-6\">\n <i class=\"bi {{model.statusIcon}}\"></i> {{model.status|uppercase}}\n </span>\n {{#model.cancel_requested}}\n <span class=\"badge bg-warning ms-1\">\n <i class=\"bi bi-exclamation-triangle\"></i> Cancel Requested\n </span>\n {{/model.cancel_requested}}\n </div>\n </div>\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Created</label>\n <div>{{model.created|datetime}}</div>\n </div>\n {{#model.started_at}}\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Started</label>\n <div>{{model.started_at|datetime}}</div>\n </div>\n {{/model.started_at}}\n {{#model.finished_at}}\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Finished</label>\n <div>{{model.finished_at|datetime}}</div>\n </div>\n {{/model.finished_at}}\n {{#model.duration_ms}}\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Duration</label>\n <div>{{model.formattedDuration}}</div>\n </div>\n {{/model.duration_ms}}\n </div>\n </div>\n </div>\n </div>\n </div>\n `,\n model: this.model\n });\n\n // Create Payload view\n this.payloadView = new View({\n template: `\n <div class=\"job-payload-tab\">\n <pre class=\"bg-light p-3 rounded\"><code>{{{model.payload|json}}}</code></pre>\n </div>\n `,\n model: this.model,\n });\n\n // Create Events table\n const eventsCollection = new JobEventList({\n params: { job: this.model.get('id'), size: 10 }\n });\n this.eventsView = new TableView({\n collection: eventsCollection,\n hideActivePillNames: ['job'],\n columns: [\n { key: 'at', label: 'Timestamp', formatter: 'datetime', sortable: true },\n { key: 'event', label: 'Event', formatter: 'badge' },\n { key: 'details|json', label: 'Details' }\n ]\n });\n\n // Create Logs table\n const logsCollection = new JobLogList({\n params: { job: this.model.get('id'), size: 10 }\n });\n this.logsView = new TableView({\n collection: logsCollection,\n hideActivePillNames: ['job'],\n columns: [\n { key: 'created|datetime', label: 'Created', sortable: true },\n { key: 'kind', label: 'Kind', formatter: 'badge' },\n { key: 'message', label: 'Message' }\n ]\n });\n\n // Create TabView\n this.tabView = new TabView({\n tabs: {\n 'Overview': this.overviewView,\n 'Payload': this.payloadView,\n 'Events': this.eventsView,\n 'Logs': this.logsView\n },\n activeTab: 'Overview',\n containerId: 'job-details-tabs'\n });\n this.addChild(this.tabView);\n\n // Create ContextMenu\n const contextMenuItems = [\n { label: 'Refresh', action: 'refresh-job', icon: 'bi-arrow-clockwise' }\n ];\n\n if (this.model.canCancel && this.model.canCancel()) {\n contextMenuItems.push({\n label: 'Cancel Job',\n action: 'cancel-job',\n icon: 'bi-x-circle',\n class: 'text-danger'\n });\n }\n\n if (this.model.canRetry && this.model.canRetry()) {\n contextMenuItems.push({\n label: 'Retry Job',\n action: 'retry-job',\n icon: 'bi-arrow-repeat',\n class: 'text-primary'\n });\n }\n\n const jobMenu = new ContextMenu({\n containerId: 'job-context-menu',\n className: \"context-menu-view header-menu-absolute\",\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: contextMenuItems\n }\n });\n this.addChild(jobMenu);\n\n await this.model.fetch({ params: { graph: \"detail\" } });\n // Start auto-refresh if job is active\n // this.startAutoRefresh();\n }\n\n async onBeforeRender() {\n await this.prepareJobData();\n }\n\n async prepareJobData() {\n if (!this.model) return;\n\n // Status styling\n this.model._.statusBadgeClass = this.model.getStatusBadgeClass ? this.model.getStatusBadgeClass() : 'bg-secondary';\n this.model._.statusIcon = this.model.getStatusIcon ? this.model.getStatusIcon() : 'bi-question-circle';\n this.model._.formattedDuration = this.model.getFormattedDuration ? this.model.getFormattedDuration() : 'N/A';\n }\n\n async loadJobDetails() {\n if (!this.model?.get('id')) return;\n\n try {\n if (this.model.getDetailedStatus) {\n await this.model.getDetailedStatus();\n }\n await this.prepareJobData();\n } catch (error) {\n console.error('Failed to load job details:', error);\n }\n }\n\n async onActionRefreshJob() {\n await this.model.fetch({ params: { graph: \"detail\" } });\n }\n\n async onActionCancelJob() {\n if (confirm('Are you sure you want to cancel this job?')) {\n try {\n const response = await this.model.cancel();\n if (response.success) {\n await this.loadJobDetails();\n await this.render();\n this.emit('job-cancelled', { job: this.model });\n } else {\n alert('Failed to cancel job: ' + (response.data?.error || 'Unknown error'));\n }\n } catch (error) {\n console.error('Failed to cancel job:', error);\n alert('Failed to cancel job: ' + error.message);\n }\n }\n }\n\n async onActionRetryJob() {\n const retryData = await Dialog.showForm({\n title: 'Retry Job',\n formConfig: JobForms.retry\n });\n\n if (retryData) {\n try {\n const response = await this.model.retry(retryData.delay || 0);\n if (response.success) {\n this.emit('job-retried', { job: this.model, newJobId: response.newJobId });\n } else {\n alert('Failed to retry job: ' + (response.data?.error || 'Unknown error'));\n }\n } catch (error) {\n console.error('Failed to retry job:', error);\n alert('Failed to retry job: ' + error.message);\n }\n }\n }\n\n startAutoRefresh() {\n if (this.autoRefreshInterval) clearInterval(this.autoRefreshInterval);\n\n if (this.model?.isActive && this.model.isActive()) {\n this.autoRefreshInterval = setInterval(async () => {\n try {\n await this.loadJobDetails();\n if (this.isMounted()) {\n await this.render();\n }\n } catch (error) {\n console.error('Auto-refresh failed:', error);\n }\n }, 5000);\n }\n }\n\n stopAutoRefresh() {\n if (this.autoRefreshInterval) {\n clearInterval(this.autoRefreshInterval);\n this.autoRefreshInterval = null;\n }\n }\n\n async onDestroy() {\n this.stopAutoRefresh();\n await super.onDestroy();\n }\n\n // Static show method for backward compatibility (if needed)\n static async show(job, options = {}) {\n const view = new JobDetailsView({ model: job });\n\n return await Dialog.showDialog({\n title: `<i class=\"bi bi-info-circle me-2\"></i>Job Details - ${job.get('id')}`,\n body: view,\n size: 'xl',\n scrollable: true,\n buttons: [{ text: 'Close', class: 'btn-secondary', dismiss: true }],\n onHide: () => view.stopAutoRefresh(),\n ...options\n });\n }\n}\n\nJob.VIEW_CLASS = JobDetailsView;\n\nexport default JobDetailsView;\n","/**\n * JobsTable - Jobs management table\n */\n\nimport TableView from '@core/views/table/TableView.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport { JobList, JobForms } from '@core/models/Job.js';\nimport JobDetailsView from '../JobDetailsView.js';\n\nexport default class JobsTable extends TableView {\n constructor(options = {}) {\n super({\n Collection: JobList,\n collectionParams: {\n size: 15,\n sort: '-created'\n },\n options: {\n searchable: true,\n sortable: true,\n paginated: true,\n size: 15,\n ...options.options\n },\n columns: [\n {\n key: 'id',\n label: 'Job ID',\n formatter: 'truncate_middle(12)',\n sortable: true,\n filter: { type: 'text', placeholder: 'Job ID...' }\n },\n {\n key: 'status',\n label: 'Status',\n formatter: (value, context) => {\n const job = context.row;\n const badgeClass = job.getStatusBadgeClass ? job.getStatusBadgeClass() : 'bg-secondary';\n const icon = job.getStatusIcon ? job.getStatusIcon() : 'bi-question';\n return `<span class=\"badge ${badgeClass}\"><i class=\"${icon} me-1\"></i>${value.toUpperCase()}</span>`;\n },\n sortable: true,\n filter: {\n type: 'select',\n options: [\n { value: 'pending', label: 'Pending' },\n { value: 'running', label: 'Running' },\n { value: 'completed', label: 'Completed' },\n { value: 'failed', label: 'Failed' },\n { value: 'canceled', label: 'Canceled' },\n { value: 'expired', label: 'Expired' }\n ]\n }\n },\n {\n key: 'channel',\n label: 'Channel',\n formatter: 'badge',\n sortable: true,\n filter: { type: 'text', placeholder: 'Channel...' }\n },\n {\n key: 'created',\n label: 'Created',\n formatter: 'datetime',\n sortable: true,\n filter: {\n type: 'daterange',\n label: 'Created Date'\n }\n },\n {\n key: 'started_at',\n label: 'Started',\n formatter: 'datetime',\n sortable: true\n },\n {\n key: 'finished_at',\n label: 'Finished',\n formatter: 'datetime',\n sortable: true\n }\n ],\n contextMenu: [\n {\n label: 'View Details',\n action: 'view-job-details',\n icon: 'bi-info-circle'\n },\n {\n label: 'View Events',\n action: 'view-job-events',\n icon: 'bi-clock-history'\n },\n { separator: true },\n {\n label: 'Cancel Job',\n action: 'cancel-job',\n icon: 'bi-x-circle',\n danger: true,\n condition: (job) => job.canCancel && job.canCancel()\n },\n {\n label: 'Retry Job',\n action: 'retry-job',\n icon: 'bi-arrow-clockwise',\n condition: (job) => job.canRetry && job.canRetry()\n },\n {\n label: 'Clone Job',\n action: 'clone-job',\n icon: 'bi-copy'\n },\n { separator: true },\n {\n label: 'Export Job',\n action: 'export-job',\n icon: 'bi-download'\n }\n ],\n filters: [\n {\n key: 'func',\n label: 'Function',\n type: \"text\",\n }\n ],\n batchActions: [\n {\n label: 'Cancel Selected',\n action: 'batch-cancel',\n icon: 'bi-x-circle'\n },\n {\n label: 'Retry Selected',\n action: 'batch-retry',\n icon: 'bi-arrow-clockwise'\n },\n {\n label: 'Export Selected',\n action: 'batch-export',\n icon: 'bi-download'\n }\n ],\n ...options\n });\n }\n\n async onItemViewJobDetails(job) {\n if (job) {\n await JobDetailsView.show(job);\n }\n }\n\n async onItemCancelJob(job) {\n const confirmed = await Dialog.showConfirm('Are you sure you want to cancel this job?');\n if (job && confirmed) {\n try {\n const result = await job.cancel();\n if (result.success) {\n this.getApp().toast.success('Job cancelled successfully');\n await this.collection.fetch();\n } else {\n this.getApp().toast.error(result.data?.error || 'Failed to cancel job');\n }\n } catch (error) {\n this.getApp().toast.error('Error cancelling job: ' + error.message);\n }\n }\n }\n\n async onItemRetryJob(job) {\n if (job) {\n const result = await Dialog.showForm({\n title: 'Retry Job',\n formConfig: JobForms.retry\n });\n\n if (result) {\n try {\n const retryResult = await job.retry(result.delay || 0);\n if (retryResult.success) {\n this.getApp().toast.success('Job queued for retry');\n await this.collection.fetch();\n } else {\n this.getApp().toast.error(retryResult.data?.error || 'Failed to retry job');\n }\n } catch (error) {\n this.getApp().toast.error('Error retrying job: ' + error.message);\n }\n }\n }\n }\n\n async onItemCloneJob(job) {\n if (job) {\n const payload = job.getPayload();\n const result = await Dialog.showForm({\n title: 'Clone Job',\n formConfig: {\n ...JobForms.clone,\n fields: JobForms.clone.fields.map(field => {\n if (field.name === 'payload') {\n field.value = JSON.stringify(payload, null, 2);\n } else if (field.name === 'channel') {\n field.value = job.get('channel');\n }\n return field;\n })\n }\n });\n\n if (result) {\n try {\n let newPayload = {};\n if (result.payload) {\n newPayload = JSON.parse(result.payload);\n }\n\n const cloneData = {\n payload: newPayload,\n channel: result.channel || job.get('channel'),\n delay: result.delay || 0\n };\n\n const cloneResult = await job.cloneJob(cloneData);\n if (cloneResult.success) {\n this.getApp().toast.success('Job cloned successfully');\n await this.collection.fetch();\n } else {\n this.getApp().toast.error(cloneResult.data?.error || 'Failed to clone job');\n }\n } catch (error) {\n this.getApp().toast.error('Error cloning job: ' + error.message);\n }\n }\n }\n }\n}\n","/**\n * RunnersTable - Job runners management table\n */\n\nimport TableView from '@core/views/table/TableView.js';\nimport { JobRunnerList } from '@core/models/JobRunner.js';\n\nexport default class RunnersTable extends TableView {\n constructor(options = {}) {\n super({\n Collection: JobRunnerList,\n options: {\n searchable: true,\n sortable: true,\n paginated: true,\n size: 10,\n ...options.options\n },\n columns: [\n {\n key: 'runner_id',\n label: 'Runner ID',\n formatter: 'truncate_middle(16)',\n sortable: true\n },\n {\n key: 'alive',\n label: 'Status',\n formatter: (value) => {\n const isAlive = value === true;\n const badgeClass = isAlive ? 'bg-success' : 'bg-danger';\n const icon = isAlive ? 'bi-check-circle-fill' : 'bi-x-octagon-fill';\n const text = isAlive ? 'ALIVE' : 'DEAD';\n return `<span class=\"badge ${badgeClass}\"><i class=\"${icon} me-1\"></i>${text}</span>`;\n },\n sortable: true,\n filter: {\n type: 'select',\n options: [\n { value: true, label: 'Alive' },\n { value: false, label: 'Dead' }\n ]\n }\n },\n {\n key: 'channels',\n label: 'Channels',\n formatter: (channels) => {\n if (!channels || !channels.length) return 'None';\n return channels.map(c => `<span class=\"badge bg-secondary me-1\">${c}</span>`).join('');\n },\n sortable: false\n },\n {\n key: 'jobs_processed',\n label: 'Processed',\n sortable: true\n },\n {\n key: 'jobs_failed',\n label: 'Failed',\n formatter: (value) => {\n const badgeClass = value > 0 ? 'bg-danger' : 'bg-success';\n return `<span class=\"badge ${badgeClass}\">${value}</span>`;\n },\n sortable: true\n },\n {\n key: 'last_heartbeat',\n label: 'Last Heartbeat',\n formatter: (value) => {\n if (!value) return 'Never';\n const heartbeatTime = new Date(value);\n const now = new Date();\n const diffMs = now - heartbeatTime;\n const diffSeconds = Math.floor(diffMs / 1000);\n\n if (diffSeconds < 60) return `${diffSeconds}s ago`;\n if (diffSeconds < 3600) return `${Math.floor(diffSeconds / 60)}m ago`;\n return `${Math.floor(diffSeconds / 3600)}h ago`;\n },\n sortable: true\n },\n {\n key: 'started',\n label: 'Uptime',\n formatter: (value) => {\n if (!value) return 'Unknown';\n const startTime = new Date(value);\n const now = new Date();\n const diffMs = now - startTime;\n const diffSeconds = Math.floor(diffMs / 1000);\n\n if (diffSeconds < 60) return `${diffSeconds}s`;\n if (diffSeconds < 3600) return `${Math.floor(diffSeconds / 60)}m`;\n if (diffSeconds < 86400) return `${Math.floor(diffSeconds / 3600)}h`;\n return `${Math.floor(diffSeconds / 86400)}d`;\n },\n sortable: true\n }\n ],\n contextMenu: [\n {\n label: 'Ping Runner',\n action: 'ping-runner',\n icon: 'bi-wifi'\n },\n {\n label: 'View Details',\n action: 'view-runner-details',\n icon: 'bi-info-circle'\n },\n { separator: true },\n {\n label: 'Pause Runner',\n action: 'pause-runner',\n icon: 'bi-pause-circle',\n condition: (runner) => runner.get('alive') === true\n },\n {\n label: 'Resume Runner',\n action: 'resume-runner',\n icon: 'bi-play-circle',\n condition: (runner) => runner.get('alive') !== true\n },\n { separator: true },\n {\n label: 'Shutdown Runner',\n action: 'shutdown-runner',\n icon: 'bi-power',\n danger: true,\n condition: (runner) => runner.get('alive') === true\n }\n ],\n ...options\n });\n }\n\n async onActionPingRunner(event, element) {\n const runnerId = element.getAttribute('data-id');\n const runner = this.collection.get(runnerId);\n if (runner) {\n try {\n const result = await runner.ping();\n if (result.success) {\n this.getApp().toast.success('Runner ping successful');\n await this.collection.fetch();\n } else {\n this.getApp().toast.error(result.data?.error || 'Runner ping failed');\n }\n } catch (error) {\n this.getApp().toast.error('Error pinging runner: ' + error.message);\n }\n }\n }\n\n async onActionShutdownRunner(event, element) {\n const runnerId = element.getAttribute('data-id');\n const runner = this.collection.get(runnerId);\n if (runner && confirm('Are you sure you want to shutdown this runner?')) {\n try {\n const result = await runner.shutdown(true);\n if (result.success) {\n this.getApp().toast.success('Runner shutdown initiated');\n await this.collection.fetch();\n } else {\n this.getApp().toast.error(result.data?.error || 'Failed to shutdown runner');\n }\n } catch (error) {\n this.getApp().toast.error('Error shutting down runner: ' + error.message);\n }\n }\n }\n}\n","/**\n * ScheduledJobsTable - Scheduled jobs table\n */\n\nimport TableView from '@core/views/table/TableView.js';\nimport { JobList } from '@core/models/Job.js';\n\nexport default class ScheduledJobsTable extends TableView {\n constructor(options = {}) {\n super({\n Collection: JobList,\n collectionParams: { status: \"pending\" },\n hideActivePillNames: ['status'],\n options: {\n searchable: true,\n sortable: true,\n paginated: true,\n size: 10,\n ...options.options\n },\n columns: [\n {\n key: 'id',\n label: 'Job ID',\n formatter: 'truncate_middle(12)',\n sortable: true\n },\n {\n key: 'func',\n label: 'Function',\n sortable: true\n },\n {\n key: 'channel',\n label: 'Channel',\n formatter: 'badge',\n sortable: true\n },\n {\n key: 'run_at',\n label: 'Scheduled For',\n formatter: 'datetime',\n sortable: true\n },\n {\n key: 'created',\n label: 'Created',\n formatter: 'datetime',\n sortable: true\n },\n {\n key: 'expires_at',\n label: 'Expires At',\n formatter: 'datetime',\n sortable: true\n }\n ],\n ...options\n });\n }\n}\n","/**\n * JobsAdminPage - Async Job Engine management dashboard\n * Main page orchestrating job monitoring and management components\n */\n\nimport Page from '@core/Page.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport { Job, JobsEngineStats } from '@core/models/Job.js';\nimport { JobRunner, JobRunnerForms } from '@core/models/JobRunner.js';\nimport { MetricsChart } from '@ext/charts/index.js';\n\n// Import component views\nimport JobStatsView from './JobStatsView.js';\nimport JobHealthView from './JobHealthView.js';\nimport JobsTable from './tables/JobsTable.js';\nimport RunnersTable from './tables/RunnersTable.js';\nimport ScheduledJobsTable from './tables/ScheduledJobsTable.js';\n\nexport default class JobsAdminPage extends Page {\n constructor(options = {}) {\n super({\n title: 'Jobs Management',\n className: 'jobs-admin-page',\n ...options\n });\n\n this.pageTitle = 'Jobs Management';\n this.pageSubtitle = 'Async job monitoring and runner management';\n this.lastUpdated = new Date().toLocaleString();\n this.autoRefreshInterval = null;\n this.refreshRate = 30000; // 30 seconds default\n\n this.template = `\n <div class=\"jobs-admin-container\">\n <!-- Page Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-4\">\n <div>\n <h1 class=\"h3 mb-1\">{{pageTitle}}</h1>\n <p class=\"text-muted mb-0\">{{pageSubtitle}}</p>\n <small class=\"text-info\">\n <i class=\"bi bi-arrow-clockwise me-1\"></i>\n Auto-refresh: {{refreshRateSeconds}}s | Last updated: {{lastUpdated}}\n </small>\n </div>\n <div class=\"btn-group\" role=\"group\">\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm\"\n data-action=\"refresh-all\" title=\"Refresh All Data\">\n <i class=\"bi bi-arrow-clockwise\"></i> Refresh\n </button>\n <button type=\"button\" class=\"btn btn-outline-primary btn-sm\"\n data-action=\"export-data\" title=\"Export Data\">\n <i class=\"bi bi-download\"></i> Export\n </button>\n <div class=\"dropdown\">\n <button class=\"btn btn-outline-secondary btn-sm dropdown-toggle\"\n type=\"button\" data-bs-toggle=\"dropdown\">\n <i class=\"bi bi-gear\"></i> Settings\n </button>\n <ul class=\"dropdown-menu dropdown-menu-end\">\n <li><h6 class=\"dropdown-header\">Auto Refresh</h6></li>\n <li><button class=\"dropdown-item\" data-action=\"set-refresh-rate\" data-rate=\"5\">5 seconds</button></li>\n <li><button class=\"dropdown-item\" data-action=\"set-refresh-rate\" data-rate=\"10\">10 seconds</button></li>\n <li><button class=\"dropdown-item\" data-action=\"set-refresh-rate\" data-rate=\"30\">30 seconds</button></li>\n <li><button class=\"dropdown-item\" data-action=\"set-refresh-rate\" data-rate=\"0\">Off</button></li>\n <li><hr class=\"dropdown-divider\"></li>\n <li><button class=\"dropdown-item\" data-action=\"runner-broadcast\">Broadcast Command</button></li>\n </ul>\n </div>\n <div class=\"dropdown\">\n <button class=\"btn btn-danger btn-sm dropdown-toggle\"\n type=\"button\" data-bs-toggle=\"dropdown\">\n <i class=\"bi bi-wrench\"></i> Manage\n </button>\n <ul class=\"dropdown-menu dropdown-menu-end\">\n <li><button class=\"dropdown-item\" data-action=\"run-simple-job\"><i class=\"bi bi-play-circle me-2\"></i>Run Simple Job</button></li>\n <li><button class=\"dropdown-item\" data-action=\"run-test-jobs\"><i class=\"bi bi-robot me-2\"></i>Run Test Jobs</button></li>\n <li><hr class=\"dropdown-divider\"></li>\n <li><button class=\"dropdown-item\" data-action=\"clear-stuck\"><i class=\"bi bi-wrench me-2\"></i>Clear Stuck Jobs</button></li>\n <li><button class=\"dropdown-item\" data-action=\"clear-channel\"><i class=\"bi bi-eraser me-2\"></i>Clear Channel</button></li>\n <li><button class=\"dropdown-item\" data-action=\"purge-jobs\"><i class=\"bi bi-trash me-2\"></i>Purge Jobs</button></li>\n <li><button class=\"dropdown-item\" data-action=\"cleanup-consumers\"><i class=\"bi bi-people me-2\"></i>Cleanup Consumers</button></li>\n </ul>\n </div>\n </div>\n </div>\n\n <!-- Job Stats -->\n <div data-container=\"job-stats\"></div>\n\n <!-- Job Health -->\n <div class=\"row\">\n <div class=\"col-12\">\n <div data-container=\"job-health\"></div>\n </div>\n <div class=\"col-12\">\n <div class=\"mb-3\" data-container=\"job-metrics\"></div>\n </div>\n </div>\n\n <!-- Job Tables -->\n <div class=\"card border shadow\">\n <div class=\"card-header\">\n <h5 class=\"card-title mb-0\">\n <i class=\"bi bi-list-task me-2\"></i>Job Management\n </h5>\n </div>\n <div class=\"card-body\">\n <div data-container=\"job-tables\"></div>\n </div>\n </div>\n </div>\n `;\n }\n\n async onInit() {\n\n this.jobStats = new JobsEngineStats();\n // Create child views\n this.jobStatsView = new JobStatsView({\n containerId: 'job-stats',\n model: this.jobStats\n });\n this.addChild(this.jobStatsView);\n\n this.jobHealthView = new JobHealthView({\n containerId: 'job-health',\n model: this.jobStats\n });\n this.addChild(this.jobHealthView);\n\n this.jobTablesView = new TabView({\n containerId: 'job-tables',\n tabs: {\n 'Jobs': new JobsTable(),\n 'Runners': new RunnersTable(),\n 'Scheduled': new ScheduledJobsTable()\n },\n activeTab: 'Jobs'\n });\n this.addChild(this.jobTablesView);\n\n // Create API Metrics Chart\n this.jobMetricsChart = new MetricsChart({\n title: `<i class=\"bi bi-graph-up me-2\"></i> Job Metrics`,\n endpoint: '/api/metrics/fetch',\n height: 100,\n granularity: 'hours',\n category: 'jobs_channels',\n account: 'global',\n chartType: 'bar',\n showDateRange: false,\n yAxis: {\n label: 'Count',\n beginAtZero: true\n },\n tooltip: {\n y: 'number'\n },\n containerId: 'job-metrics'\n });\n this.addChild(this.jobMetricsChart);\n\n await this.jobStats.fetch();\n\n }\n\n // Auto-refresh management\n startAutoRefresh() {\n if (this.autoRefreshInterval) {\n clearInterval(this.autoRefreshInterval);\n }\n\n if (this.refreshRate > 0) {\n this.autoRefreshInterval = setInterval(async () => {\n await this.refreshData();\n }, this.refreshRate);\n }\n }\n\n async refreshData() {\n try {\n // Refresh stats and health\n await this.jobStats.fetch();\n\n // Refresh active table\n const activeTab = this.jobTablesView?.getActiveTab();\n if (activeTab) {\n const activeTable = this.jobTablesView.getTab(activeTab);\n if (activeTable?.collection?.fetch) {\n await activeTable.collection.fetch();\n }\n }\n\n this.lastUpdated = new Date().toLocaleString();\n this.updateHeaderTimestamp();\n\n } catch (error) {\n console.error('Failed to refresh jobs dashboard:', error);\n }\n }\n\n updateHeaderTimestamp() {\n const timestampElement = this.element?.querySelector('.text-info');\n if (timestampElement) {\n timestampElement.innerHTML = `\n <i class=\"bi bi-arrow-clockwise me-1\"></i>\n Auto-refresh: ${this.refreshRate / 1000}s | Last updated: ${this.lastUpdated}\n `;\n }\n }\n\n get refreshRateSeconds() {\n return this.refreshRate / 1000;\n }\n\n // Action handlers\n async onActionRefreshAll(event, element) {\n try {\n const icon = element.querySelector('i');\n icon?.classList.add('spinning');\n element.disabled = true;\n\n await this.refreshData();\n await this.render();\n\n } catch (error) {\n console.error('Failed to refresh jobs dashboard:', error);\n } finally {\n const icon = element.querySelector('i');\n icon?.classList.remove('spinning');\n element.disabled = false;\n }\n }\n\n async onActionSetRefreshRate(event, element) {\n const rate = parseInt(element.getAttribute('data-rate')) * 1000;\n this.refreshRate = rate;\n this.startAutoRefresh();\n\n const rateText = rate === 0 ? 'Off' : `${rate / 1000}s`;\n this.getApp().toast.success(`Auto-refresh set to ${rateText}`);\n }\n\n async onActionExportData() {\n await Dialog.showAlert({\n title: 'Export Data',\n message: 'Data export functionality coming soon!',\n type: 'info'\n });\n }\n\n // Job management actions\n async onActionRunSimpleJob(event, element) {\n const confirmed = await Dialog.showConfirm({\n title: 'Run Simple Job',\n message: 'This will run a simple test job to verify the job system is working correctly.',\n confirmText: 'Run Test',\n confirmClass: 'btn-success'\n });\n\n if (confirmed) {\n await this.executeJobAction(element, () => Job.test(), 'Test job started successfully');\n }\n }\n\n async onActionRunTestJobs(event, element) {\n const confirmed = await Dialog.showConfirm({\n title: 'Run Test Jobs',\n message: 'This will run a suite of test jobs to verify all job functionalities.',\n confirmText: 'Run Tests',\n confirmClass: 'btn-success'\n });\n\n if (confirmed) {\n await this.executeJobAction(element, () => Job.tests(), 'Test suite started successfully');\n }\n }\n\n async onActionClearStuck(event, element) {\n const channels = this.jobHealthView?.health?.channelsArray || [];\n const channelOptions = [\n { value: '', label: 'All Channels' },\n ...channels.map(ch => ({ value: ch.channel, label: ch.channel }))\n ];\n\n const result = await Dialog.showForm({\n title: 'Clear Stuck Jobs',\n formConfig: {\n fields: [\n {\n name: 'channel',\n type: 'select',\n label: 'Channel',\n options: channelOptions,\n value: '',\n help: 'Select specific channel or leave empty for all channels'\n }\n ]\n }\n });\n\n if (result) {\n await this.executeJobAction(\n element,\n () => Job.clearStuck(result.channel || null),\n (response) => {\n const count = response.data.count || 0;\n const channelText = result.channel ? ` from channel \"${result.channel}\"` : '';\n return `Cleared ${count} stuck job${count !== 1 ? 's' : ''}${channelText}`;\n }\n );\n }\n }\n\n async onActionClearChannel(event, element) {\n const channels = this.jobHealthView?.health?.channelsArray || [];\n const channelOptions = channels.map(ch => ({ value: ch.channel, label: ch.channel }));\n\n const result = await Dialog.showForm({\n title: 'Clear Channel',\n formConfig: {\n fields: [\n {\n name: 'channel',\n type: 'select',\n label: 'Channel',\n options: channelOptions,\n required: true,\n help: 'Select the channel to clear.'\n }\n ]\n }\n });\n\n if (result) {\n await this.executeJobAction(\n element,\n () => Job.clearChannel(result.channel),\n `Channel \"${result.channel}\" cleared successfully.`\n );\n }\n }\n\n async onActionPurgeJobs(event, element) {\n const result = await Dialog.showForm({\n title: 'Purge Old Jobs',\n formConfig: {\n fields: [\n {\n name: 'days_old',\n type: 'number',\n label: 'Days Old',\n value: 30,\n required: true,\n help: 'Delete jobs older than this many days.'\n }\n ]\n }\n });\n\n if (result) {\n await this.executeJobAction(\n element,\n () => Job.purgeJobs(result.days_old),\n (response) => {\n const count = response.data.count || 0;\n return `Purged ${count} old job(s).`;\n }\n );\n }\n }\n\n async onActionCleanupConsumers(event, element) {\n const confirmed = await Dialog.showConfirm({\n title: 'Cleanup Consumers',\n message: 'This will remove stale consumer records from the system. This is generally safe.',\n confirmText: 'Cleanup',\n confirmClass: 'btn-warning'\n });\n\n if (confirmed) {\n await this.executeJobAction(\n element,\n () => Job.cleanConsumers(),\n (response) => {\n const count = response.data.count || 0;\n return `Cleaned up ${count} consumer(s).`;\n }\n );\n }\n }\n\n async onActionRunnerBroadcast() {\n const result = await Dialog.showForm({\n title: 'Broadcast Command to All Runners',\n formConfig: JobRunnerForms.broadcast\n });\n\n if (result) {\n try {\n const broadcastResult = await JobRunner.broadcast(\n result.command,\n {},\n result.timeout\n );\n\n if (broadcastResult.success) {\n this.getApp().toast.success(`Broadcast command \"${result.command}\" sent successfully`);\n await this.refreshData();\n } else {\n this.getApp().toast.error(broadcastResult.data?.error || 'Failed to broadcast command');\n }\n } catch (error) {\n console.error('Failed to broadcast command:', error);\n this.getApp().toast.error('Error broadcasting command: ' + error.message);\n }\n }\n }\n\n // Helper method to execute job actions with consistent error handling\n async executeJobAction(element, actionFn, successMessage) {\n try {\n element.disabled = true;\n const icon = element.querySelector('i');\n icon?.classList.add('spinning');\n\n const result = await actionFn();\n if (result.success && result.data?.status) {\n const message = typeof successMessage === 'function'\n ? successMessage(result)\n : successMessage;\n this.getApp().toast.success(message);\n await this.refreshData();\n } else {\n this.getApp().toast.error(result.data?.error || 'Operation failed');\n }\n } catch (error) {\n console.error('Job action failed:', error);\n this.getApp().toast.error('Error: ' + error.message);\n } finally {\n element.disabled = false;\n const icon = element.querySelector('i');\n icon?.classList.remove('spinning');\n }\n }\n\n async onEnter() {\n // Start auto-refresh\n this.startAutoRefresh();\n }\n\n async onExit() {\n if (this.autoRefreshInterval) {\n clearInterval(this.autoRefreshInterval);\n this.autoRefreshInterval = null;\n }\n }\n\n // Public API\n async refreshDashboard() {\n return await this.refreshData();\n }\n\n getStats() {\n return this.jobStatsView?.stats || {};\n }\n\n getHealth() {\n return this.jobHealthView?.health || {};\n }\n}\n","/**\n * TaskDetailsView - Detailed task information view for Dialog display\n * Shows comprehensive task data, logs, metrics, and available actions\n */\n\nimport View from '@core/View.js';\n\nexport default class TaskDetailsView extends View {\n constructor(options = {}) {\n super({\n ...options,\n className: 'mojo-task-details-view'\n });\n\n this.task = options.task || null;\n this.logs = [];\n this.metrics = null;\n }\n\n async getTemplate() {\n return `\n <div class=\"mojo-task-details-container\">\n {{#task}}\n <!-- Task Overview -->\n <div class=\"card border-0 bg-light mb-3\">\n <div class=\"card-body\">\n <div class=\"row\">\n <div class=\"col-md-6\">\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Task ID</label>\n <div class=\"font-monospace\">{{id}}</div>\n </div>\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Function</label>\n <div>{{function}}</div>\n </div>\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Channel</label>\n <div>\n <span class=\"badge bg-primary\">{{channel}}</span>\n </div>\n </div>\n </div>\n <div class=\"col-md-6\">\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Status</label>\n <div>\n <span class=\"badge {{statusBadgeClass}} fs-6\">\n <i class=\"bi {{statusIcon}}\"></i> {{status|uppercase}}\n </span>\n </div>\n </div>\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Created</label>\n <div>{{created|datetime}}</div>\n </div>\n {{#completed_at}}\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Completed</label>\n <div>{{completed_at|datetime}}</div>\n </div>\n {{/completed_at}}\n {{#expires}}\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Expires</label>\n <div class=\"{{expiresClass}}\">{{expires|datetime}}</div>\n </div>\n {{/expires}}\n </div>\n </div>\n </div>\n </div>\n\n <!-- Task Data -->\n {{#data}}\n <div class=\"card mb-3\">\n <div class=\"card-header py-2\">\n <h6 class=\"mb-0\">\n <i class=\"bi bi-database me-2\"></i>Task Data\n </h6>\n </div>\n <div class=\"card-body\">\n <pre class=\"bg-light p-3 rounded mb-0\"><code>{{dataFormatted}}</code></pre>\n </div>\n </div>\n {{/data}}\n\n <!-- Error Information -->\n {{#error}}\n <div class=\"card border-danger mb-3\">\n <div class=\"card-header bg-danger-subtle py-2\">\n <h6 class=\"mb-0 text-danger\">\n <i class=\"bi bi-exclamation-triangle me-2\"></i>Error Details\n </h6>\n </div>\n <div class=\"card-body\">\n <div class=\"alert alert-danger mb-0\">\n <strong>Error:</strong> {{error}}\n </div>\n {{#errorDetails}}\n <div class=\"mt-3\">\n <label class=\"form-label fw-bold small\">Stack Trace:</label>\n <pre class=\"bg-light p-3 rounded small mb-0\"><code>{{errorDetails}}</code></pre>\n </div>\n {{/errorDetails}}\n </div>\n </div>\n {{/error}}\n\n <!-- Performance Metrics -->\n {{#metrics}}\n <div class=\"card mb-3\">\n <div class=\"card-header py-2\">\n <h6 class=\"mb-0\">\n <i class=\"bi bi-speedometer2 me-2\"></i>Performance Metrics\n </h6>\n </div>\n <div class=\"card-body\">\n <div class=\"row\">\n <div class=\"col-md-3 col-6 mb-3\">\n <div class=\"text-center\">\n <div class=\"h5 mb-1 text-primary\">{{executionTime}}ms</div>\n <small class=\"text-muted\">Execution Time</small>\n </div>\n </div>\n <div class=\"col-md-3 col-6 mb-3\">\n <div class=\"text-center\">\n <div class=\"h5 mb-1 text-info\">{{memoryUsage}}MB</div>\n <small class=\"text-muted\">Memory Usage</small>\n </div>\n </div>\n <div class=\"col-md-3 col-6 mb-3\">\n <div class=\"text-center\">\n <div class=\"h5 mb-1 text-warning\">{{cpuUsage}}%</div>\n <small class=\"text-muted\">CPU Usage</small>\n </div>\n </div>\n <div class=\"col-md-3 col-6 mb-3\">\n <div class=\"text-center\">\n <div class=\"h5 mb-1 text-secondary\">{{retryCount}}</div>\n <small class=\"text-muted\">Retry Count</small>\n </div>\n </div>\n </div>\n </div>\n </div>\n {{/metrics}}\n\n <!-- Task Logs -->\n <div class=\"card\">\n <div class=\"card-header py-2 d-flex justify-content-between align-items-center\">\n <h6 class=\"mb-0\">\n <i class=\"bi bi-journal-text me-2\"></i>Task Logs\n </h6>\n <button class=\"btn btn-sm btn-outline-primary\" data-action=\"refresh-logs\">\n <i class=\"bi bi-arrow-clockwise\"></i> Refresh\n </button>\n </div>\n <div class=\"card-body p-0\">\n <div class=\"mojo-task-logs-container\" style=\"max-height: 300px; overflow-y: auto;\">\n {{#logs.length}}\n {{#logs}}\n <div class=\"log-entry p-3 border-bottom\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div class=\"log-message flex-grow-1\">\n <span class=\"badge bg-{{levelClass}} me-2\">{{level|uppercase}}</span>\n {{message}}\n </div>\n <small class=\"text-muted ms-3\">{{timestamp|time}}</small>\n </div>\n </div>\n {{/logs}}\n {{/logs.length}}\n {{^logs.length}}\n <div class=\"text-center text-muted py-5\">\n <i class=\"bi bi-journal fs-1 opacity-50\"></i>\n <p class=\"mb-0 mt-2\">No logs available for this task</p>\n </div>\n {{/logs.length}}\n </div>\n </div>\n </div>\n {{/task}}\n\n {{^task}}\n <div class=\"alert alert-warning\">\n <i class=\"bi bi-exclamation-triangle me-2\"></i>\n No task data available\n </div>\n {{/task}}\n </div>\n `;\n }\n\n async onInit() {\n if (this.task) {\n await this.prepareTaskData();\n await this.loadTaskLogs();\n await this.loadTaskMetrics();\n }\n }\n\n async prepareTaskData() {\n if (!this.task) return;\n\n // Status styling\n this.task.statusBadgeClass = this.getStatusBadgeClass(this.task.status);\n this.task.statusIcon = this.getStatusIcon(this.task.status);\n\n // Format data for display\n if (this.task.data && typeof this.task.data === 'object') {\n this.task.dataFormatted = JSON.stringify(this.task.data, null, 2);\n }\n\n // Check if task has expired\n if (this.task.expires) {\n this.task.expiresClass = this.task.expires * 1000 < Date.now() ? 'text-danger' : 'text-muted';\n }\n }\n\n getStatusBadgeClass(status) {\n const classes = {\n 'pending': 'bg-primary',\n 'running': 'bg-success', \n 'completed': 'bg-info',\n 'error': 'bg-danger',\n 'cancelled': 'bg-secondary',\n 'expired': 'bg-warning'\n };\n return classes[status] || 'bg-secondary';\n }\n\n getStatusIcon(status) {\n const icons = {\n 'pending': 'bi-hourglass',\n 'running': 'bi-arrow-repeat',\n 'completed': 'bi-check-circle',\n 'error': 'bi-x-octagon',\n 'cancelled': 'bi-x-circle',\n 'expired': 'bi-clock'\n };\n return icons[status] || 'bi-question-circle';\n }\n\n getLogLevelClass(level) {\n const classes = {\n 'debug': 'secondary',\n 'info': 'primary',\n 'warning': 'warning',\n 'error': 'danger'\n };\n return classes[level] || 'secondary';\n }\n\n async loadTaskLogs() {\n if (!this.task?.id) return;\n\n try {\n const response = await this.getApp().rest.GET(`/api/tasks/${this.task.id}/logs`);\n if (response.success && response.data.status) {\n this.logs = response.data.data.map(log => ({\n ...log,\n levelClass: this.getLogLevelClass(log.level)\n }));\n } else {\n this.logs = [];\n }\n } catch (error) {\n console.error('Failed to load task logs:', error);\n this.logs = [];\n }\n }\n\n async loadTaskMetrics() {\n if (!this.task?.id) return;\n\n try {\n const response = await this.getApp().rest.GET(`/api/tasks/${this.task.id}/metrics`);\n if (response.success && response.data.status) {\n this.metrics = response.data.data;\n }\n } catch (error) {\n console.error('Failed to load task metrics:', error);\n this.metrics = null;\n }\n }\n\n async setTask(task) {\n this.task = task;\n await this.prepareTaskData();\n await this.loadTaskLogs();\n await this.loadTaskMetrics();\n \n if (this.isMounted()) {\n await this.render();\n }\n }\n\n async onActionRefreshLogs(action, event, element) {\n if (!this.task?.id) return;\n\n try {\n element.disabled = true;\n const icon = element.querySelector('i');\n if (icon) icon.classList.add('spinning');\n\n await this.loadTaskLogs();\n await this.render();\n \n } catch (error) {\n console.error('Failed to refresh logs:', error);\n this.showError('Failed to refresh logs: ' + error.message);\n } finally {\n element.disabled = false;\n const icon = element.querySelector('i');\n if (icon) icon.classList.remove('spinning');\n }\n }\n\n // Static method for easy Dialog integration\n static async show(task, options = {}) {\n const view = new TaskDetailsView({ task });\n await view.onInit();\n\n const buttons = [];\n\n // Add action buttons based on task status\n if (['pending', 'running'].includes(task.status)) {\n buttons.push({\n text: 'Cancel Task',\n class: 'btn-outline-danger',\n action: async () => {\n if (confirm('Are you sure you want to cancel this task?')) {\n try {\n const response = await view.getApp().rest.POST(`/api/tasks/${task.id}/cancel`);\n if (response.success && response.data.status) {\n view.showSuccess('Task cancelled successfully');\n return { action: 'cancelled', task };\n } else {\n view.showError(response.data.error || 'Failed to cancel task');\n }\n } catch (error) {\n view.showError('Failed to cancel task: ' + error.message);\n }\n }\n return null;\n }\n });\n }\n\n if (task.status === 'error') {\n buttons.push({\n text: 'Retry Task',\n class: 'btn-outline-primary',\n action: async () => {\n try {\n const response = await view.getApp().rest.POST(`/api/tasks/${task.id}/retry`);\n if (response.success && response.data.status) {\n view.showSuccess('Task queued for retry');\n return { action: 'retried', task };\n } else {\n view.showError(response.data.error || 'Failed to retry task');\n }\n } catch (error) {\n view.showError('Failed to retry task: ' + error.message);\n }\n return null;\n }\n });\n }\n\n buttons.push({\n text: 'Clone Task',\n class: 'btn-outline-info',\n action: async () => {\n try {\n const response = await view.getApp().rest.POST(`/api/tasks/${task.id}/clone`);\n if (response.success && response.data.status) {\n view.showSuccess('Task cloned successfully');\n return { action: 'cloned', originalTask: task, newTask: response.data.data };\n } else {\n view.showError(response.data.error || 'Failed to clone task');\n }\n } catch (error) {\n view.showError('Failed to clone task: ' + error.message);\n }\n return null;\n }\n });\n\n buttons.push({\n text: 'Export',\n class: 'btn-outline-secondary',\n action: () => {\n try {\n const exportData = {\n task: task,\n logs: view.logs,\n metrics: view.metrics,\n exported_at: new Date().toISOString(),\n exported_by: 'task-management-system'\n };\n\n const blob = new Blob([JSON.stringify(exportData, null, 2)], {\n type: 'application/json'\n });\n \n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `task-${task.id}-${Date.now()}.json`;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n\n view.showSuccess('Task data exported successfully');\n return null;\n } catch (error) {\n view.showError('Failed to export task data');\n return null;\n }\n }\n });\n\n buttons.push({\n text: 'Close',\n class: 'btn-secondary',\n dismiss: true\n });\n\n return await Dialog.showDialog({\n title: `<i class=\"bi bi-info-circle me-2\"></i>Task Details - ${task.id}`,\n body: view,\n size: 'lg',\n scrollable: true,\n buttons: buttons,\n ...options\n });\n }\n}","/**\n * RunnerDetailsView - Detailed runner information view for Dialog display\n * Shows comprehensive runner status, performance, tasks, and management actions\n */\n\nimport View from '@core/View.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\n\nexport default class RunnerDetailsView extends View {\n constructor(options = {}) {\n super({\n ...options,\n className: 'mojo-runner-details-view'\n });\n\n this.runner = options.runner || null;\n this.currentTasks = [];\n this.logs = [];\n this.metrics = null;\n this.config = null;\n }\n\n async getTemplate() {\n return `\n <div class=\"mojo-runner-details-container\">\n {{#runner}}\n <!-- Runner Overview -->\n <div class=\"card border-0 bg-light mb-3\">\n <div class=\"card-body\">\n <div class=\"row\">\n <div class=\"col-md-6\">\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Hostname</label>\n <div class=\"h5 mb-0 font-monospace\">{{hostname}}</div>\n </div>\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Status</label>\n <div>\n <span class=\"badge {{statusBadgeClass}} fs-6\">\n <i class=\"bi {{statusIcon}}\"></i> {{status|uppercase}}\n </span>\n </div>\n </div>\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Workers</label>\n <div class=\"h5 mb-0\">\n {{#metrics.activeWorkers}}{{metrics.activeWorkers}}/{{/metrics.activeWorkers}}{{max_workers}} workers\n {{#metrics.workerUtilization}}\n <div class=\"progress mt-1\" style=\"height: 6px;\">\n <div class=\"progress-bar {{metrics.utilizationClass}}\" style=\"width: {{metrics.workerUtilization}}%\"></div>\n </div>\n {{/metrics.workerUtilization}}\n </div>\n </div>\n </div>\n <div class=\"col-md-6\">\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Started</label>\n <div>{{started_at|datetime}}</div>\n </div>\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Last Ping</label>\n <div class=\"{{pingAgeClass}}\">{{last_ping|datetime}} ({{pingAgeText}})</div>\n </div>\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Uptime</label>\n <div>{{uptimeText}}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Channel Assignment -->\n <div class=\"card mb-3\">\n <div class=\"card-header py-2\">\n <h6 class=\"mb-0\">\n <i class=\"bi bi-collection me-2\"></i>Assigned Channels\n </h6>\n </div>\n <div class=\"card-body\">\n {{#channels.length}}\n <div class=\"d-flex flex-wrap gap-2\">\n {{#channels}}\n <span class=\"badge bg-primary-subtle text-primary px-3 py-2\">\n {{.}}\n </span>\n {{/channels}}\n </div>\n {{/channels.length}}\n {{^channels.length}}\n <div class=\"text-center text-muted py-3\">\n <i class=\"bi bi-collection opacity-50\"></i>\n <p class=\"mb-0 mt-2\">No channels assigned</p>\n </div>\n {{/channels.length}}\n </div>\n </div>\n\n <!-- Performance Metrics -->\n {{#metrics}}\n <div class=\"card mb-3\">\n <div class=\"card-header py-2 d-flex justify-content-between align-items-center\">\n <h6 class=\"mb-0\">\n <i class=\"bi bi-speedometer2 me-2\"></i>Performance Metrics\n </h6>\n <button class=\"btn btn-sm btn-outline-primary\" data-action=\"refresh-metrics\">\n <i class=\"bi bi-arrow-clockwise\"></i> Refresh\n </button>\n </div>\n <div class=\"card-body\">\n <div class=\"row\">\n <div class=\"col-lg-3 col-md-6 mb-3\">\n <div class=\"text-center p-3 bg-light rounded\">\n <div class=\"h4 mb-1 text-success\">{{tasksCompleted|number}}</div>\n <small class=\"text-muted\">Tasks Completed</small>\n {{#tasksCompletedToday}}\n <div class=\"small text-success mt-1\">+{{tasksCompletedToday}} today</div>\n {{/tasksCompletedToday}}\n </div>\n </div>\n <div class=\"col-lg-3 col-md-6 mb-3\">\n <div class=\"text-center p-3 bg-light rounded\">\n <div class=\"h4 mb-1 text-info\">{{avgExecutionTime}}ms</div>\n <small class=\"text-muted\">Avg Execution</small>\n <div class=\"small {{performanceTrend.class}} mt-1\">\n <i class=\"bi {{performanceTrend.icon}}\"></i> {{performanceTrend.text}}\n </div>\n </div>\n </div>\n <div class=\"col-lg-3 col-md-6 mb-3\">\n <div class=\"text-center p-3 bg-light rounded\">\n <div class=\"h4 mb-1 text-danger\">{{errorCount|number}}</div>\n <small class=\"text-muted\">Errors</small>\n <div class=\"small text-muted mt-1\">{{errorRate}}% error rate</div>\n </div>\n </div>\n <div class=\"col-lg-3 col-md-6 mb-3\">\n <div class=\"text-center p-3 bg-light rounded\">\n <div class=\"h4 mb-1 text-warning\">{{queueBacklog|number}}</div>\n <small class=\"text-muted\">Queue Backlog</small>\n </div>\n </div>\n </div>\n\n <!-- Resource Usage Bars -->\n <div class=\"row mt-3\">\n <div class=\"col-md-6 mb-2\">\n <label class=\"form-label fw-bold small\">CPU Usage</label>\n <div class=\"progress\" style=\"height: 20px;\">\n <div class=\"progress-bar {{cpu.class}}\" style=\"width: {{cpu.percentage}}%\">\n {{cpu.percentage}}%\n </div>\n </div>\n </div>\n <div class=\"col-md-6 mb-2\">\n <label class=\"form-label fw-bold small\">Memory Usage</label>\n <div class=\"progress\" style=\"height: 20px;\">\n <div class=\"progress-bar {{memory.class}}\" style=\"width: {{memory.percentage}}%\">\n {{memory.used}}MB / {{memory.total}}MB\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n {{/metrics}}\n\n <!-- Current Tasks -->\n <div class=\"card mb-3\">\n <div class=\"card-header py-2 d-flex justify-content-between align-items-center\">\n <h6 class=\"mb-0\">\n <i class=\"bi bi-list-task me-2\"></i>Current Tasks ({{currentTasks.length}})\n </h6>\n <button class=\"btn btn-sm btn-outline-primary\" data-action=\"refresh-tasks\">\n <i class=\"bi bi-arrow-clockwise\"></i> Refresh\n </button>\n </div>\n <div class=\"card-body p-0\">\n {{#currentTasks.length}}\n <div class=\"table-responsive\">\n <table class=\"table table-sm table-hover mb-0\">\n <thead class=\"table-light\">\n <tr>\n <th class=\"border-0\">Task ID</th>\n <th class=\"border-0\">Function</th>\n <th class=\"border-0\">Channel</th>\n <th class=\"border-0\">Started</th>\n <th class=\"border-0\">Duration</th>\n <th class=\"border-0 text-end\">Actions</th>\n </tr>\n </thead>\n <tbody>\n {{#currentTasks}}\n <tr>\n <td class=\"font-monospace small\">{{id|truncate(12)}}</td>\n <td>{{function}}</td>\n <td><span class=\"badge bg-primary-subtle text-primary\">{{channel}}</span></td>\n <td>{{started|time}}</td>\n <td class=\"text-muted\">{{duration}}</td>\n <td class=\"text-end\">\n <div class=\"btn-group btn-group-sm\">\n <button class=\"btn btn-outline-info\" data-action=\"view-task\" data-task-id=\"{{id}}\" title=\"View Details\">\n <i class=\"bi bi-eye\"></i>\n </button>\n <button class=\"btn btn-outline-warning\" data-action=\"cancel-task\" data-task-id=\"{{id}}\" title=\"Cancel\">\n <i class=\"bi bi-x-circle\"></i>\n </button>\n </div>\n </td>\n </tr>\n {{/currentTasks}}\n </tbody>\n </table>\n </div>\n {{/currentTasks.length}}\n {{^currentTasks.length}}\n <div class=\"text-center text-muted py-5\">\n <i class=\"bi bi-list-task fs-1 opacity-50\"></i>\n <p class=\"mb-0 mt-2\">No active tasks</p>\n </div>\n {{/currentTasks.length}}\n </div>\n </div>\n\n <!-- Runner Logs -->\n <div class=\"card\">\n <div class=\"card-header py-2 d-flex justify-content-between align-items-center\">\n <h6 class=\"mb-0\">\n <i class=\"bi bi-journal-text me-2\"></i>Runner Logs\n </h6>\n <div class=\"btn-group btn-group-sm\">\n <button class=\"btn btn-outline-secondary active\" data-action=\"filter-logs\" data-level=\"all\">All</button>\n <button class=\"btn btn-outline-primary\" data-action=\"filter-logs\" data-level=\"info\">Info</button>\n <button class=\"btn btn-outline-warning\" data-action=\"filter-logs\" data-level=\"warning\">Warning</button>\n <button class=\"btn btn-outline-danger\" data-action=\"filter-logs\" data-level=\"error\">Error</button>\n <button class=\"btn btn-outline-primary\" data-action=\"refresh-logs\">\n <i class=\"bi bi-arrow-clockwise\"></i>\n </button>\n </div>\n </div>\n <div class=\"card-body p-0\">\n <div class=\"mojo-runner-logs-container\" style=\"max-height: 300px; overflow-y: auto;\">\n {{#logs.length}}\n {{#logs}}\n <div class=\"log-entry p-3 border-bottom\" data-level=\"{{level}}\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div class=\"log-message flex-grow-1\">\n <span class=\"badge bg-{{levelClass}} me-2\">{{level|uppercase}}</span>\n {{message}}\n </div>\n <small class=\"text-muted ms-3\">{{timestamp|time}}</small>\n </div>\n </div>\n {{/logs}}\n {{/logs.length}}\n {{^logs.length}}\n <div class=\"text-center text-muted py-5\">\n <i class=\"bi bi-journal fs-1 opacity-50\"></i>\n <p class=\"mb-0 mt-2\">No logs available</p>\n </div>\n {{/logs.length}}\n </div>\n </div>\n </div>\n {{/runner}}\n\n {{^runner}}\n <div class=\"alert alert-warning\">\n <i class=\"bi bi-exclamation-triangle me-2\"></i>\n No runner data available\n </div>\n {{/runner}}\n </div>\n `;\n }\n\n async onInit() {\n if (this.runner) {\n await this.prepareRunnerData();\n await this.loadRunnerMetrics();\n await this.loadCurrentTasks();\n await this.loadRunnerLogs();\n }\n }\n\n async prepareRunnerData() {\n if (!this.runner) return;\n\n // Status styling\n this.runner.isActive = this.runner.status === 'active';\n this.runner.statusBadgeClass = this.runner.isActive ? 'bg-success' : 'bg-warning';\n this.runner.statusIcon = this.runner.isActive ? 'bi-check-circle-fill' : 'bi-exclamation-triangle-fill';\n\n // Calculate ping age and uptime\n if (this.runner.ping_age !== undefined) {\n this.runner.pingAgeText = this.formatDuration(this.runner.ping_age);\n this.runner.pingAgeClass = this.runner.ping_age > 300 ? 'text-danger' : 'text-muted';\n }\n\n if (this.runner.started_at) {\n const uptimeSeconds = Date.now() / 1000 - this.runner.started_at;\n this.runner.uptimeText = this.formatUptime(uptimeSeconds);\n }\n }\n\n async loadRunnerMetrics() {\n if (!this.runner?.hostname) return;\n\n try {\n const response = await this.getApp().rest.GET(`/api/runners/${this.runner.hostname}/metrics`);\n if (response.success && response.data.status) {\n const data = response.data.data;\n this.metrics = {\n activeWorkers: data.activeWorkers || 0,\n tasksCompleted: data.tasksCompleted || 0,\n tasksCompletedToday: data.tasksCompletedToday || 0,\n avgExecutionTime: data.avgExecutionTime || 0,\n errorCount: data.errorCount || 0,\n errorRate: data.errorRate || 0,\n queueBacklog: data.queueBacklog || 0,\n workerUtilization: Math.round((data.activeWorkers / this.runner.max_workers) * 100),\n utilizationClass: this.getUtilizationClass(data.activeWorkers / this.runner.max_workers),\n performanceTrend: this.getPerformanceTrend(data.avgExecutionTime || 0),\n cpu: this.getResourceStatus(data.cpuUsage || 0),\n memory: this.getMemoryStatus(data.memoryUsed || 0, data.memoryTotal || 1000)\n };\n }\n } catch (error) {\n console.error('Failed to load runner metrics:', error);\n this.metrics = this.getDefaultMetrics();\n }\n }\n\n async loadCurrentTasks() {\n if (!this.runner?.hostname) return;\n\n try {\n const response = await this.getApp().rest.GET(`/api/runners/${this.runner.hostname}/tasks`);\n if (response.success && response.data.status) {\n this.currentTasks = response.data.data.map(task => ({\n ...task,\n duration: this.formatDuration(Date.now() / 1000 - task.started)\n }));\n } else {\n this.currentTasks = [];\n }\n } catch (error) {\n console.error('Failed to load current tasks:', error);\n this.currentTasks = [];\n }\n }\n\n async loadRunnerLogs() {\n if (!this.runner?.hostname) return;\n\n try {\n const response = await this.getApp().rest.GET(`/api/runners/${this.runner.hostname}/logs?limit=50`);\n if (response.success && response.data.status) {\n this.logs = response.data.data.map(log => ({\n ...log,\n levelClass: this.getLogLevelClass(log.level)\n }));\n } else {\n this.logs = [];\n }\n } catch (error) {\n console.error('Failed to load runner logs:', error);\n this.logs = [];\n }\n }\n\n getDefaultMetrics() {\n return {\n activeWorkers: 0,\n tasksCompleted: 0,\n tasksCompletedToday: 0,\n avgExecutionTime: 0,\n errorCount: 0,\n errorRate: 0,\n queueBacklog: 0,\n workerUtilization: 0,\n utilizationClass: 'bg-secondary',\n performanceTrend: { class: 'text-muted', icon: 'bi-dash', text: 'No data' },\n cpu: { percentage: 0, class: 'bg-secondary' },\n memory: { used: 0, total: 0, percentage: 0, class: 'bg-secondary' }\n };\n }\n\n getUtilizationClass(utilization) {\n if (utilization > 0.9) return 'bg-danger';\n if (utilization > 0.7) return 'bg-warning';\n if (utilization > 0.5) return 'bg-info';\n return 'bg-success';\n }\n\n getPerformanceTrend(avgTime) {\n if (avgTime < 1000) {\n return { class: 'text-success', icon: 'bi-arrow-up', text: 'Excellent' };\n } else if (avgTime < 5000) {\n return { class: 'text-warning', icon: 'bi-arrow-right', text: 'Good' };\n } else {\n return { class: 'text-danger', icon: 'bi-arrow-down', text: 'Slow' };\n }\n }\n\n getResourceStatus(usage) {\n const percentage = Math.round(usage);\n let cssClass = 'bg-success';\n \n if (percentage > 80) cssClass = 'bg-danger';\n else if (percentage > 60) cssClass = 'bg-warning';\n else if (percentage > 40) cssClass = 'bg-info';\n\n return { percentage, class: cssClass };\n }\n\n getMemoryStatus(used, total) {\n const percentage = Math.round((used / total) * 100);\n return {\n used: Math.round(used),\n total: Math.round(total),\n percentage,\n class: this.getResourceStatus(percentage).class\n };\n }\n\n getLogLevelClass(level) {\n const classes = {\n 'debug': 'secondary',\n 'info': 'primary',\n 'warning': 'warning',\n 'error': 'danger'\n };\n return classes[level] || 'secondary';\n }\n\n formatUptime(seconds) {\n const days = Math.floor(seconds / 86400);\n const hours = Math.floor((seconds % 86400) / 3600);\n const minutes = Math.floor((seconds % 3600) / 60);\n \n if (days > 0) return `${days}d ${hours}h ${minutes}m`;\n if (hours > 0) return `${hours}h ${minutes}m`;\n return `${minutes}m`;\n }\n\n formatDuration(seconds) {\n if (seconds < 60) return `${Math.round(seconds)}s`;\n if (seconds < 3600) return `${Math.round(seconds / 60)}m`;\n if (seconds < 86400) return `${Math.round(seconds / 3600)}h`;\n return `${Math.round(seconds / 86400)}d`;\n }\n\n async setRunner(runner) {\n this.runner = runner;\n await this.prepareRunnerData();\n await this.loadRunnerMetrics();\n await this.loadCurrentTasks();\n await this.loadRunnerLogs();\n \n if (this.isMounted()) {\n await this.render();\n }\n }\n\n // Action handlers\n async onActionRefreshMetrics(action, event, element) {\n await this.loadRunnerMetrics();\n await this.render();\n }\n\n async onActionRefreshTasks(action, event, element) {\n await this.loadCurrentTasks();\n await this.render();\n }\n\n async onActionRefreshLogs(action, event, element) {\n await this.loadRunnerLogs();\n await this.render();\n }\n\n async onActionFilterLogs(action, event, element) {\n const level = element.getAttribute('data-level');\n const logEntries = this.element.querySelectorAll('.log-entry');\n \n logEntries.forEach(entry => {\n if (level === 'all' || entry.getAttribute('data-level') === level) {\n entry.style.display = 'block';\n } else {\n entry.style.display = 'none';\n }\n });\n\n // Update active filter button\n this.element.querySelectorAll('[data-action=\"filter-logs\"]').forEach(btn => {\n btn.classList.remove('active');\n });\n element.classList.add('active');\n }\n\n async onActionViewTask(action, event, element) {\n const taskId = element.getAttribute('data-task-id');\n this.emit('task:view', { taskId, runner: this.runner });\n }\n\n async onActionCancelTask(action, event, element) {\n const taskId = element.getAttribute('data-task-id');\n \n if (!confirm('Are you sure you want to cancel this task?')) {\n return;\n }\n\n try {\n const response = await this.getApp().rest.POST(`/api/tasks/${taskId}/cancel`);\n \n if (response.success && response.data.status) {\n this.showSuccess('Task cancelled successfully');\n await this.loadCurrentTasks();\n await this.render();\n this.emit('task:cancelled', { taskId, runner: this.runner });\n } else {\n this.showError(response.data.error || 'Failed to cancel task');\n }\n } catch (error) {\n console.error('Failed to cancel task:', error);\n this.showError('Failed to cancel task: ' + error.message);\n }\n }\n\n // Static method for easy Dialog integration\n static async show(runner, options = {}) {\n const view = new RunnerDetailsView({ runner });\n await view.onInit();\n\n const buttons = [];\n\n // Add action buttons based on runner status\n if (runner.status === 'active') {\n buttons.push({\n text: 'Pause Runner',\n class: 'btn-warning',\n action: async () => {\n if (confirm(`Are you sure you want to pause runner \"${runner.hostname}\"?`)) {\n try {\n const response = await view.getApp().rest.POST(`/api/runners/${runner.hostname}/pause`);\n if (response.success && response.data.status) {\n view.showSuccess('Runner paused successfully');\n return { action: 'paused', runner };\n } else {\n view.showError(response.data.error || 'Failed to pause runner');\n }\n } catch (error) {\n view.showError('Failed to pause runner: ' + error.message);\n }\n }\n return null;\n }\n });\n } else {\n buttons.push({\n text: 'Restart Runner',\n class: 'btn-success',\n action: async () => {\n if (confirm(`Are you sure you want to restart runner \"${runner.hostname}\"?`)) {\n try {\n const response = await view.getApp().rest.POST(`/api/runners/${runner.hostname}/restart`);\n if (response.success && response.data.status) {\n view.showSuccess('Runner restart initiated');\n return { action: 'restarted', runner };\n } else {\n view.showError(response.data.error || 'Failed to restart runner');\n }\n } catch (error) {\n view.showError('Failed to restart runner: ' + error.message);\n }\n }\n return null;\n }\n });\n }\n\n buttons.push({\n text: 'Configure',\n class: 'btn-outline-primary',\n action: () => {\n view.emit('runner:configure', { runner });\n return null;\n }\n });\n\n buttons.push({\n text: 'Remove Runner',\n class: 'btn-outline-danger',\n action: async () => {\n const confirmMessage = `Are you sure you want to remove runner \"${runner.hostname}\"? This action cannot be undone.`;\n if (confirm(confirmMessage)) {\n try {\n const response = await view.getApp().rest.DELETE(`/api/runners/${runner.hostname}`);\n if (response.success && response.data.status) {\n view.showSuccess('Runner removed successfully');\n return { action: 'removed', runner };\n } else {\n view.showError(response.data.error || 'Failed to remove runner');\n }\n } catch (error) {\n view.showError('Failed to remove runner: ' + error.message);\n }\n }\n return null;\n }\n });\n\n buttons.push({\n text: 'Export',\n class: 'btn-outline-secondary',\n action: () => {\n try {\n const exportData = {\n runner: runner,\n metrics: view.metrics,\n currentTasks: view.currentTasks,\n logs: view.logs,\n exported_at: new Date().toISOString(),\n exported_by: 'task-management-system'\n };\n\n const blob = new Blob([JSON.stringify(exportData, null, 2)], {\n type: 'application/json'\n });\n \n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `runner-${runner.hostname}-${Date.now()}.json`;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n\n view.showSuccess('Runner data exported successfully');\n return null;\n } catch (error) {\n view.showError('Failed to export runner data');\n return null;\n }\n }\n });\n\n buttons.push({\n text: 'Close',\n class: 'btn-secondary',\n dismiss: true\n });\n\n return await Dialog.showDialog({\n title: `<i class=\"bi bi-cpu me-2\"></i>Runner Details - ${runner.hostname}`,\n body: view,\n size: 'xl',\n scrollable: true,\n buttons: buttons,\n ...options\n });\n }\n}","/**\n * TaskManagementPage - Async task monitoring and management dashboard\n */\n\nimport Page from '@core/Page.js';\nimport View from '@core/View.js';\nimport Collection from '@core/Collection.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport TableView from '@core/views/table/TableView.js';\nimport { MetricsChart } from '@ext/charts/index.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport TaskDetailsView from './TaskDetailsView.js';\nimport RunnerDetailsView from './RunnerDetailsView.js';\n\n// Task Stats Header View\nclass TaskStatsView extends View {\n constructor(options = {}) {\n super({\n ...options,\n className: 'mojo-task-stats-section'\n });\n\n this.stats = {\n pending: 0,\n running: 0,\n completed: 0,\n errors: 0\n };\n }\n\n async getTemplate() {\n return `\n <div class=\"mojo-task-stats-header mb-4\">\n <div class=\"row\">\n <div class=\"col-xl-3 col-lg-6 col-12 mb-3\">\n <div class=\"card h-100 border-0 shadow-sm\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div>\n <h6 class=\"card-title text-muted mb-2\">Pending Tasks</h6>\n <h3 class=\"mb-1 fw-bold\">{{stats.pending}}</h3>\n <span class=\"badge bg-primary-subtle text-primary\">\n <i class=\"bi bi-clock\"></i> Queued\n </span>\n </div>\n <div class=\"text-primary\">\n <i class=\"bi bi-hourglass fs-2\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"col-xl-3 col-lg-6 col-12 mb-3\">\n <div class=\"card h-100 border-0 shadow-sm\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div>\n <h6 class=\"card-title text-muted mb-2\">Running Tasks</h6>\n <h3 class=\"mb-1 fw-bold\">{{stats.running}}</h3>\n <span class=\"badge bg-success-subtle text-success\">\n <i class=\"bi bi-play-circle\"></i> Active\n </span>\n </div>\n <div class=\"text-success\">\n <i class=\"bi bi-arrow-repeat fs-2\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"col-xl-3 col-lg-6 col-12 mb-3\">\n <div class=\"card h-100 border-0 shadow-sm\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div>\n <h6 class=\"card-title text-muted mb-2\">Completed Tasks</h6>\n <h3 class=\"mb-1 fw-bold\">{{stats.completed}}</h3>\n <span class=\"badge bg-info-subtle text-info\">\n <i class=\"bi bi-check-circle\"></i> Done\n </span>\n </div>\n <div class=\"text-info\">\n <i class=\"bi bi-check-square fs-2\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"col-xl-3 col-lg-6 col-12 mb-3\">\n <div class=\"card h-100 border-0 shadow-sm\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div>\n <h6 class=\"card-title text-muted mb-2\">Failed Tasks</h6>\n <h3 class=\"mb-1 fw-bold\">{{stats.errors}}</h3>\n <span class=\"badge bg-danger-subtle text-danger\">\n <i class=\"bi bi-exclamation-circle\"></i> Errors\n </span>\n </div>\n <div class=\"text-danger\">\n <i class=\"bi bi-x-octagon fs-2\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n `;\n }\n\n async loadStats() {\n try {\n const response = await this.getApp().rest.GET('/api/tasks/status');\n if (response.success && response.data.status) {\n this.stats = response.data.data;\n }\n } catch (error) {\n console.error('Failed to load task stats:', error);\n }\n }\n\n async onInit() {\n await this.loadStats();\n }\n}\n\n// Task Runners Status View\nclass TaskRunnersView extends View {\n constructor(options = {}) {\n super({\n ...options,\n className: 'mojo-task-runners-section'\n });\n\n this.runners = [];\n }\n\n async getTemplate() {\n return `\n <div class=\"card border shadow-sm mb-4\">\n <div class=\"card-header d-flex justify-content-between align-items-center\">\n <h5 class=\"card-title mb-0\">\n <i class=\"bi bi-cpu me-2\"></i>Task Runners\n </h5>\n <button class=\"btn btn-sm btn-outline-primary\" data-action=\"refresh-runners\">\n <i class=\"bi bi-arrow-clockwise\"></i> Refresh\n </button>\n </div>\n <div class=\"card-body\">\n {{#runners.length}}\n <div class=\"mojo-task-runner-list\">\n {{#runners}}\n <div class=\"mojo-task-runner-item p-3 mb-2 bg-light rounded\">\n <div class=\"row align-items-center\">\n <div class=\"col-md-8 col-lg-9\">\n <div class=\"d-flex align-items-center\">\n <div class=\"mojo-task-runner-status me-3\">\n <span class=\"badge {{statusBadge}}\">\n <i class=\"bi {{statusIcon}}\"></i> {{status}}\n </span>\n </div>\n <div class=\"mojo-task-runner-info\">\n <div class=\"mojo-task-runner-name\">\n <strong>{{hostname}}</strong>\n {{#max_workers}}<span class=\"text-muted ms-2\">• {{max_workers}} workers</span>{{/max_workers}}\n </div>\n <div class=\"mojo-task-runner-channels\">\n <small class=\"text-muted\">\n {{#channels.length}}Channels: {{#channels}}{{.}}{{^last}}, {{/last}}{{/channels}}{{/channels.length}}\n {{^channels.length}}No channels assigned{{/channels.length}}\n </small>\n </div>\n </div>\n </div>\n </div>\n <div class=\"col-md-4 col-lg-3\">\n <div class=\"mojo-task-runner-actions d-flex justify-content-end align-items-center\">\n <small class=\"text-muted me-2 d-none d-sm-inline\">{{pingAgeText}}</small>\n <div class=\"dropdown\">\n <button class=\"btn btn-sm btn-outline-secondary dropdown-toggle\"\n data-bs-toggle=\"dropdown\" aria-expanded=\"false\">\n <i class=\"bi bi-three-dots-vertical\"></i>\n </button>\n <ul class=\"dropdown-menu dropdown-menu-end\">\n <li><button class=\"dropdown-item\" data-action=\"view-runner-details\" data-runner-id=\"{{id}}\">\n <i class=\"bi bi-info-circle me-2\"></i>View Details\n </button></li>\n {{#isActive}}\n <li><button class=\"dropdown-item text-warning\" data-action=\"pause-runner\" data-runner-id=\"{{id}}\">\n <i class=\"bi bi-pause me-2\"></i>Pause Runner\n </button></li>\n {{/isActive}}\n {{^isActive}}\n <li><button class=\"dropdown-item text-success\" data-action=\"restart-runner\" data-runner-id=\"{{id}}\">\n <i class=\"bi bi-play me-2\"></i>Restart Runner\n </button></li>\n {{/isActive}}\n <li><hr class=\"dropdown-divider\"></li>\n <li><button class=\"dropdown-item text-danger\" data-action=\"remove-runner\" data-runner-id=\"{{id}}\">\n <i class=\"bi bi-trash me-2\"></i>Remove Runner\n </button></li>\n </ul>\n </div>\n </div>\n </div>\n </div>\n <div class=\"row mt-2 d-sm-none\">\n <div class=\"col-12\">\n <small class=\"text-muted\">Last ping: {{pingAgeText}}</small>\n </div>\n </div>\n </div>\n {{/runners}}\n </div>\n {{/runners.length}}\n {{^runners.length}}\n <div class=\"text-center text-muted py-4\">\n <i class=\"bi bi-cpu fs-1\"></i>\n <p class=\"mt-2\">No task runners found</p>\n </div>\n {{/runners.length}}\n </div>\n </div>\n `;\n }\n\n async loadRunners() {\n try {\n const response = await this.getApp().rest.GET('/api/tasks/runners');\n if (response.success && response.data.status) {\n this.runners = response.data.data.map(runner => {\n const isActive = runner.status === 'active';\n const pingAge = runner.ping_age || 0;\n\n return {\n ...runner,\n isActive,\n statusBadge: isActive ? 'bg-success' : 'bg-warning',\n statusIcon: isActive ? 'bi-check-circle-fill' : 'bi-exclamation-triangle-fill',\n pingAgeText: this.formatPingAge(pingAge)\n };\n });\n }\n } catch (error) {\n console.error('Failed to load runners:', error);\n }\n }\n\n formatPingAge(seconds) {\n if (seconds < 60) return `${Math.round(seconds)}s ago`;\n if (seconds < 3600) return `${Math.round(seconds / 60)}m ago`;\n return `${Math.round(seconds / 3600)}h ago`;\n }\n\n async onInit() {\n await this.loadRunners();\n }\n\n async onActionRefreshRunners(event, element) {\n await this.loadRunners();\n }\n\n async onActionViewRunnerDetails(event, element) {\n const runnerId = element.getAttribute('data-runner-id');\n const runner = this.runners.find(r => r.id === runnerId);\n\n if (runner) {\n const result = await RunnerDetailsView.show(runner);\n\n // Refresh runners list if any action was taken\n if (result?.action) {\n await this.loadRunners();\n this.emit('runner:' + result.action, result);\n }\n }\n }\n\n async onActionPauseRunner(event, element) {\n const runnerId = element.getAttribute('data-runner-id');\n const runner = this.runners.find(r => r.id === runnerId);\n\n if (!runner || !confirm(`Are you sure you want to pause runner \"${runner.hostname}\"?`)) {\n return;\n }\n\n try {\n element.disabled = true;\n const response = await this.getApp().rest.POST(`/api/runners/${runnerId}/pause`);\n\n if (response.success && response.data.status) {\n this.showSuccess('Runner paused successfully');\n await this.loadRunners();\n } else {\n this.showError(response.data.error || 'Failed to pause runner');\n }\n } catch (error) {\n console.error('Failed to pause runner:', error);\n this.showError('Failed to pause runner: ' + error.message);\n } finally {\n element.disabled = false;\n }\n }\n\n async onActionRestartRunner(event, element) {\n const runnerId = element.getAttribute('data-runner-id');\n const runner = this.runners.find(r => r.id === runnerId);\n\n if (!runner || !confirm(`Are you sure you want to restart runner \"${runner.hostname}\"?`)) {\n return;\n }\n\n try {\n element.disabled = true;\n const response = await this.getApp().rest.POST(`/api/runners/${runnerId}/restart`);\n\n if (response.success && response.data.status) {\n this.showSuccess('Runner restart initiated');\n await this.loadRunners();\n } else {\n this.showError(response.data.error || 'Failed to restart runner');\n }\n } catch (error) {\n console.error('Failed to restart runner:', error);\n this.showError('Failed to restart runner: ' + error.message);\n } finally {\n element.disabled = false;\n }\n }\n\n async onActionRemoveRunner(event, element) {\n const runnerId = element.getAttribute('data-runner-id');\n const runner = this.runners.find(r => r.id === runnerId);\n\n if (!runner) return;\n\n const confirmMessage = `Are you sure you want to remove runner \"${runner.hostname}\"? This action cannot be undone.`;\n if (!confirm(confirmMessage)) {\n return;\n }\n\n try {\n element.disabled = true;\n const response = await this.getApp().rest.DELETE(`/api/runners/${runnerId}`);\n\n if (response.success && response.data.status) {\n this.showSuccess('Runner removed successfully');\n await this.loadRunners();\n } else {\n this.showError(response.data.error || 'Failed to remove runner');\n }\n } catch (error) {\n console.error('Failed to remove runner:', error);\n this.showError('Failed to remove runner: ' + error.message);\n } finally {\n element.disabled = false;\n }\n }\n}\n\n// Task Charts View\nclass TaskChartsView extends View {\n constructor(options = {}) {\n super({\n ...options,\n className: 'mojo-task-charts-section'\n });\n }\n\n async getTemplate() {\n return `\n <div class=\"row mb-4\">\n <div class=\"col-xl-6 col-lg-12 mb-4\">\n <div class=\"card border shadow-sm\">\n <div class=\"card-body\" style=\"min-height: 300px;\">\n <div data-container=\"task-flow-chart\"></div>\n </div>\n </div>\n </div>\n <div class=\"col-xl-6 col-lg-12 mb-4\">\n <div class=\"card border shadow-sm\">\n <div class=\"card-body\" style=\"min-height: 300px;\">\n <div data-container=\"task-errors-chart\"></div>\n </div>\n </div>\n </div>\n </div>\n `;\n }\n\n async onInit() {\n // Task Flow Chart (Published vs Completed)\n this.taskFlowChart = new MetricsChart({\n title: '<i class=\"bi bi-graph-up me-2\"></i>Task Flow',\n endpoint: '/api/metrics/fetch',\n height: 280,\n granularity: 'hours',\n slugs: ['tasks_pub', 'tasks_completed'],\n account: 'global',\n chartType: 'line',\n showDateRange: false,\n colors: [\n 'rgba(13, 110, 253, 0.8)', // Primary blue for published\n 'rgba(25, 135, 84, 0.8)' // Success green for completed\n ],\n yAxis: {\n label: 'Count',\n beginAtZero: true\n },\n tooltip: {\n y: 'number'\n },\n containerId: 'task-flow-chart'\n });\n this.addChild(this.taskFlowChart);\n\n // Task Errors Chart\n this.taskErrorsChart = new MetricsChart({\n title: '<i class=\"bi bi-exclamation-triangle me-2\"></i>Task Issues',\n endpoint: '/api/metrics/fetch',\n height: 280,\n granularity: 'hours',\n slugs: ['tasks_errors', 'tasks_expired'],\n account: 'global',\n chartType: 'line',\n showDateRange: false,\n colors: [\n 'rgba(220, 53, 69, 0.8)', // Danger red for errors\n 'rgba(255, 193, 7, 0.8)' // Warning yellow for expired\n ],\n yAxis: {\n label: 'Count',\n beginAtZero: true\n },\n tooltip: {\n y: 'number'\n },\n containerId: 'task-errors-chart'\n });\n this.addChild(this.taskErrorsChart);\n }\n}\n\n// Task Table Views\nclass BaseTaskTable extends TableView {\n constructor(options = {}) {\n super({\n showPagination: true,\n showSearch: true,\n showRefresh: true,\n pageSize: 10,\n columns: [\n { key: 'id', label: 'Task ID', sortable: true },\n { key: 'function', label: 'Function', sortable: true },\n { key: 'channel', label: 'Channel', sortable: true },\n { key: 'created', label: 'Created', sortable: true, formatter: 'datetime' },\n { key: 'status', label: 'Status', formatter: 'badge' }\n ],\n ...options\n });\n }\n\n buildActionCell(item) {\n const actions = [];\n\n if (item.status === 'pending') {\n actions.push(`\n <button class=\"btn btn-sm btn-outline-danger\" data-action=\"cancel-task\" data-task-id=\"${item.id}\" title=\"Cancel Task\">\n <i class=\"bi bi-x-circle\"></i>\n </button>\n `);\n }\n\n if (item.status === 'error') {\n actions.push(`\n <button class=\"btn btn-sm btn-outline-primary\" data-action=\"retry-task\" data-task-id=\"${item.id}\" title=\"Retry Task\">\n <i class=\"bi bi-arrow-clockwise\"></i>\n </button>\n `);\n }\n\n actions.push(`\n <button class=\"btn btn-sm btn-outline-info\" data-action=\"view-task-details\" data-task-id=\"${item.id}\" title=\"View Details\">\n <i class=\"bi bi-info-circle\"></i>\n </button>\n `);\n\n return actions.join(' ');\n }\n\n async onActionCancelTask(action, event, element) {\n const taskId = element.getAttribute('data-task-id');\n\n if (!confirm('Are you sure you want to cancel this task?')) {\n return;\n }\n\n try {\n element.disabled = true;\n const response = await this.getApp().rest.POST(`/api/tasks/${taskId}/cancel`);\n\n if (response.success && response.data.status) {\n this.showSuccess('Task cancelled successfully');\n // Refresh the active table\n const activeTab = this.getParentPage()?.taskTablesView?.getActiveTab();\n if (activeTab) {\n const activeTable = this.getParentPage()?.taskTablesView?.getTab(activeTab);\n if (activeTable?.refresh) {\n await activeTable.refresh();\n }\n }\n } else {\n this.showError(response.data.error || 'Failed to cancel task');\n }\n } catch (error) {\n console.error('Failed to cancel task:', error);\n this.showError('Failed to cancel task: ' + error.message);\n } finally {\n element.disabled = false;\n }\n }\n\n async onActionRetryTask(action, event, element) {\n const taskId = element.getAttribute('data-task-id');\n\n try {\n element.disabled = true;\n const response = await this.getApp().rest.POST(`/api/tasks/${taskId}/retry`);\n\n if (response.success && response.data.status) {\n this.showSuccess('Task queued for retry');\n // Refresh the active table\n const activeTab = this.getParentPage()?.taskTablesView?.getActiveTab();\n if (activeTab) {\n const activeTable = this.getParentPage()?.taskTablesView?.getTab(activeTab);\n if (activeTable?.refresh) {\n await activeTable.refresh();\n }\n }\n } else {\n this.showError(response.data.error || 'Failed to retry task');\n }\n } catch (error) {\n console.error('Failed to retry task:', error);\n this.showError('Failed to retry task: ' + error.message);\n } finally {\n element.disabled = false;\n }\n }\n\n async onActionViewTaskDetails(action, event, element) {\n const taskId = element.getAttribute('data-task-id');\n\n try {\n // Fetch detailed task data\n const response = await this.getApp().rest.GET(`/api/tasks/${taskId}/details`);\n\n if (response.success && response.data.status) {\n const task = response.data.data;\n const result = await TaskDetailsView.show(task);\n\n // Refresh tables if any action was taken\n if (result?.action) {\n await this.refreshActiveTables();\n this.emit('task:' + result.action, result);\n }\n } else {\n this.showError(response.data.error || 'Failed to load task details');\n }\n } catch (error) {\n console.error('Failed to load task details:', error);\n this.showError('Failed to load task details: ' + error.message);\n }\n }\n\n getParentPage() {\n // Helper to get the parent TaskManagementPage\n let parent = this.parent;\n while (parent && parent.constructor.name !== 'TaskManagementPage') {\n parent = parent.parent;\n }\n return parent;\n }\n\n async refreshActiveTables() {\n const parentPage = this.getParentPage();\n if (parentPage?.taskTablesView) {\n const activeTab = parentPage.taskTablesView.getActiveTab();\n if (activeTab) {\n const activeTable = parentPage.taskTablesView.getTab(activeTab);\n if (activeTable?.refresh) {\n await activeTable.refresh();\n }\n }\n }\n }\n}\n\nclass PendingTasksTable extends TableView {\n constructor(options = {}) {\n super({\n ...options,\n title: 'Pending Tasks',\n collection: new Collection({\n endpoint: '/api/tasks/pending'\n }),\n columns: [\n { key: 'id', label: 'Task ID', sortable: true },\n { key: 'function', label: 'Function', sortable: true },\n { key: 'channel', label: 'Channel', sortable: true },\n { key: 'created', label: 'Created', sortable: true, formatter: 'datetime' },\n { key: 'status', label: 'Status', formatter: 'badge' }\n ],\n });\n }\n}\n\nclass RunningTasksTable extends TableView {\n constructor(options = {}) {\n super({\n ...options,\n title: 'Running Tasks',\n collection: new Collection({\n endpoint: '/api/tasks/running'\n }),\n columns: [\n { key: 'id', label: 'Task ID', sortable: true },\n { key: 'function', label: 'Function', sortable: true },\n { key: 'channel', label: 'Channel', sortable: true },\n { key: 'created', label: 'Created', sortable: true, formatter: 'datetime' },\n { key: 'status', label: 'Status', formatter: 'badge' }\n ],\n });\n }\n}\n\nclass CompletedTasksTable extends TableView {\n constructor(options = {}) {\n super({\n ...options,\n title: 'Completed Tasks',\n collection: new Collection({\n endpoint: '/api/tasks/completed'\n }),\n columns: [\n { key: 'id', label: 'Task ID', sortable: true },\n { key: 'function', label: 'Function', sortable: true },\n { key: 'channel', label: 'Channel', sortable: true },\n { key: 'created', label: 'Created', sortable: true, formatter: 'datetime' },\n { key: 'completed_at', label: 'Completed', sortable: true, formatter: 'datetime' },\n { key: 'status', label: 'Status', formatter: 'badge' }\n ]\n });\n }\n}\n\nclass ErrorTasksTable extends TableView {\n constructor(options = {}) {\n super({\n ...options,\n title: 'Failed Tasks',\n collection: new Collection({\n endpoint: '/api/tasks/errors'\n }),\n columns: [\n { key: 'id', label: 'Task ID', sortable: true },\n { key: 'function', label: 'Function', sortable: true },\n { key: 'channel', label: 'Channel', sortable: true },\n { key: 'created', label: 'Created', sortable: true, formatter: 'datetime' },\n { key: 'error', label: 'Error', sortable: false },\n { key: 'status', label: 'Status', formatter: 'badge' }\n ]\n });\n }\n}\n\n// Main Task Management Page\nexport default class TaskManagementPage extends Page {\n constructor(options = {}) {\n super({\n ...options,\n title: 'Task Management',\n className: 'mojo-task-management-page'\n });\n\n this.pageTitle = 'Task Management';\n this.pageSubtitle = 'Async task monitoring and runner management';\n this.lastUpdated = new Date().toLocaleString();\n }\n\n async getTemplate() {\n return `\n <div class=\"mojo-task-management-container\">\n <!-- Page Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-4\">\n <div>\n <h1 class=\"h3 mb-1\">{{pageTitle}}</h1>\n <p class=\"text-muted mb-0\">{{pageSubtitle}}</p>\n <small class=\"text-info\">\n <i class=\"bi bi-cpu me-1\"></i>\n Real-time task processing and runner monitoring\n </small>\n </div>\n <div class=\"btn-group\" role=\"group\">\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm\"\n data-action=\"refresh-all\" title=\"Refresh All Data\">\n <i class=\"bi bi-arrow-clockwise\"></i> Refresh\n </button>\n <button type=\"button\" class=\"btn btn-outline-primary btn-sm\"\n data-action=\"export-tasks\" title=\"Export Task Data\">\n <i class=\"bi bi-download\"></i> Export\n </button>\n <button type=\"button\" class=\"btn btn-outline-success btn-sm\"\n data-action=\"manage-channels\" title=\"Manage Channels\">\n <i class=\"bi bi-collection\"></i> Channels\n </button>\n </div>\n </div>\n\n <!-- Task Stats -->\n <div data-container=\"task-stats\"></div>\n\n <!-- Task Runners -->\n <div data-container=\"task-runners\"></div>\n\n <!-- Task Charts -->\n <div data-container=\"task-charts\"></div>\n\n <!-- Task Tables -->\n <div class=\"card border shadow-sm\">\n <div class=\"card-header\">\n <h5 class=\"card-title mb-0\">\n <i class=\"bi bi-list-task me-2\"></i>Task Management\n </h5>\n </div>\n <div class=\"card-body\">\n <div data-container=\"task-tables\"></div>\n </div>\n </div>\n\n <!-- System Status Footer -->\n <div class=\"row mt-4\">\n <div class=\"col-12\">\n <div class=\"alert alert-info border-0\" role=\"alert\">\n <div class=\"d-flex align-items-center\">\n <i class=\"bi bi-info-circle-fill me-2\"></i>\n <div>\n <strong>Task System Status:</strong> Monitoring active.\n Last updated: <span class=\"text-muted\">{{lastUpdated}}</span>\n </div>\n <div class=\"ms-auto\">\n <button class=\"btn btn-sm btn-outline-info\" data-action=\"view-system-logs\">\n <i class=\"bi bi-journal-text\"></i> View Logs\n </button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n `;\n }\n\n async onInit() {\n // Create Task Stats View\n this.taskStatsView = new TaskStatsView({\n containerId: 'task-stats'\n });\n this.addChild(this.taskStatsView);\n\n // Create Task Runners View\n this.taskRunnersView = new TaskRunnersView({\n containerId: 'task-runners'\n });\n this.addChild(this.taskRunnersView);\n\n // Create Task Charts View\n this.taskChartsView = new TaskChartsView({\n containerId: 'task-charts'\n });\n this.addChild(this.taskChartsView);\n\n // Create Task Tables with TabView\n this.taskTablesView = new TabView({\n containerId: 'task-tables',\n tabs: {\n 'Pending': new PendingTasksTable(),\n 'Running': new RunningTasksTable(),\n 'Completed': new CompletedTasksTable(),\n 'Errors': new ErrorTasksTable()\n },\n activeTab: 'Pending'\n });\n this.addChild(this.taskTablesView);\n }\n\n async onActionRefreshAll(action, event, element) {\n try {\n // Show loading state\n const icon = element.querySelector('i');\n icon?.classList.add('bi-spin');\n element.disabled = true;\n\n // Refresh all components\n const promises = [\n this.taskStatsView?.loadStats(),\n this.taskRunnersView?.loadRunners(),\n this.taskChartsView?.taskFlowChart?.refresh(),\n this.taskChartsView?.taskErrorsChart?.refresh()\n ].filter(Boolean);\n\n // Refresh active table\n const activeTab = this.taskTablesView?.getActiveTab();\n if (activeTab) {\n const activeTable = this.taskTablesView.getTab(activeTab);\n if (activeTable?.refresh) {\n promises.push(activeTable.refresh());\n }\n }\n\n await Promise.allSettled(promises);\n\n // Update timestamp\n this.lastUpdated = new Date().toLocaleString();\n\n // Emit refresh event\n const eventBus = this.getApp()?.events;\n if (eventBus) {\n eventBus.emit('tasks:dashboard-refreshed', {\n page: this,\n timestamp: this.lastUpdated\n });\n }\n\n } catch (error) {\n console.error('Failed to refresh task dashboard:', error);\n } finally {\n // Reset button state\n const icon = element.querySelector('i');\n icon?.classList.remove('bi-spin');\n element.disabled = false;\n }\n }\n\n async onActionExportTasks(action, event, element) {\n try {\n // Export charts\n await this.taskChartsView?.taskFlowChart?.export('png');\n await this.taskChartsView?.taskErrorsChart?.export('png');\n\n // Export active table\n const activeTab = this.taskTablesView?.getActiveTab();\n if (activeTab) {\n const activeTable = this.taskTablesView.getTab(activeTab);\n if (activeTable?.exportToCSV) {\n activeTable.exportToCSV();\n }\n }\n\n } catch (error) {\n console.error('Failed to export task data:', error);\n }\n }\n\n async onActionManageChannels(event, element) {\n try {\n // For now, show a simple info modal with channel information\n const response = await this.getApp().rest.GET('/api/tasks/channels');\n\n if (response.success && response.data.status) {\n const channels = response.data.data;\n\n // Create a simple info alert\n const channelList = channels.map(channel =>\n `${channel.name} (${channel.pending} pending, ${channel.running} running)`\n ).join('\\n');\n\n alert(`Task Channels:\\n\\n${channelList}\\n\\nFull channel management interface coming soon!`);\n } else {\n this.showError('Failed to load channel information');\n }\n } catch (error) {\n console.error('Failed to load channels:', error);\n // Navigate to dedicated channels page as fallback\n const router = this.getApp()?.router;\n if (router) {\n router.navigateTo('/admin/task-channels');\n }\n }\n }\n\n async onActionViewSystemLogs(event, element) {\n try {\n // Try to get recent system logs related to tasks\n const response = await this.getApp().rest.GET('/api/tasks/logs?limit=50');\n\n if (response.success && response.data.status) {\n const logs = response.data.data;\n\n // Create a simple log preview\n const logPreview = logs.slice(0, 10).map(log =>\n `[${new Date(log.timestamp * 1000).toLocaleString()}] ${log.level.toUpperCase()}: ${log.message}`\n ).join('\\n');\n\n const viewFullLogs = confirm(`Recent Task System Logs:\\n\\n${logPreview}\\n\\n... and ${logs.length - 10} more entries.\\n\\nView full logs?`);\n\n if (viewFullLogs) {\n const router = this.getApp()?.router;\n if (router) {\n router.navigateTo('/admin/logs?filter=tasks');\n }\n }\n } else {\n // Navigate to system logs as fallback\n const router = this.getApp()?.router;\n if (router) {\n router.navigateTo('/admin/logs');\n }\n }\n } catch (error) {\n console.error('Failed to load system logs:', error);\n // Navigate to system logs as fallback\n const router = this.getApp()?.router;\n if (router) {\n router.navigateTo('/admin/logs');\n }\n }\n }\n\n // Public API\n async refreshDashboard() {\n return this.onActionRefreshAll(null, null, { disabled: false, querySelector: () => null });\n }\n\n getStats() {\n return this.taskStatsView?.stats || {};\n }\n\n getRunners() {\n return this.taskRunnersView?.runners || [];\n }\n\n getCharts() {\n return {\n taskFlow: this.taskChartsView?.taskFlowChart,\n taskErrors: this.taskChartsView?.taskErrorsChart\n };\n }\n\n\n}\n","/**\n * LogView - Detailed view for a Log record\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport DataView from '@core/views/data/DataView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport { Log } from '@core/models/Log.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport DeviceView from '../account/devices/DeviceView.js';\nimport GeoIPView from '../account/devices/GeoIPView.js';\n\nclass LogView extends View {\n constructor(options = {}) {\n super({\n className: 'log-view',\n ...options\n });\n\n this.model = options.model || new Log(options.data || {});\n this.logIcon = this.getIconForLog(this.model.get('level'));\n\n this.template = `\n <div class=\"log-view-container\">\n <!-- Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-4\">\n <div class=\"d-flex align-items-center gap-3\">\n <div class=\"fs-1 {{logIcon.color}}\">\n <i class=\"bi {{logIcon.icon}}\"></i>\n </div>\n <div>\n <h4 class=\"mb-1\">\n <span class=\"badge bg-secondary\">{{model.method}}</span> {{model.path}}\n </h4>\n <div class=\"text-muted small\">\n {{model.created|datetime}}\n </div>\n </div>\n </div>\n <div data-container=\"log-context-menu\"></div>\n </div>\n\n <!-- Tabs -->\n <div data-container=\"log-tabs\"></div>\n </div>\n `;\n }\n\n getIconForLog(level) {\n const lvl = level?.toLowerCase();\n if (lvl === 'error' || lvl === 'critical') return { icon: 'bi-x-octagon-fill', color: 'text-danger' };\n if (lvl === 'warning') return { icon: 'bi-exclamation-triangle-fill', color: 'text-warning' };\n if (lvl === 'info') return { icon: 'bi-info-circle-fill', color: 'text-info' };\n return { icon: 'bi-journal-text', color: 'text-secondary' };\n }\n\n async onInit() {\n // Overview Tab\n this.overviewView = new DataView({\n model: this.model,\n className: \"p-3\",\n columns: 2,\n fields: [\n { name: 'id', label: 'Log ID' },\n { name: 'level', label: 'Level', format: 'badge' },\n { name: 'kind', label: 'Kind' },\n { name: 'ip', label: 'IP Address', template: '<a href=\"#\" data-action=\"view-ip\">{{model.ip}}</a>' },\n { name: 'uid', label: 'User ID' },\n { name: 'username', label: 'Username' },\n { name: 'duid', label: 'Device ID', template: '<a href=\"#\" data-action=\"view-device\">{{model.duid|truncate_middle(32)}}</a>' },\n { name: 'model_name', label: 'Related Model' },\n { name: 'model_id', label: 'Related Model ID' },\n ]\n });\n\n // Log Content Tab\n const logContent = this.model.get('log');\n let formattedLog = logContent;\n try {\n // Attempt to parse and prettify if it's a JSON string\n const parsed = JSON.parse(logContent);\n formattedLog = JSON.stringify(parsed, null, 2);\n } catch (e) {\n // Not a valid JSON string, display as is\n }\n\n this.logContentView = new View({\n template: `\n <div class=\"position-relative\">\n <button class=\"btn btn-sm btn-outline-secondary position-absolute top-0 end-0 mt-2 me-2\" data-action=\"copy-log\">\n <i class=\"bi bi-clipboard\"></i> Copy\n </button>\n <pre class=\"bg-light p-3 border rounded\" style=\"max-height: 600px; overflow-y: auto;\"><code>${formattedLog}</code></pre>\n </div>\n `,\n onActionCopyLog: () => {\n navigator.clipboard.writeText(formattedLog);\n this.getApp()?.toast?.success('Log content copied to clipboard.');\n }\n });\n\n // TabView\n this.tabView = new TabView({\n containerId: 'log-tabs',\n tabs: {\n 'Overview': this.overviewView,\n 'Log Content': this.logContentView,\n },\n activeTab: 'Overview'\n });\n this.addChild(this.tabView);\n\n // ContextMenu\n const logMenu = new ContextMenu({\n containerId: 'log-context-menu',\n className: \"context-menu-view header-menu-absolute\",\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: [\n { label: 'View User', action: 'view-user', icon: 'bi-person', disabled: !this.model.get('uid') },\n { label: 'View Device', action: 'view-device', icon: 'bi-phone', disabled: !this.model.get('duid') },\n { type: 'divider' },\n { label: 'Delete Log', action: 'delete-log', icon: 'bi-trash', danger: true }\n ]\n }\n });\n this.addChild(logMenu);\n }\n\n async onActionViewIp(event) {\n event.preventDefault();\n const ip = this.model.get('ip');\n if (ip) {\n GeoIPView.show(ip);\n }\n }\n\n async onActionViewDevice(event) {\n event.preventDefault();\n const duid = this.model.get('duid');\n if (duid) {\n DeviceView.show(duid);\n }\n }\n\n async onActionViewUser() {\n console.log(\"TODO: View user\", this.model.get('uid'));\n }\n\n async onActionDeleteLog() {\n const confirmed = await Dialog.confirm(\n `Are you sure you want to delete this log entry? This action cannot be undone.`,\n 'Confirm Deletion',\n { confirmClass: 'btn-danger', confirmText: 'Delete' }\n );\n if (confirmed) {\n const resp = await this.model.destroy();\n if (resp.success) {\n this.emit('log:deleted', { model: this.model });\n }\n }\n }\n}\n\nLog.VIEW_CLASS = LogView;\n\nexport default LogView;\n","/**\n * LogTablePage - Log management using TablePage component\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { LogList } from '@core/models/Log.js';\nimport LogView from './LogView.js';\n\nclass LogTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_logs',\n pageName: 'Manage Logs',\n router: \"admin/logs\",\n Collection: LogList,\n\n itemViewClass: LogView,\n viewDialogOptions: {\n header: false,\n size: 'xl'\n },\n\n // Column definitions\n columns: [\n {\n key: 'created|epoch|datetime',\n label: 'Timestamp',\n sortable: true,\n filter: {\n type: 'daterange',\n }\n },\n {\n key: 'level',\n label: 'Level',\n sortable: true,\n formatter: 'badge',\n filter: {\n type: 'select',\n options: [\n { value: 'info', label: 'Info' },\n { value: 'warning', label: 'Warning' },\n { value: 'error', label: 'Error' }\n ]\n }\n },\n {\n key: 'kind',\n label: 'Kind',\n filter: {\n type: \"text\"\n }\n },\n {\n key: 'method',\n label: 'Method',\n filter: {\n type: \"text\"\n }\n },\n {\n key: 'path',\n label: 'Path',\n filter: {\n type: \"text\"\n }\n },\n {\n key: 'username',\n label: 'User',\n filter: {\n type: \"text\"\n }\n },\n {\n key: 'ip',\n label: 'IP',\n filter: {\n type: \"text\"\n }\n },\n {\n key: 'duid',\n label: 'Browser ID',\n formatter: 'truncate_middle(16)',\n filter: {\n type: \"text\"\n }\n }\n ],\n\n // Default sort by timestamp descending (newest first)\n defaultQuery: {\n sort: '-created'\n },\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: false, // Logs are typically not manually created\n showExport: true,\n\n // Empty state\n emptyMessage: 'No log entries found.',\n\n // Batch actions\n batchBarLocation: 'top',\n batchActions: [\n { label: \"Export\", icon: \"bi bi-download\", action: \"batch-export\" },\n { label: \"Archive\", icon: \"bi bi-archive\", action: \"batch-archive\" },\n { label: \"Delete\", icon: \"bi bi-trash\", action: \"batch-delete\" },\n { label: \"Mark as Reviewed\", icon: \"bi bi-check2\", action: \"batch-reviewed\" }\n ],\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n}\n\nexport default LogTablePage;\n","import View from '@core/View.js';\nimport DataView from '@core/views/data/DataView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport { MetricsPermission, MetricsForms } from '@core/models/Metrics.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\n\nclass MetricsPermissionsView extends View {\n constructor(options = {}) {\n super({\n className: 'metrics-permissions-view',\n ...options\n });\n\n this.model = options.model || new MetricsPermission(options.data || {});\n\n this.template = `\n <div class=\"container p-3\">\n <div class=\"d-flex justify-content-between align-items-center mb-4\">\n <div>\n <h3 class=\"mb-1\">Permissions for {{model.account}}</h3>\n </div>\n <div data-container=\"context-menu\"></div>\n </div>\n <div data-container=\"data-view\"></div>\n </div>\n `;\n }\n\n async onInit() {\n this.dataView = new DataView({\n containerId: 'data-view',\n model: this.model,\n fields: [\n { name: 'view_permissions', label: 'View Permissions', format: 'list|badge' },\n { name: 'write_permissions', label: 'Write Permissions', format: 'list|badge' },\n ]\n });\n this.addChild(this.dataView);\n\n const contextMenu = new ContextMenu({\n containerId: 'context-menu',\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: [\n { label: 'Edit', action: 'edit', icon: 'bi-pencil' },\n { label: 'Delete', action: 'delete', icon: 'bi-trash', danger: true },\n ]\n }\n });\n this.addChild(contextMenu);\n }\n\n async onActionEdit() {\n const resp = await Dialog.showModelForm({\n title: `Edit Permissions for ${this.model.get('account')}`,\n model: this.model,\n formConfig: MetricsForms.edit\n });\n if (resp) {\n this.model.set(resp.data.data);\n this.render();\n }\n }\n\n async onActionDelete() {\n const confirmed = await Dialog.confirm(`Are you sure you want to delete all permissions for ${this.model.get('account')}?`);\n if (confirmed) {\n await this.model.destroy();\n this.emit('deleted', this.model);\n }\n }\n}\n\nMetricsPermission.VIEW_CLASS = MetricsPermissionsView;\n\nexport default MetricsPermissionsView;\n","import TablePage from '@core/pages/TablePage.js';\nimport { MetricsPermissionList, MetricsForms } from '@core/models/Metrics.js';\nimport MetricsPermissionsView from './MetricsPermissionsView.js';\n\nclass MetricsPermissionsTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_metrics_permissions',\n pageName: 'Metrics Permissions',\n router: \"admin/metrics/permissions\",\n Collection: MetricsPermissionList,\n formEdit: MetricsForms.edit,\n itemViewClass: MetricsPermissionsView,\n viewDialogOptions: {\n header: false,\n size: 'lg'\n },\n\n columns: [\n { key: 'account', label: 'Account', sortable: true },\n { key: 'view_permissions', label: 'View Permissions', formatter: 'list|badge' },\n { key: 'write_permissions', label: 'Write Permissions', formatter: 'list|badge' },\n ],\n\n selectable: true,\n searchable: true,\n sortable: true,\n paginated: true,\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n tableOptions: {\n pageSizes: [10, 25, 50],\n defaultPageSize: 25,\n emptyMessage: 'No metrics permissions found.',\n emptyIcon: 'bi-bar-chart-line',\n actions: [\"view\", \"edit\", \"delete\"],\n }\n });\n }\n}\n\nexport default MetricsPermissionsTablePage;\n","/**\n * FileManagerTablePage - File Manager backend management using TablePage component\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { FileManagerList, FileManagerForms } from '@core/models/Files.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\n\nclass FileManagerTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_file_managers',\n pageName: 'Manage Storage Backends',\n router: \"admin/file-managers\",\n Collection: FileManagerList,\n\n formCreate: FileManagerForms.create,\n formEdit: FileManagerForms.edit,\n\n // Column definitions\n columns: [\n {\n key: 'id',\n label: 'ID',\n sortable: true,\n class: 'text-muted'\n },\n {\n key: 'name',\n label: 'Name',\n formatter: \"default('Unnamed Backend')\"\n },\n {\n key: 'backend_url',\n label: 'Backend URL',\n sortable: true\n },\n {\n key: 'is_default',\n label: 'Default',\n formatter: \"boolean|badge\"\n },\n {\n key: 'is_active',\n label: 'Active',\n formatter: \"boolean|badge\"\n },\n {\n key: 'backend_type',\n label: 'Type',\n formatter: \"default('Unknown')\"\n },\n {\n key: 'created',\n label: 'Created',\n formatter: \"epoch|datetime\"\n }\n ],\n\n contextMenu: [\n { icon: 'bi-pencil', action: 'edit', label: 'Edit Name' },\n { icon: 'bi-shield', action: 'edit-credentials', label: 'Edit Credentials' },\n { icon: 'bi-person', action: 'edit-owners', label: 'Edit Owners' },\n { divider: true },\n { icon: 'bi-copy', action: 'clone', label: 'Clone Manager' },\n { divider: true },\n { icon: 'bi-check', action: 'test-connection', label: 'Test Connection' },\n { icon: 'bi-question-circle', action: 'check-cors', label: 'Check CORS' },\n { icon: 'bi-wrench', action: 'fix-cors', label: 'Fix CORS' },\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No storage backends found. Click \"Add Storage Backend\" to configure your first backend.',\n\n // Batch actions\n batchBarLocation: 'top',\n batchActions: [\n { label: \"Delete\", icon: \"bi bi-trash\", action: \"batch-delete\" },\n { label: \"Export\", icon: \"bi bi-download\", action: \"batch-export\" },\n { label: \"Activate\", icon: \"bi bi-check-circle\", action: \"batch-activate\" },\n { label: \"Deactivate\", icon: \"bi bi-x-circle\", action: \"batch-deactivate\" }\n ],\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n\n async onActionEditOwners(event, element) {\n const item = this.collection.get(element.dataset.id);\n const result = await Dialog.showModelForm({\n title: 'Edit Owners',\n model: item,\n fields: FileManagerForms.owners.fields\n });\n if (!result) return true;\n if (result.success) {\n this.getApp().toast.success(\"Owners Updated successfully\");\n } else {\n this.getApp().toast.error(\"Owners update failed\");\n }\n }\n\n async onActionCheckCors(event, element) {\n const item = this.collection.get(element.dataset.id);\n const result = await item.save({check_cors: true});\n if (result.success && result.data.status) {\n // this.getApp().toast.success(result.data.result);\n await Dialog.showData({\n title: `Audit Report - ${item._.name}`,\n data: result.data,\n size: 'lg'\n });\n } else {\n this.getApp().toast.error(\"Connection test failed\");\n }\n return true;\n }\n\n async onActionTestConnection(event, element) {\n const item = this.collection.get(element.dataset.id);\n const result = await item.save({test_connection: true});\n if (result.success && result.data.status) {\n this.getApp().toast.success(\"Connection test successful\");\n } else {\n this.getApp().toast.error(\"Connection test failed\");\n }\n return true;\n }\n\n async onActionEditCredentials(event, element) {\n const item = this.collection.get(element.dataset.id);\n const result = await Dialog.showModelForm({\n title: 'Edit Credentials',\n model: item,\n fields: FileManagerForms.credentials.fields\n });\n\n if (!result) return true;\n\n if (result.success && result.data.status) {\n this.getApp().toast.success(\"Credentials updated successfully\");\n } else {\n this.getApp().toast.error(\"Failed to update credentials\");\n }\n return true;\n }\n\n async onActionClone(event, element) {\n\n const confirmed = await Dialog.showConfirm({\n title: 'Clone File Manager',\n message: 'This will create a clone with the same credentials.',\n });\n\n if (!confirmed) {\n return true;\n }\n\n const item = this.collection.get(element.dataset.id);\n const result = await item.save({clone: true});\n if (result.success && result.data.status) {\n this.getApp().toast.success(\"Connection cloned successfully\");\n this.collection.fetch();\n } else {\n this.getApp().toast.error(\"Failed to clone connection\");\n }\n return true;\n }\n}\n\nexport default FileManagerTablePage;\n","/**\n * FileView - Comprehensive file management interface\n *\n * Features:\n * - Clean header with file thumbnail/icon, name, size, and status\n * - Tabbed interface for Info (metadata) and Renditions\n * - Integrated with DataView, Table, and ContextMenu components\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport DataView from '@core/views/data/DataView.js';\nimport TableView from '@core/views/table/TableView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport Collection from '@core/Collection.js';\nimport { File, FileForms } from '@core/models/Files.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport LightboxGallery from '@ext/lightbox/LightboxGallery.js';\nimport PDFViewer from '@ext/lightbox/PDFViewer.js';\n\nclass FileView extends View {\n constructor(options = {}) {\n super({\n className: 'file-view',\n ...options\n });\n\n this.model = options.model || new File(options.data || {});\n this.isImage = this.model.get('category') === 'image';\n\n // Prepare renditions for the table by converting the renditions object to an array\n const renditionsData = this.model.get('renditions') || {};\n this.renditionsCollection = new Collection(Object.values(renditionsData));\n\n this.template = `\n <div class=\"file-view-container\">\n <!-- Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-4\">\n <!-- Left Side: Thumbnail & Info -->\n <div class=\"d-flex align-items-center gap-3\">\n <div class=\"file-thumbnail\" style=\"width: 80px; height: 80px;\">\n {{#isImage}}\n <a href=\"{{model.url}}\" target=\"_blank\" title=\"View original file\">\n <img src=\"{{model.renditions.thumbnail.url|default(model.url)}}\" class=\"img-fluid rounded\" style=\"width: 80px; height: 80px; object-fit: cover;\">\n </a>\n {{/isImage}}\n {{^isImage}}\n <div class=\"avatar-placeholder rounded bg-light d-flex align-items-center justify-content-center\" style=\"width: 80px; height: 80px;\">\n <i class=\"bi bi-file-earmark-text text-secondary\" style=\"font-size: 40px;\"></i>\n </div>\n {{/isImage}}\n </div>\n <div>\n <h3 class=\"mb-1\" style=\"word-break: break-all;\">{{model.filename|truncate(40)}}</h3>\n <div class=\"text-muted small\">\n <span><i class=\"bi bi-hdd\"></i> {{model.file_size|filesize}}</span>\n <span class=\"mx-2\">|</span>\n <span>{{model.content_type}}</span>\n </div>\n <div class=\"text-muted small mt-1\">\n Uploaded: {{model.created|datetime}}\n </div>\n </div>\n </div>\n\n <!-- Right Side: Status & Actions -->\n <div class=\"d-flex align-items-center gap-4\">\n <div class=\"text-end\">\n <div class=\"d-flex align-items-center gap-2 justify-content-end\">\n <span class=\"badge {{model.upload_status|badge}}\">{{model.upload_status|capitalize}}</span>\n </div>\n <div class=\"text-muted small mt-1\">\n Public: {{{model.is_public|yesnoicon}}}\n </div>\n </div>\n <div data-container=\"file-context-menu\"></div>\n </div>\n </div>\n\n <!-- Tab Container -->\n <div data-container=\"file-tabs\"></div>\n </div>\n `;\n }\n\n async onInit() {\n // Info Tab using DataView\n this.infoView = new DataView({\n model: this.model,\n className: \"p-3\",\n showEmptyValues: true,\n emptyValueText: '—',\n columns: 2,\n fields: [\n { name: 'id', label: 'ID' },\n { name: 'filename', label: 'Filename' },\n { name: 'storage_filename', label: 'Storage Filename' },\n { name: 'content_type', label: 'Content Type' },\n { name: 'file_size', label: 'File Size', format: 'filesize' },\n { name: 'category', label: 'Category' },\n { name: 'upload_status', label: 'Status', format: 'badge' },\n { name: 'created', label: 'Created', format: 'datetime' },\n { name: 'modified', label: 'Modified', format: 'datetime' },\n { name: 'user.display_name', label: 'Uploaded By' },\n { name: 'file_manager.name', label: 'Storage Backend' },\n { name: 'storage_file_path', label: 'Storage Path' },\n { name: 'url', label: 'Public URL', format: 'url' },\n { name: 'is_public', label: 'Is Public', format: 'boolean' },\n ]\n });\n\n // Renditions Tab using TableView\n this.renditionsView = new TableView({\n collection: this.renditionsCollection,\n columns: [\n { key: 'role', label: 'Role', formatter: 'badge' },\n { key: 'filename', label: 'Filename', formatter: 'truncate(40)' },\n { key: 'file_size', label: 'Size', formatter: 'filesize' },\n { key: 'content_type', label: 'Content Type' },\n {\n key: 'actions',\n label: 'Actions',\n template: `\n <a href=\"{{url}}\" target=\"_blank\" class=\"btn btn-sm btn-outline-primary\" title=\"View\">\n <i class=\"bi bi-eye\"></i>\n </a>\n <a href=\"{{url}}\" download=\"{{filename}}\" class=\"btn btn-sm btn-outline-secondary\" title=\"Download\">\n <i class=\"bi bi-download\"></i>\n </a>\n `\n }\n ]\n });\n\n // Create TabView, only showing Renditions tab if they exist\n const tabs = { 'Info': this.infoView };\n tabs['Renditions'] = this.renditionsView;\n\n this.tabView = new TabView({\n tabs: tabs,\n activeTab: 'Info',\n containerId: 'file-tabs'\n });\n this.addChild(this.tabView);\n\n // Create ContextMenu\n const fileMenu = new ContextMenu({\n containerId: 'file-context-menu',\n className: \"context-menu-view header-menu-absolute\",\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: [\n { label: 'View', action: 'view-file', icon: 'bi-eye' },\n { label: 'Download', action: 'download-file', icon: 'bi bi-download' },\n { label: 'Edit Details', action: 'edit-file', icon: 'bi bi-pencil' },\n { type: 'divider' },\n this.model.get('is_public')\n ? { label: 'Make Private', action: 'make-private', icon: 'bi bi-lock' }\n : { label: 'Make Public', action: 'make-public', icon: 'bi bi-unlock' },\n { type: 'divider' },\n { label: 'Delete File', action: 'delete-file', icon: 'bi bi-trash', danger: true }\n ]\n }\n });\n this.addChild(fileMenu);\n }\n\n async onActionViewFile() {\n const contentType = this.model.get('content_type');\n const fileUrl = this.model.get('url');\n\n if (contentType.startsWith('image/')) {\n const renditions = this.model.get('renditions') || {};\n const images = [\n { src: fileUrl, alt: 'Original' },\n ...Object.values(renditions).map(r => ({ src: r.url, alt: r.role }))\n ];\n LightboxGallery.show(images, { fitToScreen: false });\n } else if (contentType === 'application/pdf') {\n PDFViewer.showDialog(fileUrl, { title: this.model.get('filename') });\n } else {\n window.open(fileUrl, '_blank');\n }\n }\n\n async onActionDownloadFile() {\n const url = this.model.get('url');\n if (url) {\n const a = document.createElement('a');\n a.href = url;\n a.download = this.model.get('filename');\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n }\n }\n\n async onActionEditFile() {\n const resp = await Dialog.showModelForm({\n title: `Edit File - ${this.model.get('filename')}`,\n model: this.model,\n formConfig: FileForms.edit,\n });\n if (resp) {\n this.render();\n }\n }\n\n async onActionMakePublic() {\n await this.model.save({ is_public: true });\n this.render();\n }\n\n async onActionMakePrivate() {\n await this.model.save({ is_public: false });\n this.render();\n }\n\n async onActionDeleteFile() {\n const confirmed = await Dialog.confirm(\n `Are you sure you want to delete the file \"${this.model.get('filename')}\"? This action cannot be undone.`,\n 'Confirm Deletion',\n { confirmClass: 'btn-danger', confirmText: 'Delete' }\n );\n if (confirmed) {\n const resp = await this.model.destroy();\n if (resp.success) {\n this.emit('file:deleted', { model: this.model });\n }\n }\n }\n}\n\nFile.VIEW_CLASS = FileView;\n\nexport default FileView;\n","/**\n * FileTablePage - File management using TablePage component\n * Clean implementation with file drop support\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { File, FileList, FileForms } from '@core/models/Files.js';\nimport FileView from './FileView.js';\nimport applyFileDropMixin from '@core/mixins/FileDropMixin.js';\n\nclass FileTablePage extends TablePage {\n constructor(options = {}) {\n super({\n name: 'admin_files',\n pageName: 'Manage Files',\n router: \"admin/files\",\n Collection: FileList,\n\n // Don't use formCreate - we have custom file upload handling\n formEdit: FileForms.edit,\n itemViewClass: FileView,\n \n // Custom add handler for file uploads\n onAdd: async (event) => {\n await this.handleFileUpload(event);\n },\n\n viewDialogOptions: {\n header: false,\n size: 'xl'\n },\n\n // Column definitions\n columns: [\n {\n key: 'id',\n label: 'ID',\n sortable: true,\n class: 'text-muted'\n },\n {\n key: 'filename',\n label: 'Filename'\n },\n {\n key: 'content_type',\n label: 'Type',\n formatter: \"default('Unknown')\"\n },\n {\n key: 'file_size',\n label: 'Size',\n formatter: \"filesize\"\n },\n {\n key: 'group.name',\n label: 'Group',\n formatter: \"default('No Group')\"\n },\n {\n key: 'upload_status',\n label: 'Status',\n formatter: \"badge\"\n },\n {\n key: 'created',\n label: 'Uploaded',\n formatter: \"epoch|datetime\"\n }\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No files found. Click \"Add File\" to upload your first file.',\n\n // Batch actions\n batchBarLocation: 'top',\n batchActions: [\n { label: \"Download\", icon: \"bi bi-download\", action: \"batch-download\" },\n { label: \"Delete\", icon: \"bi bi-trash\", action: \"batch-delete\" },\n { label: \"Move to Group\", icon: \"bi bi-folder\", action: \"batch-move\" }\n ],\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n },\n ...options,\n });\n\n // Enable file drop support\n this.enableFileDrop({\n acceptedTypes: ['*/*'],\n maxFileSize: 100 * 1024 * 1024, // 100MB\n multiple: false,\n validateOnDrop: true\n });\n }\n\n /**\n * Handle file upload via file picker\n * Opens native file picker and uses same upload flow as drag-drop\n */\n async handleFileUpload(event) {\n if (event) event.preventDefault();\n\n // Create hidden file input element\n const fileInput = document.createElement('input');\n fileInput.type = 'file';\n fileInput.accept = '*/*';\n fileInput.multiple = false;\n fileInput.style.display = 'none';\n\n // Handle file selection\n fileInput.addEventListener('change', async (e) => {\n const file = e.target.files[0];\n \n if (!file) {\n return;\n }\n\n // Validate file size (same as FileDropMixin config)\n const maxSize = 100 * 1024 * 1024; // 100MB\n if (file.size > maxSize) {\n this.showError(`File size (${this._formatFileSize(file.size)}) exceeds maximum (${this._formatFileSize(maxSize)})`);\n return;\n }\n\n // Use the same upload flow as drag-drop\n try {\n const fileModel = new File();\n let extra = {};\n if (this.options.requiresGroup && this.getApp().activeGroup) {\n extra.group = this.getApp().activeGroup.id;\n }\n\n const upload = fileModel.upload({\n file: file,\n name: file.name,\n description: `File uploaded on ${new Date().toLocaleDateString()}`,\n showToast: true,\n onProgress: (progressInfo) => {\n console.log(`Upload progress: ${progressInfo.percentage}%`);\n },\n onComplete: (result) => {\n console.log('Upload completed:', result);\n this.refresh();\n },\n onError: (error) => {\n console.error('Upload failed:', error);\n this.showError('Upload failed: ' + error.message);\n },\n ...extra\n });\n\n await upload;\n } catch (error) {\n console.error('Error starting file upload:', error);\n this.showError('Failed to start file upload: ' + error.message);\n } finally {\n // Clean up file input\n fileInput.remove();\n }\n });\n\n // Trigger file picker\n document.body.appendChild(fileInput);\n fileInput.click();\n }\n\n /**\n * Format file size for display\n * @param {number} bytes - File size in bytes\n * @returns {string} Formatted file size\n * @private\n */\n _formatFileSize(bytes) {\n if (bytes === 0) return '0 Bytes';\n const k = 1024;\n const sizes = ['Bytes', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];\n }\n\n async onFileDrop(files, event, validation) {\n const file = files[0];\n console.log(`File Dropped: ${file.name} (${file.type}) (${file.size} bytes)`);\n\n try {\n // Create new File model instance\n const fileModel = new File();\n let extra = {};\n if (this.options.requiresGroup && this.getApp().activeGroup) {\n extra.group = this.getApp().activeGroup.id;\n }\n\n // Start upload with progress tracking\n const upload = fileModel.upload({\n file: file,\n name: file.name,\n description: `File uploaded via drag & drop on ${new Date().toLocaleDateString()}`,\n showToast: true,\n onProgress: (progressInfo) => {\n console.log(`Upload progress: ${progressInfo.percentage}%`);\n },\n onComplete: (result) => {\n console.log('Upload completed:', result);\n this.refresh();\n },\n onError: (error) => {\n console.error('Upload failed:', error);\n this.showError('Upload failed: ' + error.message);\n },\n ...extra\n });\n\n await upload;\n } catch (error) {\n console.error('Error starting file upload:', error);\n this.showError('Failed to start file upload: ' + error.message);\n }\n }\n}\n\n// Apply file drop mixin\napplyFileDropMixin(FileTablePage);\n\nexport default FileTablePage;\n","/**\n * S3BucketTablePage - S3 Bucket management using TablePage component\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { S3BucketList, S3BucketForms } from '@core/models/AWS.js';\n\nclass S3BucketTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_s3_buckets',\n pageName: 'Manage S3 Buckets',\n router: \"admin/s3-buckets\",\n Collection: S3BucketList,\n \n formCreate: S3BucketForms.create,\n formEdit: S3BucketForms.edit,\n\n // Column definitions\n columns: [\n {\n key: 'id',\n label: 'ID',\n width: '60px',\n sortable: true,\n class: 'text-muted'\n },\n {\n key: 'name',\n label: 'Bucket Name',\n sortable: true\n },\n {\n key: 'created',\n label: 'Created',\n formatter: \"epoch|datetime\"\n }\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No S3 buckets found. Click \"Add S3 Bucket\" to create your first bucket.',\n\n // Batch actions\n batchBarLocation: 'top',\n batchActions: [\n { label: \"Delete\", icon: \"bi bi-trash\", action: \"batch-delete\" },\n { label: \"Export\", icon: \"bi bi-download\", action: \"batch-export\" },\n { label: \"Make Public\", icon: \"bi bi-unlock\", action: \"batch-public\" },\n { label: \"Make Private\", icon: \"bi bi-lock\", action: \"batch-private\" },\n { label: \"Empty Bucket\", icon: \"bi bi-bucket\", action: \"batch-empty\" }\n ],\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n}\n\nexport default S3BucketTablePage;","/**\n * MOJO Admin Extension - Entry (2.1.0)\n */\n\n// Bundle admin CSS\nimport '@ext/admin/css/admin.css';\n\n// Admin Pages\nexport { default as AdminDashboardPage } from '@ext/admin/account/AdminDashboardPage.js';\nexport { default as UserTablePage } from '@ext/admin/account/users/UserTablePage.js';\nexport { default as MemberTablePage } from '@ext/admin/account/users/MemberTablePage.js';\nexport { default as GroupTablePage } from '@ext/admin/account/groups/GroupTablePage.js';\nexport { default as UserDeviceTablePage } from '@ext/admin/account/devices/UserDeviceTablePage.js';\nexport { default as UserDeviceLocationTablePage } from '@ext/admin/account/devices/UserDeviceLocationTablePage.js';\nexport { default as GeoLocatedIPTablePage } from '@ext/admin/account/devices/GeoLocatedIPTablePage.js';\n\nexport { default as IncidentDashboardPage } from '@ext/admin/incidents/IncidentDashboardPage.js';\nexport { default as IncidentTablePage } from '@ext/admin/incidents/IncidentTablePage.js';\nexport { default as EventTablePage } from '@ext/admin/incidents/EventTablePage.js';\nexport { default as TicketTablePage } from '@ext/admin/incidents/TicketTablePage.js';\nexport { default as RuleSetTablePage } from '@ext/admin/incidents/RuleSetTablePage.js';\n\nexport { default as EmailDomainTablePage } from '@ext/admin/messaging/email/EmailDomainTablePage.js';\nexport { default as EmailMailboxTablePage } from '@ext/admin/messaging/email/EmailMailboxTablePage.js';\nexport { default as EmailTemplateTablePage } from '@ext/admin/messaging/email/EmailTemplateTablePage.js';\nexport { default as SentMessageTablePage } from '@ext/admin/messaging/email/SentMessageTablePage.js';\nexport { default as PhoneNumberTablePage } from '@ext/admin/messaging/sms/PhoneNumberTablePage.js';\nexport { default as SMSTablePage } from '@ext/admin/messaging/sms/SMSTablePage.js';\nexport { default as PushDashboardPage } from '@ext/admin/messaging/push/PushDashboardPage.js';\nexport { default as PushConfigTablePage } from '@ext/admin/messaging/push/PushConfigTablePage.js';\nexport { default as PushTemplateTablePage } from '@ext/admin/messaging/push/PushTemplateTablePage.js';\nexport { default as PushDeliveryTablePage } from '@ext/admin/messaging/push/PushDeliveryTablePage.js';\nexport { default as PushDeviceTablePage } from '@ext/admin/messaging/push/PushDeviceTablePage.js';\n\nexport { default as JobsAdminPage } from '@ext/admin/jobs/JobsAdminPage.js';\nexport { default as TaskManagementPage } from '@ext/admin/jobs/TaskManagementPage.js';\n\nexport { default as LogTablePage } from '@ext/admin/monitoring/LogTablePage.js';\nexport { default as MetricsPermissionsTablePage } from '@ext/admin/monitoring/MetricsPermissionsTablePage.js';\n\nexport { default as FileManagerTablePage } from '@ext/admin/storage/FileManagerTablePage.js';\nexport { default as FileTablePage } from '@ext/admin/storage/FileTablePage.js';\nexport { default as S3BucketTablePage } from '@ext/admin/storage/S3BucketTablePage.js';\n\n// Admin Views\nexport { default as DeviceView } from '@ext/admin/account/devices/DeviceView.js';\nexport { default as GeoIPView } from '@ext/admin/account/devices/GeoIPView.js';\nexport { default as GroupView } from '@ext/admin/account/groups/GroupView.js';\nexport { default as MemberView } from '@ext/admin/account/users/MemberView.js';\nexport { default as UserView } from '@ext/admin/account/users/UserView.js';\n\nexport { default as IncidentView } from '@ext/admin/incidents/IncidentView.js';\nexport { default as EventView } from '@ext/admin/incidents/EventView.js';\nexport { default as TicketView } from '@ext/admin/incidents/TicketView.js';\nexport { default as RuleSetView } from '@ext/admin/incidents/RuleSetView.js';\n\nexport { default as EmailTemplateView } from '@ext/admin/messaging/email/EmailTemplateView.js';\nexport { default as EmailView } from '@ext/admin/messaging/email/EmailView.js';\nexport { default as PhoneNumberView } from '@ext/admin/messaging/sms/PhoneNumberView.js';\nexport { default as PushDeliveryView } from '@ext/admin/messaging/push/PushDeliveryView.js';\nexport { default as PushDeviceView } from '@ext/admin/messaging/push/PushDeviceView.js';\n\nexport { default as JobDetailsView } from '@ext/admin/jobs/JobDetailsView.js';\nexport { default as JobHealthView } from '@ext/admin/jobs/JobHealthView.js';\nexport { default as JobStatsView } from '@ext/admin/jobs/JobStatsView.js';\n\nexport { default as LogView } from '@ext/admin/monitoring/LogView.js';\nexport { default as MetricsPermissionsView } from '@ext/admin/monitoring/MetricsPermissionsView.js';\n\nexport { default as FileView } from '@ext/admin/storage/FileView.js';\n\n// Admin Components\nexport { default as RunnerDetailsView } from '@ext/admin/jobs/RunnerDetailsView.js';\nexport { default as TaskDetailsView } from '@ext/admin/jobs/TaskDetailsView.js';\n// Convenience\nexport { default as WebApp } from '@core/WebApp.js';\n\n// Version info passthrough\nexport {\n VERSION_INFO,\n VERSION,\n VERSION_MAJOR,\n VERSION_MINOR,\n VERSION_REVISION,\n BUILD_TIME\n} from './version.js';\n\n\n\n// Import all admin page classes for the register function\nimport AdminDashboardPageClass from '@ext/admin/account/AdminDashboardPage.js';\nimport UserTablePageClass from '@ext/admin/account/users/UserTablePage.js';\nimport MemberTablePageClass from '@ext/admin/account/users/MemberTablePage.js';\nimport GroupTablePageClass from '@ext/admin/account/groups/GroupTablePage.js';\nimport UserDeviceTablePageClass from '@ext/admin/account/devices/UserDeviceTablePage.js';\nimport UserDeviceLocationTablePageClass from '@ext/admin/account/devices/UserDeviceLocationTablePage.js';\nimport GeoLocatedIPTablePageClass from '@ext/admin/account/devices/GeoLocatedIPTablePage.js';\n\nimport IncidentDashboardPageClass from '@ext/admin/incidents/IncidentDashboardPage.js';\nimport IncidentTablePageClass from '@ext/admin/incidents/IncidentTablePage.js';\nimport EventTablePageClass from '@ext/admin/incidents/EventTablePage.js';\nimport TicketTablePageClass from '@ext/admin/incidents/TicketTablePage.js';\nimport RuleSetTablePageClass from '@ext/admin/incidents/RuleSetTablePage.js';\n\nimport EmailDomainTablePageClass from '@ext/admin/messaging/email/EmailDomainTablePage.js';\nimport EmailMailboxTablePageClass from '@ext/admin/messaging/email/EmailMailboxTablePage.js';\nimport EmailTemplateTablePageClass from '@ext/admin/messaging/email/EmailTemplateTablePage.js';\nimport SentMessageTablePageClass from '@ext/admin/messaging/email/SentMessageTablePage.js';\nimport PhoneNumberTablePageClass from '@ext/admin/messaging/sms/PhoneNumberTablePage.js';\nimport SMSTablePageClass from '@ext/admin/messaging/sms/SMSTablePage.js';\nimport PushDashboardPageClass from '@ext/admin/messaging/push/PushDashboardPage.js';\nimport PushConfigTablePageClass from '@ext/admin/messaging/push/PushConfigTablePage.js';\nimport PushTemplateTablePageClass from '@ext/admin/messaging/push/PushTemplateTablePage.js';\nimport PushDeliveryTablePageClass from '@ext/admin/messaging/push/PushDeliveryTablePage.js';\nimport PushDeviceTablePageClass from '@ext/admin/messaging/push/PushDeviceTablePage.js';\n\nimport JobsAdminPageClass from '@ext/admin/jobs/JobsAdminPage.js';\nimport TaskManagementPageClass from '@ext/admin/jobs/TaskManagementPage.js';\n\nimport LogTablePageClass from '@ext/admin/monitoring/LogTablePage.js';\nimport MetricsPermissionsTablePageClass from '@ext/admin/monitoring/MetricsPermissionsTablePage.js';\n\nimport FileManagerTablePageClass from '@ext/admin/storage/FileManagerTablePage.js';\nimport FileTablePageClass from '@ext/admin/storage/FileTablePage.js';\nimport S3BucketTablePageClass from '@ext/admin/storage/S3BucketTablePage.js';\n\n/**\n * Register all admin pages to a WebApp instance\n * @param {WebApp} app - The WebApp instance to register pages to\n * @returns {void}\n */\nexport function registerSystemPages(app, addToMenu = true) {\n // Register all admin pages with consistent naming\n app.registerPage('system/dashboard', AdminDashboardPageClass, {permissions: [\"view_admin\"]});\n app.registerPage('system/jobs', JobsAdminPageClass, {permissions: [\"view_jobs\", \"manage_jobs\"]});\n app.registerPage('system/users', UserTablePageClass, {permissions: [\"manage_users\"]});\n app.registerPage('system/groups', GroupTablePageClass, {permissions: [\"manage_groups\"]});\n app.registerPage('system/members', MemberTablePageClass, {permissions: [\"manage_members\"]});\n app.registerPage('system/s3buckets', S3BucketTablePageClass, {permissions: [\"manage_aws\"]});\n app.registerPage('system/filemanagers', FileManagerTablePageClass, {permissions: [\"manage_files\"]});\n app.registerPage('system/files', FileTablePageClass, {permissions: [\"manage_files\"]});\n app.registerPage('system/incidents', IncidentTablePageClass, {permissions: [\"view_incidents\"]});\n app.registerPage('system/events', EventTablePageClass, {permissions: [\"view_incidents\"]});\n app.registerPage('system/logs', LogTablePageClass, {permissions: [\"view_logs\"]});\n app.registerPage('system/user/devices', UserDeviceTablePageClass, {permissions: [\"manage_users\"]});\n app.registerPage('system/user/device-locations', UserDeviceLocationTablePageClass, {permissions: [\"manage_users\"]});\n app.registerPage('system/system/geoip', GeoLocatedIPTablePageClass, {permissions: [\"manage_users\"]});\n app.registerPage('system/email/mailboxes', EmailMailboxTablePageClass, {permissions: [\"manage_aws\"]});\n app.registerPage('system/email/domains', EmailDomainTablePageClass, {permissions: [\"manage_aws\"]});\n app.registerPage('system/email/sent', SentMessageTablePageClass, {permissions: [\"manage_aws\"]});\n app.registerPage('system/email/templates', EmailTemplateTablePageClass, {permissions: [\"manage_aws\"]});\n app.registerPage('system/incident-dashboard', IncidentDashboardPageClass, { permissions: [\"view_incidents\"] });\n app.registerPage('system/rulesets', RuleSetTablePageClass, { permissions: [\"manage_incidents\"] });\n app.registerPage('system/tickets', TicketTablePageClass, { permissions: [\"manage_incidents\"] });\n app.registerPage('system/metrics/permissions', MetricsPermissionsTablePageClass, { permissions: [\"manage_metrics\"] });\n app.registerPage('system/push/dashboard', PushDashboardPageClass, { permissions: [\"manage_users\"] });\n app.registerPage('system/push/configs', PushConfigTablePageClass, { permissions: [\"manage_users\"] });\n app.registerPage('system/push/templates', PushTemplateTablePageClass, { permissions: [\"manage_users\"] });\n app.registerPage('system/push/deliveries', PushDeliveryTablePageClass, { permissions: [\"manage_users\"] });\n app.registerPage('system/push/devices', PushDeviceTablePageClass, { permissions: [\"manage_users\"] });\n app.registerPage('system/phonehub/numbers', PhoneNumberTablePageClass, { permissions: [\"manage_users\"] });\n app.registerPage('system/phonehub/sms', SMSTablePageClass, { permissions: [\"manage_users\"] });\n\n // Check if sidebar exists and has an admin menu config\n if (addToMenu && app.sidebar && app.sidebar.getMenuConfig) {\n const adminMenuConfig = app.sidebar.getMenuConfig('system');\n if (adminMenuConfig && adminMenuConfig.items) {\n // Add admin pages to sidebar menu\n const adminMenuItems = [\n {\n text: 'Dashboard',\n route: '?page=system/dashboard',\n icon: 'bi-speedometer2',\n permissions: [\"view_admin\"]\n },\n {\n text: 'Jobs Management',\n route: '?page=system/jobs',\n icon: 'bi-gear-wide-connected',\n permissions: [\"view_jobs\", \"manage_jobs\"]\n },\n {\n text: 'Users',\n route: '?page=system/users',\n icon: 'bi-people',\n permissions: [\"manage_users\"]\n },\n {\n text: 'Groups',\n route: '?page=system/groups',\n icon: 'bi-diagram-3',\n permissions: [\"manage_groups\"]\n },\n {\n text: 'Incidents & Tickets',\n route: null,\n icon: 'bi-shield-exclamation',\n permissions: [\"view_incidents\"],\n children: [\n {\n text: 'Dashboard',\n route: '?page=system/incident-dashboard',\n icon: 'bi-bar-chart-line',\n permissions: [\"view_incidents\"]\n },\n {\n text: 'Incidents',\n route: '?page=system/incidents',\n icon: 'bi-exclamation-triangle',\n permissions: [\"view_incidents\"]\n },\n {\n text: 'Tickets',\n route: '?page=system/tickets',\n icon: 'bi-ticket-detailed',\n permissions: [\"manage_incidents\"]\n },\n {\n text: 'Events',\n route: '?page=system/events',\n icon: 'bi-bell',\n permissions: [\"view_incidents\"]\n },\n {\n text: 'Rule Engine',\n route: '?page=system/rulesets',\n icon: 'bi-gear-wide-connected',\n permissions: [\"manage_incidents\"]\n },\n ]\n },\n {\n text: 'Security',\n route: null,\n icon: 'bi-shield',\n permissions: [\"manage_groups\"],\n children: [\n {\n text: 'Logs',\n route: '?page=system/logs',\n icon: 'bi-journal-text',\n permissions: [\"view_logs\"]\n },\n {\n text: 'User Devices',\n route: '?page=system/user/devices',\n icon: 'bi-phone',\n permissions: [\"manage_users\"]\n },\n {\n text: 'Device Locations',\n route: '?page=system/user/device-locations',\n icon: 'bi-geo-alt',\n permissions: [\"manage_users\"]\n },\n {\n text: 'GeoIP Cache',\n route: '?page=system/system/geoip',\n icon: 'bi-globe',\n permissions: [\"manage_users\"]\n },\n {\n text: 'Metrics Permissions',\n route: '?page=system/metrics/permissions',\n icon: 'bi-bar-chart-line',\n permissions: [\"manage_metrics\"]\n }\n ]\n },\n {\n text: 'Storage',\n route: null,\n icon: 'bi-folder',\n permissions: [\"manage_files\", \"manage_aws\"],\n children: [\n {\n text: 'S3 Buckets',\n route: '?page=system/s3buckets',\n icon: 'bi-bucket',\n permissions: [\"manage_aws\"]\n },\n {\n text: 'Storage Backends',\n route: '?page=system/filemanagers',\n icon: 'bi-hdd-stack',\n permissions: [\"manage_aws\"]\n },\n {\n text: 'Files',\n route: '?page=system/files',\n icon: 'bi-file-earmark',\n permissions: [\"manage_files\"]\n },\n ]\n },\n {\n text: 'Push Notifications',\n route: null,\n icon: 'bi-broadcast',\n permissions: [\"manage_users\"],\n children: [\n { text: 'Dashboard', route: '?page=system/push/dashboard', icon: 'bi-bar-chart-line' },\n { text: 'Configurations', route: '?page=system/push/configs', icon: 'bi-gear' },\n { text: 'Templates', route: '?page=system/push/templates', icon: 'bi-file-earmark-text' },\n { text: 'Deliveries', route: '?page=system/push/deliveries', icon: 'bi-send' },\n { text: 'Devices', route: '?page=system/push/devices', icon: 'bi-phone' },\n ]\n },\n {\n text: 'Email Admin',\n route: null,\n icon: 'bi-envelope',\n permissions: [\"manage_aws\"],\n children: [\n {\n text: 'Domains',\n route: '?page=system/email/domains',\n icon: 'bi-globe',\n permissions: [\"manage_aws\"]\n },\n {\n text: 'Mailboxes',\n route: '?page=system/email/mailboxes',\n icon: 'bi-inbox',\n permissions: [\"manage_aws\"]\n },\n {\n text: 'Sent',\n route: '?page=system/email/sent',\n icon: 'bi-send-check',\n permissions: [\"manage_aws\"]\n },\n {\n text: 'Templates',\n route: '?page=system/email/templates',\n icon: 'bi-file-text',\n permissions: [\"manage_aws\"]\n }\n ]\n },\n {\n text: 'Phone Hub',\n route: null,\n icon: 'bi-telephone',\n permissions: [\"manage_users\"],\n children: [\n {\n text: 'Numbers',\n route: '?page=system/phonehub/numbers',\n icon: 'bi-collection',\n permissions: [\"manage_users\"]\n },\n {\n text: 'SMS',\n route: '?page=system/phonehub/sms',\n icon: 'bi-chat-dots',\n permissions: [\"manage_users\"]\n }\n ]\n }\n ];\n\n // Add items to existing admin menu\n adminMenuConfig.items.unshift(...adminMenuItems);\n }\n }\n\n}\n\nexport { registerSystemPages as registerAdminPages };\n"],"names":["AdminHeaderView","View","constructor","options","super","title","headerActions","label","icon","action","buttonClass","className","this","stats","user_activity_day","total_users","group_activity_day","total_groups","api_calls","apiChange","incidents","incidentsChange","prepareStatsForTemplate","getTemplate","onInit","userActivity","MetricsMiniChartWidget","subtitle","background","textColor","granularity","trendRange","trendOffset","slugs","account","chartType","showTooltip","showXAxis","height","chartWidth","color","fill","fillColor","smoothing","showTrending","showSettings","showDateRange","containerId","addChild","groupActivity","apiActivity","endpoint","incidentActivity","onBeforeRender","loadValues","response","getApp","rest","GET","success","data","status","Object","assign","header","render","error","console","loadStats","AdminDashboardPage","Page","pageTitle","pageSubtitle","lastUpdated","Date","toLocaleString","headerView","apiMetricsChart","MetricsChart","yAxis","beginAtZero","tooltip","y","onActionRefreshAll","event","element","button","currentTarget","querySelector","classList","add","disabled","promises","refresh","filter","Boolean","Promise","allSettled","eventBus","events","emit","page","timestamp","alert","innerHTML","setTimeout","remove","onActionExportMetrics","export","charts","onActionViewAlerts","router","navigateTo","onActionViewSystemStatus","refreshDashboard","getCharts","apiMetrics","getStats","onAfterRender","UserView","model","User","tabView","profileView","groupsView","eventsView","logsView","template","setModel","DataView","showEmptyValues","fields","UserDataView","profile","permsView","FormView","PERMISSION_FIELDS","autosaveModelField","membersCollection","MemberList","params","user","get","size","TableView","collection","hideActivePillNames","columns","key","formatter","sortable","eventsCollection","IncidentEventList","model_name","model_id","width","userDevices","UserDeviceList","devicesView","userLocations","UserDeviceLocationList","locationsView","pushDevices","PushDeviceList","pushDevicesView","logsCollection","LogList","permissions","name","type","startName","endName","fieldName","format","displayFormat","separator","value","activityCollection","uid","activityView","TabView","tabs","Profile","Permissions","Groups","Events","Logs","Activity","Devices","Locations","activeTab","enableResponsive","dropdownStyle","minWidth","userMenu","ContextMenu","context","config","items","onActionEditUser","frmConfig","UserForms","edit","Dialog","showModelForm","id","modelName","formConfig","onActionResetPassword","onActionDeactivateUser","confirm","save","is_active","toast","onActionActivateUser","onActionViewGroup","getAttribute","onActionRemoveFromGroup","onActionViewEvent","onActionViewLog","showTab","tabName","getActiveTab","_onModelChange","create","VIEW_CLASS","UserTablePage","TablePage","pageName","Collection","UserList","viewDialogOptions","defaultQuery","sort","class","visibility","filters","defaultValue","selectable","searchable","filterable","paginated","showRefresh","showAdd","showExport","emptyMessage","contextMenu","tableOptions","striped","bordered","hover","responsive","onActionEditPermissions","preventDefault","item","dataset","_","username","onActionChangePassword","showForm","attributes","autocomplete","readonly","tabindex","style","passwordUsage","required","showToggle","new_password","MOJOUtils","checkPasswordStrength","score","resp","onPasswordChange","onActionSendInvite","send_invite","MemberView","Member","overviewView","Overview","memberMenu","danger","onActionEditMembership","MemberForms","onActionViewUser","onActionRemoveMember","destroy","MemberTablePage","formEdit","itemViewClass","batchBarLocation","batchActions","GroupView","Group","membersView","childrenView","group","addButtonLabel","onAdd","async","onInviteClick","childrenCollection","GroupList","parent","toolbarButtons","Members","Children","groupMenu","onActionEditGroup","GroupForms","detailed","onActionAddMember","onActionAddChildGroup","onActionDeactivateGroup","onActionActivateGroup","onActionViewParent","parentId","groupId","stopPropagation","app","email","showLoading","POST","hideLoading","fetch","onActionAddMultiple","labelField","itemTemplate","valueField","enableSearch","searchPlaceholder","defaultParams","warn","warning","GroupTablePage","formCreate","GroupKindOptions","onActionMakeActive","setActiveGroup","DeviceView","UserDevice","deviceInfo","deviceIcon","getIconForDevice","os","family","toLowerCase","browser","user_agent","device","includes","infoView","locationsCollection","user_device","Info","deviceMenu","userId","onActionDeleteDevice","show","duid","getByDuid","view","dialog","body","buttons","text","dismiss","document","message","UserDeviceTablePage","UserDeviceLocationTablePage","GeoIPView","GeoLocatedIP","hasCoordinates","detailsView","emptyValueText","cols","networkView","riskView","metadataView","source_ip","ip","Location","Network","Metadata","lat","lng","locationStr","join","mapView","MapView","markers","popup","tileLayer","zoom","menuItems","push","geoIPMenu","window","bootstrap","Tooltip","querySelectorAll","forEach","el","existing","getInstance","dispose","onActionEditLocation","EDIT_LOCATION_FORM","onActionEditSecurity","EDIT_SECURITY_FORM","onActionEditNetwork","EDIT_NETWORK_FORM","onActionRefreshGeoip","info","onActionThreatAnalysis","threat_analysis","onActionViewOnMap","url","open","onActionDeleteGeoip","confirmClass","confirmText","lookup","GeoLocatedIPTablePage","GeoLocatedIPList","itemView","clickAction","tableViewOptions","evt","onLookup","tableView","_onRowView","MapLibreView","center","pitch","bearing","mapStyle","showNavigationControl","autoFitBounds","map","mapMarkers","pendingMarkers","lineSources","pendingLineData","Map","loadMapLibre","initializeMap","maplibregl","link","createElement","rel","href","head","appendChild","resolve","reject","script","src","onload","onerror","getMapStyle","styles","streets","dark","version","sources","osm","tiles","tileSize","attribution","layers","source","glyphs","light","satellite","terrain","mapElement","mapCenter","length","container","addControl","NavigationControl","on","initialMarkers","updateMarkers","addLineSource","clear","addMarkers","Array","isArray","markerData","markerSize","Number","borderRadius","backgroundColor","border","boxShadow","cursor","display","alignItems","justifyContent","marker","Marker","setLngLat","addTo","mapPopup","Popup","offset","setHTML","setPopup","fitBounds","bounds","LngLatBounds","extend","padding","maxZoom","newMarkers","clearMarkers","flyTo","paint","layout","sourceId","getLayer","removeLayer","getSource","removeSource","addSource","addLayer","set","updateLineSource","setData","entries","setPaintProperty","setLayoutProperty","setView","setZoom","setPitch","setBearing","onBeforeDestroy","COUNTRY_CENTROIDS","AD","AE","AF","AG","AI","AL","AM","AO","AQ","AR","AS","AT","AU","AW","AZ","BA","BB","BD","BE","BF","BG","BH","BI","BJ","BL","BM","BN","BO","BQ","BR","BS","BT","BV","BW","BY","BZ","CA","CC","CD","CF","CG","CH","CI","CK","CL","CM","CN","CO","CR","CU","CV","CW","CX","CY","CZ","DE","DJ","DK","DM","DO","DZ","EC","EE","EG","ER","ES","ET","FI","FJ","FK","FM","FO","FR","GA","GB","GD","GE","GF","GG","GH","GI","GL","GM","GN","GP","GQ","GR","GS","GT","GU","GW","GY","HM","HN","HR","HT","HU","ID","IE","IL","IM","IN","IO","IQ","IR","IS","IT","JE","JM","JO","JP","KE","KG","KH","KI","KM","KN","KP","KR","KW","KY","KZ","LA","LB","LC","LI","LK","LR","LS","LT","LU","LV","LY","MA","MC","MD","ME","MF","MG","MH","MK","ML","MM","MN","MP","MQ","MR","MS","MT","MU","MV","MW","MX","MY","MZ","NA","NC","NE","NF","NG","NI","NL","NO","NP","NR","NU","NZ","OM","PA","PE","PF","PG","PH","PK","PL","PM","PN","PR","PS","PT","PW","PY","QA","RE","RO","RS","RU","RW","SA","SB","SC","SD","SE","SG","SH","SI","SJ","SK","SL","SM","SN","SO","SR","SS","ST","SV","SX","SY","SZ","TC","TD","TF","TG","TH","TJ","TK","TL","TM","TN","TO","TR","TT","TV","TZ","UA","UG","UM","US","UY","UZ","VA","VC","VE","VG","VI","VN","VU","WF","WS","YE","YT","ZA","ZM","ZW","MetricsCountryMapView","mapHeight","category","maxCountries","metricLabel","mapOptions","showRoutes","routeOrigin","_refreshing","statusEl","setStatus","metrics","fetchMetrics","applyMetrics","Error","with_labels","slugsArray","labels","iso","series","total","reduce","sum","centroid","toUpperCase","code","values","renderLegend","a","b","topEntries","slice","maxValue","entry","intensity","Math","round","getMarkerColor","renderRoutes","end","mix","idx","legendEl","rows","percent","toFixed","origin","geojson","features","geometry","coordinates","properties","textContent","IncidentDashboardHeader","IncidentStats","IncidentDashboardPage","eventsWidget","defaultDateRange","valueFormat","settingsKey","incidentsWidget","eventsByCountryChart","showMetricsFilter","maxDatasets","colors","incidentsByCountryChart","eventsCountryMap","myTicketsCollection","TicketList","myTicketsTable","newIncidentsCollection","IncidentList","highPriorityIncidentsTable","refreshTasks","then","StackTraceView","stackTrace","formattedStackTrace","formatStackTrace","lines","JSON","stringify","split","html","line","index","trim","escapeHtml","match","funcName","filePath","lineNum","colNum","startsWith","updateStackTrace","newStackTrace","IncidentHistoryAdapter","incidentId","IncidentHistoryList","incident","models","transform","author","avatarUrl","content","attachments","addNote","history","IncidentHistory","note","kind","IncidentView","Incident","incidentIcon","getIconForIncident","state","s","actions","adapter","historyView","ChatView","metadata","stack_trace","stackTraceView","keys","incidentMenu","onActionEditIncident","IncidentForms","onActionResolveIncident","onActionDeleteIncident","IncidentTablePage","onActionBatchResolve","selected","getSelectedItems","all","onActionBatchOpen","onActionBatchPause","onActionBatchIgnore","onActionBatchMerge","_event","_element","result","parentModel","find","merge","mergeIds","EventView","IncidentEvent","eventIcon","getIconForEvent","level","eventMenu","onActionViewIncident","onActionViewModel","onActionDeleteEvent","EventTablePage","IncidentEventForms","category__not","TicketNoteAdapter","ticketId","TicketNoteList","TicketNote","media","files","TicketView","Ticket","chatView","theme","currentUserId","getCurrentUserId","inputPlaceholder","inputButtonText","ticketMenu","currentUser","onActionEditTicket","TicketForms","onActionChangeStatus","currentStatus","replace","onActionSetPriority","currentPriority","p","priority","onActionAssignUser","onActionCloseTicket","TicketTablePage","editable","editableOptions","placeHolder","TicketCategories","RuleSetView","RuleSet","matchByValue","matchByOption","MatchByOptions","opt","matchByLabel","String","bundleByValue","bundleByOption","BundleByOptions","bundleByLabel","configView","rulesCollection","RuleList","rulesView","divider","addFormDefaults","Configuration","Rules","onActionEditRuleset","EDIT_FORM","onActionDisableRuleset","newStatus","showToast","onActionDeleteRuleset","closest","bsModal","Modal","hide","RuleSetTablePage","RuleSetList","v","pageSizes","defaultPageSize","emptyIcon","EmailDomainTablePage","EmailDomainList","EmailDomainForms","onActionEditAwsCreds","credentials","onActionOnboard","EmailDomain","formData","onboard","err","showError","onActionAudit","audit","showDialog","onActionReconcile","reconcile","EmailMailboxTablePage","MailboxList","MailboxForms","onActionSendEmail","from_email","Mailbox","sendEmail","msg","details","onActionSendTemplateEmail","EmailTemplateView","EmailTemplate","hasHtml","hasText","hasMetadata","EmailTemplateTablePage","EmailTemplateList","EmailTemplateForms","scrollable","formDialogConfig","EmailView","SentMessage","hasContext","SentMessageTablePage","SentMessageList","PhoneNumber","Model","normalize","phoneNumber","countryCode","payload","phone_number","country_code","PhoneNumberList","ModelClass","SMS","send","SMSList","PhoneNumberView","carrierView","addressView","Carrier","Address","ctxMenu","onActionRefreshLookup","number","force_refresh","e","onActionDeletePhone","MODEL_CLASS","PhoneNumberTablePage","addButtonIcon","SMSView","messageView","deliveryView","Message","Delivery","onActionRefreshSms","onActionDeleteSms","SMSTablePage","PushDashboardPage","deliveriesChart","statusChart","PieChart","recentDeliveries","PushDeliveryList","_sort","_limit","failedDeliveries","PushConfigTablePage","PushConfigList","PushConfigForms","PushTemplateTablePage","PushTemplateList","PushTemplateForms","PushDeliveryView","PushDeliveryTablePage","PushDeviceView","dataView","PushDeviceTablePage","JobStatsView","pending","running","completed","failed","scheduled","isMounted","totals","JobHealthView","health","runners","active","channels","loadHealth","overall_status","runners_active","scheduler","healthStatusClass","getHealthStatusClass","setupChannelDisplay","setupSchedulerDisplay","channelsArray","channel","channelStatus","totalJobs","queued_count","inflight_count","statusBadgeClass","getChannelBadgeClass","statusIcon","getChannelIcon","queued","inflight","schedulerStatusText","schedulerStatusClass","schedulerIcon","healthy","critical","unknown","onActionRefreshHealth","onActionSystemSettings","showAlert","JobDetailsView","Job","payloadView","autoRefreshInterval","JobEventList","job","JobLogList","Payload","contextMenuItems","canCancel","canRetry","jobMenu","graph","prepareJobData","getStatusBadgeClass","getStatusIcon","formattedDuration","getFormattedDuration","loadJobDetails","getDetailedStatus","onActionRefreshJob","onActionCancelJob","cancel","onActionRetryJob","retryData","JobForms","retry","delay","newJobId","startAutoRefresh","clearInterval","isActive","setInterval","stopAutoRefresh","onDestroy","onHide","JobsTable","JobList","collectionParams","placeholder","row","condition","onItemViewJobDetails","onItemCancelJob","confirmed","showConfirm","onItemRetryJob","retryResult","onItemCloneJob","getPayload","clone","field","newPayload","parse","cloneData","cloneResult","cloneJob","RunnersTable","JobRunnerList","isAlive","c","heartbeatTime","diffMs","diffSeconds","floor","startTime","runner","onActionPingRunner","runnerId","ping","onActionShutdownRunner","shutdown","ScheduledJobsTable","JobsAdminPage","refreshRate","jobStats","JobsEngineStats","jobStatsView","jobHealthView","jobTablesView","Jobs","Runners","Scheduled","jobMetricsChart","refreshData","activeTable","getTab","updateHeaderTimestamp","timestampElement","refreshRateSeconds","onActionSetRefreshRate","rate","parseInt","rateText","onActionExportData","onActionRunSimpleJob","executeJobAction","test","onActionRunTestJobs","tests","onActionClearStuck","channelOptions","ch","help","clearStuck","count","onActionClearChannel","clearChannel","onActionPurgeJobs","purgeJobs","days_old","onActionCleanupConsumers","cleanConsumers","onActionRunnerBroadcast","JobRunnerForms","broadcast","broadcastResult","JobRunner","command","timeout","actionFn","successMessage","onEnter","onExit","getHealth","TaskDetailsView","task","logs","prepareTaskData","loadTaskLogs","loadTaskMetrics","dataFormatted","expires","expiresClass","now","cancelled","expired","getLogLevelClass","debug","log","levelClass","setTask","onActionRefreshLogs","showSuccess","originalTask","newTask","exportData","exported_at","toISOString","exported_by","blob","Blob","URL","createObjectURL","download","click","removeChild","revokeObjectURL","RunnerDetailsView","currentTasks","prepareRunnerData","loadRunnerMetrics","loadCurrentTasks","loadRunnerLogs","ping_age","pingAgeText","formatDuration","pingAgeClass","started_at","uptimeSeconds","uptimeText","formatUptime","hostname","activeWorkers","tasksCompleted","tasksCompletedToday","avgExecutionTime","errorCount","errorRate","queueBacklog","workerUtilization","max_workers","utilizationClass","getUtilizationClass","performanceTrend","getPerformanceTrend","cpu","getResourceStatus","cpuUsage","memory","getMemoryStatus","memoryUsed","memoryTotal","getDefaultMetrics","duration","started","percentage","used","utilization","avgTime","usage","cssClass","seconds","days","hours","minutes","setRunner","onActionRefreshMetrics","onActionRefreshTasks","onActionFilterLogs","btn","onActionViewTask","taskId","onActionCancelTask","confirmMessage","DELETE","TaskStatsView","errors","TaskRunnersView","loadRunners","pingAge","statusBadge","formatPingAge","onActionRefreshRunners","onActionViewRunnerDetails","r","onActionPauseRunner","onActionRestartRunner","onActionRemoveRunner","TaskChartsView","taskFlowChart","taskErrorsChart","PendingTasksTable","RunningTasksTable","CompletedTasksTable","ErrorTasksTable","TaskManagementPage","taskStatsView","taskRunnersView","taskChartsView","taskTablesView","Pending","Running","Completed","Errors","onActionExportTasks","exportToCSV","onActionManageChannels","channelList","onActionViewSystemLogs","logPreview","getRunners","taskFlow","taskErrors","LogView","Log","logIcon","getIconForLog","lvl","logContent","formattedLog","parsed","logContentView","onActionCopyLog","navigator","clipboard","writeText","logMenu","onActionViewIp","onActionViewDevice","onActionDeleteLog","LogTablePage","MetricsPermissionsView","MetricsPermission","onActionEdit","MetricsForms","onActionDelete","MetricsPermissionsTablePage","MetricsPermissionList","FileManagerTablePage","FileManagerList","FileManagerForms","onActionEditOwners","owners","onActionCheckCors","check_cors","showData","onActionTestConnection","test_connection","onActionEditCredentials","onActionClone","FileView","File","isImage","renditionsData","renditionsCollection","renditionsView","fileMenu","onActionViewFile","contentType","fileUrl","renditions","images","alt","role","LightboxGallery","fitToScreen","PDFViewer","onActionDownloadFile","onActionEditFile","FileForms","onActionMakePublic","is_public","onActionMakePrivate","onActionDeleteFile","FileTablePage","FileList","handleFileUpload","enableFileDrop","acceptedTypes","maxFileSize","multiple","validateOnDrop","fileInput","accept","addEventListener","file","target","maxSize","_formatFileSize","fileModel","extra","requiresGroup","activeGroup","upload","description","toLocaleDateString","onProgress","progressInfo","onComplete","onError","bytes","i","parseFloat","pow","onFileDrop","validation","applyFileDropMixin","S3BucketTablePage","S3BucketList","S3BucketForms","registerSystemPages","addToMenu","registerPage","AdminDashboardPageClass","JobsAdminPageClass","UserTablePageClass","GroupTablePageClass","MemberTablePageClass","S3BucketTablePageClass","FileManagerTablePageClass","FileTablePageClass","IncidentTablePageClass","EventTablePageClass","LogTablePageClass","UserDeviceTablePageClass","UserDeviceLocationTablePageClass","GeoLocatedIPTablePageClass","EmailMailboxTablePageClass","EmailDomainTablePageClass","SentMessageTablePageClass","EmailTemplateTablePageClass","IncidentDashboardPageClass","RuleSetTablePageClass","TicketTablePageClass","MetricsPermissionsTablePageClass","PushDashboardPageClass","PushConfigTablePageClass","PushTemplateTablePageClass","PushDeliveryTablePageClass","PushDeviceTablePageClass","PhoneNumberTablePageClass","SMSTablePageClass","sidebar","getMenuConfig","adminMenuConfig","adminMenuItems","route","children","unshift"],"mappings":"imBASA,MAAMA,wBAAwBC,EAAAA,KAC5B,WAAAC,CAAYC,EAAU,IACpBC,MAAM,CACJC,MAAO,eACJF,EACHG,cAAe,CACX,CACIC,MAAO,SACPC,KAAM,cACNC,OAAQ,SACRC,YAAa,gBAGrBC,UAAW,yBAIbC,KAAKC,MAAQ,CACXC,kBAAmB,EACnBC,YAAa,EACbC,mBAAoB,EACpBC,aAAc,EACdC,UAAW,EACXC,UAAW,GACXC,UAAW,EACXC,gBAAiB,IAInBT,KAAKU,yBACP,CAEA,iBAAMC,GACJ,MAAO,4oBAqBT,CAEA,YAAMC,GAIJZ,KAAKa,aAAe,IAAIC,yBAAuB,CAC7ClB,KAAM,oBACNH,MAAO,gBACPsB,SAAU,4HACVC,WAAY,UACZC,UAAW,UACXC,YAAa,OACbC,WAAY,EACZC,YAAa,EACbC,MAAO,CAAC,qBACRC,QAAS,SACTC,UAAW,MACXC,aAAa,EACbC,WAAW,EACXC,OAAQ,GACRC,WAAY,OACZC,MAAO,2BACPC,MAAM,EACNC,UAAW,2BACXC,UAAW,GACXC,cAAc,EACdC,cAAc,EACdC,eAAe,EACfC,YAAa,sBAEfnC,KAAKoC,SAASpC,KAAKa,cAEnBb,KAAKqC,cAAgB,IAAIvB,yBAAuB,CAC5ClB,KAAM,wBACNH,MAAO,iBACPsB,SAAU,6HACVC,WAAY,UACZC,UAAW,UACbC,YAAa,OACbC,WAAY,EACZC,YAAa,EACbC,MAAO,CAAC,sBACRC,QAAS,SACTC,UAAW,MACXC,aAAa,EACbC,WAAW,EACXC,OAAQ,GACRC,WAAY,OACZC,MAAO,2BACPC,MAAM,EACNC,UAAW,2BACXC,UAAW,GACXC,cAAc,EACdG,YAAa,uBAEfnC,KAAKoC,SAASpC,KAAKqC,eAEnBrC,KAAKsC,YAAc,IAAIxB,yBAAuB,CAC1ClB,KAAM,sBACNH,MAAO,eACPsB,SAAU,sHACVC,WAAY,UACZC,UAAW,UACbsB,SAAU,qBACVpB,WAAY,EACZC,YAAa,EACbF,YAAa,OACbG,MAAO,CAAC,aACRC,QAAS,SACTC,UAAW,OACXC,aAAa,EACbC,WAAW,EACXC,OAAQ,GACRC,WAAY,OACZC,MAAO,2BACPC,MAAM,EACNC,UAAW,2BACXC,UAAW,GACXC,cAAc,EACdG,YAAa,qBAEfnC,KAAKoC,SAASpC,KAAKsC,aAEnBtC,KAAKwC,iBAAmB,IAAI1B,yBAAuB,CAC/ClB,KAAM,kCACNH,MAAO,YACPsB,SAAU,sHACVC,WAAY,UACZC,UAAW,UACbsB,SAAU,qBACVpB,WAAY,EACZC,YAAa,EACbF,YAAa,OACbG,MAAO,CAAC,aACRC,QAAS,WACTC,UAAW,OACXC,aAAa,EACbC,WAAW,EACXC,OAAQ,GACRC,WAAY,OACZC,MAAO,2BACPC,MAAM,EACNC,UAAW,2BACXC,UAAW,GACXC,cAAc,EACdG,YAAa,0BAEfnC,KAAKoC,SAASpC,KAAKwC,iBACrB,CAEA,oBAAMC,GAEN,CAEA,uBAAA/B,GAMA,CAEA,gBAAMgC,GACF,IACE,MAAMC,QAAiB3C,KAAK4C,SAASC,KAAKC,IAAI,yBAA0B,CACtEzB,MAAO,CAAC,cAAe,gBACvBC,QAAS,WAEPqB,EAASI,SAAWJ,EAASK,KAAKC,QACpCC,OAAOC,OAAOnD,KAAKC,MAAO0C,EAASK,KAAKA,MAEtChD,KAAKqC,gBACLrC,KAAKqC,cAAce,OAAO/C,aAAeL,KAAKC,MAAMI,cAAgB,EACpEL,KAAKqC,cAAce,OAAOC,UAE1BrD,KAAKa,eACLb,KAAKa,aAAauC,OAAOjD,YAAcH,KAAKC,MAAME,aAAe,EACjEH,KAAKa,aAAauC,OAAOC,SAE/B,OAASC,GACPC,QAAQD,MAAM,8BAA+BA,EAC/C,CACJ,CAEA,eAAME,GAEJ,IACE,MAAMb,QAAiB3C,KAAK4C,SAASC,KAAKC,IAAI,sBAAuB,CACnEzB,MAAO,CAAC,eAAgB,oBAAqB,YACzC,YAAa,aAAc,sBAC/BC,QAAS,SACTJ,YAAa,SAEXyB,EAASI,SAAWJ,EAASK,KAAKC,SACpCC,OAAOC,OAAOnD,KAAKC,MAAO0C,EAASK,KAAKA,MACxChD,KAAKU,0BAET,OAAS4C,GACPC,QAAQD,MAAM,8BAA+BA,EAC/C,CACF,EAGa,MAAMG,2BAA2BC,EAAAA,KAC9C,WAAApE,CAAYC,EAAU,IACpBC,MAAM,IACDD,EACHE,MAAO,kBACPM,UAAW,yBAIbC,KAAK2D,UAAY,kBACjB3D,KAAK4D,aAAe,wCACtB,CAEA,iBAAMjD,GACJ,MAAO,+5EA2DT,CAEA,YAAMC,GAEJZ,KAAK6D,4BAAA,IAAkBC,MAAOC,iBAG9B/D,KAAKgE,WAAa,IAAI5E,gBAAgB,CACpC+C,YAAa,iBAEfnC,KAAKoC,SAASpC,KAAKgE,YAGnBhE,KAAKiE,gBAAkB,IAAIC,eAAa,CACtCzE,MAAO,kDACP8C,SAAU,qBACVb,OAAQ,IACRR,YAAa,QACbG,MAAO,CAAC,YAAa,cACrBC,QAAS,SACTC,UAAW,OACXW,eAAe,EACfiC,MAAO,CACLxE,MAAO,QACPyE,aAAa,GAEfC,QAAS,CACPC,EAAG,UAELnC,YAAa,sBAEfnC,KAAKoC,SAASpC,KAAKiE,gBAErB,CAGA,wBAAMM,CAAmBC,EAAOC,GAC9B,IAEE,MAAMC,EAASD,GAAWD,GAAOG,eAAiB,KAC5C/E,EAAO8E,GAAQE,gBAAgB,KACrChF,GAAMiF,UAAUC,IAAI,WAChBJ,IAAQA,EAAOK,UAAW,GAG9B,MAAMC,EAAW,CACfhF,KAAKgE,YAAYtB,aACjB1C,KAAKiE,iBAAiBgB,WACtBC,OAAOC,eAEHC,QAAQC,WAAWL,GAIzBhF,KAAK6D,4BAAA,IAAkBC,MAAOC,iBAG9B,MAAMuB,EAAWtF,KAAK4C,UAAU2C,OAC5BD,GACFA,EAASE,KAAK,4BAA6B,CACzCC,KAAMzF,KACN0F,UAAW1F,KAAK6D,aAItB,OAASP,GACPC,QAAQD,MAAM,+BAAgCA,GAE9C,MAAMqC,EAAQ3F,KAAKyE,QAAQG,cAAc,kBACrCe,IACFA,EAAM5F,UAAY,8BAClB4F,EAAMC,UAAY,oQAUlBC,WAAW,KACTF,EAAM5F,UAAY,+BAClB4F,EAAMC,UAAY,4QAK6B5F,KAAK6D,4EAInD,KAEP,CAAA,QAEE,MAAMjE,EAAO6E,EAAQG,cAAc,KACnChF,GAAMiF,UAAUiB,OAAO,WACnBpB,gBAAeK,UAAW,EAChC,CACF,CAEA,2BAAMgB,CAAsBvB,EAAOC,GACjC,UAEQzE,KAAKiE,iBAAiB+B,OAAO,QAGnC,MAAMV,EAAWtF,KAAK4C,UAAU2C,OAC5BD,GACFA,EAASE,KAAK,yBAA0B,CACtCC,KAAMzF,KACNiG,OAAQ,CAAC,gBAIf,OAAS3C,GACPC,QAAQD,MAAM,4BAA6BA,EAC7C,CACF,CAEA,wBAAM4C,CAAmB1B,EAAOC,GAE9B,MAAM0B,EAASnG,KAAK4C,UAAUuD,OAC1BA,GACFA,EAAOC,WAAW,gBAEtB,CAEA,8BAAMC,CAAyB7B,EAAOC,GAEpC,MAAM0B,EAASnG,KAAK4C,UAAUuD,OAC1BA,GACFA,EAAOC,WAAW,uBAEtB,CAGA,sBAAME,GACJ,OAAOtG,KAAKuE,mBAAmB,KAAM,KAAM,CAAEQ,UAAU,EAAOH,cAAe,IAAM,MACrF,CAEA,SAAA2B,GACE,MAAO,CACLC,WAAYxG,KAAKiE,gBAErB,CAEA,QAAAwC,GACE,OAAOzG,KAAKgE,YAAY/D,OAAS,CAAA,CACnC,CAEA,mBAAMyG,GACJ1G,KAAKgE,YAAYtB,YACnB,EC9aF,MAAMiE,iBAAiBtH,EAAAA,KACnB,WAAAC,CAAYC,EAAU,IAClBC,MAAM,CACFO,UAAW,eACRR,IAIPS,KAAK4G,MAAQrH,EAAQqH,OAAS,IAAIC,EAAAA,KAAKtH,EAAQyD,MAAQ,IAGvDhD,KAAK8G,QAAU,KACf9G,KAAK+G,YAAc,KACnB/G,KAAKgH,WAAa,KAClBhH,KAAKiH,WAAa,KAClBjH,KAAKkH,SAAW,KAGhBlH,KAAKmH,SAAW,2RAQpB,CAEA,YAAMvG,GAEFZ,KAAKoD,OAAS,IAAI/D,OAAK,CACnB8C,YAAa,cACbgF,SAAU,+nDA8BdnH,KAAKoD,OAAOgE,SAASpH,KAAK4G,OAC1B5G,KAAKoC,SAASpC,KAAKoD,QAEnBpD,KAAK+G,YAAc,IAAIM,UAAS,CAC5BT,MAAO5G,KAAK4G,MACZ7G,UAAW,MACXuH,iBAAiB,EACjBC,OAAQC,EAAAA,aAAaC,QAAQF,SAGjCvH,KAAK0H,UAAY,IAAIC,WAAS,CAC1BJ,OAAQV,EAAAA,KAAKe,kBACbhB,MAAO5G,KAAK4G,MACZiB,oBAAoB,IAIxB,MAAMC,EAAoB,IAAIC,aAAW,CACrCC,OAAQ,CAAEC,KAAMjI,KAAK4G,MAAMsB,IAAI,MAAOC,KAAM,KAEhDnI,KAAKgH,WAAa,IAAIoB,YAAU,CAC5BC,WAAYP,EACZQ,oBAAqB,CAAC,QACtBC,QAAS,CACL,CACIC,IAAK,UACL7I,MAAO,cACP8I,UAAW,OACXC,UAAU,GAEd,CACIF,IAAK,aACL7I,MAAO,aACP+I,UAAU,GAEd,CACIF,IAAK,yBACL7I,MAAO,kBAOnB,MAAMgJ,EAAmB,IAAIC,oBAAkB,CAC3CZ,OAAQ,CACJG,KAAM,EACNU,WAAY,eACZC,SAAU9I,KAAK4G,MAAMsB,IAAI,SAGjClI,KAAKiH,WAAa,IAAImB,YAAU,CAC5BC,WAAYM,EACZL,oBAAqB,CAAC,aAAc,YACpCC,QAAS,CACL,CACIC,IAAK,KACL7I,MAAO,KACP+I,UAAU,EACVK,MAAO,QAEX,CACIP,IAAK,UACL7I,MAAO,OACP8I,UAAW,WACXC,UAAU,EACVK,MAAO,SAEX,CACIP,IAAK,iBACL7I,MAAO,YAEX,CACI6I,IAAK,QACL7I,MAAO,YAKnB,MAAMqJ,EAAc,IAAIC,iBAAe,CACnCjB,OAAQ,CACJG,KAAM,EACNF,KAAMjI,KAAK4G,MAAMsB,IAAI,SAG7BlI,KAAKkJ,YAAc,IAAId,YAAU,CAC7BC,WAAYW,EACZV,oBAAqB,CAAC,QACtBC,QAAS,CACL,CAAEC,IAAK,2BAA4B7I,MAAO,YAAa+I,UAAU,GACjE,CAAEF,IAAK,gCAAiC7I,MAAO,UAAW8I,UAAW,gBACrE,CAAED,IAAK,wBAAyB7I,MAAO,KAAM8I,UAAW,gBACxD,CAAED,IAAK,aAAc7I,MAAO,aAAc8I,UAAW,kBACrD,CAAED,IAAK,YAAa7I,MAAO,YAAa8I,UAAW,mBAEvDN,KAAM,IAIV,MAAMgB,EAAgB,IAAIC,yBAAuB,CAC7CpB,OAAQ,CACJG,KAAM,EACNF,KAAMjI,KAAK4G,MAAMsB,IAAI,SAG7BlI,KAAKqJ,cAAgB,IAAIjB,YAAU,CAC/BC,WAAYc,EAEZb,oBAAqB,CAAC,QACtBC,QAAS,CACL,CAAEC,IAAK,cAAe7I,MAAO,SAAUwH,SAAU,qGAAsGuB,UAAU,GACjK,CAAEF,IAAK,mBAAoB7I,MAAO,OAAQ8I,UAAW,gBACrD,CAAED,IAAK,qBAAsB7I,MAAO,SAAU8I,UAAW,gBACzD,CAAED,IAAK,2BAA4B7I,MAAO,UAAW8I,UAAW,gBAChE,CAAED,IAAK,YAAa7I,MAAO,YAAa8I,UAAW,mBAEvDN,KAAM,IAGV,MAAMmB,EAAc,IAAIC,iBAAe,CACnCvB,OAAQ,CACJG,KAAM,EACNF,KAAMjI,KAAK4G,MAAMsB,IAAI,SAG7BlI,KAAKwJ,gBAAkB,IAAIpB,YAAU,CACjCC,WAAYiB,EACZhB,oBAAqB,CAAC,QACtBC,QAAS,CACL,CAAEC,IAAK,2BAA4B7I,MAAO,YAAa+I,UAAU,GACjE,CAAEF,IAAK,gCAAiC7I,MAAO,UAAW8I,UAAW,gBACrE,CAAED,IAAK,wBAAyB7I,MAAO,KAAM8I,UAAW,gBACxD,CAAED,IAAK,aAAc7I,MAAO,aAAc8I,UAAW,kBACrD,CAAED,IAAK,YAAa7I,MAAO,YAAa8I,UAAW,mBAEvDN,KAAM,IAIV,MAAMsB,EAAiB,IAAIC,UAAQ,CAC/B1B,OAAQ,CACJG,KAAM,EACNU,WAAY,eACZC,SAAU9I,KAAK4G,MAAMsB,IAAI,SAGjClI,KAAKkH,SAAW,IAAIkB,YAAU,CAC1BC,WAAYoB,EACZE,YAAa,YACbrB,oBAAqB,CAAC,aAAc,YACpCC,QAAS,CACL,CACIC,IAAK,UACL7I,MAAO,YACP+I,UAAU,EACVD,UAAW,iBACXvD,OAAQ,CACJ0E,KAAM,UACNC,KAAM,YACNC,UAAW,WACXC,QAAS,SACTC,UAAW,WACXrK,MAAO,aACPsK,OAAQ,aACRC,cAAe,eACfC,UAAW,SAGnB,CACI3B,IAAK,QACL7I,MAAO,QACP+I,UAAU,EACVxD,OAAQ,CACJ2E,KAAM,SACNtK,QAAS,CACL,CAAE6K,MAAO,OAAQzK,MAAO,QACxB,CAAEyK,MAAO,UAAWzK,MAAO,WAC3B,CAAEyK,MAAO,QAASzK,MAAO,YAIrC,CACI6I,IAAK,OACL7I,MAAO,OACPuF,OAAQ,CACJ2E,KAAM,SAGd,CACID,KAAM,MACNjK,MAAO,UAMnB,MAAM0K,EAAqB,IAAIX,UAAQ,CACnC1B,OAAQ,CACJG,KAAM,EACNmC,IAAKtK,KAAK4G,MAAMsB,IAAI,SAG5BlI,KAAKuK,aAAe,IAAInC,YAAU,CAC9BC,WAAYgC,EACZ/B,oBAAqB,CAAC,OACtBqB,YAAa,YACbpB,QAAS,CACL,CACIC,IAAK,UACL7I,MAAO,YACP+I,UAAU,EACVD,UAAW,iBACXvD,OAAQ,CACJ0E,KAAM,UACNC,KAAM,YACNC,UAAW,WACXC,QAAS,SACTC,UAAW,WACXrK,MAAO,aACPsK,OAAQ,aACRC,cAAe,eACfC,UAAW,SAGnB,CACI3B,IAAK,QACL7I,MAAO,QACP+I,UAAU,EACVxD,OAAQ,CACJ2E,KAAM,SACNtK,QAAS,CACL,CAAE6K,MAAO,OAAQzK,MAAO,QACxB,CAAEyK,MAAO,UAAWzK,MAAO,WAC3B,CAAEyK,MAAO,QAASzK,MAAO,YAIrC,CACI6I,IAAK,OACL7I,MAAO,OACPuF,OAAQ,CACJ2E,KAAM,SAGd,CACID,KAAM,OACNjK,MAAO,WAMnBK,KAAK8G,QAAU,IAAI0D,UAAQ,CACvBC,KAAM,CACFC,QAAW1K,KAAK+G,YAChB4D,YAAe3K,KAAK0H,UACpBkD,OAAU5K,KAAKgH,WACf6D,OAAU7K,KAAKiH,WACf6D,KAAQ9K,KAAKkH,SACb6D,SAAY/K,KAAKuK,aACjBS,QAAWhL,KAAKkJ,YAChB+B,UAAajL,KAAKqJ,cAClB,eAAgBrJ,KAAKwJ,iBAEzB0B,UAAW,UACX/I,YAAa,YACbgJ,kBAAkB,EAClBC,cAAe,SACfC,SAAU,MAIdrL,KAAKoC,SAASpC,KAAK8G,SAEnB,MAAMwE,EAAW,IAAIC,cAAY,CAC7BpJ,YAAa,oBACbpC,UAAW,yCACXyL,QAASxL,KAAK4G,MACd6E,OAAQ,CACJ7L,KAAM,yBACN8L,MAAO,CACH,CAAE/L,MAAO,YAAaE,OAAQ,YAAaD,KAAM,aACjD,CAAED,MAAO,iBAAkBE,OAAQ,iBAAkBD,KAAM,UAC3D,CAAEiK,KAAM,WACR7J,KAAK4G,MAAMsB,IAAI,aACT,CAAEvI,MAAO,kBAAmBE,OAAQ,kBAAmBD,KAAM,kBAC7D,CAAED,MAAO,gBAAiBE,OAAQ,gBAAiBD,KAAM,uBAI3EI,KAAKoC,SAASkJ,EAClB,CAEA,sBAAMK,GACF,IAAIC,EAAYC,EAAAA,UAAUC,WACPC,EAAAA,OAAOC,cAAc,CACpCvM,MAAO,WAAWO,KAAK4G,MAAMqF,MAAMjM,KAAKT,QAAQ2M,YAChDtF,MAAO5G,KAAK4G,MACZuF,WAAYP,KAGZ5L,KAAKqD,QAEb,CACA,2BAAM+I,GAA8D,CAEpE,4BAAMC,SACgBN,SAAOO,QAAQ,sDAEvBtM,KAAK4G,MAAM2F,KAAK,CAAEC,WAAW,IACnCxM,KAAK4C,SAAS6J,MAAM1J,QAAQ,mBAE5B/C,KAAK4C,SAAS6J,MAAMnJ,MAAM,wBAElC,CAEA,0BAAMoJ,SACgBX,SAAOO,QAAQ,qDAEvBtM,KAAK4G,MAAM2F,KAAK,CAAEC,WAAW,IACnCxM,KAAK4C,SAAS6J,MAAM1J,QAAQ,mBAE5B/C,KAAK4C,SAAS6J,MAAMnJ,MAAM,uBAElC,CAGA,uBAAMqJ,CAAkB9M,EAAQ2E,EAAOC,GACnBA,EAAQmI,aAAa,UAGzC,CAEA,6BAAMC,CAAwBhN,EAAQ2E,EAAOC,GACzBA,EAAQmI,aAAa,UAGzC,CAEA,uBAAME,CAAkBjN,EAAQ2E,EAAOC,GACnBA,EAAQmI,aAAa,UAGzC,CAEA,qBAAMG,CAAgBlN,EAAQ2E,EAAOC,GACnBA,EAAQmI,aAAa,UAGvC,CAEA,aAAMI,CAAQC,GACNjN,KAAK8G,eACC9G,KAAK8G,QAAQkG,QAAQC,EAEnC,CAEA,YAAAC,GACI,OAAOlN,KAAK8G,QAAU9G,KAAK8G,QAAQoG,eAAiB,IACxD,CAEA,cAAAC,GAEA,CAGA,aAAOC,CAAO7N,EAAU,IACpB,OAAO,IAAIoH,SAASpH,EACxB,EAGJsH,EAAAA,KAAKwG,WAAa1G,SC7blB,MAAM2G,sBAAsBC,EAAAA,UACxB,WAAAjO,CAAYC,EAAU,IAClBC,MAAM,IACCD,EACHqK,KAAM,cACN4D,SAAU,eACVrH,OAAQ,cACRsH,WAAYC,EAAAA,SAEZC,kBAAmB,CAAEvK,QAAQ,GAE7BwK,aAAc,CACVC,KAAM,iBACNrB,WAAW,GAIfjE,QAAS,CACL,CACIC,IAAK,KACL7I,MAAO,KACP+I,UAAU,EACVoF,MAAO,cAQX,CACItF,IAAK,sCACL7I,MAAO,gBAEX,CACIA,MAAO,OACP6I,IAAK,2BACLrB,SAAU,m5BAOVuB,UAAU,GAEd,CACIF,IAAK,QACL7I,MAAO,QACPoO,WAAY,KACZhO,UAAW,mBAQf,CACIyI,IAAK,gBACL7I,MAAO,gBACP8I,UAAW,WACX1I,UAAW,oBAInBiO,QAAS,CACL,CACIxF,IAAK,YACL7I,MAAO,SACPkK,KAAM,UACNoE,cAAc,GAElB,CACIzF,IAAK,QACL7I,MAAO,QACPkK,KAAM,OACNoE,aAAc,IAElB,CACIzF,IAAK,WACL7I,MAAO,WACPkK,KAAM,OACNoE,aAAc,IAElB,CACIzF,IAAK,wBACL7I,MAAO,aACPkK,KAAM,OACNoE,aAAc,IAElB,CACIzF,IAAK,gBACLqB,KAAM,YACNC,UAAW,WACXC,QAAS,SACTC,UAAW,WACXrK,MAAO,aACPsK,OAAQ,aACRC,cAAe,eACfC,UAAW,SAKnB+D,YAAY,EACZC,YAAY,EACZzF,UAAU,EACV0F,YAAY,EACZC,WAAW,EAGXC,aAAa,EACbC,SAAS,EACTC,YAAY,EAGZC,aAAc,oDAGdC,YAAa,CACT,CACI9O,KAAM,YACNC,OAAQ,OACRF,MAAO,gBAEX,CACIC,KAAM,kBACNC,OAAQ,mBACRF,MAAO,oBAEX,CACIC,KAAM,YACNC,OAAQ,kBACRF,MAAO,mBAEX,CAAEwK,WAAW,GACb,CACIvK,KAAM,cACNC,OAAQ,cACRF,MAAO,gBAKfgP,aAAc,CACVC,SAAS,EACTC,UAAU,EACVC,OAAO,EACPC,YAAY,IAGxB,CAEA,6BAAMC,CAAwBxK,EAAOC,GAEjCD,EAAMyK,iBACN,MAAMC,EAAOlP,KAAKqI,WAAWH,IAAIzD,EAAQ0K,QAAQlD,UAC5BF,EAAAA,OAAOC,cAAc,CACxCpF,MAAOsI,EACP/G,KAAM,KACN1I,MAAO,yBAAyByP,EAAKE,EAAEC,YACvC9H,OAAQsE,EAAAA,UAAUlC,YAAYpC,QAEpC,CAEA,4BAAM+H,CAAuB9K,EAAOC,GAEhC,MAAMyK,EAAOlP,KAAKqI,WAAWH,IAAIzD,EAAQ0K,QAAQlD,IAC3CjJ,QAAa+I,EAAAA,OAAOwD,SAAS,CAC/B9P,MAAO,wBAAwByP,EAAKE,EAAEC,YACtC9H,OAAQ,CACJ,CACIsC,KAAM,OACND,KAAM,WACNQ,MAAO8E,EAAKhH,IAAI,UAAYgH,EAAKhH,IAAI,YACrCsH,WAAY,CACRC,aAAc,WACdC,SAAU,WACVC,SAAU,KACVC,MAAO,wEAGf,CACIhG,KAAM,eACNjK,MAAO,eACPkK,KAAM,WACNgG,cAAe,MACfC,UAAU,EACVC,YAAY,EACZP,WAAY,CACRC,aAAc,oBAM9B,GAAIzM,GAAQA,EAAKgN,aAAc,CAG3B,GADeC,EAAAA,UAAUC,sBAAsBlN,EAAKgN,cACzCG,MAAQ,EAGf,OAFAnQ,KAAK4C,SAAS6J,MAAMnJ,MAAM,iJACpBtD,KAAKsP,uBAAuB9K,EAAOC,IAG7C,MAAM2L,QAAalB,EAAK3C,KAAK,CAACyD,aAAchN,EAAKgN,eAC5ChQ,KAAKqQ,iBAAiBD,UACjBpQ,KAAKsP,uBAAuB9K,EAAOC,EAEjD,CACJ,CAEA,gBAAA4L,CAAiBD,GACb,OAAIA,EAAKrN,SACL/C,KAAK4C,SAAS6J,MAAM1J,QAAQ,kCACrB,IAEHqN,EAAKpN,MAAQoN,EAAKpN,KAAKM,MACvBtD,KAAK4C,SAAS6J,MAAMnJ,MAAM8M,EAAKpN,KAAKM,OAEpCtD,KAAK4C,SAAS6J,MAAMnJ,MAAM,8BAG3B,EACX,CAEA,wBAAMgN,CAAmB9L,EAAOC,GAC5B,MAAMyK,EAAOlP,KAAKqI,WAAWH,IAAIzD,EAAQ0K,QAAQlD,IAC3CmE,QAAalB,EAAK3C,KAAK,CAACgE,aAAa,IAC3C,OAAIH,EAAKrN,SACL/C,KAAK4C,SAAS6J,MAAM1J,QAAQ,6BACrB,IAEHqN,EAAKpN,MAAQoN,EAAKpN,KAAKM,MACvBtD,KAAK4C,SAAS6J,MAAMnJ,MAAM8M,EAAKpN,KAAKM,OAEpCtD,KAAK4C,SAAS6J,MAAMnJ,MAAM,0BAG3B,EACX,ECjPJ,MAAMkN,mBAAmBnR,EAAAA,KACrB,WAAAC,CAAYC,EAAU,IAClBC,MAAM,CACFO,UAAW,iBACRR,IAGPS,KAAK4G,MAAQrH,EAAQqH,OAAS,IAAI6J,EAAAA,OAAOlR,EAAQyD,MAAQ,IAEzDhD,KAAKmH,SAAW,4sCAyBpB,CAEA,YAAMvG,GAEFZ,KAAK0Q,aAAe,IAAIrJ,UAAS,CAC7BT,MAAO5G,KAAK4G,MACZ7G,UAAW,MACXwI,QAAS,EACThB,OAAQ,CACJ,CAAEqC,KAAM,KAAMjK,MAAO,iBACrB,CAAEiK,KAAM,UAAWjK,MAAO,WAC1B,CAAEiK,KAAM,oBAAqBjK,MAAO,aACpC,CAAEiK,KAAM,aAAcjK,MAAO,cAC7B,CAAEiK,KAAM,WAAYjK,MAAO,YAC3B,CAAEiK,KAAM,aAAcjK,MAAO,cAC7B,CAAEiK,KAAM,OAAQjK,MAAO,QACvB,CAAEiK,KAAM,SAAUjK,MAAO,UACzB,CAAEiK,KAAM,UAAWjK,MAAO,cAAesK,OAAQ,eAIzDjK,KAAK8G,QAAU,IAAI0D,UAAQ,CACvBrI,YAAa,cACbsI,KAAM,CACFkG,SAAY3Q,KAAK0Q,cAErBxF,UAAW,aAEflL,KAAKoC,SAASpC,KAAK8G,SAGnB,MAAM8J,EAAa,IAAIrF,cAAY,CAC/BpJ,YAAa,sBACbpC,UAAW,yCACXyL,QAASxL,KAAK4G,MACd6E,OAAQ,CACJ7L,KAAM,yBACN8L,MAAO,CACH,CAAE/L,MAAO,kBAAmBE,OAAQ,kBAAmBD,KAAM,aAC7D,CAAEiK,KAAM,WACR,CAAElK,MAAO,YAAaE,OAAQ,YAAaD,KAAM,aACjD,CAAED,MAAO,aAAcE,OAAQ,aAAcD,KAAM,gBACnD,CAAEiK,KAAM,WACR,CAAElK,MAAO,oBAAqBE,OAAQ,gBAAiBD,KAAM,WAAYiR,QAAQ,OAI7F7Q,KAAKoC,SAASwO,EAClB,CAEA,4BAAME,SACiB/E,EAAAA,OAAOC,cAAc,CACpCvM,MAAO,kBACPmH,MAAO5G,KAAK4G,MACZuF,WAAY4E,EAAAA,YAAYjF,QAGxB9L,KAAKqD,QAEb,CAEA,sBAAM2N,GAC6BhR,KAAK4G,MAAMsB,IAAI,UAClD,CAEA,uBAAMyE,GAC8B3M,KAAK4G,MAAMsB,IAAI,WACnD,CAEA,0BAAM+I,SACsBlF,EAAAA,OAAOO,QAC3B,mCAAmCtM,KAAK4G,MAAMsB,IAAI,6BAA6BlI,KAAK4G,MAAMsB,IAAI,iBAC9F,2BAGmBlI,KAAK4G,MAAMsK,WACrBnO,SACL/C,KAAKwF,KAAK,iBAAkB,CAAEoB,MAAO5G,KAAK4G,OAGtD,EAGJ6J,EAAAA,OAAOpD,WAAamD,WCvHpB,MAAMW,wBAAwB5D,EAAAA,UAC1B,WAAAjO,CAAYC,EAAU,IAClBC,MAAM,IACCD,EACHqK,KAAM,gBACN4D,SAAU,iBACVrH,OAAQ,gBACRsH,WAAY1F,EAAAA,WAEZqJ,SAAUL,EAAAA,YAAYjF,KACtBuF,cAAeb,WAEf7C,kBAAmB,CACfvK,QAAQ,EACR+E,KAAM,MAIVI,QAAS,CACL,CACIC,IAAK,KACL7I,MAAO,KACPoJ,MAAO,OACPL,UAAU,EACVoF,MAAO,cAEX,CACItF,IAAK,oBACL7I,MAAO,OACP8I,UAAW,2BAEf,CACID,IAAK,aACL7I,MAAO,QACP8I,UAAW,uBAEf,CACID,IAAK,aACL7I,MAAO,QACP8I,UAAW,4BAEf,CACID,IAAK,OACL7I,MAAO,OACP8I,UAAW,SAEf,CACID,IAAK,SACL7I,MAAO,SACP8I,UAAW,SAEf,CACID,IAAK,UACL7I,MAAO,QACP8I,UAAW,mBAKnByF,YAAY,EACZC,YAAY,EACZzF,UAAU,EACV0F,YAAY,EACZC,WAAW,EAGXC,aAAa,EACbC,SAAS,EACTC,YAAY,EAGZC,aAAc,+DAGd6C,iBAAkB,MAClBC,aAAc,CACV,CAAE5R,MAAO,SAAUC,KAAM,oBAAqBC,OAAQ,gBACtD,CAAEF,MAAO,SAAUC,KAAM,iBAAkBC,OAAQ,gBACnD,CAAEF,MAAO,cAAeC,KAAM,oBAAqBC,OAAQ,cAC3D,CAAEF,MAAO,WAAYC,KAAM,qBAAsBC,OAAQ,kBACzD,CAAEF,MAAO,aAAcC,KAAM,iBAAkBC,OAAQ,qBAI3D8O,aAAc,CACVC,SAAS,EACTC,UAAU,EACVC,OAAO,EACPC,YAAY,IAGxB,ECjFJ,MAAMyC,kBAAkBnS,EAAAA,KACpB,WAAAC,CAAYC,EAAU,IAClBC,MAAM,CACFO,UAAW,gBACRR,IAIPS,KAAK4G,MAAQrH,EAAQqH,OAAS,IAAI6K,EAAAA,MAAMlS,EAAQyD,MAAQ,IAGxDhD,KAAK8G,QAAU,KACf9G,KAAK0R,YAAc,KACnB1R,KAAK2R,aAAe,KACpB3R,KAAKkH,SAAW,KAGhBlH,KAAKmH,SAAW,knGAqDpB,CAEA,YAAMvG,GAEF,MAAMkH,EAAoB,IAAIC,aAAW,CACrCC,OAAQ,CAAE4J,MAAO5R,KAAK4G,MAAMsB,IAAI,MAAOC,KAAM,KAEjDnI,KAAK0R,YAAc,IAAItJ,YAAU,CAC7BC,WAAYP,EACZQ,oBAAqB,CAAC,SACtBC,QAAS,CACL,CAAEC,IAAK,oBAAqB7I,MAAO,OAAQ+I,UAAU,GACrD,CAAEF,IAAK,aAAc7I,MAAO,QAAS+I,UAAU,GAC/C,CAAEF,IAAK,UAAW7I,MAAO,cAAe8I,UAAW,OAAQC,UAAU,IAEzE6F,SAAS,EACTsD,eAAgB,SAChBC,MAAOC,MAAOvN,IACVxE,KAAKgS,cAAcxN,MAK3B,MAAMyN,EAAqB,IAAIC,YAAU,CACrClK,OAAQ,CAAEmK,OAAQnS,KAAK4G,MAAMsB,IAAI,MAAOC,KAAM,KAElDnI,KAAK2R,aAAe,IAAIvJ,YAAU,CAC9BC,WAAY4J,EACZ3J,oBAAqB,CAAC,UACtBC,QAAS,CACL,CAAEC,IAAK,OAAQ7I,MAAO,OAAQ+I,UAAU,GACxC,CAAEF,IAAK,OAAQ7I,MAAO,OAAQ8I,UAAW,SACzC,CAAED,IAAK,UAAW7I,MAAO,UAAW8I,UAAW,OAAQC,UAAU,IAGrE0J,eAAgB,CACZ,CACIzS,MAAO,eACPC,KAAM,oBACNC,OAAQ,eACRE,UAAW,kBAMvB,MAAM0J,EAAiB,IAAIC,UAAQ,CAC/B1B,OAAQ,CACJG,KAAM,EACNU,WAAY,gBACZC,SAAU9I,KAAK4G,MAAMsB,IAAI,SAGjClI,KAAKkH,SAAW,IAAIkB,YAAU,CAC1BC,WAAYoB,EACZE,YAAa,YACbrB,oBAAqB,CAAC,aAAc,YACpCC,QAAS,CACL,CAAEC,IAAK,UAAW7I,MAAO,YAAa+I,UAAU,EAAMD,UAAW,kBACjE,CAAED,IAAK,QAAS7I,MAAO,QAAS+I,UAAU,EAAMD,UAAW,SAC3D,CAAED,IAAK,OAAQ7I,MAAO,QACtB,CAAE6I,IAAK,MAAO7I,MAAO,UAK7BK,KAAK8G,QAAU,IAAI0D,UAAQ,CACvBC,KAAM,CACF4H,QAAWrS,KAAK0R,YAChBY,SAAYtS,KAAK2R,aACjB7G,KAAQ9K,KAAKkH,UAEjBgE,UAAW,UACX/I,YAAa,eAEjBnC,KAAKoC,SAASpC,KAAK8G,SAGnB,MAAMyL,EAAY,IAAIhH,cAAY,CAC9BpJ,YAAa,qBACbpC,UAAW,yCACXyL,QAASxL,KAAK4G,MACd6E,OAAQ,CACJ7L,KAAM,yBACN8L,MAAO,CACH,CAAE/L,MAAO,aAAcE,OAAQ,aAAcD,KAAM,aACnD,CAAED,MAAO,aAAcE,OAAQ,aAAcD,KAAM,kBACnD,CAAED,MAAO,kBAAmBE,OAAQ,kBAAmBD,KAAM,gBAC7D,CAAEiK,KAAM,WACR7J,KAAK4G,MAAMsB,IAAI,aACT,CAAEvI,MAAO,mBAAoBE,OAAQ,mBAAoBD,KAAM,eAC/D,CAAED,MAAO,iBAAkBE,OAAQ,iBAAkBD,KAAM,uBAI7EI,KAAKoC,SAASmQ,EAClB,CAEA,uBAAMC,SACiBzG,EAAAA,OAAOC,cAAc,CACpCvM,MAAO,gBAAgBO,KAAK4G,MAAMsB,IAAI,UACtCtB,MAAO5G,KAAK4G,MACZuB,KAAM,KACNgE,WAAYsG,EAAAA,WAAWC,YAGvB1S,KAAKqD,QAEb,CAGA,uBAAMsP,GAA+D3S,KAAK4G,MAAMqF,EAAK,CACrF,2BAAM2G,GAAkE5S,KAAK4G,MAAMqF,EAAK,CACxF,6BAAM4G,GAAkE7S,KAAK4G,MAAMqF,EAAK,CACxF,2BAAM6G,GAA8D9S,KAAK4G,MAAMqF,EAAK,CAEpF,wBAAM8G,CAAmBvO,EAAOC,GAC5B,MAAMuO,EAAWvO,EAAQ0K,QAAQlD,GAIjCjM,KAAKwF,KAAK,oBAAqB,CAAEyN,QAASD,GAC9C,CAEA,mBAAMhB,CAAcxN,GAChBA,EAAMyK,iBACNzK,EAAM0O,kBACN,MAAMC,EAAMnT,KAAK4C,SACXI,QAAamQ,EAAI5D,SAAS,CAC5B9P,MAAO,kBAAoBO,KAAK4G,MAAMsB,IAAI,QAC1CX,OAAQ,CACJ,CACIsC,KAAM,QACND,KAAM,QACNjK,MAAO,QACPmQ,UAAU,EACVvH,QAAS,OAKrB,GAAIvF,GAAQA,EAAKoQ,MAAO,CACpBD,EAAIE,cACJ,MAAMjD,QAAa+C,EAAItQ,KAAKyQ,KAAK,2BAA4B,CACzD1B,MAAO5R,KAAK4G,MAAMqF,GAClBmH,MAAOpQ,EAAKoQ,QAEhBD,EAAII,cACAnD,EAAKrN,SACLoQ,EAAI1G,MAAM1J,QAAQ,6BAClB/C,KAAK0R,YAAYrJ,WAAWmL,SAE5BL,EAAI1G,MAAMnJ,MAAM,wBAExB,CACJ,CAGA,yBAAMmQ,GACF,MAAMzQ,QAAa+I,EAAAA,OAAOwD,SAAS,CAC/B9P,MAAO,iBACP8H,OAAQ,CACJ,CACIqC,KAAM,YACNC,KAAM,wBACNiG,UAAU,EACVrC,WAAYyE,EAAAA,UACZwB,WAAY,OACZC,aAAc,gOAMdC,WAAY,KACZC,cAAc,EACdC,kBAAmB,mBACnBC,cAAe,CACXvH,WAAW,EACXrE,KAAM,IACNyJ,MAAO5R,KAAK4G,MAAMqF,QAK9BjJ,IACAO,QAAQyQ,KAAKhR,GACbhD,KAAK4C,SAAS6J,MAAMwH,QAAQ,4BAGpC,EAIJxC,EAAAA,MAAMpE,WAAamE,UClRnB,MAAM0C,uBAAuB3G,EAAAA,UACzB,WAAAjO,CAAYC,EAAU,IAClBC,MAAM,IACCD,EACHqK,KAAM,eACN4D,SAAU,gBACVrH,OAAQ,eACRsH,WAAYyE,EAAAA,UAEZiC,WAAY1B,EAAAA,WAAWrF,OACvBgE,SAAUqB,EAAAA,WAAW3G,KACrBuF,cAAeG,UAEf7D,kBAAmB,CACfvK,QAAQ,GAGZwK,aAAc,CACVC,KAAM,MACNrB,UAAW,GAIfjE,QAAS,CACL,CACIC,IAAK,KACL7I,MAAO,KACP+I,UAAU,EACVoF,MAAO,cAGX,CACItF,IAAK,OACL7I,MAAO,gBAEX,CACI6I,IAAK,aACL7I,MAAO,OACPuF,OAAQ,CACJ2E,KAAM,SACNtK,QAASkS,EAAAA,MAAM2C,mBAGvB,CACI5L,IAAK,sBACL7I,MAAO,UACPoO,WAAY,MAEhB,CACIvF,IAAK,cACL7I,MAAO,SACP8I,UAAW,eACXsF,WAAY,KACZD,MAAO,mBAEX,CACItF,IAAK,UACL7I,MAAO,UACPI,UAAW,kBACX0I,UAAW,iBACXsF,WAAY,MAEhB,CACIvF,IAAK,gBACL7I,MAAO,WACPI,UAAW,kBACX0I,UAAW,WACXsF,WAAY,OAIpBC,QAAS,CACL,CACIxF,IAAK,YACL7I,MAAO,SACPkK,KAAM,SACNtK,QAAS,CACL,CAAEI,MAAO,SAAUyK,OAAO,GAC1B,CAAEzK,MAAO,WAAYyK,OAAO,MAKxCsE,YAAa,CACT,CACI9O,KAAM,YACNC,OAAQ,OACRF,MAAO,cAEX,CACIC,KAAM,cACNC,OAAQ,cACRF,MAAO,sBAKfuO,YAAY,EACZC,YAAY,EACZzF,UAAU,EACV0F,YAAY,EACZC,WAAW,EAGXC,aAAa,EACbC,SAAS,EACTC,YAAY,EAGZC,aAAc,+DAGd6C,iBAAkB,MAClBC,aAAc,CACV,CAAE5R,MAAO,SAAUC,KAAM,cAAeC,OAAQ,gBAChD,CAAEF,MAAO,SAAUC,KAAM,iBAAkBC,OAAQ,gBACnD,CAAEF,MAAO,WAAYC,KAAM,qBAAsBC,OAAQ,kBACzD,CAAEF,MAAO,aAAcC,KAAM,iBAAkBC,OAAQ,oBACvD,CAAEF,MAAO,OAAQC,KAAM,oBAAqBC,OAAQ,eAIxD8O,aAAc,CACVC,SAAS,EACTC,UAAU,EACVC,OAAO,EACPC,YAAY,IAGxB,CAEA,kBAAAsF,CAAmB7P,EAAOC,GACtB,MAAMyK,EAAOlP,KAAKqI,WAAWH,IAAIzD,EAAQ0K,QAAQlD,IACjDjM,KAAK4C,SAAS0R,eAAepF,EACjC,ECpIJ,MAAMqF,mBAAmBlV,EAAAA,KACrB,WAAAC,CAAYC,EAAU,IAClBC,MAAM,CACFO,UAAW,iBACRR,IAGPS,KAAK4G,MAAQrH,EAAQqH,OAAS,IAAI4N,EAAAA,WAAWjV,EAAQyD,MAAQ,IAC7DhD,KAAKyU,WAAazU,KAAK4G,MAAMsB,IAAI,gBAAkB,CAAA,EACnDlI,KAAK0U,WAAa1U,KAAK2U,iBAAiB3U,KAAKyU,YAE7CzU,KAAKmH,SAAW,+wDAqCpB,CAEA,gBAAAwN,CAAiBF,GACb,MAAMG,EAAKH,GAAYG,IAAIC,QAAQC,eAAiB,GAC9CC,EAAUN,GAAYO,YAAYH,QAAQC,eAAiB,GAC3DG,EAASR,GAAYQ,QAAQJ,QAAQC,eAAiB,GAE5D,OAAIC,EAAQG,SAAS,UAAkB,oBACnCH,EAAQG,SAAS,WAAmB,qBACpCH,EAAQG,SAAS,UAAkB,oBACnCH,EAAQG,SAAS,QAAgB,kBAEjCN,EAAGM,SAAS,QAAUN,EAAGM,SAAS,OAAe,WACjDN,EAAGM,SAAS,WAAmB,aAC/BN,EAAGM,SAAS,WAAmB,cAC/BN,EAAGM,SAAS,SAAiB,YAE7BD,EAAOC,SAAS,UAAkB,WAClCD,EAAOC,SAAS,QAAgB,YAE7B,WACX,CAEA,YAAMtU,GAEFZ,KAAKmV,SAAW,IAAI9V,OAAK,CACrBuH,MAAO5G,KAAK4G,MACZ7G,UAAW,MACXoH,SAAU,ihDA+Bd,MAAMiO,EAAsB,IAAIhM,yBAAuB,CACnDpB,OAAQ,CAAEqN,YAAarV,KAAK4G,MAAMsB,IAAI,MAAOC,KAAM,MAEvDnI,KAAKqJ,cAAgB,IAAIjB,YAAU,CAC/BC,WAAY+M,EACZ9M,oBAAqB,CAAC,eACtBC,QAAS,CACL,CAAEC,IAAK,aAAc7I,MAAO,aAAc+I,UAAU,GACpD,CAAEF,IAAK,mBAAoB7I,MAAO,OAAQ8I,UAAW,gBACrD,CAAED,IAAK,qBAAsB7I,MAAO,SAAU8I,UAAW,gBACzD,CAAED,IAAK,2BAA4B7I,MAAO,UAAW8I,UAAW,gBAChE,CAAED,IAAK,aAAc7I,MAAO,aAAc8I,UAAW,YACrD,CAAED,IAAK,YAAa7I,MAAO,YAAa8I,UAAW,eAK3DzI,KAAK8G,QAAU,IAAI0D,UAAQ,CACvBC,KAAM,CACF6K,KAAQtV,KAAKmV,SACblK,UAAajL,KAAKqJ,eAEtB6B,UAAW,OACX/I,YAAa,gBAEjBnC,KAAKoC,SAASpC,KAAK8G,SAGnB,MAAMyO,EAAa,IAAIhK,cAAY,CAC/BpJ,YAAa,sBACbpC,UAAW,yCACXyL,QAASxL,KAAK4G,MACd6E,OAAQ,CACJ7L,KAAM,yBACN8L,MAAO,CACH,CAAE/L,MAAO,YAAaE,OAAQ,YAAaD,KAAM,aACjD,CAAED,MAAO,eAAgBE,OAAQ,eAAgBD,KAAM,kBAAmBmF,UAAU,GACpF,CAAE8E,KAAM,WACR,CAAElK,MAAO,gBAAiBE,OAAQ,gBAAiBD,KAAM,WAAYiR,QAAQ,OAIzF7Q,KAAKoC,SAASmT,EAClB,CAEA,sBAAMvE,GAEwBhR,KAAK4G,MAAMsB,IAAI,QACzClI,KAAKwF,KAAK,YAAa,CAAEgQ,OAAQxV,KAAK4G,MAAMsB,IAAI,SAAS+D,IAC7D,CAEA,0BAAMwJ,GAE4BzV,KAAK4G,MAAMqF,EAC7C,CAEA,iBAAayJ,CAAKC,GACd,MAAM/O,QAAc4N,aAAWoB,UAAUD,GACzC,GAAI/O,EAAO,CACP,MAAMiP,EAAO,IAAItB,WAAW,CAAE3N,UACxBkP,EAAS,IAAI/J,SAAO,CACtB3I,QAAQ,EACR+E,KAAM,KACN4N,KAAMF,EACNG,QAAS,CAAC,CAAEC,KAAM,QAASnI,MAAO,gBAAiBoI,SAAS,MAIhE,aAFMJ,EAAOzS,QAAO,EAAM8S,SAASJ,MACnCD,EAAOJ,OACAI,CACX,CAEA,OADA/J,SAAOpG,MAAM,CAAEyQ,QAAS,oCAAoCT,IAAQ9L,KAAM,YACnE,IACX,EAGJ2K,EAAAA,WAAWnH,WAAakH,WCxLxB,MAAM8B,4BAA4B9I,EAAAA,UAC9B,WAAAjO,CAAYC,EAAU,IAClBC,MAAM,IACCD,EACHqK,KAAM,qBACN4D,SAAU,eACVrH,OAAQ,qBACRsH,WAAYxE,EAAAA,eAEZoI,cAAekD,WACf5G,kBAAmB,CACfvK,QAAQ,EACR+E,KAAM,MAIVI,QAAS,CACL,CAAEC,IAAK,KAAM7I,MAAO,KAAMoJ,MAAO,OAAQL,UAAU,EAAMoF,MAAO,cAChE,CAAEtF,IAAK,OAAQ7I,MAAO,YAAa+I,UAAU,EAAMD,UAAW,uBAC9D,CAAED,IAAK,oBAAqB7I,MAAO,OAAQ+I,UAAU,EAAMD,UAAW,gBACtE,CAAED,IAAK,gCAAiC7I,MAAO,UAAW8I,UAAW,gBACrE,CAAED,IAAK,wBAAyB7I,MAAO,KAAM8I,UAAW,gBACxD,CAAED,IAAK,UAAW7I,MAAO,UAAW+I,UAAU,GAC9C,CAAEF,IAAK,aAAc7I,MAAO,aAAc8I,UAAW,kBACrD,CAAED,IAAK,YAAa7I,MAAO,YAAa8I,UAAW,mBAIvDyF,YAAY,EACZC,YAAY,EACZzF,UAAU,EACV0F,YAAY,EACZC,WAAW,EAGXC,aAAa,EACbC,SAAS,EACTC,YAAY,EAGZC,aAAc,yBAGdE,aAAc,CACVC,SAAS,EACTC,UAAU,EACVC,OAAO,EACPC,YAAY,IAGxB,ECnDJ,MAAMuH,oCAAoC/I,EAAAA,UACtC,WAAAjO,CAAYC,EAAU,IAClBC,MAAM,IACCD,EACHqK,KAAM,8BACN4D,SAAU,mBACVrH,OAAQ,8BACRsH,WAAYrE,EAAAA,uBAGZb,QAAS,CACL,CAAEC,IAAK,KAAM7I,MAAO,KAAMoJ,MAAO,OAAQL,UAAU,EAAMoF,MAAO,cAChE,CAAEtF,IAAK,oBAAqB7I,MAAO,OAAQ+I,UAAU,GACrD,CAAEF,IAAK,cAAe7I,MAAO,SAAUwH,SAAU,yFAA0FuB,UAAU,GACrJ,CAAEF,IAAK,aAAc7I,MAAO,aAAc+I,UAAU,GACpD,CAAEF,IAAK,mBAAoB7I,MAAO,OAAQ8I,UAAW,gBACrD,CAAED,IAAK,qBAAsB7I,MAAO,SAAU8I,UAAW,gBACzD,CAAED,IAAK,2BAA4B7I,MAAO,UAAW8I,UAAW,gBAChE,CAAED,IAAK,YAAa7I,MAAO,YAAa8I,UAAW,mBAIvDyF,YAAY,EACZC,YAAY,EACZzF,UAAU,EACV0F,YAAY,EACZC,WAAW,EAGXC,aAAa,EACbC,SAAS,EACTC,YAAY,EAGZC,aAAc,6BAGdE,aAAc,CACVC,SAAS,EACTC,UAAU,EACVC,OAAO,EACPC,YAAY,IAGxB,ECrCJ,MAAMwH,kBAAkBlX,EAAAA,KACpB,WAAAC,CAAYC,EAAU,IAClBC,MAAM,CACFO,UAAW,gBACRR,IAGPS,KAAK4G,MAAQrH,EAAQqH,OAAS,IAAI4P,EAAAA,aAAajX,EAAQyD,MAAQ,IAC/DhD,KAAKyW,eAAiBzW,KAAK4G,MAAMsB,IAAI,aAAelI,KAAK4G,MAAMsB,IAAI,aAEnElI,KAAKmH,SAAW,ipIAwDpB,CAEA,YAAMvG,GAEFZ,KAAK0W,YAAc,IAAIrP,UAAS,CAC5BT,MAAO5G,KAAK4G,MACZ7G,UAAW,MACXuH,iBAAiB,EACjBqP,eAAgB,IAChBpO,QAAS,EACThB,OAAQ,CACJ,CAAEqC,KAAM,aAAcjK,MAAO,aAAciX,KAAM,GACjD,CAAEhN,KAAM,SAAUjK,MAAO,SAAUiX,KAAM,GACzC,CAAEhN,KAAM,eAAgBjK,MAAO,UAAWiX,KAAM,GAChD,CAAEhN,KAAM,eAAgBjK,MAAO,eAAgBiX,KAAM,GACrD,CAAEhN,KAAM,SAAUjK,MAAO,SAAUiX,KAAM,GACzC,CAAEhN,KAAM,OAAQjK,MAAO,OAAQiX,KAAM,GACrC,CAAEhN,KAAM,cAAejK,MAAO,cAAeiX,KAAM,GACnD,CAAEhN,KAAM,WAAYjK,MAAO,WAAYiX,KAAM,GAC7C,CAAEhN,KAAM,WAAYjK,MAAO,WAAYiX,KAAM,GAC7C,CAAEhN,KAAM,YAAajK,MAAO,YAAaiX,KAAM,MAK/C5W,KAAK6W,YAAc,IAAIxP,UAAS,CAC5BT,MAAO5G,KAAK4G,MACZ7G,UAAW,MACXuH,iBAAiB,EACjBqP,eAAgB,IAChBpO,QAAS,EACThB,OAAQ,CACJ,CAAEqC,KAAM,SAAUjK,MAAO,gBAAiB8I,UAAW,YAAamO,KAAM,GACxE,CAAEhN,KAAM,SAAUjK,MAAO,MAAO8I,UAAW,YAAamO,KAAM,GAC9D,CAAEhN,KAAM,WAAYjK,MAAO,QAAS8I,UAAW,YAAamO,KAAM,GAClE,CAAEhN,KAAM,WAAYjK,MAAO,iBAAkB8I,UAAW,YAAamO,KAAM,GAC3E,CAAEhN,KAAM,gBAAiBjK,MAAO,aAAc8I,UAAW,YAAamO,KAAM,GAC5E,CAAEhN,KAAM,YAAajK,MAAO,SAAU8I,UAAW,YAAamO,KAAM,GACpE,CAAEhN,KAAM,iBAAkBjK,MAAO,iBAAkBiX,KAAM,GACzD,CAAEhN,KAAM,MAAOjK,MAAO,MAAOiX,KAAM,GACnC,CAAEhN,KAAM,UAAWjK,MAAO,mBAAoBiX,KAAM,GACpD,CAAEhN,KAAM,MAAOjK,MAAO,MAAOiX,KAAM,IACnC,CAAEhN,KAAM,kBAAmBjK,MAAO,kBAAmBiX,KAAM,MAKnE5W,KAAK8W,SAAW,IAAIzP,UAAS,CACzBT,MAAO5G,KAAK4G,MACZ7G,UAAW,MACXuH,iBAAiB,EACjBqP,eAAgB,IAChBpO,QAAS,EACThB,OAAQ,CACJ,CAAEqC,KAAM,eAAgBjK,MAAO,eAAgBiX,KAAM,GACrD,CAAEhN,KAAM,aAAcjK,MAAO,aAAciX,KAAM,GACjD,CAAEhN,KAAM,YAAajK,MAAO,SAAU8I,UAAW,YAAamO,KAAM,GACpE,CAAEhN,KAAM,gBAAiBjK,MAAO,aAAc8I,UAAW,YAAamO,KAAM,GAC5E,CAAEhN,KAAM,oBAAqBjK,MAAO,iBAAkB8I,UAAW,YAAamO,KAAM,GACpF,CAAEhN,KAAM,kBAAmBjK,MAAO,eAAgB8I,UAAW,YAAamO,KAAM,MAKhG5W,KAAK+W,aAAe,IAAI1P,UAAS,CAC7BT,MAAO5G,KAAK4G,MACZ7G,UAAW,MACXuH,iBAAiB,EACjBqP,eAAgB,IAChBpO,QAAS,EACThB,OAAQ,CACJ,CAAEqC,KAAM,KAAMjK,MAAO,YAAaiX,KAAM,GACxC,CAAEhN,KAAM,WAAYjK,MAAO,gBAAiB8I,UAAW,aAAcmO,KAAM,GAC3E,CAAEhN,KAAM,UAAWjK,MAAO,UAAW8I,UAAW,WAAYmO,KAAM,GAClE,CAAEhN,KAAM,WAAYjK,MAAO,gBAAiB8I,UAAW,WAAYmO,KAAM,GACzE,CAAEhN,KAAM,YAAajK,MAAO,YAAa8I,UAAW,WAAYmO,KAAM,GACtE,CAAEhN,KAAM,aAAcjK,MAAO,UAAW8I,UAAW,WAAYmO,KAAM,MAK7E,MAAMjO,EAAmB,IAAIC,oBAAkB,CAC3CZ,OAAQ,CACJG,KAAM,EACN6O,UAAWhX,KAAK4G,MAAMsB,IAAI,iBAGlClI,KAAKiH,WAAa,IAAImB,YAAU,CAC5BC,WAAYM,EACZL,oBAAqB,CAAC,aACtBC,QAAS,CACL,CAAEC,IAAK,KAAM7I,MAAO,KAAM+I,UAAU,EAAMK,MAAO,QACjD,CAAEP,IAAK,UAAW7I,MAAO,OAAQ8I,UAAW,WAAYC,UAAU,EAAMK,MAAO,SAC/E,CAAEP,IAAK,iBAAkB7I,MAAO,YAChC,CAAE6I,IAAK,QAAS7I,MAAO,YAK/B,MAAM8J,EAAiB,IAAIC,UAAQ,CAC/B1B,OAAQ,CACJG,KAAM,EACN8O,GAAIjX,KAAK4G,MAAMsB,IAAI,iBAG3BlI,KAAKkH,SAAW,IAAIkB,YAAU,CAC1BC,WAAYoB,EACZE,YAAa,YACbrB,oBAAqB,CAAC,MACtBC,QAAS,CACL,CACIC,IAAK,UACL7I,MAAO,YACP+I,UAAU,EACVD,UAAW,iBACXvD,OAAQ,CACJ0E,KAAM,UACNC,KAAM,YACNC,UAAW,WACXC,QAAS,SACTC,UAAW,WACXrK,MAAO,aACPsK,OAAQ,aACRC,cAAe,eACfC,UAAW,SAGnB,CACI3B,IAAK,QACL7I,MAAO,QACP+I,UAAU,EACVxD,OAAQ,CACJ2E,KAAM,SACNtK,QAAS,CACL,CAAE6K,MAAO,OAAQzK,MAAO,QACxB,CAAEyK,MAAO,UAAWzK,MAAO,WAC3B,CAAEyK,MAAO,QAASzK,MAAO,YAIrC,CAAE6I,IAAK,OAAQ7I,MAAO,OAAQuF,OAAQ,CAAE2E,KAAM,SAC9C,CAAED,KAAM,MAAOjK,MAAO,UAI9B,MAAM8K,EAAO,CACTyM,SAAYlX,KAAK0W,YACjBS,QAAWnX,KAAK6W,YAChB,oBAAqB7W,KAAK8W,SAC1BjM,OAAU7K,KAAKiH,WACf6D,KAAQ9K,KAAKkH,SACbkQ,SAAYpX,KAAK+W,cAIrB,GAAI/W,KAAKyW,eAAgB,CACrB,MAAMY,EAAMrX,KAAK4G,MAAMsB,IAAI,YACrBoP,EAAMtX,KAAK4G,MAAMsB,IAAI,aAKrBqP,EAAc,CAJPvX,KAAK4G,MAAMsB,IAAI,SAAW,UACxBlI,KAAK4G,MAAMsB,IAAI,WAAa,GAC3BlI,KAAK4G,MAAMsB,IAAI,iBAAmB,IAENhD,OAAOC,SAASqS,KAAK,MAEjExX,KAAKyX,QAAU,IAAIC,UAAQ,CACvBC,QAAS,CAAC,CACNN,MACAC,MACAM,MAAO,WAAW5X,KAAK4G,MAAMsB,IAAI,6BAA6BqP,MAElEM,UAAW,QACXC,KAAM,EACNpW,OAAQ,MAEZ+I,EAAU,IAAIzK,KAAKyX,OACvB,CAEAzX,KAAK8G,QAAU,IAAI0D,UAAQ,CACvBrI,YAAa,aACbsI,OACAS,UAAWlL,KAAKyW,eAAiB,MAAQ,aAE7CzW,KAAKoC,SAASpC,KAAK8G,SAGnB,MAAMiR,EAAY,CACd,CAAEpY,MAAO,gBAAiBE,OAAQ,gBAAiBD,KAAM,cACzD,CAAED,MAAO,gBAAiBE,OAAQ,gBAAiBD,KAAM,kBACzD,CAAED,MAAO,eAAgBE,OAAQ,eAAgBD,KAAM,gBACvD,CAAEiK,KAAM,WACR,CAAElK,MAAO,sBAAuBE,OAAQ,gBAAiBD,KAAM,uBAG/DI,KAAKyW,gBACLsB,EAAUC,KAAK,CACXrY,MAAO,cACPE,OAAQ,cACRD,KAAM,WAIdmY,EAAUC,KACN,CAAEnO,KAAM,WACR,CAAElK,MAAO,gBAAiBE,OAAQ,eAAgBD,KAAM,WAAYiR,QAAQ,IAGhF,MAAMoH,EAAY,IAAI1M,cAAY,CAC9BpJ,YAAa,qBACbpC,UAAW,yCACXyL,QAASxL,KAAK4G,MACd6E,OAAQ,CACJ7L,KAAM,yBACN8L,MAAOqM,KAGf/X,KAAKoC,SAAS6V,EAClB,CAEA,mBAAMvR,SACIlH,MAAMkH,gBAGRwR,OAAOC,WAAaD,OAAOC,UAAUC,SAAWpY,KAAKyE,SAC1BzE,KAAKyE,QAAQ4T,iBAAiB,8BACtCC,QAAQC,IAEvB,MAAMC,EAAWN,OAAOC,UAAUC,QAAQK,YAAYF,GAClDC,GAAwC,mBAArBA,EAASE,SAC5BF,EAASE,UAEb,IAAIR,OAAOC,UAAUC,QAAQG,IAGzC,CAEA,0BAAMI,SACiB5M,EAAAA,OAAOC,cAAc,CACpCvM,MAAO,mBAAmBO,KAAK4G,MAAMsB,IAAI,gBACzCtB,MAAO5G,KAAK4G,MACZuF,WAAYqK,EAAAA,aAAaoC,6BAInB5Y,KAAKqD,SACXrD,KAAK4C,UAAU6J,OAAO1J,QAAQ,iCAEtC,CAEA,0BAAM8V,SACiB9M,EAAAA,OAAOC,cAAc,CACpCvM,MAAO,mBAAmBO,KAAK4G,MAAMsB,IAAI,gBACzCtB,MAAO5G,KAAK4G,MACZuF,WAAYqK,EAAAA,aAAasC,6BAInB9Y,KAAKqD,SACXrD,KAAK4C,UAAU6J,OAAO1J,QAAQ,0CAEtC,CAEA,yBAAMgW,SACiBhN,EAAAA,OAAOC,cAAc,CACpCvM,MAAO,kBAAkBO,KAAK4G,MAAMsB,IAAI,gBACxCtB,MAAO5G,KAAK4G,MACZuF,WAAYqK,EAAAA,aAAawC,4BAInBhZ,KAAKqD,SACXrD,KAAK4C,UAAU6J,OAAO1J,QAAQ,4CAEtC,CAEA,0BAAMkW,SAEIjZ,KAAK4G,MAAM2F,KAAK,CAAEtH,SAAS,IACjCjF,KAAK4C,UAAU6J,OAAOyM,KAAK,4BAA8BlZ,KAAK4G,MAAMsB,IAAI,cAC5E,CAEA,4BAAMiR,SAEInZ,KAAK4G,MAAM2F,KAAK,CAAE6M,iBAAiB,IACzCpZ,KAAK4C,UAAU6J,OAAOyM,KAAK,kCAAoClZ,KAAK4G,MAAMsB,IAAI,cAClF,CAEA,uBAAMmR,GACF,GAAIrZ,KAAKyW,eAAgB,CACrB,MAEM6C,EAAM,mDAFAtZ,KAAK4G,MAAMsB,IAAI,eACflI,KAAK4G,MAAMsB,IAAI,eAE3BgQ,OAAOqB,KAAKD,EAAK,SACrB,CACJ,CAEA,yBAAME,SACsBzN,EAAAA,OAAOO,QAC3B,yDAAyDtM,KAAK4G,MAAMsB,IAAI,kBACxE,mBACA,CAAEuR,aAAc,aAAcC,YAAa,mBAGxB1Z,KAAK4G,MAAMsK,WACrBnO,SACL/C,KAAKwF,KAAK,gBAAiB,CAAEoB,MAAO5G,KAAK4G,OAGrD,CAEA,iBAAa8O,CAAKuB,GACd,MAAMrQ,QAAc4P,eAAamD,OAAO1C,GACxC,GAAIrQ,EAAO,CACP,MAAMiP,EAAO,IAAIU,UAAU,CAAE3P,UACvBkP,EAAS,IAAI/J,SAAO,CACtB3I,QAAQ,EACR+E,KAAM,KACN4N,KAAMF,EACNG,QAAS,CAAC,CAAEC,KAAM,QAASnI,MAAO,gBAAiBoI,SAAS,MAIhE,aAFMJ,EAAOzS,QAAO,EAAM8S,SAASJ,MACnCD,EAAOJ,OACAI,CACX,CAEA,OADA/J,SAAOpG,MAAM,CAAEyQ,QAAS,2CAA2Ca,IAAMpN,KAAM,YACxE,IACX,EAGJ2M,EAAAA,aAAanJ,WAAakJ,UChZ1B,MAAMqD,8BAA8BrM,EAAAA,UAChC,WAAAjO,CAAYC,EAAU,IAClBC,MAAM,IACCD,EACHqK,KAAM,qBACN4D,SAAU,cACVrH,OAAQ,qBACRsH,WAAYoM,EAAAA,iBAEZC,SAAUvD,UACV5I,kBAAmB,CACfvK,QAAQ,EACR+E,KAAM,MAIVI,QAAS,CACL,CAAEC,IAAK,aAAc7I,MAAO,aAAc+I,UAAU,GACpD,CAAEF,IAAK,OAAQ7I,MAAO,OAAQ+I,UAAU,EAAMD,UAAW,gBACzD,CAAED,IAAK,SAAU7I,MAAO,SAAU+I,UAAU,EAAMD,UAAW,gBAC7D,CAAED,IAAK,eAAgB7I,MAAO,UAAW+I,UAAU,EAAMD,UAAW,gBACpE,CAAED,IAAK,MAAO7I,MAAO,MAAO+I,UAAU,EAAMD,UAAW,gBACvD,CAAED,IAAK,eAAgB7I,MAAO,SAAU8I,UAAW,iBAIvDyF,YAAY,EACZC,YAAY,EACZzF,UAAU,EACV0F,YAAY,EACZC,WAAW,EAIX0L,YAAa,OAGbzL,aAAa,EACbC,SAAS,EACTC,YAAY,EAGZC,aAAc,0BAGdE,aAAc,CACVC,SAAS,EACTC,UAAU,EACVC,OAAO,EACPC,YAAY,GAEhBiL,iBAAkB,CACdnI,eAAgB,YAChBC,MAAQmI,IACJA,EAAIhL,iBAEJjP,KAAKka,cAIrB,CAEA,cAAMA,GAEF,MAAMlX,QAAahD,KAAK4C,SAAS2M,SAAS,CACtC9P,MAAO,YACP8H,OAAQ,CACJ,CACIqC,KAAM,KACNC,KAAM,OACNiG,UAAU,MAItB,GAAI9M,GAAQA,EAAKiU,GAAI,CACjB,MAAMrQ,QAAc4P,EAAAA,aAAamD,OAAO3W,EAAKiU,IACzCrQ,GACA5G,KAAKma,UAAUC,WAAW,CAAExT,SAEpC,CACJ,EC/DJ,MAAMyT,qBAAqBhb,EAAAA,KACvB,WAAAC,CAAYC,EAAU,IAClBC,MAAM,CACFO,UAAW,mBACRR,IAGPS,KAAK2X,QAAUpY,EAAQoY,SAAW,GAClC3X,KAAKsa,OAAS/a,EAAQ+a,QAAU,KAChCta,KAAK8X,KAAOvY,EAAQuY,MAAQ,GAC5B9X,KAAK0B,OAASnC,EAAQmC,QAAU,IAChC1B,KAAKua,MAAQhb,EAAQgb,OAAS,EAC9Bva,KAAKwa,QAAUjb,EAAQib,SAAW,EAClCxa,KAAKya,SAAWlb,EAAQqQ,OAAS,UACjC5P,KAAK0a,uBAA0D,IAAlCnb,EAAQmb,sBACrC1a,KAAK2a,eAA0C,IAA1Bpb,EAAQob,cAE7B3a,KAAK4a,IAAM,KACX5a,KAAK6a,WAAa,GAClB7a,KAAK8a,eAAiB,IAAI9a,KAAK2X,SAC/B3X,KAAK+a,YAAcxb,EAAQwb,aAAe,GAC1C/a,KAAKgb,mCAAsBC,IAE3Bjb,KAAKmH,SAAW,+NAKpB,CAEA,mBAAMT,SACI1G,KAAKkb,qBACLlb,KAAKmb,eACf,CAEA,kBAAMD,GAEF,GAAIhD,OAAOkD,WAAY,OAGvB,MAAMC,EAAOlF,SAASmF,cAAc,QAMpC,OALAD,EAAKE,IAAM,aACXF,EAAKG,KAAO,2DACZrF,SAASsF,KAAKC,YAAYL,GAGnB,IAAIjW,QAAQ,CAACuW,EAASC,KACzB,MAAMC,EAAS1F,SAASmF,cAAc,UACtCO,EAAOC,IAAM,0DACbD,EAAOE,OAASJ,EAChBE,EAAOG,QAAUJ,EACjBzF,SAASsF,KAAKC,YAAYG,IAElC,CAEA,WAAAI,GACI,MAAMC,EAAS,CACXC,QAAS,4CACTC,KAAM,CACFC,QAAS,EACTC,QAAS,CACLC,IAAK,CACD1S,KAAM,SACN2S,MAAO,CAAC,oDACRC,SAAU,IACVC,YAAa,iCAGrBC,OAAQ,CAAC,CACL1Q,GAAI,MACJpC,KAAM,SACN+S,OAAQ,QAEZC,OAAQ,+DAEZC,MAAO,CACHT,QAAS,EACTC,QAAS,CACLC,IAAK,CACD1S,KAAM,SACN2S,MAAO,CAAC,oDACRC,SAAU,IACVC,YAAa,iCAGrBC,OAAQ,CAAC,CACL1Q,GAAI,MACJpC,KAAM,SACN+S,OAAQ,QAEZC,OAAQ,+DAEZE,UAAW,CACPV,QAAS,EACTC,QAAS,CACLS,UAAW,CACPlT,KAAM,SACN2S,MAAO,CAAC,iGACRC,SAAU,IACVC,YAAa,WAGrBC,OAAQ,CAAC,CACL1Q,GAAI,YACJpC,KAAM,SACN+S,OAAQ,cAEZC,OAAQ,+DAEZG,QAAS,CACLX,QAAS,EACTC,QAAS,CACLU,QAAS,CACLnT,KAAM,SACN2S,MAAO,CAAC,kDACRC,SAAU,IACVC,YAAa,+BAGrBC,OAAQ,CAAC,CACL1Q,GAAI,UACJpC,KAAM,SACN+S,OAAQ,YAEZC,OAAQ,gEAIhB,OAAOX,EAAOlc,KAAKya,WAAayB,EAAOC,OAC3C,CAEA,mBAAMhB,GACF,MAAM8B,EAAajd,KAAKyE,QAAQG,cAAc,aAAa5E,KAAKiM,MAChE,IAAKgR,IAAe/E,OAAOkD,WAAY,OAGvC,IAAI8B,EAAYld,KAAKsa,QAChB4C,GAAald,KAAK2X,QAAQwF,OAAS,IAEpCD,EAAY,CAACld,KAAK2X,QAAQ,GAAGL,IAAKtX,KAAK2X,QAAQ,GAAGN,MAEjD6F,IAEDA,EAAY,CAAC,EAAG,IAIpBld,KAAK4a,IAAM,IAAI1C,OAAOkD,WAAWH,IAAI,CACjCmC,UAAWH,EACXrN,MAAO5P,KAAKic,cACZ3B,OAAQ4C,EACRpF,KAAM9X,KAAK8X,KACXyC,MAAOva,KAAKua,MACZC,QAASxa,KAAKwa,UAIdxa,KAAK0a,uBACL1a,KAAK4a,IAAIyC,WAAW,IAAInF,OAAOkD,WAAWkC,kBAAqB,aAInEtd,KAAK4a,IAAI2C,GAAG,OAAQ,KAChB,MAAMC,EAAiBxd,KAAK8a,eAAeqC,OAASnd,KAAK8a,eAAiB9a,KAAK2X,QAC/E3X,KAAKyd,cAAcD,GACnBxd,KAAK+a,YAAYzC,QAAQsE,GAAU5c,KAAK0d,cAAcd,IACtD5c,KAAKgb,gBAAgB1C,QAAQsE,GAAU5c,KAAK0d,cAAcd,IAC1D5c,KAAKgb,gBAAgB2C,SAE7B,CAEA,UAAAC,CAAWjG,GACF3X,KAAK4a,KAAQiD,MAAMC,QAAQnG,GAKhCA,EAAQW,QAAQyF,IACZ,MAAMzG,IAAEA,EAAAD,IAAKA,EAAAO,MAAKA,QAAOhW,EAAAhC,KAAOA,EAAAuI,KAAMA,GAAS4V,EAE/C,IAAKzG,IAAQD,EAAK,OAGlB,MAAMkB,EAAKpC,SAASmF,cAAc,OAClC/C,EAAGxY,UAAY,kBACf,MAAMie,EAAaC,OAAO9V,IAAS,GACnCoQ,EAAG3I,MAAM7G,MAAQ,GAAGiV,MACpBzF,EAAG3I,MAAMlO,OAAS,GAAGsc,MACrBzF,EAAG3I,MAAMsO,aAAe,MACxB3F,EAAG3I,MAAMuO,gBAAkBvc,GAAS,UACpC2W,EAAG3I,MAAMwO,OAAS,kBAClB7F,EAAG3I,MAAMyO,UAAY,4BACrB9F,EAAG3I,MAAM0O,OAAS,UAClB/F,EAAG3I,MAAM2O,QAAU,OACnBhG,EAAG3I,MAAM4O,WAAa,SACtBjG,EAAG3I,MAAM6O,eAAiB,SAGtB7e,IACA2Y,EAAG3S,UAAY,aAAahG,iCAIhC,MAAM8e,EAAS,IAAIxG,OAAOkD,WAAWuD,OAAO,CAAEla,QAAS8T,IAClDqG,UAAU,CAACtH,EAAKD,IAChBwH,MAAM7e,KAAK4a,KAGhB,GAAIhD,EAAO,CACP,MAAMkH,EAAW,IAAI5G,OAAOkD,WAAW2D,MAAM,CAAEC,OAAQ,KAClDC,QAAQrH,GACb8G,EAAOQ,SAASJ,EACpB,CAEA9e,KAAK6a,WAAW7C,KAAK0G,KAzCrB1e,KAAK8a,eAAiB+C,MAAMC,QAAQnG,GAAWA,EAAU,EA2CjE,CAEA,SAAAwH,GACI,IAAKnf,KAAK4a,KAA+B,IAAxB5a,KAAK2X,QAAQwF,OAAc,OAE5C,MAAMiC,EAAS,IAAIlH,OAAOkD,WAAWiE,aAErCrf,KAAK2X,QAAQW,QAAQoG,IACjBU,EAAOE,OAAO,CAACZ,EAAOpH,IAAKoH,EAAOrH,QAGtCrX,KAAK4a,IAAIuE,UAAUC,EAAQ,CACvBG,QAAS,GACTC,QAAS,IAEjB,CAEA,aAAA/B,CAAcgC,GACV,MAAM9H,EAAUkG,MAAMC,QAAQ2B,GAAcA,EAAa,GACzDzf,KAAK2X,QAAUA,EACf3X,KAAK8a,eAAiBnD,EACjB3X,KAAK4a,MAEV5a,KAAK0f,eACL1f,KAAK4d,WAAWjG,GAEZ3X,KAAK2a,eAAiBhD,EAAQwF,OAAS,EACvCnd,KAAKmf,YACqB,IAAnBxH,EAAQwF,QAAgBnd,KAAK4a,KACpC5a,KAAK4a,IAAI+E,MAAM,CACXrF,OAAQ,CAAC3C,EAAQ,GAAGL,IAAKK,EAAQ,GAAGN,KACpCS,KAAM9X,KAAK8X,OAGvB,CAEA,YAAA4H,GACI1f,KAAK6a,WAAWvC,QAAQoG,IACpBA,EAAO5Y,WAEX9F,KAAK6a,WAAa,EACtB,CAEA,aAAA6C,EAAczR,GAAEA,EAAAjJ,KAAIA,EAAA4c,MAAMA,EAAAC,OAAOA,IAC7B,IAAK5T,IAAOjJ,EAAM,OAElB,MAAM8c,EAAW,GAAG7T,WAEfjM,KAAK4a,KAKN5a,KAAK4a,IAAImF,SAAS9T,IAClBjM,KAAK4a,IAAIoF,YAAY/T,GAErBjM,KAAK4a,IAAIqF,UAAUH,IACnB9f,KAAK4a,IAAIsF,aAAaJ,GAG1B9f,KAAK4a,IAAIuF,UAAUL,EAAU,CACzBjW,KAAM,UACN7G,SAGJhD,KAAK4a,IAAIwF,SAAS,CACdnU,KACApC,KAAM,OACN+S,OAAQkD,EACRF,MAAO,CACH,aAAc,UACd,aAAc,EACd,eAAgB,MACbA,GAEPC,OAAQ,CACJ,WAAY,QACZ,YAAa,WACVA,MA7BP7f,KAAKgb,gBAAgBqF,IAAIpU,EAAI,CAAEA,KAAIjJ,OAAM4c,QAAOC,UAgCxD,CAEA,gBAAAS,CAAiBrU,GAAIjJ,KAAEA,EAAA4c,MAAMA,SAAOC,GAAW,IAC3C,IAAK5T,IAAOjJ,EAAM,OAClB,MAAM8c,EAAW,GAAG7T,WACfjM,KAAK4a,IAIN5a,KAAK4a,IAAIqF,UAAUH,IACnB9f,KAAK4a,IAAIqF,UAAUH,GAAUS,QAAQvd,IACjC4c,GAASC,KACLD,GACA1c,OAAOsd,QAAQZ,GAAOtH,QAAQ,EAAE9P,EAAK4B,MACjCpK,KAAK4a,IAAI6F,iBAAiBxU,EAAIzD,EAAK4B,KAGvCyV,GACA3c,OAAOsd,QAAQX,GAAQvH,QAAQ,EAAE9P,EAAK4B,MAClCpK,KAAK4a,IAAI8F,kBAAkBzU,EAAIzD,EAAK4B,OAKhDpK,KAAK0d,cAAc,CAAEzR,KAAIjJ,OAAM4c,QAAOC,WAlBtC7f,KAAKgb,gBAAgBqF,IAAIpU,EAAI,CAAEA,KAAIjJ,OAAM4c,QAAOC,UAoBxD,CAEA,OAAAc,CAAQrJ,EAAKD,EAAKS,EAAO,MAChB9X,KAAK4a,KACV5a,KAAK4a,IAAI+E,MAAM,CACXrF,OAAQ,CAAChD,EAAKD,GACdS,KAAMA,GAAQ9X,KAAK8X,MAE3B,CAEA,OAAA8I,CAAQ9I,GACC9X,KAAK4a,KACV5a,KAAK4a,IAAIgG,QAAQ9I,EACrB,CAEA,QAAA+I,CAAStG,GACAva,KAAK4a,KACV5a,KAAK4a,IAAIiG,SAAStG,EACtB,CAEA,UAAAuG,CAAWtG,GACFxa,KAAK4a,KACV5a,KAAK4a,IAAIkG,WAAWtG,EACxB,CAEA,qBAAMuG,GACE/gB,KAAK4a,MACL5a,KAAK4a,IAAI9U,SACT9F,KAAK4a,IAAM,YAETpb,MAAMuhB,iBAChB,EC3XG,MAAMC,EAAoB,CAC/BC,GAAM,CAAErX,KAAM,UAAWyN,IAAK,kBAAmBC,IAAK,oBACtD4J,GAAM,CAAEtX,KAAM,uBAAwByN,IAAK,kBAAmBC,IAAK,mBACnE6J,GAAM,CAAEvX,KAAM,cAAeyN,IAAK,kBAAmBC,IAAK,mBAC1D8J,GAAM,CAAExX,KAAM,sBAAuByN,IAAK,kBAAmBC,KAAK,mBAClE+J,GAAM,CAAEzX,KAAM,WAAYyN,IAAK,mBAAoBC,KAAK,mBACxDgK,GAAM,CAAE1X,KAAM,UAAWyN,IAAK,kBAAmBC,IAAK,oBACtDiK,GAAM,CAAE3X,KAAM,UAAWyN,IAAK,kBAAmBC,IAAK,mBACtDkK,GAAM,CAAE5X,KAAM,SAAUyN,KAAK,mBAAqBC,IAAK,iBACvDmK,GAAM,CAAE7X,KAAM,aAAcyN,KAAK,kBAAoBC,KAAK,oBAC1DoK,GAAM,CAAE9X,KAAM,YAAayN,KAAK,mBAAqBC,KAAK,mBAC1DqK,GAAM,CAAE/X,KAAM,iBAAkByN,KAAK,mBAAqBC,KAAK,mBAC/DsK,GAAM,CAAEhY,KAAM,UAAWyN,IAAK,mBAAoBC,IAAK,oBACvDuK,GAAM,CAAEjY,KAAM,YAAayN,KAAK,mBAAqBC,IAAK,oBAC1DwK,GAAM,CAAElY,KAAM,QAASyN,IAAK,mBAAoBC,KAAK,mBACrDyK,GAAM,CAAEnY,KAAM,aAAcyN,IAAK,iBAAkBC,IAAK,oBACxD0K,GAAM,CAAEpY,KAAM,yBAA0ByN,IAAK,kBAAmBC,IAAK,mBACrE2K,GAAM,CAAErY,KAAM,WAAYyN,IAAK,mBAAoBC,KAAK,oBACxD4K,GAAM,CAAEtY,KAAM,aAAcyN,IAAK,gBAAiBC,IAAK,mBACvD6K,GAAM,CAAEvY,KAAM,UAAWyN,IAAK,iBAAkBC,IAAK,mBACrD8K,GAAM,CAAExY,KAAM,eAAgByN,IAAK,mBAAoBC,KAAK,oBAC5D+K,GAAM,CAAEzY,KAAM,WAAYyN,IAAK,kBAAmBC,IAAK,oBACvDgL,GAAM,CAAE1Y,KAAM,UAAWyN,IAAK,kBAAmBC,IAAK,oBACtDiL,GAAM,CAAE3Y,KAAM,UAAWyN,KAAK,kBAAoBC,IAAK,mBACvDkL,GAAM,CAAE5Y,KAAM,QAASyN,IAAK,kBAAmBC,IAAK,mBACpDmL,GAAM,CAAE7Y,KAAM,mBAAoByN,IAAK,kBAAmBC,KAAK,mBAC/DoL,GAAM,CAAE9Y,KAAM,UAAWyN,IAAK,mBAAoBC,KAAK,kBACvDqL,GAAM,CAAE/Y,KAAM,oBAAqByN,IAAK,kBAAmBC,IAAK,mBAChEsL,GAAM,CAAEhZ,KAAM,UAAWyN,KAAK,iBAAmBC,KAAK,mBACtDuL,GAAM,CAAEjZ,KAAM,UAAWyN,IAAK,mBAAoBC,KAAK,mBACvDuL,GAAM,CAAEjZ,KAAM,OAAQyN,IAAK,mBAAoBC,KAAK,mBACpDuL,GAAM,CAAEjZ,KAAM,kBAAmByN,IAAK,iBAAkBC,KAAK,oBAC7DwL,GAAM,CAAElZ,KAAM,SAAUyN,KAAK,mBAAqBC,KAAK,oBACvDyL,GAAM,CAAEnZ,KAAM,UAAWyN,IAAK,kBAAmBC,KAAK,mBACtD0L,GAAM,CAAEpZ,KAAM,SAAUyN,IAAK,kBAAmBC,IAAK,mBACrD2L,GAAM,CAAErZ,KAAM,gBAAiByN,KAAK,kBAAoBC,IAAK,mBAC7D4L,GAAM,CAAEtZ,KAAM,WAAYyN,KAAK,mBAAqBC,IAAK,mBACzD6L,GAAM,CAAEvZ,KAAM,UAAWyN,IAAK,kBAAmBC,IAAK,oBACtD8L,GAAM,CAAExZ,KAAM,SAAUyN,IAAK,kBAAmBC,KAAK,mBACrD+L,GAAM,CAAEzZ,KAAM,SAAUyN,IAAK,mBAAoBC,KAAK,mBACtDgM,GAAM,CAAE1Z,KAAM,gBAAiByN,KAAK,mBAAqBC,IAAK,mBAC9DiM,GAAM,CAAE3Z,KAAM,YAAayN,KAAK,kBAAoBC,IAAK,oBACzDkM,GAAM,CAAE5Z,KAAM,2BAA4ByN,IAAK,kBAAmBC,IAAK,oBACvEmM,GAAM,CAAE7Z,KAAM,QAASyN,KAAK,kBAAqBC,IAAK,oBACtDoM,GAAM,CAAE9Z,KAAM,cAAeyN,IAAK,kBAAmBC,IAAK,mBAC1DqM,GAAM,CAAE/Z,KAAM,gBAAiByN,IAAK,kBAAmBC,KAAK,mBAC5DsM,GAAM,CAAEha,KAAM,eAAgByN,KAAK,mBAAqBC,KAAK,oBAC7DuM,GAAM,CAAEja,KAAM,QAASyN,KAAK,kBAAoBC,KAAK,mBACrDwM,GAAM,CAAEla,KAAM,WAAYyN,IAAK,kBAAmBC,IAAK,oBACvDyM,GAAM,CAAEna,KAAM,QAASyN,IAAK,kBAAmBC,IAAK,oBACpD0M,GAAM,CAAEpa,KAAM,WAAYyN,IAAK,kBAAmBC,KAAK,kBACvD2M,GAAM,CAAEra,KAAM,aAAcyN,IAAK,kBAAmBC,KAAK,mBACzD4M,GAAM,CAAEta,KAAM,OAAQyN,IAAK,mBAAoBC,KAAK,mBACpD6M,GAAM,CAAEva,KAAM,aAAcyN,IAAK,mBAAoBC,KAAK,mBAC1D8M,GAAM,CAAExa,KAAM,UAAWyN,IAAK,mBAAoBC,KAAK,mBACvD+M,GAAM,CAAEza,KAAM,mBAAoByN,KAAK,mBAAqBC,IAAK,oBACjEgN,GAAM,CAAE1a,KAAM,SAAUyN,IAAK,kBAAmBC,IAAK,oBACrDiN,GAAM,CAAE3a,KAAM,iBAAkByN,IAAK,kBAAmBC,IAAK,oBAC7DkN,GAAM,CAAE5a,KAAM,UAAWyN,IAAK,kBAAmBC,IAAK,oBACtDmN,GAAM,CAAE7a,KAAM,WAAYyN,IAAK,mBAAoBC,IAAK,oBACxDoN,GAAM,CAAE9a,KAAM,UAAWyN,IAAK,kBAAmBC,IAAK,mBACtDqN,GAAM,CAAE/a,KAAM,WAAYyN,IAAK,mBAAoBC,KAAK,oBACxDsN,GAAM,CAAEhb,KAAM,qBAAsByN,IAAK,kBAAmBC,KAAK,mBACjEuN,GAAM,CAAEjb,KAAM,UAAWyN,IAAK,mBAAoBC,IAAK,oBACvDwN,GAAM,CAAElb,KAAM,UAAWyN,KAAK,mBAAqBC,KAAK,kBACxDyN,GAAM,CAAEnb,KAAM,UAAWyN,IAAK,mBAAoBC,IAAK,oBACvD0N,GAAM,CAAEpb,KAAM,QAASyN,IAAK,kBAAmBC,IAAK,oBACpD2N,GAAM,CAAErb,KAAM,UAAWyN,IAAK,mBAAoBC,IAAK,kBACvD4N,GAAM,CAAEtb,KAAM,QAASyN,IAAK,mBAAoBC,KAAK,oBACrD4N,GAAM,CAAEtb,KAAM,WAAYyN,IAAK,mBAAoBC,KAAK,mBACxD6N,GAAM,CAAEvb,KAAM,WAAYyN,IAAK,kBAAmBC,IAAK,oBACvD8N,GAAM,CAAExb,KAAM,UAAWyN,IAAK,kBAAmBC,IAAK,mBACtD+N,GAAM,CAAEzb,KAAM,OAAQyN,KAAK,mBAAqBC,IAAK,oBACrDgO,GAAM,CAAE1b,KAAM,mBAAoByN,KAAK,kBAAoBC,KAAK,oBAChEiO,GAAM,CAAE3b,KAAM,aAAcyN,IAAK,mBAAoBC,IAAK,mBAC1DkO,GAAM,CAAE5b,KAAM,gBAAiByN,IAAK,mBAAoBC,KAAK,oBAC7DmO,GAAM,CAAE7b,KAAM,SAAUyN,IAAK,iBAAkBC,IAAK,oBACpDoO,GAAM,CAAE9b,KAAM,QAASyN,KAAK,iBAAoBC,IAAK,oBACrDqO,GAAM,CAAE/b,KAAM,iBAAkByN,IAAK,kBAAmBC,KAAK,mBAC7DsO,GAAM,CAAEhc,KAAM,UAAWyN,IAAK,mBAAoBC,KAAK,mBACvDuO,GAAM,CAAEjc,KAAM,UAAWyN,IAAK,kBAAmBC,IAAK,oBACtDwO,GAAM,CAAElc,KAAM,gBAAiByN,IAAK,kBAAmBC,KAAK,mBAC5DyO,GAAM,CAAEnc,KAAM,WAAYyN,IAAK,kBAAmBC,KAAK,mBACvD0O,GAAM,CAAEpc,KAAM,QAASyN,IAAK,iBAAkBC,KAAK,mBACnD2O,GAAM,CAAErc,KAAM,YAAayN,IAAK,kBAAmBC,KAAK,mBACxD4O,GAAM,CAAEtc,KAAM,YAAayN,IAAK,kBAAmBC,KAAK,mBACxD6O,GAAM,CAAEvc,KAAM,SAAUyN,IAAK,mBAAoBC,KAAK,oBACtD8O,GAAM,CAAExc,KAAM,SAAUyN,IAAK,mBAAoBC,KAAK,oBACtD+O,GAAM,CAAEzc,KAAM,aAAcyN,IAAK,kBAAmBC,KAAK,mBACzDgP,GAAM,CAAE1c,KAAM,oBAAqByN,IAAK,mBAAoBC,IAAK,oBACjEiP,GAAM,CAAE3c,KAAM,SAAUyN,IAAK,kBAAmBC,IAAK,oBACrDkP,GAAM,CAAE5c,KAAM,2CAA4CyN,KAAK,kBAAoBC,KAAK,mBACxFmP,GAAM,CAAE7c,KAAM,YAAayN,IAAK,mBAAoBC,KAAK,mBACzDoP,GAAM,CAAE9c,KAAM,OAAQyN,IAAK,mBAAoBC,IAAK,oBACpDqP,GAAM,CAAE/c,KAAM,gBAAiByN,IAAK,mBAAoBC,KAAK,oBAC7DsP,GAAM,CAAEhd,KAAM,SAAUyN,IAAK,iBAAkBC,KAAK,mBACpDuP,GAAM,CAAEjd,KAAM,oCAAqCyN,KAAK,mBAAqBC,IAAK,mBAClFwP,GAAM,CAAEld,KAAM,WAAYyN,IAAK,mBAAoBC,KAAK,mBACxDyP,GAAM,CAAEnd,KAAM,UAAWyN,IAAK,kBAAmBC,IAAK,oBACtD0P,GAAM,CAAEpd,KAAM,QAASyN,IAAK,mBAAoBC,KAAK,gBACrD2P,GAAM,CAAErd,KAAM,UAAWyN,IAAK,kBAAmBC,IAAK,mBACtD4P,GAAM,CAAEtd,KAAM,YAAayN,IAAK,mBAAqBC,IAAK,oBAC1D6P,GAAM,CAAEvd,KAAM,UAAWyN,IAAK,kBAAmBC,KAAK,mBACtD8P,GAAM,CAAExd,KAAM,SAAUyN,IAAK,mBAAoBC,IAAK,oBACtD+P,GAAM,CAAEzd,KAAM,cAAeyN,IAAK,kBAAmBC,KAAK,mBAC1DgQ,GAAM,CAAE1d,KAAM,QAASyN,IAAK,mBAAoBC,IAAK,mBACrDiQ,GAAM,CAAE3d,KAAM,iCAAkCyN,KAAK,kBAAoBC,IAAK,mBAC9EkQ,GAAM,CAAE5d,KAAM,OAAQyN,IAAK,mBAAoBC,IAAK,oBACpDmQ,GAAM,CAAE7d,KAAM,OAAQyN,IAAK,mBAAoBC,IAAK,oBACpDoQ,GAAM,CAAE9d,KAAM,UAAWyN,IAAK,kBAAmBC,KAAK,mBACtDqQ,GAAM,CAAE/d,KAAM,QAASyN,IAAK,kBAAmBC,IAAK,oBACpDsQ,GAAM,CAAEhe,KAAM,SAAUyN,IAAK,mBAAoBC,KAAK,oBACtDuQ,GAAM,CAAEje,KAAM,UAAWyN,IAAK,kBAAmBC,KAAK,mBACtDwQ,GAAM,CAAEle,KAAM,SAAUyN,IAAK,mBAAoBC,IAAK,mBACtDyQ,GAAM,CAAEne,KAAM,QAASyN,IAAK,kBAAmBC,IAAK,oBACpD0Q,GAAM,CAAEpe,KAAM,QAASyN,IAAK,kBAAoBC,IAAK,mBACrD2Q,GAAM,CAAEre,KAAM,aAAcyN,IAAK,kBAAmBC,IAAK,mBACzD4Q,GAAM,CAAEte,KAAM,WAAYyN,IAAK,mBAAoBC,IAAK,oBACxD6Q,GAAM,CAAEve,KAAM,WAAYyN,IAAK,mBAAoBC,KAAK,oBACxD8Q,GAAM,CAAExe,KAAM,UAAWyN,KAAK,mBAAqBC,IAAK,mBACxD+Q,GAAM,CAAEze,KAAM,wBAAyByN,IAAK,mBAAoBC,KAAK,mBACrEgR,GAAM,CAAE1e,KAAM,cAAeyN,IAAK,kBAAmBC,IAAK,mBAC1DiR,GAAM,CAAE3e,KAAM,cAAeyN,IAAK,mBAAoBC,IAAK,oBAC3DkR,GAAM,CAAE5e,KAAM,SAAUyN,IAAK,mBAAoBC,IAAK,mBACtDmR,GAAM,CAAE7e,KAAM,iBAAkByN,IAAK,mBAAoBC,KAAK,mBAC9DoR,GAAM,CAAE9e,KAAM,aAAcyN,IAAK,mBAAoBC,IAAK,kBAC1DqR,GAAM,CAAE/e,KAAM,OAAQyN,IAAK,mBAAoBC,IAAK,oBACpDsR,GAAM,CAAEhf,KAAM,UAAWyN,IAAK,kBAAmBC,IAAK,mBACtDuR,GAAM,CAAEjf,KAAM,cAAeyN,IAAK,mBAAoBC,KAAK,kBAC3DwR,GAAM,CAAElf,KAAM,gBAAiByN,IAAK,kBAAmBC,IAAK,mBAC5DyR,GAAM,CAAEnf,KAAM,YAAayN,IAAK,kBAAmBC,IAAK,mBACxD0R,GAAM,CAAEpf,KAAM,UAAWyN,IAAK,iBAAkBC,KAAK,mBACrD2R,GAAM,CAAErf,KAAM,UAAWyN,KAAK,kBAAoBC,IAAK,mBACvD4R,GAAM,CAAEtf,KAAM,YAAayN,IAAK,kBAAmBC,IAAK,oBACxD6R,GAAM,CAAEvf,KAAM,aAAcyN,IAAK,kBAAmBC,IAAK,mBACzD8R,GAAM,CAAExf,KAAM,SAAUyN,IAAK,mBAAoBC,IAAK,oBACtD+R,GAAM,CAAEzf,KAAM,QAASyN,IAAK,mBAAoBC,IAAK,mBACrDgS,GAAM,CAAE1f,KAAM,UAAWyN,IAAK,mBAAoBC,KAAK,mBACvDiS,GAAM,CAAE3f,KAAM,SAAUyN,IAAK,kBAAmBC,IAAK,mBACrDkS,GAAM,CAAE5f,KAAM,UAAWyN,IAAK,iBAAkBC,IAAK,oBACrDmS,GAAM,CAAE7f,KAAM,aAAcyN,IAAK,kBAAmBC,IAAK,mBACzDoS,GAAM,CAAE9f,KAAM,eAAgByN,IAAK,mBAAoBC,KAAK,mBAC5DqS,GAAM,CAAE/f,KAAM,aAAcyN,KAAK,kBAAoBC,IAAK,mBAC1DsS,GAAM,CAAEhgB,KAAM,mBAAoByN,IAAK,kBAAmBC,IAAK,oBAC/DuS,GAAM,CAAEjgB,KAAM,kBAAmByN,IAAK,kBAAmBC,IAAK,mBAC9DwS,GAAM,CAAElgB,KAAM,OAAQyN,IAAK,mBAAoBC,KAAK,mBACpDyS,GAAM,CAAEngB,KAAM,UAAWyN,IAAK,mBAAoBC,IAAK,mBACvD0S,GAAM,CAAEpgB,KAAM,WAAYyN,IAAK,kBAAmBC,IAAK,mBACvD2S,GAAM,CAAErgB,KAAM,2BAA4ByN,IAAK,mBAAoBC,IAAK,oBACxE4S,GAAM,CAAEtgB,KAAM,aAAcyN,IAAK,mBAAoBC,KAAK,mBAC1D6S,GAAM,CAAEvgB,KAAM,aAAcyN,IAAK,mBAAoBC,KAAK,oBAC1D8S,GAAM,CAAExgB,KAAM,aAAcyN,IAAK,mBAAoBC,KAAK,mBAC1D+S,GAAM,CAAEzgB,KAAM,QAASyN,IAAK,mBAAoBC,IAAK,oBACrDgT,GAAM,CAAE1gB,KAAM,YAAayN,KAAK,kBAAoBC,IAAK,mBACzDiT,GAAM,CAAE3gB,KAAM,WAAYyN,KAAK,kBAAqBC,IAAK,mBACzDkT,GAAM,CAAE5gB,KAAM,SAAUyN,KAAK,mBAAqBC,IAAK,mBACvDmT,GAAM,CAAE7gB,KAAM,SAAUyN,IAAK,kBAAmBC,KAAK,oBACrDoT,GAAM,CAAE9gB,KAAM,WAAYyN,IAAK,mBAAoBC,IAAK,oBACxDqT,GAAM,CAAE/gB,KAAM,aAAcyN,KAAK,mBAAqBC,IAAK,oBAC3DsT,GAAM,CAAEhhB,KAAM,UAAWyN,KAAK,kBAAoBC,IAAK,mBACvDuT,GAAM,CAAEjhB,KAAM,gBAAiByN,KAAK,kBAAoBC,IAAK,oBAC7DwT,GAAM,CAAElhB,KAAM,QAASyN,IAAK,kBAAmBC,IAAK,kBACpDyT,GAAM,CAAEnhB,KAAM,iBAAkByN,KAAK,mBAAqBC,IAAK,oBAC/D0T,GAAM,CAAEphB,KAAM,UAAWyN,IAAK,iBAAkBC,IAAK,mBACrD2T,GAAM,CAAErhB,KAAM,YAAayN,IAAK,mBAAoBC,KAAK,iBACzD4T,GAAM,CAAEthB,KAAM,cAAeyN,IAAK,mBAAoBC,IAAK,mBAC3D6T,GAAM,CAAEvhB,KAAM,SAAUyN,IAAK,kBAAmBC,IAAK,oBACrD8T,GAAM,CAAExhB,KAAM,QAASyN,IAAK,mBAAoBC,IAAK,kBACrD+T,GAAM,CAAEzhB,KAAM,QAASyN,KAAK,kBAAqBC,IAAK,oBACtDgU,GAAM,CAAE1hB,KAAM,OAAQyN,KAAK,kBAAoBC,KAAK,oBACpDiU,GAAM,CAAE3hB,KAAM,cAAeyN,KAAK,kBAAoBC,IAAK,oBAC3DkU,GAAM,CAAE5hB,KAAM,OAAQyN,IAAK,iBAAkBC,IAAK,iBAClDmU,GAAM,CAAE7hB,KAAM,SAAUyN,IAAK,kBAAmBC,KAAK,mBACrDoU,GAAM,CAAE9hB,KAAM,OAAQyN,KAAK,kBAAoBC,KAAK,mBACpDqU,GAAM,CAAE/hB,KAAM,mBAAoByN,KAAK,mBAAqBC,KAAK,oBACjEsU,GAAM,CAAEhiB,KAAM,mBAAoByN,KAAK,kBAAoBC,IAAK,mBAChEuU,GAAM,CAAEjiB,KAAM,cAAeyN,IAAK,mBAAoBC,IAAK,oBAC3DwU,GAAM,CAAEliB,KAAM,WAAYyN,IAAK,mBAAoBC,IAAK,mBACxDyU,GAAM,CAAEniB,KAAM,SAAUyN,IAAK,kBAAmBC,IAAK,mBACrD0U,GAAM,CAAEpiB,KAAM,4BAA6ByN,IAAK,kBAAmBC,KAAK,mBACxE2U,GAAM,CAAEriB,KAAM,WAAYyN,KAAK,mBAAqBC,KAAK,mBACzD4U,GAAM,CAAEtiB,KAAM,cAAeyN,IAAK,mBAAoBC,KAAK,mBAC3D6U,GAAM,CAAEviB,KAAM,wBAAyByN,IAAK,mBAAoBC,IAAK,mBACrE8U,GAAM,CAAExiB,KAAM,WAAYyN,IAAK,kBAAmBC,KAAK,mBACvD+U,GAAM,CAAEziB,KAAM,QAASyN,IAAK,kBAAmBC,IAAK,oBACpDgV,GAAM,CAAE1iB,KAAM,WAAYyN,KAAK,kBAAoBC,KAAK,mBACxDiV,GAAM,CAAE3iB,KAAM,QAASyN,IAAK,mBAAoBC,IAAK,mBACrDkV,GAAM,CAAE5iB,KAAM,UAAWyN,KAAK,mBAAqBC,IAAK,mBACxDmV,GAAM,CAAE7iB,KAAM,UAAWyN,IAAK,kBAAmBC,IAAK,oBACtDoV,GAAM,CAAE9iB,KAAM,SAAUyN,IAAK,kBAAmBC,IAAK,mBACrDqV,GAAM,CAAE/iB,KAAM,qBAAsByN,IAAK,mBAAoBC,IAAK,kBAClEsV,GAAM,CAAEhjB,KAAM,SAAUyN,KAAK,kBAAoBC,IAAK,oBACtDuV,GAAM,CAAEjjB,KAAM,eAAgByN,IAAK,mBAAoBC,IAAK,oBAC5DwV,GAAM,CAAEljB,KAAM,kBAAmByN,KAAK,kBAAoBC,IAAK,oBAC/DyV,GAAM,CAAEnjB,KAAM,aAAcyN,KAAK,kBAAoBC,IAAK,mBAC1D0V,GAAM,CAAEpjB,KAAM,QAASyN,IAAK,kBAAmBC,IAAK,oBACpD2V,GAAM,CAAErjB,KAAM,SAAUyN,IAAK,kBAAmBC,IAAK,oBACrD4V,GAAM,CAAEtjB,KAAM,YAAayN,IAAK,mBAAoBC,IAAK,oBACzD6V,GAAM,CAAEvjB,KAAM,eAAgByN,KAAK,mBAAqBC,KAAK,mBAC7D8V,GAAM,CAAExjB,KAAM,WAAYyN,IAAK,kBAAmBC,IAAK,oBACvD+V,GAAM,CAAEzjB,KAAM,WAAYyN,IAAK,kBAAmBC,IAAK,oBACvDgW,GAAM,CAAE1jB,KAAM,WAAYyN,IAAK,kBAAmBC,IAAK,oBACvDiW,GAAM,CAAE3jB,KAAM,eAAgByN,IAAK,kBAAmBC,KAAK,mBAC3DkW,GAAM,CAAE5jB,KAAM,aAAcyN,IAAK,mBAAoBC,IAAK,oBAC1DmW,GAAM,CAAE7jB,KAAM,UAAWyN,IAAK,mBAAoBC,KAAK,oBACvDoW,GAAM,CAAE9jB,KAAM,UAAWyN,IAAK,kBAAmBC,IAAK,mBACtDqW,GAAM,CAAE/jB,KAAM,WAAYyN,IAAK,kBAAmBC,KAAK,oBACvDsW,GAAM,CAAEhkB,KAAM,cAAeyN,IAAK,kBAAmBC,IAAK,kBAC1DuW,GAAM,CAAEjkB,KAAM,wBAAyByN,IAAK,mBAAqBC,IAAK,mBACtEwW,GAAM,CAAElkB,KAAM,cAAeyN,IAAK,mBAAoBC,KAAK,mBAC3DyW,GAAM,CAAEnkB,KAAM,eAAgByN,IAAK,kBAAmBC,KAAK,mBAC3D0W,GAAM,CAAEpkB,KAAM,QAASyN,IAAK,kBAAmBC,IAAK,kBACpD2W,GAAM,CAAErkB,KAAM,WAAYyN,KAAK,mBAAqBC,IAAK,oBACzD4W,GAAM,CAAEtkB,KAAM,2BAA4ByN,IAAK,mBAAoBC,KAAK,mBACxE6W,GAAM,CAAEvkB,KAAM,OAAQyN,IAAK,mBAAoBC,IAAK,oBACpD8W,GAAM,CAAExkB,KAAM,sBAAuByN,KAAK,kBAAoBC,IAAK,mBACnE8W,GAAM,CAAExkB,KAAM,8BAA+ByN,KAAK,kBAAoBC,IAAK,mBAC3E8W,GAAM,CAAExkB,KAAM,mBAAoByN,KAAK,mBAAqBC,IAAK,oBACjE+W,GAAM,CAAEzkB,KAAM,OAAQyN,IAAK,kBAAmBC,IAAK,mBACnDgX,GAAM,CAAE1kB,KAAM,WAAYyN,IAAK,kBAAmBC,IAAK,oBACvDiX,GAAM,CAAE3kB,KAAM,aAAcyN,IAAK,kBAAmBC,IAAK,mBACzDkX,GAAM,CAAE5kB,KAAM,UAAWyN,KAAK,kBAAoBC,KAAK,oBACvDmX,GAAM,CAAE7kB,KAAM,cAAeyN,KAAK,kBAAoBC,IAAK,oBAC3DoX,GAAM,CAAE9kB,KAAM,eAAgByN,IAAK,kBAAmBC,IAAK,kBAC3DqX,GAAM,CAAE/kB,KAAM,UAAWyN,IAAK,kBAAmBC,IAAK,kBACtDsX,GAAM,CAAEhlB,KAAM,QAASyN,KAAK,kBAAoBC,KAAK,oBACrDuX,GAAM,CAAEjlB,KAAM,SAAUyN,IAAK,kBAAmBC,IAAK,mBACrDwX,GAAM,CAAEllB,KAAM,sBAAuByN,IAAK,mBAAoBC,KAAK,mBACnEyX,GAAM,CAAEnlB,KAAM,SAAUyN,KAAK,kBAAoBC,IAAK,kBACtD0X,GAAM,CAAEplB,KAAM,WAAYyN,KAAK,kBAAoBC,IAAK,mBACxD2X,GAAM,CAAErlB,KAAM,UAAWyN,IAAK,mBAAoBC,IAAK,mBACvD4X,GAAM,CAAEtlB,KAAM,SAAUyN,IAAK,mBAAoBC,IAAK,mBACtD6X,GAAM,CAAEvlB,KAAM,uCAAwCyN,IAAK,mBAAoBC,IAAK,oBACpF8X,GAAM,CAAExlB,KAAM,gBAAiByN,IAAK,iBAAkBC,KAAK,mBAC3D+X,GAAM,CAAEzlB,KAAM,UAAWyN,KAAK,kBAAoBC,KAAK,mBACvDgY,GAAM,CAAE1lB,KAAM,aAAcyN,IAAK,iBAAkBC,IAAK,kBACxDiY,GAAM,CAAE3lB,KAAM,eAAgByN,IAAK,kBAAmBC,IAAK,oBAC3DkY,GAAM,CAAE5lB,KAAM,mCAAoCyN,IAAK,mBAAoBC,KAAK,oBAChFmY,GAAM,CAAE7lB,KAAM,YAAayN,IAAK,kBAAmBC,KAAK,mBACxDoY,GAAM,CAAE9lB,KAAM,yBAA0ByN,IAAK,kBAAmBC,KAAK,mBACrEqY,GAAM,CAAE/lB,KAAM,oBAAqByN,IAAK,mBAAoBC,KAAK,mBACjEsY,GAAM,CAAEhmB,KAAM,UAAWyN,IAAK,mBAAoBC,IAAK,oBACvDuY,GAAM,CAAEjmB,KAAM,UAAWyN,KAAK,mBAAqBC,IAAK,oBACxDwY,GAAM,CAAElmB,KAAM,oBAAqByN,KAAK,mBAAqBC,KAAK,oBAClEyY,GAAM,CAAEnmB,KAAM,QAASyN,KAAK,mBAAqBC,KAAK,oBACtD0Y,GAAM,CAAEpmB,KAAM,QAASyN,IAAK,mBAAoBC,IAAK,mBACrD2Y,GAAM,CAAErmB,KAAM,UAAWyN,KAAK,mBAAqBC,IAAK,oBACxD4Y,GAAM,CAAEtmB,KAAM,eAAgByN,KAAK,kBAAoBC,IAAK,mBAC5D6Y,GAAM,CAAEvmB,KAAM,SAAUyN,KAAK,mBAAqBC,IAAK,mBACvD8Y,GAAM,CAAExmB,KAAM,WAAYyN,KAAK,kBAAoBC,IAAK,qBCtP1D,MAAM+Y,8BAA8BhxB,EAAAA,KAChC,WAAAC,CAAYC,EAAU,IAClB,MAAM+wB,EAAY/wB,EAAQmC,QAAU,IACpClC,MAAM,CACFO,UAAW,8BACRR,IAGPS,KAAKuC,SAAWhD,EAAQgD,UAAY,qBACpCvC,KAAKsB,QAAU/B,EAAQ+B,SAAW,SAClCtB,KAAKuwB,SAAWhxB,EAAQgxB,UAAY,KACpCvwB,KAAKqB,MAAQ9B,EAAQ8B,OAAS,KAC9BrB,KAAKkB,YAAc3B,EAAQ2B,aAAe,OAC1ClB,KAAKwwB,aAAejxB,EAAQixB,cAAgB,GAC5CxwB,KAAKywB,YAAclxB,EAAQkxB,aAAe,SAC1CzwB,KAAK0B,OAAS4uB,EACdtwB,KAAKya,SAAWlb,EAAQkb,UAAY,OACpCza,KAAK0wB,WAAanxB,EAAQmxB,YAAc,CAAA,EACxC1wB,KAAK2wB,YAAoC,IAAvBpxB,EAAQoxB,WAC1B3wB,KAAK4wB,YAAcrxB,EAAQqxB,aAAe,CAAEtZ,KAAK,OAASD,IAAK,OAAQzN,KAAM,cAC7E5J,KAAK6wB,aAAc,CACvB,CAEA,iBAAMlwB,GACF,MAAO,oHAEmDX,KAAKiM,yBAAyBjM,KAAK0B,2HAIjG,CAEA,YAAMd,GACFZ,KAAK8wB,SAAW3a,SAASmF,cAAc,OACvCtb,KAAK8wB,SAAS/wB,UAAY,6BAC1BC,KAAKyE,QAAQiX,YAAY1b,KAAK8wB,UAE9B9wB,KAAKyX,QAAU,IAAI4C,aAAa,CAC5BlY,YAAa,GAAGnC,KAAKiM,SACrBvK,OAAQ1B,KAAK0B,OACbkO,MAAO5P,KAAKya,SACZ3C,KAAM9X,KAAK0wB,WAAW5Y,MAAQ,IAC9BwC,OAAQta,KAAK0wB,WAAWpW,QAAU,CAAC,GAAI,IACvCC,MAAOva,KAAK0wB,WAAWnW,OAAS,GAChCC,QAASxa,KAAK0wB,WAAWlW,SAAW,EACpCE,sBAAuB1a,KAAK0wB,WAAWhW,wBAAyB,EAChEC,cAAe3a,KAAK0wB,WAAW/V,gBAAiB,IAEpD3a,KAAKoC,SAASpC,KAAKyX,eACbzX,KAAKiF,SACf,CAEA,aAAMA,GACF,IAAIjF,KAAK6wB,YAAT,CACA7wB,KAAK6wB,aAAc,EACnB7wB,KAAK+wB,UAAU,qBACf,IACA,MAAMC,QAAgBhxB,KAAKixB,qBACrBjxB,KAAKkxB,aAAaF,GACpBhxB,KAAK+wB,UAAU,GACnB,OAASztB,GACLC,QAAQD,MAAM,sCAAuCA,GACrDtD,KAAK+wB,UAAU,kCACnB,CAAA,QACI/wB,KAAK6wB,aAAc,CACvB,CAZsB,CAa1B,CAEA,kBAAMI,GACF,MAAMpuB,EAAO7C,KAAK4C,UAAUC,KAC5B,IAAKA,EACD,MAAM,IAAIsuB,MAAM,2BAEpB,MAAMnpB,EAAS,CACX1G,QAAStB,KAAKsB,QACdJ,YAAalB,KAAKkB,YAClBkwB,aAAa,GAGjB,GADIpxB,KAAKuwB,WAAUvoB,EAAOuoB,SAAWvwB,KAAKuwB,UACtCvwB,KAAKqB,MAAO,CACZ,MAAMgwB,EAAaxT,MAAMC,QAAQ9d,KAAKqB,OAASrB,KAAKqB,MAAQ,CAACrB,KAAKqB,OAClE2G,EAAO,WAAaqpB,CACxB,CACA,MAAM1uB,QAAiBE,EAAKC,IAAI9C,KAAKuC,SAAUyF,GAC/C,IAAKrF,EAASI,UAAYJ,EAASK,MAAMC,OACrC,MAAM,IAAIkuB,MAAMxuB,EAASK,MAAMM,OAAS,qBAE5C,OAAOX,EAASK,KAAKA,IACzB,CAEA,kBAAMkuB,CAAaluB,GACf,MAAMguB,EAAUhuB,GAAMA,MAAQ,CAAA,EACxBsuB,EAAStuB,GAAMsuB,QAAU,GACzB9Q,EAAU,GAehB,GAbAtd,OAAOsd,QAAQwQ,GAAS1Y,QAAQ,EAAEiZ,EAAKC,MACnC,MAAMC,EAAQD,EAAOE,OAAO,CAACC,EAAKvnB,IAAUunB,GAAO1T,OAAO7T,IAAU,GAAI,GACxE,IAAKqnB,EAAO,OACZ,MAAMG,EAAW5Q,EAAkBuQ,EAAIM,eAClCD,GACLpR,EAAQxI,KAAK,CACT8Z,KAAMP,EAAIM,cACVJ,QACAM,OAAQP,EACRI,gBAIHpR,EAAQrD,OAIT,OAHAnd,KAAKyX,QAAQgG,cAAc,IAC3Bzd,KAAKgyB,aAAa,SAClBhyB,KAAK+wB,UAAU,qDAInBvQ,EAAQ3S,KAAK,CAACokB,EAAGC,IAAMA,EAAET,MAAQQ,EAAER,OACnC,MAAMU,EAAa3R,EAAQ4R,MAAM,EAAGpyB,KAAKwwB,cACnC6B,EAAWF,EAAW,IAAIV,OAAS,EAEnC9Z,EAAUwa,EAAWvX,IAAI0X,IAC3B,MAAMC,EAAYD,EAAMb,MAAQY,EAC1BrU,EAAawU,KAAKC,MAAM,GAAiB,GAAZF,GACnC,MAAO,CACHjb,IAAKgb,EAAMV,SAASta,IACpBD,IAAKib,EAAMV,SAASva,IACpBlP,KAAM6V,EACNpc,MAAO5B,KAAK0yB,eAAeH,GAC3B3a,MAAO,oFAEW0a,EAAMV,SAAShoB,wEACE0oB,EAAMb,MAAM1tB,oBAAoB/D,KAAKywB,sEAM5EzwB,KAAK2wB,YAAc3wB,KAAK4wB,aAAatZ,KAAOtX,KAAK4wB,aAAavZ,KAC9DM,EAAQK,KAAK,CACTV,IAAKtX,KAAK4wB,YAAYtZ,IACtBD,IAAKrX,KAAK4wB,YAAYvZ,IACtBlP,KAAM,GACNvG,MAAO,UACPhC,KAAM,sBACNgY,MAAO,oFAEW5X,KAAK4wB,YAAYhnB,MAAQ,iJAOnD5J,KAAKyX,QAAQgG,cAAc9F,GAC3B3X,KAAKgyB,aAAaG,EAAYb,GAC1BtxB,KAAK2wB,YACL3wB,KAAK2yB,aAAaR,EAAYE,EAEtC,CAEA,cAAAK,CAAeH,GACX,MACMK,EAAM,CAAC,IAAK,IAAK,GACjBC,EAFQ,CAAC,GAAI,IAAK,KAENjY,IAAI,CAACxQ,EAAO0oB,IAC1BN,KAAKC,MAAMroB,GAASwoB,EAAIE,GAAO1oB,GAASmoB,IAE5C,MAAO,QAAQM,EAAI,OAAOA,EAAI,OAAOA,EAAI,UAC7C,CAEA,YAAAb,CAAaxR,GACT,MAAMuS,EAAW/yB,KAAKyE,QAAQG,cAAc,0BAC5C,IAAKmuB,EAAU,OACf,IAAKvS,EAAQrD,OAET,YADA4V,EAASntB,UAAY,IAIzB,MAAMysB,EAAW7R,EAAQ,IAAIiR,OAAS,EAChCuB,EAAOxS,EAAQ5F,IAAI0X,IACrB,MAAMW,GAAYX,EAAMb,MAAQY,EAAY,KAAKa,QAAQ,GACzD,MAAO,mLAGiCZ,EAAMV,SAAShoB,uEACV0oB,EAAMR,0IAGZQ,EAAMb,MAAM1tB,6EACXkvB,wFAIzCzb,KAAK,IAERub,EAASntB,UAAYotB,CACzB,CAEA,YAAAL,CAAanS,EAAS6R,GAClB,MAAMc,EAASnzB,KAAK4wB,aAAe,KACnC,KAAKuC,GAAWA,EAAO7b,KAAQ6b,EAAO9b,KAAQrX,KAAKyX,SAAS,OAE5D,MAeM2b,EAAU,CACZvpB,KAAM,oBACNwpB,SAjBa7S,EAAQ5F,IAAI0X,IAAA,CACzBzoB,KAAM,UACNypB,SAAU,CACNzpB,KAAM,aACN0pB,YAAa,CACT,CAACJ,EAAO7b,IAAK6b,EAAO9b,KACpB,CAACib,EAAMV,SAASta,IAAKgb,EAAMV,SAASva,OAG5Cmc,WAAY,CACR/B,MAAOa,EAAMb,MACbc,UAAWD,EAAMb,MAAQY,OAuCjCryB,KAAKyX,QAAQsD,YAAc/a,KAAKyX,QAAQsD,YAAY7V,OAAO4W,GAAOA,EAAI7P,KAAO,GAAGjM,KAAKiM,aACrFjM,KAAKyX,QAAQ6I,iBAAiB,GAAGtgB,KAAKiM,YAAa,CAC/CjJ,KAAMowB,EACNxT,MAjCU,CACV,aAAc,CACV,cACA,CAAC,UACD,CAAC,MAAO,aACR,EACA,0BACA,EACA,2BAEJ,aAAc,CACV,cACA,CAAC,UACD,CAAC,MAAO,aACR,EACA,EACA,EACA,GAEJ,eAAgB,CACZ,cACA,CAAC,UACD,CAAC,MAAO,aACR,EACA,GACA,EACA,MASZ,CAEA,SAAAmR,CAAU3a,GACDpW,KAAK8wB,WACV9wB,KAAK8wB,SAAS2C,YAAcrd,GAAW,GACvCpW,KAAK8wB,SAASlhB,MAAM2O,QAAUnI,EAAU,QAAU,OACtD,ECzPJ,MAAMsd,gCAAgCr0B,EAAAA,KAClC,WAAAC,CAAYC,EAAU,IAClBC,MAAM,IACCD,EACHQ,UAAW,8BAGfC,KAAK4G,MAAQ,IAAI+sB,eACrB,CAEA,iBAAMhzB,GACF,MAAO,2xHAoEX,CAEA,oBAAM8B,SACIzC,KAAK4G,MAAM4M,OACrB,EAIJ,MAAMogB,8BAA8BlwB,EAAAA,KAChC,WAAApE,CAAYC,EAAU,IAClBC,MAAM,IACCD,EACHE,MAAO,sBACPM,UAAW,2BAEnB,CAEA,iBAAMY,GACF,MAAO,koLAuGX,CAEA,YAAMC,GACFZ,KAAKoD,OAAS,IAAIswB,wBAAwB,CACtCvxB,YAAa,WAEjBnC,KAAKoC,SAASpC,KAAKoD,QAEnBpD,KAAK6zB,aAAe,IAAI/yB,yBAAuB,CAC3CqB,YAAa,gBACbvC,KAAM,sBACNH,MAAO,gBACPsB,SAAU,kEACVC,WAAY,UACZC,UAAW,UACXsB,SAAU,qBACVrB,YAAa,OACbG,MAAO,CAAC,mBACRC,QAAS,WACTC,UAAW,OACXC,aAAa,EACbC,WAAW,EACXC,OAAQ,IACRE,MAAO,wBACPC,MAAM,EACNC,UAAW,wBACXC,UAAW,GACX+xB,iBAAkB,KAClBC,YAAa,SACb/xB,cAAc,EACdC,cAAc,EACd+xB,YAAa,8BAEjBh0B,KAAKoC,SAASpC,KAAK6zB,cAEnB7zB,KAAKi0B,gBAAkB,IAAInzB,yBAAuB,CAC9CqB,YAAa,mBACbvC,KAAM,kCACNH,MAAO,mBACPsB,SAAU,kEACVC,WAAY,UACZC,UAAW,UACXsB,SAAU,qBACVrB,YAAa,OACbG,MAAO,CAAC,aACRC,QAAS,WACTC,UAAW,OACXC,aAAa,EACbC,WAAW,EACXC,OAAQ,IACRE,MAAO,wBACPC,MAAM,EACNC,UAAW,yBACXC,UAAW,GACX+xB,iBAAkB,KAClBC,YAAa,SACb/xB,cAAc,EACdC,cAAc,EACd+xB,YAAa,iCAEjBh0B,KAAKoC,SAASpC,KAAKi0B,iBAEnBj0B,KAAKk0B,qBAAuB,IAAIhwB,eAAa,CACzCzE,MAAO,wEACP8C,SAAU,qBACVjB,QAAS,WACTivB,SAAU,6BACVrvB,YAAa,OACbK,UAAW,OACXW,eAAe,EACfiyB,mBAAmB,EACnBzyB,OAAQ,IACR0yB,YAAa,GACbC,OAAQ,CACJ,4BAEJlwB,MAAO,CACHxE,MAAO,SACPyE,aAAa,GAEjBC,QAAS,CAAEC,EAAG,YACdnC,YAAa,4BAEjBnC,KAAKoC,SAASpC,KAAKk0B,sBAEnBl0B,KAAKs0B,wBAA0B,IAAIpwB,eAAa,CAC5CzE,MAAO,0DACP8C,SAAU,qBACVjB,QAAS,WACTivB,SAAU,sBACVrvB,YAAa,OACbK,UAAW,OACXW,eAAe,EACfiyB,mBAAmB,EACnBzyB,OAAQ,IACR0yB,YAAa,GACbC,OAAQ,CACJ,2BAEJlwB,MAAO,CACHxE,MAAO,YACPyE,aAAa,GAEjBC,QAAS,CAAEC,EAAG,YACdnC,YAAa,+BAEjBnC,KAAKoC,SAASpC,KAAKs0B,yBAEnBt0B,KAAKu0B,iBAAmB,IAAIlE,sBAAsB,CAC9CluB,YAAa,qBACbouB,SAAU,6BACVjvB,QAAS,WACTkvB,aAAc,GACdC,YAAa,SACb/uB,OAAQ,IACR+Y,SAAU,SAEdza,KAAKoC,SAASpC,KAAKu0B,kBAEnB,MAAMC,EAAsB,IAAIC,aAAW,CACvCzsB,OAAQ,CACJ/E,OAAQ,SAGhBjD,KAAK00B,eAAiB,IAAItsB,YAAU,CAChCjG,YAAa,mBACb1C,MAAO,cACP4I,WAAYmsB,EACZjsB,QAAS,CACL,CAAEC,IAAK,KAAM7I,MAAO,MACpB,CAAE6I,IAAK,QAAS7I,MAAO,SACvB,CAAE6I,IAAK,WAAY7I,MAAO,eAGlCK,KAAKoC,SAASpC,KAAK00B,gBAEnB,MAAMC,EAAyB,IAAIC,eAAa,CAC5C5sB,OAAQ,CACJ/E,OAAQ,SAGhBjD,KAAK60B,2BAA6B,IAAIzsB,YAAU,CAC5CjG,YAAa,gCACb1C,MAAO,gBACP4I,WAAYssB,EACZpsB,QAAS,CACL,CAAEC,IAAK,KAAM7I,MAAO,MACpB,CAAE6I,IAAK,QAAS7I,MAAO,SACvB,CAAE6I,IAAK,SAAU7I,MAAO,SAAU8I,UAAW,YAGrDzI,KAAKoC,SAASpC,KAAK60B,2BACvB,CAEA,wBAAMtwB,CAAmBC,EAAOC,GAC5B,MAAMC,EAASD,GAAWD,GAAOG,eAAiB,KAC5C/E,EAAO8E,GAAQE,gBAAgB,KACrChF,GAAMiF,UAAUC,IAAI,WAChBJ,IAAQA,EAAOK,UAAW,GAE9B,MAAM+vB,EAAe,CACjB90B,KAAKoD,QAAQwD,OAAO4M,SAASuhB,KAAK,IAAM/0B,KAAKoD,OAAOC,UACpDrD,KAAK6zB,cAAc5uB,UACnBjF,KAAKi0B,iBAAiBhvB,UACtBjF,KAAKk0B,sBAAsBjvB,UAC3BjF,KAAKs0B,yBAAyBrvB,UAC9BjF,KAAKu0B,kBAAkBtvB,UACvBjF,KAAK00B,gBAAgBrsB,YAAYmL,QACjCxT,KAAK60B,4BAA4BxsB,YAAYmL,SAC/CtO,OAAOC,eAEHC,QAAQC,WAAWyvB,GAEzBl1B,GAAMiF,UAAUiB,OAAO,WACnBpB,IAAQA,EAAOK,UAAW,EAClC,ECjYJ,MAAMiwB,uBAAuB31B,EAAAA,KACzB,WAAAC,CAAYC,EAAU,IAClBC,MAAM,CACFO,UAAW,sBACRR,IAGPS,KAAKi1B,WAAa11B,EAAQ01B,YAAc,GAExCj1B,KAAKmH,SAAW,+oEAsDpB,CAEA,oBAAM1E,GACFzC,KAAKk1B,oBAAsBl1B,KAAKm1B,iBAAiBn1B,KAAKi1B,WAC1D,CAEA,gBAAAE,CAAiBF,GACb,IAAKA,EACD,MAAO,6DAIX,MAEMG,GAFiC,iBAAfH,EAA0BA,EAAaI,KAAKC,UAAUL,EAAY,KAAM,IAEzEM,MAAM,MAC7B,IAAIC,EAAO,GA4DX,OA1DAJ,EAAM9c,QAAQ,CAACmd,EAAMC,KACjB,IAAKD,EAAKE,OAEN,YADAH,GAAQ,8CAKZ,GAAc,IAAVE,IAAgBD,EAAKvgB,SAAS,WAAaugB,EAAKvgB,SAAS,eAEzD,YADAsgB,GAAQ,mDAAmDx1B,KAAK41B,WAAWH,YAS/E,IAAII,EAAQJ,EAAKI,MAHG,mCAIpB,GAAIA,EAAO,CACP,MAAM,CAAGC,EAAUC,EAAUC,EAASC,GAAUJ,EAKhD,YAJAL,GAAQ,2GACiCx1B,KAAK41B,WAAWE,EAASH,4EACvB31B,KAAK41B,WAAWG,4CAAmDC,YAAkBC,oCAGpI,CAGA,GADAJ,EAAQJ,EAAKI,MAZa,gCAatBA,EAAO,CACP,OAASE,EAAUC,EAASC,GAAUJ,EAItC,YAHAL,GAAQ,8GACoCx1B,KAAK41B,WAAWG,4CAAmDC,YAAkBC,mCAGrI,CAKA,GADAJ,EAAQJ,EAAKI,MADS,iDAElBA,EAAO,CACP,OAASE,EAAUC,EAASF,GAAYD,EAKxC,YAJAL,GAAQ,iHACuCx1B,KAAK41B,WAAWG,mDAA0DC,gFAChFh2B,KAAK41B,WAAWE,oCAG7D,CAGIL,EAAKE,OAAOO,WAAW,OACvBV,GAAQ,kDAAkDx1B,KAAK41B,WAAWH,WAK9ED,GAAQ,qDAAqDx1B,KAAK41B,WAAWH,aAG1ED,CACX,CAEA,gBAAAW,CAAiBC,GACbp2B,KAAKi1B,WAAamB,EAClBp2B,KAAKqD,QACT,ECpJJ,MAAMgzB,uBACF,WAAA/2B,CAAYg3B,GACRt2B,KAAKs2B,WAAaA,EAClBt2B,KAAKqI,WAAa,IAAIkuB,EAAAA,oBAAoB,CAAEvuB,OAAQ,CAAEwuB,SAAUx2B,KAAKs2B,aACzE,CAEA,WAAM9iB,GAEF,aADMxT,KAAKqI,WAAWmL,QACfxT,KAAKqI,WAAWouB,OAAO7b,OAAY5a,KAAK02B,UAAUxnB,GAC7D,CAEA,SAAAwnB,CAAUxnB,GACN,MAAO,CACHjD,GAAIiD,EAAKhH,IAAI,MACb2B,KAA2B,YAArBqF,EAAKhH,IAAI,QAAwB,eAAiB,eACxDyuB,OAAQ,CACJ/sB,KAAMsF,EAAKhH,IAAI,oBAAsB,SACrC0uB,UAAW1nB,EAAKhH,IAAI,kBAExBxC,UAAWwJ,EAAKhH,IAAI,WACpB2uB,QAAS3nB,EAAKhH,IAAI,QAClB4uB,YAAa,GAErB,CAEA,aAAMC,CAAQ/zB,GACV,MAAMg0B,EAAU,IAAIC,kBAAgB,CAChCT,SAAUx2B,KAAKs2B,WACfY,KAAMl0B,EAAKiT,KACXkhB,KAAM,YAEJ/mB,QAAa4mB,EAAQzqB,OAI3B,OAHI6D,EAAKrN,eACC/C,KAAKqI,WAAWmL,QAEnBpD,CACX,ECvBJ,MAAMgnB,qBAAqB/3B,EAAAA,KACvB,WAAAC,CAAYC,EAAU,IAClBC,MAAM,CACFO,UAAW,mBACRR,IAGPS,KAAK4G,MAAQrH,EAAQqH,OAAS,IAAIywB,EAAAA,SAAS93B,EAAQyD,MAAQ,IAC3DhD,KAAKs3B,aAAet3B,KAAKu3B,mBAAmBv3B,KAAK4G,MAAMsB,IAAI,UAE3DlI,KAAKmH,SAAW,8/CA+BpB,CAEA,kBAAAowB,CAAmBC,GACf,MAAMC,EAAID,GAAO1iB,cACjB,MAAU,aAAN2iB,GAA0B,WAANA,EAAuB,CAAE73B,KAAM,uBAAwBgC,MAAO,gBAC5E,QAAN61B,GAAqB,WAANA,EAAuB,CAAE73B,KAAM,+BAAgCgC,MAAO,eAC/E,WAAN61B,GAAwB,WAANA,EAAuB,CAAE73B,KAAM,uBAAwBgC,MAAO,gBAC7E,CAAEhC,KAAM,wBAAyBgC,MAAO,iBACnD,CAEA,YAAMhB,GAEFZ,KAAK0Q,aAAe,IAAIrJ,UAAS,CAC7BT,MAAO5G,KAAK4G,MACZ7G,UAAW,MACXwI,QAAS,EACThB,OAAQ,CACJ,CAAEqC,KAAM,KAAMjK,MAAO,eACrB,CAAEiK,KAAM,QAASjK,MAAO,QAASsK,OAAQ,SACzC,CAAEL,KAAM,WAAYjK,MAAO,YAC3B,CAAEiK,KAAM,WAAYjK,MAAO,YAC3B,CAAEiK,KAAM,aAAcjK,MAAO,iBAC7B,CAAEiK,KAAM,WAAYjK,MAAO,oBAC3B,CAAEiK,KAAM,UAAWjK,MAAO,UAAW4I,QAAS,GAAI0B,OAAQ,UAKlE,MAAMtB,EAAmB,IAAIC,oBAAkB,CAC3CZ,OAAQ,CAAEwuB,SAAUx2B,KAAK4G,MAAMsB,IAAI,SAEvClI,KAAKiH,WAAa,IAAImB,YAAU,CAC5BC,WAAYM,EACZL,oBAAqB,CAAC,YACtBC,QAAS,CACL,CAAEC,IAAK,KAAM7I,MAAO,KAAMoJ,MAAO,OAAQL,UAAU,GACnD,CAAEF,IAAK,UAAW7I,MAAO,OAAQ8I,UAAW,WAAYC,UAAU,EAAMK,MAAO,SAC/E,CAAEP,IAAK,WAAY7I,MAAO,WAAY8I,UAAW,QAASC,UAAU,GACpE,CAAEF,IAAK,QAAS7I,MAAO,QAAS+I,UAAU,GAC1C,CAAEF,IAAK,QAAS7I,MAAO,QAAS+I,UAAU,EAAMK,MAAO,SAE3DwF,SAAS,EACTmpB,QAAS,CAAC,QACVrpB,WAAW,EACXlG,KAAM,KAIV,MAAMwvB,EAAU,IAAItB,uBAAuBr2B,KAAK4G,MAAMsB,IAAI,OAC1DlI,KAAK43B,YAAc,IAAIC,WAAS,CAAEF,YAElC,MAAMltB,EAAO,CACTkG,SAAY3Q,KAAK0Q,aACjB7F,OAAU7K,KAAKiH,WACf,qBAAsBjH,KAAK43B,aAGzBE,EAAW93B,KAAK4G,MAAMsB,IAAI,aAAe,CAAA,EAG3C4vB,EAASC,cACT/3B,KAAKg4B,eAAiB,IAAIhD,eAAe,CACrCC,WAAY6C,EAASC,cAEzBttB,EAAK,eAAiBzK,KAAKg4B,gBAI3B90B,OAAO+0B,KAAKH,GAAU3a,OAAS,IAC/Bnd,KAAK+W,aAAe,IAAI1X,OAAK,CACzBuH,MAAO5G,KAAK4G,MACZO,SAAU,0FAEdsD,EAAe,SAAIzK,KAAK+W,cAG5B/W,KAAK8G,QAAU,IAAI0D,UAAQ,CACvBrI,YAAa,gBACbsI,OACAS,UAAW,aAEflL,KAAKoC,SAASpC,KAAK8G,SAGnB,MAAMoxB,EAAe,IAAI3sB,cAAY,CACjCpJ,YAAa,wBACbqJ,QAASxL,KAAK4G,MACd6E,OAAQ,CACJ7L,KAAM,yBACN8L,MAAO,CACH,CAAE/L,MAAO,gBAAiBE,OAAQ,gBAAiBD,KAAM,aACzD,CAAED,MAAO,UAAWE,OAAQ,mBAAoBD,KAAM,mBACtD,CAAEiK,KAAM,WACR,CAAElK,MAAO,kBAAmBE,OAAQ,kBAAmBD,KAAM,WAAYiR,QAAQ,OAI7F7Q,KAAKoC,SAAS81B,EAClB,CAEA,0BAAMC,SACiBpsB,EAAAA,OAAOC,cAAc,CACpCvM,MAAO,kBAAkBO,KAAK4G,MAAMqF,KACpCrF,MAAO5G,KAAK4G,MACZuF,WAAYisB,EAAAA,cAActsB,QAG1B9L,KAAKqD,QAEb,CAEA,6BAAMg1B,SACIr4B,KAAK4G,MAAM2F,KAAK,CAAEirB,MAAO,aAC/Bx3B,KAAKqD,SACLrD,KAAKwF,KAAK,mBAAoB,CAAEoB,MAAO5G,KAAK4G,OAChD,CAEA,4BAAM0xB,SACsBvsB,EAAAA,OAAOO,QAC3B,iDACA,mBACA,CAAEmN,aAAc,aAAcC,YAAa,mBAGxB1Z,KAAK4G,MAAMsK,WACrBnO,SACL/C,KAAKwF,KAAK,mBAAoB,CAAEoB,MAAO5G,KAAK4G,OAGxD,EAGJywB,EAAAA,SAAShqB,WAAa+pB,aCnLtB,MAAMmB,0BAA0BhrB,EAAAA,UAC5B,WAAAjO,CAAYC,EAAU,IAClBC,MAAM,IACCD,EACHqK,KAAM,kBACN4D,SAAU,mBACVrH,OAAQ,kBACRsH,WAAYmnB,EAAAA,aAEZzgB,WAAYikB,EAAAA,cAAchrB,OAC1BgE,SAAUgnB,EAAAA,cAActsB,KACxBuF,cAAe+lB,aAEfzpB,kBAAmB,CACfvK,QAAQ,EACR+E,KAAM,MAGVyF,aAAc,CACVC,KAAM,MACN5K,OAAQ,OAIZsF,QAAS,CACL,CACIC,IAAK,KACL7I,MAAO,KACPoJ,MAAO,OACPL,UAAU,EACVoF,MAAO,cAEX,CACItF,IAAK,SAAU7I,MAAO,SACtBuF,OAAQ,CACJ2E,KAAM,cACNtK,QAAS,CAAC,MAAO,OAAQ,SAAU,WAAY,KAAM,aAG7D,CACIiJ,IAAK,UACL7I,MAAO,UACP8I,UAAW,kBAEf,CACID,IAAK,QACL7I,MAAO,QACP+I,UAAU,EACVxD,OAAQ,CAAC2E,KAAK,SAElB,CACIrB,IAAK,WACL7I,MAAO,WACP+I,UAAU,EACVxD,OAAQ,CAAC2E,KAAK,SAElB,CACIrB,IAAK,WACL7I,MAAO,WACPuF,OAAQ,CAAC2E,KAAK,SAElB,CACIrB,IAAK,QACL7I,MAAO,QACP8I,UAAW,4CAInBuF,QAAS,CACL,CACIxF,IAAK,gBACL7I,MAAO,eACPuF,OAAQ,CAAC2E,KAAK,UAKtBqE,YAAY,EACZC,YAAY,EACZzF,UAAU,EACV0F,YAAY,EACZC,WAAW,EAGXC,aAAa,EACbC,SAAS,EACTC,YAAY,EAGZC,aAAc,0EAGd6C,iBAAkB,MAClBC,aAAc,CACV,CAAE5R,MAAO,OAAQC,KAAM,qBAAsBC,OAAQ,QACrD,CAAEF,MAAO,UAAWC,KAAM,qBAAsBC,OAAQ,WACxD,CAAEF,MAAO,QAASC,KAAM,qBAAsBC,OAAQ,SACtD,CAAEF,MAAO,SAAUC,KAAM,iBAAkBC,OAAQ,UACnD,CAAEF,MAAO,QAASC,KAAM,cAAeC,OAAQ,UAInD8O,aAAc,CACVC,SAAS,EACTC,UAAU,EACVC,OAAO,EACPC,YAAY,IAGxB,CAEA,0BAAMypB,CAAqBh0B,EAAOC,GAC9B,MAAMg0B,EAAWz4B,KAAKma,UAAUue,mBAChC,IAAKD,EAAStb,OAAQ,OAEtB,MAAMhK,EAAMnT,KAAK4C,eACIuQ,EAAI7G,QAAQ,kCAAkCmsB,EAAStb,6BAEtE/X,QAAQuzB,IAAIF,EAAS7d,IAAI1L,GAAQA,EAAKtI,MAAM2F,KAAK,CAACtJ,OAAQ,eAChEjD,KAAKma,UAAU9R,WAAWmL,QAC9B,CAEA,uBAAMolB,CAAkBp0B,EAAOC,GAC3B,MAAMg0B,EAAWz4B,KAAKma,UAAUue,mBAChC,IAAKD,EAAStb,OAAQ,OAEtB,MAAMhK,EAAMnT,KAAK4C,eACIuQ,EAAI7G,QAAQ,iCAAiCmsB,EAAStb,6BAErE/X,QAAQuzB,IAAIF,EAAS7d,IAAI1L,GAAQA,EAAKtI,MAAM2F,KAAK,CAACtJ,OAAQ,WAChEjD,KAAKma,UAAU9R,WAAWmL,QAC9B,CAEA,wBAAMqlB,CAAmBr0B,EAAOC,GAC5B,MAAMg0B,EAAWz4B,KAAKma,UAAUue,mBAChC,IAAKD,EAAStb,OAAQ,OAEtB,MAAMhK,EAAMnT,KAAK4C,eACIuQ,EAAI7G,QAAQ,kCAAkCmsB,EAAStb,6BAEtE/X,QAAQuzB,IAAIF,EAAS7d,IAAI1L,GAAQA,EAAKtI,MAAM2F,KAAK,CAACtJ,OAAQ,aAChEjD,KAAKma,UAAU9R,WAAWmL,QAC9B,CAEA,yBAAMslB,CAAoBt0B,EAAOC,GAC7B,MAAMg0B,EAAWz4B,KAAKma,UAAUue,mBAChC,IAAKD,EAAStb,OAAQ,OAEtB,MAAMhK,EAAMnT,KAAK4C,eACIuQ,EAAI7G,QAAQ,mCAAmCmsB,EAAStb,6BAEvE/X,QAAQuzB,IAAIF,EAAS7d,IAAI1L,GAAQA,EAAKtI,MAAM2F,KAAK,CAACtJ,OAAQ,cAChEjD,KAAKma,UAAU9R,WAAWmL,QAC9B,CAEA,wBAAMulB,CAAmBC,EAAQC,GAC7B,MAAMR,EAAWz4B,KAAKma,UAAUue,mBAChC,IAAKD,EAAStb,OAAQ,OAEtB,MAAMhK,EAAMnT,KAAK4C,SACXs2B,QAAe/lB,EAAI5D,SAAS,CAC9B9P,MAAO,SAASg5B,EAAStb,mBACzB5V,OAAQ,CACJ,CACIqC,KAAM,QACNC,KAAM,SACNlK,MAAO,yBACPJ,QAASk5B,EAAS7d,IAAI1L,IAAA,CAAU9E,MAAO8E,EAAKtI,MAAMqF,GAAItM,MAAOuP,EAAKtI,MAAMqF,MACxE6D,UAAU,MAItB,IAAKopB,EAAQ,OAGb,MAAMC,EAAcV,EAASW,KAAKlqB,GAAQA,EAAKtI,MAAMqF,IAAMitB,EAAOG,QAAQzyB,MAC1E,IAAKuyB,EAAa,OAGlB,MAAMG,EAAWb,EACZ7d,IAAI1L,GAAQA,EAAKtI,MAAMqF,IACvB/G,OAAO+G,GAAMA,GAAMitB,EAAOG,aAGzBF,EAAY5sB,KAAK,CAAE8sB,MAAOC,IAChCt5B,KAAKma,UAAU9R,WAAWmL,OAC9B,ECvLJ,MAAM+lB,kBAAkBl6B,EAAAA,KACpB,WAAAC,CAAYC,EAAU,IAClBC,MAAM,CACFO,UAAW,gBACRR,IAGPS,KAAK4G,MAAQrH,EAAQqH,OAAS,IAAI4yB,EAAAA,cAAcj6B,EAAQyD,MAAQ,IAChEhD,KAAKy5B,UAAYz5B,KAAK05B,gBAAgB15B,KAAK4G,MAAMsB,IAAI,UAErDlI,KAAKmH,SAAW,mqCAyBpB,CAEA,eAAAuyB,CAAgBC,GACZ,OAAIA,GAAS,GAAW,CAAE/5B,KAAM,8BAA+BgC,MAAO,eAClE+3B,GAAS,GAAW,CAAE/5B,KAAM,+BAAgCgC,MAAO,gBACnE+3B,GAAS,GAAW,CAAE/5B,KAAM,sBAAuBgC,MAAO,aACvD,CAAEhC,KAAM,eAAgBgC,MAAO,iBAC1C,CAEA,YAAMhB,GAEFZ,KAAK0Q,aAAe,IAAIrJ,UAAS,CAC7BT,MAAO5G,KAAK4G,MACZ7G,UAAW,MACXwI,QAAS,EACThB,OAAQ,CACJ,CAAEqC,KAAM,KAAMjK,MAAO,YACrB,CAAEiK,KAAM,QAASjK,MAAO,SACxB,CAAEiK,KAAM,WAAYjK,MAAO,YAC3B,CAAEiK,KAAM,WAAYjK,MAAO,eAC3B,CAAEiK,KAAM,aAAcjK,MAAO,iBAC7B,CAAEiK,KAAM,WAAYjK,MAAO,oBAC3B,CAAEiK,KAAM,UAAWjK,MAAO,UAAW4I,QAAS,OAItD,MAAMkC,EAAO,CAAEkG,SAAY3Q,KAAK0Q,cAE1BonB,EAAW93B,KAAK4G,MAAMsB,IAAI,aAAe,CAAA,EAG3C4vB,EAASC,cACT/3B,KAAKg4B,eAAiB,IAAIhD,eAAe,CACrCC,WAAY6C,EAASC,cAEzBttB,EAAK,eAAiBzK,KAAKg4B,gBAI3B90B,OAAO+0B,KAAKH,GAAU3a,OAAS,IAC/Bnd,KAAK+W,aAAe,IAAI1X,OAAK,CACzBuH,MAAO5G,KAAK4G,MACZO,SAAU,0FAEdsD,EAAe,SAAIzK,KAAK+W,cAG5B/W,KAAK8G,QAAU,IAAI0D,UAAQ,CACvBrI,YAAa,aACbsI,OACAS,UAAW,aAEflL,KAAKoC,SAASpC,KAAK8G,SAGnB,MAAMiR,EAAY,CACd,CAAEpY,MAAO,gBAAiBE,OAAQ,gBAAiBD,KAAM,wBAAyBmF,UAAW/E,KAAK4G,MAAMsB,IAAI,aAC5G,CAAEvI,MAAO,qBAAsBE,OAAQ,aAAcD,KAAM,wBAAyBmF,UAAW/E,KAAK4G,MAAMsB,IAAI,aAC9G,CAAE2B,KAAM,WACR,CAAElK,MAAO,eAAgBE,OAAQ,eAAgBD,KAAM,WAAYiR,QAAQ,IAGzE+oB,EAAY,IAAIruB,cAAY,CAC9BpJ,YAAa,qBACbpC,UAAW,yCACXyL,QAASxL,KAAK4G,MACd6E,OAAQ,CACJ7L,KAAM,yBACN8L,MAAOqM,KAGf/X,KAAKoC,SAASw3B,EAClB,CAEA,0BAAMC,GACiC75B,KAAK4G,MAAMsB,IAAI,WACtD,CAEA,uBAAM4xB,GAC8B95B,KAAK4G,MAAMsB,IAAI,cAAelI,KAAK4G,MAAMsB,IAAI,WACjF,CAEA,yBAAM6xB,SACsBhuB,EAAAA,OAAOO,QAC3B,4EACA,mBACA,CAAEmN,aAAc,aAAcC,YAAa,mBAGxB1Z,KAAK4G,MAAMsK,WACrBnO,SACL/C,KAAKwF,KAAK,gBAAiB,CAAEoB,MAAO5G,KAAK4G,OAGrD,EAGJ4yB,EAAAA,cAAcnsB,WAAaksB,UCvI3B,MAAMS,uBAAuBzsB,EAAAA,UACzB,WAAAjO,CAAYC,EAAU,IAClBC,MAAM,IACCD,EACHqK,KAAM,eACN4D,SAAU,gBACVrH,OAAQ,eACRsH,WAAY7E,EAAAA,kBAEZwI,SAAU6oB,EAAAA,mBAAmBnuB,KAC7BuF,cAAekoB,UAEf5rB,kBAAmB,CACfvK,QAAQ,EACR+E,KAAM,MAGVyF,aAAc,CACVC,KAAM,MACNqsB,cAAe,SAInB3xB,QAAS,CACL,CAAEC,IAAK,UAAW7I,MAAO,YAAa+I,UAAU,EAAMD,UAAW,YACjE,CACID,IAAK,QAAS7I,MAAO,QACrB+I,UAAU,EAAMD,UAAW,QAC3BvD,OAAQ,CACJ2E,KAAM,SACNtK,QAAS,CACL,CAAE6K,MAAO,IAAKzK,MAAO,YACrB,CAAEyK,MAAO,IAAKzK,MAAO,WACrB,CAAEyK,MAAO,IAAKzK,MAAO,QACrB,CAAEyK,MAAO,IAAKzK,MAAO,SACrB,CAAEyK,MAAO,IAAKzK,MAAO,YAIjC,CACI6I,IAAK,QACL7I,MAAO,QACP+I,UAAU,EAAMD,UAAW,QAC3BvD,OAAQ,CACJ2E,KAAM,WACNtK,QAAS,CACL,CAAE6K,MAAO,UAAWzK,MAAO,WAC3B,CAAEyK,MAAO,WAAYzK,MAAO,YAC5B,CAAEyK,MAAO,QAASzK,MAAO,SACzB,CAAEyK,MAAO,UAAWzK,MAAO,gBAC3B,CAAEyK,MAAO,UAAWzK,MAAO,WAC3B,CAAEyK,MAAO,OAAQzK,MAAO,QACxB,CAAEyK,MAAO,MAAOzK,MAAO,UAKnC,CACI6I,IAAK,WACL7I,MAAO,WACP+I,UAAU,EAAMD,UAAW,QAC3BvD,OAAQ,CACJ2E,KAAM,WACNtK,QAAS,CACL,CAAE6K,MAAO,aAAczK,MAAO,cAC9B,CAAEyK,MAAO,YAAazK,MAAO,aAC7B,CAAEyK,MAAO,OAAQzK,MAAO,QACxB,CAAEyK,MAAO,WAAYzK,MAAO,eAIxC,CAAE6I,IAAK,QAAS7I,MAAO,QAAS+I,UAAU,EAAMD,UAAW,gBAC3D,CACID,IAAK,YAAa7I,MAAO,YAAa+I,UAAU,EAChDxD,OAAQ,CACJ2E,KAAM,SAGd,CACIrB,IAAK,kBAAmB7I,MAAO,SAC/B+I,UAAU,EACVxD,OAAQ,CACJ2E,KAAM,UAKlBmE,QAAS,CACL,CACIxF,IAAK,gBACL7I,MAAO,eACPuF,OAAQ,CAAC2E,KAAK,UAKtBqE,YAAY,EACZC,YAAY,EACZzF,UAAU,EACV0F,YAAY,EACZC,WAAW,EAGXC,aAAa,EACbC,SAAS,EACTC,YAAY,EAGZC,aAAc,mBAGdE,aAAc,CACVC,SAAS,EACTC,UAAU,EACVC,OAAO,EACPC,YAAY,IAGxB,EC7HJ,MAAMorB,kBACF,WAAA76B,CAAY86B,GACRp6B,KAAKo6B,SAAWA,EAChBp6B,KAAKqI,WAAa,IAAIgyB,EAAAA,eAAe,CAAEryB,OAAQ,CAAEmK,OAAQnS,KAAKo6B,SAAUvsB,KAAM,UAAW1F,KAAM,MACnG,CAEA,WAAMqL,GAEF,aADMxT,KAAKqI,WAAWmL,QACfxT,KAAKqI,WAAWouB,OAAO7b,OAAY5a,KAAK02B,UAAUQ,GAC7D,CAEA,SAAAR,CAAUQ,GACN,MAAO,CACHjrB,GAAIirB,EAAKhvB,IAAI,MACb2B,KAAM,eACN8sB,OAAQ,CACJ1qB,GAAIirB,EAAKhvB,IAAI,WACb0B,KAAMstB,EAAKhvB,IAAI,sBAAwB,SACvC0uB,UAAWM,EAAKhvB,IAAI,oBAExBxC,UAAWwxB,EAAKhvB,IAAI,WACpB2uB,QAASK,EAAKhvB,IAAI,QAClB4uB,YAAaI,EAAKhvB,IAAI,SAAW,CAACgvB,EAAKhvB,IAAI,UAAY,GAE/D,CAEA,aAAM6uB,CAAQ/zB,GACV,MAAMk0B,EAAO,IAAIoD,aACXlqB,QAAa8mB,EAAK3qB,KAAK,CACzB4F,OAAQnS,KAAKo6B,SACblD,KAAMl0B,EAAKiT,KACXskB,MAAOv3B,EAAKw3B,OAASx3B,EAAKw3B,MAAMrd,OAAS,EAAIna,EAAKw3B,MAAM,GAAGvuB,GAAK,OAKpE,OAHImE,EAAKrN,eACC/C,KAAKqI,WAAWmL,QAEnBpD,CACX,EC/BJ,MAAMqqB,mBAAmBp7B,EAAAA,KACrB,WAAAC,CAAYC,EAAU,IAClBC,MAAM,CACFO,UAAW,iBACRR,IAGPS,KAAK4G,MAAQrH,EAAQqH,OAAS,IAAI8zB,EAAAA,OAAOn7B,EAAQyD,MAAQ,IAEzDhD,KAAKmH,SAAW,22FAiDpB,CAEA,YAAMvG,GAEF,MAAM+2B,EAAU,IAAIwC,kBAAkBn6B,KAAK4G,MAAMsB,IAAI,OACrDlI,KAAK26B,SAAW,IAAI9C,WAAS,CACzB11B,YAAa,YACbw1B,UACAiD,MAAO,UACPC,cAAe76B,KAAK86B,mBACpBC,iBAAkB,gBAClBC,gBAAiB,aAErBh7B,KAAKoC,SAASpC,KAAK26B,UAGnB,MAAMM,EAAa,IAAI1vB,cAAY,CAC/BpJ,YAAa,sBACbpC,UAAW,yCACXyL,QAASxL,KAAK4G,MACd6E,OAAQ,CACJ7L,KAAM,yBACN8L,MAAO,CACH,CAAE/L,MAAO,cAAeE,OAAQ,cAAeD,KAAM,aACrD,CAAED,MAAO,gBAAiBE,OAAQ,gBAAiBD,KAAM,UACzD,CAAED,MAAO,eAAgBE,OAAQ,eAAgBD,KAAM,WACvD,CAAED,MAAO,cAAeE,OAAQ,cAAeD,KAAM,aACrD,CAAEiK,KAAM,WACR,CAAElK,MAAO,eAAgBE,OAAQ,eAAgBD,KAAM,mBAInEI,KAAKoC,SAAS64B,EAClB,CAMA,gBAAAH,GAEI,MAAMI,EAAchjB,OAAO/E,KAAKqkB,OAAOvvB,KACvC,OAAOizB,GAAajvB,IAAM,IAC9B,CAGA,wBAAMkvB,SACiBpvB,EAAAA,OAAOC,cAAc,CACpCvM,MAAO,gBAAgBO,KAAK4G,MAAMsB,IAAI,WAAWlI,KAAK4G,MAAMsB,IAAI,WAChEtB,MAAO5G,KAAK4G,MACZuB,KAAM,KACNZ,OAAQ6zB,EAAAA,YAAYtvB,KAAKvE,UAGzBvH,KAAKqD,QAEb,CAEA,0BAAMg4B,GACF,MACMC,EAAgBt7B,KAAK4G,MAAMsB,IAAI,UAE/BgxB,QAAentB,EAAAA,OAAOwD,SAAS,CACjC9P,MAAO,uBACP0I,KAAM,KACNZ,OAAQ,CACJ,CACIqC,KAAM,SACNjK,MAAO,aACPkK,KAAM,SACNtK,QAXK,CAAC,MAAO,OAAQ,cAAe,UAAW,WAAY,SAAU,WAWnDqb,IAAI6c,IAAA,CAAQrtB,MAAOqtB,EAAG93B,MAAO83B,EAAE8D,QAAQ,IAAK,KAAK1J,iBACnEznB,MAAOkxB,EACPxrB,UAAU,MAKtB,GAAIopB,EACA,UACUl5B,KAAK4G,MAAM2F,KAAK,CAAEtJ,OAAQi2B,EAAOj2B,SACvCjD,KAAKqD,QACT,OAASC,GACLyI,EAAAA,OAAOpG,MAAM,CACTkE,KAAM,QACNpK,MAAO,QACP2W,QAAS,mCAAqC9S,EAAM8S,SAE5D,CAER,CAEA,yBAAMolB,GACF,MACMC,EAAkBz7B,KAAK4G,MAAMsB,IAAI,YAEjCgxB,QAAentB,EAAAA,OAAOwD,SAAS,CACjC9P,MAAO,sBACP0I,KAAM,KACNZ,OAAQ,CACJ,CACIqC,KAAM,WACNjK,MAAO,iBACPkK,KAAM,SACNtK,QAXO,CAAC,MAAO,SAAU,OAAQ,UAWbqb,IAAI8gB,IAAA,CAAQtxB,MAAOsxB,EAAG/7B,MAAO+7B,EAAE7J,iBACnDznB,MAAOqxB,EACP3rB,UAAU,MAKtB,GAAIopB,EACA,UACUl5B,KAAK4G,MAAM2F,KAAK,CAAEovB,SAAUzC,EAAOyC,WACzC37B,KAAKqD,QACT,OAASC,GACLyI,EAAAA,OAAOpG,MAAM,CACTkE,KAAM,QACNpK,MAAO,QACP2W,QAAS,qCAAuC9S,EAAM8S,SAE9D,CAER,CAEA,wBAAMwlB,GAEF7vB,EAAAA,OAAOpG,MAAM,CACTlG,MAAO,cACP2W,QAAS,qDAEjB,CAEA,yBAAMylB,GAQF,SAPwB9vB,EAAAA,OAAOO,QAAQ,CACnC7M,MAAO,eACP2W,QAAS,0CAA0CpW,KAAK4G,MAAMsB,IAAI,SAClEwR,YAAa,eACbD,aAAc,gBAId,UACUzZ,KAAK4G,MAAM2F,KAAK,CAAEtJ,OAAQ,WAChCjD,KAAKqD,SACL0I,EAAAA,OAAOpG,MAAM,CACTkE,KAAM,UACNpK,MAAO,UACP2W,QAAS,wCAEjB,OAAS9S,GACLyI,EAAAA,OAAOpG,MAAM,CACTkE,KAAM,QACNpK,MAAO,QACP2W,QAAS,2BAA6B9S,EAAM8S,SAEpD,CAER,EAGJskB,EAAAA,OAAOrtB,WAAaotB,WCzNpB,MAAMqB,wBAAwBvuB,EAAAA,UAC1B,WAAAjO,CAAYC,EAAU,IAClBC,MAAM,CACFoK,KAAM,gBACN4D,SAAU,UACVrH,OAAQ,gBACRsH,WAAYgnB,EAAAA,WAEZtgB,WAAYinB,EAAAA,YAAYhuB,OACxBgE,SAAUgqB,EAAAA,YAAYtvB,KACtBuF,cAAeopB,WAEf9sB,kBAAmB,CACfvK,QAAQ,GAIZmF,QAAS,CACL,CAAEC,IAAK,KAAM7I,MAAO,KAAMoJ,MAAO,OAAQL,UAAU,EAAMoF,MAAO,cAChE,CAAEtF,IAAK,QAAS7I,MAAO,QAAS+I,UAAU,GAC1C,CACIF,IAAK,SAAU7I,MAAO,SAAU+I,UAAU,EAC1CqzB,UAAU,EACVC,gBAAiB,CACfnyB,KAAM,SACNtK,QAAS,CACL,MAAO,OAAQ,SAAU,WAAY,KAAM,YAGjD2F,OAAQ,CACN2E,KAAM,cACNoyB,YAAa,gBACb18B,QAAS,CACL,MAAO,OAAQ,SAAU,WAAY,KAAM,aAIrD,CAAEiJ,IAAK,WAAY7I,MAAO,WAAY+I,UAAU,GAChD,CACIF,IAAK,WAAY7I,MAAO,WAAY+I,UAAU,EAC9CqzB,UAAU,EACVC,gBAAiB,CACfnyB,KAAM,SACNtK,QAAS,IACD2D,OAAO+0B,KAAKiE,EAAAA,oBAGtBh3B,OAAQ,CACN2E,KAAM,cACNoyB,YAAa,kBACb18B,QAAS,IACD2D,OAAO+0B,KAAKiE,EAAAA,qBAI1B,CAAE1zB,IAAK,wBAAyB7I,MAAO,WAAY+I,UAAU,EAAMD,UAAW,yBAC9E,CAAED,IAAK,cAAe7I,MAAO,cAAe+I,UAAU,GACtD,CAAEF,IAAK,UAAW7I,MAAO,UAAW+I,UAAU,EAAMD,UAAW,aAInEyF,YAAY,EACZC,YAAY,EACZzF,UAAU,EACV0F,YAAY,EACZC,WAAW,EAGXC,aAAa,EACbC,SAAS,EACTC,YAAY,EAGZC,aAAc,oBAGdE,aAAc,CACVC,SAAS,EACTC,UAAU,EACVC,OAAO,EACPC,YAAY,MAEbxP,GAEX,ECrFJ,MAAM48B,oBAAoB98B,EAAAA,KACtB,WAAAC,CAAYC,EAAU,IAClBC,MAAM,CACFO,UAAW,kBACRR,IAGPS,KAAK4G,MAAQrH,EAAQqH,OAAS,IAAIw1B,EAAAA,QAAQ78B,EAAQyD,MAAQ,IAE1DhD,KAAKmH,SAAW,q1BAkBpB,CAEA,YAAMvG,GAEF,MAAMy7B,EAAer8B,KAAK4G,MAAMsB,IAAI,YAC9Bo0B,EAAgBC,EAAAA,eAAenD,KAAKoD,GAAOA,EAAIpyB,QAAUiyB,GACzDI,EAAeH,EAAgBA,EAAc38B,MAAQ+8B,OAAOL,GAE5DM,EAAgB38B,KAAK4G,MAAMsB,IAAI,aAC/B00B,EAAiBC,EAAAA,gBAAgBzD,KAAKoD,GAAOA,EAAIpyB,QAAUuyB,GAC3DG,EAAgBF,EAAiBA,EAAej9B,MAAQ+8B,OAAOC,GAGrE38B,KAAK+8B,WAAa,IAAI11B,UAAS,CAC3BT,MAAO5G,KAAK4G,MACZ7G,UAAW,MACXwI,QAAS,EACThB,OAAQ,CACJ,CAAEqC,KAAM,OAAQjK,MAAO,OAAQiX,KAAM,GACrC,CAAEhN,KAAM,WAAYjK,MAAO,WAAY8I,UAAW,QAASmO,KAAM,GACjE,CAAEhN,KAAM,YAAajK,MAAO,YAAa8I,UAAW,aAAcmO,KAAM,GACxE,CAAEhN,KAAM,WAAYjK,MAAO,WAAYiX,KAAM,GAC7C,CAAEhN,KAAM,KAAMjK,MAAO,aAAciX,KAAM,GAEzC,CACIhN,KAAM,WACNjK,MAAO,cACPwH,SAAUs1B,EACV7lB,KAAM,GAEV,CACIhN,KAAM,YACNjK,MAAO,YACPwH,SAAU21B,EACVlmB,KAAM,GAEV,CAAEhN,KAAM,iBAAkBjK,MAAO,iBAAkBiX,KAAM,GACzD,CAAEhN,KAAM,qBAAsBjK,MAAO,qBAAsB8I,UAAW,aAAcmO,KAAM,GAC1F,CAAEhN,KAAM,UAAWjK,MAAO,UAAWiX,KAAM,OAKnD,MAAMomB,EAAkB,IAAIC,WAAS,CACjCj1B,OAAQ,CAAEmK,OAAQnS,KAAK4G,MAAMsB,IAAI,SAErClI,KAAKk9B,UAAY,IAAI90B,YAAU,CAC3BC,WAAY20B,EACZ10B,oBAAqB,CAAC,UACtBC,QAAS,CACL,CAAEC,IAAK,KAAM7I,MAAO,KAAMoJ,MAAO,QACjC,CAAEP,IAAK,OAAQ7I,MAAO,QACtB,CAAE6I,IAAK,aAAc7I,MAAO,SAC5B,CAAE6I,IAAK,aAAc7I,MAAO,aAAcoJ,MAAO,SACjD,CAAEP,IAAK,QAAS7I,MAAO,SACvB,CAAE6I,IAAK,aAAc7I,MAAO,OAAQoJ,MAAO,UAE/CwF,SAAS,EACTwL,YAAa,OACb2d,QAAS,CAAC,OAAQ,UAClBhpB,YAAa,CACT,CAAE/O,MAAO,YAAaE,OAAQ,OAAQD,KAAM,aAC5C,CAAED,MAAO,iBAAkBE,OAAQ,YAAaD,KAAM,YACtD,CAAEu9B,SAAS,GACX,CAAEx9B,MAAO,cAAeE,OAAQ,SAAUD,KAAM,WAAYiR,QAAQ,IAGxEusB,gBAAiB,CACbjrB,OAAQnS,KAAK4G,MAAMsB,IAAI,SAI/BlI,KAAK8G,QAAU,IAAI0D,UAAQ,CACvBrI,YAAa,eACbsI,KAAM,CACF4yB,cAAiBr9B,KAAK+8B,WACtBO,MAASt9B,KAAKk9B,WAElBhyB,UAAW,kBAEflL,KAAKoC,SAASpC,KAAK8G,SAEnB,MAAM4H,EAAc,IAAInD,cAAY,CAChCpJ,YAAa,uBACbqJ,QAASxL,KAAK4G,MACd6E,OAAQ,CACJ7L,KAAM,yBACN8L,MAAO,CACH,CAAE/L,MAAO,eAAgBE,OAAQ,eAAgBD,KAAM,aACvD,CAAED,MAAO,UAAWE,OAAQ,kBAAmBD,KAAM,iBACrD,CAAEiK,KAAM,WACR,CAAElK,MAAO,iBAAkBE,OAAQ,iBAAkBD,KAAM,WAAYiR,QAAQ,OAI3F7Q,KAAKoC,SAASsM,EAClB,CAKA,yBAAM6uB,SACiBxxB,EAAAA,OAAOC,cAAc,CACpCvM,MAAO,kBAAkBO,KAAK4G,MAAMsB,IAAI,UACxCtB,MAAO5G,KAAK4G,MACZuF,WAAYiwB,EAAAA,QAAQoB,mBAGdx9B,KAAKqD,QAEnB,CAKA,4BAAMo6B,GACF,MACMC,GADW19B,KAAK4G,MAAMsB,IAAI,aAGhC,IACIlI,KAAK4G,MAAMyZ,IAAI,YAAaqd,SACtB19B,KAAK4G,MAAM2F,aACXvM,KAAKqD,SAEX0I,EAAAA,OAAO4xB,UAAU,CACbvnB,QAAS,WAAWsnB,EAAY,UAAY,0BAC5C7zB,KAAM,WAEd,OAASvG,GACLyI,EAAAA,OAAO4xB,UAAU,CACbvnB,QAAS,6BAA6B9S,EAAM8S,UAC5CvM,KAAM,SAEd,CACJ,CAKA,2BAAM+zB,GAQF,SAPwB7xB,EAAAA,OAAOO,QAAQ,CACnC7M,MAAO,iBACP2W,QAAS,gDAAgDpW,KAAK4G,MAAMsB,IAAI,0CACxEwR,YAAa,SACbD,aAAc,eAId,UACUzZ,KAAK4G,MAAMsK,UAEjBnF,EAAAA,OAAO4xB,UAAU,CACbvnB,QAAS,+BACTvM,KAAM,YAIV,MAAMiM,EAAS9V,KAAKyE,SAASo5B,QAAQ,UACrC,GAAI/nB,EAAQ,CACR,MAAMgoB,EAAU3lB,UAAU4lB,MAAMtlB,YAAY3C,GACxCgoB,GACAA,EAAQE,MAEhB,CAGAh+B,KAAKwF,KAAK,kBAAmB,CAAEoB,MAAO5G,KAAK4G,OAC/C,OAAStD,GACLyI,EAAAA,OAAO4xB,UAAU,CACbvnB,QAAS,6BAA6B9S,EAAM8S,UAC5CvM,KAAM,SAEd,CAER,EAGJsyB,YAAY9uB,WAAa8uB,YChNzB,MAAM8B,yBAAyB1wB,EAAAA,UAC3B,WAAAjO,CAAYC,EAAU,IAClBC,MAAM,IACCD,EACHqK,KAAM,iBACN4D,SAAU,cACVrH,OAAQ,iBACRsH,WAAYywB,EAAAA,YACZpkB,SAAUqiB,YACVxuB,kBAAmB,CACfvK,QAAQ,EACR+E,KAAM,MAGVI,QAAS,CACL,CAAEC,IAAK,KAAM7I,MAAO,KAAMoJ,MAAO,OAAQL,UAAU,EAAMoF,MAAO,cAChE,CAAEtF,IAAK,OAAQ7I,MAAO,OAAQ+I,UAAU,GACxC,CAAEF,IAAK,WAAY7I,MAAO,WAAY+I,UAAU,EAAMD,UAAW,SACjE,CAAED,IAAK,WAAY7I,MAAO,WAAY+I,UAAU,GAChD,CAAEF,IAAK,WAAY7I,MAAO,cAAe8I,UAAY01B,GAAY,IAANA,EAAU,MAAQ,QAGjFjwB,YAAY,EACZC,YAAY,EACZzF,UAAU,EACV2F,WAAW,EACXC,aAAa,EACbC,SAAS,EACTC,YAAY,EAEZG,aAAc,CACVyvB,UAAW,CAAC,GAAI,GAAI,IACpBC,gBAAiB,GACjB5vB,aAAc,sBACd6vB,UAAW,UACX5G,QAAS,CAAC,OAAQ,OAAQ,YAGtC,EChCJ,MAAM6G,6BAA6BhxB,EAAAA,UACjC,WAAAjO,CAAYC,EAAU,IACpBC,MAAM,IACDD,EACHqK,KAAM,sBACN4D,SAAU,gBACVrH,OAAQ,sBACRsH,WAAY+wB,EAAAA,gBACZrqB,WAAYsqB,EAAAA,iBAAiBrxB,OAC7BgE,SAAUqtB,EAAAA,iBAAiB3yB,KAG3BvD,QAAS,CACP,CACEC,IAAK,KACL7I,MAAO,KACPoJ,MAAO,OACPL,UAAU,EACVoF,MAAO,cAET,CACEtF,IAAK,OACL7I,MAAO,SACP+I,UAAU,GAEZ,CACEF,IAAK,SACL7I,MAAO,SACP+I,UAAU,EACVD,UAAW,gBAEb,CACED,IAAK,oBACL7I,MAAO,YACP8I,UAAW,iBAEb,CACED,IAAK,WACL7I,MAAO,gBACP8I,UAAW,iBAEb,CACED,IAAK,WACL7I,MAAO,gBACP8I,UAAW,iBAEb,CACED,IAAK,UACL7I,MAAO,UACP8I,UAAW,mBAKfyF,YAAY,EACZC,YAAY,EACZzF,UAAU,EACV0F,YAAY,EACZC,WAAW,EAGXC,aAAa,EACbC,SAAS,EACTC,YAAY,EAGZC,aAAc,uDAGdC,YAAa,CACT,CACE9O,KAAM,kBACNC,OAAQ,iBACRF,MAAO,wBAEX,CACEC,KAAM,oBACNC,OAAQ,UACRF,MAAO,WAET,CACEC,KAAM,kBACNC,OAAQ,QACRF,MAAO,SAET,CACEC,KAAM,kBACNC,OAAQ,YACRF,MAAO,cAKXgP,aAAc,CACZC,SAAS,EACTC,UAAU,EACVC,OAAO,EACPC,YAAY,IAGlB,CAEE,0BAAM2vB,CAAqBl6B,EAAOC,GAC9B,MAAMyK,EAAOlP,KAAKqI,WAAWH,IAAIzD,EAAQ0K,QAAQlD,IAMjD,aALqBF,EAAAA,OAAOC,cAAc,CAClCpF,MAAOsI,EACP/C,WAAYsyB,EAAAA,iBAAiBE,eAG9B,CACX,CAEF,qBAAMC,CAAgBp6B,EAAOC,GAC3B,MAAMyK,EAAOlP,KAAKqI,WAAWH,IAAIzD,EAAQ0K,QAAQlD,IAC3CrF,EAAQ,IAAIi4B,EAAAA,YAAY,CAAE5yB,GAAIiD,EAAKjD,KAEnC6yB,QAAiB/yB,EAAAA,OAAOwD,SAASkvB,EAAAA,iBAAiBM,SACxD,GAAKD,EAEL,IACE,MAAM1uB,QAAaxJ,EAAMm4B,QAAQD,GACjC,IAAK1uB,EAAKrN,QACR,MAAM,IAAIouB,MAAM/gB,EAAKgG,SAAW,mCAElC,IAAKhG,EAAKpN,MAAMC,OACd,MAAM,IAAIkuB,MAAM/gB,EAAKpN,MAAMM,OAAS,qBAGtCtD,KAAK4C,UAAU6J,OAAO1J,QAAQ,kDACxB/C,KAAKiF,SACb,OAAS+5B,GACPz7B,QAAQD,MAAM,iBAAkB07B,GAChCh/B,KAAKi/B,UAAUD,EAAI5oB,SAAW,2BAChC,CACF,CAEA,mBAAM8oB,CAAc16B,EAAOC,GACzB,MAAMyK,EAAOlP,KAAKqI,WAAWH,IAAIzD,EAAQ0K,QAAQlD,IAC3CrF,EAAQ,IAAIi4B,EAAAA,YAAY,CAAE5yB,GAAIiD,EAAKjD,KAEzC,IACE,MAAMmE,QAAaxJ,EAAMu4B,QACzB,IAAK/uB,EAAKrN,QACR,MAAM,IAAIouB,MAAM/gB,EAAKgG,SAAW,8BAElC,IAAKhG,EAAKpN,MAAMC,OACd,MAAM,IAAIkuB,MAAM/gB,EAAKpN,MAAMM,OAAS,gBAGtC,MAAM41B,EAAS9oB,EAAKpN,MAAMA,MAAQ,CAAA,QAC5B+I,EAAAA,OAAOqzB,WAAW,CACtB3/B,MAAO,kBAAkByP,EAAKtF,OAC9BmM,KAAM,IAAI1W,EAAAA,KAAK,CACb8H,SAAU,yNAMVnE,KAAM,CAAEk2B,YAEV/wB,KAAM,MAEV,OAAS62B,GACPz7B,QAAQD,MAAM,eAAgB07B,GAC9Bh/B,KAAKi/B,UAAUD,EAAI5oB,SAAW,yBAChC,CACF,CAEA,uBAAMipB,CAAkB76B,EAAOC,GAC7B,MAAMyK,EAAOlP,KAAKqI,WAAWH,IAAIzD,EAAQ0K,QAAQlD,IAC3CrF,EAAQ,IAAIi4B,EAAAA,YAAY,CAAE5yB,GAAIiD,EAAKjD,KAEzC,IACE,MAAMmE,QAAaxJ,EAAM04B,YACzB,IAAKlvB,EAAKrN,QACR,MAAM,IAAIouB,MAAM/gB,EAAKgG,SAAW,kCAElC,IAAKhG,EAAKpN,MAAMC,OACd,MAAM,IAAIkuB,MAAM/gB,EAAKpN,MAAMM,OAAS,oBAGtCtD,KAAK4C,UAAU6J,OAAO1J,QAAQ,0CACxB/C,KAAKiF,SACb,OAAS+5B,GACPz7B,QAAQD,MAAM,mBAAoB07B,GAClCh/B,KAAKi/B,UAAUD,EAAI5oB,SAAW,6BAChC,CACF,EC7LF,MAAMmpB,8BAA8BhyB,EAAAA,UAChC,WAAAjO,CAAYC,EAAU,IAClBC,MAAM,IACCD,EACHqK,KAAM,wBACN4D,SAAU,YACVrH,OAAQ,wBACRsH,WAAY+xB,EAAAA,YAEZrrB,WAAYsrB,EAAAA,aAAaryB,OACzBgE,SAAUquB,EAAAA,aAAa3zB,KACvBiO,YAAa,OAGbxR,QAAS,CACL,CAAEC,IAAK,KAAM7I,MAAO,KAAMoJ,MAAO,OAAQL,UAAU,EAAMoF,MAAO,cAChE,CAAEtF,IAAK,QAAS7I,MAAO,QAAS+I,UAAU,GAC1C,CAAEF,IAAK,cAAe7I,MAAO,SAAU+I,UAAU,EAAMD,UAAW,gBAClE,CAAED,IAAK,gBAAiB7I,MAAO,UAAW8I,UAAW,iBACrD,CAAED,IAAK,iBAAkB7I,MAAO,WAAY8I,UAAW,iBACvD,CAAED,IAAK,oBAAqB7I,MAAO,iBAAkB8I,UAAW,iBAChE,CAAED,IAAK,oBAAqB7I,MAAO,iBAAkB8I,UAAW,kBAIpEyF,YAAY,EACZC,YAAY,EACZzF,UAAU,EACV0F,YAAY,EACZC,WAAW,EAGXC,aAAa,EACbC,SAAS,EACTC,YAAY,EAGZC,aAAc,yDAGdC,YAAa,CACT,CAAE9O,KAAM,cAAeC,OAAQ,aAAcF,MAAO,cACpD,CAAEC,KAAM,cAAeC,OAAQ,sBAAuBF,MAAO,wBAIjEgP,aAAc,CACVC,SAAS,EACTC,UAAU,EACVC,OAAO,EACPC,YAAY,IAGxB,CAEA,uBAAM2wB,CAAkBl7B,EAAOC,GAC3B,MAAMyK,EAAOlP,KAAKqI,WAAWH,IAAIzD,EAAQ0K,QAAQlD,IAE3CjJ,QAAa+I,EAAAA,OAAOwD,SAAS,CAC/B9P,MAAO,aACP8H,OAAQ,CACJ,CAAEqC,KAAM,KAAMjK,MAAO,KAAMkK,KAAM,QAASiG,UAAU,GACpD,CAAElG,KAAM,UAAWjK,MAAO,UAAWkK,KAAM,OAAQiG,UAAU,GAC7D,CAAElG,KAAM,YAAajK,MAAO,OAAQkK,KAAM,WAAYiG,UAAU,MAGxE9M,EAAK28B,WAAazwB,EAAKhH,IAAI,SAC3B,MAAMgxB,QAAe0G,UAAQC,UAAU78B,GACvC,GAAIk2B,EAAOn2B,QACP/C,KAAK4C,SAAS6J,MAAM1J,QAAQ,+BACzB,CACH,IAAI+8B,EAAM,uBACN5G,EAAOl2B,KAAK+8B,QACZD,EAAM5G,EAAOl2B,KAAK+8B,QACX7G,EAAOl2B,KAAKM,QACnBw8B,EAAM5G,EAAOl2B,KAAKM,OAGtBtD,KAAK4C,SAAS6J,MAAMnJ,MAAMw8B,EAC9B,CACJ,CAEA,+BAAME,CAA0Bx7B,EAAOC,GACnC,MAAMyK,EAAOlP,KAAKqI,WAAWH,IAAIzD,EAAQ0K,QAAQlD,IAE3CjJ,QAAa+I,EAAAA,OAAOwD,SAAS,CAC/B9P,MAAO,aACP8H,OAAQ,CACJ,CAAEqC,KAAM,KAAMjK,MAAO,KAAMkK,KAAM,QAASiG,UAAU,GACpD,CAAElG,KAAM,UAAWjK,MAAO,UAAWkK,KAAM,OAAQiG,UAAU,GAC7D,CAAElG,KAAM,gBAAiBjK,MAAO,WAAYkK,KAAM,OAAQiG,UAAU,GACpE,CAAElG,KAAM,mBAAoBjK,MAAO,UAAWkK,KAAM,WAAYiG,UAAU,MAGlF9M,EAAK28B,WAAazwB,EAAKhH,IAAI,SAC3B,MAAMgxB,QAAe0G,UAAQC,UAAU78B,GACvC,GAAIk2B,EAAOn2B,QACP/C,KAAK4C,SAAS6J,MAAM1J,QAAQ,+BACzB,CACH,IAAI+8B,EAAM,uBACN5G,EAAOl2B,KAAK+8B,QACZD,EAAM5G,EAAOl2B,KAAK+8B,QACX7G,EAAOl2B,KAAKM,QACnBw8B,EAAM5G,EAAOl2B,KAAKM,OAGtBtD,KAAK4C,SAAS6J,MAAMnJ,MAAMw8B,EAC9B,CACJ,EC7GJ,MAAMG,0BAA0B5gC,EAAAA,KAC5B,WAAAC,CAAYC,EAAU,IAClBC,MAAM,CACFO,UAAW,yBACRR,IAGPS,KAAK4G,MAAQrH,EAAQqH,OAAS,IAAIs5B,EAAAA,cAAc3gC,EAAQyD,MAAQ,IAChEhD,KAAKmgC,UAAYngC,KAAK4G,MAAMsB,IAAI,iBAChClI,KAAKogC,UAAYpgC,KAAK4G,MAAMsB,IAAI,iBAChClI,KAAKqgC,YAAcrgC,KAAK4G,MAAMsB,IAAI,aAAehF,OAAO+0B,KAAKj4B,KAAK4G,MAAMsB,IAAI,aAAaiV,OAAS,EAElGnd,KAAKmH,SAAW,6iBAcpB,CAEA,YAAMvG,GACF,MAAM6J,EAAO,CAAA,EAETzK,KAAKmgC,UACL11B,EAAK,gBAAkB,IAAIpL,OAAK,CAC5BuH,MAAO5G,KAAK4G,MACZO,SAAU,+HAIdnH,KAAKogC,UACL31B,EAAK,gBAAkB,IAAIpL,OAAK,CAC5BuH,MAAO5G,KAAK4G,MACZO,SAAU,qJAIdnH,KAAKqgC,cACL51B,EAAe,SAAI,IAAIpL,OAAK,CACxBuH,MAAO5G,KAAK4G,MACZO,SAAU,gHAIlBnH,KAAK8G,QAAU,IAAI0D,UAAQ,CACvBrI,YAAa,gBACbsI,OACAS,UAAWhI,OAAO+0B,KAAKxtB,GAAM,IAAM,KAEvCzK,KAAKoC,SAASpC,KAAK8G,QACvB,ECzDJ,MAAMw5B,+BAA+B/yB,EAAAA,UACjC,WAAAjO,CAAYC,EAAU,IAClBC,MAAM,IACCD,EACHqK,KAAM,wBACN4D,SAAU,kBACVrH,OAAQ,wBACRsH,WAAY8yB,EAAAA,kBAEZpsB,WAAYqsB,EAAAA,mBAAmBpzB,OAC/BgE,SAAUovB,EAAAA,mBAAmB10B,KAC7BuF,cAAe4uB,kBACflmB,YAAa,OAEbpM,kBAAmB,CACfvK,QAAQ,EACR+E,KAAM,KACNs4B,YAAY,GAGhBC,iBAAkB,CACdv4B,KAAM,cAIVI,QAAS,CACL,CAAEC,IAAK,KAAM7I,MAAO,KAAMoJ,MAAO,OAAQL,UAAU,EAAMoF,MAAO,cAChE,CAAEtF,IAAK,OAAQ7I,MAAO,OAAQ+I,UAAU,GACxC,CAAEF,IAAK,UAAW7I,MAAO,UAAW8I,UAAW,YAC/C,CAAED,IAAK,WAAY7I,MAAO,WAAY8I,UAAW,aAIrDyF,YAAY,EACZC,YAAY,EACZzF,UAAU,EACV0F,YAAY,EACZC,WAAW,EAGXC,aAAa,EACbC,SAAS,EACTC,YAAY,EAGZC,aAAc,2EAGdE,aAAc,CACVC,SAAS,EACTC,UAAU,EACVC,OAAO,EACPC,YAAY,IAGxB,ECxDJ,MAAM4xB,kBAAkBthC,EAAAA,KACpB,WAAAC,CAAYC,EAAU,IAClBC,MAAM,CACFO,UAAW,gBACRR,IAGPS,KAAK4G,MAAQrH,EAAQqH,OAAS,IAAIg6B,EAAAA,YAAYrhC,EAAQyD,MAAQ,IAC9DhD,KAAKmgC,UAAYngC,KAAK4G,MAAMsB,IAAI,aAChClI,KAAKogC,UAAYpgC,KAAK4G,MAAMsB,IAAI,aAChClI,KAAK6gC,WAAa7gC,KAAK4G,MAAMsB,IAAI,qBAAuBhF,OAAO+0B,KAAKj4B,KAAK4G,MAAMsB,IAAI,qBAAqBiV,OAAS,EAEjHnd,KAAKmH,SAAW,szCA0BpB,CAEA,YAAMvG,GACF,MAAM6J,EAAO,CAAA,EAETzK,KAAKmgC,UACL11B,EAAW,KAAI,IAAIpL,OAAK,CACpBuH,MAAO5G,KAAK4G,MACZO,SAAU,6HAIdnH,KAAKogC,UACL31B,EAAW,KAAI,IAAIpL,OAAK,CACpBuH,MAAO5G,KAAK4G,MACZO,SAAU,iJAIdnH,KAAK6gC,aACLp2B,EAAc,QAAI,IAAIpL,OAAK,CACvBuH,MAAO5G,KAAK4G,MACZO,SAAU,uHAIlBnH,KAAK8G,QAAU,IAAI0D,UAAQ,CACvBrI,YAAa,aACbsI,OACAS,UAAWlL,KAAKmgC,QAAU,OAAUngC,KAAKogC,QAAU,OAAS,YAEhEpgC,KAAKoC,SAASpC,KAAK8G,QACvB,EAGJ85B,EAAAA,YAAYvzB,WAAaszB,UCxEzB,MAAMG,6BAA6BvzB,EAAAA,UAC/B,WAAAjO,CAAYC,EAAU,IAClBC,MAAM,IACCD,EACHqK,KAAM,mBACN4D,SAAU,gBACVrH,OAAQ,mBACRsH,WAAYszB,EAAAA,gBAEZ1vB,cAAesvB,UACfhzB,kBAAmB,CACfvK,QAAQ,EACR+E,KAAM,KACNs4B,YAAY,GAIhBl4B,QAAS,CACL,CAAEC,IAAK,KAAM7I,MAAO,KAAMoJ,MAAO,OAAQL,UAAU,EAAMoF,MAAO,cAChE,CAAEtF,IAAK,gBAAiB7I,MAAO,OAAQ+I,UAAU,GACjD,CAAEF,IAAK,eAAgB7I,MAAO,KAAM+I,UAAU,EAAOD,UAAW,QAChE,CAAED,IAAK,UAAW7I,MAAO,UAAW+I,UAAU,GAC9C,CAAEF,IAAK,SAAU7I,MAAO,SAAU8I,UAAW,SAC7C,CAAED,IAAK,gBAAiB7I,MAAO,SAAU8I,UAAW,6BACpD,CAAED,IAAK,UAAW7I,MAAO,UAAW8I,UAAW,aAInDyF,YAAY,EACZC,YAAY,EACZzF,UAAU,EACV0F,YAAY,EACZC,WAAW,EAGXC,aAAa,EACbC,SAAS,EACTC,YAAY,EAGZC,aAAc,0BAGdE,aAAc,CACVC,SAAS,EACTC,UAAU,EACVC,OAAO,EACPC,YAAY,IAGxB,ECxBJ,MAAMiyB,oBAAoBC,EAAAA,MACxB,WAAA3hC,CAAY0D,EAAO,GAAIzD,EAAU,CAAA,GAC/BC,MAAMwD,EAAM,CACVT,SAAU,0BACPhD,GAEP,CAQA,sBAAa2hC,CAAUC,EAAaC,EAAc,MAChD,MACMC,EAAU,CACdC,aAAcH,GAEZC,MAAqBG,aAAeH,GAExC,MAAMhxB,QAAavN,EAAAA,KAAKyQ,KANZ,iCAMsB+tB,GAC5BtrB,EAAO3F,GAAMpN,MAAQoN,EAG3B,OAF4B,IAAjB2F,GAAM9S,SAAqC,IAAlB8S,GAAMhT,QAIjC,CAAEA,SAAS,EAAMu+B,aADLvrB,GAAM/S,MAAMs+B,cAAgBvrB,GAAMurB,aACHt+B,KAAM+S,GAAM/S,MAAQ+S,EAAMpT,SAAUyN,GAEjF,CAAErN,SAAS,EAAOO,MAAOyS,GAAMzS,OAAS,uBAAwBX,SAAUyN,EACnF,CAQA,mBAAauJ,CAAOwnB,EAAa5hC,EAAU,IACzC,MACM6Q,QAAavN,OAAKyQ,KADZ,8BACsB,CAChCguB,aAAcH,KACX5hC,IAECwW,EAAO3F,GAAMpN,MAAQoN,EAG3B,IAF4B,IAAjB2F,GAAM9S,SAAqC,IAAlB8S,GAAMhT,QAElC,CACN,MAAMC,EAAO+S,GAAM/S,MAAQ,CAAA,EAE3B,MAAO,CAAED,SAAS,EAAM6D,MADV,IAAIo6B,YAAYh+B,EAAM,CAAET,SAAU,yBACjBS,OAAML,SAAUyN,EACjD,CACA,MAAO,CAAErN,SAAS,EAAOO,MAAOyS,GAAMzS,OAAS,sBAAuBX,SAAUyN,EAClF,EAQF,MAAMoxB,wBAAwB/zB,EAAAA,WAC5B,WAAAnO,CAAYC,EAAU,IACpBC,MAAM,CACJiiC,WAAYT,YACZz+B,SAAU,uBACV4F,KAAM,MACH5I,GAEP,EAYF,MAAMmiC,YAAYT,EAAAA,MAChB,WAAA3hC,CAAY0D,EAAO,GAAIzD,EAAU,CAAA,GAC/BC,MAAMwD,EAAM,CACVT,SAAU,uBACPhD,GAEP,CAOA,iBAAaoiC,CAAK35B,EAAS,IACzB,MACMoI,QAAavN,EAAAA,KAAKyQ,KADZ,yBACsBtL,GAC5B+N,EAAO3F,GAAMpN,MAAQoN,EAG3B,IAF4B,IAAjB2F,GAAM9S,SAAqC,IAAlB8S,GAAMhT,QAElC,CACN,MAAMC,EAAO+S,GAAM/S,MAAQ,CAAA,EAE3B,MAAO,CAAED,SAAS,EAAM6D,MADV,IAAI86B,IAAI1+B,EAAM,CAAET,SAAU,sBACTS,OAAML,SAAUyN,EACjD,CACA,MAAO,CAAErN,SAAS,EAAOO,MAAOyS,GAAMzS,OAAS,qBAAsBX,SAAUyN,EACjF,EAUF,MAAMwxB,gBAAgBn0B,EAAAA,WACpB,WAAAnO,CAAYC,EAAU,IACpBC,MAAM,CACJiiC,WAAYC,IACZn/B,SAAU,oBACV4F,KAAM,MACH5I,GAEP,EC/IF,MAAMsiC,wBAAwBxiC,EAAAA,KAC5B,WAAAC,CAAYC,EAAU,IACpBC,MAAM,CACJO,UAAW,uBACRR,IAGLS,KAAK4G,MAAQrH,EAAQqH,OAAS,IAAIo6B,YAAYzhC,EAAQyD,MAAQ,IAE9DhD,KAAKmH,SAAW,wzCAgClB,CAEA,YAAMvG,GAEJZ,KAAK0Q,aAAe,IAAIrJ,UAAS,CAC/BT,MAAO5G,KAAK4G,MACZ7G,UAAW,MACXuH,iBAAiB,EACjBqP,eAAgB,IAChBpO,QAAS,EACThB,OAAQ,CACN,CAAEqC,KAAM,eAAgBjK,MAAO,eAAgBiX,KAAM,GACrD,CAAEhN,KAAM,eAAgBjK,MAAO,eAAgBiX,KAAM,GACrD,CAAEhN,KAAM,SAAUjK,MAAO,SAAUiX,KAAM,GACzC,CAAEhN,KAAM,QAASjK,MAAO,QAASiX,KAAM,GACvC,CAAEhN,KAAM,mBAAoBjK,MAAO,mBAAoBiX,KAAM,GAC7D,CAAEhN,KAAM,aAAcjK,MAAO,aAAc8I,UAAW,aAAcmO,KAAM,GAC1E,CAAEhN,KAAM,WAAYjK,MAAO,QAAS8I,UAAW,YAAamO,KAAM,GAClE,CAAEhN,KAAM,YAAajK,MAAO,SAAU8I,UAAW,YAAamO,KAAM,GACpE,CAAEhN,KAAM,UAAWjK,MAAO,OAAQ8I,UAAW,YAAamO,KAAM,MAKpE5W,KAAK8hC,YAAc,IAAIz6B,UAAS,CAC9BT,MAAO5G,KAAK4G,MACZ7G,UAAW,MACXuH,iBAAiB,EACjBqP,eAAgB,IAChBpO,QAAS,EACThB,OAAQ,CACN,CAAEqC,KAAM,UAAWjK,MAAO,UAAWiX,KAAM,GAC3C,CAAEhN,KAAM,YAAajK,MAAO,YAAa8I,UAAW,aAAcmO,KAAM,GACxE,CAAEhN,KAAM,kBAAmBjK,MAAO,kBAAmB8I,UAAW,aAAcmO,KAAM,GACpF,CAAEhN,KAAM,eAAgBjK,MAAO,eAAgBiX,KAAM,GACrD,CAAEhN,KAAM,iBAAkBjK,MAAO,cAAe8I,UAAW,WAAYmO,KAAM,GAC7E,CAAEhN,KAAM,oBAAqBjK,MAAO,gBAAiB8I,UAAW,WAAYmO,KAAM,MAKtF5W,KAAK+hC,YAAc,IAAI16B,UAAS,CAC9BT,MAAO5G,KAAK4G,MACZ7G,UAAW,MACXuH,iBAAiB,EACjBqP,eAAgB,IAChBpO,QAAS,EACThB,OAAQ,CACN,CAAEqC,KAAM,gBAAiBjK,MAAO,iBAAkBiX,KAAM,IACxD,CAAEhN,KAAM,eAAgBjK,MAAO,OAAQiX,KAAM,GAC7C,CAAEhN,KAAM,gBAAiBjK,MAAO,QAASiX,KAAM,GAC/C,CAAEhN,KAAM,cAAejK,MAAO,MAAOiX,KAAM,GAC3C,CAAEhN,KAAM,kBAAmBjK,MAAO,UAAWiX,KAAM,MAKvD5W,KAAK+W,aAAe,IAAI1P,UAAS,CAC/BT,MAAO5G,KAAK4G,MACZ7G,UAAW,MACXuH,iBAAiB,EACjBqP,eAAgB,IAChBpO,QAAS,EACThB,OAAQ,CACN,CAAEqC,KAAM,KAAMjK,MAAO,YAAaiX,KAAM,GACxC,CAAEhN,KAAM,UAAWjK,MAAO,UAAW8I,UAAW,WAAYmO,KAAM,GAClE,CAAEhN,KAAM,WAAYjK,MAAO,gBAAiB8I,UAAW,WAAYmO,KAAM,MAI7E,MAAMnM,EAAO,CACXkG,SAAY3Q,KAAK0Q,aACjBsxB,QAAWhiC,KAAK8hC,YAChBG,QAAWjiC,KAAK+hC,YAChB3qB,SAAYpX,KAAK+W,cAGnB/W,KAAK8G,QAAU,IAAI0D,UAAQ,CACzBrI,YAAa,aACbsI,OACAS,UAAW,aAEblL,KAAKoC,SAASpC,KAAK8G,SAGnB,MAMMo7B,EAAU,IAAI32B,cAAY,CAC9BpJ,YAAa,qBACbpC,UAAW,yCACXyL,QAASxL,KAAK4G,MACd6E,OAAQ,CACN7L,KAAM,yBACN8L,MAZc,CAChB,CAAE/L,MAAO,iBAAkBE,OAAQ,iBAAkBD,KAAM,mBAC3D,CAAEiK,KAAM,WACR,CAAElK,MAAO,gBAAiBE,OAAQ,eAAgBD,KAAM,WAAYiR,QAAQ,OAY9E7Q,KAAKoC,SAAS8/B,EAChB,CAIA,2BAAMC,GACJ,MAAMC,EAASpiC,KAAK4G,MAAMsB,IAAI,gBAC9B,GAAKk6B,EAKL,IACEpiC,KAAK4C,UAAU6J,OAAOyM,OAAO,wBAE7B,MAAM9I,QAAa4wB,YAAYrnB,OAAOyoB,EAAQ,CAAEC,eAAe,IAC/D,GAAIjyB,EAAKrN,SAAWqN,EAAKpN,KACvBhD,KAAK4G,MAAMyZ,IAAIjQ,EAAKpN,YACdhD,KAAKqD,SACXrD,KAAK4C,UAAU6J,OAAO1J,UAAU,wBAC3B,CACL,MAAM+8B,EAAM1vB,EAAK9M,OAAS,gBAC1BtD,KAAK4C,UAAU6J,OAAOnJ,QAAQw8B,EAChC,CACF,OAASwC,GACPtiC,KAAK4C,UAAU6J,OAAOnJ,QAAQg/B,EAAElsB,SAAW,gBAC7C,MAlBEpW,KAAK4C,UAAU6J,OAAOwH,UAAU,4BAmBpC,CAEA,yBAAMsuB,GAOJ,SANwBx2B,EAAAA,OAAOO,QAC7B,mDAAmDtM,KAAK4G,MAAMsB,IAAI,iBAAmB,kBACrF,mBACA,CAAEuR,aAAc,aAAcC,YAAa,WAK7C,IACE,MAAMtJ,QAAapQ,KAAK4G,MAAMsK,UAC1Bd,GAAMrN,QACR/C,KAAKwF,KAAK,gBAAiB,CAAEoB,MAAO5G,KAAK4G,QAEzC5G,KAAK4C,UAAU6J,OAAOnJ,QAAQ,gBAElC,OAASg/B,GACPtiC,KAAK4C,UAAU6J,OAAOnJ,QAAQg/B,EAAElsB,SAAW,gBAC7C,CACF,EAGFyrB,gBAAgBW,YAAcxB,YCvM9B,MAAMyB,6BAA6Bl1B,EAAAA,UACjC,WAAAjO,CAAYC,EAAU,IACpBC,MAAM,IACDD,EAGHqK,KAAM,yBACN4D,SAAU,gBACVrH,OAAQ,yBAGRsH,WAAY+zB,gBAGZ1nB,SAAU+nB,gBACVl0B,kBAAmB,CACjBvK,QAAQ,GAKVmF,QAAS,CACP,CAAEC,IAAK,eAAgB7I,MAAO,eAAgB+I,UAAU,GACxD,CAAEF,IAAK,UAAW7I,MAAO,UAAW+I,UAAU,EAAMD,UAAW,gBAC/D,CAAED,IAAK,YAAa7I,MAAO,YAAa+I,UAAU,EAAMD,UAAW,cACnE,CAAED,IAAK,YAAa7I,MAAO,SAAU8I,UAAW,aAChD,CAAED,IAAK,UAAW7I,MAAO,OAAQ8I,UAAW,aAC5C,CAAED,IAAK,WAAY7I,MAAO,QAAS8I,UAAW,aAC9C,CAAED,IAAK,mBAAoB7I,MAAO,QAAS+I,UAAU,EAAMD,UAAW,gBACtE,CAAED,IAAK,aAAc7I,MAAO,aAAc8I,UAAW,cACrD,CAAED,IAAK,0BAA2B7I,MAAO,cAAe+I,UAAU,IAIpEwF,YAAY,EACZC,YAAY,EACZzF,UAAU,EACV0F,YAAY,EACZC,WAAW,EAGX0L,YAAa,OAGbzL,aAAa,EACbC,SAAS,EACTC,YAAY,EAGZC,aAAc,0BAGdE,aAAc,CACZC,SAAS,EACTC,UAAU,EACVC,OAAO,EACPC,YAAY,GAGdiL,iBAAkB,CACdnI,eAAgB,SAChB6wB,cAAe,YACf5wB,MAAQmI,IACJA,EAAIhL,iBAEJjP,KAAKka,cAIjB,CAEA,cAAMA,GAEF,MAAMlX,QAAahD,KAAK4C,SAAS2M,SAAS,CACtC9P,MAAO,sBACP8H,OAAQ,CACJ,CACIqC,KAAM,SACNC,KAAM,OACNiG,UAAU,MAItB,GAAI9M,GAAQA,EAAKo/B,OAAQ,CACrB,MAAMhyB,QAAa4wB,YAAYrnB,OAAO3W,EAAKo/B,QACvChyB,EAAKxJ,OACL5G,KAAKma,UAAUC,WAAWhK,EAElC,CACJ,EClFF,MAAMuyB,gBAAgBtjC,EAAAA,KACpB,WAAAC,CAAYC,EAAU,IACpBC,MAAM,CACJO,UAAW,cACRR,IAGLS,KAAK4G,MAAQrH,EAAQqH,OAAS,IAAI86B,IAAIniC,EAAQyD,MAAQ,IAEtDhD,KAAKmH,SAAW,2kDAsClB,CAEA,YAAMvG,GAEJZ,KAAK4iC,YAAc,IAAIv7B,UAAS,CAC9BT,MAAO5G,KAAK4G,MACZ7G,UAAW,MACXuH,iBAAiB,EACjBqP,eAAgB,IAChBpO,QAAS,EACThB,OAAQ,CACN,CAAEqC,KAAM,YAAajK,MAAO,YAAa8I,UAAW,aAAcmO,KAAM,GACxE,CAAEhN,KAAM,SAAUjK,MAAO,SAAU8I,UAAW,aAAcmO,KAAM,GAClE,CAAEhN,KAAM,cAAejK,MAAO,OAAQiX,KAAM,GAC5C,CAAEhN,KAAM,YAAajK,MAAO,KAAMiX,KAAM,GACxC,CAAEhN,KAAM,OAAQjK,MAAO,eAAgBiX,KAAM,OAKjD5W,KAAK6iC,aAAe,IAAIx7B,UAAS,CAC/BT,MAAO5G,KAAK4G,MACZ7G,UAAW,MACXuH,iBAAiB,EACjBqP,eAAgB,IAChBpO,QAAS,EACThB,OAAQ,CACN,CAAEqC,KAAM,WAAYjK,MAAO,WAAY8I,UAAW,aAAcmO,KAAM,GACtE,CAAEhN,KAAM,sBAAuBjK,MAAO,sBAAuBiX,KAAM,GACnE,CAAEhN,KAAM,UAAWjK,MAAO,UAAW8I,UAAW,WAAYmO,KAAM,GAClE,CAAEhN,KAAM,eAAgBjK,MAAO,eAAgB8I,UAAW,WAAYmO,KAAM,GAC5E,CAAEhN,KAAM,aAAcjK,MAAO,aAAciX,KAAM,GACjD,CAAEhN,KAAM,gBAAiBjK,MAAO,gBAAiBiX,KAAM,OAK3D5W,KAAK+W,aAAe,IAAI1P,UAAS,CAC/BT,MAAO5G,KAAK4G,MACZ7G,UAAW,MACXuH,iBAAiB,EACjBqP,eAAgB,IAChBpO,QAAS,EACThB,OAAQ,CACN,CAAEqC,KAAM,KAAMjK,MAAO,YAAaiX,KAAM,GACxC,CAAEhN,KAAM,UAAWjK,MAAO,UAAW8I,UAAW,WAAYmO,KAAM,GAClE,CAAEhN,KAAM,WAAYjK,MAAO,gBAAiB8I,UAAW,WAAYmO,KAAM,MAI7E,MAAMnM,EAAO,CACXq4B,QAAW9iC,KAAK4iC,YAChBG,SAAY/iC,KAAK6iC,aACjBzrB,SAAYpX,KAAK+W,cAGnB/W,KAAK8G,QAAU,IAAI0D,UAAQ,CACzBrI,YAAa,WACbsI,OACAS,UAAW,YAEblL,KAAKoC,SAASpC,KAAK8G,SAGnB,MAMMo7B,EAAU,IAAI32B,cAAY,CAC9BpJ,YAAa,mBACbpC,UAAW,yCACXyL,QAASxL,KAAK4G,MACd6E,OAAQ,CACN7L,KAAM,yBACN8L,MAZc,CAChB,CAAE/L,MAAO,UAAWE,OAAQ,cAAeD,KAAM,mBACjD,CAAEiK,KAAM,WACR,CAAElK,MAAO,iBAAkBE,OAAQ,aAAcD,KAAM,WAAYiR,QAAQ,OAY7E7Q,KAAKoC,SAAS8/B,EAChB,CAIA,wBAAMc,GACJ,IACEhjC,KAAK4C,UAAU6J,OAAOyM,OAAO,+BACvBlZ,KAAK4G,MAAM4M,cACXxT,KAAKqD,SACXrD,KAAK4C,UAAU6J,OAAO1J,UAAU,oBAClC,OAASu/B,GACPtiC,KAAK4C,UAAU6J,OAAOnJ,QAAQg/B,EAAElsB,SAAW,iBAC7C,CACF,CAEA,uBAAM6sB,GAQJ,SALwBl3B,EAAAA,OAAOO,QADnB,gDADE,mBAEqC,CACjDmN,aAAc,aACdC,YAAa,WAKf,IACE,MAAMtJ,QAAapQ,KAAK4G,MAAMsK,UAC1Bd,GAAMrN,QACR/C,KAAKwF,KAAK,cAAe,CAAEoB,MAAO5G,KAAK4G,QAEvC5G,KAAK4C,UAAU6J,OAAOnJ,QAAQ,gBAElC,OAASg/B,GACPtiC,KAAK4C,UAAU6J,OAAOnJ,QAAQg/B,EAAElsB,SAAW,gBAC7C,CACF,EAGFusB,QAAQH,YAAcd,IC5KtB,MAAMwB,qBAAqB31B,EAAAA,UACzB,WAAAjO,CAAYC,EAAU,IACpBC,MAAM,IACDD,EAGHqK,KAAM,qBACN4D,SAAU,eACVrH,OAAQ,qBAGRsH,WAAYm0B,QAGZ9nB,SAAU6oB,QACVh1B,kBAAmB,CACjBvK,QAAQ,EACR+E,KAAM,MAIRI,QAAS,CACP,CAAEC,IAAK,YAAa7I,MAAO,YAAa+I,UAAU,GAClD,CAAEF,IAAK,cAAe7I,MAAO,OAAQ+I,UAAU,EAAMD,UAAW,gBAChE,CAAED,IAAK,YAAa7I,MAAO,KAAM+I,UAAU,EAAMD,UAAW,gBAC5D,CAAED,IAAK,SAAU7I,MAAO,SAAU+I,UAAU,GAC5C,CAAEF,IAAK,WAAY7I,MAAO,WAAY+I,UAAU,EAAMD,UAAW,gBACjE,CAAED,IAAK,OAAQ7I,MAAO,UAAW8I,UAAW,gBAC5C,CAAED,IAAK,UAAW7I,MAAO,UAAW+I,UAAU,EAAMD,UAAW,YAC/D,CAAED,IAAK,eAAgB7I,MAAO,eAAgB+I,UAAU,EAAMD,UAAW,YACzE,CAAED,IAAK,UAAW7I,MAAO,UAAW+I,UAAU,EAAMD,UAAW,aAIjEyF,YAAY,EACZC,YAAY,EACZzF,UAAU,EACV0F,YAAY,EACZC,WAAW,EAGX0L,YAAa,OAGbzL,aAAa,EACbC,SAAS,EACTC,YAAY,EAGZC,aAAc,yBAGdE,aAAc,CACZC,SAAS,EACTC,UAAU,EACVC,OAAO,EACPC,YAAY,IAGlB,EC/DF,MAAMo0B,0BAA0Bz/B,EAAAA,KAC5B,WAAApE,CAAYC,EAAU,IAClBC,MAAM,IACCD,EACHE,MAAO,+BACPM,UAAW,uBAEnB,CAEA,iBAAMY,GACF,MAAO,osCA0BX,CAEA,YAAMC,GACFZ,KAAKojC,gBAAkB,IAAIl/B,eAAa,CACpC/B,YAAa,mBACbI,SAAU,qBACVlB,MAAO,CAAC,YAAa,eACrBE,UAAW,SAEfvB,KAAKoC,SAASpC,KAAKojC,iBAEnBpjC,KAAKqjC,YAAc,IAAIC,WAAS,CAC5BnhC,YAAa,eACbI,SAAU,oCAEdvC,KAAKoC,SAASpC,KAAKqjC,aAEnBrjC,KAAKujC,iBAAmB,IAAIn7B,YAAU,CAClCjG,YAAa,oBACb1C,MAAO,oBACPgO,WAAY,IAAI+1B,mBAAiB,CAAEx7B,OAAQ,CAAEy7B,MAAO,WAAYC,OAAQ,KACxEn7B,QAAS,CAAC,CAAEC,IAAK,QAAS7I,MAAO,SAAW,CAAE6I,IAAK,SAAU7I,MAAO,SAAU8I,UAAW,YAE7FzI,KAAKoC,SAASpC,KAAKujC,kBAEnBvjC,KAAK2jC,iBAAmB,IAAIv7B,YAAU,CAClCjG,YAAa,oBACb1C,MAAO,oBACPgO,WAAY,IAAI+1B,EAAAA,iBAAiB,CAAEx7B,OAAQ,CAAE/E,OAAQ,SAAUwgC,MAAO,WAAYC,OAAQ,KAC1Fn7B,QAAS,CAAC,CAAEC,IAAK,QAAS7I,MAAO,SAAW,CAAE6I,IAAK,gBAAiB7I,MAAO,YAE/EK,KAAKoC,SAASpC,KAAK2jC,iBACvB,ECtEJ,MAAMC,4BAA4Br2B,EAAAA,UAC9B,WAAAjO,CAAYC,EAAU,IAClBC,MAAM,IACCD,EACHqK,KAAM,qBACN4D,SAAU,sBACVrH,OAAQ,qBACRsH,WAAYo2B,EAAAA,eACZ1vB,WAAY2vB,EAAAA,gBAAgB12B,OAC5BgE,SAAU0yB,EAAAA,gBAAgBh4B,KAE1BvD,QAAS,CACL,CAAEC,IAAK,KAAM7I,MAAO,KAAMoJ,MAAO,QACjC,CAAEP,IAAK,OAAQ7I,MAAO,QACtB,CAAE6I,IAAK,aAAc7I,MAAO,QAAS8I,UAAW,sBAChD,CAAED,IAAK,gBAAiB7I,MAAO,cAC/B,CAAE6I,IAAK,YAAa7I,MAAO,SAAUsK,OAAQ,YAGjDkE,YAAY,EACZzF,UAAU,EACV2F,WAAW,EACXC,aAAa,EACbC,SAAS,EACTC,YAAY,EACZkpB,QAAS,CAAC,OAAQ,UAClB/oB,aAAc,CACVyvB,UAAW,CAAC,GAAI,GAAI,IACpBC,gBAAiB,GACjB5vB,aAAc,gCACd6vB,UAAW,YAIvB,EClCJ,MAAMyF,8BAA8Bx2B,EAAAA,UAChC,WAAAjO,CAAYC,EAAU,IAClBC,MAAM,IACCD,EACHqK,KAAM,uBACN4D,SAAU,iBACVrH,OAAQ,uBACRsH,WAAYu2B,EAAAA,iBACZ7vB,WAAY8vB,EAAAA,kBAAkB72B,OAC9BgE,SAAU6yB,EAAAA,kBAAkBn4B,KAE5BvD,QAAS,CACL,CAAEC,IAAK,KAAM7I,MAAO,KAAMoJ,MAAO,QACjC,CAAEP,IAAK,OAAQ7I,MAAO,QACtB,CAAE6I,IAAK,WAAY7I,MAAO,YAC1B,CAAE6I,IAAK,aAAc7I,MAAO,QAAS8I,UAAW,sBAChD,CAAED,IAAK,WAAY7I,MAAO,YAC1B,CAAE6I,IAAK,YAAa7I,MAAO,SAAUsK,OAAQ,YAGjDkE,YAAY,EACZzF,UAAU,EACV2F,WAAW,EACXC,aAAa,EACbC,SAAS,EACTC,YAAY,EAEZG,aAAc,CACVyvB,UAAW,CAAC,GAAI,GAAI,IACpBC,gBAAiB,GACjB5vB,aAAc,2BACd6vB,UAAW,uBACX5G,QAAS,CAAC,OAAQ,YAG9B,ECpCJ,MAAMwM,yBAAyB7kC,EAAAA,KAC3B,WAAAC,CAAYC,EAAU,IAClBC,MAAM,CACFO,UAAW,wBACRR,IAEPS,KAAK4G,MAAQrH,EAAQqH,KACzB,CAEA,WAAAjG,GACI,MAAO,koCAwBX,EChCJ,MAAMwjC,8BAA8B52B,EAAAA,UAChC,WAAAjO,CAAYC,EAAU,IAClBC,MAAM,IACCD,EACHqK,KAAM,wBACN4D,SAAU,kBACVrH,OAAQ,wBACRsH,WAAY+1B,EAAAA,iBACZnyB,cAAe6yB,iBACfv2B,kBAAmB,CACfvK,QAAQ,EACR+E,KAAM,MAGVI,QAAS,CACL,CAAEC,IAAK,KAAM7I,MAAO,KAAMoJ,MAAO,QACjC,CAAEP,IAAK,UAAW7I,MAAO,YAAa8I,UAAW,YACjD,CAAED,IAAK,oBAAqB7I,MAAO,QACnC,CAAE6I,IAAK,qBAAsB7I,MAAO,UACpC,CAAE6I,IAAK,QAAS7I,MAAO,SACvB,CAAE6I,IAAK,WAAY7I,MAAO,YAC1B,CAAE6I,IAAK,SAAU7I,MAAO,SAAU8I,UAAW,UAGjD0F,YAAY,EACZzF,UAAU,EACV2F,WAAW,EACXC,aAAa,EAEbK,aAAc,CACVyvB,UAAW,CAAC,GAAI,GAAI,IACpBC,gBAAiB,GACjB5vB,aAAc,uBACd6vB,UAAW,UACX5G,QAAS,CAAC,UAGtB,ECtCJ,MAAM0M,uBAAuB/kC,EAAAA,KACzB,WAAAC,CAAYC,EAAU,IAClBC,MAAM,CACFO,UAAW,sBACRR,IAEPS,KAAK4G,MAAQrH,EAAQqH,KACzB,CAEA,WAAAjG,GACI,MAAO,8OAOX,CAEA,MAAAC,GACIZ,KAAKqkC,SAAW,IAAIh9B,UAAS,CACzBlF,YAAa,YACbyE,MAAO5G,KAAK4G,MACZW,OAAQ,CACJ,CAAEqC,KAAM,WAAYjK,MAAO,WAAYsK,OAAQ,SAC/C,CAAEL,KAAM,eAAgBjK,MAAO,eAAgBsK,OAAQ,WACvD,CAAEL,KAAM,cAAejK,MAAO,eAC9B,CAAEiK,KAAM,aAAcjK,MAAO,cAC7B,CAAEiK,KAAM,YAAajK,MAAO,YAAasK,OAAQ,YACjD,CAAEL,KAAM,mBAAoBjK,MAAO,cAAesK,OAAQ,WAGlEjK,KAAKoC,SAASpC,KAAKqkC,SACvB,EChCJ,MAAMC,4BAA4B/2B,EAAAA,UAC9B,WAAAjO,CAAYC,EAAU,IAClBC,MAAM,IACCD,EACHqK,KAAM,qBACN4D,SAAU,qBACVrH,OAAQ,qBACRsH,WAAYlE,EAAAA,eACZ8H,cAAe+yB,eACfz2B,kBAAmB,CACfvK,QAAQ,EACR+E,KAAM,MAGVI,QAAS,CACL,CAAEC,IAAK,KAAM7I,MAAO,KAAMoJ,MAAO,QACjC,CAAEP,IAAK,oBAAqB7I,MAAO,QACnC,CAAE6I,IAAK,cAAe7I,MAAO,eAC7B,CAAE6I,IAAK,WAAY7I,MAAO,WAAY8I,UAAW,SACjD,CAAED,IAAK,cAAe7I,MAAO,eAC7B,CAAE6I,IAAK,eAAgB7I,MAAO,eAAgBsK,OAAQ,WACtD,CAAEzB,IAAK,YAAa7I,MAAO,YAAa8I,UAAW,aAGvD0F,YAAY,EACZzF,UAAU,EACV2F,WAAW,EACXC,aAAa,EAEbK,aAAc,CACVyvB,UAAW,CAAC,GAAI,GAAI,IACpBC,gBAAiB,GACjB5vB,aAAc,oBACd6vB,UAAW,WACX5G,QAAS,CAAC,OAAQ,YAG9B,EClCW,MAAM6M,qBAAqBllC,EAAAA,KACtC,WAAAC,CAAYC,EAAU,IAClBC,MAAM,CACFO,UAAW,uBACRR,IAGPS,KAAKC,MAAQ,CACTukC,QAAS,EACTC,QAAS,EACTC,UAAW,EACXC,OAAQ,EACRC,UAAW,GAGf5kC,KAAKmH,SAAW,8jLAoGpB,CAEA,cAAAgG,GACEnN,KAAKwD,YACDxD,KAAK6kC,aACL7kC,KAAKqD,QAEX,CAEA,eAAMG,GACFxD,KAAKC,MAAQD,KAAK4G,MAAM4I,WAAWs1B,MACvC,EC7HW,MAAMC,sBAAsB1lC,EAAAA,KACvC,WAAAC,CAAYC,EAAU,IAClBC,MAAM,CACFO,UAAW,wBACRR,IAGPS,KAAKglC,OAAS,CACV/hC,OAAQ,UACRgiC,QAAS,CAAEC,OAAQ,EAAGzT,MAAO,GAC7B0T,SAAU,IAGdnlC,KAAKmH,SAAW,2zGAwDpB,CAEA,cAAAgG,GACEnN,KAAKolC,aACDplC,KAAK6kC,aACL7kC,KAAKqD,QAEX,CAEA,gBAAM+hC,GACF,IAAKplC,KAAK4G,MAAMwI,EAAE01B,OAAQ,OAC1B,MAAM9hC,EAAOhD,KAAK4G,MAAM4I,WAExB,IAAI61B,EAAiB,UACc,IAA/BriC,EAAK8hC,OAAOQ,eACZD,EAAiB,WACTriC,EAAKuiC,UAAUL,SACvBG,EAAiB,WAErBrlC,KAAKglC,OAAS,CACVK,iBACAF,SAAUniC,EAAKmiC,SACfF,QAAS,CACLC,OAAQliC,EAAK8hC,OAAOQ,eACpB7T,MAAOzuB,EAAKiiC,QAAQ9nB,QAExB2nB,OAAQ9hC,EAAK8hC,OACbS,UAAWviC,EAAKuiC,WAGpBvlC,KAAKwlC,kBAAoBxlC,KAAKylC,qBAAqBzlC,KAAKglC,OAAOK,gBAC/DrlC,KAAK0lC,sBACL1lC,KAAK2lC,uBAET,CAEA,mBAAAD,GACS1lC,KAAKglC,OAAOG,WAEjBnlC,KAAKglC,OAAOY,cAAgB1iC,OAAO6uB,OAAO/xB,KAAKglC,OAAOG,UAAUvqB,IAAIirB,IAChE,IAAIC,EAAgB,UAGpB,MAAMC,GAAaF,EAAQG,cAAgB,IAAMH,EAAQI,gBAAkB,GAQ3E,OAPIF,EAAY,KACZD,EAAgB,YAEhBC,EAAY,KAA2B,IAApBF,EAAQZ,WAC3Ba,EAAgB,YAGb,IACAD,EACH5iC,OAAQ6iC,EACRI,iBAAkBlmC,KAAKmmC,qBAAqBL,GAC5CM,WAAYpmC,KAAKqmC,eAAeP,GAChCQ,OAAQT,EAAQG,cAAgB,EAChCO,SAAUV,EAAQI,gBAAkB,EAEpCzB,QAASqB,EAAQG,cAAgB,EACjCvB,QAASoB,EAAQI,gBAAkB,KAG/C,CAEA,qBAAAN,GACI,IAAK3lC,KAAKglC,OAAOO,UAIb,OAHAvlC,KAAKwmC,oBAAsB,UAC3BxmC,KAAKymC,qBAAuB,kBAC5BzmC,KAAK0mC,cAAgB,2BAIrB1mC,KAAKglC,OAAOO,UAAUL,QACtBllC,KAAKwmC,oBAAsB,UAC3BxmC,KAAKymC,qBAAuB,eAC5BzmC,KAAK0mC,cAAgB,yBAErB1mC,KAAKwmC,oBAAsB,UAC3BxmC,KAAKymC,qBAAuB,cAC5BzmC,KAAK0mC,cAAgB,oBAE7B,CAEA,oBAAAjB,CAAqBxiC,GAOjB,MANgB,CACZ0jC,QAAW,eACX1yB,QAAW,eACX2yB,SAAY,cACZC,QAAW,cAEA5jC,IAAW,YAC9B,CAEA,oBAAAkjC,CAAqBljC,GAMjB,MALgB,CACZ0jC,QAAW,aACX1yB,QAAW,aACX2yB,SAAY,aAED3jC,IAAW,cAC9B,CAEA,cAAAojC,CAAepjC,GAMX,MALc,CACV0jC,QAAW,uBACX1yB,QAAW,+BACX2yB,SAAY,qBAEH3jC,IAAW,qBAC5B,CAEA,2BAAM6jC,CAAsBtiC,EAAOC,GAC/B,IACIA,EAAQM,UAAW,QACb/E,KAAK4G,MAAM4M,OACrB,OAASlQ,GACLC,QAAQD,MAAM,4BAA6BA,EAC/C,CAAA,QACImB,EAAQM,UAAW,CACvB,CACJ,CAEA,4BAAMgiC,SACIh7B,EAAAA,OAAOi7B,UAAU,CACnBvnC,MAAO,kBACP2W,QAAS,yCACTvM,KAAM,QAEd,EC7LJ,MAAMo9B,uBAAuB5nC,EAAAA,KACzB,WAAAC,CAAYC,EAAU,IAClBC,MAAM,CACFO,UAAW,sBACRR,IAIPS,KAAK4G,MAAQrH,EAAQqH,OAAS,IAAIsgC,EAAAA,IAAI3nC,EAAQyD,MAAQ,IAGtDhD,KAAK8G,QAAU,KACf9G,KAAK0Q,aAAe,KACpB1Q,KAAKmnC,YAAc,KACnBnnC,KAAKiH,WAAa,KAClBjH,KAAKkH,SAAW,KAGhBlH,KAAKonC,oBAAsB,KAG3BpnC,KAAKmH,SAAW,wyGAyDpB,CAEA,YAAMvG,GAEFZ,KAAK0Q,aAAe,IAAIrR,OAAK,CACzB8H,SAAU,60IAqEVP,MAAO5G,KAAK4G,QAIhB5G,KAAKmnC,YAAc,IAAI9nC,OAAK,CACxB8H,SAAU,2LAKVP,MAAO5G,KAAK4G,QAIhB,MAAM+B,EAAmB,IAAI0+B,eAAa,CACtCr/B,OAAQ,CAAEs/B,IAAKtnC,KAAK4G,MAAMsB,IAAI,MAAOC,KAAM,MAE/CnI,KAAKiH,WAAa,IAAImB,YAAU,CAC5BC,WAAYM,EACZL,oBAAqB,CAAC,OACtBC,QAAS,CACL,CAAEC,IAAK,KAAM7I,MAAO,YAAa8I,UAAW,WAAYC,UAAU,GAClE,CAAEF,IAAK,QAAS7I,MAAO,QAAS8I,UAAW,SAC3C,CAAED,IAAK,eAAgB7I,MAAO,cAKtC,MAAM8J,EAAiB,IAAI89B,aAAW,CAClCv/B,OAAQ,CAAEs/B,IAAKtnC,KAAK4G,MAAMsB,IAAI,MAAOC,KAAM,MAE/CnI,KAAKkH,SAAW,IAAIkB,YAAU,CAC1BC,WAAYoB,EACZnB,oBAAqB,CAAC,OACtBC,QAAS,CACL,CAAEC,IAAK,mBAAoB7I,MAAO,UAAW+I,UAAU,GACvD,CAAEF,IAAK,OAAQ7I,MAAO,OAAQ8I,UAAW,SACzC,CAAED,IAAK,UAAW7I,MAAO,cAKjCK,KAAK8G,QAAU,IAAI0D,UAAQ,CACvBC,KAAM,CACFkG,SAAY3Q,KAAK0Q,aACjB82B,QAAWxnC,KAAKmnC,YAChBt8B,OAAU7K,KAAKiH,WACf6D,KAAQ9K,KAAKkH,UAEjBgE,UAAW,WACX/I,YAAa,qBAEjBnC,KAAKoC,SAASpC,KAAK8G,SAGnB,MAAM2gC,EAAmB,CACrB,CAAE9nC,MAAO,UAAWE,OAAQ,cAAeD,KAAM,uBAGjDI,KAAK4G,MAAM8gC,WAAa1nC,KAAK4G,MAAM8gC,aACnCD,EAAiBzvB,KAAK,CAClBrY,MAAO,aACPE,OAAQ,aACRD,KAAM,cACNkO,MAAO,gBAIX9N,KAAK4G,MAAM+gC,UAAY3nC,KAAK4G,MAAM+gC,YAClCF,EAAiBzvB,KAAK,CAClBrY,MAAO,YACPE,OAAQ,YACRD,KAAM,kBACNkO,MAAO,iBAIf,MAAM85B,EAAU,IAAIr8B,cAAY,CAC5BpJ,YAAa,mBACbpC,UAAW,yCACXyL,QAASxL,KAAK4G,MACd6E,OAAQ,CACJ7L,KAAM,yBACN8L,MAAO+7B,KAGfznC,KAAKoC,SAASwlC,SAER5nC,KAAK4G,MAAM4M,MAAM,CAAExL,OAAQ,CAAE6/B,MAAO,WAG9C,CAEA,oBAAMplC,SACIzC,KAAK8nC,gBACf,CAEA,oBAAMA,GACG9nC,KAAK4G,QAGV5G,KAAK4G,MAAMwI,EAAE82B,iBAAmBlmC,KAAK4G,MAAMmhC,oBAAsB/nC,KAAK4G,MAAMmhC,sBAAwB,eACpG/nC,KAAK4G,MAAMwI,EAAEg3B,WAAapmC,KAAK4G,MAAMohC,cAAgBhoC,KAAK4G,MAAMohC,gBAAkB,qBAClFhoC,KAAK4G,MAAMwI,EAAE64B,kBAAoBjoC,KAAK4G,MAAMshC,qBAAuBloC,KAAK4G,MAAMshC,uBAAyB,MAC3G,CAEA,oBAAMC,GACF,GAAKnoC,KAAK4G,OAAOsB,IAAI,MAErB,IACQlI,KAAK4G,MAAMwhC,yBACLpoC,KAAK4G,MAAMwhC,0BAEfpoC,KAAK8nC,gBACf,OAASxkC,GACLC,QAAQD,MAAM,8BAA+BA,EACjD,CACJ,CAEA,wBAAM+kC,SACIroC,KAAK4G,MAAM4M,MAAM,CAAExL,OAAQ,CAAE6/B,MAAO,WAC9C,CAEA,uBAAMS,GACF,GAAIh8B,QAAQ,6CACR,IACI,MAAM3J,QAAiB3C,KAAK4G,MAAM2hC,SAC9B5lC,EAASI,eACH/C,KAAKmoC,uBACLnoC,KAAKqD,SACXrD,KAAKwF,KAAK,gBAAiB,CAAE8hC,IAAKtnC,KAAK4G,SAEvCjB,MAAM,0BAA4BhD,EAASK,MAAMM,OAAS,iBAElE,OAASA,GACLC,QAAQD,MAAM,wBAAyBA,GACvCqC,MAAM,yBAA2BrC,EAAM8S,QAC3C,CAER,CAEA,sBAAMoyB,GACF,MAAMC,QAAkB18B,EAAAA,OAAOwD,SAAS,CACpC9P,MAAO,YACP0M,WAAYu8B,EAAAA,SAASC,QAGzB,GAAIF,EACA,IACI,MAAM9lC,QAAiB3C,KAAK4G,MAAM+hC,MAAMF,EAAUG,OAAS,GACvDjmC,EAASI,QACT/C,KAAKwF,KAAK,cAAe,CAAE8hC,IAAKtnC,KAAK4G,MAAOiiC,SAAUlmC,EAASkmC,WAE/DljC,MAAM,yBAA2BhD,EAASK,MAAMM,OAAS,iBAEjE,OAASA,GACLC,QAAQD,MAAM,uBAAwBA,GACtCqC,MAAM,wBAA0BrC,EAAM8S,QAC1C,CAER,CAEA,gBAAA0yB,GACQ9oC,KAAKonC,qBAAqB2B,cAAc/oC,KAAKonC,qBAE7CpnC,KAAK4G,OAAOoiC,UAAYhpC,KAAK4G,MAAMoiC,aACnChpC,KAAKonC,oBAAsB6B,YAAYl3B,UACnC,UACU/R,KAAKmoC,iBACPnoC,KAAK6kC,mBACC7kC,KAAKqD,QAEnB,OAASC,GACLC,QAAQD,MAAM,uBAAwBA,EAC1C,GACD,KAEX,CAEA,eAAA4lC,GACQlpC,KAAKonC,sBACL2B,cAAc/oC,KAAKonC,qBACnBpnC,KAAKonC,oBAAsB,KAEnC,CAEA,eAAM+B,GACFnpC,KAAKkpC,wBACC1pC,MAAM2pC,WAChB,CAGA,iBAAazzB,CAAK4xB,EAAK/nC,EAAU,IAC7B,MAAMsW,EAAO,IAAIoxB,eAAe,CAAErgC,MAAO0gC,IAEzC,aAAav7B,EAAAA,OAAOqzB,WAAW,CAC3B3/B,MAAO,uDAAuD6nC,EAAIp/B,IAAI,QACtE6N,KAAMF,EACN1N,KAAM,KACNs4B,YAAY,EACZzqB,QAAS,CAAC,CAAEC,KAAM,QAASnI,MAAO,gBAAiBoI,SAAS,IAC5DkzB,OAAQ,IAAMvzB,EAAKqzB,qBAChB3pC,GAEX,EAGJ2nC,EAAAA,IAAI75B,WAAa45B,eC/WF,MAAMoC,kBAAkBjhC,EAAAA,UACnC,WAAA9I,CAAYC,EAAU,IAClBC,MAAM,CACFiO,WAAY67B,EAAAA,QACZC,iBAAkB,CACdphC,KAAM,GACN0F,KAAM,YAEVtO,QAAS,CACL4O,YAAY,EACZzF,UAAU,EACV2F,WAAW,EACXlG,KAAM,MACH5I,EAAQA,SAEfgJ,QAAS,CACL,CACIC,IAAK,KACL7I,MAAO,SACP8I,UAAW,sBACXC,UAAU,EACVxD,OAAQ,CAAE2E,KAAM,OAAQ2/B,YAAa,cAEzC,CACIhhC,IAAK,SACL7I,MAAO,SACP8I,UAAW,CAAC2B,EAAOoB,KACf,MAAM87B,EAAM97B,EAAQi+B,IAGpB,MAAO,sBAFYnC,EAAIS,oBAAsBT,EAAIS,sBAAwB,6BAC5DT,EAAIU,cAAgBV,EAAIU,gBAAkB,2BACiB59B,EAAMynB,wBAElFnpB,UAAU,EACVxD,OAAQ,CACJ2E,KAAM,SACNtK,QAAS,CACL,CAAE6K,MAAO,UAAWzK,MAAO,WAC3B,CAAEyK,MAAO,UAAWzK,MAAO,WAC3B,CAAEyK,MAAO,YAAazK,MAAO,aAC7B,CAAEyK,MAAO,SAAUzK,MAAO,UAC1B,CAAEyK,MAAO,WAAYzK,MAAO,YAC5B,CAAEyK,MAAO,UAAWzK,MAAO,cAIvC,CACI6I,IAAK,UACL7I,MAAO,UACP8I,UAAW,QACXC,UAAU,EACVxD,OAAQ,CAAE2E,KAAM,OAAQ2/B,YAAa,eAEzC,CACIhhC,IAAK,UACL7I,MAAO,UACP8I,UAAW,WACXC,UAAU,EACVxD,OAAQ,CACJ2E,KAAM,YACNlK,MAAO,iBAGf,CACI6I,IAAK,aACL7I,MAAO,UACP8I,UAAW,WACXC,UAAU,GAEd,CACIF,IAAK,cACL7I,MAAO,WACP8I,UAAW,WACXC,UAAU,IAGlBgG,YAAa,CACT,CACI/O,MAAO,eACPE,OAAQ,mBACRD,KAAM,kBAEV,CACID,MAAO,cACPE,OAAQ,kBACRD,KAAM,oBAEV,CAAEuK,WAAW,GACb,CACIxK,MAAO,aACPE,OAAQ,aACRD,KAAM,cACNiR,QAAQ,EACR64B,UAAYpC,GAAQA,EAAII,WAAaJ,EAAII,aAE7C,CACI/nC,MAAO,YACPE,OAAQ,YACRD,KAAM,qBACN8pC,UAAYpC,GAAQA,EAAIK,UAAYL,EAAIK,YAE5C,CACIhoC,MAAO,YACPE,OAAQ,YACRD,KAAM,WAEV,CAAEuK,WAAW,GACb,CACIxK,MAAO,aACPE,OAAQ,aACRD,KAAM,gBAGdoO,QAAS,CACL,CACIxF,IAAK,OACL7I,MAAO,WACPkK,KAAM,SAGd0H,aAAc,CACV,CACI5R,MAAO,kBACPE,OAAQ,eACRD,KAAM,eAEV,CACID,MAAO,iBACPE,OAAQ,cACRD,KAAM,sBAEV,CACID,MAAO,kBACPE,OAAQ,eACRD,KAAM,mBAGXL,GAEX,CAEA,0BAAMoqC,CAAqBrC,GACnBA,SACML,eAAevxB,KAAK4xB,EAElC,CAEA,qBAAMsC,CAAgBtC,GAClB,MAAMuC,QAAkB99B,SAAO+9B,YAAY,6CAC3C,GAAIxC,GAAOuC,EACP,IACI,MAAM3Q,QAAeoO,EAAIiB,SACrBrP,EAAOn2B,SACP/C,KAAK4C,SAAS6J,MAAM1J,QAAQ,oCACtB/C,KAAKqI,WAAWmL,SAEtBxT,KAAK4C,SAAS6J,MAAMnJ,MAAM41B,EAAOl2B,MAAMM,OAAS,uBAExD,OAASA,GACLtD,KAAK4C,SAAS6J,MAAMnJ,MAAM,yBAA2BA,EAAM8S,QAC/D,CAER,CAEA,oBAAM2zB,CAAezC,GACjB,GAAIA,EAAK,CACL,MAAMpO,QAAentB,EAAAA,OAAOwD,SAAS,CACjC9P,MAAO,YACP0M,WAAYu8B,EAAAA,SAASC,QAGzB,GAAIzP,EACA,IACI,MAAM8Q,QAAoB1C,EAAIqB,MAAMzP,EAAO0P,OAAS,GAChDoB,EAAYjnC,SACZ/C,KAAK4C,SAAS6J,MAAM1J,QAAQ,8BACtB/C,KAAKqI,WAAWmL,SAEtBxT,KAAK4C,SAAS6J,MAAMnJ,MAAM0mC,EAAYhnC,MAAMM,OAAS,sBAE7D,OAASA,GACLtD,KAAK4C,SAAS6J,MAAMnJ,MAAM,uBAAyBA,EAAM8S,QAC7D,CAER,CACJ,CAEA,oBAAM6zB,CAAe3C,GACjB,GAAIA,EAAK,CACL,MAAMjG,EAAUiG,EAAI4C,aACdhR,QAAentB,EAAAA,OAAOwD,SAAS,CACjC9P,MAAO,YACP0M,WAAY,IACLu8B,EAAAA,SAASyB,MACZ5iC,OAAQmhC,EAAAA,SAASyB,MAAM5iC,OAAOqT,IAAIwvB,IACX,YAAfA,EAAMxgC,KACNwgC,EAAMhgC,MAAQirB,KAAKC,UAAU+L,EAAS,KAAM,GACtB,YAAf+I,EAAMxgC,OACbwgC,EAAMhgC,MAAQk9B,EAAIp/B,IAAI,YAEnBkiC,OAKnB,GAAIlR,EACA,IACI,IAAImR,EAAa,CAAA,EACbnR,EAAOmI,UACPgJ,EAAahV,KAAKiV,MAAMpR,EAAOmI,UAGnC,MAAMkJ,EAAY,CACdlJ,QAASgJ,EACTxE,QAAS3M,EAAO2M,SAAWyB,EAAIp/B,IAAI,WACnC0gC,MAAO1P,EAAO0P,OAAS,GAGrB4B,QAAoBlD,EAAImD,SAASF,GACnCC,EAAYznC,SACZ/C,KAAK4C,SAAS6J,MAAM1J,QAAQ,iCACtB/C,KAAKqI,WAAWmL,SAEtBxT,KAAK4C,SAAS6J,MAAMnJ,MAAMknC,EAAYxnC,MAAMM,OAAS,sBAE7D,OAASA,GACLtD,KAAK4C,SAAS6J,MAAMnJ,MAAM,sBAAwBA,EAAM8S,QAC5D,CAER,CACJ,ECvOW,MAAMs0B,qBAAqBtiC,EAAAA,UACtC,WAAA9I,CAAYC,EAAU,IAClBC,MAAM,CACFiO,WAAYk9B,EAAAA,cACZprC,QAAS,CACL4O,YAAY,EACZzF,UAAU,EACV2F,WAAW,EACXlG,KAAM,MACH5I,EAAQA,SAEfgJ,QAAS,CACL,CACIC,IAAK,YACL7I,MAAO,YACP8I,UAAW,sBACXC,UAAU,GAEd,CACIF,IAAK,QACL7I,MAAO,SACP8I,UAAY2B,IACR,MAAMwgC,GAAoB,IAAVxgC,EAIhB,MAAO,sBAHYwgC,EAAU,aAAe,0BAC/BA,EAAU,uBAAyB,iCACnCA,EAAU,QAAU,iBAGrCliC,UAAU,EACVxD,OAAQ,CACJ2E,KAAM,SACNtK,QAAS,CACL,CAAE6K,OAAO,EAAMzK,MAAO,SACtB,CAAEyK,OAAO,EAAOzK,MAAO,WAInC,CACI6I,IAAK,WACL7I,MAAO,WACP8I,UAAY08B,GACHA,GAAaA,EAAShoB,OACpBgoB,EAASvqB,IAAIiwB,GAAK,yCAAyCA,YAAYrzB,KAAK,IADzC,OAG9C9O,UAAU,GAEd,CACIF,IAAK,iBACL7I,MAAO,YACP+I,UAAU,GAEd,CACIF,IAAK,cACL7I,MAAO,SACP8I,UAAY2B,GAED,sBADYA,EAAQ,EAAI,YAAc,iBACDA,WAEhD1B,UAAU,GAEd,CACIF,IAAK,iBACL7I,MAAO,iBACP8I,UAAY2B,IACR,IAAKA,EAAO,MAAO,QACnB,MAAM0gC,EAAgB,IAAIhnC,KAAKsG,GAEzB2gC,qBADUjnC,KACKgnC,EACfE,EAAcxY,KAAKyY,MAAMF,EAAS,KAExC,OAAIC,EAAc,GAAW,GAAGA,SAC5BA,EAAc,KAAa,GAAGxY,KAAKyY,MAAMD,EAAc,WACpD,GAAGxY,KAAKyY,MAAMD,EAAc,cAEvCtiC,UAAU,GAEd,CACIF,IAAK,UACL7I,MAAO,SACP8I,UAAY2B,IACR,IAAKA,EAAO,MAAO,UACnB,MAAM8gC,EAAY,IAAIpnC,KAAKsG,GAErB2gC,qBADUjnC,KACKonC,EACfF,EAAcxY,KAAKyY,MAAMF,EAAS,KAExC,OAAIC,EAAc,GAAW,GAAGA,KAC5BA,EAAc,KAAa,GAAGxY,KAAKyY,MAAMD,EAAc,OACvDA,EAAc,MAAc,GAAGxY,KAAKyY,MAAMD,EAAc,SACrD,GAAGxY,KAAKyY,MAAMD,EAAc,WAEvCtiC,UAAU,IAGlBgG,YAAa,CACT,CACI/O,MAAO,cACPE,OAAQ,cACRD,KAAM,WAEV,CACID,MAAO,eACPE,OAAQ,sBACRD,KAAM,kBAEV,CAAEuK,WAAW,GACb,CACIxK,MAAO,eACPE,OAAQ,eACRD,KAAM,kBACN8pC,UAAYyB,IAAmC,IAAxBA,EAAOjjC,IAAI,UAEtC,CACIvI,MAAO,gBACPE,OAAQ,gBACRD,KAAM,iBACN8pC,UAAYyB,IAAmC,IAAxBA,EAAOjjC,IAAI,UAEtC,CAAEiC,WAAW,GACb,CACIxK,MAAO,kBACPE,OAAQ,kBACRD,KAAM,WACNiR,QAAQ,EACR64B,UAAYyB,IAAmC,IAAxBA,EAAOjjC,IAAI,cAGvC3I,GAEX,CAEA,wBAAM6rC,CAAmB5mC,EAAOC,GAC5B,MAAM4mC,EAAW5mC,EAAQmI,aAAa,WAChCu+B,EAASnrC,KAAKqI,WAAWH,IAAImjC,GACnC,GAAIF,EACA,IACI,MAAMjS,QAAeiS,EAAOG,OACxBpS,EAAOn2B,SACP/C,KAAK4C,SAAS6J,MAAM1J,QAAQ,gCACtB/C,KAAKqI,WAAWmL,SAEtBxT,KAAK4C,SAAS6J,MAAMnJ,MAAM41B,EAAOl2B,MAAMM,OAAS,qBAExD,OAASA,GACLtD,KAAK4C,SAAS6J,MAAMnJ,MAAM,yBAA2BA,EAAM8S,QAC/D,CAER,CAEA,4BAAMm1B,CAAuB/mC,EAAOC,GAChC,MAAM4mC,EAAW5mC,EAAQmI,aAAa,WAChCu+B,EAASnrC,KAAKqI,WAAWH,IAAImjC,GACnC,GAAIF,GAAU7+B,QAAQ,kDAClB,IACI,MAAM4sB,QAAeiS,EAAOK,UAAS,GACjCtS,EAAOn2B,SACP/C,KAAK4C,SAAS6J,MAAM1J,QAAQ,mCACtB/C,KAAKqI,WAAWmL,SAEtBxT,KAAK4C,SAAS6J,MAAMnJ,MAAM41B,EAAOl2B,MAAMM,OAAS,4BAExD,OAASA,GACLtD,KAAK4C,SAAS6J,MAAMnJ,MAAM,+BAAiCA,EAAM8S,QACrE,CAER,ECrKW,MAAMq1B,2BAA2BrjC,EAAAA,UAC5C,WAAA9I,CAAYC,EAAU,IAClBC,MAAM,CACFiO,WAAY67B,EAAAA,QACZC,iBAAkB,CAAEtmC,OAAQ,WAC5BqF,oBAAqB,CAAC,UACtB/I,QAAS,CACL4O,YAAY,EACZzF,UAAU,EACV2F,WAAW,EACXlG,KAAM,MACH5I,EAAQA,SAEfgJ,QAAS,CACL,CACIC,IAAK,KACL7I,MAAO,SACP8I,UAAW,sBACXC,UAAU,GAEd,CACIF,IAAK,OACL7I,MAAO,WACP+I,UAAU,GAEd,CACIF,IAAK,UACL7I,MAAO,UACP8I,UAAW,QACXC,UAAU,GAEd,CACIF,IAAK,SACL7I,MAAO,gBACP8I,UAAW,WACXC,UAAU,GAEd,CACIF,IAAK,UACL7I,MAAO,UACP8I,UAAW,WACXC,UAAU,GAEd,CACIF,IAAK,aACL7I,MAAO,aACP8I,UAAW,WACXC,UAAU,OAGfnJ,GAEX,ECxCW,MAAMmsC,sBAAsBhoC,EAAAA,KACvC,WAAApE,CAAYC,EAAU,IAClBC,MAAM,CACFC,MAAO,kBACPM,UAAW,qBACRR,IAGPS,KAAK2D,UAAY,kBACjB3D,KAAK4D,aAAe,6CACpB5D,KAAK6D,4BAAA,IAAkBC,MAAOC,iBAC9B/D,KAAKonC,oBAAsB,KAC3BpnC,KAAK2rC,YAAc,IAEnB3rC,KAAKmH,SAAW,u9JAgFpB,CAEA,YAAMvG,GAEFZ,KAAK4rC,SAAW,IAAIC,kBAEpB7rC,KAAK8rC,aAAe,IAAIvH,aAAa,CACjCpiC,YAAa,YACbyE,MAAO5G,KAAK4rC,WAEhB5rC,KAAKoC,SAASpC,KAAK8rC,cAEnB9rC,KAAK+rC,cAAgB,IAAIhH,cAAc,CACnC5iC,YAAa,aACbyE,MAAO5G,KAAK4rC,WAEhB5rC,KAAKoC,SAASpC,KAAK+rC,eAEnB/rC,KAAKgsC,cAAgB,IAAIxhC,UAAQ,CAC7BrI,YAAa,aACbsI,KAAM,CACFwhC,KAAQ,IAAI5C,UACZ6C,QAAW,IAAIxB,aACfyB,UAAa,IAAIV,oBAErBvgC,UAAW,SAEflL,KAAKoC,SAASpC,KAAKgsC,eAGnBhsC,KAAKosC,gBAAkB,IAAIloC,eAAa,CACtCzE,MAAO,kDACP8C,SAAU,qBACVb,OAAQ,IACRR,YAAa,QACbqvB,SAAU,gBACVjvB,QAAS,SACTC,UAAW,MACXW,eAAe,EACfiC,MAAO,CACLxE,MAAO,QACPyE,aAAa,GAEfC,QAAS,CACPC,EAAG,UAELnC,YAAa,gBAEfnC,KAAKoC,SAASpC,KAAKosC,uBAEbpsC,KAAK4rC,SAASp4B,OAExB,CAGA,gBAAAs1B,GACQ9oC,KAAKonC,qBACL2B,cAAc/oC,KAAKonC,qBAGnBpnC,KAAK2rC,YAAc,IACnB3rC,KAAKonC,oBAAsB6B,YAAYl3B,gBAC7B/R,KAAKqsC,eACZrsC,KAAK2rC,aAEhB,CAEA,iBAAMU,GACF,UAEUrsC,KAAK4rC,SAASp4B,QAGpB,MAAMtI,EAAYlL,KAAKgsC,eAAe9+B,eACtC,GAAIhC,EAAW,CACX,MAAMohC,EAActsC,KAAKgsC,cAAcO,OAAOrhC,GAC1CohC,GAAajkC,YAAYmL,aACnB84B,EAAYjkC,WAAWmL,OAErC,CAEAxT,KAAK6D,4BAAA,IAAkBC,MAAOC,iBAC9B/D,KAAKwsC,uBAET,OAASlpC,GACLC,QAAQD,MAAM,oCAAqCA,EACvD,CACJ,CAEA,qBAAAkpC,GACI,MAAMC,EAAmBzsC,KAAKyE,SAASG,cAAc,cACjD6nC,IACAA,EAAiB7mC,UAAY,+FAET5F,KAAK2rC,YAAc,wBAAyB3rC,KAAK6D,4BAG7E,CAEA,sBAAI6oC,GACA,OAAO1sC,KAAK2rC,YAAc,GAC9B,CAGA,wBAAMpnC,CAAmBC,EAAOC,GAC5B,IACI,MAAM7E,EAAO6E,EAAQG,cAAc,KACnChF,GAAMiF,UAAUC,IAAI,YACpBL,EAAQM,UAAW,QAEb/E,KAAKqsC,oBACLrsC,KAAKqD,QAEf,OAASC,GACLC,QAAQD,MAAM,oCAAqCA,EACvD,CAAA,QACI,MAAM1D,EAAO6E,EAAQG,cAAc,KACnChF,GAAMiF,UAAUiB,OAAO,YACvBrB,EAAQM,UAAW,CACvB,CACJ,CAEA,4BAAM4nC,CAAuBnoC,EAAOC,GAChC,MAAMmoC,EAAqD,IAA9CC,SAASpoC,EAAQmI,aAAa,cAC3C5M,KAAK2rC,YAAciB,EACnB5sC,KAAK8oC,mBAEL,MAAMgE,EAAoB,IAATF,EAAa,MAAWA,EAAO,IAAV,IACtC5sC,KAAK4C,SAAS6J,MAAM1J,QAAQ,uBAAuB+pC,IACvD,CAEA,wBAAMC,SACIhhC,EAAAA,OAAOi7B,UAAU,CACnBvnC,MAAO,cACP2W,QAAS,yCACTvM,KAAM,QAEd,CAGA,0BAAMmjC,CAAqBxoC,EAAOC,SACNsH,EAAAA,OAAO+9B,YAAY,CACvCrqC,MAAO,iBACP2W,QAAS,iFACTsD,YAAa,WACbD,aAAc,uBAIRzZ,KAAKitC,iBAAiBxoC,EAAS,IAAMyiC,EAAAA,IAAIgG,OAAQ,gCAE/D,CAEA,yBAAMC,CAAoB3oC,EAAOC,SACLsH,EAAAA,OAAO+9B,YAAY,CACvCrqC,MAAO,gBACP2W,QAAS,wEACTsD,YAAa,YACbD,aAAc,uBAIRzZ,KAAKitC,iBAAiBxoC,EAAS,IAAMyiC,EAAAA,IAAIkG,QAAS,kCAEhE,CAEA,wBAAMC,CAAmB7oC,EAAOC,GAC5B,MACM6oC,EAAiB,CACnB,CAAEljC,MAAO,GAAIzK,MAAO,oBAFPK,KAAK+rC,eAAe/G,QAAQY,eAAiB,IAG9ChrB,IAAI2yB,IAAA,CAASnjC,MAAOmjC,EAAG1H,QAASlmC,MAAO4tC,EAAG1H,YAGpD3M,QAAentB,EAAAA,OAAOwD,SAAS,CACjC9P,MAAO,mBACP0M,WAAY,CACR5E,OAAQ,CACJ,CACIqC,KAAM,UACNC,KAAM,SACNlK,MAAO,UACPJ,QAAS+tC,EACTljC,MAAO,GACPojC,KAAM,+DAMlBtU,SACMl5B,KAAKitC,iBACPxoC,EACA,IAAMyiC,EAAAA,IAAIuG,WAAWvU,EAAO2M,SAAW,MACtCljC,IACG,MAAM+qC,EAAQ/qC,EAASK,KAAK0qC,OAAS,EAErC,MAAO,WAAWA,cAA4B,IAAVA,EAAc,IAAM,KADpCxU,EAAO2M,QAAU,kBAAkB3M,EAAO2M,WAAa,MAK3F,CAEA,0BAAM8H,CAAqBnpC,EAAOC,GAC9B,MACM6oC,GADWttC,KAAK+rC,eAAe/G,QAAQY,eAAiB,IAC9BhrB,IAAI2yB,IAAA,CAASnjC,MAAOmjC,EAAG1H,QAASlmC,MAAO4tC,EAAG1H,WAEpE3M,QAAentB,EAAAA,OAAOwD,SAAS,CACjC9P,MAAO,gBACP0M,WAAY,CACR5E,OAAQ,CACJ,CACIqC,KAAM,UACNC,KAAM,SACNlK,MAAO,UACPJ,QAAS+tC,EACTx9B,UAAU,EACV09B,KAAM,oCAMlBtU,SACMl5B,KAAKitC,iBACPxoC,EACA,IAAMyiC,MAAI0G,aAAa1U,EAAO2M,SAC9B,YAAY3M,EAAO2M,iCAG/B,CAEA,uBAAMgI,CAAkBrpC,EAAOC,GAC3B,MAAMy0B,QAAentB,EAAAA,OAAOwD,SAAS,CACjC9P,MAAO,iBACP0M,WAAY,CACR5E,OAAQ,CACJ,CACIqC,KAAM,WACNC,KAAM,SACNlK,MAAO,WACPyK,MAAO,GACP0F,UAAU,EACV09B,KAAM,8CAMlBtU,SACMl5B,KAAKitC,iBACPxoC,EACA,IAAMyiC,MAAI4G,UAAU5U,EAAO6U,UAC1BprC,GAEU,UADOA,EAASK,KAAK0qC,OAAS,gBAKrD,CAEA,8BAAMM,CAAyBxpC,EAAOC,SACVsH,EAAAA,OAAO+9B,YAAY,CACvCrqC,MAAO,oBACP2W,QAAS,mFACTsD,YAAa,UACbD,aAAc,uBAIRzZ,KAAKitC,iBACPxoC,EACA,IAAMyiC,EAAAA,IAAI+G,iBACTtrC,GAEU,cADOA,EAASK,KAAK0qC,OAAS,iBAKrD,CAEA,6BAAMQ,GACF,MAAMhV,QAAentB,EAAAA,OAAOwD,SAAS,CACjC9P,MAAO,mCACP0M,WAAYgiC,EAAAA,eAAeC,YAG/B,GAAIlV,EACA,IACI,MAAMmV,QAAwBC,EAAAA,UAAUF,UACpClV,EAAOqV,QACP,CAAA,EACArV,EAAOsV,SAGPH,EAAgBtrC,SAChB/C,KAAK4C,SAAS6J,MAAM1J,QAAQ,sBAAsBm2B,EAAOqV,oCACnDvuC,KAAKqsC,eAEXrsC,KAAK4C,SAAS6J,MAAMnJ,MAAM+qC,EAAgBrrC,MAAMM,OAAS,8BAEjE,OAASA,GACLC,QAAQD,MAAM,+BAAgCA,GAC9CtD,KAAK4C,SAAS6J,MAAMnJ,MAAM,+BAAiCA,EAAM8S,QACrE,CAER,CAGA,sBAAM62B,CAAiBxoC,EAASgqC,EAAUC,GACtC,IACIjqC,EAAQM,UAAW,EACnB,MAAMnF,EAAO6E,EAAQG,cAAc,KACnChF,GAAMiF,UAAUC,IAAI,YAEpB,MAAMo0B,QAAeuV,IACrB,GAAIvV,EAAOn2B,SAAWm2B,EAAOl2B,MAAMC,OAAQ,CACvC,MAAMmT,EAAoC,mBAAnBs4B,EACjBA,EAAexV,GACfwV,EACN1uC,KAAK4C,SAAS6J,MAAM1J,QAAQqT,SACtBpW,KAAKqsC,aACf,MACIrsC,KAAK4C,SAAS6J,MAAMnJ,MAAM41B,EAAOl2B,MAAMM,OAAS,mBAExD,OAASA,GACLC,QAAQD,MAAM,qBAAsBA,GACpCtD,KAAK4C,SAAS6J,MAAMnJ,MAAM,UAAYA,EAAM8S,QAChD,CAAA,QACI3R,EAAQM,UAAW,EACnB,MAAMnF,EAAO6E,EAAQG,cAAc,KACnChF,GAAMiF,UAAUiB,OAAO,WAC3B,CACJ,CAEA,aAAM6oC,GAEF3uC,KAAK8oC,kBACT,CAEA,YAAM8F,GACE5uC,KAAKonC,sBACL2B,cAAc/oC,KAAKonC,qBACnBpnC,KAAKonC,oBAAsB,KAEnC,CAGA,sBAAM9gC,GACF,aAAatG,KAAKqsC,aACtB,CAEA,QAAA5lC,GACI,OAAOzG,KAAK8rC,cAAc7rC,OAAS,CAAA,CACvC,CAEA,SAAA4uC,GACI,OAAO7uC,KAAK+rC,eAAe/G,QAAU,CAAA,CACzC,EC/cW,MAAM8J,wBAAwBzvC,EAAAA,KAC3C,WAAAC,CAAYC,EAAU,IACpBC,MAAM,IACDD,EACHQ,UAAW,2BAGbC,KAAK+uC,KAAOxvC,EAAQwvC,MAAQ,KAC5B/uC,KAAKgvC,KAAO,GACZhvC,KAAKgxB,QAAU,IACjB,CAEA,iBAAMrwB,GACJ,MAAO,smNA4KT,CAEA,YAAMC,GACAZ,KAAK+uC,aACD/uC,KAAKivC,wBACLjvC,KAAKkvC,qBACLlvC,KAAKmvC,kBAEf,CAEA,qBAAMF,GACCjvC,KAAK+uC,OAGV/uC,KAAK+uC,KAAK7I,iBAAmBlmC,KAAK+nC,oBAAoB/nC,KAAK+uC,KAAK9rC,QAChEjD,KAAK+uC,KAAK3I,WAAapmC,KAAKgoC,cAAchoC,KAAK+uC,KAAK9rC,QAGhDjD,KAAK+uC,KAAK/rC,MAAkC,iBAAnBhD,KAAK+uC,KAAK/rC,OACrChD,KAAK+uC,KAAKK,cAAgB/Z,KAAKC,UAAUt1B,KAAK+uC,KAAK/rC,KAAM,KAAM,IAI7DhD,KAAK+uC,KAAKM,UACZrvC,KAAK+uC,KAAKO,aAAmC,IAApBtvC,KAAK+uC,KAAKM,QAAiBvrC,KAAKyrC,MAAQ,cAAgB,cAErF,CAEA,mBAAAxH,CAAoB9kC,GASlB,MARgB,CACduhC,QAAW,aACXC,QAAW,aACXC,UAAa,UACbphC,MAAS,YACTksC,UAAa,eACbC,QAAW,cAEExsC,IAAW,cAC5B,CAEA,aAAA+kC,CAAc/kC,GASZ,MARc,CACZuhC,QAAW,eACXC,QAAW,kBACXC,UAAa,kBACbphC,MAAS,eACTksC,UAAa,cACbC,QAAW,YAEAxsC,IAAW,oBAC1B,CAEA,gBAAAysC,CAAiB/V,GAOf,MANgB,CACdgW,MAAS,YACTz2B,KAAQ,UACRjF,QAAW,UACX3Q,MAAS,UAEIq2B,IAAU,WAC3B,CAEA,kBAAMuV,GACJ,GAAKlvC,KAAK+uC,MAAM9iC,GAEhB,IACE,MAAMtJ,QAAiB3C,KAAK4C,SAASC,KAAKC,IAAI,cAAc9C,KAAK+uC,KAAK9iC,WAClEtJ,EAASI,SAAWJ,EAASK,KAAKC,OACpCjD,KAAKgvC,KAAOrsC,EAASK,KAAKA,KAAK4X,IAAIg1B,IAAA,IAC9BA,EACHC,WAAY7vC,KAAK0vC,iBAAiBE,EAAIjW,UAGxC35B,KAAKgvC,KAAO,EAEhB,OAAS1rC,GACPC,QAAQD,MAAM,4BAA6BA,GAC3CtD,KAAKgvC,KAAO,EACd,CACF,CAEA,qBAAMG,GACJ,GAAKnvC,KAAK+uC,MAAM9iC,GAEhB,IACE,MAAMtJ,QAAiB3C,KAAK4C,SAASC,KAAKC,IAAI,cAAc9C,KAAK+uC,KAAK9iC,cAClEtJ,EAASI,SAAWJ,EAASK,KAAKC,SACpCjD,KAAKgxB,QAAUruB,EAASK,KAAKA,KAEjC,OAASM,GACPC,QAAQD,MAAM,+BAAgCA,GAC9CtD,KAAKgxB,QAAU,IACjB,CACF,CAEA,aAAM8e,CAAQf,GACZ/uC,KAAK+uC,KAAOA,QACN/uC,KAAKivC,wBACLjvC,KAAKkvC,qBACLlvC,KAAKmvC,kBAEPnvC,KAAK6kC,mBACD7kC,KAAKqD,QAEf,CAEA,yBAAM0sC,CAAoBlwC,EAAQ2E,EAAOC,GACvC,GAAKzE,KAAK+uC,MAAM9iC,GAEhB,IACExH,EAAQM,UAAW,EACnB,MAAMnF,EAAO6E,EAAQG,cAAc,KAC/BhF,GAAMA,EAAKiF,UAAUC,IAAI,kBAEvB9E,KAAKkvC,qBACLlvC,KAAKqD,QAEb,OAASC,GACPC,QAAQD,MAAM,0BAA2BA,GACzCtD,KAAKi/B,UAAU,2BAA6B37B,EAAM8S,QACpD,CAAA,QACE3R,EAAQM,UAAW,EACnB,MAAMnF,EAAO6E,EAAQG,cAAc,KAC/BhF,GAAMA,EAAKiF,UAAUiB,OAAO,WAClC,CACF,CAGA,iBAAa4P,CAAKq5B,EAAMxvC,EAAU,IAChC,MAAMsW,EAAO,IAAIi5B,gBAAgB,CAAEC,eAC7Bl5B,EAAKjV,SAEX,MAAMoV,EAAU,GA2GhB,MAxGI,CAAC,UAAW,WAAWd,SAAS65B,EAAK9rC,SACvC+S,EAAQgC,KAAK,CACX/B,KAAM,cACNnI,MAAO,qBACPjO,OAAQkS,UACN,GAAIzF,QAAQ,8CACV,IACE,MAAM3J,QAAiBkT,EAAKjT,SAASC,KAAKyQ,KAAK,cAAcy7B,EAAK9iC,aAClE,GAAItJ,EAASI,SAAWJ,EAASK,KAAKC,OAEpC,OADA4S,EAAKm6B,YAAY,+BACV,CAAEnwC,OAAQ,YAAakvC,QAE9Bl5B,EAAKopB,UAAUt8B,EAASK,KAAKM,OAAS,wBAE1C,OAASA,GACPuS,EAAKopB,UAAU,0BAA4B37B,EAAM8S,QACnD,CAEF,OAAO,QAKO,UAAhB24B,EAAK9rC,QACP+S,EAAQgC,KAAK,CACX/B,KAAM,aACNnI,MAAO,sBACPjO,OAAQkS,UACN,IACE,MAAMpP,QAAiBkT,EAAKjT,SAASC,KAAKyQ,KAAK,cAAcy7B,EAAK9iC,YAClE,GAAItJ,EAASI,SAAWJ,EAASK,KAAKC,OAEpC,OADA4S,EAAKm6B,YAAY,yBACV,CAAEnwC,OAAQ,UAAWkvC,QAE5Bl5B,EAAKopB,UAAUt8B,EAASK,KAAKM,OAAS,uBAE1C,OAASA,GACPuS,EAAKopB,UAAU,yBAA2B37B,EAAM8S,QAClD,CACA,OAAO,QAKbJ,EAAQgC,KAAK,CACX/B,KAAM,aACNnI,MAAO,mBACPjO,OAAQkS,UACN,IACE,MAAMpP,QAAiBkT,EAAKjT,SAASC,KAAKyQ,KAAK,cAAcy7B,EAAK9iC,YAClE,GAAItJ,EAASI,SAAWJ,EAASK,KAAKC,OAEpC,OADA4S,EAAKm6B,YAAY,4BACV,CAAEnwC,OAAQ,SAAUowC,aAAclB,EAAMmB,QAASvtC,EAASK,KAAKA,MAEtE6S,EAAKopB,UAAUt8B,EAASK,KAAKM,OAAS,uBAE1C,OAASA,GACPuS,EAAKopB,UAAU,yBAA2B37B,EAAM8S,QAClD,CACA,OAAO,QAIXJ,EAAQgC,KAAK,CACX/B,KAAM,SACNnI,MAAO,wBACPjO,OAAQ,KACN,IACE,MAAMswC,EAAa,CACjBpB,OACAC,KAAMn5B,EAAKm5B,KACXhe,QAASnb,EAAKmb,QACdof,4BAAA,IAAiBtsC,MAAOusC,cACxBC,YAAa,0BAGTC,EAAO,IAAIC,KAAK,CAACnb,KAAKC,UAAU6a,EAAY,KAAM,IAAK,CAC3DtmC,KAAM,qBAGFyP,EAAMm3B,IAAIC,gBAAgBH,GAC1Bte,EAAI9b,SAASmF,cAAc,KASjC,OARA2W,EAAEzW,KAAOlC,EACT2Y,EAAE0e,SAAW,QAAQ5B,EAAK9iC,MAAMnI,KAAKyrC,aACrCp5B,SAASJ,KAAK2F,YAAYuW,GAC1BA,EAAE2e,QACFz6B,SAASJ,KAAK86B,YAAY5e,GAC1Bwe,IAAIK,gBAAgBx3B,GAEpBzD,EAAKm6B,YAAY,mCACV,IACT,OAAS1sC,GAEP,OADAuS,EAAKopB,UAAU,8BACR,IACT,KAIJjpB,EAAQgC,KAAK,CACX/B,KAAM,QACNnI,MAAO,gBACPoI,SAAS,UAGEnK,OAAOqzB,WAAW,CAC7B3/B,MAAO,wDAAwDsvC,EAAK9iC,KACpE8J,KAAMF,EACN1N,KAAM,KACNs4B,YAAY,EACZzqB,aACGzW,GAEP,EC/aa,MAAMwxC,0BAA0B1xC,EAAAA,KAC7C,WAAAC,CAAYC,EAAU,IACpBC,MAAM,IACDD,EACHQ,UAAW,6BAGbC,KAAKmrC,OAAS5rC,EAAQ4rC,QAAU,KAChCnrC,KAAKgxC,aAAe,GACpBhxC,KAAKgvC,KAAO,GACZhvC,KAAKgxB,QAAU,KACfhxB,KAAKyL,OAAS,IAChB,CAEA,iBAAM9K,GACJ,MAAO,gwWA4PT,CAEA,YAAMC,GACAZ,KAAKmrC,eACDnrC,KAAKixC,0BACLjxC,KAAKkxC,0BACLlxC,KAAKmxC,yBACLnxC,KAAKoxC,iBAEf,CAEA,uBAAMH,GACJ,GAAKjxC,KAAKmrC,SAGVnrC,KAAKmrC,OAAOnC,SAAkC,WAAvBhpC,KAAKmrC,OAAOloC,OACnCjD,KAAKmrC,OAAOjF,iBAAmBlmC,KAAKmrC,OAAOnC,SAAW,aAAe,aACrEhpC,KAAKmrC,OAAO/E,WAAapmC,KAAKmrC,OAAOnC,SAAW,uBAAyB,oCAG5C,IAAzBhpC,KAAKmrC,OAAOkG,WACdrxC,KAAKmrC,OAAOmG,YAActxC,KAAKuxC,eAAevxC,KAAKmrC,OAAOkG,UAC1DrxC,KAAKmrC,OAAOqG,aAAexxC,KAAKmrC,OAAOkG,SAAW,IAAM,cAAgB,cAGtErxC,KAAKmrC,OAAOsG,YAAY,CAC1B,MAAMC,EAAgB5tC,KAAKyrC,MAAQ,IAAOvvC,KAAKmrC,OAAOsG,WACtDzxC,KAAKmrC,OAAOwG,WAAa3xC,KAAK4xC,aAAaF,EAC7C,CACF,CAEA,uBAAMR,GACJ,GAAKlxC,KAAKmrC,QAAQ0G,SAElB,IACE,MAAMlvC,QAAiB3C,KAAK4C,SAASC,KAAKC,IAAI,gBAAgB9C,KAAKmrC,OAAO0G,oBAC1E,GAAIlvC,EAASI,SAAWJ,EAASK,KAAKC,OAAQ,CAC5C,MAAMD,EAAOL,EAASK,KAAKA,KAC3BhD,KAAKgxB,QAAU,CACb8gB,cAAe9uC,EAAK8uC,eAAiB,EACrCC,eAAgB/uC,EAAK+uC,gBAAkB,EACvCC,oBAAqBhvC,EAAKgvC,qBAAuB,EACjDC,iBAAkBjvC,EAAKivC,kBAAoB,EAC3CC,WAAYlvC,EAAKkvC,YAAc,EAC/BC,UAAWnvC,EAAKmvC,WAAa,EAC7BC,aAAcpvC,EAAKovC,cAAgB,EACnCC,kBAAmB7f,KAAKC,MAAOzvB,EAAK8uC,cAAgB9xC,KAAKmrC,OAAOmH,YAAe,KAC/EC,iBAAkBvyC,KAAKwyC,oBAAoBxvC,EAAK8uC,cAAgB9xC,KAAKmrC,OAAOmH,aAC5EG,iBAAkBzyC,KAAK0yC,oBAAoB1vC,EAAKivC,kBAAoB,GACpEU,IAAK3yC,KAAK4yC,kBAAkB5vC,EAAK6vC,UAAY,GAC7CC,OAAQ9yC,KAAK+yC,gBAAgB/vC,EAAKgwC,YAAc,EAAGhwC,EAAKiwC,aAAe,KAE3E,CACF,OAAS3vC,GACPC,QAAQD,MAAM,iCAAkCA,GAChDtD,KAAKgxB,QAAUhxB,KAAKkzC,mBACtB,CACF,CAEA,sBAAM/B,GACJ,GAAKnxC,KAAKmrC,QAAQ0G,SAElB,IACE,MAAMlvC,QAAiB3C,KAAK4C,SAASC,KAAKC,IAAI,gBAAgB9C,KAAKmrC,OAAO0G,kBACtElvC,EAASI,SAAWJ,EAASK,KAAKC,OACpCjD,KAAKgxC,aAAeruC,EAASK,KAAKA,KAAK4X,IAAIm0B,IAAA,IACtCA,EACHoE,SAAUnzC,KAAKuxC,eAAeztC,KAAKyrC,MAAQ,IAAOR,EAAKqE,YAGzDpzC,KAAKgxC,aAAe,EAExB,OAAS1tC,GACPC,QAAQD,MAAM,gCAAiCA,GAC/CtD,KAAKgxC,aAAe,EACtB,CACF,CAEA,oBAAMI,GACJ,GAAKpxC,KAAKmrC,QAAQ0G,SAElB,IACE,MAAMlvC,QAAiB3C,KAAK4C,SAASC,KAAKC,IAAI,gBAAgB9C,KAAKmrC,OAAO0G,0BACtElvC,EAASI,SAAWJ,EAASK,KAAKC,OACpCjD,KAAKgvC,KAAOrsC,EAASK,KAAKA,KAAK4X,IAAIg1B,IAAA,IAC9BA,EACHC,WAAY7vC,KAAK0vC,iBAAiBE,EAAIjW,UAGxC35B,KAAKgvC,KAAO,EAEhB,OAAS1rC,GACPC,QAAQD,MAAM,8BAA+BA,GAC7CtD,KAAKgvC,KAAO,EACd,CACF,CAEA,iBAAAkE,GACE,MAAO,CACLpB,cAAe,EACfC,eAAgB,EAChBC,oBAAqB,EACrBC,iBAAkB,EAClBC,WAAY,EACZC,UAAW,EACXC,aAAc,EACdC,kBAAmB,EACnBE,iBAAkB,eAClBE,iBAAkB,CAAE3kC,MAAO,aAAclO,KAAM,UAAWqW,KAAM,WAChE08B,IAAK,CAAEU,WAAY,EAAGvlC,MAAO,gBAC7BglC,OAAQ,CAAEQ,KAAM,EAAG7hB,MAAO,EAAG4hB,WAAY,EAAGvlC,MAAO,gBAEvD,CAEA,mBAAA0kC,CAAoBe,GAClB,OAAIA,EAAc,GAAY,YAC1BA,EAAc,GAAY,aAC1BA,EAAc,GAAY,UACvB,YACT,CAEA,mBAAAb,CAAoBc,GAClB,OAAIA,EAAU,IACL,CAAE1lC,MAAO,eAAgBlO,KAAM,cAAeqW,KAAM,aAClDu9B,EAAU,IACZ,CAAE1lC,MAAO,eAAgBlO,KAAM,iBAAkBqW,KAAM,QAEvD,CAAEnI,MAAO,cAAelO,KAAM,gBAAiBqW,KAAM,OAEhE,CAEA,iBAAA28B,CAAkBa,GAChB,MAAMJ,EAAa7gB,KAAKC,MAAMghB,GAC9B,IAAIC,EAAW,aAMf,OAJIL,EAAa,GAAIK,EAAW,YACvBL,EAAa,GAAIK,EAAW,aAC5BL,EAAa,KAAIK,EAAW,WAE9B,CAAEL,aAAYvlC,MAAO4lC,EAC9B,CAEA,eAAAX,CAAgBO,EAAM7hB,GACpB,MAAM4hB,EAAa7gB,KAAKC,MAAO6gB,EAAO7hB,EAAS,KAC/C,MAAO,CACL6hB,KAAM9gB,KAAKC,MAAM6gB,GACjB7hB,MAAOe,KAAKC,MAAMhB,GAClB4hB,aACAvlC,MAAO9N,KAAK4yC,kBAAkBS,GAAYvlC,MAE9C,CAEA,gBAAA4hC,CAAiB/V,GAOf,MANgB,CACdgW,MAAS,YACTz2B,KAAQ,UACRjF,QAAW,UACX3Q,MAAS,UAEIq2B,IAAU,WAC3B,CAEA,YAAAiY,CAAa+B,GACX,MAAMC,EAAOphB,KAAKyY,MAAM0I,EAAU,OAC5BE,EAAQrhB,KAAKyY,MAAO0I,EAAU,MAAS,MACvCG,EAAUthB,KAAKyY,MAAO0I,EAAU,KAAQ,IAE9C,OAAIC,EAAO,EAAU,GAAGA,MAASC,MAAUC,KACvCD,EAAQ,EAAU,GAAGA,MAAUC,KAC5B,GAAGA,IACZ,CAEA,cAAAvC,CAAeoC,GACb,OAAIA,EAAU,GAAW,GAAGnhB,KAAKC,MAAMkhB,MACnCA,EAAU,KAAa,GAAGnhB,KAAKC,MAAMkhB,EAAU,OAC/CA,EAAU,MAAc,GAAGnhB,KAAKC,MAAMkhB,EAAU,SAC7C,GAAGnhB,KAAKC,MAAMkhB,EAAU,SACjC,CAEA,eAAMI,CAAU5I,GACdnrC,KAAKmrC,OAASA,QACRnrC,KAAKixC,0BACLjxC,KAAKkxC,0BACLlxC,KAAKmxC,yBACLnxC,KAAKoxC,iBAEPpxC,KAAK6kC,mBACD7kC,KAAKqD,QAEf,CAGA,4BAAM2wC,CAAuBn0C,EAAQ2E,EAAOC,SACpCzE,KAAKkxC,0BACLlxC,KAAKqD,QACb,CAEA,0BAAM4wC,CAAqBp0C,EAAQ2E,EAAOC,SAClCzE,KAAKmxC,yBACLnxC,KAAKqD,QACb,CAEA,yBAAM0sC,CAAoBlwC,EAAQ2E,EAAOC,SACjCzE,KAAKoxC,uBACLpxC,KAAKqD,QACb,CAEA,wBAAM6wC,CAAmBr0C,EAAQ2E,EAAOC,GACtC,MAAMk1B,EAAQl1B,EAAQmI,aAAa,cAChB5M,KAAKyE,QAAQ4T,iBAAiB,cAEtCC,QAAQga,IACH,QAAVqH,GAAmBrH,EAAM1lB,aAAa,gBAAkB+sB,EAC1DrH,EAAM1iB,MAAM2O,QAAU,QAEtB+T,EAAM1iB,MAAM2O,QAAU,SAK1Bve,KAAKyE,QAAQ4T,iBAAiB,+BAA+BC,QAAQ67B,IACnEA,EAAItvC,UAAUiB,OAAO,YAEvBrB,EAAQI,UAAUC,IAAI,SACxB,CAEA,sBAAMsvC,CAAiBv0C,EAAQ2E,EAAOC,GACpC,MAAM4vC,EAAS5vC,EAAQmI,aAAa,gBACpC5M,KAAKwF,KAAK,YAAa,CAAE6uC,SAAQlJ,OAAQnrC,KAAKmrC,QAChD,CAEA,wBAAMmJ,CAAmBz0C,EAAQ2E,EAAOC,GACtC,MAAM4vC,EAAS5vC,EAAQmI,aAAa,gBAEpC,GAAKN,QAAQ,8CAIb,IACE,MAAM3J,QAAiB3C,KAAK4C,SAASC,KAAKyQ,KAAK,cAAc+gC,YAEzD1xC,EAASI,SAAWJ,EAASK,KAAKC,QACpCjD,KAAKgwC,YAAY,qCACXhwC,KAAKmxC,yBACLnxC,KAAKqD,SACXrD,KAAKwF,KAAK,iBAAkB,CAAE6uC,SAAQlJ,OAAQnrC,KAAKmrC,UAEnDnrC,KAAKi/B,UAAUt8B,EAASK,KAAKM,OAAS,wBAE1C,OAASA,GACPC,QAAQD,MAAM,yBAA0BA,GACxCtD,KAAKi/B,UAAU,0BAA4B37B,EAAM8S,QACnD,CACF,CAGA,iBAAaV,CAAKy1B,EAAQ5rC,EAAU,IAClC,MAAMsW,EAAO,IAAIk7B,kBAAkB,CAAE5F,iBAC/Bt1B,EAAKjV,SAEX,MAAMoV,EAAU,GAwHhB,MArHsB,WAAlBm1B,EAAOloC,OACT+S,EAAQgC,KAAK,CACX/B,KAAM,eACNnI,MAAO,cACPjO,OAAQkS,UACN,GAAIzF,QAAQ,0CAA0C6+B,EAAO0G,cAC3D,IACE,MAAMlvC,QAAiBkT,EAAKjT,SAASC,KAAKyQ,KAAK,gBAAgB63B,EAAO0G,kBACtE,GAAIlvC,EAASI,SAAWJ,EAASK,KAAKC,OAEpC,OADA4S,EAAKm6B,YAAY,8BACV,CAAEnwC,OAAQ,SAAUsrC,UAE3Bt1B,EAAKopB,UAAUt8B,EAASK,KAAKM,OAAS,yBAE1C,OAASA,GACPuS,EAAKopB,UAAU,2BAA6B37B,EAAM8S,QACpD,CAEF,OAAO,QAIXJ,EAAQgC,KAAK,CACX/B,KAAM,iBACNnI,MAAO,cACPjO,OAAQkS,UACN,GAAIzF,QAAQ,4CAA4C6+B,EAAO0G,cAC7D,IACE,MAAMlvC,QAAiBkT,EAAKjT,SAASC,KAAKyQ,KAAK,gBAAgB63B,EAAO0G,oBACtE,GAAIlvC,EAASI,SAAWJ,EAASK,KAAKC,OAEpC,OADA4S,EAAKm6B,YAAY,4BACV,CAAEnwC,OAAQ,YAAasrC,UAE9Bt1B,EAAKopB,UAAUt8B,EAASK,KAAKM,OAAS,2BAE1C,OAASA,GACPuS,EAAKopB,UAAU,6BAA+B37B,EAAM8S,QACtD,CAEF,OAAO,QAKbJ,EAAQgC,KAAK,CACX/B,KAAM,YACNnI,MAAO,sBACPjO,OAAQ,KACNgW,EAAKrQ,KAAK,mBAAoB,CAAE2lC,WACzB,QAIXn1B,EAAQgC,KAAK,CACX/B,KAAM,gBACNnI,MAAO,qBACPjO,OAAQkS,UACN,MAAMwiC,EAAiB,2CAA2CpJ,EAAO0G,2CACzE,GAAIvlC,QAAQioC,GACV,IACE,MAAM5xC,QAAiBkT,EAAKjT,SAASC,KAAK2xC,OAAO,gBAAgBrJ,EAAO0G,YACxE,GAAIlvC,EAASI,SAAWJ,EAASK,KAAKC,OAEpC,OADA4S,EAAKm6B,YAAY,+BACV,CAAEnwC,OAAQ,UAAWsrC,UAE5Bt1B,EAAKopB,UAAUt8B,EAASK,KAAKM,OAAS,0BAE1C,OAASA,GACPuS,EAAKopB,UAAU,4BAA8B37B,EAAM8S,QACrD,CAEF,OAAO,QAIXJ,EAAQgC,KAAK,CACX/B,KAAM,SACNnI,MAAO,wBACPjO,OAAQ,KACN,IACE,MAAMswC,EAAa,CACjBhF,SACAna,QAASnb,EAAKmb,QACdggB,aAAcn7B,EAAKm7B,aACnBhC,KAAMn5B,EAAKm5B,KACXoB,4BAAA,IAAiBtsC,MAAOusC,cACxBC,YAAa,0BAGTC,EAAO,IAAIC,KAAK,CAACnb,KAAKC,UAAU6a,EAAY,KAAM,IAAK,CAC3DtmC,KAAM,qBAGFyP,EAAMm3B,IAAIC,gBAAgBH,GAC1Bte,EAAI9b,SAASmF,cAAc,KASjC,OARA2W,EAAEzW,KAAOlC,EACT2Y,EAAE0e,SAAW,UAAUxF,EAAO0G,YAAY/tC,KAAKyrC,aAC/Cp5B,SAASJ,KAAK2F,YAAYuW,GAC1BA,EAAE2e,QACFz6B,SAASJ,KAAK86B,YAAY5e,GAC1Bwe,IAAIK,gBAAgBx3B,GAEpBzD,EAAKm6B,YAAY,qCACV,IACT,OAAS1sC,GAEP,OADAuS,EAAKopB,UAAU,gCACR,IACT,KAIJjpB,EAAQgC,KAAK,CACX/B,KAAM,QACNnI,MAAO,gBACPoI,SAAS,UAGEnK,EAAAA,OAAOqzB,WAAW,CAC7B3/B,MAAO,kDAAkD0rC,EAAO0G,WAChE97B,KAAMF,EACN1N,KAAM,KACNs4B,YAAY,EACZzqB,aACGzW,GAEP,ECxoBF,MAAMk1C,sBAAsBp1C,EAAAA,KAC1B,WAAAC,CAAYC,EAAU,IACpBC,MAAM,IACDD,EACHQ,UAAW,4BAGbC,KAAKC,MAAQ,CACXukC,QAAS,EACTC,QAAS,EACTC,UAAW,EACXgQ,OAAQ,EAEZ,CAEA,iBAAM/zC,GACJ,MAAO,4xGAiFT,CAEA,eAAM6C,GACJ,IACE,MAAMb,QAAiB3C,KAAK4C,SAASC,KAAKC,IAAI,qBAC1CH,EAASI,SAAWJ,EAASK,KAAKC,SACpCjD,KAAKC,MAAQ0C,EAASK,KAAKA,KAE/B,OAASM,GACPC,QAAQD,MAAM,6BAA8BA,EAC9C,CACF,CAEA,YAAM1C,SACEZ,KAAKwD,WACb,EAIF,MAAMmxC,wBAAwBt1C,EAAAA,KAC5B,WAAAC,CAAYC,EAAU,IACpBC,MAAM,IACDD,EACHQ,UAAW,8BAGbC,KAAKilC,QAAU,EACjB,CAEA,iBAAMtkC,GACJ,MAAO,igJAsFT,CAEA,iBAAMi0C,GACJ,IACE,MAAMjyC,QAAiB3C,KAAK4C,SAASC,KAAKC,IAAI,sBAC1CH,EAASI,SAAWJ,EAASK,KAAKC,SACpCjD,KAAKilC,QAAUtiC,EAASK,KAAKA,KAAK4X,IAAIuwB,IACpC,MAAMnC,EAA6B,WAAlBmC,EAAOloC,OAClB4xC,EAAU1J,EAAOkG,UAAY,EAEnC,MAAO,IACFlG,EACHnC,WACA8L,YAAa9L,EAAW,aAAe,aACvC5C,WAAY4C,EAAW,uBAAyB,+BAChDsI,YAAatxC,KAAK+0C,cAAcF,MAIxC,OAASvxC,GACPC,QAAQD,MAAM,0BAA2BA,EAC3C,CACF,CAEA,aAAAyxC,CAAcpB,GACZ,OAAIA,EAAU,GAAW,GAAGnhB,KAAKC,MAAMkhB,UACnCA,EAAU,KAAa,GAAGnhB,KAAKC,MAAMkhB,EAAU,WAC5C,GAAGnhB,KAAKC,MAAMkhB,EAAU,YACjC,CAEA,YAAM/yC,SACEZ,KAAK40C,aACb,CAEA,4BAAMI,CAAuBxwC,EAAOC,SAC5BzE,KAAK40C,aACb,CAEA,+BAAMK,CAA0BzwC,EAAOC,GACrC,MAAM4mC,EAAW5mC,EAAQmI,aAAa,kBAChCu+B,EAASnrC,KAAKilC,QAAQ7L,KAAK8b,GAAKA,EAAEjpC,KAAOo/B,GAE/C,GAAIF,EAAQ,CACV,MAAMjS,QAAe6X,kBAAkBr7B,KAAKy1B,GAGxCjS,GAAQr5B,eACJG,KAAK40C,cACX50C,KAAKwF,KAAK,UAAY0zB,EAAOr5B,OAAQq5B,GAEzC,CACF,CAEA,yBAAMic,CAAoB3wC,EAAOC,GAC/B,MAAM4mC,EAAW5mC,EAAQmI,aAAa,kBAChCu+B,EAASnrC,KAAKilC,QAAQ7L,KAAK8b,GAAKA,EAAEjpC,KAAOo/B,GAE/C,GAAKF,GAAW7+B,QAAQ,0CAA0C6+B,EAAO0G,cAIzE,IACEptC,EAAQM,UAAW,EACnB,MAAMpC,QAAiB3C,KAAK4C,SAASC,KAAKyQ,KAAK,gBAAgB+3B,WAE3D1oC,EAASI,SAAWJ,EAASK,KAAKC,QACpCjD,KAAKgwC,YAAY,oCACXhwC,KAAK40C,eAEX50C,KAAKi/B,UAAUt8B,EAASK,KAAKM,OAAS,yBAE1C,OAASA,GACPC,QAAQD,MAAM,0BAA2BA,GACzCtD,KAAKi/B,UAAU,2BAA6B37B,EAAM8S,QACpD,CAAA,QACE3R,EAAQM,UAAW,CACrB,CACF,CAEA,2BAAMqwC,CAAsB5wC,EAAOC,GACjC,MAAM4mC,EAAW5mC,EAAQmI,aAAa,kBAChCu+B,EAASnrC,KAAKilC,QAAQ7L,KAAK8b,GAAKA,EAAEjpC,KAAOo/B,GAE/C,GAAKF,GAAW7+B,QAAQ,4CAA4C6+B,EAAO0G,cAI3E,IACEptC,EAAQM,UAAW,EACnB,MAAMpC,QAAiB3C,KAAK4C,SAASC,KAAKyQ,KAAK,gBAAgB+3B,aAE3D1oC,EAASI,SAAWJ,EAASK,KAAKC,QACpCjD,KAAKgwC,YAAY,kCACXhwC,KAAK40C,eAEX50C,KAAKi/B,UAAUt8B,EAASK,KAAKM,OAAS,2BAE1C,OAASA,GACPC,QAAQD,MAAM,4BAA6BA,GAC3CtD,KAAKi/B,UAAU,6BAA+B37B,EAAM8S,QACtD,CAAA,QACE3R,EAAQM,UAAW,CACrB,CACF,CAEA,0BAAMswC,CAAqB7wC,EAAOC,GAChC,MAAM4mC,EAAW5mC,EAAQmI,aAAa,kBAChCu+B,EAASnrC,KAAKilC,QAAQ7L,KAAK8b,GAAKA,EAAEjpC,KAAOo/B,GAE/C,IAAKF,EAAQ,OAEb,MAAMoJ,EAAiB,2CAA2CpJ,EAAO0G,2CACzE,GAAKvlC,QAAQioC,GAIb,IACE9vC,EAAQM,UAAW,EACnB,MAAMpC,QAAiB3C,KAAK4C,SAASC,KAAK2xC,OAAO,gBAAgBnJ,KAE7D1oC,EAASI,SAAWJ,EAASK,KAAKC,QACpCjD,KAAKgwC,YAAY,qCACXhwC,KAAK40C,eAEX50C,KAAKi/B,UAAUt8B,EAASK,KAAKM,OAAS,0BAE1C,OAASA,GACPC,QAAQD,MAAM,2BAA4BA,GAC1CtD,KAAKi/B,UAAU,4BAA8B37B,EAAM8S,QACrD,CAAA,QACE3R,EAAQM,UAAW,CACrB,CACF,EAIF,MAAMuwC,uBAAuBj2C,EAAAA,KAC3B,WAAAC,CAAYC,EAAU,IACpBC,MAAM,IACDD,EACHQ,UAAW,4BAEf,CAEA,iBAAMY,GACJ,MAAO,slBAkBT,CAEA,YAAMC,GAEJZ,KAAKu1C,cAAgB,IAAIrxC,eAAa,CACpCzE,MAAO,+CACP8C,SAAU,qBACVb,OAAQ,IACRR,YAAa,QACbG,MAAO,CAAC,YAAa,mBACrBC,QAAS,SACTC,UAAW,OACXW,eAAe,EACfmyB,OAAQ,CACN,0BACA,0BAEFlwB,MAAO,CACLxE,MAAO,QACPyE,aAAa,GAEfC,QAAS,CACPC,EAAG,UAELnC,YAAa,oBAEfnC,KAAKoC,SAASpC,KAAKu1C,eAGnBv1C,KAAKw1C,gBAAkB,IAAItxC,eAAa,CACtCzE,MAAO,6DACP8C,SAAU,qBACVb,OAAQ,IACRR,YAAa,QACbG,MAAO,CAAC,eAAgB,iBACxBC,QAAS,SACTC,UAAW,OACXW,eAAe,EACfmyB,OAAQ,CACN,yBACA,0BAEFlwB,MAAO,CACLxE,MAAO,QACPyE,aAAa,GAEfC,QAAS,CACPC,EAAG,UAELnC,YAAa,sBAEfnC,KAAKoC,SAASpC,KAAKw1C,gBACrB,EA8JF,MAAMC,0BAA0BrtC,EAAAA,UAC9B,WAAA9I,CAAYC,EAAU,IACpBC,MAAM,IACDD,EACHE,MAAO,gBACP4I,WAAY,IAAIoF,EAAAA,WAAW,CACzBlL,SAAU,uBAEZgG,QAAS,CACP,CAAEC,IAAK,KAAM7I,MAAO,UAAW+I,UAAU,GACzC,CAAEF,IAAK,WAAY7I,MAAO,WAAY+I,UAAU,GAChD,CAAEF,IAAK,UAAW7I,MAAO,UAAW+I,UAAU,GAC9C,CAAEF,IAAK,UAAW7I,MAAO,UAAW+I,UAAU,EAAMD,UAAW,YAC/D,CAAED,IAAK,SAAU7I,MAAO,SAAU8I,UAAW,WAGnD,EAGF,MAAMitC,0BAA0BttC,EAAAA,UAC9B,WAAA9I,CAAYC,EAAU,IACpBC,MAAM,IACDD,EACHE,MAAO,gBACP4I,WAAY,IAAIoF,EAAAA,WAAW,CACzBlL,SAAU,uBAEZgG,QAAS,CACP,CAAEC,IAAK,KAAM7I,MAAO,UAAW+I,UAAU,GACzC,CAAEF,IAAK,WAAY7I,MAAO,WAAY+I,UAAU,GAChD,CAAEF,IAAK,UAAW7I,MAAO,UAAW+I,UAAU,GAC9C,CAAEF,IAAK,UAAW7I,MAAO,UAAW+I,UAAU,EAAMD,UAAW,YAC/D,CAAED,IAAK,SAAU7I,MAAO,SAAU8I,UAAW,WAGnD,EAGF,MAAMktC,4BAA4BvtC,EAAAA,UAChC,WAAA9I,CAAYC,EAAU,IACpBC,MAAM,IACDD,EACHE,MAAO,kBACP4I,WAAY,IAAIoF,EAAAA,WAAW,CACzBlL,SAAU,yBAEZgG,QAAS,CACP,CAAEC,IAAK,KAAM7I,MAAO,UAAW+I,UAAU,GACzC,CAAEF,IAAK,WAAY7I,MAAO,WAAY+I,UAAU,GAChD,CAAEF,IAAK,UAAW7I,MAAO,UAAW+I,UAAU,GAC9C,CAAEF,IAAK,UAAW7I,MAAO,UAAW+I,UAAU,EAAMD,UAAW,YAC/D,CAAED,IAAK,eAAgB7I,MAAO,YAAa+I,UAAU,EAAMD,UAAW,YACtE,CAAED,IAAK,SAAU7I,MAAO,SAAU8I,UAAW,WAGnD,EAGF,MAAMmtC,wBAAwBxtC,EAAAA,UAC5B,WAAA9I,CAAYC,EAAU,IACpBC,MAAM,IACDD,EACHE,MAAO,eACP4I,WAAY,IAAIoF,EAAAA,WAAW,CACzBlL,SAAU,sBAEZgG,QAAS,CACP,CAAEC,IAAK,KAAM7I,MAAO,UAAW+I,UAAU,GACzC,CAAEF,IAAK,WAAY7I,MAAO,WAAY+I,UAAU,GAChD,CAAEF,IAAK,UAAW7I,MAAO,UAAW+I,UAAU,GAC9C,CAAEF,IAAK,UAAW7I,MAAO,UAAW+I,UAAU,EAAMD,UAAW,YAC/D,CAAED,IAAK,QAAS7I,MAAO,QAAS+I,UAAU,GAC1C,CAAEF,IAAK,SAAU7I,MAAO,SAAU8I,UAAW,WAGnD,EAIa,MAAMotC,2BAA2BnyC,EAAAA,KAC9C,WAAApE,CAAYC,EAAU,IACpBC,MAAM,IACDD,EACHE,MAAO,kBACPM,UAAW,8BAGbC,KAAK2D,UAAY,kBACjB3D,KAAK4D,aAAe,8CACpB5D,KAAK6D,4BAAA,IAAkBC,MAAOC,gBAChC,CAEA,iBAAMpD,GACJ,MAAO,isFAsET,CAEA,YAAMC,GAEJZ,KAAK81C,cAAgB,IAAIrB,cAAc,CACrCtyC,YAAa,eAEfnC,KAAKoC,SAASpC,KAAK81C,eAGnB91C,KAAK+1C,gBAAkB,IAAIpB,gBAAgB,CACzCxyC,YAAa,iBAEfnC,KAAKoC,SAASpC,KAAK+1C,iBAGnB/1C,KAAKg2C,eAAiB,IAAIV,eAAe,CACvCnzC,YAAa,gBAEfnC,KAAKoC,SAASpC,KAAKg2C,gBAGnBh2C,KAAKi2C,eAAiB,IAAIzrC,UAAQ,CAChCrI,YAAa,cACbsI,KAAM,CACJyrC,QAAW,IAAIT,kBACfU,QAAW,IAAIT,kBACfU,UAAa,IAAIT,oBACjBU,OAAU,IAAIT,iBAEhB1qC,UAAW,YAEblL,KAAKoC,SAASpC,KAAKi2C,eACrB,CAEA,wBAAM1xC,CAAmB1E,EAAQ2E,EAAOC,GACtC,IAEE,MAAM7E,EAAO6E,EAAQG,cAAc,KACnChF,GAAMiF,UAAUC,IAAI,WACpBL,EAAQM,UAAW,EAGnB,MAAMC,EAAW,CACfhF,KAAK81C,eAAetyC,YACpBxD,KAAK+1C,iBAAiBnB,cACtB50C,KAAKg2C,gBAAgBT,eAAetwC,UACpCjF,KAAKg2C,gBAAgBR,iBAAiBvwC,WACtCC,OAAOC,SAGH+F,EAAYlL,KAAKi2C,gBAAgB/oC,eACvC,GAAIhC,EAAW,CACb,MAAMohC,EAActsC,KAAKi2C,eAAe1J,OAAOrhC,GAC3CohC,GAAarnC,SACfD,EAASgT,KAAKs0B,EAAYrnC,UAE9B,OAEMG,QAAQC,WAAWL,GAGzBhF,KAAK6D,4BAAA,IAAkBC,MAAOC,iBAG9B,MAAMuB,EAAWtF,KAAK4C,UAAU2C,OAC5BD,GACFA,EAASE,KAAK,4BAA6B,CACzCC,KAAMzF,KACN0F,UAAW1F,KAAK6D,aAItB,OAASP,GACPC,QAAQD,MAAM,oCAAqCA,EACrD,CAAA,QAEE,MAAM1D,EAAO6E,EAAQG,cAAc,KACnChF,GAAMiF,UAAUiB,OAAO,WACvBrB,EAAQM,UAAW,CACrB,CACF,CAEA,yBAAMuxC,CAAoBz2C,EAAQ2E,EAAOC,GACvC,UAEQzE,KAAKg2C,gBAAgBT,eAAevvC,OAAO,cAC3ChG,KAAKg2C,gBAAgBR,iBAAiBxvC,OAAO,QAGnD,MAAMkF,EAAYlL,KAAKi2C,gBAAgB/oC,eACvC,GAAIhC,EAAW,CACb,MAAMohC,EAActsC,KAAKi2C,eAAe1J,OAAOrhC,GAC3CohC,GAAaiK,aACfjK,EAAYiK,aAEhB,CAEF,OAASjzC,GACPC,QAAQD,MAAM,8BAA+BA,EAC/C,CACF,CAEA,4BAAMkzC,CAAuBhyC,EAAOC,GAClC,IAEE,MAAM9B,QAAiB3C,KAAK4C,SAASC,KAAKC,IAAI,uBAE9C,GAAIH,EAASI,SAAWJ,EAASK,KAAKC,OAAQ,CAC5C,MAGMwzC,EAHW9zC,EAASK,KAAKA,KAGF4X,IAAIirB,GAC/B,GAAGA,EAAQj8B,SAASi8B,EAAQrB,oBAAoBqB,EAAQpB,oBACxDjtB,KAAK,MAEP7R,MAAM,qBAAqB8wC,sDAC7B,MACEz2C,KAAKi/B,UAAU,qCAEnB,OAAS37B,GACPC,QAAQD,MAAM,2BAA4BA,GAE1C,MAAM6C,EAASnG,KAAK4C,UAAUuD,OAC1BA,GACFA,EAAOC,WAAW,uBAEtB,CACF,CAEA,4BAAMswC,CAAuBlyC,EAAOC,GAClC,IAEE,MAAM9B,QAAiB3C,KAAK4C,SAASC,KAAKC,IAAI,4BAE9C,GAAIH,EAASI,SAAWJ,EAASK,KAAKC,OAAQ,CAC5C,MAAM+rC,EAAOrsC,EAASK,KAAKA,KAGrB2zC,EAAa3H,EAAK5c,MAAM,EAAG,IAAIxX,OACnC,IAAI,IAAI9W,KAAqB,IAAhB8rC,EAAIlqC,WAAkB3B,qBAAqB6rC,EAAIjW,MAAM9H,kBAAkB+d,EAAIx5B,WACxFoB,KAAK,MAIP,GAFqBlL,QAAQ,+BAA+BqqC,gBAAyB3H,EAAK7xB,OAAS,uCAEjF,CAChB,MAAMhX,EAASnG,KAAK4C,UAAUuD,OAC1BA,GACFA,EAAOC,WAAW,2BAEtB,CACF,KAAO,CAEL,MAAMD,EAASnG,KAAK4C,UAAUuD,OAC1BA,GACFA,EAAOC,WAAW,cAEtB,CACF,OAAS9C,GACPC,QAAQD,MAAM,8BAA+BA,GAE7C,MAAM6C,EAASnG,KAAK4C,UAAUuD,OAC1BA,GACFA,EAAOC,WAAW,cAEtB,CACF,CAGA,sBAAME,GACJ,OAAOtG,KAAKuE,mBAAmB,KAAM,KAAM,CAAEQ,UAAU,EAAOH,cAAe,IAAM,MACrF,CAEA,QAAA6B,GACE,OAAOzG,KAAK81C,eAAe71C,OAAS,CAAA,CACtC,CAEA,UAAA22C,GACE,OAAO52C,KAAK+1C,iBAAiB9Q,SAAW,EAC1C,CAEA,SAAA1+B,GACE,MAAO,CACLswC,SAAU72C,KAAKg2C,gBAAgBT,cAC/BuB,WAAY92C,KAAKg2C,gBAAgBR,gBAErC,ECz6BF,MAAMuB,gBAAgB13C,EAAAA,KAClB,WAAAC,CAAYC,EAAU,IAClBC,MAAM,CACFO,UAAW,cACRR,IAGPS,KAAK4G,MAAQrH,EAAQqH,OAAS,IAAIowC,EAAAA,IAAIz3C,EAAQyD,MAAQ,IACtDhD,KAAKi3C,QAAUj3C,KAAKk3C,cAAcl3C,KAAK4G,MAAMsB,IAAI,UAEjDlI,KAAKmH,SAAW,whCAwBpB,CAEA,aAAA+vC,CAAcvd,GACV,MAAMwd,EAAMxd,GAAO7kB,cACnB,MAAY,UAARqiC,GAA2B,aAARA,EAA2B,CAAEv3C,KAAM,oBAAqBgC,MAAO,eAC1E,YAARu1C,EAA0B,CAAEv3C,KAAM,+BAAgCgC,MAAO,gBACjE,SAARu1C,EAAuB,CAAEv3C,KAAM,sBAAuBgC,MAAO,aAC1D,CAAEhC,KAAM,kBAAmBgC,MAAO,iBAC7C,CAEA,YAAMhB,GAEFZ,KAAK0Q,aAAe,IAAIrJ,UAAS,CAC7BT,MAAO5G,KAAK4G,MACZ7G,UAAW,MACXwI,QAAS,EACThB,OAAQ,CACJ,CAAEqC,KAAM,KAAMjK,MAAO,UACrB,CAAEiK,KAAM,QAASjK,MAAO,QAASsK,OAAQ,SACzC,CAAEL,KAAM,OAAQjK,MAAO,QACvB,CAAEiK,KAAM,KAAMjK,MAAO,aAAcwH,SAAU,sDAC7C,CAAEyC,KAAM,MAAOjK,MAAO,WACtB,CAAEiK,KAAM,WAAYjK,MAAO,YAC3B,CAAEiK,KAAM,OAAQjK,MAAO,YAAawH,SAAU,gFAC9C,CAAEyC,KAAM,aAAcjK,MAAO,iBAC7B,CAAEiK,KAAM,WAAYjK,MAAO,uBAKnC,MAAMy3C,EAAap3C,KAAK4G,MAAMsB,IAAI,OAClC,IAAImvC,EAAeD,EACnB,IAEI,MAAME,EAASjiB,KAAKiV,MAAM8M,GAC1BC,EAAehiB,KAAKC,UAAUgiB,EAAQ,KAAM,EAChD,OAAShV,GAET,CAEAtiC,KAAKu3C,eAAiB,IAAIl4C,OAAK,CAC3B8H,SAAU,yYAK4FkwC,uDAGtGG,gBAAiB,KACbC,UAAUC,UAAUC,UAAUN,GAC9Br3C,KAAK4C,UAAU6J,OAAO1J,QAAQ,uCAKtC/C,KAAK8G,QAAU,IAAI0D,UAAQ,CACvBrI,YAAa,WACbsI,KAAM,CACFkG,SAAY3Q,KAAK0Q,aACjB,cAAe1Q,KAAKu3C,gBAExBrsC,UAAW,aAEflL,KAAKoC,SAASpC,KAAK8G,SAGnB,MAAM8wC,EAAU,IAAIrsC,cAAY,CAC5BpJ,YAAa,mBACbpC,UAAW,yCACXyL,QAASxL,KAAK4G,MACd6E,OAAQ,CACJ7L,KAAM,yBACN8L,MAAO,CACH,CAAE/L,MAAO,YAAaE,OAAQ,YAAaD,KAAM,YAAamF,UAAW/E,KAAK4G,MAAMsB,IAAI,QACxF,CAAEvI,MAAO,cAAeE,OAAQ,cAAeD,KAAM,WAAYmF,UAAW/E,KAAK4G,MAAMsB,IAAI,SAC3F,CAAE2B,KAAM,WACR,CAAElK,MAAO,aAAcE,OAAQ,aAAcD,KAAM,WAAYiR,QAAQ,OAInF7Q,KAAKoC,SAASw1C,EAClB,CAEA,oBAAMC,CAAerzC,GACjBA,EAAMyK,iBACN,MAAMgI,EAAKjX,KAAK4G,MAAMsB,IAAI,MACtB+O,GACAV,UAAUb,KAAKuB,EAEvB,CAEA,wBAAM6gC,CAAmBtzC,GACrBA,EAAMyK,iBACN,MAAM0G,EAAO3V,KAAK4G,MAAMsB,IAAI,QACxByN,GACApB,WAAWmB,KAAKC,EAExB,CAEA,sBAAM3E,GAC6BhR,KAAK4G,MAAMsB,IAAI,MAClD,CAEA,uBAAM6vC,SACsBhsC,EAAAA,OAAOO,QAC3B,gFACA,mBACA,CAAEmN,aAAc,aAAcC,YAAa,mBAGxB1Z,KAAK4G,MAAMsK,WACrBnO,SACL/C,KAAKwF,KAAK,cAAe,CAAEoB,MAAO5G,KAAK4G,OAGnD,EAGJowC,EAAAA,IAAI3pC,WAAa0pC,QC7JjB,MAAMiB,qBAAqBzqC,EAAAA,UACvB,WAAAjO,CAAYC,EAAU,IAClBC,MAAM,IACCD,EACHqK,KAAM,aACN4D,SAAU,cACVrH,OAAQ,aACRsH,WAAY/D,EAAAA,QAEZ2H,cAAe0lC,QACfppC,kBAAmB,CACfvK,QAAQ,EACR+E,KAAM,MAIVI,QAAS,CACL,CACIC,IAAK,yBACL7I,MAAO,YACP+I,UAAU,EACVxD,OAAQ,CACJ2E,KAAM,cAGd,CACIrB,IAAK,QACL7I,MAAO,QACP+I,UAAU,EACVD,UAAW,QACXvD,OAAQ,CACJ2E,KAAM,SACNtK,QAAS,CACL,CAAE6K,MAAO,OAAQzK,MAAO,QACxB,CAAEyK,MAAO,UAAWzK,MAAO,WAC3B,CAAEyK,MAAO,QAASzK,MAAO,YAIrC,CACI6I,IAAK,OACL7I,MAAO,OACPuF,OAAQ,CACJ2E,KAAM,SAGd,CACIrB,IAAK,SACL7I,MAAO,SACPuF,OAAQ,CACJ2E,KAAM,SAGd,CACIrB,IAAK,OACL7I,MAAO,OACPuF,OAAQ,CACJ2E,KAAM,SAGd,CACIrB,IAAK,WACL7I,MAAO,OACPuF,OAAQ,CACJ2E,KAAM,SAGd,CACIrB,IAAK,KACL7I,MAAO,KACPuF,OAAQ,CACJ2E,KAAM,SAGd,CACIrB,IAAK,OACL7I,MAAO,aACP8I,UAAW,sBACXvD,OAAQ,CACJ2E,KAAM,UAMlB+D,aAAc,CACVC,KAAM,YAIVK,YAAY,EACZC,YAAY,EACZzF,UAAU,EACV0F,YAAY,EACZC,WAAW,EAGXC,aAAa,EACbC,SAAS,EACTC,YAAY,EAGZC,aAAc,wBAGd6C,iBAAkB,MAClBC,aAAc,CACV,CAAE5R,MAAO,SAAUC,KAAM,iBAAkBC,OAAQ,gBACnD,CAAEF,MAAO,UAAWC,KAAM,gBAAiBC,OAAQ,iBACnD,CAAEF,MAAO,SAAUC,KAAM,cAAeC,OAAQ,gBAChD,CAAEF,MAAO,mBAAoBC,KAAM,eAAgBC,OAAQ,mBAI/D8O,aAAc,CACVC,SAAS,EACTC,UAAU,EACVC,OAAO,EACPC,YAAY,IAGxB,EC5HJ,MAAMkpC,+BAA+B54C,EAAAA,KACjC,WAAAC,CAAYC,EAAU,IAClBC,MAAM,CACFO,UAAW,8BACRR,IAGPS,KAAK4G,MAAQrH,EAAQqH,OAAS,IAAIsxC,EAAAA,kBAAkB34C,EAAQyD,MAAQ,IAEpEhD,KAAKmH,SAAW,sbAWpB,CAEA,YAAMvG,GACFZ,KAAKqkC,SAAW,IAAIh9B,UAAS,CACzBlF,YAAa,YACbyE,MAAO5G,KAAK4G,MACZW,OAAQ,CACJ,CAAEqC,KAAM,mBAAoBjK,MAAO,mBAAoBsK,OAAQ,cAC/D,CAAEL,KAAM,oBAAqBjK,MAAO,oBAAqBsK,OAAQ,iBAGzEjK,KAAKoC,SAASpC,KAAKqkC,UAEnB,MAAM31B,EAAc,IAAInD,cAAY,CAChCpJ,YAAa,eACbqJ,QAASxL,KAAK4G,MACd6E,OAAQ,CACJ7L,KAAM,yBACN8L,MAAO,CACH,CAAE/L,MAAO,OAAQE,OAAQ,OAAQD,KAAM,aACvC,CAAED,MAAO,SAAUE,OAAQ,SAAUD,KAAM,WAAYiR,QAAQ,OAI3E7Q,KAAKoC,SAASsM,EAClB,CAEA,kBAAMypC,GACF,MAAM/nC,QAAarE,EAAAA,OAAOC,cAAc,CACpCvM,MAAO,wBAAwBO,KAAK4G,MAAMsB,IAAI,aAC9CtB,MAAO5G,KAAK4G,MACZuF,WAAYisC,EAAAA,aAAatsC,OAEzBsE,IACApQ,KAAK4G,MAAMyZ,IAAIjQ,EAAKpN,KAAKA,MACzBhD,KAAKqD,SAEb,CAEA,oBAAMg1C,SACsBtsC,SAAOO,QAAQ,uDAAuDtM,KAAK4G,MAAMsB,IAAI,uBAEnGlI,KAAK4G,MAAMsK,UACjBlR,KAAKwF,KAAK,UAAWxF,KAAK4G,OAElC,EAGJsxC,EAAAA,kBAAkB7qC,WAAa4qC,uBCtE/B,MAAMK,oCAAoC/qC,EAAAA,UACtC,WAAAjO,CAAYC,EAAU,IAClBC,MAAM,IACCD,EACHqK,KAAM,4BACN4D,SAAU,sBACVrH,OAAQ,4BACRsH,WAAY8qC,EAAAA,sBACZnnC,SAAUgnC,EAAAA,aAAatsC,KACvBuF,cAAe4mC,uBACftqC,kBAAmB,CACfvK,QAAQ,EACR+E,KAAM,MAGVI,QAAS,CACL,CAAEC,IAAK,UAAW7I,MAAO,UAAW+I,UAAU,GAC9C,CAAEF,IAAK,mBAAoB7I,MAAO,mBAAoB8I,UAAW,cACjE,CAAED,IAAK,oBAAqB7I,MAAO,oBAAqB8I,UAAW,eAGvEyF,YAAY,EACZC,YAAY,EACZzF,UAAU,EACV2F,WAAW,EACXC,aAAa,EACbC,SAAS,EACTC,YAAY,EAEZG,aAAc,CACVyvB,UAAW,CAAC,GAAI,GAAI,IACpBC,gBAAiB,GACjB5vB,aAAc,gCACd6vB,UAAW,oBACX5G,QAAS,CAAC,OAAQ,OAAQ,YAGtC,EChCJ,MAAM8gB,6BAA6BjrC,EAAAA,UAC/B,WAAAjO,CAAYC,EAAU,IAClBC,MAAM,IACCD,EACHqK,KAAM,sBACN4D,SAAU,0BACVrH,OAAQ,sBACRsH,WAAYgrC,EAAAA,gBAEZtkC,WAAYukC,EAAAA,iBAAiBtrC,OAC7BgE,SAAUsnC,EAAAA,iBAAiB5sC,KAG3BvD,QAAS,CACL,CACIC,IAAK,KACL7I,MAAO,KACP+I,UAAU,EACVoF,MAAO,cAEX,CACItF,IAAK,OACL7I,MAAO,OACP8I,UAAW,8BAEf,CACID,IAAK,cACL7I,MAAO,cACP+I,UAAU,GAEd,CACIF,IAAK,aACL7I,MAAO,UACP8I,UAAW,iBAEf,CACID,IAAK,YACL7I,MAAO,SACP8I,UAAW,iBAEf,CACID,IAAK,eACL7I,MAAO,OACP8I,UAAW,sBAEf,CACID,IAAK,UACL7I,MAAO,UACP8I,UAAW,mBAInBiG,YAAa,CACT,CAAE9O,KAAM,YAAaC,OAAQ,OAAQF,MAAO,aAC5C,CAAEC,KAAM,YAAaC,OAAQ,mBAAoBF,MAAO,oBACxD,CAAEC,KAAM,YAAaC,OAAQ,cAAeF,MAAO,eACnD,CAAEw9B,SAAS,GACX,CAAEv9B,KAAM,UAAWC,OAAQ,QAASF,MAAO,iBAC3C,CAAEw9B,SAAS,GACX,CAAEv9B,KAAM,WAAYC,OAAQ,kBAAmBF,MAAO,mBACtD,CAAEC,KAAM,qBAAsBC,OAAQ,aAAcF,MAAO,cAC3D,CAAEC,KAAM,YAAaC,OAAQ,WAAYF,MAAO,aAIpDuO,YAAY,EACZC,YAAY,EACZzF,UAAU,EACV0F,YAAY,EACZC,WAAW,EAGXC,aAAa,EACbC,SAAS,EACTC,YAAY,EAGZC,aAAc,0FAGd6C,iBAAkB,MAClBC,aAAc,CACV,CAAE5R,MAAO,SAAUC,KAAM,cAAeC,OAAQ,gBAChD,CAAEF,MAAO,SAAUC,KAAM,iBAAkBC,OAAQ,gBACnD,CAAEF,MAAO,WAAYC,KAAM,qBAAsBC,OAAQ,kBACzD,CAAEF,MAAO,aAAcC,KAAM,iBAAkBC,OAAQ,qBAI3D8O,aAAc,CACVC,SAAS,EACTC,UAAU,EACVC,OAAO,EACPC,YAAY,IAGxB,CAEA,wBAAM4pC,CAAmBn0C,EAAOC,GAC5B,MAAMyK,EAAOlP,KAAKqI,WAAWH,IAAIzD,EAAQ0K,QAAQlD,IAC3CitB,QAAentB,EAAAA,OAAOC,cAAc,CACtCvM,MAAO,cACPmH,MAAOsI,EACP3H,OAAQmxC,EAAAA,iBAAiBE,OAAOrxC,SAEpC,IAAK2xB,EAAQ,OAAO,EAChBA,EAAOn2B,QACP/C,KAAK4C,SAAS6J,MAAM1J,QAAQ,+BAE5B/C,KAAK4C,SAAS6J,MAAMnJ,MAAM,uBAElC,CAEA,uBAAMu1C,CAAkBr0C,EAAOC,GAC3B,MAAMyK,EAAOlP,KAAKqI,WAAWH,IAAIzD,EAAQ0K,QAAQlD,IAC3CitB,QAAehqB,EAAK3C,KAAK,CAACusC,YAAY,IAW5C,OAVI5f,EAAOn2B,SAAWm2B,EAAOl2B,KAAKC,aAExB8I,EAAAA,OAAOgtC,SAAS,CAClBt5C,MAAO,kBAAkByP,EAAKE,EAAExF,OAChC5G,KAAMk2B,EAAOl2B,KACbmF,KAAM,OAGVnI,KAAK4C,SAAS6J,MAAMnJ,MAAM,2BAEvB,CACX,CAEA,4BAAM01C,CAAuBx0C,EAAOC,GAChC,MAAMyK,EAAOlP,KAAKqI,WAAWH,IAAIzD,EAAQ0K,QAAQlD,IAC3CitB,QAAehqB,EAAK3C,KAAK,CAAC0sC,iBAAiB,IAMjD,OALI/f,EAAOn2B,SAAWm2B,EAAOl2B,KAAKC,OAC9BjD,KAAK4C,SAAS6J,MAAM1J,QAAQ,8BAE5B/C,KAAK4C,SAAS6J,MAAMnJ,MAAM,2BAEvB,CACX,CAEA,6BAAM41C,CAAwB10C,EAAOC,GACjC,MAAMyK,EAAOlP,KAAKqI,WAAWH,IAAIzD,EAAQ0K,QAAQlD,IAC3CitB,QAAentB,EAAAA,OAAOC,cAAc,CACtCvM,MAAO,mBACPmH,MAAOsI,EACP3H,OAAQmxC,EAAAA,iBAAiB/Z,YAAYp3B,SAGzC,OAAK2xB,IAEDA,EAAOn2B,SAAWm2B,EAAOl2B,KAAKC,OAC9BjD,KAAK4C,SAAS6J,MAAM1J,QAAQ,oCAE5B/C,KAAK4C,SAAS6J,MAAMnJ,MAAM,iCAEvB,EACX,CAEA,mBAAM61C,CAAc30C,EAAOC,GAOvB,WALwBsH,EAAAA,OAAO+9B,YAAY,CACvCrqC,MAAO,qBACP2W,QAAS,yDAIT,OAAO,EAGX,MAAMlH,EAAOlP,KAAKqI,WAAWH,IAAIzD,EAAQ0K,QAAQlD,IAC3CitB,QAAehqB,EAAK3C,KAAK,CAAC49B,OAAO,IAOvC,OANIjR,EAAOn2B,SAAWm2B,EAAOl2B,KAAKC,QAC9BjD,KAAK4C,SAAS6J,MAAM1J,QAAQ,kCAC5B/C,KAAKqI,WAAWmL,SAEhBxT,KAAK4C,SAAS6J,MAAMnJ,MAAM,+BAEvB,CACX,ECvKJ,MAAM81C,iBAAiB/5C,EAAAA,KACnB,WAAAC,CAAYC,EAAU,IAClBC,MAAM,CACFO,UAAW,eACRR,IAGPS,KAAK4G,MAAQrH,EAAQqH,OAAS,IAAIyyC,EAAAA,KAAK95C,EAAQyD,MAAQ,IACvDhD,KAAKs5C,QAAyC,UAA/Bt5C,KAAK4G,MAAMsB,IAAI,YAG9B,MAAMqxC,EAAiBv5C,KAAK4G,MAAMsB,IAAI,eAAiB,CAAA,EACvDlI,KAAKw5C,qBAAuB,IAAI/rC,EAAAA,WAAWvK,OAAO6uB,OAAOwnB,IAEzDv5C,KAAKmH,SAAW,8xFAiDpB,CAEA,YAAMvG,GAEFZ,KAAKmV,SAAW,IAAI9N,UAAS,CACzBT,MAAO5G,KAAK4G,MACZ7G,UAAW,MACXuH,iBAAiB,EACjBqP,eAAgB,IAChBpO,QAAS,EACThB,OAAQ,CACJ,CAAEqC,KAAM,KAAMjK,MAAO,MACrB,CAAEiK,KAAM,WAAYjK,MAAO,YAC3B,CAAEiK,KAAM,mBAAoBjK,MAAO,oBACnC,CAAEiK,KAAM,eAAgBjK,MAAO,gBAC/B,CAAEiK,KAAM,YAAajK,MAAO,YAAasK,OAAQ,YACjD,CAAEL,KAAM,WAAYjK,MAAO,YAC3B,CAAEiK,KAAM,gBAAiBjK,MAAO,SAAUsK,OAAQ,SAClD,CAAEL,KAAM,UAAWjK,MAAO,UAAWsK,OAAQ,YAC7C,CAAEL,KAAM,WAAYjK,MAAO,WAAYsK,OAAQ,YAC/C,CAAEL,KAAM,oBAAqBjK,MAAO,eACpC,CAAEiK,KAAM,oBAAqBjK,MAAO,mBACpC,CAAEiK,KAAM,oBAAqBjK,MAAO,gBACpC,CAAEiK,KAAM,MAAOjK,MAAO,aAAcsK,OAAQ,OAC5C,CAAEL,KAAM,YAAajK,MAAO,YAAasK,OAAQ,cAKzDjK,KAAKy5C,eAAiB,IAAIrxC,YAAU,CAChCC,WAAYrI,KAAKw5C,qBACjBjxC,QAAS,CACL,CAAEC,IAAK,OAAQ7I,MAAO,OAAQ8I,UAAW,SACzC,CAAED,IAAK,WAAY7I,MAAO,WAAY8I,UAAW,gBACjD,CAAED,IAAK,YAAa7I,MAAO,OAAQ8I,UAAW,YAC9C,CAAED,IAAK,eAAgB7I,MAAO,gBAC9B,CACI6I,IAAK,UACL7I,MAAO,UACPwH,SAAU,0bAatB,MAAMsD,EAAO,CAAE6K,KAAQtV,KAAKmV,UAC5B1K,EAAiB,WAAIzK,KAAKy5C,eAE1Bz5C,KAAK8G,QAAU,IAAI0D,UAAQ,CACvBC,OACAS,UAAW,OACX/I,YAAa,cAEjBnC,KAAKoC,SAASpC,KAAK8G,SAGnB,MAAM4yC,EAAW,IAAInuC,cAAY,CAC7BpJ,YAAa,oBACbpC,UAAW,yCACXyL,QAASxL,KAAK4G,MACd6E,OAAQ,CACJ7L,KAAM,yBACN8L,MAAO,CACH,CAAE/L,MAAO,OAAQE,OAAQ,YAAaD,KAAM,UAC5C,CAAED,MAAO,WAAYE,OAAQ,gBAAiBD,KAAM,kBACpD,CAAED,MAAO,eAAgBE,OAAQ,YAAaD,KAAM,gBACpD,CAAEiK,KAAM,WACR7J,KAAK4G,MAAMsB,IAAI,aACT,CAAEvI,MAAO,eAAgBE,OAAQ,eAAgBD,KAAM,cACvD,CAAED,MAAO,cAAeE,OAAQ,cAAeD,KAAM,gBAC3D,CAAEiK,KAAM,WACR,CAAElK,MAAO,cAAeE,OAAQ,cAAeD,KAAM,cAAeiR,QAAQ,OAIxF7Q,KAAKoC,SAASs3C,EAClB,CAEA,sBAAMC,GACF,MAAMC,EAAc55C,KAAK4G,MAAMsB,IAAI,gBAC7B2xC,EAAU75C,KAAK4G,MAAMsB,IAAI,OAE/B,GAAI0xC,EAAY1jB,WAAW,UAAW,CAClC,MAAM4jB,EAAa95C,KAAK4G,MAAMsB,IAAI,eAAiB,CAAA,EAC7C6xC,EAAS,CACX,CAAEj+B,IAAK+9B,EAASG,IAAK,eAClB92C,OAAO6uB,OAAO+nB,GAAYl/B,IAAIs6B,IAAA,CAAQp5B,IAAKo5B,EAAE57B,IAAK0gC,IAAK9E,EAAE+E,SAEhEC,EAAAA,gBAAgBxkC,KAAKqkC,EAAQ,CAAEI,aAAa,GAChD,KAA2B,oBAAhBP,EACPQ,YAAUhb,WAAWya,EAAS,CAAEp6C,MAAOO,KAAK4G,MAAMsB,IAAI,cAEtDgQ,OAAOqB,KAAKsgC,EAAS,SAE7B,CAEA,0BAAMQ,GACF,MAAM/gC,EAAMtZ,KAAK4G,MAAMsB,IAAI,OAC3B,GAAIoR,EAAK,CACL,MAAM2Y,EAAI9b,SAASmF,cAAc,KACjC2W,EAAEzW,KAAOlC,EACT2Y,EAAE0e,SAAW3wC,KAAK4G,MAAMsB,IAAI,YAC5BiO,SAASJ,KAAK2F,YAAYuW,GAC1BA,EAAE2e,QACFz6B,SAASJ,KAAK86B,YAAY5e,EAC9B,CACJ,CAEA,sBAAMqoB,SACiBvuC,EAAAA,OAAOC,cAAc,CACpCvM,MAAO,eAAeO,KAAK4G,MAAMsB,IAAI,cACrCtB,MAAO5G,KAAK4G,MACZuF,WAAYouC,EAAAA,UAAUzuC,QAGtB9L,KAAKqD,QAEb,CAEA,wBAAMm3C,SACIx6C,KAAK4G,MAAM2F,KAAK,CAAEkuC,WAAW,IACnCz6C,KAAKqD,QACT,CAEA,yBAAMq3C,SACI16C,KAAK4G,MAAM2F,KAAK,CAAEkuC,WAAW,IACnCz6C,KAAKqD,QACT,CAEA,wBAAMs3C,SACsB5uC,EAAAA,OAAOO,QAC3B,6CAA6CtM,KAAK4G,MAAMsB,IAAI,8CAC5D,mBACA,CAAEuR,aAAc,aAAcC,YAAa,mBAGxB1Z,KAAK4G,MAAMsK,WACrBnO,SACL/C,KAAKwF,KAAK,eAAgB,CAAEoB,MAAO5G,KAAK4G,OAGpD,EAGJyyC,EAAAA,KAAKhsC,WAAa+rC,SChOlB,MAAMwB,sBAAsBrtC,EAAAA,UACxB,WAAAjO,CAAYC,EAAU,IAClBC,MAAM,CACFoK,KAAM,cACN4D,SAAU,eACVrH,OAAQ,cACRsH,WAAYotC,EAAAA,SAGZzpC,SAAUmpC,EAAAA,UAAUzuC,KACpBuF,cAAe+nC,SAGftnC,MAAOC,MAAOvN,UACJxE,KAAK86C,iBAAiBt2C,IAGhCmJ,kBAAmB,CACfvK,QAAQ,EACR+E,KAAM,MAIVI,QAAS,CACL,CACIC,IAAK,KACL7I,MAAO,KACP+I,UAAU,EACVoF,MAAO,cAEX,CACItF,IAAK,WACL7I,MAAO,YAEX,CACI6I,IAAK,eACL7I,MAAO,OACP8I,UAAW,sBAEf,CACID,IAAK,YACL7I,MAAO,OACP8I,UAAW,YAEf,CACID,IAAK,aACL7I,MAAO,QACP8I,UAAW,uBAEf,CACID,IAAK,gBACL7I,MAAO,SACP8I,UAAW,SAEf,CACID,IAAK,UACL7I,MAAO,WACP8I,UAAW,mBAKnByF,YAAY,EACZC,YAAY,EACZzF,UAAU,EACV0F,YAAY,EACZC,WAAW,EAGXC,aAAa,EACbC,SAAS,EACTC,YAAY,EAGZC,aAAc,8DAGd6C,iBAAkB,MAClBC,aAAc,CACV,CAAE5R,MAAO,WAAYC,KAAM,iBAAkBC,OAAQ,kBACrD,CAAEF,MAAO,SAAUC,KAAM,cAAeC,OAAQ,gBAChD,CAAEF,MAAO,gBAAiBC,KAAM,eAAgBC,OAAQ,eAI5D8O,aAAc,CACVC,SAAS,EACTC,UAAU,EACVC,OAAO,EACPC,YAAY,MAEbxP,IAIPS,KAAK+6C,eAAe,CAChBC,cAAe,CAAC,OAChBC,YAAa,UACbC,UAAU,EACVC,gBAAgB,GAExB,CAMA,sBAAML,CAAiBt2C,GACfA,KAAayK,iBAGjB,MAAMmsC,EAAYjlC,SAASmF,cAAc,SACzC8/B,EAAUvxC,KAAO,OACjBuxC,EAAUC,OAAS,MACnBD,EAAUF,UAAW,EACrBE,EAAUxrC,MAAM2O,QAAU,OAG1B68B,EAAUE,iBAAiB,SAAUvpC,MAAOuwB,IACxC,MAAMiZ,EAAOjZ,EAAEkZ,OAAOhhB,MAAM,GAE5B,IAAK+gB,EACD,OAIJ,MAAME,EAAU,UAChB,GAAIF,EAAKpzC,KAAOszC,EACZz7C,KAAKi/B,UAAU,cAAcj/B,KAAK07C,gBAAgBH,EAAKpzC,2BAA2BnI,KAAK07C,gBAAgBD,YAK3G,IACI,MAAME,EAAY,IAAItC,OACtB,IAAIuC,EAAQ,CAAA,EACR57C,KAAKT,QAAQs8C,eAAiB77C,KAAK4C,SAASk5C,cAC5CF,EAAMhqC,MAAQ5R,KAAK4C,SAASk5C,YAAY7vC,IAG5C,MAAM8vC,EAASJ,EAAUI,OAAO,CAC5BR,OACA3xC,KAAM2xC,EAAK3xC,KACXoyC,YAAa,oCAAA,IAAwBl4C,MAAOm4C,uBAC5Cte,WAAW,EACXue,WAAaC,IACuBA,EAAa9I,YAEjD+I,WAAaljB,IAETl5B,KAAKiF,WAETo3C,QAAU/4C,IACNC,QAAQD,MAAM,iBAAkBA,GAChCtD,KAAKi/B,UAAU,kBAAoB37B,EAAM8S,aAE1CwlC,UAGDG,CACV,OAASz4C,GACLC,QAAQD,MAAM,8BAA+BA,GAC7CtD,KAAKi/B,UAAU,gCAAkC37B,EAAM8S,QAC3D,CAAA,QAEIglC,EAAUt1C,QACd,IAIJqQ,SAASJ,KAAK2F,YAAY0/B,GAC1BA,EAAUxK,OACd,CAQA,eAAA8K,CAAgBY,GACZ,GAAc,IAAVA,EAAa,MAAO,UACxB,MAEMC,EAAI/pB,KAAKyY,MAAMzY,KAAKod,IAAI0M,GAAS9pB,KAAKod,IAFlC,OAGV,OAAO4M,YAAYF,EAAQ9pB,KAAKiqB,IAHtB,KAG6BF,IAAIrpB,QAAQ,IAAM,IAF3C,CAAC,QAAS,KAAM,KAAM,MAEiCqpB,EACzE,CAEA,gBAAMG,CAAWliB,EAAOh2B,EAAOm4C,GAC3B,MAAMpB,EAAO/gB,EAAM,GACU+gB,EAAK3xC,KAAS2xC,EAAK1xC,KAAU0xC,EAAKpzC,KAE/D,IAEI,MAAMwzC,EAAY,IAAItC,OACtB,IAAIuC,EAAQ,CAAA,EACR57C,KAAKT,QAAQs8C,eAAiB77C,KAAK4C,SAASk5C,cAC5CF,EAAMhqC,MAAQ5R,KAAK4C,SAASk5C,YAAY7vC,IAI5C,MAAM8vC,EAASJ,EAAUI,OAAO,CAC5BR,OACA3xC,KAAM2xC,EAAK3xC,KACXoyC,YAAa,oDAAA,IAAwCl4C,MAAOm4C,uBAC5Dte,WAAW,EACXue,WAAaC,IACuBA,EAAa9I,YAEjD+I,WAAaljB,IAETl5B,KAAKiF,WAETo3C,QAAU/4C,IACNC,QAAQD,MAAM,iBAAkBA,GAChCtD,KAAKi/B,UAAU,kBAAoB37B,EAAM8S,aAE1CwlC,UAGDG,CACV,OAASz4C,GACLC,QAAQD,MAAM,8BAA+BA,GAC7CtD,KAAKi/B,UAAU,gCAAkC37B,EAAM8S,QAC3D,CACJ,EAIJwmC,EAAAA,mBAAmBhC,eCvOnB,MAAMiC,0BAA0BtvC,EAAAA,UAC5B,WAAAjO,CAAYC,EAAU,IAClBC,MAAM,IACCD,EACHqK,KAAM,mBACN4D,SAAU,oBACVrH,OAAQ,mBACRsH,WAAYqvC,EAAAA,aAEZ3oC,WAAY4oC,EAAAA,cAAc3vC,OAC1BgE,SAAU2rC,EAAAA,cAAcjxC,KAGxBvD,QAAS,CACL,CACIC,IAAK,KACL7I,MAAO,KACPoJ,MAAO,OACPL,UAAU,EACVoF,MAAO,cAEX,CACItF,IAAK,OACL7I,MAAO,cACP+I,UAAU,GAEd,CACIF,IAAK,UACL7I,MAAO,UACP8I,UAAW,mBAKnByF,YAAY,EACZC,YAAY,EACZzF,UAAU,EACV0F,YAAY,EACZC,WAAW,EAGXC,aAAa,EACbC,SAAS,EACTC,YAAY,EAGZC,aAAc,0EAGd6C,iBAAkB,MAClBC,aAAc,CACV,CAAE5R,MAAO,SAAUC,KAAM,cAAeC,OAAQ,gBAChD,CAAEF,MAAO,SAAUC,KAAM,iBAAkBC,OAAQ,gBACnD,CAAEF,MAAO,cAAeC,KAAM,eAAgBC,OAAQ,gBACtD,CAAEF,MAAO,eAAgBC,KAAM,aAAcC,OAAQ,iBACrD,CAAEF,MAAO,eAAgBC,KAAM,eAAgBC,OAAQ,gBAI3D8O,aAAc,CACVC,SAAS,EACTC,UAAU,EACVC,OAAO,EACPC,YAAY,IAGxB,ECyDG,SAASiuC,EAAoB7pC,EAAK8pC,GAAY,GAiCjD,GA/BA9pC,EAAI+pC,aAAa,mBAAoBC,mBAAyB,CAACxzC,YAAa,CAAC,gBAC7EwJ,EAAI+pC,aAAa,cAAeE,cAAoB,CAACzzC,YAAa,CAAC,YAAa,iBAChFwJ,EAAI+pC,aAAa,eAAgBG,cAAoB,CAAC1zC,YAAa,CAAC,kBACpEwJ,EAAI+pC,aAAa,gBAAiBI,eAAqB,CAAC3zC,YAAa,CAAC,mBACtEwJ,EAAI+pC,aAAa,iBAAkBK,gBAAsB,CAAC5zC,YAAa,CAAC,oBACxEwJ,EAAI+pC,aAAa,mBAAoBM,kBAAwB,CAAC7zC,YAAa,CAAC,gBAC5EwJ,EAAI+pC,aAAa,sBAAuBO,qBAA2B,CAAC9zC,YAAa,CAAC,kBAClFwJ,EAAI+pC,aAAa,eAAgBQ,cAAoB,CAAC/zC,YAAa,CAAC,kBACpEwJ,EAAI+pC,aAAa,mBAAoBS,kBAAwB,CAACh0C,YAAa,CAAC,oBAC5EwJ,EAAI+pC,aAAa,gBAAiBU,eAAqB,CAACj0C,YAAa,CAAC,oBACtEwJ,EAAI+pC,aAAa,cAAeW,aAAmB,CAACl0C,YAAa,CAAC,eAClEwJ,EAAI+pC,aAAa,sBAAuBY,oBAA0B,CAACn0C,YAAa,CAAC,kBACjFwJ,EAAI+pC,aAAa,+BAAgCa,4BAAkC,CAACp0C,YAAa,CAAC,kBAClGwJ,EAAI+pC,aAAa,sBAAuBc,sBAA4B,CAACr0C,YAAa,CAAC,kBACnFwJ,EAAI+pC,aAAa,yBAA0Be,sBAA4B,CAACt0C,YAAa,CAAC,gBACtFwJ,EAAI+pC,aAAa,uBAAwBgB,qBAA2B,CAACv0C,YAAa,CAAC,gBACnFwJ,EAAI+pC,aAAa,oBAAqBiB,qBAA2B,CAACx0C,YAAa,CAAC,gBAChFwJ,EAAI+pC,aAAa,yBAA0BkB,uBAA6B,CAACz0C,YAAa,CAAC,gBACvFwJ,EAAI+pC,aAAa,4BAA6BmB,sBAA4B,CAAE10C,YAAa,CAAC,oBAC1FwJ,EAAI+pC,aAAa,kBAAmBoB,iBAAuB,CAAE30C,YAAa,CAAC,sBAC3EwJ,EAAI+pC,aAAa,iBAAkBqB,gBAAsB,CAAE50C,YAAa,CAAC,sBACzEwJ,EAAI+pC,aAAa,6BAA8BsB,4BAAkC,CAAE70C,YAAa,CAAC,oBACjGwJ,EAAI+pC,aAAa,wBAAyBuB,kBAAwB,CAAE90C,YAAa,CAAC,kBAClFwJ,EAAI+pC,aAAa,sBAAuBwB,oBAA0B,CAAE/0C,YAAa,CAAC,kBAClFwJ,EAAI+pC,aAAa,wBAAyByB,sBAA4B,CAAEh1C,YAAa,CAAC,kBACtFwJ,EAAI+pC,aAAa,yBAA0B0B,sBAA4B,CAAEj1C,YAAa,CAAC,kBACvFwJ,EAAI+pC,aAAa,sBAAuB2B,oBAA0B,CAAEl1C,YAAa,CAAC,kBAClFwJ,EAAI+pC,aAAa,0BAA2B4B,qBAA2B,CAAEn1C,YAAa,CAAC,kBACvFwJ,EAAI+pC,aAAa,sBAAuB6B,aAAmB,CAAEp1C,YAAa,CAAC,kBAGvEszC,GAAa9pC,EAAI6rC,SAAW7rC,EAAI6rC,QAAQC,cAAe,CACvD,MAAMC,EAAkB/rC,EAAI6rC,QAAQC,cAAc,UAClD,GAAIC,GAAmBA,EAAgBxzC,MAAO,CAE1C,MAAMyzC,EAAiB,CACnB,CACIlpC,KAAM,YACNmpC,MAAO,yBACPx/C,KAAM,kBACN+J,YAAa,CAAC,eAElB,CACIsM,KAAM,kBACNmpC,MAAO,oBACPx/C,KAAM,yBACN+J,YAAa,CAAC,YAAa,gBAE/B,CACIsM,KAAM,QACNmpC,MAAO,qBACPx/C,KAAM,YACN+J,YAAa,CAAC,iBAElB,CACIsM,KAAM,SACNmpC,MAAO,sBACPx/C,KAAM,eACN+J,YAAa,CAAC,kBAElB,CACIsM,KAAM,sBACNmpC,MAAO,KACPx/C,KAAM,wBACN+J,YAAa,CAAC,kBACd01C,SAAU,CACN,CACIppC,KAAM,YACNmpC,MAAO,kCACPx/C,KAAM,oBACN+J,YAAa,CAAC,mBAElB,CACIsM,KAAM,YACNmpC,MAAO,yBACPx/C,KAAM,0BACN+J,YAAa,CAAC,mBAElB,CACIsM,KAAM,UACNmpC,MAAO,uBACPx/C,KAAM,qBACN+J,YAAa,CAAC,qBAElB,CACIsM,KAAM,SACNmpC,MAAO,sBACPx/C,KAAM,UACN+J,YAAa,CAAC,mBAElB,CACIsM,KAAM,cACNmpC,MAAO,wBACPx/C,KAAM,yBACN+J,YAAa,CAAC,uBAI1B,CACIsM,KAAM,WACNmpC,MAAO,KACPx/C,KAAM,YACN+J,YAAa,CAAC,iBACd01C,SAAU,CACN,CACIppC,KAAM,OACNmpC,MAAO,oBACPx/C,KAAM,kBACN+J,YAAa,CAAC,cAElB,CACIsM,KAAM,eACNmpC,MAAO,4BACPx/C,KAAM,WACN+J,YAAa,CAAC,iBAElB,CACIsM,KAAM,mBACNmpC,MAAO,qCACPx/C,KAAM,aACN+J,YAAa,CAAC,iBAElB,CACIsM,KAAM,cACNmpC,MAAO,4BACPx/C,KAAM,WACN+J,YAAa,CAAC,iBAElB,CACIsM,KAAM,sBACNmpC,MAAO,mCACPx/C,KAAM,oBACN+J,YAAa,CAAC,qBAI1B,CACIsM,KAAM,UACNmpC,MAAO,KACPx/C,KAAM,YACN+J,YAAa,CAAC,eAAgB,cAC9B01C,SAAU,CACN,CACIppC,KAAM,aACNmpC,MAAO,yBACPx/C,KAAM,YACN+J,YAAa,CAAC,eAElB,CACIsM,KAAM,mBACNmpC,MAAO,4BACPx/C,KAAM,eACN+J,YAAa,CAAC,eAElB,CACIsM,KAAM,QACNmpC,MAAO,qBACPx/C,KAAM,kBACN+J,YAAa,CAAC,mBAI1B,CACIsM,KAAM,qBACNmpC,MAAO,KACPx/C,KAAM,eACN+J,YAAa,CAAC,gBACd01C,SAAU,CACN,CAAEppC,KAAM,YAAampC,MAAO,8BAA+Bx/C,KAAM,qBACjE,CAAEqW,KAAM,iBAAkBmpC,MAAO,4BAA6Bx/C,KAAM,WACpE,CAAEqW,KAAM,YAAampC,MAAO,8BAA+Bx/C,KAAM,wBACjE,CAAEqW,KAAM,aAAcmpC,MAAO,+BAAgCx/C,KAAM,WACnE,CAAEqW,KAAM,UAAWmpC,MAAO,4BAA6Bx/C,KAAM,cAGrE,CACIqW,KAAM,cACNmpC,MAAO,KACPx/C,KAAM,cACN+J,YAAa,CAAC,cACd01C,SAAU,CACN,CACIppC,KAAM,UACNmpC,MAAO,6BACPx/C,KAAM,WACN+J,YAAa,CAAC,eAElB,CACIsM,KAAM,YACNmpC,MAAO,+BACPx/C,KAAM,WACN+J,YAAa,CAAC,eAElB,CACIsM,KAAM,OACNmpC,MAAO,0BACPx/C,KAAM,gBACN+J,YAAa,CAAC,eAElB,CACIsM,KAAM,YACNmpC,MAAO,+BACPx/C,KAAM,eACN+J,YAAa,CAAC,iBAI1B,CACIsM,KAAM,YACNmpC,MAAO,KACPx/C,KAAM,eACN+J,YAAa,CAAC,gBACd01C,SAAU,CACN,CACIppC,KAAM,UACNmpC,MAAO,gCACPx/C,KAAM,gBACN+J,YAAa,CAAC,iBAElB,CACIsM,KAAM,MACNmpC,MAAO,4BACPx/C,KAAM,eACN+J,YAAa,CAAC,oBAO9Bu1C,EAAgBxzC,MAAM4zC,WAAWH,EACrC,CACJ,CAEJ"}
1
+ {"version":3,"file":"admin.cjs.js","sources":["../src/extensions/admin/account/AdminDashboardPage.js","../src/extensions/admin/account/users/UserView.js","../src/extensions/admin/account/users/UserTablePage.js","../src/extensions/admin/account/users/MemberView.js","../src/extensions/admin/account/users/MemberTablePage.js","../src/extensions/admin/account/groups/GroupView.js","../src/extensions/admin/account/groups/GroupTablePage.js","../src/extensions/admin/account/devices/DeviceView.js","../src/extensions/admin/account/devices/UserDeviceTablePage.js","../src/extensions/admin/account/devices/UserDeviceLocationTablePage.js","../src/extensions/admin/account/devices/GeoIPView.js","../src/extensions/admin/account/devices/GeoLocatedIPTablePage.js","../src/extensions/map/MapLibreView.js","../src/extensions/map/countryCentroids.js","../src/extensions/map/MetricsCountryMapView.js","../src/extensions/admin/incidents/IncidentDashboardPage.js","../src/core/views/data/StackTraceView.js","../src/extensions/admin/incidents/adapters/IncidentHistoryAdapter.js","../src/extensions/admin/incidents/IncidentView.js","../src/extensions/admin/incidents/IncidentTablePage.js","../src/extensions/admin/incidents/EventView.js","../src/extensions/admin/incidents/EventTablePage.js","../src/extensions/admin/incidents/adapters/TicketNoteAdapter.js","../src/extensions/admin/incidents/TicketView.js","../src/extensions/admin/incidents/TicketTablePage.js","../src/extensions/admin/incidents/RuleSetView.js","../src/extensions/admin/incidents/RuleSetTablePage.js","../src/extensions/admin/messaging/email/EmailDomainTablePage.js","../src/extensions/admin/messaging/email/EmailMailboxTablePage.js","../src/extensions/admin/messaging/email/EmailTemplateView.js","../src/extensions/admin/messaging/email/EmailTemplateTablePage.js","../src/extensions/admin/messaging/email/EmailView.js","../src/extensions/admin/messaging/email/SentMessageTablePage.js","../src/core/models/Phonehub.js","../src/extensions/admin/messaging/sms/PhoneNumberView.js","../src/extensions/admin/messaging/sms/PhoneNumberTablePage.js","../src/extensions/admin/messaging/sms/SMSView.js","../src/extensions/admin/messaging/sms/SMSTablePage.js","../src/extensions/admin/messaging/push/PushDashboardPage.js","../src/extensions/admin/messaging/push/PushConfigTablePage.js","../src/extensions/admin/messaging/push/PushTemplateTablePage.js","../src/extensions/admin/messaging/push/PushDeliveryView.js","../src/extensions/admin/messaging/push/PushDeliveryTablePage.js","../src/extensions/admin/messaging/push/PushDeviceView.js","../src/extensions/admin/messaging/push/PushDeviceTablePage.js","../src/extensions/admin/jobs/JobStatsView.js","../src/extensions/admin/jobs/JobHealthView.js","../src/extensions/admin/jobs/JobDetailsView.js","../src/extensions/admin/jobs/JobsAdminPage.js","../src/extensions/admin/jobs/TaskDetailsView.js","../src/extensions/admin/jobs/RunnerDetailsView.js","../src/extensions/admin/jobs/TaskManagementPage.js","../src/extensions/admin/monitoring/LogView.js","../src/extensions/admin/monitoring/LogTablePage.js","../src/extensions/admin/monitoring/MetricsPermissionsView.js","../src/extensions/admin/monitoring/MetricsPermissionsTablePage.js","../src/extensions/admin/storage/FileManagerTablePage.js","../src/extensions/admin/storage/FileView.js","../src/extensions/admin/storage/FileTablePage.js","../src/extensions/admin/storage/S3BucketTablePage.js","../src/admin.js"],"sourcesContent":["/**\n * AdminDashboardPage - Administrative dashboard with system metrics and charts\n */\n\nimport Page from '@core/Page.js';\nimport View from '@core/View.js';\nimport { MetricsChart, MetricsMiniChart, MetricsMiniChartWidget } from '@ext/charts/index.js';\n\n// Embedded HeaderView for dashboard statistics\nclass AdminHeaderView extends View {\n constructor(options = {}) {\n super({\n title: 'Dashboard',\n ...options,\n headerActions: [\n {\n label: 'Export',\n icon: 'bi-download',\n action: 'export',\n buttonClass: 'btn-primary'\n }\n ],\n className: 'admin-header-section'\n });\n\n // Mock data - replace with real API calls\n this.stats = {\n user_activity_day: 0,\n total_users: 0,\n group_activity_day: 0,\n total_groups: 0,\n api_calls: 0,\n apiChange: '',\n incidents: 0,\n incidentsChange: ''\n };\n\n // Prepare formatted data for template\n this.prepareStatsForTemplate();\n }\n\n async getTemplate() {\n return `\n <div class=\"admin-stats-header mb-4\">\n <div class=\"row\">\n <div class=\"col-xl-3 col-lg-6 col-12 mb-3\">\n <div data-container=\"user_activity_day\"></div>\n </div>\n\n <div class=\"col-xl-3 col-lg-6 col-12 mb-3\">\n <div data-container=\"group_activity_day\"></div>\n </div>\n\n <div class=\"col-xl-3 col-lg-6 col-12 mb-3\">\n <div data-container=\"api_activity_day\"></div>\n </div>\n\n <div class=\"col-xl-3 col-lg-6 col-12 mb-3\">\n <div data-container=\"incident_activity_day\"></div>\n </div>\n </div>\n </div>\n `;\n }\n\n async onInit() {\n // TODO: Replace with actual API calls to fetch real statistics\n // this.loadStats();\n // slug: user_activity_day, group_activity_day\n this.userActivity = new MetricsMiniChartWidget({\n icon: \"bi bi-people fs-2\",\n title: 'User Activity',\n subtitle: '{{now_value}} <span class=\"subtitle-label\">{{now_label}}</span> {{total_users}} <span class=\"subtitle-label\">Total</span>',\n background: \"#5388D6\",\n textColor: \"#FFFFFF\",\n granularity: 'days',\n trendRange: 4,\n trendOffset: 0,\n slugs: ['user_activity_day'],\n account: 'global',\n chartType: 'bar',\n showTooltip: true,\n showXAxis: true,\n height: 50,\n chartWidth: '100%',\n color: 'rgba(245, 245, 255, 0.8)',\n fill: true,\n fillColor: 'rgba(245, 245, 255, 0.6)',\n smoothing: 0.3,\n showTrending: true,\n showSettings: true,\n showDateRange: true,\n containerId: 'user_activity_day'\n });\n this.addChild(this.userActivity);\n\n this.groupActivity = new MetricsMiniChartWidget({\n icon: \"bi bi-collection fs-2\",\n title: 'Group Activity',\n subtitle: '{{now_value}} <span class=\"subtitle-label\">{{now_label}}</span> {{total_groups}} <span class=\"subtitle-label\">Total</span>',\n background: \"#1f6a7a\",\n textColor: \"#FFFFFF\",\n granularity: 'days',\n trendRange: 4,\n trendOffset: 0,\n slugs: ['group_activity_day'],\n account: 'global',\n chartType: 'bar',\n showTooltip: true,\n showXAxis: true,\n height: 50,\n chartWidth: '100%',\n color: 'rgba(245, 245, 255, 0.8)',\n fill: true,\n fillColor: 'rgba(245, 245, 255, 0.6)',\n smoothing: 0.3,\n showTrending: true,\n containerId: 'group_activity_day'\n });\n this.addChild(this.groupActivity);\n\n this.apiActivity = new MetricsMiniChartWidget({\n icon: \"bi bi-graph-up fs-2\",\n title: 'API Requests',\n subtitle: '{{now_value}} <span class=\"subtitle-label\">{{now_label}}</span> {{total}} <span class=\"subtitle-label\">Total</span>',\n background: \"#50A079\",\n textColor: \"#FFFFFF\",\n endpoint: '/api/metrics/fetch',\n trendRange: 4,\n trendOffset: 0,\n granularity: 'days',\n slugs: ['api_calls'],\n account: 'global',\n chartType: 'line',\n showTooltip: true,\n showXAxis: true,\n height: 50,\n chartWidth: '100%',\n color: 'rgba(245, 245, 255, 0.8)',\n fill: true,\n fillColor: 'rgba(245, 245, 255, 0.6)',\n smoothing: 0.3,\n showTrending: true,\n containerId: 'api_activity_day'\n });\n this.addChild(this.apiActivity);\n\n this.incidentActivity = new MetricsMiniChartWidget({\n icon: \"bi bi-exclamation-triangle fs-2\",\n title: 'Incidents',\n subtitle: '{{now_value}} <span class=\"subtitle-label\">{{now_label}}</span> {{total}} <span class=\"subtitle-label\">Total</span>',\n background: \"#B14545\",\n textColor: \"#FFFFFF\",\n endpoint: '/api/metrics/fetch',\n trendRange: 4,\n trendOffset: 0,\n granularity: 'days',\n slugs: ['incidents'],\n account: 'incident',\n chartType: 'line',\n showTooltip: true,\n showXAxis: true,\n height: 50,\n chartWidth: '100%',\n color: 'rgba(245, 245, 255, 0.8)',\n fill: true,\n fillColor: 'rgba(245, 245, 255, 0.6)',\n smoothing: 0.3,\n showTrending: true,\n containerId: 'incident_activity_day'\n });\n this.addChild(this.incidentActivity);\n }\n\n async onBeforeRender() {\n\n }\n\n prepareStatsForTemplate() {\n // Determine badge and icon classes based on incidents change\n // const isDecreasing = this.stats.incidentsChange.startsWith('-');\n // this.stats.incidentsBadgeClass = isDecreasing ? 'bg-success-subtle text-success' : 'bg-warning-subtle text-warning';\n // this.stats.incidentsIconClass = isDecreasing ? 'arrow-down' : 'arrow-up';\n\n }\n\n async loadValues() {\n try {\n const response = await this.getApp().rest.GET('/api/metrics/value/get', {\n slugs: ['total_users', 'total_groups'],\n account: \"global\"\n });\n if (response.success && response.data.status) {\n Object.assign(this.stats, response.data.data);\n }\n if (this.groupActivity) {\n this.groupActivity.header.total_groups = this.stats.total_groups || 0;\n this.groupActivity.header.render();\n }\n if (this.userActivity) {\n this.userActivity.header.total_users = this.stats.total_users || 0;\n this.userActivity.header.render();\n }\n } catch (error) {\n console.error('Failed to load admin stats:', error);\n }\n }\n\n async loadStats() {\n // Example of how to load real data\n try {\n const response = await this.getApp().rest.GET('/api/metrics/series', {\n slugs: ['user_created', 'user_activity_day', \"incidents\",\n \"api_calls\", \"api_errors\", \"group_activity_day\"],\n account: \"global\",\n granularity: \"days\"\n });\n if (response.success && response.data.status) {\n Object.assign(this.stats, response.data.data);\n this.prepareStatsForTemplate();\n }\n } catch (error) {\n console.error('Failed to load admin stats:', error);\n }\n }\n}\n\nexport default class AdminDashboardPage extends Page {\n constructor(options = {}) {\n super({\n ...options,\n title: 'Admin Dashboard',\n className: 'admin-dashboard-page'\n });\n\n // Page data\n this.pageTitle = 'Admin Dashboard';\n this.pageSubtitle = 'System monitoring and metrics overview';\n }\n\n async getTemplate() {\n return `\n <div class=\"admin-dashboard-container container-lg\">\n <!-- Page Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-2\">\n <div>\n <p class=\"text-muted mb-0\">{{pageSubtitle}}</p>\n <small class=\"text-info\">\n <i class=\"bi bi-shield-check me-1\"></i>\n Real-time system metrics and performance monitoring\n </small>\n </div>\n <div class=\"btn-group\" role=\"group\">\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm\"\n data-action=\"refresh-all\" title=\"Refresh All Charts\">\n <i class=\"bi bi-arrow-clockwise\"></i> Refresh\n </button>\n <button type=\"button\" class=\"btn btn-outline-primary btn-sm\"\n data-action=\"export-metrics\" title=\"Export Metrics Data\">\n <i class=\"bi bi-download\"></i> Export\n </button>\n <button type=\"button\" class=\"btn btn-outline-warning btn-sm\"\n data-action=\"view-alerts\" title=\"View System Alerts\">\n <i class=\"bi bi-bell\"></i> Alerts\n </button>\n </div>\n </div>\n\n <!-- Stats Header -->\n <div data-container=\"admin-header\"></div>\n <div data-container=\"example-chart\"></div>\n <!-- Charts Section -->\n <div class=\"row\">\n <!-- Full Width API Metrics Chart -->\n <div class=\"col-12 mb-4\">\n <div data-container=\"api-metrics-chart\"></div>\n </div>\n </div>\n\n <!-- System Status Footer -->\n <div class=\"row\">\n <div class=\"col-12\">\n <div class=\"alert alert-success border-0\" role=\"alert\">\n <div class=\"d-flex align-items-center\">\n <i class=\"bi bi-check-circle-fill me-2\"></i>\n <div>\n <strong>System Status:</strong> All systems operational.\n Last updated: <span class=\"text-muted\">{{lastUpdated}}</span>\n </div>\n <div class=\"ms-auto\">\n <button class=\"btn btn-sm btn-outline-success\" data-action=\"view-system-status\">\n <i class=\"bi bi-info-circle\"></i> Details\n </button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n `;\n }\n\n async onInit() {\n // Set last updated time\n this.lastUpdated = new Date().toLocaleString();\n\n // Create and add header view\n this.headerView = new AdminHeaderView({\n containerId: 'admin-header'\n });\n this.addChild(this.headerView);\n\n // Create API Metrics Chart\n this.apiMetricsChart = new MetricsChart({\n title: `<i class=\"bi bi-graph-up me-2\"></i> API Metrics`,\n endpoint: '/api/metrics/fetch',\n height: 250,\n granularity: 'hours',\n slugs: ['api_calls', 'api_errors'],\n account: 'global',\n chartType: 'line',\n showDateRange: false,\n yAxis: {\n label: 'Count',\n beginAtZero: true\n },\n tooltip: {\n y: 'number'\n },\n containerId: 'api-metrics-chart'\n });\n this.addChild(this.apiMetricsChart);\n\n }\n\n // Action Handlers\n async onActionRefreshAll(event, element) {\n try {\n // Show loading state\n const button = element || event?.currentTarget || null;\n const icon = button?.querySelector?.('i');\n icon?.classList.add('bi-spin');\n if (button) button.disabled = true;\n\n // Refresh all charts\n const promises = [\n this.headerView?.loadValues(),\n this.apiMetricsChart?.refresh()\n ].filter(Boolean);\n\n await Promise.allSettled(promises);\n\n\n // Update last updated time\n this.lastUpdated = new Date().toLocaleString();\n\n // Emit refresh event\n const eventBus = this.getApp()?.events;\n if (eventBus) {\n eventBus.emit('admin:dashboard-refreshed', {\n page: this,\n timestamp: this.lastUpdated\n });\n }\n\n } catch (error) {\n console.error('Failed to refresh dashboard:', error);\n // Show error feedback\n const alert = this.element.querySelector('.alert-success');\n if (alert) {\n alert.className = 'alert alert-danger border-0';\n alert.innerHTML = `\n <div class=\"d-flex align-items-center\">\n <i class=\"bi bi-exclamation-triangle-fill me-2\"></i>\n <div>\n <strong>Error:</strong> Failed to refresh dashboard data.\n </div>\n </div>\n `;\n\n // Reset after 5 seconds\n setTimeout(() => {\n alert.className = 'alert alert-success border-0';\n alert.innerHTML = `\n <div class=\"d-flex align-items-center\">\n <i class=\"bi bi-check-circle-fill me-2\"></i>\n <div>\n <strong>System Status:</strong> All systems operational.\n Last updated: <span class=\"text-muted\">${this.lastUpdated}</span>\n </div>\n </div>\n `;\n }, 5000);\n }\n } finally {\n // Reset button state\n const icon = element.querySelector('i');\n icon?.classList.remove('bi-spin');\n if (button) button.disabled = false;\n }\n }\n\n async onActionExportMetrics(event, element) {\n try {\n // Export all charts as PNG\n await this.apiMetricsChart?.export('png');\n\n // Show success feedback\n const eventBus = this.getApp()?.events;\n if (eventBus) {\n eventBus.emit('admin:metrics-exported', {\n page: this,\n charts: ['api-metrics']\n });\n }\n\n } catch (error) {\n console.error('Failed to export metrics:', error);\n }\n }\n\n async onActionViewAlerts(event, element) {\n // Navigate to alerts page or show alerts modal\n const router = this.getApp()?.router;\n if (router) {\n router.navigateTo('/admin/alerts');\n }\n }\n\n async onActionViewSystemStatus(event, element) {\n // Navigate to system status page\n const router = this.getApp()?.router;\n if (router) {\n router.navigateTo('/admin/system-status');\n }\n }\n\n // Public API\n async refreshDashboard() {\n return this.onActionRefreshAll(null, null, { disabled: false, querySelector: () => null });\n }\n\n getCharts() {\n return {\n apiMetrics: this.apiMetricsChart\n };\n }\n\n getStats() {\n return this.headerView?.stats || {};\n }\n\n async onAfterRender() {\n this.headerView?.loadValues();\n }\n}\n","/**\n * UserView - Comprehensive user management interface\n *\n * Features:\n * - Two-row header with avatar, name, contact info, and activity\n * - Tabbed interface for Profile, Groups, Events, and Logs\n * - Integrated with DataView and Table components\n * - Clean Bootstrap 5 styling\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport DataView from '@core/views/data/DataView.js';\nimport TableView from '@core/views/table/TableView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport { User, UserDataView, UserForms, UserDeviceList, UserDeviceLocationList } from '@core/models/User.js';\nimport { LogList } from '@core/models/Log.js';\nimport { IncidentEventList } from '@core/models/Incident.js';\nimport { MemberList } from '@core/models/Member.js';\nimport { PushDeviceList } from '@core/models/Push.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport FormView from '@core/forms/FormView.js';\n\n\nclass UserView extends View {\n constructor(options = {}) {\n super({\n className: 'user-view',\n ...options\n });\n\n // User model instance\n this.model = options.model || new User(options.data || {});\n\n // Tab views\n this.tabView = null;\n this.profileView = null;\n this.groupsView = null;\n this.eventsView = null;\n this.logsView = null;\n\n // Set template\n this.template = `\n <div class=\"user-view-container\">\n <!-- User Header -->\n <div data-container=\"user-header\"></div>\n <!-- Tab Container -->\n <div data-container=\"user-tabs\"></div>\n </div>\n `;\n }\n\n async onInit() {\n // Create Profile tab using DataView\n this.header = new View({\n containerId: 'user-header',\n template: `\n <div class=\"d-flex justify-content-between align-items-start mb-4\">\n <!-- Left Side: Primary Identity -->\n <div class=\"d-flex align-items-center gap-3\">\n {{{model.avatar|avatar('md','rounded-circle')}}}\n <div>\n <h3 class=\"mb-0\">{{model.display_name|default('Unnamed User')}}</h3>\n <a href=\"mailto:{{model.email}}\" class=\"text-decoration-none text-body\">{{model.email}}</a>{{{model.email|clipboard}}}\n {{#model.phone_number}}\n <div class=\"text-muted small mt-1\">{{{model.phone_number|phone(false)}}}</div>\n {{/model.phone_number}}\n </div>\n </div>\n\n <!-- Right Side: Status & Actions -->\n <div class=\"d-flex align-items-start gap-4\">\n <div class=\"text-end\">\n <div class=\"d-flex align-items-center gap-2\">\n <i class=\"bi bi-circle-fill fs-8 {{model.is_active|boolean('text-success','text-secondary')}}\"></i>\n <span>{{model.is_active|boolean('Active','Inactive')}}</span>\n </div>\n {{#model.last_activity}}\n <div class=\"text-muted small mt-1\">Last active {{model.last_activity|relative}}</div>\n {{/model.last_activity}}\n </div>\n <div data-container=\"user-context-menu\"></div>\n </div>\n </div>`\n });\n\n this.header.setModel(this.model);\n this.addChild(this.header);\n\n this.profileView = new DataView({\n model: this.model,\n className: \"p-3\",\n showEmptyValues: true,\n fields: UserDataView.profile.fields\n });\n\n this.permsView = new FormView({\n fields: User.PERMISSION_FIELDS,\n model: this.model, // Set model during construction\n autosaveModelField: true // Enable auto-save with status indicators\n });\n\n // Create Groups table with MemberList collection\n const membersCollection = new MemberList({\n params: { user: this.model.get('id'), size: 5 }\n });\n this.groupsView = new TableView({\n collection: membersCollection,\n hideActivePillNames: ['user'],\n columns: [\n {\n key: 'created',\n label: 'Date Joined',\n formatter: 'date',\n sortable: true\n },\n {\n key: 'group.name',\n label: 'Group Name',\n sortable: true\n },\n {\n key: 'permissions|keys|badge',\n label: 'Permissions'\n }\n\n ]\n });\n\n // Create Events table with IncidentEventList collection\n const eventsCollection = new IncidentEventList({\n params: {\n size: 5,\n model_name: \"account.User\",\n model_id: this.model.get('id')\n }\n });\n this.eventsView = new TableView({\n collection: eventsCollection,\n hideActivePillNames: ['model_name', 'model_id'],\n columns: [\n {\n key: 'id',\n label: 'ID',\n sortable: true,\n width: '40px'\n },\n {\n key: 'created',\n label: 'Date',\n formatter: 'datetime',\n sortable: true,\n width: '150px'\n },\n {\n key: 'category|badge',\n label: 'Category'\n },\n {\n key: 'title',\n label: 'Title'\n }\n ]\n });\n\n const userDevices = new UserDeviceList({\n params: {\n size: 5,\n user: this.model.get('id')\n }\n });\n this.devicesView = new TableView({\n collection: userDevices,\n hideActivePillNames: ['user'],\n columns: [\n { key: 'duid|truncate_middle(16)', label: 'Device ID', sortable: true },\n { key: 'device_info.user_agent.family', label: 'Browser', formatter: \"default('—')\" },\n { key: 'device_info.os.family', label: 'OS', formatter: \"default('—')\" },\n { key: 'first_seen', label: 'First Seen', formatter: \"epoch|datetime\" },\n { key: 'last_seen', label: 'Last Seen', formatter: \"epoch|datetime\" }\n ],\n size: 5\n });\n\n\n const userLocations = new UserDeviceLocationList({\n params: {\n size: 5,\n user: this.model.get('id')\n }\n });\n this.locationsView = new TableView({\n collection: userLocations,\n\n hideActivePillNames: ['user'],\n columns: [\n { key: 'user_device', label: 'Device', template: '{{model.user_device.device_info.user_agent.family}} on {{model.user_device.device_info.os.family}}', sortable: true },\n { key: 'geolocation.city', label: 'City', formatter: \"default('—')\" },\n { key: 'geolocation.region', label: 'Region', formatter: \"default('—')\" },\n { key: 'geolocation.country_name', label: 'Country', formatter: \"default('—')\" },\n { key: 'last_seen', label: 'Last Seen', formatter: \"epoch|datetime\" }\n ],\n size: 5\n });\n\n const pushDevices = new PushDeviceList({\n params: {\n size: 5,\n user: this.model.get('id')\n }\n });\n this.pushDevicesView = new TableView({\n collection: pushDevices,\n hideActivePillNames: ['user'],\n columns: [\n { key: 'duid|truncate_middle(16)', label: 'Device ID', sortable: true },\n { key: 'device_info.user_agent.family', label: 'Browser', formatter: \"default('—')\" },\n { key: 'device_info.os.family', label: 'OS', formatter: \"default('—')\" },\n { key: 'first_seen', label: 'First Seen', formatter: \"epoch|datetime\" },\n { key: 'last_seen', label: 'Last Seen', formatter: \"epoch|datetime\" }\n ],\n size: 5\n });\n\n // Create Logs table with LogList collection\n const logsCollection = new LogList({\n params: {\n size: 5,\n model_name: \"account.User\",\n model_id: this.model.get('id')\n }\n });\n this.logsView = new TableView({\n collection: logsCollection,\n permissions: 'view_logs',\n hideActivePillNames: ['model_name', 'model_id'],\n columns: [\n {\n key: 'created',\n label: 'Timestamp',\n sortable: true,\n formatter: \"epoch|datetime\",\n filter: {\n name: \"created\",\n type: 'daterange',\n startName: 'dr_start',\n endName: 'dr_end',\n fieldName: 'dr_field',\n label: 'Date Range',\n format: 'YYYY-MM-DD',\n displayFormat: 'MMM DD, YYYY',\n separator: ' to '\n }\n },\n {\n key: 'level',\n label: 'Level',\n sortable: true,\n filter: {\n type: 'select',\n options: [\n { value: 'info', label: 'Info' },\n { value: 'warning', label: 'Warning' },\n { value: 'error', label: 'Error' }\n ]\n }\n },\n {\n key: 'kind',\n label: 'Kind',\n filter: {\n type: 'text'\n }\n },\n {\n name: 'log',\n label: 'Log',\n }\n ]\n });\n\n // Create Logs table with LogList collection\n const activityCollection = new LogList({\n params: {\n size: 5,\n uid: this.model.get('id')\n }\n });\n this.activityView = new TableView({\n collection: activityCollection,\n hideActivePillNames: ['uid'],\n permissions: 'view_logs',\n columns: [\n {\n key: 'created',\n label: 'Timestamp',\n sortable: true,\n formatter: \"epoch|datetime\",\n filter: {\n name: \"created\",\n type: 'daterange',\n startName: 'dr_start',\n endName: 'dr_end',\n fieldName: 'dr_field',\n label: 'Date Range',\n format: 'YYYY-MM-DD',\n displayFormat: 'MMM DD, YYYY',\n separator: ' to '\n }\n },\n {\n key: 'level',\n label: 'Level',\n sortable: true,\n filter: {\n type: 'select',\n options: [\n { value: 'info', label: 'Info' },\n { value: 'warning', label: 'Warning' },\n { value: 'error', label: 'Error' }\n ]\n }\n },\n {\n key: 'kind',\n label: 'Kind',\n filter: {\n type: 'text'\n }\n },\n {\n name: 'path',\n label: 'Path',\n }\n ]\n });\n\n // Create TabView with all tabs and responsive behavior\n this.tabView = new TabView({\n tabs: {\n 'Profile': this.profileView,\n 'Permissions': this.permsView,\n 'Groups': this.groupsView,\n 'Events': this.eventsView,\n 'Logs': this.logsView,\n 'Activity': this.activityView,\n \"Devices\": this.devicesView,\n \"Locations\": this.locationsView,\n \"Push Devices\": this.pushDevicesView\n },\n activeTab: 'Profile',\n containerId: 'user-tabs',\n enableResponsive: true,\n dropdownStyle: \"select\",\n minWidth: 300, // Switch to dropdown below 600px\n });\n\n // Add TabView as child\n this.addChild(this.tabView);\n\n const userMenu = new ContextMenu({\n containerId: 'user-context-menu',\n className: \"context-menu-view header-menu-absolute\",\n context: this.model, // Pass the user model as context\n config: {\n icon: 'bi-three-dots-vertical',\n items: [\n { label: 'Edit User', action: 'edit-user', icon: 'bi-pencil' },\n { label: 'Reset Password', action: 'reset-password', icon: 'bi-key' },\n { type: 'divider' },\n this.model.get('is_active')\n ? { label: 'Deactivate User', action: 'deactivate-user', icon: 'bi-person-dash' }\n : { label: 'Activate User', action: 'activate-user', icon: 'bi-person-check' },\n ]\n }\n });\n this.addChild(userMenu);\n }\n\n async onActionEditUser() {\n let frmConfig = UserForms.edit;\n const resp = await Dialog.showModelForm({\n title: `EDIT - #${this.model.id} ${this.options.modelName}`,\n model: this.model,\n formConfig: frmConfig,\n });\n if (resp) {\n this.render();\n }\n }\n async onActionResetPassword() { console.log(\"TODO: reset password\") }\n\n async onActionDeactivateUser() {\n const res = await Dialog.confirm(\"Are you sure you want to disable this user?\");\n if (res) {\n await this.model.save({ is_active: false });\n this.getApp().toast.success(\"Member disable\");\n } else {\n this.getApp().toast.error(\"Member disable failed\");\n }\n }\n\n async onActionActivateUser() {\n const res = await Dialog.confirm(\"Are you sure you want to enable this user?\");\n if (res) {\n await this.model.save({ is_active: true });\n this.getApp().toast.success(\"Member enabled\");\n } else {\n this.getApp().toast.error(\"Member enable failed\");\n }\n }\n\n // Action handlers for table interactions\n async onActionViewGroup(action, event, element) {\n const groupId = element.getAttribute('data-id');\n console.log('View group:', groupId);\n // TODO: Implement group view dialog\n }\n\n async onActionRemoveFromGroup(action, event, element) {\n const groupId = element.getAttribute('data-id');\n console.log('Remove user from group:', groupId);\n // TODO: Implement remove from group functionality\n }\n\n async onActionViewEvent(action, event, element) {\n const eventId = element.getAttribute('data-id');\n console.log('View event:', eventId);\n // TODO: Implement event view dialog\n }\n\n async onActionViewLog(action, event, element) {\n const logId = element.getAttribute('data-id');\n console.log('View log:', logId);\n // TODO: Implement log view dialog\n }\n\n async showTab(tabName) {\n if (this.tabView) {\n await this.tabView.showTab(tabName);\n }\n }\n\n getActiveTab() {\n return this.tabView ? this.tabView.getActiveTab() : null;\n }\n\n _onModelChange() {\n // do nothing, we do not want model changes to render this entire view\n }\n\n // Static factory method\n static create(options = {}) {\n return new UserView(options);\n }\n}\n\nUser.VIEW_CLASS = UserView;\n\nexport default UserView;\n","/**\n * UsersPage - Simple Todo List using TablePage component\n * Demonstrates clean usage of TablePage framework features\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport {UserList, UserForms} from '@core/models/User.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport MOJOUtils from '@core/utils/MOJOUtils.js';\nimport UserView from './UserView.js';\n\n\nclass UserTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_users',\n pageName: 'Manage Users',\n router: \"admin/users\",\n Collection: UserList,\n\n viewDialogOptions: { header: false },\n\n defaultQuery: {\n sort: '-last_activity',\n is_active: true\n },\n\n // Column definitions\n columns: [\n {\n key: 'id',\n label: 'ID',\n sortable: true,\n class: 'text-muted'\n },\n // {\n // label: 'Avatar',\n // key: 'avatar|avatar(\"sm\")',\n // sortable: false,\n // visibility: 'md'\n // },\n {\n key: 'display_name|tooltip:model.username',\n label: 'Display Name',\n },\n {\n label: 'Info',\n key: 'permissions.manage_users',\n template: `\n {{^model.is_active}}<span class=\"text-danger\">DISABLED</span> {{/model.is_active}}\n {{#model.permissions.manage_users}}{{{model.permissions.manage_users|yesnoicon('bi bi-person-gear text-danger')|tooltip('Manage Users')}}} {{/model.permissions.manage_users}}\n {{#model.permissions.manage_groups}}{{{model.permissions.manage_groups|yesnoicon('bi bi-building-gear text-primary')|tooltip('Manage Groups')}}} {{/model.permissions.manage_groups}}\n {{#model.permissions.view_global}}{{{model.permissions.view_global|yesnoicon('bi bi-globe text-secondary')|tooltip('View Global Menu')}}} {{/model.permissions.view_global}}\n {{#model.permissions.view_admin}}{{{model.permissions.view_admin|yesnoicon('bi bi-wrench text-secondary')|tooltip('View Admin Menu')}}} {{/model.permissions.view_admin}}\n `,\n sortable: false,\n },\n {\n key: 'email',\n label: 'Email',\n visibility: 'xl',\n className: 'text-muted fs-8',\n },\n // {\n // key: 'username',\n // label: 'Username',\n // visibility: 'xl',\n // className: 'text-muted fs-8',\n // },\n {\n key: 'last_activity',\n label: 'Last Activity',\n formatter: \"relative\",\n className: 'text-muted fs-8',\n }\n ],\n\n filters: [\n {\n key: 'is_active',\n label: 'Active',\n type: 'boolean',\n defaultValue: true,\n },\n {\n key: 'email',\n label: 'Email',\n type: 'text',\n defaultValue: '',\n },\n {\n key: 'username',\n label: 'Username',\n type: 'text',\n defaultValue: '',\n },\n {\n key: 'locations__ip_address',\n label: 'IP Address',\n type: 'text',\n defaultValue: '',\n },\n {\n key: 'last_activity',\n type: 'daterange',\n startName: 'dr_start',\n endName: 'dr_end',\n fieldName: 'dr_field',\n label: 'Date Range',\n format: 'YYYY-MM-DD',\n displayFormat: 'MMM DD, YYYY',\n separator: ' to '\n }\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // TablePage toolbar\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No users found. Click \"Add\" to create a new user.',\n\n // Context menu configuration\n contextMenu: [\n {\n icon: 'bi-pencil',\n action: 'edit',\n label: \"Edit Profile\"\n },\n {\n icon: 'bi-shield-check',\n action: 'edit-permissions',\n label: \"Edit Permissions\"\n },\n {\n icon: 'bi-shield',\n action: 'change-password',\n label: \"Change Password\",\n },\n { separator: true },\n {\n icon: 'bi-envelope',\n action: 'send-invite',\n label: \"Send Invite\"\n }\n ],\n\n // Table display options (for HTML table styling)\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n\n async onActionEditPermissions(event, element) {\n\n event.preventDefault();\n const item = this.collection.get(element.dataset.id);\n const result = await Dialog.showModelForm({\n model: item,\n size: 'lg',\n title: `Edit Permissions for \"${item._.username}\"`,\n fields: UserForms.permissions.fields\n });\n }\n\n async onActionChangePassword(event, element) {\n // Implement password change logic here\n const item = this.collection.get(element.dataset.id);\n const data = await Dialog.showForm({\n title: `Change Password for \"${item._.username}\"`,\n fields: [\n {\n type: 'text', // Change from 'hidden' to 'text'\n name: 'username',\n value: item.get('email') || item.get('username'),\n attributes: {\n autocomplete: 'username',\n readonly: 'readonly',\n tabindex: '-1',\n style: 'position: absolute; left: -9999px; opacity: 0; height: 0; width: 0;'\n }\n },\n {\n name: 'new_password',\n label: 'New Password',\n type: 'password',\n passwordUsage: 'new',\n required: true,\n showToggle: true,\n attributes: {\n autocomplete: 'new-password' // Make sure this isn't being overridden\n }\n }\n ]\n });\n\n if (data && data.new_password) {\n // Basic password validation\n const result = MOJOUtils.checkPasswordStrength(data.new_password);\n if (result.score < 5) {\n this.getApp().toast.error('Password must be at least 6 characters long and contain at least 2 of the following: uppercase letter, lowercase letter, or number');\n await this.onActionChangePassword(event, element);\n return;\n }\n const resp = await item.save({new_password: data.new_password});\n if (!this.onPasswordChange(resp)) {\n await this.onActionChangePassword(event, element);\n }\n }\n }\n\n onPasswordChange(resp) {\n if (resp.success) {\n this.getApp().toast.success('Password changed successfully');\n return true;\n } else {\n if (resp.data && resp.data.error) {\n this.getApp().toast.error(resp.data.error);\n } else {\n this.getApp().toast.error('Failed to change password');\n }\n }\n return false;\n }\n\n async onActionSendInvite(event, element) {\n const item = this.collection.get(element.dataset.id);\n const resp = await item.save({send_invite: true});\n if (resp.success) {\n this.getApp().toast.success('Invite sent successfully');\n return true;\n } else {\n if (resp.data && resp.data.error) {\n this.getApp().toast.error(resp.data.error);\n } else {\n this.getApp().toast.error('Failed to send invite');\n }\n }\n return false;\n }\n\n}\n\nexport default UserTablePage;\n","/**\n * MemberView - Comprehensive view for a group membership record\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport DataView from '@core/views/data/DataView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport { Member, MemberForms } from '@core/models/Member.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\n\nclass MemberView extends View {\n constructor(options = {}) {\n super({\n className: 'member-view',\n ...options\n });\n\n this.model = options.model || new Member(options.data || {});\n\n this.template = `\n <div class=\"member-view-container\">\n <!-- Header -->\n <div class=\"d-flex justify-content-between align-items-start mb-4\">\n <div class=\"d-flex align-items-center gap-3\">\n {{{model.user.avatar|avatar('md','rounded-circle')}}}\n <div>\n <h3 class=\"mb-0\">{{model.user.display_name}}</h3>\n <div class=\"text-muted\">Member of <strong>{{model.group.name}}</strong></div>\n </div>\n </div>\n\n <div class=\"d-flex align-items-start gap-4\">\n <div class=\"text-end\">\n <div>Role: <span class=\"badge bg-primary\">{{model.role|capitalize}}</span></div>\n <div class=\"text-muted small mt-1\">Status: {{model.status|capitalize}}</div>\n </div>\n <div data-container=\"member-context-menu\"></div>\n </div>\n </div>\n\n <!-- Tab Container -->\n <div data-container=\"member-tabs\"></div>\n </div>\n `;\n }\n\n async onInit() {\n // Overview Tab\n this.overviewView = new DataView({\n model: this.model,\n className: \"p-3\",\n columns: 2,\n fields: [\n { name: 'id', label: 'Membership ID' },\n { name: 'user.id', label: 'User ID' },\n { name: 'user.display_name', label: 'User Name' },\n { name: 'user.email', label: 'User Email' },\n { name: 'group.id', label: 'Group ID' },\n { name: 'group.name', label: 'Group Name' },\n { name: 'role', label: 'Role' },\n { name: 'status', label: 'Status' },\n { name: 'created', label: 'Date Joined', format: 'datetime' },\n ]\n });\n\n this.tabView = new TabView({\n containerId: 'member-tabs',\n tabs: {\n 'Overview': this.overviewView,\n },\n activeTab: 'Overview'\n });\n this.addChild(this.tabView);\n\n // ContextMenu\n const memberMenu = new ContextMenu({\n containerId: 'member-context-menu',\n className: \"context-menu-view header-menu-absolute\",\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: [\n { label: 'Edit Membership', action: 'edit-membership', icon: 'bi-pencil' },\n { type: 'divider' },\n { label: 'View User', action: 'view-user', icon: 'bi-person' },\n { label: 'View Group', action: 'view-group', icon: 'bi-diagram-3' },\n { type: 'divider' },\n { label: 'Remove From Group', action: 'remove-member', icon: 'bi-trash', danger: true }\n ]\n }\n });\n this.addChild(memberMenu);\n }\n\n async onActionEditMembership() {\n const resp = await Dialog.showModelForm({\n title: `Edit Membership`,\n model: this.model,\n formConfig: MemberForms.edit,\n });\n if (resp) {\n this.render();\n }\n }\n\n async onActionViewUser() {\n console.log(\"TODO: View user\", this.model.get('user.id'));\n }\n\n async onActionViewGroup() {\n console.log(\"TODO: View group\", this.model.get('group.id'));\n }\n\n async onActionRemoveMember() {\n const confirmed = await Dialog.confirm(\n `Are you sure you want to remove ${this.model.get('user.display_name')} from ${this.model.get('group.name')}?`,\n 'Confirm Removal'\n );\n if (confirmed) {\n const resp = await this.model.destroy();\n if (resp.success) {\n this.emit('member:removed', { model: this.model });\n }\n }\n }\n}\n\nMember.VIEW_CLASS = MemberView;\n\nexport default MemberView;\n","/**\n * MemberTablePage - Member management using TablePage component\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { MemberList, MemberForms } from '@core/models/Member.js';\nimport MemberView from './MemberView.js';\n\nclass MemberTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_members',\n pageName: 'Manage Members',\n router: \"admin/members\",\n Collection: MemberList,\n \n formEdit: MemberForms.edit,\n itemViewClass: MemberView,\n \n viewDialogOptions: {\n header: false,\n size: 'lg'\n },\n \n // Column definitions\n columns: [\n {\n key: 'id',\n label: 'ID',\n width: '60px',\n sortable: true,\n class: 'text-muted'\n },\n {\n key: 'user.display_name',\n label: 'User',\n formatter: \"default('Unknown User')\"\n },\n {\n key: 'user.email',\n label: 'Email',\n formatter: \"default('No Email')\"\n },\n {\n key: 'group.name',\n label: 'Group',\n formatter: \"default('Unknown Group')\"\n },\n {\n key: 'role',\n label: 'Role',\n formatter: \"badge\"\n },\n {\n key: 'status',\n label: 'Status',\n formatter: \"badge\"\n },\n {\n key: 'created',\n label: 'Added',\n formatter: \"epoch|datetime\"\n }\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No members found. Click \"Add Member\" to add users to groups.',\n\n // Batch actions\n batchBarLocation: 'top',\n batchActions: [\n { label: \"Remove\", icon: \"bi bi-person-dash\", action: \"batch-remove\" },\n { label: \"Export\", icon: \"bi bi-download\", action: \"batch-export\" },\n { label: \"Change Role\", icon: \"bi bi-person-gear\", action: \"batch-role\" },\n { label: \"Activate\", icon: \"bi bi-check-circle\", action: \"batch-activate\" },\n { label: \"Deactivate\", icon: \"bi bi-x-circle\", action: \"batch-deactivate\" }\n ],\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n}\n\nexport default MemberTablePage;","/**\n * GroupView - Comprehensive group management interface\n *\n * Features:\n * - Clean header with group name, kind, parent info, and activity\n * - Tabbed interface for Members, Child Groups, and Logs\n * - Integrated with Table and ContextMenu components\n * - Clean Bootstrap 5 styling\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport TableView from '@core/views/table/TableView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport { Group, GroupList, GroupForms } from '@core/models/Group.js';\nimport { MemberList } from '@core/models/Member.js';\nimport { LogList } from '@core/models/Log.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\n\nclass GroupView extends View {\n constructor(options = {}) {\n super({\n className: 'group-view',\n ...options\n });\n\n // Group model instance\n this.model = options.model || new Group(options.data || {});\n\n // Tab views\n this.tabView = null;\n this.membersView = null;\n this.childrenView = null;\n this.logsView = null;\n\n // Set template\n this.template = `\n <div class=\"group-view-container\">\n <!-- Group Header -->\n <div class=\"d-flex justify-content-between align-items-start mb-4\">\n <!-- Left Side: Primary Identity -->\n <div class=\"d-flex align-items-center gap-3\">\n {{#model.avatar}}\n {{{model.avatar|avatar('md','rounded')}}}\n {{/model.avatar}}\n {{^model.avatar}}\n <div class=\"avatar-placeholder rounded-circle bg-light d-flex align-items-center justify-content-center\" style=\"width: 80px; height: 80px;\">\n <i class=\"bi bi-collection text-secondary\" style=\"font-size: 40px;\"></i>\n </div>\n {{/model.avatar}}\n <div>\n <h3 class=\"mb-1\">{{model.name|truncate(32)|default('Unnamed Group')}}</h3>\n <div class=\"text-muted small\">\n <span>ID: {{model.id}}</span>\n <span class=\"mx-2\">|</span>\n <span>Kind: {{model.kind|capitalize}}</span>\n {{#model.metadata.timezone}}\n <span class=\"mx-2\">|</span>\n <span><i class=\"bi bi-clock\"></i> {{model.metadata.timezone}}</span>\n {{/model.metadata.timezone}}\n </div>\n {{#model.parent}}\n <div class=\"text-muted small mt-2\">\n <div>Parent: <a href=\"#\" data-action=\"view-parent\" data-id=\"{{model.parent.id}}\">{{model.parent.name|truncate(32)}}</a></div>\n <div>ID: {{model.parent.id}} | Kind: {{model.parent.kind|capitalize}}</div>\n </div>\n {{/model.parent}}\n </div>\n </div>\n\n <!-- Right Side: Status & Actions -->\n <div class=\"d-flex align-items-start gap-4\">\n <div class=\"text-end\">\n <div class=\"d-flex align-items-center gap-2\">\n <i class=\"bi bi-circle-fill fs-8 {{model.is_active|boolean('text-success','text-secondary')}}\"></i>\n <span>{{model.is_active|boolean('Active','Inactive')}}</span>\n </div>\n {{#model.last_activity}}\n <div class=\"text-muted small mt-1\">Last active {{model.last_activity|relative}}</div>\n {{/model.last_activity}}\n </div>\n <div data-container=\"group-context-menu\"></div>\n </div>\n </div>\n\n <!-- Tab Container -->\n <div data-container=\"group-tabs\"></div>\n </div>\n `;\n }\n\n async onInit() {\n // Create Members table\n const membersCollection = new MemberList({\n params: { group: this.model.get('id'), size: 5 }\n });\n this.membersView = new TableView({\n collection: membersCollection,\n hideActivePillNames: ['group'],\n columns: [\n { key: 'user.display_name', label: 'User', sortable: true },\n { key: 'user.email', label: 'Email', sortable: true },\n { key: 'created', label: 'Date Joined', formatter: 'date', sortable: true }\n ],\n showAdd: true,\n addButtonLabel: \"Invite\",\n onAdd: async (event) => {\n this.onInviteClick(event);\n },\n });\n\n // Create Children (sub-groups) table\n const childrenCollection = new GroupList({\n params: { parent: this.model.get('id'), size: 5 }\n });\n this.childrenView = new TableView({\n collection: childrenCollection,\n hideActivePillNames: ['parent'],\n columns: [\n { key: 'name', label: 'Name', sortable: true },\n { key: 'kind', label: 'Kind', formatter: 'badge' },\n { key: 'created', label: 'Created', formatter: 'date', sortable: true }\n ],\n\n toolbarButtons: [\n {\n label: 'Add Multiple',\n icon: 'bi bi-plus-circle',\n action: 'add-multiple',\n className: 'btn-success'\n }\n ],\n });\n\n // Create Logs table\n const logsCollection = new LogList({\n params: {\n size: 5,\n model_name: \"account.Group\",\n model_id: this.model.get('id')\n }\n });\n this.logsView = new TableView({\n collection: logsCollection,\n permissions: 'view_logs',\n hideActivePillNames: ['model_name', 'model_id'],\n columns: [\n { key: 'created', label: 'Timestamp', sortable: true, formatter: \"epoch|datetime\" },\n { key: 'level', label: 'Level', sortable: true, formatter: 'badge' },\n { key: 'kind', label: 'Kind' },\n { key: 'log', label: 'Log' }\n ]\n });\n\n // Create TabView\n this.tabView = new TabView({\n tabs: {\n 'Members': this.membersView,\n 'Children': this.childrenView,\n 'Logs': this.logsView\n },\n activeTab: 'Members',\n containerId: 'group-tabs'\n });\n this.addChild(this.tabView);\n\n // Create ContextMenu\n const groupMenu = new ContextMenu({\n containerId: 'group-context-menu',\n className: \"context-menu-view header-menu-absolute\",\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: [\n { label: 'Edit Group', action: 'edit-group', icon: 'bi-pencil' },\n { label: 'Add Member', action: 'add-member', icon: 'bi-person-plus' },\n { label: 'Add Child Group', action: 'add-child-group', icon: 'bi-diagram-3' },\n { type: 'divider' },\n this.model.get('is_active')\n ? { label: 'Deactivate Group', action: 'deactivate-group', icon: 'bi-x-circle' }\n : { label: 'Activate Group', action: 'activate-group', icon: 'bi-check-circle' },\n ]\n }\n });\n this.addChild(groupMenu);\n }\n\n async onActionEditGroup() {\n const resp = await Dialog.showModelForm({\n title: `Edit Group - ${this.model.get('name')}`,\n model: this.model,\n size: 'lg',\n formConfig: GroupForms.detailed,\n });\n if (resp) {\n this.render(); // Re-render to show updated data in header\n }\n }\n\n // TODO: Implement other context menu actions\n async onActionAddMember() { console.log(\"TODO: Add member to group\", this.model.id); }\n async onActionAddChildGroup() { console.log(\"TODO: Add child group to\", this.model.id); }\n async onActionDeactivateGroup() { console.log(\"TODO: Deactivate group\", this.model.id); }\n async onActionActivateGroup() { console.log(\"TODO: Activate group\", this.model.id); }\n\n async onActionViewParent(event, element) {\n const parentId = element.dataset.id;\n console.log(\"TODO: View parent group\", parentId);\n // This would typically involve closing the current dialog and opening a new one for the parent.\n // The parent TablePage would need to handle this event.\n this.emit('view-parent-group', { groupId: parentId });\n }\n\n async onInviteClick(event) {\n event.preventDefault();\n event.stopPropagation();\n const app = this.getApp();\n const data = await app.showForm({\n title: \"Invite User To \" + this.model.get(\"name\"),\n fields: [\n {\n type: 'email',\n name: 'email',\n label: 'Email',\n required: true,\n columns: 12\n }\n ]\n });\n\n if (data && data.email) {\n app.showLoading();\n const resp = await app.rest.POST('/api/group/member/invite', {\n group: this.model.id,\n email: data.email\n });\n app.hideLoading();\n if (resp.success) {\n app.toast.success(\"User invited successfully\");\n this.membersView.collection.fetch();\n } else {\n app.toast.error(\"Failed to invite user\");\n }\n }\n }\n\n\n async onActionAddMultiple() {\n const data = await Dialog.showForm({\n title: \"Select Members\",\n fields: [\n {\n name: 'group_ids',\n type: 'collectionmultiselect',\n required: true,\n Collection: GroupList,\n labelField: 'name',\n itemTemplate: `\n <div class=\"ms-2\">\n <div class=\"fs-7\">{{model.name}}</div>\n <div class=\"fs-8 text-muted\">{{model.kind}}</div>\n </div>\n `,\n valueField: 'id',\n enableSearch: true,\n searchPlaceholder: 'Search groups...',\n defaultParams: {\n is_active: true,\n size: 100,\n group: this.model.id\n }\n },\n ]\n });\n if (data) {\n console.warn(data);\n this.getApp().toast.warning(\"This is only for testing\");\n }\n\n }\n\n}\n\nGroup.VIEW_CLASS = GroupView;\n\nexport default GroupView;\n","/**\n * GroupTablePage - Group management using TablePage component\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { GroupList, GroupForms, Group } from '@core/models/Group.js';\nimport GroupView from './GroupView.js';\n\nclass GroupTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_groups',\n pageName: 'Manage Groups',\n router: \"admin/groups\",\n Collection: GroupList,\n\n formCreate: GroupForms.create,\n formEdit: GroupForms.edit,\n itemViewClass: GroupView,\n\n viewDialogOptions: {\n header: false\n },\n\n defaultQuery: {\n sort: '-id',\n is_active: 1,\n },\n\n // Column definitions\n columns: [\n {\n key: 'id',\n label: 'ID',\n sortable: true,\n class: 'text-muted'\n },\n\n {\n key: 'name',\n label: 'Display Name'\n },\n {\n key: 'kind|badge',\n label: 'Kind',\n filter: {\n type: \"select\",\n options: Group.GroupKindOptions\n }\n },\n {\n key: 'is_active|yesnoicon',\n label: 'Enabled',\n visibility: 'lg'\n },\n {\n key: 'parent.name',\n label: 'Parent',\n formatter: \"default('-')\",\n visibility: 'md',\n class: 'text-muted fs-8'\n },\n {\n key: 'created',\n label: 'Created',\n className: 'text-muted fs-8',\n formatter: \"epoch|datetime\",\n visibility: 'lg'\n },\n {\n key: 'last_activity',\n label: 'Activity',\n className: 'text-muted fs-8',\n formatter: \"relative\",\n visibility: 'lg'\n }\n ],\n\n filters: [\n {\n key: 'is_active',\n label: 'Active',\n type: 'select',\n options: [\n { label: 'Active', value: true },\n { label: 'Inactive', value: false }\n ]\n }\n ],\n\n contextMenu: [\n {\n icon: 'bi-pencil',\n action: 'edit',\n label: \"Edit Group\"\n },\n {\n icon: 'bi-bullseye',\n action: 'make-active',\n label: \"Make Active Group\"\n }\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No groups found. Click \"Add Group\" to create your first one.',\n\n // Batch actions\n batchBarLocation: 'top',\n batchActions: [\n { label: \"Delete\", icon: \"bi bi-trash\", action: \"batch-delete\" },\n { label: \"Export\", icon: \"bi bi-download\", action: \"batch-export\" },\n { label: \"Activate\", icon: \"bi bi-check-circle\", action: \"batch-activate\" },\n { label: \"Deactivate\", icon: \"bi bi-x-circle\", action: \"batch-deactivate\" },\n { label: \"Move\", icon: \"bi bi-arrow-right\", action: \"batch-move\" }\n ],\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n },\n });\n }\n\n onActionMakeActive(event, element) {\n const item = this.collection.get(element.dataset.id);\n this.getApp().setActiveGroup(item);\n }\n\n}\n\nexport default GroupTablePage;\n","/**\n * DeviceView - Comprehensive user device management interface\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport TableView from '@core/views/table/TableView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport { UserDevice, UserDeviceLocationList } from '@core/models/User.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\n\nclass DeviceView extends View {\n constructor(options = {}) {\n super({\n className: 'device-view',\n ...options\n });\n\n this.model = options.model || new UserDevice(options.data || {});\n this.deviceInfo = this.model.get('device_info') || {};\n this.deviceIcon = this.getIconForDevice(this.deviceInfo);\n\n this.template = `\n <div class=\"device-view-container\">\n <!-- Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-4\">\n <!-- Left Side: Icon & Info -->\n <div class=\"d-flex align-items-center gap-3\">\n <div class=\"fs-1 text-primary\">\n <i class=\"bi {{deviceIcon}}\"></i>\n </div>\n <div>\n <h3 class=\"mb-1\">\n {{deviceInfo.user_agent.family}} on {{deviceInfo.os.family}}\n </h3>\n <div class=\"text-muted small\">\n DUID: {{model.duid|truncate_middle(32)}}\n </div>\n <div class=\"text-muted small mt-1\">\n User: <a href=\"#\" data-action=\"view-user\">{{model.user.display_name}}</a>\n </div>\n </div>\n </div>\n\n <!-- Right Side: Status & Actions -->\n <div class=\"d-flex align-items-center gap-4\">\n <div class=\"text-end\">\n <div class=\"text-muted small\">Last Seen</div>\n <div>{{model.last_seen|relative}}</div>\n <div class=\"text-muted small\">from {{model.last_ip}}</div>\n </div>\n <div data-container=\"device-context-menu\"></div>\n </div>\n </div>\n\n <!-- Tab Container -->\n <div data-container=\"device-tabs\"></div>\n </div>\n `;\n }\n\n getIconForDevice(deviceInfo) {\n const os = deviceInfo?.os?.family?.toLowerCase() || '';\n const browser = deviceInfo?.user_agent?.family?.toLowerCase() || '';\n const device = deviceInfo?.device?.family?.toLowerCase() || '';\n\n if (browser.includes('chrome')) return 'bi-browser-chrome';\n if (browser.includes('firefox')) return 'bi-browser-firefox';\n if (browser.includes('safari')) return 'bi-browser-safari';\n if (browser.includes('edge')) return 'bi-browser-edge';\n\n if (os.includes('mac') || os.includes('ios')) return 'bi-apple';\n if (os.includes('windows')) return 'bi-windows';\n if (os.includes('android')) return 'bi-android2';\n if (os.includes('linux')) return 'bi-ubuntu';\n\n if (device.includes('iphone')) return 'bi-phone';\n if (device.includes('ipad')) return 'bi-tablet';\n\n return 'bi-laptop'; // Default icon\n }\n\n async onInit() {\n // Info Tab\n this.infoView = new View({\n model: this.model,\n className: \"p-3\",\n template: `\n <div class=\"list-group\">\n <div class=\"list-group-item\">\n <div class=\"d-flex w-100 justify-content-between\">\n <h6 class=\"mb-1 text-muted\">Browser</h6>\n </div>\n <p class=\"mb-1 fs-5\">{{model.device_info.user_agent.family}} {{model.device_info.user_agent.major}}</p>\n </div>\n <div class=\"list-group-item\">\n <div class=\"d-flex w-100 justify-content-between\">\n <h6 class=\"mb-1 text-muted\">Operating System</h6>\n </div>\n <p class=\"mb-1 fs-5\">{{model.device_info.os.family}} {{model.device_info.os.major}}.{{model.device_info.os.minor}}</p>\n </div>\n <div class=\"list-group-item\">\n <div class=\"d-flex w-100 justify-content-between\">\n <h6 class=\"mb-1 text-muted\">Device</h6>\n </div>\n <p class=\"mb-1 fs-5\">{{model.device_info.device.brand}} {{model.device_info.device.model}}</p>\n </div>\n <div class=\"list-group-item\">\n <div class=\"d-flex w-100 justify-content-between\">\n <h6 class=\"mb-1 text-muted\">Full User Agent</h6>\n </div>\n <small class=\"text-muted\" style=\"word-break: break-all;\">{{model.device_info.string}}</small>\n </div>\n </div>\n `\n });\n\n // Locations Tab\n const locationsCollection = new UserDeviceLocationList({\n params: { user_device: this.model.get('id'), size: 10 }\n });\n this.locationsView = new TableView({\n collection: locationsCollection,\n hideActivePillNames: ['user_device'],\n columns: [\n { key: 'ip_address', label: 'IP Address', sortable: true },\n { key: 'geolocation.city', label: 'City', formatter: \"default('—')\" },\n { key: 'geolocation.region', label: 'Region', formatter: \"default('—')\" },\n { key: 'geolocation.country_name', label: 'Country', formatter: \"default('—')\" },\n { key: 'first_seen', label: 'First Seen', formatter: 'datetime' },\n { key: 'last_seen', label: 'Last Seen', formatter: 'datetime' },\n ]\n });\n\n // TabView\n this.tabView = new TabView({\n tabs: {\n 'Info': this.infoView,\n 'Locations': this.locationsView,\n },\n activeTab: 'Info',\n containerId: 'device-tabs'\n });\n this.addChild(this.tabView);\n\n // ContextMenu\n const deviceMenu = new ContextMenu({\n containerId: 'device-context-menu',\n className: \"context-menu-view header-menu-absolute\",\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: [\n { label: 'View User', action: 'view-user', icon: 'bi-person' },\n { label: 'Block Device', action: 'block-device', icon: 'bi-shield-slash', disabled: true },\n { type: 'divider' },\n { label: 'Delete Record', action: 'delete-device', icon: 'bi-trash', danger: true }\n ]\n }\n });\n this.addChild(deviceMenu);\n }\n\n async onActionViewUser() {\n // This would typically navigate to the user view or open it in a new dialog\n console.log(\"View user:\", this.model.get('user'));\n this.emit('view-user', { userId: this.model.get('user')?.id });\n }\n\n async onActionDeleteDevice() {\n // Placeholder for delete logic\n console.log(\"Delete device:\", this.model.id);\n }\n\n static async show(duid) {\n const model = await UserDevice.getByDuid(duid);\n if (model) {\n const view = new DeviceView({ model });\n const dialog = new Dialog({\n header: false,\n size: 'lg',\n body: view,\n buttons: [{ text: 'Close', class: 'btn-secondary', dismiss: true }]\n });\n await dialog.render(true, document.body);\n dialog.show();\n return dialog;\n }\n Dialog.alert({ message: `Could not find device with DUID: ${duid}`, type: 'warning' });\n return null;\n }\n}\n\nUserDevice.VIEW_CLASS = DeviceView;\nexport default DeviceView;\n","/**\n * UserDeviceTablePage - User device management using TablePage component\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { UserDeviceList } from '@core/models/User.js';\nimport DeviceView from './DeviceView.js';\n\nclass UserDeviceTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_user_devices',\n pageName: 'User Devices',\n router: \"admin/user/devices\",\n Collection: UserDeviceList,\n \n itemViewClass: DeviceView,\n viewDialogOptions: {\n header: false,\n size: 'lg'\n },\n\n // Column definitions\n columns: [\n { key: 'id', label: 'ID', width: '70px', sortable: true, class: 'text-muted' },\n { key: 'duid', label: 'Device ID', sortable: true, formatter: 'truncate_middle(16)' },\n { key: 'user.display_name', label: 'User', sortable: true, formatter: \"default('—')\" },\n { key: 'device_info.user_agent.family', label: 'Browser', formatter: \"default('—')\" },\n { key: 'device_info.os.family', label: 'OS', formatter: \"default('—')\" },\n { key: 'last_ip', label: 'Last IP', sortable: true },\n { key: 'first_seen', label: 'First Seen', formatter: \"epoch|datetime\" },\n { key: 'last_seen', label: 'Last Seen', formatter: \"epoch|datetime\" }\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: false,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No user devices found.',\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n}\n\nexport default UserDeviceTablePage;","/**\n * UserDeviceLocationTablePage - Device location tracking using TablePage component\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { UserDeviceLocationList } from '@core/models/User.js';\n\nclass UserDeviceLocationTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_user_device_locations',\n pageName: 'Device Locations',\n router: \"admin/user/device-locations\",\n Collection: UserDeviceLocationList,\n\n // Column definitions\n columns: [\n { key: 'id', label: 'ID', width: '70px', sortable: true, class: 'text-muted' },\n { key: 'user.display_name', label: 'User', sortable: true },\n { key: 'user_device', label: 'Device', template: '{{user_device.device_info.user_agent.family}} on {{user_device.device_info.os.family}}', sortable: true },\n { key: 'ip_address', label: 'IP Address', sortable: true },\n { key: 'geolocation.city', label: 'City', formatter: \"default('—')\" },\n { key: 'geolocation.region', label: 'Region', formatter: \"default('—')\" },\n { key: 'geolocation.country_name', label: 'Country', formatter: \"default('—')\" },\n { key: 'last_seen', label: 'Last Seen', formatter: \"epoch|datetime\" }\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: false,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No device locations found.',\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n}\n\nexport default UserDeviceLocationTablePage;","/**\n * GeoIPView - Detailed view for a GeoLocatedIP record\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport DataView from '@core/views/data/DataView.js';\nimport TableView from '@core/views/table/TableView.js';\nimport MapView from '@ext/map/MapView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport { GeoLocatedIP } from '@core/models/System.js';\nimport { IncidentEventList } from '@core/models/Incident.js';\nimport { LogList } from '@core/models/Log.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\n\nclass GeoIPView extends View {\n constructor(options = {}) {\n super({\n className: 'geoip-view',\n ...options\n });\n\n this.model = options.model || new GeoLocatedIP(options.data || {});\n this.hasCoordinates = this.model.get('latitude') && this.model.get('longitude');\n\n this.template = `\n <div class=\"geoip-view-container\">\n <!-- Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-4\">\n <!-- Left Side: Icon & Info -->\n <div class=\"d-flex align-items-center gap-3\">\n <div class=\"fs-1 text-primary\">\n <i class=\"bi bi-globe-americas\"></i>\n </div>\n <div>\n <h3 class=\"mb-1\">{{model.ip_address}}</h3>\n <div class=\"text-muted small\">\n {{model.city|default('Unknown Location')}}, {{model.country_name|default('Unknown Location')}}\n </div>\n <div class=\"text-muted small mt-1\">\n ISP: {{model.isp|capitalize}}\n </div>\n </div>\n </div>\n\n <!-- Right Side: Risk Summary + Actions -->\n <div class=\"d-flex align-items-start gap-4\">\n <!-- Risk summary -->\n <div class=\"text-end\">\n <div class=\"d-flex align-items-baseline justify-content-end gap-2\">\n <span class=\"text-muted\">Risk:</span>\n <span class=\"fw-bold fs-4\n {{#model.is_threat}} text-danger {{/model.is_threat}}\n {{#model.is_suspicious}} text-warning {{/model.is_suspicious}}\n {{^model.is_threat}}{{^model.is_suspicious}} text-success {{/model.is_suspicious}}{{/model.is_threat}}\n \">{{#model.threat_level}}{{model.threat_level|capitalize}}{{/model.threat_level}}{{^model.threat_level}}Unknown{{/model.threat_level}}</span>\n </div>\n <div class=\"mt-1 small d-flex align-items-center justify-content-end gap-2\">\n <span class=\"text-muted\">Score:</span>\n <span class=\"fw-semibold\">{{model.risk_score|default('—')}}</span>\n </div>\n <div class=\"mt-1 d-flex align-items-center justify-content-end gap-2\">\n <i class=\"bi bi-shield-lock {{#model.is_tor}}fs-4 text-success{{/model.is_tor}}{{^model.is_tor}}text-muted{{/model.is_tor}}\" data-bs-toggle=\"tooltip\" title=\"TOR exit\"></i>\n <i class=\"bi bi-shield {{#model.is_vpn}}fs-4 text-success{{/model.is_vpn}}{{^model.is_vpn}}text-muted{{/model.is_vpn}}\" data-bs-toggle=\"tooltip\" title=\"VPN detected\"></i>\n <i class=\"bi bi-cloud {{#model.is_cloud}}fs-4 text-success{{/model.is_cloud}}{{^model.is_cloud}}text-muted{{/model.is_cloud}}\" data-bs-toggle=\"tooltip\" title=\"Cloud provider\"></i>\n <i class=\"bi bi-hdd-stack {{#model.is_datacenter}}fs-4 text-success{{/model.is_datacenter}}{{^model.is_datacenter}}text-muted{{/model.is_datacenter}}\" data-bs-toggle=\"tooltip\" title=\"Datacenter\"></i>\n <i class=\"bi bi-phone {{#model.is_mobile}}fs-4 text-success{{/model.is_mobile}}{{^model.is_mobile}}text-muted{{/model.is_mobile}}\" data-bs-toggle=\"tooltip\" title=\"Mobile connection\"></i>\n <i class=\"bi bi-diagram-3 {{#model.is_proxy}}fs-4 text-success{{/model.is_proxy}}{{^model.is_proxy}}text-muted{{/model.is_proxy}}\" data-bs-toggle=\"tooltip\" title=\"Proxy\"></i>\n </div>\n </div>\n <!-- Actions: context menu aligned to top (not vertically centered) -->\n <div class=\"d-flex align-items-start\">\n <div data-container=\"geoip-context-menu\"></div>\n </div>\n </div>\n </div>\n\n <!-- Tabs -->\n <div data-container=\"geoip-tabs\"></div>\n </div>\n `;\n }\n\n async onInit() {\n // Location Details Tab\n this.detailsView = new DataView({\n model: this.model,\n className: \"p-3\",\n showEmptyValues: true,\n emptyValueText: '—',\n columns: 2,\n fields: [\n { name: 'ip_address', label: 'IP Address', cols: 4 },\n { name: 'subnet', label: 'Subnet', cols: 4 },\n { name: 'country_name', label: 'Country', cols: 4 },\n { name: 'country_code', label: 'Country Code', cols: 4 },\n { name: 'region', label: 'Region', cols: 4 },\n { name: 'city', label: 'City', cols: 4 },\n { name: 'postal_code', label: 'Postal Code', cols: 4 },\n { name: 'timezone', label: 'Timezone', cols: 4 },\n { name: 'latitude', label: 'Latitude', cols: 4 },\n { name: 'longitude', label: 'Longitude', cols: 4 },\n ]\n });\n\n // Network Tab\n this.networkView = new DataView({\n model: this.model,\n className: \"p-3\",\n showEmptyValues: true,\n emptyValueText: '—',\n columns: 2,\n fields: [\n { name: 'is_tor', label: 'TOR Exit Node', formatter: 'yesnoicon', cols: 4 },\n { name: 'is_vpn', label: 'VPN', formatter: 'yesnoicon', cols: 4 },\n { name: 'is_proxy', label: 'Proxy', formatter: 'yesnoicon', cols: 4 },\n { name: 'is_cloud', label: 'Cloud Provider', formatter: 'yesnoicon', cols: 4 },\n { name: 'is_datacenter', label: 'Datacenter', formatter: 'yesnoicon', cols: 4 },\n { name: 'is_mobile', label: 'Mobile', formatter: 'yesnoicon', cols: 4 },\n { name: 'mobile_carrier', label: 'Mobile Carrier', cols: 8 },\n { name: 'asn', label: 'ASN', cols: 4 },\n { name: 'asn_org', label: 'ASN Organization', cols: 8 },\n { name: 'isp', label: 'ISP', cols: 12 },\n { name: 'connection_type', label: 'Connection Type', cols: 6 }\n ]\n });\n\n // Risk & Reputation Tab\n this.riskView = new DataView({\n model: this.model,\n className: \"p-3\",\n showEmptyValues: true,\n emptyValueText: '—',\n columns: 2,\n fields: [\n { name: 'threat_level', label: 'Threat Level', cols: 6 },\n { name: 'risk_score', label: 'Risk Score', cols: 6 },\n { name: 'is_threat', label: 'Threat', formatter: 'yesnoicon', cols: 6 },\n { name: 'is_suspicious', label: 'Suspicious', formatter: 'yesnoicon', cols: 6 },\n { name: 'is_known_attacker', label: 'Known Attacker', formatter: 'yesnoicon', cols: 6 },\n { name: 'is_known_abuser', label: 'Known Abuser', formatter: 'yesnoicon', cols: 6 }\n ]\n });\n\n // Metadata Tab\n this.metadataView = new DataView({\n model: this.model,\n className: \"p-3\",\n showEmptyValues: true,\n emptyValueText: '—',\n columns: 2,\n fields: [\n { name: 'id', label: 'Record ID', cols: 6 },\n { name: 'provider', label: 'Data Provider', formatter: 'capitalize', cols: 6 },\n { name: 'created', label: 'Created', formatter: 'datetime', cols: 6 },\n { name: 'modified', label: 'Last Modified', formatter: 'datetime', cols: 6 },\n { name: 'last_seen', label: 'Last Seen', formatter: 'datetime', cols: 6 },\n { name: 'expires_at', label: 'Expires', formatter: 'datetime', cols: 6 }\n ]\n });\n\n // Create Events table with IncidentEventList collection\n const eventsCollection = new IncidentEventList({\n params: {\n size: 5,\n source_ip: this.model.get(\"ip_address\")\n }\n });\n this.eventsView = new TableView({\n collection: eventsCollection,\n hideActivePillNames: ['source_ip'],\n columns: [\n { key: 'id', label: 'ID', sortable: true, width: '40px' },\n { key: 'created', label: 'Date', formatter: 'datetime', sortable: true, width: '150px' },\n { key: 'category|badge', label: 'Category' },\n { key: 'title', label: 'Title' }\n ]\n });\n\n // Create Logs table with LogList collection\n const logsCollection = new LogList({\n params: {\n size: 5,\n ip: this.model.get('ip_address')\n }\n });\n this.logsView = new TableView({\n collection: logsCollection,\n permissions: 'view_logs',\n hideActivePillNames: ['ip'],\n columns: [\n {\n key: 'created',\n label: 'Timestamp',\n sortable: true,\n formatter: \"epoch|datetime\",\n filter: {\n name: \"created\",\n type: 'daterange',\n startName: 'dr_start',\n endName: 'dr_end',\n fieldName: 'dr_field',\n label: 'Date Range',\n format: 'YYYY-MM-DD',\n displayFormat: 'MMM DD, YYYY',\n separator: ' to '\n }\n },\n {\n key: 'level',\n label: 'Level',\n sortable: true,\n filter: {\n type: 'select',\n options: [\n { value: 'info', label: 'Info' },\n { value: 'warning', label: 'Warning' },\n { value: 'error', label: 'Error' }\n ]\n }\n },\n { key: 'kind', label: 'Kind', filter: { type: 'text' } },\n { name: 'log', label: 'Log' }\n ]\n });\n\n const tabs = {\n 'Location': this.detailsView,\n 'Network': this.networkView,\n 'Risk & Reputation': this.riskView,\n 'Events': this.eventsView,\n 'Logs': this.logsView,\n 'Metadata': this.metadataView\n };\n\n // Add Map tab if coordinates exist\n if (this.hasCoordinates) {\n const lat = this.model.get('latitude');\n const lng = this.model.get('longitude');\n const city = this.model.get('city') || 'Unknown';\n const region = this.model.get('region') || '';\n const country = this.model.get('country_name') || '';\n\n const locationStr = [city, region, country].filter(Boolean).join(', ');\n\n this.mapView = new MapView({\n markers: [{\n lat: lat,\n lng: lng,\n popup: `<strong>${this.model.get('ip_address')}</strong><br>${locationStr}`\n }],\n tileLayer: \"light\",\n zoom: 4,\n height: 450\n });\n tabs['Map'] = this.mapView;\n }\n\n this.tabView = new TabView({\n containerId: 'geoip-tabs',\n tabs: tabs,\n activeTab: this.hasCoordinates ? 'Map' : 'Location'\n });\n this.addChild(this.tabView);\n\n // ContextMenu\n const menuItems = [\n { label: 'Edit Location', action: 'edit-location', icon: 'bi-geo-alt' },\n { label: 'Edit Security', action: 'edit-security', icon: 'bi-shield-lock' },\n { label: 'Edit Network', action: 'edit-network', icon: 'bi-diagram-3' },\n { type: 'divider' },\n { label: 'Refresh Geolocation', action: 'refresh-geoip', icon: 'bi-arrow-clockwise' },\n ];\n\n if (this.hasCoordinates) {\n menuItems.push({\n label: 'View on Map',\n action: 'view-on-map',\n icon: 'bi-map'\n });\n }\n\n menuItems.push(\n { type: 'divider' },\n { label: 'Delete Record', action: 'delete-geoip', icon: 'bi-trash', danger: true }\n );\n\n const geoIPMenu = new ContextMenu({\n containerId: 'geoip-context-menu',\n className: \"context-menu-view header-menu-absolute\",\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: menuItems\n }\n });\n this.addChild(geoIPMenu);\n }\n\n async onAfterRender() {\n await super.onAfterRender();\n\n // Initialize Bootstrap tooltips for header icons/badges\n if (window.bootstrap && window.bootstrap.Tooltip && this.element) {\n const tooltipTriggerList = this.element.querySelectorAll('[data-bs-toggle=\"tooltip\"]');\n tooltipTriggerList.forEach(el => {\n // Dispose any existing instance (in case of re-render)\n const existing = window.bootstrap.Tooltip.getInstance(el);\n if (existing && typeof existing.dispose === 'function') {\n existing.dispose();\n }\n new window.bootstrap.Tooltip(el);\n });\n }\n }\n\n async onActionEditLocation() {\n const resp = await Dialog.showModelForm({\n title: `Edit Location - ${this.model.get('ip_address')}`,\n model: this.model,\n formConfig: GeoLocatedIP.EDIT_LOCATION_FORM,\n });\n\n if (resp) {\n await this.render();\n this.getApp()?.toast?.success('Location updated successfully');\n }\n }\n\n async onActionEditSecurity() {\n const resp = await Dialog.showModelForm({\n title: `Edit Security - ${this.model.get('ip_address')}`,\n model: this.model,\n formConfig: GeoLocatedIP.EDIT_SECURITY_FORM,\n });\n\n if (resp) {\n await this.render();\n this.getApp()?.toast?.success('Security settings updated successfully');\n }\n }\n\n async onActionEditNetwork() {\n const resp = await Dialog.showModelForm({\n title: `Edit Network - ${this.model.get('ip_address')}`,\n model: this.model,\n formConfig: GeoLocatedIP.EDIT_NETWORK_FORM,\n });\n\n if (resp) {\n await this.render();\n this.getApp()?.toast?.success('Network information updated successfully');\n }\n }\n\n async onActionRefreshGeoip() {\n // Placeholder for refresh logic, e.g., a POST request to a refresh endpoint\n await this.model.save({ refresh: true });\n this.getApp()?.toast?.info('Refresh request sent for ' + this.model.get('ip_address'));\n }\n\n async onActionThreatAnalysis() {\n // Placeholder for refresh logic, e.g., a POST request to a refresh endpoint\n await this.model.save({ threat_analysis: true });\n this.getApp()?.toast?.info('Requesting threat analysis for ' + this.model.get('ip_address'));\n }\n\n async onActionViewOnMap() {\n if (this.hasCoordinates) {\n const lat = this.model.get('latitude');\n const lon = this.model.get('longitude');\n const url = `https://www.google.com/maps/search/?api=1&query=${lat},${lon}`;\n window.open(url, '_blank');\n }\n }\n\n async onActionDeleteGeoip() {\n const confirmed = await Dialog.confirm(\n `Are you sure you want to delete the GeoIP record for \"${this.model.get('ip_address')}\"?`,\n 'Confirm Deletion',\n { confirmClass: 'btn-danger', confirmText: 'Delete' }\n );\n if (confirmed) {\n const resp = await this.model.destroy();\n if (resp.success) {\n this.emit('geoip:deleted', { model: this.model });\n }\n }\n }\n\n static async show(ip) {\n const model = await GeoLocatedIP.lookup(ip);\n if (model) {\n const view = new GeoIPView({ model });\n const dialog = new Dialog({\n header: false,\n size: 'lg',\n body: view,\n buttons: [{ text: 'Close', class: 'btn-secondary', dismiss: true }]\n });\n await dialog.render(true, document.body);\n dialog.show();\n return dialog;\n }\n Dialog.alert({ message: `Could not find geolocation data for IP: ${ip}`, type: 'warning' });\n return null;\n }\n}\n\nGeoLocatedIP.VIEW_CLASS = GeoIPView;\n\nexport default GeoIPView;\n","/**\n * GeoLocatedIPTablePage - GeoIP cache management using TablePage component\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { GeoLocatedIPList, GeoLocatedIP } from '@core/models/System.js';\nimport GeoIPView from './GeoIPView.js';\n\nclass GeoLocatedIPTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_system_geoip',\n pageName: 'GeoIP Cache',\n router: \"admin/system/geoip\",\n Collection: GeoLocatedIPList,\n\n itemView: GeoIPView,\n viewDialogOptions: {\n header: false,\n size: 'xl'\n },\n\n // Column definitions\n columns: [\n { key: 'ip_address', label: 'IP Address', sortable: true },\n { key: 'city', label: 'City', sortable: true, formatter: \"default('—')\" },\n { key: 'region', label: 'Region', sortable: true, formatter: \"default('—')\" },\n { key: 'country_name', label: 'Country', sortable: true, formatter: \"default('—')\" },\n { key: 'isp', label: 'ISP', sortable: true, formatter: \"default('—')\" },\n { key: 'threat_level', label: 'Threat', formatter: \"default('—')\"}\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Actions\n // actions: ['view', 'edit', 'delete'],\n clickAction: 'view',\n\n // Toolbar\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No GeoIP records found.',\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n },\n tableViewOptions: {\n addButtonLabel: \"Lookup IP\",\n onAdd: (evt) => {\n evt.preventDefault();\n // Implement the logic for adding a new record\n this.onLookup();\n }\n }\n });\n }\n\n async onLookup() {\n // Implement the logic for adding a new record\n const data = await this.getApp().showForm({\n title: \"Lookup IP\",\n fields: [\n {\n name: 'ip',\n type: 'text',\n required: true\n }\n ]\n });\n if (data && data.ip) {\n const model = await GeoLocatedIP.lookup(data.ip);\n if (model) {\n this.tableView._onRowView({ model });\n }\n }\n }\n}\n\nexport default GeoLocatedIPTablePage;\n","/**\n * MapLibreView - Interactive vector map component using MapLibre GL JS\n * \n * MapLibre GL is better for:\n * - Vector tiles and custom styling\n * - 3D terrain and buildings\n * - High-performance rendering\n * - Heat maps and data visualizations\n * - Complex animations\n * \n * Use MapView (Leaflet) for simple marker maps.\n * Use MapLibreView for advanced visualizations.\n * \n * @example\n * const mapView = new MapLibreView({\n * markers: [\n * { lng: -122.084, lat: 37.422, popup: 'Mountain View, CA' }\n * ],\n * style: 'streets', // 'streets', 'satellite', 'dark', 'light'\n * zoom: 10,\n * height: 400\n * });\n */\n\nimport View from '@core/View.js';\n\nclass MapLibreView extends View {\n constructor(options = {}) {\n super({\n className: 'maplibre-view',\n ...options\n });\n\n this.markers = options.markers || [];\n this.center = options.center || null;\n this.zoom = options.zoom || 13;\n this.height = options.height || 400;\n this.pitch = options.pitch || 0; // 0-60 degrees (3D tilt)\n this.bearing = options.bearing || 0; // 0-360 degrees (rotation)\n this.mapStyle = options.style || 'streets'; // 'streets', 'satellite', 'dark', 'light', 'terrain'\n this.showNavigationControl = options.showNavigationControl !== false;\n this.autoFitBounds = options.autoFitBounds !== false;\n \n this.map = null;\n this.mapMarkers = [];\n this.pendingMarkers = [...this.markers];\n this.lineSources = options.lineSources || [];\n this.pendingLineData = new Map();\n\n this.template = `\n <div class=\"maplibre-container\">\n <div id=\"maplibre-{{id}}\" style=\"height: {{height}}px; width: 100%; border-radius: 0.375rem; border: 1px solid #dee2e6;\"></div>\n </div>\n `;\n }\n\n async onAfterRender() {\n await this.loadMapLibre();\n await this.initializeMap();\n }\n\n async loadMapLibre() {\n // Check if MapLibre is already loaded\n if (window.maplibregl) return;\n\n // Load MapLibre CSS\n const link = document.createElement('link');\n link.rel = 'stylesheet';\n link.href = 'https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.css';\n document.head.appendChild(link);\n\n // Load MapLibre JS\n return new Promise((resolve, reject) => {\n const script = document.createElement('script');\n script.src = 'https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.js';\n script.onload = resolve;\n script.onerror = reject;\n document.head.appendChild(script);\n });\n }\n\n getMapStyle() {\n const styles = {\n streets: 'https://demotiles.maplibre.org/style.json',\n dark: {\n version: 8,\n sources: {\n osm: {\n type: 'raster',\n tiles: ['https://a.tile.openstreetmap.org/{z}/{x}/{y}.png'],\n tileSize: 256,\n attribution: '© OpenStreetMap contributors'\n }\n },\n layers: [{\n id: 'osm',\n type: 'raster',\n source: 'osm'\n }],\n glyphs: 'https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf'\n },\n light: {\n version: 8,\n sources: {\n osm: {\n type: 'raster',\n tiles: ['https://a.tile.openstreetmap.org/{z}/{x}/{y}.png'],\n tileSize: 256,\n attribution: '© OpenStreetMap contributors'\n }\n },\n layers: [{\n id: 'osm',\n type: 'raster',\n source: 'osm'\n }],\n glyphs: 'https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf'\n },\n satellite: {\n version: 8,\n sources: {\n satellite: {\n type: 'raster',\n tiles: ['https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}'],\n tileSize: 256,\n attribution: '© Esri'\n }\n },\n layers: [{\n id: 'satellite',\n type: 'raster',\n source: 'satellite'\n }],\n glyphs: 'https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf'\n },\n terrain: {\n version: 8,\n sources: {\n terrain: {\n type: 'raster',\n tiles: ['https://a.tile.opentopomap.org/{z}/{x}/{y}.png'],\n tileSize: 256,\n attribution: '© OpenTopoMap contributors'\n }\n },\n layers: [{\n id: 'terrain',\n type: 'raster',\n source: 'terrain'\n }],\n glyphs: 'https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf'\n }\n };\n\n return styles[this.mapStyle] || styles.streets;\n }\n\n async initializeMap() {\n const mapElement = this.element.querySelector(`#maplibre-${this.id}`);\n if (!mapElement || !window.maplibregl) return;\n\n // Determine map center\n let mapCenter = this.center;\n if (!mapCenter && this.markers.length > 0) {\n // Use first marker as center (MapLibre uses [lng, lat])\n mapCenter = [this.markers[0].lng, this.markers[0].lat];\n }\n if (!mapCenter) {\n // Default to world view\n mapCenter = [0, 0];\n }\n\n // Create map\n this.map = new window.maplibregl.Map({\n container: mapElement,\n style: this.getMapStyle(),\n center: mapCenter,\n zoom: this.zoom,\n pitch: this.pitch,\n bearing: this.bearing\n });\n\n // Add navigation control\n if (this.showNavigationControl) {\n this.map.addControl(new window.maplibregl.NavigationControl(), 'top-right');\n }\n\n // Wait for map to load before adding markers\n this.map.on('load', () => {\n const initialMarkers = this.pendingMarkers.length ? this.pendingMarkers : this.markers;\n this.updateMarkers(initialMarkers);\n this.lineSources.forEach(source => this.addLineSource(source));\n this.pendingLineData.forEach(source => this.addLineSource(source));\n this.pendingLineData.clear();\n });\n }\n\n addMarkers(markers) {\n if (!this.map || !Array.isArray(markers)) {\n this.pendingMarkers = Array.isArray(markers) ? markers : [];\n return;\n }\n\n markers.forEach(markerData => {\n const { lng, lat, popup, color, icon, size } = markerData;\n \n if (!lng || !lat) return;\n\n // Create marker element\n const el = document.createElement('div');\n el.className = 'maplibre-marker';\n const markerSize = Number(size) || 30;\n el.style.width = `${markerSize}px`;\n el.style.height = `${markerSize}px`;\n el.style.borderRadius = '50%';\n el.style.backgroundColor = color || '#3b82f6';\n el.style.border = '3px solid white';\n el.style.boxShadow = '0 2px 4px rgba(0,0,0,0.3)';\n el.style.cursor = 'pointer';\n el.style.display = 'flex';\n el.style.alignItems = 'center';\n el.style.justifyContent = 'center';\n\n // Optional icon\n if (icon) {\n el.innerHTML = `<i class=\"${icon}\" style=\"color: white;\"></i>`;\n }\n\n // Create marker\n const marker = new window.maplibregl.Marker({ element: el })\n .setLngLat([lng, lat])\n .addTo(this.map);\n\n // Add popup if provided\n if (popup) {\n const mapPopup = new window.maplibregl.Popup({ offset: 25 })\n .setHTML(popup);\n marker.setPopup(mapPopup);\n }\n\n this.mapMarkers.push(marker);\n });\n }\n\n fitBounds() {\n if (!this.map || this.markers.length === 0) return;\n\n const bounds = new window.maplibregl.LngLatBounds();\n \n this.markers.forEach(marker => {\n bounds.extend([marker.lng, marker.lat]);\n });\n\n this.map.fitBounds(bounds, {\n padding: 50,\n maxZoom: 15\n });\n }\n\n updateMarkers(newMarkers) {\n const markers = Array.isArray(newMarkers) ? newMarkers : [];\n this.markers = markers;\n this.pendingMarkers = markers;\n if (!this.map) return;\n\n this.clearMarkers();\n this.addMarkers(markers);\n\n if (this.autoFitBounds && markers.length > 1) {\n this.fitBounds();\n } else if (markers.length === 1 && this.map) {\n this.map.flyTo({\n center: [markers[0].lng, markers[0].lat],\n zoom: this.zoom\n });\n }\n }\n\n clearMarkers() {\n this.mapMarkers.forEach(marker => {\n marker.remove();\n });\n this.mapMarkers = [];\n }\n\n addLineSource({ id, data, paint, layout }) {\n if (!id || !data) return;\n\n const sourceId = `${id}-source`;\n\n if (!this.map) {\n this.pendingLineData.set(id, { id, data, paint, layout });\n return;\n }\n\n if (this.map.getLayer(id)) {\n this.map.removeLayer(id);\n }\n if (this.map.getSource(sourceId)) {\n this.map.removeSource(sourceId);\n }\n\n this.map.addSource(sourceId, {\n type: 'geojson',\n data\n });\n\n this.map.addLayer({\n id,\n type: 'line',\n source: sourceId,\n paint: {\n 'line-color': '#3b82f6',\n 'line-width': 2,\n 'line-opacity': 0.6,\n ...paint\n },\n layout: {\n 'line-cap': 'round',\n 'line-join': 'round',\n ...layout\n }\n });\n }\n\n updateLineSource(id, { data, paint, layout } = {}) {\n if (!id || !data) return;\n const sourceId = `${id}-source`;\n if (!this.map) {\n this.pendingLineData.set(id, { id, data, paint, layout });\n return;\n }\n if (this.map.getSource(sourceId)) {\n this.map.getSource(sourceId).setData(data);\n if (paint || layout) {\n if (paint) {\n Object.entries(paint).forEach(([key, value]) => {\n this.map.setPaintProperty(id, key, value);\n });\n }\n if (layout) {\n Object.entries(layout).forEach(([key, value]) => {\n this.map.setLayoutProperty(id, key, value);\n });\n }\n }\n } else {\n this.addLineSource({ id, data, paint, layout });\n }\n }\n\n setView(lng, lat, zoom = null) {\n if (!this.map) return;\n this.map.flyTo({\n center: [lng, lat],\n zoom: zoom || this.zoom\n });\n }\n\n setZoom(zoom) {\n if (!this.map) return;\n this.map.setZoom(zoom);\n }\n\n setPitch(pitch) {\n if (!this.map) return;\n this.map.setPitch(pitch);\n }\n\n setBearing(bearing) {\n if (!this.map) return;\n this.map.setBearing(bearing);\n }\n\n async onBeforeDestroy() {\n if (this.map) {\n this.map.remove();\n this.map = null;\n }\n await super.onBeforeDestroy();\n }\n}\n\nexport default MapLibreView;\n","// Auto-generated from countries.geojson - do not edit manually.\nexport const COUNTRY_CENTROIDS = {\n \"AD\": { name: \"Andorra\", lat: 42.54859834854764, lng: 1.5802243611232873 },\n \"AE\": { name: \"United Arab Emirates\", lat: 24.18250292309135, lng: 54.27920525789581 },\n \"AF\": { name: \"Afghanistan\", lat: 34.13402601376932, lng: 66.59216131095278 },\n \"AG\": { name: \"Antigua and Barbuda\", lat: 17.07146759372967, lng: -61.78530823226373 },\n \"AI\": { name: \"Anguilla\", lat: 18.222874004219086, lng: -63.06008343771806 },\n \"AL\": { name: \"Albania\", lat: 41.14165894891656, lng: 20.061082767269493 },\n \"AM\": { name: \"Armenia\", lat: 40.17841274230679, lng: 45.05490831965259 },\n \"AO\": { name: \"Angola\", lat: -12.167424062667942, lng: 17.651768783079 },\n \"AQ\": { name: \"Antarctica\", lat: -77.16987521415838, lng: -177.56451613408842 },\n \"AR\": { name: \"Argentina\", lat: -35.697270518120085, lng: -64.53238503843076 },\n \"AS\": { name: \"American Samoa\", lat: -14.305711987770538, lng: -170.7007316174498 },\n \"AT\": { name: \"Austria\", lat: 47.631858269895794, lng: 13.797778364631036 },\n \"AU\": { name: \"Australia\", lat: -25.697337673983082, lng: 134.02277170916162 },\n \"AW\": { name: \"Aruba\", lat: 12.515625722992898, lng: -69.97564014284046 },\n \"AZ\": { name: \"Azerbaijan\", lat: 40.3920509942049, lng: 48.634592670644324 },\n \"BA\": { name: \"Bosnia and Herzegovina\", lat: 44.14415356126429, lng: 17.83467240787538 },\n \"BB\": { name: \"Barbados\", lat: 13.183219369337529, lng: -59.557383949150285 },\n \"BD\": { name: \"Bangladesh\", lat: 23.673728665121, lng: 90.43212562608613 },\n \"BE\": { name: \"Belgium\", lat: 50.6182138854095, lng: 4.675010154696485 },\n \"BF\": { name: \"Burkina Faso\", lat: 12.108709036312737, lng: -1.6932816211842325 },\n \"BG\": { name: \"Bulgaria\", lat: 42.82043677302438, lng: 25.251739122561908 },\n \"BH\": { name: \"Bahrain\", lat: 26.04798501537066, lng: 50.540695402276775 },\n \"BI\": { name: \"Burundi\", lat: -3.261251993278643, lng: 29.88518227845293 },\n \"BJ\": { name: \"Benin\", lat: 9.503013199615893, lng: 2.305714528830206 },\n \"BL\": { name: \"Saint Barthelemy\", lat: 17.90561691241738, lng: -62.83051610005156 },\n \"BM\": { name: \"Bermuda\", lat: 32.315067430740726, lng: -64.7458500599169 },\n \"BN\": { name: \"Brunei Darussalam\", lat: 4.543205889917609, lng: 114.6430958360464 },\n \"BO\": { name: \"Bolivia\", lat: -16.7312488393574, lng: -64.45209597511206 },\n \"BQ\": { name: \"Bonaire\", lat: 12.180844982440338, lng: -68.29350445958761 },\n \"BQ\": { name: \"Saba\", lat: 17.632512616389718, lng: -63.23739481909494 },\n \"BQ\": { name: \"Saint Eustatius\", lat: 17.4919042294197, lng: -62.978230589445026 },\n \"BR\": { name: \"Brazil\", lat: -11.524630416426652, lng: -54.355206608256424 },\n \"BS\": { name: \"Bahamas\", lat: 24.72162633646784, lng: -78.07275370060313 },\n \"BT\": { name: \"Bhutan\", lat: 27.42163933959824, lng: 90.46716647173861 },\n \"BV\": { name: \"Bouvet Island\", lat: -54.42316679395248, lng: 3.411969465057627 },\n \"BW\": { name: \"Botswana\", lat: -22.236609002062902, lng: 23.85779956995608 },\n \"BY\": { name: \"Belarus\", lat: 53.46791374543163, lng: 27.964252054715104 },\n \"BZ\": { name: \"Belize\", lat: 17.24252476647155, lng: -88.68273510023441 },\n \"CA\": { name: \"Canada\", lat: 57.550480044655636, lng: -98.41680517868062 },\n \"CC\": { name: \"Cocos Islands\", lat: -12.171249450199545, lng: 96.83688767323002 },\n \"CD\": { name: \"Congo DRC\", lat: -3.338629596207896, lng: 23.419827574282188 },\n \"CF\": { name: \"Central African Republic\", lat: 6.331390033944319, lng: 20.520743419397256 },\n \"CG\": { name: \"Congo\", lat: -0.7294391595233845, lng: 14.879732849491393 },\n \"CH\": { name: \"Switzerland\", lat: 46.73678128684938, lng: 8.286928794895285 },\n \"CI\": { name: \"Côte d'Ivoire\", lat: 7.536779279421307, lng: -5.571710194917734 },\n \"CK\": { name: \"Cook Islands\", lat: -21.222613253399842, lng: -159.78768870952257 },\n \"CL\": { name: \"Chile\", lat: -37.82938283049967, lng: -70.76863431739216 },\n \"CM\": { name: \"Cameroon\", lat: 6.294168487480992, lng: 12.948474142398263 },\n \"CN\": { name: \"China\", lat: 38.07325481105728, lng: 104.69113855849604 },\n \"CO\": { name: \"Colombia\", lat: 4.187753877352739, lng: -72.6445066243485 },\n \"CR\": { name: \"Costa Rica\", lat: 9.863467407406214, lng: -84.14673625701816 },\n \"CU\": { name: \"Cuba\", lat: 21.476176522869448, lng: -79.69817857618705 },\n \"CV\": { name: \"Cabo Verde\", lat: 15.076411518651643, lng: -23.63401005900474 },\n \"CW\": { name: \"Curacao\", lat: 12.199996647939832, lng: -68.96939768599042 },\n \"CX\": { name: \"Christmas Island\", lat: -10.446440802183416, lng: 105.70209512200549 },\n \"CY\": { name: \"Cyprus\", lat: 35.11700416345239, lng: 33.375346009199205 },\n \"CZ\": { name: \"Czech Republic\", lat: 49.74917370930982, lng: 15.383273292023533 },\n \"DE\": { name: \"Germany\", lat: 51.08304539800482, lng: 10.426171427430804 },\n \"DJ\": { name: \"Djibouti\", lat: 11.750235727618804, lng: 42.613496898789506 },\n \"DK\": { name: \"Denmark\", lat: 56.00118817971057, lng: 9.378670542409406 },\n \"DM\": { name: \"Dominica\", lat: 15.429269860940513, lng: -61.360471946942994 },\n \"DO\": { name: \"Dominican Republic\", lat: 18.77954818522993, lng: -70.43495198520012 },\n \"DZ\": { name: \"Algeria\", lat: 28.350969744889056, lng: 2.6558464719769135 },\n \"EC\": { name: \"Ecuador\", lat: -1.5642721388853116, lng: -78.4630326109714 },\n \"EE\": { name: \"Estonia\", lat: 58.648108311231034, lng: 25.916870250633806 },\n \"EG\": { name: \"Egypt\", lat: 26.60517034450628, lng: 30.240135435012338 },\n \"ER\": { name: \"Eritrea\", lat: 15.005533147667684, lng: 39.2672401449901 },\n \"ES\": { name: \"Spain\", lat: 40.365008336683836, lng: -3.6516251409956983 },\n \"ES\": { name: \"Canarias\", lat: 28.297665106525546, lng: -16.53799441021647 },\n \"ET\": { name: \"Ethiopia\", lat: 8.729389557048396, lng: 39.914902886544276 },\n \"FI\": { name: \"Finland\", lat: 65.01578959749911, lng: 25.65738433454702 },\n \"FJ\": { name: \"Fiji\", lat: -17.822470952336204, lng: 177.98144613732626 },\n \"FK\": { name: \"Falkland Islands\", lat: -51.75901312766726, lng: -58.746646363799854 },\n \"FM\": { name: \"Micronesia\", lat: 6.8789448129255435, lng: 158.2291899444527 },\n \"FO\": { name: \"Faroe Islands\", lat: 62.130896281495346, lng: -6.9811060913122835 },\n \"FR\": { name: \"France\", lat: 46.6423682169416, lng: 2.1940236627886227 },\n \"GA\": { name: \"Gabon\", lat: -0.628448459921234, lng: 11.839410898545754 },\n \"GB\": { name: \"United Kingdom\", lat: 53.97844735080214, lng: -2.852943909329258 },\n \"GD\": { name: \"Grenada\", lat: 12.112926656338907, lng: -61.67937937204098 },\n \"GE\": { name: \"Georgia\", lat: 42.17986277737226, lng: 43.378866534112234 },\n \"GF\": { name: \"French Guiana\", lat: 3.857429742497078, lng: -53.32232307156624 },\n \"GG\": { name: \"Guernsey\", lat: 49.45870771378872, lng: -2.576392582891568 },\n \"GH\": { name: \"Ghana\", lat: 7.94530467243628, lng: -1.219233362526581 },\n \"GI\": { name: \"Gibraltar\", lat: 36.14022671336082, lng: -5.345549484594358 },\n \"GL\": { name: \"Greenland\", lat: 74.16847218965994, lng: -42.07567788066985 },\n \"GM\": { name: \"Gambia\", lat: 13.428617959189328, lng: -15.383380385869662 },\n \"GN\": { name: \"Guinea\", lat: 10.255986541378112, lng: -10.986948848040218 },\n \"GP\": { name: \"Guadeloupe\", lat: 16.24420002705553, lng: -61.54382262282755 },\n \"GQ\": { name: \"Equatorial Guinea\", lat: 1.5954643936590733, lng: 10.425456672353823 },\n \"GR\": { name: \"Greece\", lat: 39.42012261727978, lng: 23.110368936161876 },\n \"GS\": { name: \"South Georgia and South Sandwich Islands\", lat: -54.37666443862139, lng: -36.77509575898928 },\n \"GT\": { name: \"Guatemala\", lat: 15.820878515352684, lng: -90.31219349119617 },\n \"GU\": { name: \"Guam\", lat: 13.445430479945276, lng: 144.78024458298802 },\n \"GW\": { name: \"Guinea-Bissau\", lat: 11.980075324820504, lng: -14.980186756910847 },\n \"GY\": { name: \"Guyana\", lat: 4.68211981385056, lng: -58.91352612754667 },\n \"HM\": { name: \"Heard Island and McDonald Islands\", lat: -53.084170035513736, lng: 73.49298560844045 },\n \"HN\": { name: \"Honduras\", lat: 14.740370695750006, lng: -86.49251679006962 },\n \"HR\": { name: \"Croatia\", lat: 44.91192100856702, lng: 16.625761129583374 },\n \"HT\": { name: \"Haiti\", lat: 18.883520486983567, lng: -72.89291379842 },\n \"HU\": { name: \"Hungary\", lat: 47.22527332486294, lng: 19.39620048366142 },\n \"ID\": { name: \"Indonesia\", lat: 0.15591979959037652, lng: 113.96538246847302 },\n \"IE\": { name: \"Ireland\", lat: 53.30489539816495, lng: -8.241128545554096 },\n \"IL\": { name: \"Israel\", lat: 31.513542220043195, lng: 35.027923472437024 },\n \"IM\": { name: \"Isle of Man\", lat: 54.22855301008011, lng: -4.532995055468449 },\n \"IN\": { name: \"India\", lat: 23.586300567746722, lng: 81.17300408530181 },\n \"IO\": { name: \"British Indian Ocean Territory\", lat: -7.323548444385743, lng: 72.43501618476016 },\n \"IQ\": { name: \"Iraq\", lat: 33.105075667527906, lng: 43.832529181056884 },\n \"IR\": { name: \"Iran\", lat: 32.906023742890056, lng: 54.237077001065444 },\n \"IS\": { name: \"Iceland\", lat: 65.12360920205514, lng: -19.05682967106099 },\n \"IT\": { name: \"Italy\", lat: 42.98201127614267, lng: 12.763657166123137 },\n \"JE\": { name: \"Jersey\", lat: 49.215396925724306, lng: -2.1291601162653575 },\n \"JM\": { name: \"Jamaica\", lat: 18.12207889341651, lng: -77.30358894542778 },\n \"JO\": { name: \"Jordan\", lat: 31.387064884449156, lng: 36.95728884547246 },\n \"JP\": { name: \"Japan\", lat: 36.76738832597829, lng: 137.46934234351835 },\n \"KE\": { name: \"Kenya\", lat: 0.6899182318376179, lng: 37.95309411262371 },\n \"KG\": { name: \"Kyrgyzstan\", lat: 41.35698905438358, lng: 74.17532903468319 },\n \"KH\": { name: \"Cambodia\", lat: 12.699186865507775, lng: 105.03973078680701 },\n \"KI\": { name: \"Kiribati\", lat: 1.8676673749241066, lng: -157.39024189323504 },\n \"KM\": { name: \"Comoros\", lat: -11.658861474508491, lng: 43.34826587429403 },\n \"KN\": { name: \"Saint Kitts and Nevis\", lat: 17.314736299587768, lng: -62.74560385895787 },\n \"KP\": { name: \"North Korea\", lat: 40.19198091470839, lng: 127.3379805653744 },\n \"KR\": { name: \"South Korea\", lat: 36.402386712544114, lng: 127.76224551357649 },\n \"KW\": { name: \"Kuwait\", lat: 29.281360965443092, lng: 47.56311109320184 },\n \"KY\": { name: \"Cayman Islands\", lat: 19.311231805620288, lng: -81.25203208977878 },\n \"KZ\": { name: \"Kazakhstan\", lat: 47.641465195176835, lng: 66.3759193479301 },\n \"LA\": { name: \"Laos\", lat: 18.117282736873282, lng: 103.76375899026448 },\n \"LB\": { name: \"Lebanon\", lat: 33.91160170722086, lng: 35.89651946324749 },\n \"LC\": { name: \"Saint Lucia\", lat: 13.895749185129906, lng: -60.9689510935251 },\n \"LI\": { name: \"Liechtenstein\", lat: 47.14627562133036, lng: 9.547674672376376 },\n \"LK\": { name: \"Sri Lanka\", lat: 7.696630939329944, lng: 80.66931169770622 },\n \"LR\": { name: \"Liberia\", lat: 6.52012979398834, lng: -9.258988935497618 },\n \"LS\": { name: \"Lesotho\", lat: -29.60168116924817, lng: 28.24475317864227 },\n \"LT\": { name: \"Lithuania\", lat: 55.29437393417175, lng: 23.946021605013534 },\n \"LU\": { name: \"Luxembourg\", lat: 49.77523454542369, lng: 6.103230338458876 },\n \"LV\": { name: \"Latvia\", lat: 56.813853047554154, lng: 24.693671325654403 },\n \"LY\": { name: \"Libya\", lat: 27.202915771690794, lng: 17.91133392454237 },\n \"MA\": { name: \"Morocco\", lat: 28.687598134979325, lng: -8.817212587250811 },\n \"MC\": { name: \"Monaco\", lat: 43.74798224995656, lng: 7.412820873271196 },\n \"MD\": { name: \"Moldova\", lat: 47.0725674580696, lng: 28.391111865941348 },\n \"ME\": { name: \"Montenegro\", lat: 42.73694835210454, lng: 19.29505087156758 },\n \"MF\": { name: \"Saint Martin\", lat: 18.078012113224464, lng: -63.06678525361946 },\n \"MG\": { name: \"Madagascar\", lat: -19.04163612493041, lng: 46.68493466832544 },\n \"MH\": { name: \"Marshall Islands\", lat: 7.307929900994344, lng: 168.72016025351076 },\n \"MK\": { name: \"North Macedonia\", lat: 41.59402890143112, lng: 21.70998923872772 },\n \"ML\": { name: \"Mali\", lat: 17.168146208584837, lng: -4.346399841781153 },\n \"MM\": { name: \"Myanmar\", lat: 19.901227931399873, lng: 97.08892285397344 },\n \"MN\": { name: \"Mongolia\", lat: 47.08644454604851, lng: 103.3987360327455 },\n \"MP\": { name: \"Northern Mariana Islands\", lat: 15.178063516432115, lng: 145.74119737203134 },\n \"MQ\": { name: \"Martinique\", lat: 14.642697353597645, lng: -61.01432380875083 },\n \"MR\": { name: \"Mauritania\", lat: 20.466731212820022, lng: -10.495079045035716 },\n \"MS\": { name: \"Montserrat\", lat: 16.735363391460357, lng: -62.18693281256255 },\n \"MT\": { name: \"Malta\", lat: 35.890522650899314, lng: 14.441922442508494 },\n \"MU\": { name: \"Mauritius\", lat: -20.28142317475198, lng: 57.56415671066546 },\n \"MV\": { name: \"Maldives\", lat: -0.6065577168009924, lng: 73.10076245140479 },\n \"MW\": { name: \"Malawi\", lat: -13.128986464184024, lng: 34.23441182298881 },\n \"MX\": { name: \"Mexico\", lat: 23.87436068093592, lng: -101.55399731630118 },\n \"MY\": { name: \"Malaysia\", lat: 3.6716608556387857, lng: 114.63330303992869 },\n \"MZ\": { name: \"Mozambique\", lat: -17.525230309488748, lng: 35.208577031290176 },\n \"NA\": { name: \"Namibia\", lat: -21.90858163281473, lng: 18.16451345845268 },\n \"NC\": { name: \"New Caledonia\", lat: -21.33003372660827, lng: 165.50767040438612 },\n \"NE\": { name: \"Niger\", lat: 17.08105392407292, lng: 8.86863247002646 },\n \"NF\": { name: \"Norfolk Island\", lat: -29.037654445046282, lng: 167.95259597483337 },\n \"NG\": { name: \"Nigeria\", lat: 9.61029352034213, lng: 8.147714845256194 },\n \"NI\": { name: \"Nicaragua\", lat: 12.893566631930554, lng: -85.016088327669 },\n \"NL\": { name: \"Netherlands\", lat: 52.134054128923886, lng: 5.554136426128487 },\n \"NO\": { name: \"Norway\", lat: 64.97775882947745, lng: 16.670259272390894 },\n \"NP\": { name: \"Nepal\", lat: 28.300920699755657, lng: 84.1338898313567 },\n \"NR\": { name: \"Nauru\", lat: -0.5221021440668993, lng: 166.92937633139178 },\n \"NU\": { name: \"Niue\", lat: -19.05230921680393, lng: -169.86878106699083 },\n \"NZ\": { name: \"New Zealand\", lat: -43.82765432544426, lng: 170.69035541428696 },\n \"OM\": { name: \"Oman\", lat: 20.7242833183209, lng: 55.841088119829 },\n \"PA\": { name: \"Panama\", lat: 8.439536749576892, lng: -80.14428761482796 },\n \"PE\": { name: \"Peru\", lat: -8.522717984240291, lng: -74.11416196781884 },\n \"PF\": { name: \"French Polynesia\", lat: -17.674684080120677, lng: -149.40041671099763 },\n \"PG\": { name: \"Papua New Guinea\", lat: -7.156912819152135, lng: 144.8348942994695 },\n \"PH\": { name: \"Philippines\", lat: 15.586542251094242, lng: 121.82208941416745 },\n \"PK\": { name: \"Pakistan\", lat: 30.116188371410882, lng: 69.08835090769651 },\n \"PL\": { name: \"Poland\", lat: 52.06848055692473, lng: 19.43573279234468 },\n \"PM\": { name: \"Saint Pierre and Miquelon\", lat: 46.95153913615198, lng: -56.32465353437558 },\n \"PN\": { name: \"Pitcairn\", lat: -24.366121747565458, lng: -128.3149848627581 },\n \"PR\": { name: \"Puerto Rico\", lat: 18.216224086610914, lng: -66.49425339593509 },\n \"PS\": { name: \"Palestinian Territory\", lat: 31.930818736453883, lng: 35.24251184154588 },\n \"PT\": { name: \"Portugal\", lat: 39.67529214702138, lng: -7.933662183874006 },\n \"PW\": { name: \"Palau\", lat: 7.534775852547396, lng: 134.57965086721052 },\n \"PY\": { name: \"Paraguay\", lat: -23.42190559259428, lng: -58.38906357428651 },\n \"QA\": { name: \"Qatar\", lat: 25.318528486425386, lng: 51.19794918743203 },\n \"RE\": { name: \"Réunion\", lat: -21.119825460665105, lng: 55.54393506194689 },\n \"RO\": { name: \"Romania\", lat: 45.82454894397586, lng: 25.094158201563292 },\n \"RS\": { name: \"Serbia\", lat: 44.02679870131969, lng: 20.85677444395745 },\n \"RU\": { name: \"Russian Federation\", lat: 59.039434214106194, lng: 98.6704990698032 },\n \"RW\": { name: \"Rwanda\", lat: -2.014687460047154, lng: 29.919439681764082 },\n \"SA\": { name: \"Saudi Arabia\", lat: 24.136038144757897, lng: 44.600958178225596 },\n \"SB\": { name: \"Solomon Islands\", lat: -9.613104734596515, lng: 160.16475795033884 },\n \"SC\": { name: \"Seychelles\", lat: -4.660002318822744, lng: 55.47250789595527 },\n \"SD\": { name: \"Sudan\", lat: 15.67060230984256, lng: 29.951458283594064 },\n \"SE\": { name: \"Sweden\", lat: 62.73420986108448, lng: 17.062431988004956 },\n \"SG\": { name: \"Singapore\", lat: 1.3528251890006349, lng: 103.81025757634053 },\n \"SH\": { name: \"Saint Helena\", lat: -15.962963318340398, lng: -5.717391620813109 },\n \"SI\": { name: \"Slovenia\", lat: 46.13759229564504, lng: 14.890636899973781 },\n \"SJ\": { name: \"Svalbard\", lat: 78.57318936469626, lng: 16.036378851505283 },\n \"SK\": { name: \"Slovakia\", lat: 48.69808390520484, lng: 19.581015362490966 },\n \"SL\": { name: \"Sierra Leone\", lat: 8.561330384750587, lng: -11.78656695731115 },\n \"SM\": { name: \"San Marino\", lat: 43.942820729097896, lng: 12.461278349581722 },\n \"SN\": { name: \"Senegal\", lat: 14.228861491763402, lng: -14.610875368352305 },\n \"SO\": { name: \"Somalia\", lat: 6.524534573103924, lng: 45.40037867243972 },\n \"SR\": { name: \"Suriname\", lat: 4.098723595920171, lng: -55.855514311561286 },\n \"SS\": { name: \"South Sudan\", lat: 7.657782041763295, lng: 30.3851856901788 },\n \"ST\": { name: \"Sao Tome and Principe\", lat: 0.22744704294793774, lng: 6.606158796857607 },\n \"SV\": { name: \"El Salvador\", lat: 13.758041517055418, lng: -88.85911489238985 },\n \"SX\": { name: \"Sint Maarten\", lat: 18.03942608463078, lng: -63.06883135915303 },\n \"SY\": { name: \"Syria\", lat: 35.09751106058316, lng: 38.5117323139514 },\n \"SZ\": { name: \"Eswatini\", lat: -26.562540935608702, lng: 31.510685746082007 },\n \"TC\": { name: \"Turks and Caicos Islands\", lat: 21.799865427483745, lng: -71.74058946811508 },\n \"TD\": { name: \"Chad\", lat: 15.283493546654503, lng: 18.427113900363025 },\n \"TF\": { name: \"Juan De Nova Island\", lat: -17.06449193630804, lng: 42.74374761089645 },\n \"TF\": { name: \"French Southern Territories\", lat: -49.26329687105643, lng: 69.54686984724839 },\n \"TF\": { name: \"Glorioso Islands\", lat: -11.566224871643417, lng: 47.290948081543384 },\n \"TG\": { name: \"Togo\", lat: 8.660743037717811, lng: 0.8990857571109684 },\n \"TH\": { name: \"Thailand\", lat: 13.66222784745538, lng: 101.08675116214552 },\n \"TJ\": { name: \"Tajikistan\", lat: 38.56933138382972, lng: 70.94215281065698 },\n \"TK\": { name: \"Tokelau\", lat: -9.195174767256544, lng: -171.85265950722743 },\n \"TL\": { name: \"Timor-Leste\", lat: -8.809894630601962, lng: 125.95024049562659 },\n \"TM\": { name: \"Turkmenistan\", lat: 39.06069118001429, lng: 58.4577357627054 },\n \"TN\": { name: \"Tunisia\", lat: 34.08636179565723, lng: 9.65587551697984 },\n \"TO\": { name: \"Tonga\", lat: -21.15927212823853, lng: -175.20415878511247 },\n \"TR\": { name: \"Turkey\", lat: 38.93207363123396, lng: 35.56886764076691 },\n \"TT\": { name: \"Trinidad and Tobago\", lat: 10.415515638298093, lng: -61.37236579976247 },\n \"TV\": { name: \"Tuvalu\", lat: -8.514701506447222, lng: 179.217833977593 },\n \"TZ\": { name: \"Tanzania\", lat: -6.355794440041147, lng: 34.81832206060381 },\n \"UA\": { name: \"Ukraine\", lat: 48.657532515563794, lng: 31.27377208442636 },\n \"UG\": { name: \"Uganda\", lat: 1.2821729218416205, lng: 32.34371768463123 },\n \"UM\": { name: \"United States Minor Outlying Islands\", lat: 19.302045812215958, lng: 166.63800339749642 },\n \"US\": { name: \"United States\", lat: 38.8208089190304, lng: -96.33161660829639 },\n \"UY\": { name: \"Uruguay\", lat: -32.78195043831093, lng: -56.01919523458085 },\n \"UZ\": { name: \"Uzbekistan\", lat: 41.4879065244633, lng: 63.8548297593985 },\n \"VA\": { name: \"Vatican City\", lat: 41.90402351316735, lng: 12.451312917026133 },\n \"VC\": { name: \"Saint Vincent and the Grenadines\", lat: 13.254808122970651, lng: -61.193766354393034 },\n \"VE\": { name: \"Venezuela\", lat: 7.148324760507107, lng: -66.36492135985132 },\n \"VG\": { name: \"British Virgin Islands\", lat: 18.42195819619707, lng: -64.62406519257699 },\n \"VI\": { name: \"US Virgin Islands\", lat: 17.738009708772523, lng: -64.76155341409797 },\n \"VN\": { name: \"Vietnam\", lat: 16.517347170839393, lng: 105.91338832758704 },\n \"VU\": { name: \"Vanuatu\", lat: -15.189132121699332, lng: 166.84912735669738 },\n \"WF\": { name: \"Wallis and Futuna\", lat: -14.283442307826677, lng: -178.12735555777184 },\n \"WS\": { name: \"Samoa\", lat: -13.634252953274622, lng: -172.44107655740137 },\n \"YE\": { name: \"Yemen\", lat: 16.001392616307445, lng: 47.46815793206386 },\n \"YT\": { name: \"Mayotte\", lat: -12.824468416301052, lng: 45.128142327031064 },\n \"ZA\": { name: \"South Africa\", lat: -28.55361930679731, lng: 24.75252746489084 },\n \"ZM\": { name: \"Zambia\", lat: -13.162832953186246, lng: 27.75521363430896 },\n \"ZW\": { name: \"Zimbabwe\", lat: -18.92700121981475, lng: 29.717829640720844 },\n};\n\nexport function getCountryCentroid(code) {\n if (!code) return null;\n return COUNTRY_CENTROIDS[code.toUpperCase()] || null;\n}\n\nexport function getCountryCentroids() {\n return COUNTRY_CENTROIDS;\n}","import View from '@core/View.js';\nimport MapLibreView from './MapLibreView.js';\nimport { COUNTRY_CENTROIDS } from './countryCentroids.js';\n\nclass MetricsCountryMapView extends View {\n constructor(options = {}) {\n const mapHeight = options.height || 320;\n super({\n className: 'metrics-country-map-view',\n ...options\n });\n\n this.endpoint = options.endpoint || '/api/metrics/fetch';\n this.account = options.account || 'global';\n this.category = options.category || null;\n this.slugs = options.slugs || null;\n this.granularity = options.granularity || 'days';\n this.maxCountries = options.maxCountries || 12;\n this.metricLabel = options.metricLabel || 'Events';\n this.height = mapHeight;\n this.mapStyle = options.mapStyle || 'dark';\n this.mapOptions = options.mapOptions || {};\n this.showRoutes = options.showRoutes !== false;\n this.routeOrigin = options.routeOrigin || { lng: -77.346, lat: 38.958, name: 'Reston, VA' };\n this._refreshing = false;\n }\n\n async getTemplate() {\n return `\n <div class=\"metrics-country-map\">\n <div class=\"map-container mb-3\" data-container=\"${this.id}-map\" style=\"height:${this.height}px\"></div>\n <div class=\"map-legend small\" data-region=\"legend\"></div>\n </div>\n `;\n }\n\n async onInit() {\n this.statusEl = document.createElement('div');\n this.statusEl.className = 'text-muted small px-3 pb-2';\n this.element.appendChild(this.statusEl);\n\n this.mapView = new MapLibreView({\n containerId: `${this.id}-map`,\n height: this.height,\n style: this.mapStyle,\n zoom: this.mapOptions.zoom ?? 1.3,\n center: this.mapOptions.center || [10, 20],\n pitch: this.mapOptions.pitch ?? 20,\n bearing: this.mapOptions.bearing ?? 0,\n showNavigationControl: this.mapOptions.showNavigationControl ?? true,\n autoFitBounds: this.mapOptions.autoFitBounds ?? false\n });\n this.addChild(this.mapView);\n await this.refresh();\n }\n\n async refresh() {\n if (this._refreshing) return;\n this._refreshing = true;\n this.setStatus('Loading hotspots…');\n try {\n const metrics = await this.fetchMetrics();\n await this.applyMetrics(metrics);\n this.setStatus('');\n } catch (error) {\n console.error('MetricsCountryMapView refresh error', error);\n this.setStatus('Unable to load country metrics.');\n } finally {\n this._refreshing = false;\n }\n }\n\n async fetchMetrics() {\n const rest = this.getApp()?.rest;\n if (!rest) {\n throw new Error('REST client unavailable');\n }\n const params = {\n account: this.account,\n granularity: this.granularity,\n with_labels: true\n };\n if (this.category) params.category = this.category;\n if (this.slugs) {\n const slugsArray = Array.isArray(this.slugs) ? this.slugs : [this.slugs];\n params['slugs[]'] = slugsArray;\n }\n\n const response = await rest.GET(this.endpoint, params);\n if (!response.success || !response.data?.status) {\n throw new Error(response.data?.error || 'Metrics API error');\n }\n return response.data.data;\n }\n\n async applyMetrics(data) {\n const metrics = data?.data || {};\n const labels = data?.labels || [];\n const entries = [];\n\n Object.entries(metrics).forEach(([iso, series]) => {\n const total = series.reduce((sum, value) => sum + (Number(value) || 0), 0);\n if (!total) return;\n const centroid = COUNTRY_CENTROIDS[iso.toUpperCase()];\n if (!centroid) return;\n entries.push({\n code: iso.toUpperCase(),\n total,\n values: series,\n centroid\n });\n });\n\n if (!entries.length) {\n this.mapView.updateMarkers([]);\n this.renderLegend([]);\n this.setStatus('No country data available for the selected range.');\n return;\n }\n\n entries.sort((a, b) => b.total - a.total);\n const topEntries = entries.slice(0, this.maxCountries);\n const maxValue = topEntries[0]?.total || 1;\n\n const markers = topEntries.map(entry => {\n const intensity = entry.total / maxValue;\n const markerSize = Math.round(18 + intensity * 24);\n return {\n lng: entry.centroid.lng,\n lat: entry.centroid.lat,\n size: markerSize,\n color: this.getMarkerColor(intensity),\n popup: `\n <div class=\"text-center\">\n <strong>${entry.centroid.name}</strong><br/>\n <span class=\"text-muted\">${entry.total.toLocaleString()} ${this.metricLabel}</span>\n </div>\n `\n };\n });\n\n if (this.showRoutes && this.routeOrigin?.lng && this.routeOrigin?.lat) {\n markers.push({\n lng: this.routeOrigin.lng,\n lat: this.routeOrigin.lat,\n size: 26,\n color: '#0d6efd',\n icon: 'bi bi-broadcast-pin',\n popup: `\n <div class=\"text-center\">\n <strong>${this.routeOrigin.name || 'Operations Hub'}</strong><br/>\n <span class=\"text-muted\">Origin</span>\n </div>\n `\n });\n }\n\n this.mapView.updateMarkers(markers);\n this.renderLegend(topEntries, labels);\n if (this.showRoutes) {\n this.renderRoutes(topEntries, maxValue);\n }\n }\n\n getMarkerColor(intensity) {\n const start = [32, 201, 151]; // teal\n const end = [255, 193, 7]; // amber\n const mix = start.map((value, idx) =>\n Math.round(value + (end[idx] - value) * intensity)\n );\n return `rgba(${mix[0]}, ${mix[1]}, ${mix[2]}, 0.9)`;\n }\n\n renderLegend(entries) {\n const legendEl = this.element.querySelector('[data-region=\"legend\"]');\n if (!legendEl) return;\n if (!entries.length) {\n legendEl.innerHTML = '';\n return;\n }\n\n const maxValue = entries[0]?.total || 1;\n const rows = entries.map(entry => {\n const percent = ((entry.total / maxValue) * 100).toFixed(0);\n return `\n <div class=\"d-flex justify-content-between align-items-center py-1 border-top\">\n <div>\n <span class=\"fw-semibold\">${entry.centroid.name}</span>\n <span class=\"text-muted ms-1\">(${entry.code})</span>\n </div>\n <div class=\"text-end\">\n <div class=\"fw-semibold\">${entry.total.toLocaleString()}</div>\n <small class=\"text-muted\">${percent}% of top</small>\n </div>\n </div>\n `;\n }).join('');\n\n legendEl.innerHTML = rows;\n }\n\n renderRoutes(entries, maxValue) {\n const origin = this.routeOrigin || null;\n if (!origin || !origin.lng || !origin.lat || !this.mapView) return;\n\n const features = entries.map(entry => ({\n type: 'Feature',\n geometry: {\n type: 'LineString',\n coordinates: [\n [origin.lng, origin.lat],\n [entry.centroid.lng, entry.centroid.lat]\n ]\n },\n properties: {\n total: entry.total,\n intensity: entry.total / maxValue\n }\n }));\n\n const geojson = {\n type: 'FeatureCollection',\n features\n };\n\n const paint = {\n 'line-color': [\n 'interpolate',\n ['linear'],\n ['get', 'intensity'],\n 0,\n 'rgba(32, 201, 151, 0.6)',\n 1,\n 'rgba(255, 193, 7, 0.95)'\n ],\n 'line-width': [\n 'interpolate',\n ['linear'],\n ['get', 'intensity'],\n 0,\n 1.75,\n 1,\n 6\n ],\n 'line-opacity': [\n 'interpolate',\n ['linear'],\n ['get', 'intensity'],\n 0,\n 0.45,\n 1,\n 0.95\n ]\n };\n\n this.mapView.lineSources = this.mapView.lineSources.filter(src => src.id !== `${this.id}-routes`);\n this.mapView.updateLineSource(`${this.id}-routes`, {\n data: geojson,\n paint\n });\n }\n\n setStatus(message) {\n if (!this.statusEl) return;\n this.statusEl.textContent = message || '';\n this.statusEl.style.display = message ? 'block' : 'none';\n }\n\n}\n\nexport default MetricsCountryMapView;\n","import Page from '@core/Page.js';\nimport View from '@core/View.js';\nimport {\n MetricsChart,\n MetricsMiniChartWidget\n} from '@ext/charts/index.js';\nimport TableView from '@core/views/table/TableView.js';\nimport {\n IncidentList,\n IncidentStats\n} from '@core/models/Incident.js';\nimport {\n TicketList\n} from '@core/models/Tickets.js';\nimport { MetricsCountryMapView } from '@ext/map/index.js';\n\nclass IncidentDashboardHeader extends View {\n constructor(options = {}) {\n super({\n ...options,\n className: 'incident-dashboard-header'\n });\n\n this.model = new IncidentStats();\n }\n\n async getTemplate() {\n return `\n <div class=\"row\">\n <div class=\"col-xl-3 col-lg-6 col-12 mb-3\">\n <div class=\"card h-100 border-0 shadow-sm\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div>\n <h6 class=\"card-title text-muted mb-2\">Open Incidents</h6>\n <h3 class=\"mb-1 fw-bold\">{{model.incidents.open}}</h3>\n <span class=\"badge bg-danger-subtle text-danger\">{{model.incidents.new}} New</span>\n </div>\n <div class=\"text-danger\">\n <i class=\"bi bi-exclamation-triangle fs-2\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"col-xl-3 col-lg-6 col-12 mb-3\">\n <div class=\"card h-100 border-0 shadow-sm\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div>\n <h6 class=\"card-title text-muted mb-2\">Open Tickets</h6>\n <h3 class=\"mb-1 fw-bold\">{{model.tickets.open}}</h3>\n <span class=\"badge bg-warning-subtle text-warning\">{{model.tickets.new}} New</span>\n </div>\n <div class=\"text-warning\">\n <i class=\"bi bi-ticket-perforated fs-2\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"col-xl-3 col-lg-6 col-12 mb-3\">\n <div class=\"card h-100 border-0 shadow-sm\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div>\n <h6 class=\"card-title text-muted mb-2\">Recent Events</h6>\n <h3 class=\"mb-1 fw-bold\">{{model.events.recent}}</h3>\n <span class=\"badge bg-info-subtle text-info\">{{model.events.critical}} Critical</span>\n </div>\n <div class=\"text-info\">\n <i class=\"bi bi-activity fs-2\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"col-xl-3 col-lg-6 col-12 mb-3\">\n <div class=\"card h-100 border-0 shadow-sm\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div>\n <h6 class=\"card-title text-muted mb-2\">Recent Incidents</h6>\n <h3 class=\"mb-1 fw-bold\">{{model.incidents.recent}}</h3>\n <span class=\"badge bg-secondary-subtle text-secondary\">Last 24h</span>\n </div>\n <div class=\"text-secondary\">\n <i class=\"bi bi-clock-history fs-2\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n `;\n }\n\n async onBeforeRender() {\n await this.model.fetch();\n }\n}\n\n\nclass IncidentDashboardPage extends Page {\n constructor(options = {}) {\n super({\n ...options,\n title: 'Incidents Dashboard',\n className: 'incident-dashboard-page'\n });\n }\n\n async getTemplate() {\n return `\n <div class=\"container-fluid incident-dashboard\">\n <div class=\"d-flex justify-content-between align-items-center mb-3\">\n <div>\n <p class=\"text-muted mb-0\">Incident Intelligence Hub</p>\n <small class=\"text-info\">\n <i class=\"bi bi-activity me-1\"></i>\n Real-time visibility into incidents, tickets, and correlated events\n </small>\n </div>\n <div class=\"btn-group\" role=\"group\">\n <button type=\"button\"\n class=\"btn btn-outline-secondary btn-sm\"\n data-action=\"refresh-all\"\n title=\"Refresh dashboards\">\n <i class=\"bi bi-arrow-clockwise\"></i> Refresh\n </button>\n </div>\n </div>\n\n <div data-container=\"header\" class=\"mb-4\"></div>\n\n <div class=\"row g-4\">\n <div class=\"col-xl-6 col-lg-12\" data-container=\"events-widget\"></div>\n <div class=\"col-xl-6 col-lg-12\" data-container=\"incidents-widget\"></div>\n </div>\n\n\n <div class=\"row g-4 mt-1\">\n <div class=\"col-12\">\n <div class=\"card shadow-sm h-100\">\n <div class=\"card-header border-0 bg-transparent d-flex align-items-center justify-content-between\">\n <div>\n <h6 class=\"mb-0 text-uppercase small text-muted\">Global Event Hotspots</h6>\n <span class=\"text-muted small\">Clusters sized by total events</span>\n </div>\n <span class=\"badge bg-info-subtle text-info\">Interactive</span>\n </div>\n <div class=\"card-body p-0\" data-container=\"events-country-map\"></div>\n </div>\n </div>\n </div>\n\n\n <div class=\"row g-4 mt-1\">\n <div class=\"col-lg-6\">\n <div class=\"card shadow-sm h-100\">\n <div class=\"card-header border-0 bg-transparent d-flex align-items-center justify-content-between\">\n <div>\n <h6 class=\"mb-0 text-uppercase small text-muted\">Events by Country</h6>\n <span class=\"text-muted small\">Hotspots from the last 24 hours</span>\n </div>\n <span class=\"badge bg-success-subtle text-success\">Live</span>\n </div>\n <div class=\"card-body p-3\">\n <div data-container=\"events-by-country-chart\"></div>\n </div>\n </div>\n </div>\n <div class=\"col-lg-6\">\n <div class=\"card shadow-sm h-100\">\n <div class=\"card-header border-0 bg-transparent d-flex align-items-center justify-content-between\">\n <div>\n <h6 class=\"mb-0 text-uppercase small text-muted\">Incidents by Country</h6>\n <span class=\"text-muted small\">Highest volume regions</span>\n </div>\n <span class=\"badge bg-warning-subtle text-warning\">24h</span>\n </div>\n <div class=\"card-body p-3\">\n <div data-container=\"incidents-by-country-chart\"></div>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"row g-4 mt-1\">\n <div class=\"col-lg-6\">\n <div class=\"card shadow-sm h-100\">\n <div class=\"card-header border-0 bg-transparent d-flex align-items-center justify-content-between\">\n <div>\n <h6 class=\"mb-0 text-uppercase small text-muted\">New Tickets</h6>\n <span class=\"text-muted small\">Fresh tickets awaiting triage</span>\n </div>\n <i class=\"bi bi-ticket-perforated text-muted\"></i>\n </div>\n <div class=\"card-body\" data-container=\"my-tickets-table\"></div>\n </div>\n </div>\n <div class=\"col-lg-6\">\n <div class=\"card shadow-sm h-100\">\n <div class=\"card-header border-0 bg-transparent d-flex align-items-center justify-content-between\">\n <div>\n <h6 class=\"mb-0 text-uppercase small text-muted\">New Incidents</h6>\n <span class=\"text-muted small\">Newest incidents in the queue</span>\n </div>\n <i class=\"bi bi-flag text-warning\"></i>\n </div>\n <div class=\"card-body\" data-container=\"high-priority-incidents-table\"></div>\n </div>\n </div>\n </div>\n </div>\n `;\n }\n\n async onInit() {\n this.header = new IncidentDashboardHeader({\n containerId: 'header'\n });\n this.addChild(this.header);\n\n this.eventsWidget = new MetricsMiniChartWidget({\n containerId: 'events-widget',\n icon: 'bi bi-activity fs-2',\n title: 'System Events',\n subtitle: '{{now_value}} <span class=\"subtitle-label\">{{now_label}}</span>',\n background: '#154360',\n textColor: '#FFFFFF',\n endpoint: '/api/metrics/fetch',\n granularity: 'days',\n slugs: ['incident_events'],\n account: 'incident',\n chartType: 'line',\n showTooltip: true,\n showXAxis: false,\n height: 140,\n color: 'rgba(255,255,255,0.9)',\n fill: true,\n fillColor: 'rgba(255,255,255,0.2)',\n smoothing: 0.3,\n defaultDateRange: '7d',\n valueFormat: 'number',\n showTrending: true,\n showSettings: true,\n settingsKey: 'incident-dashboard-events'\n });\n this.addChild(this.eventsWidget);\n\n this.incidentsWidget = new MetricsMiniChartWidget({\n containerId: 'incidents-widget',\n icon: 'bi bi-exclamation-triangle fs-2',\n title: 'System Incidents',\n subtitle: '{{now_value}} <span class=\"subtitle-label\">{{now_label}}</span>',\n background: '#7D6608',\n textColor: '#FFFFFF',\n endpoint: '/api/metrics/fetch',\n granularity: 'days',\n slugs: ['incidents'],\n account: 'incident',\n chartType: 'line',\n showTooltip: true,\n showXAxis: false,\n height: 140,\n color: 'rgba(255,255,255,0.9)',\n fill: true,\n fillColor: 'rgba(255,255,255,0.25)',\n smoothing: 0.3,\n defaultDateRange: '7d',\n valueFormat: 'number',\n showTrending: true,\n showSettings: true,\n settingsKey: 'incident-dashboard-incidents'\n });\n this.addChild(this.incidentsWidget);\n\n this.eventsByCountryChart = new MetricsChart({\n title: '<i class=\"bi bi-globe-central-south-asia me-2\"></i> Events by Country',\n endpoint: '/api/metrics/fetch',\n account: 'incident',\n category: 'incident_events_by_country',\n granularity: 'days',\n chartType: 'line',\n showDateRange: false,\n showMetricsFilter: false,\n height: 220,\n maxDatasets: 10,\n colors: [\n 'rgba(32, 201, 151, 0.85)'\n ],\n yAxis: {\n label: 'Events',\n beginAtZero: true\n },\n tooltip: { y: 'number:0' },\n containerId: 'events-by-country-chart'\n });\n this.addChild(this.eventsByCountryChart);\n\n this.incidentsByCountryChart = new MetricsChart({\n title: '<i class=\"bi bi-geo-alt me-2\"></i> Incidents by Country',\n endpoint: '/api/metrics/fetch',\n account: 'incident',\n category: 'incidents_by_country',\n granularity: 'days',\n chartType: 'line',\n showDateRange: false,\n showMetricsFilter: false,\n height: 220,\n maxDatasets: 10,\n colors: [\n 'rgba(255, 193, 7, 0.85)'\n ],\n yAxis: {\n label: 'Incidents',\n beginAtZero: true\n },\n tooltip: { y: 'number:0' },\n containerId: 'incidents-by-country-chart'\n });\n this.addChild(this.incidentsByCountryChart);\n\n this.eventsCountryMap = new MetricsCountryMapView({\n containerId: 'events-country-map',\n category: 'incident_events_by_country',\n account: 'incident',\n maxCountries: 20,\n metricLabel: 'Events',\n height: 360,\n mapStyle: 'dark'\n });\n this.addChild(this.eventsCountryMap);\n\n const myTicketsCollection = new TicketList({\n params: {\n status: 'new'\n }\n });\n this.myTicketsTable = new TableView({\n containerId: 'my-tickets-table',\n title: 'New Tickets',\n collection: myTicketsCollection,\n columns: [\n { key: 'id', label: 'ID' },\n { key: 'title', label: 'Title' },\n { key: 'priority', label: 'Priority' }\n ]\n });\n this.addChild(this.myTicketsTable);\n\n const newIncidentsCollection = new IncidentList({\n params: {\n status: 'new'\n }\n });\n this.highPriorityIncidentsTable = new TableView({\n containerId: 'high-priority-incidents-table',\n title: 'New Incidents',\n collection: newIncidentsCollection,\n columns: [\n { key: 'id', label: 'ID' },\n { key: 'title', label: 'Title' },\n { key: 'status', label: 'Status', formatter: 'badge' }\n ]\n });\n this.addChild(this.highPriorityIncidentsTable);\n }\n\n async onActionRefreshAll(event, element) {\n const button = element || event?.currentTarget || null;\n const icon = button?.querySelector?.('i');\n icon?.classList.add('bi-spin');\n if (button) button.disabled = true;\n\n const refreshTasks = [\n this.header?.model?.fetch()?.then(() => this.header.render()),\n this.eventsWidget?.refresh(),\n this.incidentsWidget?.refresh(),\n this.eventsByCountryChart?.refresh(),\n this.incidentsByCountryChart?.refresh(),\n this.eventsCountryMap?.refresh(),\n this.myTicketsTable?.collection?.fetch(),\n this.highPriorityIncidentsTable?.collection?.fetch()\n ].filter(Boolean);\n\n await Promise.allSettled(refreshTasks);\n\n icon?.classList.remove('bi-spin');\n if (button) button.disabled = false;\n }\n}\n\nexport default IncidentDashboardPage;\n","/**\n * StackTraceView - Display formatted and color-coded stack traces\n */\n\nimport View from '@core/View.js';\n\nclass StackTraceView extends View {\n constructor(options = {}) {\n super({\n className: 'stack-trace-view',\n ...options\n });\n\n this.stackTrace = options.stackTrace || '';\n \n this.template = `\n <div class=\"stack-trace-container p-3\">\n <style>\n .stack-trace-line {\n font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', monospace;\n font-size: 13px;\n line-height: 1.6;\n padding: 4px 8px;\n margin: 0;\n border-left: 3px solid transparent;\n }\n .stack-trace-line:hover {\n background-color: rgba(0, 0, 0, 0.05);\n }\n .stack-trace-error {\n color: #dc3545;\n font-weight: 600;\n border-left-color: #dc3545;\n background-color: rgba(220, 53, 69, 0.05);\n }\n .stack-trace-file {\n color: #0d6efd;\n font-weight: 500;\n border-left-color: #0d6efd;\n }\n .stack-trace-function {\n color: #6610f2;\n font-weight: 500;\n }\n .stack-trace-location {\n color: #6c757d;\n font-size: 12px;\n }\n .stack-trace-line-number {\n color: #fd7e14;\n font-weight: 600;\n }\n .stack-trace-context {\n color: #495057;\n background-color: rgba(0, 0, 0, 0.02);\n }\n .stack-trace-container {\n background-color: #f8f9fa;\n border: 1px solid #dee2e6;\n border-radius: 0.375rem;\n max-height: 600px;\n overflow-y: auto;\n }\n </style>\n <div class=\"stack-trace-content\">\n {{{formattedStackTrace}}}\n </div>\n </div>\n `;\n }\n\n async onBeforeRender() {\n this.formattedStackTrace = this.formatStackTrace(this.stackTrace);\n }\n\n formatStackTrace(stackTrace) {\n if (!stackTrace) {\n return '<div class=\"text-muted p-3\">No stack trace available</div>';\n }\n\n // Convert to string if it's an object\n const traceStr = typeof stackTrace === 'string' ? stackTrace : JSON.stringify(stackTrace, null, 2);\n \n const lines = traceStr.split('\\n');\n let html = '';\n\n lines.forEach((line, index) => {\n if (!line.trim()) {\n html += '<div class=\"stack-trace-line\">&nbsp;</div>';\n return;\n }\n\n // Detect error message (usually the first line)\n if (index === 0 && (line.includes('Error:') || line.includes('Exception:'))) {\n html += `<div class=\"stack-trace-line stack-trace-error\">${this.escapeHtml(line)}</div>`;\n return;\n }\n\n // Detect file paths with line numbers\n // Pattern: at functionName (file.js:123:45) or file.js:123:45\n const filePattern = /(.+?)\\s*\\(([^:]+):(\\d+):(\\d+)\\)/;\n const simpleFilePattern = /^\\s*at\\s+([^:]+):(\\d+):(\\d+)/;\n \n let match = line.match(filePattern);\n if (match) {\n const [, funcName, filePath, lineNum, colNum] = match;\n html += `<div class=\"stack-trace-line stack-trace-file\">\n <span class=\"stack-trace-function\">${this.escapeHtml(funcName.trim())}</span>\n <span class=\"stack-trace-location\"> (${this.escapeHtml(filePath)}:<span class=\"stack-trace-line-number\">${lineNum}</span>:${colNum})</span>\n </div>`;\n return;\n }\n\n match = line.match(simpleFilePattern);\n if (match) {\n const [, filePath, lineNum, colNum] = match;\n html += `<div class=\"stack-trace-line stack-trace-file\">\n <span class=\"stack-trace-location\">at ${this.escapeHtml(filePath)}:<span class=\"stack-trace-line-number\">${lineNum}</span>:${colNum}</span>\n </div>`;\n return;\n }\n\n // Python-style stack trace: File \"...\", line X, in function\n const pythonPattern = /File\\s+\"([^\"]+)\",\\s+line\\s+(\\d+),\\s+in\\s+(.+)/;\n match = line.match(pythonPattern);\n if (match) {\n const [, filePath, lineNum, funcName] = match;\n html += `<div class=\"stack-trace-line stack-trace-file\">\n <span class=\"stack-trace-location\">File \"${this.escapeHtml(filePath)}\", line <span class=\"stack-trace-line-number\">${lineNum}</span>, in </span>\n <span class=\"stack-trace-function\">${this.escapeHtml(funcName)}</span>\n </div>`;\n return;\n }\n\n // Check if line starts with \"at \" (stack frame indicator)\n if (line.trim().startsWith('at ')) {\n html += `<div class=\"stack-trace-line stack-trace-file\">${this.escapeHtml(line)}</div>`;\n return;\n }\n\n // Default: context line\n html += `<div class=\"stack-trace-line stack-trace-context\">${this.escapeHtml(line)}</div>`;\n });\n\n return html;\n }\n\n updateStackTrace(newStackTrace) {\n this.stackTrace = newStackTrace;\n this.render();\n }\n}\n\nexport default StackTraceView;\n","import { IncidentHistory, IncidentHistoryList } from '@core/models/Incident.js';\n\nclass IncidentHistoryAdapter {\n constructor(incidentId) {\n this.incidentId = incidentId;\n this.collection = new IncidentHistoryList({ params: { incident: this.incidentId } });\n }\n\n async fetch() {\n await this.collection.fetch();\n return this.collection.models.map(item => this.transform(item));\n }\n\n transform(item) {\n return {\n id: item.get('id'),\n type: item.get('kind') === 'comment' ? 'user_comment' : 'system_event',\n author: {\n name: item.get('by.display_name') || 'System',\n avatarUrl: item.get('by.avatar.url')\n },\n timestamp: item.get('created'),\n content: item.get('note'),\n attachments: [] // Incident history doesn't have attachments in this phase\n };\n }\n\n async addNote(data) {\n const history = new IncidentHistory({\n incident: this.incidentId,\n note: data.text,\n kind: 'comment'\n });\n const resp = await history.save();\n if (resp.success) {\n await this.collection.fetch();\n }\n return resp;\n }\n}\n\nexport default IncidentHistoryAdapter;\n","/**\n * IncidentView - Detailed view for an Incident record\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport DataView from '@core/views/data/DataView.js';\nimport TableView from '@core/views/table/TableView.js';\nimport StackTraceView from '@core/views/data/StackTraceView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport { Incident, IncidentForms, IncidentEventList } from '@core/models/Incident.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport IncidentHistoryAdapter from './adapters/IncidentHistoryAdapter.js';\nimport ChatView from '@core/views/chat/ChatView.js';\n\nclass IncidentView extends View {\n constructor(options = {}) {\n super({\n className: 'incident-view',\n ...options\n });\n\n this.model = options.model || new Incident(options.data || {});\n this.incidentIcon = this.getIconForIncident(this.model.get('state'));\n\n this.template = `\n <div class=\"incident-view-container\">\n <!-- Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-4\">\n <div class=\"d-flex align-items-center gap-3\">\n <div class=\"fs-1 {{incidentIcon.color}}\">\n <i class=\"bi {{incidentIcon.icon}}\"></i>\n </div>\n <div>\n <h3 class=\"mb-1\">Incident #{{model.id}}</h3>\n <div class=\"text-muted small\">\n Category: {{model.category|capitalize}}\n </div>\n <div class=\"text-muted small mt-1\">\n Created: {{model.created|datetime}}\n </div>\n </div>\n </div>\n <div class=\"d-flex align-items-center gap-4\">\n <div class=\"text-end\">\n <div>State: <span class=\"badge bg-primary\">{{model.state|capitalize}}</span></div>\n <div class=\"text-muted small mt-1\">Priority: {{model.priority}}</div>\n </div>\n <div data-container=\"incident-context-menu\"></div>\n </div>\n </div>\n\n <!-- Tabs -->\n <div data-container=\"incident-tabs\"></div>\n </div>\n `;\n }\n\n getIconForIncident(state) {\n const s = state?.toLowerCase();\n if (s === 'resolved' || s === 'closed') return { icon: 'bi-check-circle-fill', color: 'text-success' };\n if (s === 'new' || s === 'opened') return { icon: 'bi-exclamation-triangle-fill', color: 'text-danger' };\n if (s === 'paused' || s === 'ignore') return { icon: 'bi-pause-circle-fill', color: 'text-warning' };\n return { icon: 'bi-shield-exclamation', color: 'text-secondary' };\n }\n\n async onInit() {\n // Overview Tab\n this.overviewView = new DataView({\n model: this.model,\n className: \"p-3\",\n columns: 2,\n fields: [\n { name: 'id', label: 'Incident ID' },\n { name: 'state', label: 'State', format: 'badge' },\n { name: 'priority', label: 'Priority' },\n { name: 'category', label: 'Category' },\n { name: 'model_name', label: 'Related Model' },\n { name: 'model_id', label: 'Related Model ID' },\n { name: 'details', label: 'Details', columns: 12, format: 'pre' },\n ]\n });\n\n // Events Tab\n const eventsCollection = new IncidentEventList({\n params: { incident: this.model.get('id') }\n });\n this.eventsView = new TableView({\n collection: eventsCollection,\n hideActivePillNames: ['incident'],\n columns: [\n { key: 'id', label: 'ID', width: '70px', sortable: true },\n { key: 'created', label: 'Date', formatter: 'datetime', sortable: true, width: '180px' },\n { key: 'category', label: 'Category', formatter: 'badge', sortable: true },\n { key: 'title', label: 'Title', sortable: true },\n { key: 'level', label: 'Level', sortable: true, width: '80px' },\n ],\n showAdd: false,\n actions: ['view'],\n paginated: true,\n size: 10\n });\n\n // History & Comments Tab\n const adapter = new IncidentHistoryAdapter(this.model.get('id'));\n this.historyView = new ChatView({ adapter });\n\n const tabs = { \n 'Overview': this.overviewView,\n 'Events': this.eventsView,\n 'History & Comments': this.historyView\n };\n \n const metadata = this.model.get('metadata') || {};\n \n // Add Stack Trace tab if present\n if (metadata.stack_trace) {\n this.stackTraceView = new StackTraceView({\n stackTrace: metadata.stack_trace\n });\n tabs['Stack Trace'] = this.stackTraceView;\n }\n \n // Add Metadata tab if there's metadata\n if (Object.keys(metadata).length > 0) {\n this.metadataView = new View({\n model: this.model,\n template: `<pre class=\"bg-light p-3 border rounded\"><code>{{{model.metadata|json}}}</code></pre>`\n });\n tabs['Metadata'] = this.metadataView;\n }\n\n this.tabView = new TabView({\n containerId: 'incident-tabs',\n tabs: tabs,\n activeTab: 'Overview'\n });\n this.addChild(this.tabView);\n\n // ContextMenu\n const incidentMenu = new ContextMenu({\n containerId: 'incident-context-menu',\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: [\n { label: 'Edit Incident', action: 'edit-incident', icon: 'bi-pencil' },\n { label: 'Resolve', action: 'resolve-incident', icon: 'bi-check-circle' },\n { type: 'divider' },\n { label: 'Delete Incident', action: 'delete-incident', icon: 'bi-trash', danger: true }\n ]\n }\n });\n this.addChild(incidentMenu);\n }\n\n async onActionEditIncident() {\n const resp = await Dialog.showModelForm({\n title: `Edit Incident #${this.model.id}`,\n model: this.model,\n formConfig: IncidentForms.edit,\n });\n if (resp) {\n this.render();\n }\n }\n \n async onActionResolveIncident() {\n await this.model.save({ state: 'resolved' });\n this.render();\n this.emit('incident:updated', { model: this.model });\n }\n\n async onActionDeleteIncident() {\n const confirmed = await Dialog.confirm(\n `Are you sure you want to delete this incident?`,\n 'Confirm Deletion',\n { confirmClass: 'btn-danger', confirmText: 'Delete' }\n );\n if (confirmed) {\n const resp = await this.model.destroy();\n if (resp.success) {\n this.emit('incident:deleted', { model: this.model });\n }\n }\n }\n}\n\nIncident.VIEW_CLASS = IncidentView;\n\nexport default IncidentView;\n","/**\n * IncidentTablePage - Incident management using TablePage component\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { IncidentList, IncidentForms } from '@core/models/Incident.js';\nimport IncidentView from './IncidentView.js';\n\nclass IncidentTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_incidents',\n pageName: 'Manage Incidents',\n router: \"admin/incidents\",\n Collection: IncidentList,\n\n formCreate: IncidentForms.create,\n formEdit: IncidentForms.edit,\n itemViewClass: IncidentView,\n\n viewDialogOptions: {\n header: false,\n size: 'xl'\n },\n\n defaultQuery: {\n sort: '-id',\n status: \"new\",\n },\n\n // Column definitions\n columns: [\n {\n key: 'id',\n label: 'ID',\n width: '60px',\n sortable: true,\n class: 'text-muted'\n },\n {\n key: 'status', label: \"Status\",\n filter: {\n type: 'multiselect',\n options: [\"new\", \"open\", \"paused\", \"resolved\", \"qa\", \"ignored\"],\n }\n },\n {\n key: 'created',\n label: 'Created',\n formatter: \"epoch|datetime\"\n },\n {\n key: 'scope',\n label: 'Scope',\n sortable: true,\n filter: {type:\"text\"}\n },\n {\n key: 'category',\n label: 'Category',\n sortable: true,\n filter: {type:\"text\"}\n },\n {\n key: 'priority',\n label: 'Priority',\n filter: {type:\"text\"}\n },\n {\n key: 'title',\n label: 'title',\n formatter: \"truncate(100)|default('No description')\"\n }\n ],\n\n filters: [\n {\n key: 'category__not',\n label: 'Not Category',\n filter: {type:\"text\"}\n },\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No incidents found. Click \"Add Incident\" to create your first incident.',\n\n // Batch actions\n batchBarLocation: 'top',\n batchActions: [\n { label: \"Open\", icon: \"bi bi-folder2-open\", action: \"open\" },\n { label: \"Resolve\", icon: \"bi bi-check-circle\", action: \"resolve\" },\n { label: \"Pause\", icon: \"bi bi-pause-circle\", action: \"pause\" },\n { label: \"Ignore\", icon: \"bi bi-x-circle\", action: \"ignore\" },\n { label: \"Merge\", icon: \"bi bi-merge\", action: \"merge\" }\n ],\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n\n async onActionBatchResolve(event, element) {\n const selected = this.tableView.getSelectedItems();\n if (!selected.length) return;\n\n const app = this.getApp();\n const result = await app.confirm(`Are you sure you want to close ${selected.length} incidents?`);\n if (!result) return;\n await Promise.all(selected.map(item => item.model.save({status: 'resolved'})));\n this.tableView.collection.fetch();\n }\n\n async onActionBatchOpen(event, element) {\n const selected = this.tableView.getSelectedItems();\n if (!selected.length) return;\n\n const app = this.getApp();\n const result = await app.confirm(`Are you sure you want to open ${selected.length} incidents?`);\n if (!result) return;\n await Promise.all(selected.map(item => item.model.save({status: 'open'})));\n this.tableView.collection.fetch();\n }\n\n async onActionBatchPause(event, element) {\n const selected = this.tableView.getSelectedItems();\n if (!selected.length) return;\n\n const app = this.getApp();\n const result = await app.confirm(`Are you sure you want to pause ${selected.length} incidents?`);\n if (!result) return;\n await Promise.all(selected.map(item => item.model.save({status: 'paused'})));\n this.tableView.collection.fetch();\n }\n\n async onActionBatchIgnore(event, element) {\n const selected = this.tableView.getSelectedItems();\n if (!selected.length) return;\n\n const app = this.getApp();\n const result = await app.confirm(`Are you sure you want to ignore ${selected.length} incidents?`);\n if (!result) return;\n await Promise.all(selected.map(item => item.model.save({status: 'ignored'})));\n this.tableView.collection.fetch();\n }\n\n async onActionBatchMerge(_event, _element) {\n const selected = this.tableView.getSelectedItems();\n if (!selected.length) return;\n\n const app = this.getApp();\n const result = await app.showForm({\n title: `Merge ${selected.length} incidents`,\n fields: [\n {\n name: 'merge',\n type: 'select',\n label: 'Select Parent Incident',\n options: selected.map(item => ({value: item.model.id, label: item.model.id})),\n required: true\n }\n ]\n });\n if (!result) return;\n\n // Find the parent model from selected items\n const parentModel = selected.find(item => item.model.id == result.merge)?.model;\n if (!parentModel) return;\n\n // Get list of all IDs to merge (excluding the parent)\n const mergeIds = selected\n .map(item => item.model.id)\n .filter(id => id != result.merge);\n\n // Save the merge operation to the parent model\n await parentModel.save({ merge: mergeIds });\n this.tableView.collection.fetch();\n }\n}\n\nexport default IncidentTablePage;\n","/**\n * EventView - Detailed view for an IncidentEvent record\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport DataView from '@core/views/data/DataView.js';\nimport StackTraceView from '@core/views/data/StackTraceView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport { IncidentEvent } from '@core/models/Incident.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\n\nclass EventView extends View {\n constructor(options = {}) {\n super({\n className: 'event-view',\n ...options\n });\n\n this.model = options.model || new IncidentEvent(options.data || {});\n this.eventIcon = this.getIconForEvent(this.model.get('level'));\n\n this.template = `\n <div class=\"event-view-container\">\n <!-- Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-4\">\n <div class=\"d-flex align-items-center gap-3\">\n <div class=\"fs-1 {{eventIcon.color}}\">\n <i class=\"bi {{eventIcon.icon}}\"></i>\n </div>\n <div>\n <h3 class=\"mb-1\">{{model.title|default('System Event')}}</h3>\n <div class=\"text-muted small\">\n Category: {{model.category|capitalize}}\n </div>\n <div class=\"text-muted small mt-1\">\n {{model.created|datetime}} from {{model.source_ip|default('Unknown IP')}}\n </div>\n </div>\n </div>\n <div data-container=\"event-context-menu\"></div>\n </div>\n\n <!-- Body -->\n <div data-container=\"event-tabs\"></div>\n </div>\n `;\n }\n\n getIconForEvent(level) {\n if (level >= 40) return { icon: 'bi-exclamation-octagon-fill', color: 'text-danger' }; // Error\n if (level >= 30) return { icon: 'bi-exclamation-triangle-fill', color: 'text-warning' }; // Warning\n if (level >= 20) return { icon: 'bi-info-circle-fill', color: 'text-info' }; // Info\n return { icon: 'bi-bell-fill', color: 'text-secondary' }; // Debug/Default\n }\n\n async onInit() {\n // Overview Tab\n this.overviewView = new DataView({\n model: this.model,\n className: \"p-3\",\n columns: 2,\n fields: [\n { name: 'id', label: 'Event ID' },\n { name: 'level', label: 'Level' },\n { name: 'hostname', label: 'Hostname' },\n { name: 'incident', label: 'Incident ID' },\n { name: 'model_name', label: 'Related Model' },\n { name: 'model_id', label: 'Related Model ID' },\n { name: 'details', label: 'Details', columns: 12 },\n ]\n });\n\n const tabs = { 'Overview': this.overviewView };\n \n const metadata = this.model.get('metadata') || {};\n \n // Add Stack Trace tab if present\n if (metadata.stack_trace) {\n this.stackTraceView = new StackTraceView({\n stackTrace: metadata.stack_trace\n });\n tabs['Stack Trace'] = this.stackTraceView;\n }\n \n // Add Metadata tab if there's metadata\n if (Object.keys(metadata).length > 0) {\n this.metadataView = new View({\n model: this.model,\n template: `<pre class=\"bg-light p-3 border rounded\"><code>{{{model.metadata|json}}}</code></pre>`\n });\n tabs['Metadata'] = this.metadataView;\n }\n\n this.tabView = new TabView({\n containerId: 'event-tabs',\n tabs: tabs,\n activeTab: 'Overview'\n });\n this.addChild(this.tabView);\n\n // ContextMenu\n const menuItems = [\n { label: 'View Incident', action: 'view-incident', icon: 'bi-shield-exclamation', disabled: !this.model.get('incident') },\n { label: 'View Related Model', action: 'view-model', icon: 'bi-box-arrow-up-right', disabled: !this.model.get('model_id') },\n { type: 'divider' },\n { label: 'Delete Event', action: 'delete-event', icon: 'bi-trash', danger: true }\n ];\n\n const eventMenu = new ContextMenu({\n containerId: 'event-context-menu',\n className: \"context-menu-view header-menu-absolute\",\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: menuItems\n }\n });\n this.addChild(eventMenu);\n }\n\n async onActionViewIncident() {\n console.log(\"TODO: View incident\", this.model.get('incident'));\n }\n\n async onActionViewModel() {\n console.log(\"TODO: View model\", this.model.get('model_name'), this.model.get('model_id'));\n }\n\n async onActionDeleteEvent() {\n const confirmed = await Dialog.confirm(\n `Are you sure you want to delete this event? This action cannot be undone.`,\n 'Confirm Deletion',\n { confirmClass: 'btn-danger', confirmText: 'Delete' }\n );\n if (confirmed) {\n const resp = await this.model.destroy();\n if (resp.success) {\n this.emit('event:deleted', { model: this.model });\n }\n }\n }\n}\n\nIncidentEvent.VIEW_CLASS = EventView;\n\nexport default EventView;\n","/**\n * EventTablePage - System events management using TablePage component\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { IncidentEventList, IncidentEventForms } from '@core/models/Incident.js';\nimport EventView from './EventView.js';\n\nclass EventTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_events',\n pageName: 'System Events',\n router: \"admin/events\",\n Collection: IncidentEventList,\n\n formEdit: IncidentEventForms.edit,\n itemViewClass: EventView,\n\n viewDialogOptions: {\n header: false,\n size: 'lg'\n },\n\n defaultQuery: {\n sort: '-id',\n category__not: \"ossec\",\n },\n\n // Column definitions\n columns: [\n { key: 'created', label: 'Timestamp', sortable: true, formatter: 'datetime' },\n {\n key: 'level', label: 'Level',\n sortable: true, formatter: 'badge',\n filter: {\n type: \"select\",\n options: [\n { value: '5', label: 'Critical' },\n { value: '4', label: 'Warning' },\n { value: '3', label: 'Info' },\n { value: '2', label: 'Debug' },\n { value: '1', label: 'Trace' }\n ]\n }\n },\n {\n key: 'scope',\n label: 'Scope',\n sortable: true, formatter: 'badge',\n filter: {\n type: \"combobox\",\n options: [\n { value: 'account', label: 'Account' },\n { value: 'incident', label: 'Incident' },\n { value: 'ossec', label: 'OSSEC' },\n { value: 'fileman', label: 'File Manager' },\n { value: 'metrics', label: 'Metrics' },\n { value: 'jobs', label: 'Jobs' },\n { value: 'aws', label: 'AWS' }\n\n ]\n }\n },\n {\n key: 'category',\n label: 'Category',\n sortable: true, formatter: 'badge',\n filter: {\n type: \"combobox\",\n options: [\n { value: 'rest_error', label: 'Rest Error' },\n { value: 'api_error', label: 'API Error' },\n { value: 'auth', label: 'Auth' },\n { value: 'database', label: 'Database' }\n ]\n }\n },\n { key: 'title', label: 'Title', sortable: true, formatter: 'truncate(50)' },\n {\n key: 'source_ip', label: 'Source IP', sortable: true,\n filter: {\n type: \"text\"\n }\n },\n {\n key: 'metadata.server', label: 'Server',\n sortable: true,\n filter: {\n type: \"text\"\n }\n },\n ],\n\n filters: [\n {\n key: 'category__not',\n label: 'Not Category',\n filter: {type:\"text\"}\n },\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: false,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No events found.',\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n}\n\nexport default EventTablePage;\n","import { TicketNote, TicketNoteList } from '@core/models/Tickets.js';\n\nclass TicketNoteAdapter {\n constructor(ticketId) {\n this.ticketId = ticketId;\n this.collection = new TicketNoteList({ params: { parent: this.ticketId, sort: 'created', size: 100} });\n }\n\n async fetch() {\n await this.collection.fetch();\n return this.collection.models.map(note => this.transform(note));\n }\n\n transform(note) {\n return {\n id: note.get('id'),\n type: 'user_comment', // Ticket notes are always user comments\n author: {\n id: note.get('user.id'),\n name: note.get('user.display_name') || 'System',\n avatarUrl: note.get('user.avatar.url')\n },\n timestamp: note.get('created'),\n content: note.get('note'),\n attachments: note.get('media') ? [note.get('media')] : []\n };\n }\n\n async addNote(data) {\n const note = new TicketNote();\n const resp = await note.save({\n parent: this.ticketId,\n note: data.text,\n media: data.files && data.files.length > 0 ? data.files[0].id : null\n });\n if (resp.success) {\n await this.collection.fetch(); // Refresh the collection\n }\n return resp;\n }\n}\n\nexport default TicketNoteAdapter;\n","import View from '@core/View.js';\nimport DataView from '@core/views/data/DataView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport { Ticket, TicketForms } from '@core/models/Tickets.js';\nimport ChatView from '@core/views/chat/ChatView.js';\nimport TicketNoteAdapter from './adapters/TicketNoteAdapter.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\n\nclass TicketView extends View {\n constructor(options = {}) {\n super({\n className: 'ticket-view',\n ...options\n });\n\n this.model = options.model || new Ticket(options.data || {});\n\n this.template = `\n <div class=\"ticket-view-container d-flex flex-column h-100\">\n <!-- Ticket Header -->\n <div class=\"d-flex justify-content-between align-items-start mb-3 flex-shrink-0\">\n <!-- Left Side: Primary Identity -->\n <div class=\"d-flex align-items-center gap-3\">\n <div class=\"avatar-placeholder rounded-circle bg-light d-flex align-items-center justify-content-center\" style=\"width: 80px; height: 80px;\">\n <i class=\"bi bi-ticket-perforated text-secondary\" style=\"font-size: 40px;\"></i>\n </div>\n <div>\n <h3 class=\"mb-1\">{{model.title|truncate(50)|default('Untitled Ticket')}}</h3>\n <div class=\"text-muted small\">\n <span>Ticket #{{model.id}}</span>\n <span class=\"mx-2\">|</span>\n <span>Priority: {{model.priority|capitalize}}</span>\n {{#model.assignee}}\n <span class=\"mx-2\">|</span>\n <span>Assigned to: {{model.assignee.display_name}}</span>\n {{/model.assignee}}\n </div>\n {{#model.incident}}\n <div class=\"text-muted small mt-1\">\n <i class=\"bi bi-exclamation-triangle\"></i> Related to incident: {{model.incident}}\n </div>\n {{/model.incident}}\n </div>\n </div>\n\n <!-- Right Side: Status & Actions -->\n <div class=\"d-flex align-items-start gap-4\">\n <div class=\"text-end\">\n <div class=\"d-flex align-items-center gap-2\">\n <span class=\"badge {{model.status|badgeClass}}\">{{model.status|capitalize}}</span>\n </div>\n {{#model.created}}\n <div class=\"text-muted small mt-1\">Created {{model.created|relative}}</div>\n {{/model.created}}\n {{#model.modified}}\n <div class=\"text-muted small\">Updated {{model.modified|relative}}</div>\n {{/model.modified}}\n </div>\n <div data-container=\"ticket-context-menu\"></div>\n </div>\n </div>\n\n <!-- Chat View (Full height) -->\n <div class=\"flex-grow-1\" style=\"min-height: 0;\" data-container=\"chat-view\"></div>\n </div>\n `;\n }\n\n async onInit() {\n // Chat View with compact theme (Option 4)\n const adapter = new TicketNoteAdapter(this.model.get('id'));\n this.chatView = new ChatView({\n containerId: 'chat-view',\n adapter: adapter,\n theme: 'compact', // Use compact admin-style theme\n currentUserId: this.getCurrentUserId(),\n inputPlaceholder: 'Add a note...',\n inputButtonText: 'Add Note'\n });\n this.addChild(this.chatView);\n\n // Context Menu\n const ticketMenu = new ContextMenu({\n containerId: 'ticket-context-menu',\n className: \"context-menu-view header-menu-absolute\",\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: [\n { label: 'Edit Ticket', action: 'edit-ticket', icon: 'bi-pencil' },\n { label: 'Change Status', action: 'change-status', icon: 'bi-tag' },\n { label: 'Set Priority', action: 'set-priority', icon: 'bi-flag' },\n { label: 'Assign User', action: 'assign-user', icon: 'bi-person' },\n { type: 'divider' },\n { label: 'Close Ticket', action: 'close-ticket', icon: 'bi-x-circle' },\n ]\n }\n });\n this.addChild(ticketMenu);\n }\n\n /**\n * Get current user ID for chat message positioning\n * @returns {number|null}\n */\n getCurrentUserId() {\n // Get from WebApp state or wherever your app stores current user\n const currentUser = window.app?.state?.user;\n return currentUser?.id || null;\n }\n\n // Context Menu Action Handlers\n async onActionEditTicket() {\n const resp = await Dialog.showModelForm({\n title: `Edit Ticket #${this.model.get('id')} - ${this.model.get('title')}`,\n model: this.model,\n size: 'lg',\n fields: TicketForms.edit.fields\n });\n if (resp) {\n this.render(); // Re-render to show updated data in header\n }\n }\n\n async onActionChangeStatus() {\n const statuses = ['new', 'open', 'in_progress', 'pending', 'resolved', 'closed', 'ignored'];\n const currentStatus = this.model.get('status');\n\n const result = await Dialog.showForm({\n title: 'Change Ticket Status',\n size: 'sm',\n fields: [\n {\n name: 'status',\n label: 'New Status',\n type: 'select',\n options: statuses.map(s => ({ value: s, label: s.replace('_', ' ').toUpperCase() })),\n value: currentStatus,\n required: true\n }\n ]\n });\n\n if (result) {\n try {\n await this.model.save({ status: result.status });\n this.render();\n } catch (error) {\n Dialog.alert({\n type: 'error',\n title: 'Error',\n message: 'Failed to update ticket status: ' + error.message\n });\n }\n }\n }\n\n async onActionSetPriority() {\n const priorities = ['low', 'normal', 'high', 'urgent'];\n const currentPriority = this.model.get('priority');\n\n const result = await Dialog.showForm({\n title: 'Set Ticket Priority',\n size: 'sm',\n fields: [\n {\n name: 'priority',\n label: 'Priority Level',\n type: 'select',\n options: priorities.map(p => ({ value: p, label: p.toUpperCase() })),\n value: currentPriority,\n required: true\n }\n ]\n });\n\n if (result) {\n try {\n await this.model.save({ priority: result.priority });\n this.render();\n } catch (error) {\n Dialog.alert({\n type: 'error',\n title: 'Error',\n message: 'Failed to update ticket priority: ' + error.message\n });\n }\n }\n }\n\n async onActionAssignUser() {\n console.log(\"TODO: Implement assign user dialog with user selector\");\n Dialog.alert({\n title: 'Coming Soon',\n message: 'User assignment feature will be implemented soon.'\n });\n }\n\n async onActionCloseTicket() {\n const confirmed = await Dialog.confirm({\n title: 'Close Ticket',\n message: `Are you sure you want to close ticket #${this.model.get('id')}?`,\n confirmText: 'Close Ticket',\n confirmClass: 'btn-warning'\n });\n\n if (confirmed) {\n try {\n await this.model.save({ status: 'closed' });\n this.render();\n Dialog.alert({\n type: 'success',\n title: 'Success',\n message: 'Ticket has been closed successfully.'\n });\n } catch (error) {\n Dialog.alert({\n type: 'error',\n title: 'Error',\n message: 'Failed to close ticket: ' + error.message\n });\n }\n }\n }\n}\n\nTicket.VIEW_CLASS = TicketView;\n\nexport default TicketView;\n","/**\n * TicketTablePage - Ticket management using TablePage component\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { TicketList, TicketForms, TicketCategories } from '@core/models/Tickets.js';\nimport TicketView from './TicketView.js';\n\nclass TicketTablePage extends TablePage {\n constructor(options = {}) {\n super({\n name: 'admin_tickets',\n pageName: 'Tickets',\n router: \"admin/tickets\",\n Collection: TicketList,\n\n formCreate: TicketForms.create,\n formEdit: TicketForms.edit,\n itemViewClass: TicketView,\n\n viewDialogOptions: {\n header: false\n },\n\n // Column definitions\n columns: [\n { key: 'id', label: 'ID', width: '70px', sortable: true, class: 'text-muted' },\n { key: 'title', label: 'Title', sortable: true},\n {\n key: 'status', label: 'Status', sortable: true,\n editable: true,\n editableOptions: {\n type: \"select\",\n options: [\n \"new\", \"open\", \"paused\", \"resolved\", \"qa\", \"ignored\"\n ]\n },\n filter: {\n type: \"multiselect\",\n placeHolder: \"Select Status\",\n options: [\n \"new\", \"open\", \"paused\", \"resolved\", \"qa\", \"ignored\"\n ]\n }\n },\n { key: 'priority', label: 'Priority', sortable: true },\n {\n key: 'category', label: 'Category', sortable: true,\n editable: true,\n editableOptions: {\n type: \"select\",\n options: [\n ... Object.keys(TicketCategories)\n ]\n },\n filter: {\n type: \"multiselect\",\n placeHolder: \"Select Category\",\n options: [\n ... Object.keys(TicketCategories)\n ]\n }\n },\n { key: 'assignee.display_name', label: 'Assignee', sortable: true, formatter: \"default('Unassigned')\" },\n { key: 'incident.id', label: 'Incident ID', sortable: true },\n { key: 'created', label: 'Created', sortable: true, formatter: 'datetime' }\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No tickets found.',\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n },\n ...options,\n });\n }\n}\n\nexport default TicketTablePage;\n","import View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport DataView from '@core/views/data/DataView.js';\nimport TableView from '@core/views/table/TableView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport { RuleSet, RuleList, BundleByOptions, MatchByOptions } from '@core/models/Incident.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\n\nclass RuleSetView extends View {\n constructor(options = {}) {\n super({\n className: 'ruleset-view',\n ...options\n });\n\n this.model = options.model || new RuleSet(options.data || {});\n\n this.template = `\n <div class=\"ruleset-view-container\">\n <!-- Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-4\">\n <div class=\"d-flex align-items-center gap-3\">\n <div class=\"fs-1 text-primary\"><i class=\"bi bi-gear-wide-connected\"></i></div>\n <div>\n <h3 class=\"mb-1\">{{model.name}}</h3>\n <div class=\"text-muted small\">Category: {{model.category}} | Priority: {{model.priority}}</div>\n </div>\n </div>\n <div data-container=\"ruleset-context-menu\"></div>\n </div>\n\n <!-- Tabs -->\n <div data-container=\"ruleset-tabs\"></div>\n </div>\n `;\n }\n\n async onInit() {\n // Get labels for current values\n const matchByValue = this.model.get('match_by');\n const matchByOption = MatchByOptions.find(opt => opt.value === matchByValue);\n const matchByLabel = matchByOption ? matchByOption.label : String(matchByValue);\n\n const bundleByValue = this.model.get('bundle_by');\n const bundleByOption = BundleByOptions.find(opt => opt.value === bundleByValue);\n const bundleByLabel = bundleByOption ? bundleByOption.label : String(bundleByValue);\n\n // Config Tab\n this.configView = new DataView({\n model: this.model,\n className: \"p-3\",\n columns: 2,\n fields: [\n { name: 'name', label: 'Name', cols: 4 },\n { name: 'category', label: 'Category', formatter: 'badge', cols: 4 },\n { name: 'is_active', label: 'Is Active', formatter: 'yesno_icon', cols: 4 },\n { name: 'priority', label: 'Priority', cols: 4 },\n { name: 'id', label: 'RuleSet ID', cols: 4 },\n\n {\n name: 'match_by',\n label: 'Match Logic',\n template: matchByLabel,\n cols: 4\n },\n {\n name: 'bundle_by',\n label: 'Bundle By',\n template: bundleByLabel,\n cols: 4\n },\n { name: 'bundle_minutes', label: 'Bundle Minutes', cols: 4 },\n { name: 'bundle_by_rule_set', label: 'Bundle By Rule Set', formatter: 'yesno_icon', cols: 4 },\n { name: 'handler', label: 'Handler', cols: 12 },\n ]\n });\n\n // Rules Tab\n const rulesCollection = new RuleList({\n params: { parent: this.model.get('id') }\n });\n this.rulesView = new TableView({\n collection: rulesCollection,\n hideActivePillNames: ['parent'],\n columns: [\n { key: 'id', label: 'ID', width: '70px' },\n { key: 'name', label: 'Name' },\n { key: 'field_name', label: 'Field' },\n { key: 'comparator', label: 'Comparator', width: '120px' },\n { key: 'value', label: 'Value' },\n { key: 'value_type', label: 'Type', width: '100px' },\n ],\n showAdd: true,\n clickAction: 'edit',\n actions: ['edit', 'delete'],\n contextMenu: [\n { label: 'Edit Rule', action: 'edit', icon: 'bi-pencil' },\n { label: 'Duplicate Rule', action: 'duplicate', icon: 'bi-files' },\n { divider: true },\n { label: 'Delete Rule', action: 'delete', icon: 'bi-trash', danger: true }\n ],\n // Pass the parent ID so new rules get associated with this ruleset\n addFormDefaults: {\n parent: this.model.get('id')\n }\n });\n\n this.tabView = new TabView({\n containerId: 'ruleset-tabs',\n tabs: {\n 'Configuration': this.configView,\n 'Rules': this.rulesView\n },\n activeTab: 'Configuration'\n });\n this.addChild(this.tabView);\n\n const contextMenu = new ContextMenu({\n containerId: 'ruleset-context-menu',\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: [\n { label: 'Edit RuleSet', action: 'edit-ruleset', icon: 'bi-pencil' },\n { label: 'Disable', action: 'disable-ruleset', icon: 'bi-toggle-off' },\n { type: 'divider' },\n { label: 'Delete RuleSet', action: 'delete-ruleset', icon: 'bi-trash', danger: true }\n ]\n }\n });\n this.addChild(contextMenu);\n }\n\n /**\n * Action handler: Edit RuleSet\n */\n async onActionEditRuleset() {\n const resp = await Dialog.showModelForm({\n title: `Edit RuleSet - ${this.model.get('name')}`,\n model: this.model,\n formConfig: RuleSet.EDIT_FORM,\n });\n if (resp) {\n await this.render();\n }\n }\n\n /**\n * Action handler: Disable/Enable RuleSet\n */\n async onActionDisableRuleset() {\n const isActive = this.model.get('is_active');\n const newStatus = !isActive;\n\n try {\n this.model.set('is_active', newStatus);\n await this.model.save();\n await this.render();\n\n Dialog.showToast({\n message: `RuleSet ${newStatus ? 'enabled' : 'disabled'} successfully`,\n type: 'success'\n });\n } catch (error) {\n Dialog.showToast({\n message: `Failed to update RuleSet: ${error.message}`,\n type: 'error'\n });\n }\n }\n\n /**\n * Action handler: Delete RuleSet\n */\n async onActionDeleteRuleset() {\n const confirmed = await Dialog.confirm({\n title: 'Delete RuleSet',\n message: `Are you sure you want to delete the ruleset \"${this.model.get('name')}\"? This action cannot be undone.`,\n confirmText: 'Delete',\n confirmClass: 'btn-danger'\n });\n\n if (confirmed) {\n try {\n await this.model.destroy();\n\n Dialog.showToast({\n message: 'RuleSet deleted successfully',\n type: 'success'\n });\n\n // Close the dialog\n const dialog = this.element?.closest('.modal');\n if (dialog) {\n const bsModal = bootstrap.Modal.getInstance(dialog);\n if (bsModal) {\n bsModal.hide();\n }\n }\n\n // Emit event to parent to refresh the table\n this.emit('ruleset:deleted', { model: this.model });\n } catch (error) {\n Dialog.showToast({\n message: `Failed to delete RuleSet: ${error.message}`,\n type: 'error'\n });\n }\n }\n }\n}\n\nRuleSetView.VIEW_CLASS = RuleSetView;\n\nexport default RuleSetView;\n","import TablePage from '@core/pages/TablePage.js';\nimport { RuleSetList } from '@core/models/Incident.js';\nimport RuleSetView from './RuleSetView.js';\n\nclass RuleSetTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_rulesets',\n pageName: 'Rule Engine',\n router: \"admin/rulesets\",\n Collection: RuleSetList,\n itemView: RuleSetView,\n viewDialogOptions: {\n header: false,\n size: 'xl'\n },\n\n columns: [\n { key: 'id', label: 'ID', width: '70px', sortable: true, class: 'text-muted' },\n { key: 'name', label: 'Name', sortable: true },\n { key: 'category', label: 'Category', sortable: true, formatter: 'badge' },\n { key: 'priority', label: 'Priority', sortable: true },\n { key: 'match_by', label: 'Match Logic', formatter: (v) => v === 0 ? 'ALL' : 'ANY' }\n ],\n\n selectable: true,\n searchable: true,\n sortable: true,\n paginated: true,\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n tableOptions: {\n pageSizes: [10, 25, 50],\n defaultPageSize: 25,\n emptyMessage: 'No rule sets found.',\n emptyIcon: 'bi-gear',\n actions: [\"view\", \"edit\", \"delete\"],\n }\n });\n }\n}\n\nexport default RuleSetTablePage;\n","/**\n * EmailDomainTablePage - Admin page for managing SES/SNS Email Domains\n * Clean implementation following simplified TablePage pattern\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport View from '@core/View.js';\nimport { EmailDomain, EmailDomainList, EmailDomainForms } from '@core/models/Email.js';\n\nclass EmailDomainTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_email_domains',\n pageName: 'Email Domains',\n router: 'admin/email/domains',\n Collection: EmailDomainList,\n formCreate: EmailDomainForms.create,\n formEdit: EmailDomainForms.edit,\n\n // Column definitions\n columns: [\n {\n key: 'id',\n label: 'ID',\n width: '70px',\n sortable: true,\n class: 'text-muted'\n },\n {\n key: 'name',\n label: 'Domain',\n sortable: true\n },\n {\n key: 'region',\n label: 'Region',\n sortable: true,\n formatter: \"default('—')\"\n },\n {\n key: 'receiving_enabled',\n label: 'Receiving',\n formatter: \"boolean|badge\"\n },\n {\n key: 'can_send',\n label: 'Send Verified',\n formatter: \"boolean|badge\"\n },\n {\n key: 'can_recv',\n label: 'Recv Verified',\n formatter: \"boolean|badge\"\n },\n {\n key: 'created',\n label: 'Created',\n formatter: \"epoch|datetime\"\n }\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No domains found. Click \"Add Domain\" to get started.',\n\n // Context menu configuration\n contextMenu: [\n {\n icon: 'bi-shield-check',\n action: 'edit-aws-creds',\n label: 'Edit AWS Credentials'\n },\n {\n icon: 'bi-rocket-takeoff',\n action: 'onboard',\n label: 'Onboard'\n },\n {\n icon: 'bi-shield-check',\n action: 'audit',\n label: 'Audit'\n },\n {\n icon: 'bi-arrow-repeat',\n action: 'reconcile',\n label: 'Reconcile'\n }\n ],\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n\n async onActionEditAwsCreds(event, element) {\n const item = this.collection.get(element.dataset.id);\n const result = await Dialog.showModelForm({\n model: item,\n formConfig: EmailDomainForms.credentials,\n }\n );\n return true;\n }\n\n async onActionOnboard(event, element) {\n const item = this.collection.get(element.dataset.id);\n const model = new EmailDomain({ id: item.id });\n\n const formData = await Dialog.showForm(EmailDomainForms.onboard);\n if (!formData) return;\n\n try {\n const resp = await model.onboard(formData);\n if (!resp.success) {\n throw new Error(resp.message || 'Network error during onboarding');\n }\n if (!resp.data?.status) {\n throw new Error(resp.data?.error || 'Onboarding failed');\n }\n\n this.getApp()?.toast?.success('Domain onboarding completed successfully');\n await this.refresh();\n } catch (err) {\n console.error('Onboard error:', err);\n this.showError(err.message || 'Failed to onboard domain');\n }\n }\n\n async onActionAudit(event, element) {\n const item = this.collection.get(element.dataset.id);\n const model = new EmailDomain({ id: item.id });\n\n try {\n const resp = await model.audit();\n if (!resp.success) {\n throw new Error(resp.message || 'Network error during audit');\n }\n if (!resp.data?.status) {\n throw new Error(resp.data?.error || 'Audit failed');\n }\n\n const result = resp.data?.data || {};\n await Dialog.showDialog({\n title: `Audit Report - ${item.name}`,\n body: new View({\n template: `\n <div>\n <p class=\"text-muted\">Drift report and status:</p>\n <pre class=\"bg-light p-3 rounded small\"><code>{{{data.result|json}}}</code></pre>\n </div>\n `,\n data: { result }\n }),\n size: 'lg'\n });\n } catch (err) {\n console.error('Audit error:', err);\n this.showError(err.message || 'Failed to audit domain');\n }\n }\n\n async onActionReconcile(event, element) {\n const item = this.collection.get(element.dataset.id);\n const model = new EmailDomain({ id: item.id });\n\n try {\n const resp = await model.reconcile();\n if (!resp.success) {\n throw new Error(resp.message || 'Network error during reconcile');\n }\n if (!resp.data?.status) {\n throw new Error(resp.data?.error || 'Reconcile failed');\n }\n\n this.getApp()?.toast?.success('Reconcile completed successfully');\n await this.refresh();\n } catch (err) {\n console.error('Reconcile error:', err);\n this.showError(err.message || 'Failed to reconcile domain');\n }\n }\n}\n\nexport default EmailDomainTablePage;\n","/**\n * EmailMailboxTablePage - Admin page for managing Mailboxes\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { MailboxList, MailboxForms, Mailbox } from '@core/models/Email.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\n\nclass EmailMailboxTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_email_mailboxes',\n pageName: 'Mailboxes',\n router: 'admin/email/mailboxes',\n Collection: MailboxList,\n\n formCreate: MailboxForms.create,\n formEdit: MailboxForms.edit,\n clickAction: \"edit\",\n\n // Table columns\n columns: [\n { key: 'id', label: 'ID', width: '70px', sortable: true, class: 'text-muted' },\n { key: 'email', label: 'Email', sortable: true },\n { key: 'domain.name', label: 'Domain', sortable: true, formatter: \"default('—')\" },\n { key: 'allow_inbound', label: 'Inbound', formatter: \"boolean|badge\" },\n { key: 'allow_outbound', label: 'Outbound', formatter: \"boolean|badge\" },\n { key: 'is_system_default', label: 'System Default', formatter: \"boolean|badge\" },\n { key: 'is_domain_default', label: 'Domain Default', formatter: \"boolean|badge\" }\n ],\n\n // Features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No mailboxes found. Click \"Add Mailbox\" to create one.',\n\n // Context menu configuration\n contextMenu: [\n { icon: 'bi-envelope', action: 'send-email', label: 'Send Email' },\n { icon: 'bi-envelope', action: 'send-template-email', label: 'Send Template Email' }\n ],\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n\n async onActionSendEmail(event, element) {\n const item = this.collection.get(element.dataset.id);\n // Implement send email action\n const data = await Dialog.showForm({\n title: 'Send Email',\n fields: [\n { name: 'to', label: 'To', type: 'email', required: true },\n { name: 'subject', label: 'Subject', type: 'text', required: true },\n { name: 'body_html', label: 'Body', type: 'textarea', required: true }\n ]\n });\n data.from_email = item.get('email');\n const result = await Mailbox.sendEmail(data);\n if (result.success) {\n this.getApp().toast.success('Email sent successfully');\n } else {\n let msg = \"Failed to send email\";\n if (result.data.details) {\n msg = result.data.details;\n } else if (result.data.error) {\n msg = result.data.error;\n }\n console.log(result);\n this.getApp().toast.error(msg);\n }\n }\n\n async onActionSendTemplateEmail(event, element) {\n const item = this.collection.get(element.dataset.id);\n // Implement send email action\n const data = await Dialog.showForm({\n title: 'Send Email',\n fields: [\n { name: 'to', label: 'To', type: 'email', required: true },\n { name: 'subject', label: 'Subject', type: 'text', required: true },\n { name: 'template_name', label: 'Template', type: 'text', required: true },\n { name: 'template_context', label: 'Context', type: 'textarea', required: true }\n ]\n });\n data.from_email = item.get('email');\n const result = await Mailbox.sendEmail(data);\n if (result.success) {\n this.getApp().toast.success('Email sent successfully');\n } else {\n let msg = \"Failed to send email\";\n if (result.data.details) {\n msg = result.data.details;\n } else if (result.data.error) {\n msg = result.data.error;\n }\n console.log(result);\n this.getApp().toast.error(msg);\n }\n }\n}\n\nexport default EmailMailboxTablePage;\n","/**\n * EmailTemplateView - A clean view for previewing email templates\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport { EmailTemplate } from '@core/models/Email.js';\n\nclass EmailTemplateView extends View {\n constructor(options = {}) {\n super({\n className: 'email-template-view',\n ...options\n });\n\n this.model = options.model || new EmailTemplate(options.data || {});\n this.hasHtml = !!this.model.get('html_template');\n this.hasText = !!this.model.get('text_template');\n this.hasMetadata = this.model.get('metadata') && Object.keys(this.model.get('metadata')).length > 0;\n\n this.template = `\n <div class=\"email-template-view-container p-3\">\n <!-- Header -->\n <div class=\"template-header border-bottom pb-3 mb-3\">\n <h4 class=\"mb-1\">{{model.name}}</h4>\n <div class=\"text-muted\">\n <strong>Subject:</strong> {{model.subject_template|default('Not set')}}\n </div>\n </div>\n\n <!-- Tabs -->\n <div data-container=\"template-tabs\"></div>\n </div>\n `;\n }\n\n async onInit() {\n const tabs = {};\n\n if (this.hasHtml) {\n tabs['HTML Preview'] = new View({\n model: this.model,\n template: `<div class=\"email-html-content border rounded p-3\" style=\"height: 500px; overflow-y: auto;\">{{model.html_template}}</div>`\n });\n }\n\n if (this.hasText) {\n tabs['Text Version'] = new View({\n model: this.model,\n template: `<pre class=\"email-text-content p-3 bg-light border rounded\" style=\"white-space: pre-wrap; word-wrap: break-word;\">{{model.text_template}}</pre>`\n });\n }\n\n if (this.hasMetadata) {\n tabs['Metadata'] = new View({\n model: this.model,\n template: `<pre class=\"email-metadata-content p-3 bg-light border rounded\"><code>{{model.metadata|json}}</code></pre>`\n });\n }\n\n this.tabView = new TabView({\n containerId: 'template-tabs',\n tabs: tabs,\n activeTab: Object.keys(tabs)[0] || ''\n });\n this.addChild(this.tabView);\n }\n}\n\nexport default EmailTemplateView;\n","/**\n * EmailTemplateTablePage - Admin page for managing Email Templates\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { EmailTemplateList, EmailTemplateForms } from '@core/models/Email.js';\nimport EmailTemplateView from './EmailTemplateView.js';\n\nclass EmailTemplateTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_email_templates',\n pageName: 'Email Templates',\n router: 'admin/email/templates',\n Collection: EmailTemplateList,\n\n formCreate: EmailTemplateForms.create,\n formEdit: EmailTemplateForms.edit,\n itemViewClass: EmailTemplateView,\n clickAction: \"edit\",\n\n viewDialogOptions: {\n header: false,\n size: 'xl',\n scrollable: true\n },\n\n formDialogConfig: { // Dialog options for forms\n size: 'fullscreen',\n },\n\n // Table columns\n columns: [\n { key: 'id', label: 'ID', width: '70px', sortable: true, class: 'text-muted' },\n { key: 'name', label: 'Name', sortable: true },\n { key: 'created', label: 'Created', formatter: 'datetime' },\n { key: 'modified', label: 'Modified', formatter: 'datetime' }\n ],\n\n // Features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No email templates found. Click \"Add Template\" to create your first one.',\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n}\n\nexport default EmailTemplateTablePage;\n","/**\n * EmailView - A slick, email-client-like view for sent messages\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport { SentMessage } from '@core/models/Email.js';\n\nclass EmailView extends View {\n constructor(options = {}) {\n super({\n className: 'email-view',\n ...options\n });\n\n this.model = options.model || new SentMessage(options.data || {});\n this.hasHtml = !!this.model.get('body_html');\n this.hasText = !!this.model.get('body_text');\n this.hasContext = this.model.get('template_context') && Object.keys(this.model.get('template_context')).length > 0;\n\n this.template = `\n <div class=\"email-view-container p-3\">\n <!-- Email Header -->\n <div class=\"email-header border-bottom pb-3 mb-3\">\n <h4 class=\"mb-2\">{{model.subject}}</h4>\n <div class=\"d-flex justify-content-between align-items-center\">\n <div class=\"d-flex align-items-center\">\n <div class=\"flex-shrink-0\">\n <i class=\"bi bi-person-circle fs-2 text-secondary\"></i>\n </div>\n <div class=\"ms-3\">\n <div class=\"fw-bold\">{{model.mailbox.email}}</div>\n <div class=\"text-muted small\">To: {{model.to_addresses|list}}</div>\n </div>\n </div>\n <div class=\"text-end\">\n <div class=\"text-muted small\">{{model.created|datetime}}</div>\n <span class=\"badge {{model.status|badge}} mt-1\">{{model.status|capitalize}}</span>\n </div>\n </div>\n </div>\n\n <!-- Email Body Tabs -->\n <div data-container=\"email-tabs\"></div>\n </div>\n `;\n }\n\n async onInit() {\n const tabs = {};\n\n if (this.hasHtml) {\n tabs['HTML'] = new View({\n model: this.model,\n template: `<div class=\"email-html-content border rounded p-3\" style=\"height: 500px; overflow-y: auto;\">{{{model.body_html}}}</div>`\n });\n }\n\n if (this.hasText) {\n tabs['Text'] = new View({\n model: this.model,\n template: `<pre class=\"email-text-content p-3 bg-light border rounded\" style=\"white-space: pre-wrap; word-wrap: break-word;\">{{model.body_text}}</pre>`\n });\n }\n\n if (this.hasContext) {\n tabs['Context'] = new View({\n model: this.model,\n template: `<pre class=\"email-context-content p-3 bg-light border rounded\"><code>{{model.template_context|json}}</code></pre>`\n });\n }\n\n this.tabView = new TabView({\n containerId: 'email-tabs',\n tabs: tabs,\n activeTab: this.hasHtml ? 'HTML' : (this.hasText ? 'Text' : 'Context')\n });\n this.addChild(this.tabView);\n }\n}\n\nSentMessage.VIEW_CLASS = EmailView;\n\n\nexport default EmailView;\n","/**\n * SentMessageTablePage - Admin page for viewing sent emails\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { SentMessageList } from '@core/models/Email.js';\nimport EmailView from './EmailView.js';\n\nclass SentMessageTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_email_sent',\n pageName: 'Sent Messages',\n router: 'admin/email/sent',\n Collection: SentMessageList,\n \n itemViewClass: EmailView,\n viewDialogOptions: {\n header: false,\n size: 'xl',\n scrollable: true\n },\n\n // Table columns\n columns: [\n { key: 'id', label: 'ID', width: '70px', sortable: true, class: 'text-muted' },\n { key: 'mailbox.email', label: 'From', sortable: true },\n { key: 'to_addresses', label: 'To', sortable: false, formatter: \"list\" },\n { key: 'subject', label: 'Subject', sortable: true },\n { key: 'status', label: 'Status', formatter: 'badge' },\n { key: 'status_reason', label: 'Reason', formatter: \"truncate(80)|default('—')\" },\n { key: 'created', label: 'Created', formatter: 'datetime' }\n ],\n\n // Features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: false,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No sent messages found.',\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n}\n\nexport default SentMessageTablePage;","/**\n * Phonehub.js\n * Models and Collections for PhoneHub phone numbers and SMS with helper actions.\n *\n * Endpoints (all require authentication unless noted):\n * - Phone Number\n * - POST /api/phonehub/number/normalize\n * - POST /api/phonehub/number/lookup\n * - GET /api/phonehub/number\n * - GET /api/phonehub/number/:id\n * - PUT /api/phonehub/number/:id\n * - DELETE /api/phonehub/number/:id\n *\n * - SMS\n * - POST /api/phonehub/sms/send\n * - GET /api/phonehub/sms\n * - GET /api/phonehub/sms/:id\n *\n * Notes on response shapes:\n * - List endpoints typically return { status: true, data: [...], count, size, start }\n * - Single item endpoints typically return { status: true, data: {...} }\n * - Action endpoints (normalize, lookup, send) return { status: true, data: {...} } or { status: false, error }\n */\n\nimport Model from '@core/Model.js';\nimport Collection from '@core/Collection.js';\nimport rest from '@core/Rest.js';\n\n// ======================================================\n// PhoneNumber Model & Collection\n// ======================================================\n\n/**\n * PhoneNumber - Represents a cached phone number lookup result.\n */\nclass PhoneNumber extends Model {\n constructor(data = {}, options = {}) {\n super(data, {\n endpoint: '/api/phonehub/number',\n ...options\n });\n }\n\n /**\n * Normalize an arbitrary phone_number string to E.164 format.\n * @param {string} phoneNumber - Raw phone number input.\n * @param {string} [countryCode='US'] - Optional country code (default US).\n * @returns {Promise<{success: boolean, phone_number?: string, data?: object, error?: string, response?: any}>}\n */\n static async normalize(phoneNumber, countryCode = 'US') {\n const url = '/api/phonehub/number/normalize';\n const payload = {\n phone_number: phoneNumber\n };\n if (countryCode) payload.country_code = countryCode;\n\n const resp = await rest.POST(url, payload);\n const body = resp?.data ?? resp; // rest wrapper or raw\n const ok = body?.status === true || body?.success === true;\n\n if (ok) {\n const normalized = body?.data?.phone_number ?? body?.phone_number;\n return { success: true, phone_number: normalized, data: body?.data ?? body, response: resp };\n }\n return { success: false, error: body?.error || 'Normalization failed', response: resp };\n }\n\n /**\n * Lookup phone number details (carrier, line_type, owner, etc.)\n * @param {string} phoneNumber - E.164 or raw phone number.\n * @param {object} [options] - { force_refresh?: boolean, group?: number }\n * @returns {Promise<{success: boolean, model?: PhoneNumber, data?: object, error?: string, response?: any}>}\n */\n static async lookup(phoneNumber, options = {}) {\n const url = '/api/phonehub/number/lookup';\n const resp = await rest.POST(url, {\n phone_number: phoneNumber,\n ...options\n });\n const body = resp?.data ?? resp;\n const ok = body?.status === true || body?.success === true;\n\n if (ok) {\n const data = body?.data ?? {};\n const model = new PhoneNumber(data, { endpoint: '/api/phonehub/number' });\n return { success: true, model, data, response: resp };\n }\n return { success: false, error: body?.error || 'Phone lookup failed', response: resp };\n }\n\n\n}\n\n/**\n * PhoneNumberList - Paginated collection of PhoneNumber models.\n */\nclass PhoneNumberList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: PhoneNumber,\n endpoint: '/api/phonehub/number',\n size: 10,\n ...options\n });\n }\n\n\n}\n\n// ======================================================\n// SMS Model & Collection\n// ======================================================\n\n/**\n * SMS - Represents an SMS message (sent or received).\n */\nclass SMS extends Model {\n constructor(data = {}, options = {}) {\n super(data, {\n endpoint: '/api/phonehub/sms',\n ...options\n });\n }\n\n /**\n * Send SMS via PhoneHub (Twilio under the hood).\n * @param {object} params - { to_number, body, from_number?, group?, metadata? }\n * @returns {Promise<{success: boolean, model?: SMS, data?: object, error?: string, response?: any}>}\n */\n static async send(params = {}) {\n const url = '/api/phonehub/sms/send';\n const resp = await rest.POST(url, params);\n const body = resp?.data ?? resp;\n const ok = body?.status === true || body?.success === true;\n\n if (ok) {\n const data = body?.data ?? {};\n const model = new SMS(data, { endpoint: '/api/phonehub/sms' });\n return { success: true, model, data, response: resp };\n }\n return { success: false, error: body?.error || 'Failed to send SMS', response: resp };\n }\n\n\n\n\n}\n\n/**\n * SMSList - Paginated collection of SMS models.\n */\nclass SMSList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: SMS,\n endpoint: '/api/phonehub/sms',\n size: 10,\n ...options\n });\n }\n\n\n}\n\n// Exported API\nexport {\n PhoneNumber,\n PhoneNumberList,\n SMS,\n SMSList\n};\n\nexport default {\n PhoneNumber,\n PhoneNumberList,\n SMS,\n SMSList\n};","/**\n * PhoneNumberView - Detailed view for a PhoneHub PhoneNumber record\n *\n * Mirrors GeoIPView structure:\n * - Header with primary identifier and quick info\n * - Tabbed content using TabView and DataView sections\n * - Minimal context menu for refresh and delete\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport DataView from '@core/views/data/DataView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport { PhoneNumber } from '@core/models/Phonehub.js';\n\nclass PhoneNumberView extends View {\n constructor(options = {}) {\n super({\n className: 'phone-number-view',\n ...options\n });\n\n this.model = options.model || new PhoneNumber(options.data || {});\n\n this.template = `\n <div class=\"phone-number-view-container\">\n <!-- Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-4\">\n <!-- Left Side: Icon & Info -->\n <div class=\"d-flex align-items-center gap-3\">\n <div class=\"fs-1 text-primary\">\n <i class=\"bi bi-telephone\"></i>\n </div>\n <div>\n <h3 class=\"mb-1\">{{model.phone_number|default('Unknown Number')}}</h3>\n <div class=\"text-muted small\">\n {{model.carrier|default('—')}} {{#model.line_type}}· {{model.line_type|capitalize}}{{/model.line_type}}\n </div>\n <div class=\"text-muted small mt-1\">\n {{#model.country_code}}Country: {{model.country_code}}{{/model.country_code}}\n {{#model.region}} · Region: {{model.region}}{{/model.region}}\n {{#model.state}} · State: {{model.state}}{{/model.state}}\n </div>\n </div>\n </div>\n\n <!-- Right Side: Actions -->\n <div class=\"d-flex align-items-center gap-4\">\n <div data-container=\"phone-context-menu\"></div>\n </div>\n </div>\n\n <!-- Tabs -->\n <div data-container=\"phone-tabs\"></div>\n </div>\n `;\n }\n\n async onInit() {\n // Overview Tab\n this.overviewView = new DataView({\n model: this.model,\n className: 'p-3',\n showEmptyValues: true,\n emptyValueText: '—',\n columns: 2,\n fields: [\n { name: 'phone_number', label: 'Phone Number', cols: 6 },\n { name: 'country_code', label: 'Country Code', cols: 6 },\n { name: 'region', label: 'Region', cols: 6 },\n { name: 'state', label: 'State', cols: 6 },\n { name: 'registered_owner', label: 'Registered Owner', cols: 6 },\n { name: 'owner_type', label: 'Owner Type', formatter: 'capitalize', cols: 6 },\n { name: 'is_valid', label: 'Valid', formatter: 'yesnoicon', cols: 4 },\n { name: 'is_mobile', label: 'Mobile', formatter: 'yesnoicon', cols: 4 },\n { name: 'is_voip', label: 'VOIP', formatter: 'yesnoicon', cols: 4 },\n ]\n });\n\n // Carrier & Status Tab\n this.carrierView = new DataView({\n model: this.model,\n className: 'p-3',\n showEmptyValues: true,\n emptyValueText: '—',\n columns: 2,\n fields: [\n { name: 'carrier', label: 'Carrier', cols: 6 },\n { name: 'line_type', label: 'Line Type', formatter: 'capitalize', cols: 6 },\n { name: 'lookup_provider', label: 'Lookup Provider', formatter: 'capitalize', cols: 6 },\n { name: 'lookup_count', label: 'Lookup Count', cols: 6 },\n { name: 'last_lookup_at', label: 'Last Lookup', formatter: 'datetime', cols: 6 },\n { name: 'lookup_expires_at', label: 'Cache Expires', formatter: 'datetime', cols: 6 }\n ]\n });\n\n // Address Tab\n this.addressView = new DataView({\n model: this.model,\n className: 'p-3',\n showEmptyValues: true,\n emptyValueText: '—',\n columns: 2,\n fields: [\n { name: 'address_line1', label: 'Address Line 1', cols: 12 },\n { name: 'address_city', label: 'City', cols: 4 },\n { name: 'address_state', label: 'State', cols: 4 },\n { name: 'address_zip', label: 'ZIP', cols: 4 },\n { name: 'address_country', label: 'Country', cols: 6 }\n ]\n });\n\n // Metadata Tab\n this.metadataView = new DataView({\n model: this.model,\n className: 'p-3',\n showEmptyValues: true,\n emptyValueText: '—',\n columns: 2,\n fields: [\n { name: 'id', label: 'Record ID', cols: 6 },\n { name: 'created', label: 'Created', formatter: 'datetime', cols: 6 },\n { name: 'modified', label: 'Last Modified', formatter: 'datetime', cols: 6 }\n ]\n });\n\n const tabs = {\n 'Overview': this.overviewView,\n 'Carrier': this.carrierView,\n 'Address': this.addressView,\n 'Metadata': this.metadataView\n };\n\n this.tabView = new TabView({\n containerId: 'phone-tabs',\n tabs,\n activeTab: 'Overview'\n });\n this.addChild(this.tabView);\n\n // Minimal ContextMenu\n const menuItems = [\n { label: 'Refresh Lookup', action: 'refresh-lookup', icon: 'bi-arrow-repeat' },\n { type: 'divider' },\n { label: 'Delete Record', action: 'delete-phone', icon: 'bi-trash', danger: true }\n ];\n\n const ctxMenu = new ContextMenu({\n containerId: 'phone-context-menu',\n className: 'context-menu-view header-menu-absolute',\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: menuItems\n }\n });\n this.addChild(ctxMenu);\n }\n\n // Actions\n\n async onActionRefreshLookup() {\n const number = this.model.get('phone_number');\n if (!number) {\n this.getApp()?.toast?.warning?.('No phone number to lookup');\n return;\n }\n\n try {\n this.getApp()?.toast?.info?.('Refreshing lookup...');\n // Force refresh lookup and update current model\n const resp = await PhoneNumber.lookup(number, { force_refresh: true });\n if (resp.success && resp.data) {\n this.model.set(resp.data);\n await this.render();\n this.getApp()?.toast?.success?.('Lookup refreshed');\n } else {\n const msg = resp.error || 'Lookup failed';\n this.getApp()?.toast?.error?.(msg);\n }\n } catch (e) {\n this.getApp()?.toast?.error?.(e.message || 'Lookup failed');\n }\n }\n\n async onActionDeletePhone() {\n const confirmed = await Dialog.confirm(\n `Are you sure you want to delete the record for \"${this.model.get('phone_number') || 'this number'}\"?`,\n 'Confirm Deletion',\n { confirmClass: 'btn-danger', confirmText: 'Delete' }\n );\n\n if (!confirmed) return;\n\n try {\n const resp = await this.model.destroy();\n if (resp?.success) {\n this.emit('phone:deleted', { model: this.model });\n } else {\n this.getApp()?.toast?.error?.('Delete failed');\n }\n } catch (e) {\n this.getApp()?.toast?.error?.(e.message || 'Delete failed');\n }\n }\n}\n\nPhoneNumberView.MODEL_CLASS = PhoneNumber;\n\nexport default PhoneNumberView;\n","/**\n * PhoneNumberTablePage - PhoneHub numbers management using TablePage component\n * Mirrors the GeoLocatedIPTablePage pattern with minimal, consistent configuration.\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { PhoneNumberList, PhoneNumber } from '@core/models/Phonehub.js';\nimport PhoneNumberView from './PhoneNumberView.js';\n\nclass PhoneNumberTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n\n // Identity\n name: 'admin_phonehub_numbers',\n pageName: 'Phone Numbers',\n router: 'admin/phonehub/numbers',\n\n // Data source\n Collection: PhoneNumberList,\n\n // Item view configuration\n itemView: PhoneNumberView,\n viewDialogOptions: {\n header: false,\n // size: 'xl'\n },\n\n // Column definitions\n columns: [\n { key: 'phone_number', label: 'Phone Number', sortable: true },\n { key: 'carrier', label: 'Carrier', sortable: true, formatter: \"default('—')\" },\n { key: 'line_type', label: 'Line Type', sortable: true, formatter: \"capitalize\" },\n { key: 'is_mobile', label: 'Mobile', formatter: 'yesnoicon' },\n { key: 'is_voip', label: 'VOIP', formatter: 'yesnoicon' },\n { key: 'is_valid', label: 'Valid', formatter: 'yesnoicon' },\n { key: 'registered_owner', label: 'Owner', sortable: true, formatter: \"default('—')\" },\n { key: 'owner_type', label: 'Owner Type', formatter: \"capitalize\" },\n { key: 'last_lookup_at|relative', label: 'Last Lookup', sortable: true},\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Row action\n clickAction: 'view',\n\n // Toolbar\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No phone numbers found.',\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n },\n\n tableViewOptions: {\n addButtonLabel: \"Lookup\",\n addButtonIcon: 'bi-search',\n onAdd: (evt) => {\n evt.preventDefault();\n // Implement the logic for adding a new record\n this.onLookup();\n }\n }\n });\n }\n\n async onLookup() {\n // Implement the logic for adding a new record\n const data = await this.getApp().showForm({\n title: \"Lookup Phone Number\",\n fields: [\n {\n name: 'number',\n type: 'text',\n required: true\n }\n ]\n });\n if (data && data.number) {\n const resp = await PhoneNumber.lookup(data.number);\n if (resp.model) {\n this.tableView._onRowView(resp);\n }\n }\n }\n}\n\nexport default PhoneNumberTablePage;\n","/**\n * SMSView - Detailed view for a PhoneHub SMS record\n *\n * Mirrors GeoIPView structure:\n * - Header with key info (direction, from/to, status)\n * - Tabbed content using TabView and DataView sections\n * - Minimal context menu for refresh and delete\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport DataView from '@core/views/data/DataView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport { SMS } from '@core/models/Phonehub.js';\n\nclass SMSView extends View {\n constructor(options = {}) {\n super({\n className: 'sms-view',\n ...options\n });\n\n this.model = options.model || new SMS(options.data || {});\n\n this.template = `\n <div class=\"sms-view-container\">\n <!-- Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-4\">\n <!-- Left Side: Icon & Info -->\n <div class=\"d-flex align-items-center gap-3\">\n <div class=\"fs-1 text-primary\">\n <i class=\"bi bi-chat-dots\"></i>\n </div>\n <div>\n <h3 class=\"mb-1\">\n {{#model.direction}}{{model.direction|capitalize}}{{/model.direction}}\n {{^model.direction}}Message{{/model.direction}}\n <small class=\"text-muted ms-2\">\n {{#model.status}}[{{model.status|capitalize}}]{{/model.status}}\n </small>\n </h3>\n <div class=\"text-muted small\">\n {{#model.from_number}}From: {{model.from_number}}{{/model.from_number}}\n {{#model.to_number}} · To: {{model.to_number}}{{/model.to_number}}\n </div>\n <div class=\"text-muted small mt-1\">\n {{#model.provider}}Provider: {{model.provider|capitalize}}{{/model.provider}}\n {{#model.provider_message_id}} · SID: {{model.provider_message_id}}{{/model.provider_message_id}}\n </div>\n </div>\n </div>\n\n <!-- Right Side: Actions -->\n <div class=\"d-flex align-items-center gap-4\">\n <div data-container=\"sms-context-menu\"></div>\n </div>\n </div>\n\n <!-- Tabs -->\n <div data-container=\"sms-tabs\"></div>\n </div>\n `;\n }\n\n async onInit() {\n // Message Tab\n this.messageView = new DataView({\n model: this.model,\n className: 'p-3',\n showEmptyValues: true,\n emptyValueText: '—',\n columns: 2,\n fields: [\n { name: 'direction', label: 'Direction', formatter: 'capitalize', cols: 4 },\n { name: 'status', label: 'Status', formatter: 'capitalize', cols: 4 },\n { name: 'from_number', label: 'From', cols: 6 },\n { name: 'to_number', label: 'To', cols: 6 },\n { name: 'body', label: 'Message Body', cols: 12 }\n ]\n });\n\n // Delivery Tab\n this.deliveryView = new DataView({\n model: this.model,\n className: 'p-3',\n showEmptyValues: true,\n emptyValueText: '—',\n columns: 2,\n fields: [\n { name: 'provider', label: 'Provider', formatter: 'capitalize', cols: 6 },\n { name: 'provider_message_id', label: 'Provider Message ID', cols: 6 },\n { name: 'sent_at', label: 'Sent At', formatter: 'datetime', cols: 6 },\n { name: 'delivered_at', label: 'Delivered At', formatter: 'datetime', cols: 6 },\n { name: 'error_code', label: 'Error Code', cols: 6 },\n { name: 'error_message', label: 'Error Message', cols: 12 }\n ]\n });\n\n // Metadata Tab\n this.metadataView = new DataView({\n model: this.model,\n className: 'p-3',\n showEmptyValues: true,\n emptyValueText: '—',\n columns: 2,\n fields: [\n { name: 'id', label: 'Record ID', cols: 6 },\n { name: 'created', label: 'Created', formatter: 'datetime', cols: 6 },\n { name: 'modified', label: 'Last Modified', formatter: 'datetime', cols: 6 }\n ]\n });\n\n const tabs = {\n 'Message': this.messageView,\n 'Delivery': this.deliveryView,\n 'Metadata': this.metadataView\n };\n\n this.tabView = new TabView({\n containerId: 'sms-tabs',\n tabs,\n activeTab: 'Message'\n });\n this.addChild(this.tabView);\n\n // ContextMenu (minimal)\n const menuItems = [\n { label: 'Refresh', action: 'refresh-sms', icon: 'bi-arrow-repeat' },\n { type: 'divider' },\n { label: 'Delete Message', action: 'delete-sms', icon: 'bi-trash', danger: true }\n ];\n\n const ctxMenu = new ContextMenu({\n containerId: 'sms-context-menu',\n className: 'context-menu-view header-menu-absolute',\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: menuItems\n }\n });\n this.addChild(ctxMenu);\n }\n\n // Actions\n\n async onActionRefreshSms() {\n try {\n this.getApp()?.toast?.info?.('Refreshing message...');\n await this.model.fetch();\n await this.render();\n this.getApp()?.toast?.success?.('Message refreshed');\n } catch (e) {\n this.getApp()?.toast?.error?.(e.message || 'Refresh failed');\n }\n }\n\n async onActionDeleteSms() {\n const title = 'Confirm Deletion';\n const msg = `Are you sure you want to delete this message?`;\n const confirmed = await Dialog.confirm(msg, title, {\n confirmClass: 'btn-danger',\n confirmText: 'Delete'\n });\n\n if (!confirmed) return;\n\n try {\n const resp = await this.model.destroy();\n if (resp?.success) {\n this.emit('sms:deleted', { model: this.model });\n } else {\n this.getApp()?.toast?.error?.('Delete failed');\n }\n } catch (e) {\n this.getApp()?.toast?.error?.(e.message || 'Delete failed');\n }\n }\n}\n\nSMSView.MODEL_CLASS = SMS;\n\nexport default SMSView;","/**\n * SMSTablePage - PhoneHub SMS management using TablePage component\n * Mirrors the GeoLocatedIPTablePage pattern with minimal, consistent configuration.\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { SMSList } from '@core/models/Phonehub.js';\nimport SMSView from './SMSView.js';\n\nclass SMSTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n\n // Identity\n name: 'admin_phonehub_sms',\n pageName: 'SMS Messages',\n router: 'admin/phonehub/sms',\n\n // Data source\n Collection: SMSList,\n\n // Item view configuration\n itemView: SMSView,\n viewDialogOptions: {\n header: false,\n size: 'xl'\n },\n\n // Column definitions\n columns: [\n { key: 'direction', label: 'Direction', sortable: true },\n { key: 'from_number', label: 'From', sortable: true, formatter: \"default('—')\" },\n { key: 'to_number', label: 'To', sortable: true, formatter: \"default('—')\" },\n { key: 'status', label: 'Status', sortable: true },\n { key: 'provider', label: 'Provider', sortable: true, formatter: \"default('—')\" },\n { key: 'body', label: 'Message', formatter: \"default('—')\" },\n { key: 'sent_at', label: 'Sent At', sortable: true, formatter: 'datetime' },\n { key: 'delivered_at', label: 'Delivered At', sortable: true, formatter: 'datetime' },\n { key: 'created', label: 'Created', sortable: true, formatter: 'datetime' }\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Row action\n clickAction: 'view',\n\n // Toolbar\n showRefresh: true,\n showAdd: false,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No SMS messages found.',\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n}\n\nexport default SMSTablePage;","import Page from '@core/Page.js';\nimport { MetricsChart, PieChart } from '@ext/charts/index.js';\nimport TableView from '@core/views/table/TableView.js';\nimport { PushDeliveryList } from '@core/models/Push.js';\n\nclass PushDashboardPage extends Page {\n constructor(options = {}) {\n super({\n ...options,\n title: 'Push Notifications Dashboard',\n className: 'push-dashboard-page'\n });\n }\n\n async getTemplate() {\n return `\n <div class=\"container-fluid\">\n <h1 class=\"h3 mb-4\">Push Notifications</h1>\n <div class=\"row\">\n <!-- Stat cards -->\n </div>\n <div class=\"row\">\n <div class=\"col-xl-8 col-lg-7\">\n <div class=\"card shadow mb-4\">\n <div class=\"card-header\">Notifications Over Time</div>\n <div class=\"card-body\" data-container=\"deliveries-chart\"></div>\n </div>\n </div>\n <div class=\"col-xl-4 col-lg-5\">\n <div class=\"card shadow mb-4\">\n <div class=\"card-header\">Delivery Status</div>\n <div class=\"card-body\" data-container=\"status-chart\"></div>\n </div>\n </div>\n </div>\n <div class=\"row\">\n <div class=\"col-lg-6 mb-4\" data-container=\"recent-deliveries\"></div>\n <div class=\"col-lg-6 mb-4\" data-container=\"failed-deliveries\"></div>\n </div>\n </div>\n `;\n }\n\n async onInit() {\n this.deliveriesChart = new MetricsChart({\n containerId: 'deliveries-chart',\n endpoint: '/api/metrics/fetch',\n slugs: ['push_sent', 'push_failed'],\n chartType: 'line'\n });\n this.addChild(this.deliveriesChart);\n\n this.statusChart = new PieChart({\n containerId: 'status-chart',\n endpoint: '/api/account/devices/push/stats' // Assuming this returns data for pie chart\n });\n this.addChild(this.statusChart);\n\n this.recentDeliveries = new TableView({\n containerId: 'recent-deliveries',\n title: 'Recent Deliveries',\n Collection: new PushDeliveryList({ params: { _sort: '-created', _limit: 5 } }),\n columns: [{ key: 'title', label: 'Title' }, { key: 'status', label: 'Status', formatter: 'badge' }]\n });\n this.addChild(this.recentDeliveries);\n\n this.failedDeliveries = new TableView({\n containerId: 'failed-deliveries',\n title: 'Failed Deliveries',\n Collection: new PushDeliveryList({ params: { status: 'failed', _sort: '-created', _limit: 5 } }),\n columns: [{ key: 'title', label: 'Title' }, { key: 'error_message', label: 'Error' }]\n });\n this.addChild(this.failedDeliveries);\n }\n}\n\nexport default PushDashboardPage;\n","import TablePage from '@core/pages/TablePage.js';\nimport { PushConfigList, PushConfigForms } from '@core/models/Push.js';\n\nclass PushConfigTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_push_configs',\n pageName: 'Push Configurations',\n router: \"admin/push/configs\",\n Collection: PushConfigList,\n formCreate: PushConfigForms.create,\n formEdit: PushConfigForms.edit,\n\n columns: [\n { key: 'id', label: 'ID', width: '70px' },\n { key: 'name', label: 'Name' },\n { key: 'group.name', label: 'Group', formatter: \"default('Default')\" },\n { key: 'fcm_sender_id', label: 'Project ID' },\n { key: 'is_active', label: 'Active', format: 'boolean' },\n ],\n\n searchable: true,\n sortable: true,\n paginated: true,\n showRefresh: true,\n showAdd: true,\n showExport: true,\n actions: [\"edit\", \"delete\"],\n tableOptions: {\n pageSizes: [10, 25, 50],\n defaultPageSize: 25,\n emptyMessage: 'No push configurations found.',\n emptyIcon: 'bi-gear',\n\n }\n });\n }\n}\n\nexport default PushConfigTablePage;\n","import TablePage from '@core/pages/TablePage.js';\nimport { PushTemplateList, PushTemplateForms } from '@core/models/Push.js';\n\nclass PushTemplateTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_push_templates',\n pageName: 'Push Templates',\n router: \"admin/push/templates\",\n Collection: PushTemplateList,\n formCreate: PushTemplateForms.create,\n formEdit: PushTemplateForms.edit,\n\n columns: [\n { key: 'id', label: 'ID', width: '70px' },\n { key: 'name', label: 'Name' },\n { key: 'category', label: 'Category' },\n { key: 'group.name', label: 'Group', formatter: \"default('Default')\" },\n { key: 'priority', label: 'Priority' },\n { key: 'is_active', label: 'Active', format: 'boolean' },\n ],\n\n searchable: true,\n sortable: true,\n paginated: true,\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n tableOptions: {\n pageSizes: [10, 25, 50],\n defaultPageSize: 25,\n emptyMessage: 'No push templates found.',\n emptyIcon: 'bi-file-earmark-text',\n actions: [\"edit\", \"delete\"],\n }\n });\n }\n}\n\nexport default PushTemplateTablePage;\n","import View from '@core/View.js';\n\nclass PushDeliveryView extends View {\n constructor(options = {}) {\n super({\n className: 'push-delivery-view',\n ...options\n });\n this.model = options.model;\n }\n\n getTemplate() {\n return `\n <div class=\"p-3\">\n <div class=\"phone-mockup\">\n <div class=\"phone-screen\">\n <div class=\"notification\">\n <div class=\"notification-header\">\n <i class=\"bi bi-app-indicator\"></i>\n <strong>Your App</strong>\n <span class=\"ms-auto small text-muted\">now</span>\n </div>\n <div class=\"notification-body\">\n <div class=\"fw-bold\">{{model.title}}</div>\n <div>{{model.body}}</div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"mt-3\">\n <h5>Delivery Details</h5>\n <p><strong>Status:</strong> <span class=\"badge {{model.status|badge}}\">{{model.status}}</span></p>\n <p><strong>Error:</strong> {{model.error_message|default('None')}}</p>\n </div>\n </div>\n `;\n }\n}\n\nexport default PushDeliveryView;\n","import TablePage from '@core/pages/TablePage.js';\nimport { PushDeliveryList } from '@core/models/Push.js';\nimport PushDeliveryView from './PushDeliveryView.js';\n\nclass PushDeliveryTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_push_deliveries',\n pageName: 'Push Deliveries',\n router: \"admin/push/deliveries\",\n Collection: PushDeliveryList,\n itemViewClass: PushDeliveryView,\n viewDialogOptions: {\n header: false,\n size: 'md'\n },\n\n columns: [\n { key: 'id', label: 'ID', width: '70px' },\n { key: 'created', label: 'Timestamp', formatter: 'datetime' },\n { key: 'user.display_name', label: 'User' },\n { key: 'device.device_name', label: 'Device' },\n { key: 'title', label: 'Title' },\n { key: 'category', label: 'Category' },\n { key: 'status', label: 'Status', formatter: 'badge' },\n ],\n \n searchable: true,\n sortable: true,\n paginated: true,\n showRefresh: true,\n\n tableOptions: {\n pageSizes: [10, 25, 50],\n defaultPageSize: 25,\n emptyMessage: 'No deliveries found.',\n emptyIcon: 'bi-send',\n actions: [\"view\"],\n }\n });\n }\n}\n\nexport default PushDeliveryTablePage;\n","import View from '@core/View.js';\nimport DataView from '@core/views/data/DataView.js';\n\nclass PushDeviceView extends View {\n constructor(options = {}) {\n super({\n className: 'push-device-view',\n ...options\n });\n this.model = options.model;\n }\n\n getTemplate() {\n return `\n <div class=\"p-3\">\n <h3>{{model.device_name}}</h3>\n <p class=\"text-muted\">{{model.user.display_name}}</p>\n <div data-container=\"data-view\"></div>\n </div>\n `;\n }\n\n onInit() {\n this.dataView = new DataView({\n containerId: 'data-view',\n model: this.model,\n fields: [\n { name: 'platform', label: 'Platform', format: 'badge' },\n { name: 'push_enabled', label: 'Push Enabled', format: 'boolean' },\n { name: 'app_version', label: 'App Version' },\n { name: 'os_version', label: 'OS Version' },\n { name: 'last_seen', label: 'Last Seen', format: 'datetime' },\n { name: 'push_preferences', label: 'Preferences', format: 'json' },\n ]\n });\n this.addChild(this.dataView);\n }\n}\n\nexport default PushDeviceView;\n","import TablePage from '@core/pages/TablePage.js';\nimport { PushDeviceList } from '@core/models/Push.js';\nimport PushDeviceView from './PushDeviceView.js';\n\nclass PushDeviceTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_push_devices',\n pageName: 'Registered Devices',\n router: \"admin/push/devices\",\n Collection: PushDeviceList,\n itemViewClass: PushDeviceView,\n viewDialogOptions: {\n header: false,\n size: 'lg'\n },\n\n columns: [\n { key: 'id', label: 'ID', width: '70px' },\n { key: 'user.display_name', label: 'User' },\n { key: 'device_name', label: 'Device Name' },\n { key: 'platform', label: 'Platform', formatter: 'badge' },\n { key: 'app_version', label: 'App Version' },\n { key: 'push_enabled', label: 'Push Enabled', format: 'boolean' },\n { key: 'last_seen', label: 'Last Seen', formatter: 'datetime' },\n ],\n \n searchable: true,\n sortable: true,\n paginated: true,\n showRefresh: true,\n\n tableOptions: {\n pageSizes: [10, 25, 50],\n defaultPageSize: 25,\n emptyMessage: 'No devices found.',\n emptyIcon: 'bi-phone',\n actions: [\"view\", \"delete\"],\n }\n });\n }\n}\n\nexport default PushDeviceTablePage;\n","/**\n * JobStatsView - Job statistics overview cards\n */\n\nimport View from '@core/View.js';\nimport { Job } from '@core/models/Job.js';\n\nexport default class JobStatsView extends View {\n constructor(options = {}) {\n super({\n className: 'job-stats-section',\n ...options\n });\n\n this.stats = {\n pending: 0,\n running: 0,\n completed: 0,\n failed: 0,\n scheduled: 0\n };\n\n this.template = `\n <div class=\"job-stats-header mb-4\">\n <div class=\"row\">\n <div class=\"col-xl-2 col-lg-4 col-md-6 col-12 mb-3\">\n <div class=\"card h-100 border-0 shadow\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div>\n <h6 class=\"card-title text-muted mb-2\">Pending</h6>\n <h3 class=\"mb-1 fw-bold\">{{stats.pending}}</h3>\n <span class=\"badge bg-primary-subtle text-primary\">\n <i class=\"bi bi-hourglass\"></i> Queued\n </span>\n </div>\n <div class=\"text-primary\">\n <i class=\"bi bi-clock fs-2\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"col-xl-2 col-lg-4 col-md-6 col-12 mb-3\">\n <div class=\"card h-100 border-0 shadow\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div>\n <h6 class=\"card-title text-muted mb-2\">Running</h6>\n <h3 class=\"mb-1 fw-bold\">{{stats.running}}</h3>\n <span class=\"badge bg-success-subtle text-success\">\n <i class=\"bi bi-arrow-repeat\"></i> Active\n </span>\n </div>\n <div class=\"text-success\">\n <i class=\"bi bi-play-circle fs-2\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"col-xl-2 col-lg-4 col-md-6 col-12 mb-3\">\n <div class=\"card h-100 border-0 shadow\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div>\n <h6 class=\"card-title text-muted mb-2\">Scheduled</h6>\n <h3 class=\"mb-1 fw-bold\">{{stats.scheduled}}</h3>\n <span class=\"badge bg-warning-subtle text-warning\">\n <i class=\"bi bi-calendar-event\"></i> Planned\n </span>\n </div>\n <div class=\"text-warning\">\n <i class=\"bi bi-calendar3 fs-2\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"col-xl-3 col-lg-6 col-md-6 col-12 mb-3\">\n <div class=\"card h-100 border-0 shadow\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div>\n <h6 class=\"card-title text-muted mb-2\">Completed</h6>\n <h3 class=\"mb-1 fw-bold\">{{stats.completed}}</h3>\n <span class=\"badge bg-info-subtle text-info\">\n <i class=\"bi bi-check-circle\"></i> Done\n </span>\n </div>\n <div class=\"text-info\">\n <i class=\"bi bi-check-square fs-2\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"col-xl-3 col-lg-6 col-md-6 col-12 mb-3\">\n <div class=\"card h-100 border-0 shadow\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div>\n <h6 class=\"card-title text-muted mb-2\">Failed</h6>\n <h3 class=\"mb-1 fw-bold\">{{stats.failed}}</h3>\n <span class=\"badge bg-danger-subtle text-danger\">\n <i class=\"bi bi-x-octagon\"></i> Errors\n </span>\n </div>\n <div class=\"text-danger\">\n <i class=\"bi bi-exclamation-triangle fs-2\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n `;\n }\n\n _onModelChange() {\n this.loadStats();\n if (this.isMounted()) {\n this.render();\n }\n }\n\n async loadStats() {\n this.stats = this.model.attributes.totals;\n }\n}\n","/**\n * JobHealthView - System health overview\n */\n\nimport View from '@core/View.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport { Job } from '@core/models/Job.js';\n\nexport default class JobHealthView extends View {\n constructor(options = {}) {\n super({\n className: 'job-health-section',\n ...options\n });\n\n this.health = {\n status: 'unknown',\n runners: { active: 0, total: 0 },\n channels: []\n };\n\n this.template = `\n <div class=\"job-health-header mb-4\">\n <div class=\"card border-0 shadow\">\n <div class=\"card-body\">\n <div class=\"row align-items-center\">\n <div class=\"col-md-6\">\n <div class=\"d-flex align-items-center\">\n <div class=\"health-indicator me-3\">\n <i class=\"bi bi-circle-fill fs-4 {{healthStatusClass}}\"></i>\n </div>\n <div>\n <h5 class=\"mb-1\">Service Health: {{health.overall_status|capitalize}}</h5>\n <small class=\"text-muted d-block\">\n Workers: {{health.runners.active}}/{{health.runners.total}} active\n </small>\n <small class=\"text-muted d-block\">\n Scheduler:\n <span class=\"{{schedulerStatusClass}} fw-bold\">\n <i class=\"bi {{schedulerIcon}} me-1\"></i>{{schedulerStatusText}}\n </span>\n </small>\n </div>\n </div>\n </div>\n <div class=\"col-md-6\">\n <div class=\"d-flex justify-content-end\">\n <div class=\"btn-group\">\n <button class=\"btn btn-sm btn-outline-primary\" data-action=\"refresh-health\">\n <i class=\"bi bi-arrow-clockwise\"></i> Refresh\n </button>\n <button class=\"btn btn-sm btn-outline-secondary\" data-action=\"system-settings\">\n <i class=\"bi bi-gear\"></i> Settings\n </button>\n </div>\n </div>\n </div>\n </div>\n {{#health.channelsArray.length}}\n <div class=\"row mt-3\">\n <div class=\"col-12\">\n <small class=\"text-muted d-block mb-2\">Channel Status:</small>\n <div class=\"d-flex flex-wrap gap-2\">\n {{#health.channelsArray}}\n <span class=\"badge {{statusBadgeClass}} d-flex align-items-center\">\n <i class=\"bi {{statusIcon}} me-1\"></i>\n {{channel}} ({{queued}} queued, {{inflight}} inflight)\n </span>\n {{/health.channelsArray}}\n </div>\n </div>\n </div>\n {{/health.channelsArray.length}}\n </div>\n </div>\n </div>\n `;\n }\n\n _onModelChange() {\n this.loadHealth();\n if (this.isMounted()) {\n this.render();\n }\n }\n\n async loadHealth() {\n if (!this.model._.totals) return;\n const data = this.model.attributes\n // Determine overall health status\n let overall_status = 'healthy';\n if (data.totals.runners_active === 0) {\n overall_status = 'critical';\n } else if (!data.scheduler.active) {\n overall_status = 'warning';\n }\n this.health = {\n overall_status,\n channels: data.channels,\n runners: {\n active: data.totals.runners_active,\n total: data.runners.length\n },\n totals: data.totals,\n scheduler: data.scheduler\n };\n\n this.healthStatusClass = this.getHealthStatusClass(this.health.overall_status);\n this.setupChannelDisplay();\n this.setupSchedulerDisplay();\n\n }\n\n setupChannelDisplay() {\n if (!this.health.channels) return;\n\n this.health.channelsArray = Object.values(this.health.channels).map(channel => {\n let channelStatus = 'healthy';\n\n // Determine status based on queue backlog and runner availability\n const totalJobs = (channel.queued_count || 0) + (channel.inflight_count || 0);\n if (totalJobs > 50) {\n channelStatus = 'warning';\n }\n if (totalJobs > 100 || channel.runners === 0) {\n channelStatus = 'critical';\n }\n\n return {\n ...channel,\n status: channelStatus,\n statusBadgeClass: this.getChannelBadgeClass(channelStatus),\n statusIcon: this.getChannelIcon(channelStatus),\n queued: channel.queued_count || 0,\n inflight: channel.inflight_count || 0,\n // For backward compatibility, map to old names\n pending: channel.queued_count || 0,\n running: channel.inflight_count || 0\n };\n });\n }\n\n setupSchedulerDisplay() {\n if (!this.health.scheduler) {\n this.schedulerStatusText = 'Unknown';\n this.schedulerStatusClass = 'text-muted';\n this.schedulerIcon = 'bi-question-circle-fill';\n return;\n }\n\n if (this.health.scheduler.active) {\n this.schedulerStatusText = 'Running';\n this.schedulerStatusClass = 'text-success';\n this.schedulerIcon = 'bi-check-circle-fill';\n } else {\n this.schedulerStatusText = 'Stopped';\n this.schedulerStatusClass = 'text-danger';\n this.schedulerIcon = 'bi-x-octagon-fill';\n }\n }\n\n getHealthStatusClass(status) {\n const classes = {\n 'healthy': 'text-success',\n 'warning': 'text-warning',\n 'critical': 'text-danger',\n 'unknown': 'text-muted'\n };\n return classes[status] || 'text-muted';\n }\n\n getChannelBadgeClass(status) {\n const classes = {\n 'healthy': 'bg-success',\n 'warning': 'bg-warning',\n 'critical': 'bg-danger'\n };\n return classes[status] || 'bg-secondary';\n }\n\n getChannelIcon(status) {\n const icons = {\n 'healthy': 'bi-check-circle-fill',\n 'warning': 'bi-exclamation-triangle-fill',\n 'critical': 'bi-x-octagon-fill'\n };\n return icons[status] || 'bi-dash-circle-fill';\n }\n\n async onActionRefreshHealth(event, element) {\n try {\n element.disabled = true;\n await this.model.fetch();\n } catch (error) {\n console.error('Failed to refresh health:', error);\n } finally {\n element.disabled = false;\n }\n }\n\n async onActionSystemSettings() {\n await Dialog.showAlert({\n title: 'System Settings',\n message: 'System settings interface coming soon!',\n type: 'info'\n });\n }\n}\n","/**\n * JobDetailsView - Comprehensive job details interface\n *\n * Features:\n * - Clean header with job status, timing, and function info\n * - Tabbed interface for Overview, Payload, Events, and Logs\n * - Integrated with Table and ContextMenu components\n * - Clean Bootstrap 5 styling\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport TableView from '@core/views/table/TableView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport { Job, JobEventList, JobLogList, JobForms } from '@core/models/Job.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\n\nclass JobDetailsView extends View {\n constructor(options = {}) {\n super({\n className: 'job-details-view',\n ...options\n });\n\n // Job model instance\n this.model = options.model || new Job(options.data || {});\n\n // Tab views\n this.tabView = null;\n this.overviewView = null;\n this.payloadView = null;\n this.eventsView = null;\n this.logsView = null;\n\n // Auto refresh\n this.autoRefreshInterval = null;\n\n // Set template\n this.template = `\n <div class=\"job-details-container\">\n <!-- Job Header -->\n <div class=\"d-flex justify-content-between align-items-start mb-4\">\n <!-- Left Side: Primary Identity -->\n <div class=\"d-flex align-items-center gap-3\">\n <div class=\"avatar-placeholder rounded-circle bg-light d-flex align-items-center justify-content-center\" style=\"width: 80px; height: 80px;\">\n <i class=\"bi {{model.statusIcon}} text-secondary\" style=\"font-size: 40px;\"></i>\n </div>\n <div>\n <h3 class=\"mb-1\">{{model.func|truncate(32)|default('Unknown Function')}}</h3>\n <div class=\"text-muted small\">\n <span>ID: {{model.id}}</span>\n <span class=\"mx-2\">|</span>\n <span>Channel: <span class=\"badge bg-primary\">{{model.channel}}</span></span>\n {{#model.runner_id}}\n <span class=\"mx-2\">|</span>\n <span>Runner: {{model.runner_id|truncate(16)}}</span>\n {{/model.runner_id}}\n </div>\n <div class=\"text-muted small mt-2\">\n <div>Created: {{model.created|datetime}}</div>\n {{#model.started_at}}\n <div>Started: {{model.started_at|datetime}}</div>\n {{/model.started_at}}\n {{#model.finished_at}}\n <div>Finished: {{model.finished_at|datetime}}</div>\n {{/model.finished_at}}\n </div>\n </div>\n </div>\n\n <!-- Right Side: Status & Actions -->\n <div class=\"d-flex align-items-start gap-4\">\n <div class=\"text-end\">\n <div class=\"d-flex align-items-center gap-2\">\n <span class=\"badge {{model.statusBadgeClass}} fs-6\">\n <i class=\"bi {{model.statusIcon}}\"></i> {{model.status|uppercase}}\n </span>\n {{#model.cancel_requested}}\n <span class=\"badge bg-warning ms-1\">\n <i class=\"bi bi-exclamation-triangle\"></i> Cancel Requested\n </span>\n {{/model.cancel_requested}}\n </div>\n {{#model.formattedDuration}}\n <div class=\"text-muted small mt-1\">Duration: {{model.formattedDuration}}</div>\n {{/model.formattedDuration}}\n </div>\n <div data-container=\"job-context-menu\"></div>\n </div>\n </div>\n\n <!-- Tab Container -->\n <div data-container=\"job-details-tabs\"></div>\n </div>\n `;\n }\n\n async onInit() {\n // Create Overview view\n this.overviewView = new View({\n template: `\n <div class=\"job-overview-tab\">\n <div class=\"card border-0 bg-light mb-3\">\n <div class=\"card-body\">\n <div class=\"row\">\n <div class=\"col-md-6\">\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Job ID</label>\n <div class=\"font-monospace\">{{model.id}}</div>\n </div>\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Function</label>\n <div class=\"font-monospace\">{{model.func}}</div>\n </div>\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Channel</label>\n <div>\n <span class=\"badge bg-primary\">{{model.channel}}</span>\n </div>\n </div>\n {{#model.runner_id}}\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Runner</label>\n <div class=\"font-monospace small\">{{model.runner_id}}</div>\n </div>\n {{/model.runner_id}}\n </div>\n <div class=\"col-md-6\">\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Status</label>\n <div>\n <span class=\"badge {{model.statusBadgeClass}} fs-6\">\n <i class=\"bi {{model.statusIcon}}\"></i> {{model.status|uppercase}}\n </span>\n {{#model.cancel_requested}}\n <span class=\"badge bg-warning ms-1\">\n <i class=\"bi bi-exclamation-triangle\"></i> Cancel Requested\n </span>\n {{/model.cancel_requested}}\n </div>\n </div>\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Created</label>\n <div>{{model.created|datetime}}</div>\n </div>\n {{#model.started_at}}\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Started</label>\n <div>{{model.started_at|datetime}}</div>\n </div>\n {{/model.started_at}}\n {{#model.finished_at}}\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Finished</label>\n <div>{{model.finished_at|datetime}}</div>\n </div>\n {{/model.finished_at}}\n {{#model.duration_ms}}\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Duration</label>\n <div>{{model.formattedDuration}}</div>\n </div>\n {{/model.duration_ms}}\n </div>\n </div>\n </div>\n </div>\n </div>\n `,\n model: this.model\n });\n\n // Create Payload view\n this.payloadView = new View({\n template: `\n <div class=\"job-payload-tab\">\n <pre class=\"bg-light p-3 rounded\"><code>{{{model.payload|json}}}</code></pre>\n </div>\n `,\n model: this.model,\n });\n\n // Create Events table\n const eventsCollection = new JobEventList({\n params: { job: this.model.get('id'), size: 10 }\n });\n this.eventsView = new TableView({\n collection: eventsCollection,\n hideActivePillNames: ['job'],\n columns: [\n { key: 'at', label: 'Timestamp', formatter: 'datetime', sortable: true },\n { key: 'event', label: 'Event', formatter: 'badge' },\n { key: 'details|json', label: 'Details' }\n ]\n });\n\n // Create Logs table\n const logsCollection = new JobLogList({\n params: { job: this.model.get('id'), size: 10 }\n });\n this.logsView = new TableView({\n collection: logsCollection,\n hideActivePillNames: ['job'],\n columns: [\n { key: 'created|datetime', label: 'Created', sortable: true },\n { key: 'kind', label: 'Kind', formatter: 'badge' },\n { key: 'message', label: 'Message' }\n ]\n });\n\n // Create TabView\n this.tabView = new TabView({\n tabs: {\n 'Overview': this.overviewView,\n 'Payload': this.payloadView,\n 'Events': this.eventsView,\n 'Logs': this.logsView\n },\n activeTab: 'Overview',\n containerId: 'job-details-tabs'\n });\n this.addChild(this.tabView);\n\n // Create ContextMenu\n const contextMenuItems = [\n { label: 'Refresh', action: 'refresh-job', icon: 'bi-arrow-clockwise' }\n ];\n\n if (this.model.canCancel && this.model.canCancel()) {\n contextMenuItems.push({\n label: 'Cancel Job',\n action: 'cancel-job',\n icon: 'bi-x-circle',\n class: 'text-danger'\n });\n }\n\n if (this.model.canRetry && this.model.canRetry()) {\n contextMenuItems.push({\n label: 'Retry Job',\n action: 'retry-job',\n icon: 'bi-arrow-repeat',\n class: 'text-primary'\n });\n }\n\n const jobMenu = new ContextMenu({\n containerId: 'job-context-menu',\n className: \"context-menu-view header-menu-absolute\",\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: contextMenuItems\n }\n });\n this.addChild(jobMenu);\n\n await this.model.fetch({ params: { graph: \"detail\" } });\n // Start auto-refresh if job is active\n // this.startAutoRefresh();\n }\n\n async onBeforeRender() {\n await this.prepareJobData();\n }\n\n async prepareJobData() {\n if (!this.model) return;\n\n // Status styling\n this.model._.statusBadgeClass = this.model.getStatusBadgeClass ? this.model.getStatusBadgeClass() : 'bg-secondary';\n this.model._.statusIcon = this.model.getStatusIcon ? this.model.getStatusIcon() : 'bi-question-circle';\n this.model._.formattedDuration = this.model.getFormattedDuration ? this.model.getFormattedDuration() : 'N/A';\n }\n\n async loadJobDetails() {\n if (!this.model?.get('id')) return;\n\n try {\n if (this.model.getDetailedStatus) {\n await this.model.getDetailedStatus();\n }\n await this.prepareJobData();\n } catch (error) {\n console.error('Failed to load job details:', error);\n }\n }\n\n async onActionRefreshJob() {\n await this.model.fetch({ params: { graph: \"detail\" } });\n }\n\n async onActionCancelJob() {\n if (confirm('Are you sure you want to cancel this job?')) {\n try {\n const response = await this.model.cancel();\n if (response.success) {\n await this.loadJobDetails();\n await this.render();\n this.emit('job-cancelled', { job: this.model });\n } else {\n alert('Failed to cancel job: ' + (response.data?.error || 'Unknown error'));\n }\n } catch (error) {\n console.error('Failed to cancel job:', error);\n alert('Failed to cancel job: ' + error.message);\n }\n }\n }\n\n async onActionRetryJob() {\n const retryData = await Dialog.showForm({\n title: 'Retry Job',\n formConfig: JobForms.retry\n });\n\n if (retryData) {\n try {\n const response = await this.model.retry(retryData.delay || 0);\n if (response.success) {\n this.emit('job-retried', { job: this.model, newJobId: response.newJobId });\n } else {\n alert('Failed to retry job: ' + (response.data?.error || 'Unknown error'));\n }\n } catch (error) {\n console.error('Failed to retry job:', error);\n alert('Failed to retry job: ' + error.message);\n }\n }\n }\n\n startAutoRefresh() {\n if (this.autoRefreshInterval) clearInterval(this.autoRefreshInterval);\n\n if (this.model?.isActive && this.model.isActive()) {\n this.autoRefreshInterval = setInterval(async () => {\n try {\n await this.loadJobDetails();\n if (this.isMounted()) {\n await this.render();\n }\n } catch (error) {\n console.error('Auto-refresh failed:', error);\n }\n }, 5000);\n }\n }\n\n stopAutoRefresh() {\n if (this.autoRefreshInterval) {\n clearInterval(this.autoRefreshInterval);\n this.autoRefreshInterval = null;\n }\n }\n\n async onDestroy() {\n this.stopAutoRefresh();\n await super.onDestroy();\n }\n\n // Static show method for backward compatibility (if needed)\n static async show(job, options = {}) {\n const view = new JobDetailsView({ model: job });\n\n return await Dialog.showDialog({\n title: `<i class=\"bi bi-info-circle me-2\"></i>Job Details - ${job.get('id')}`,\n body: view,\n size: 'xl',\n scrollable: true,\n buttons: [{ text: 'Close', class: 'btn-secondary', dismiss: true }],\n onHide: () => view.stopAutoRefresh(),\n ...options\n });\n }\n}\n\nJob.VIEW_CLASS = JobDetailsView;\n\nexport default JobDetailsView;\n","/**\n * JobsAdminPage - Async Job Engine management dashboard\n * Main page orchestrating job monitoring and management components\n */\n\nimport Page from '@core/Page.js';\nimport View from '@core/View.js';\nimport TableView from '@core/views/table/TableView.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport { Job, JobList, JobsEngineStats } from '@core/models/Job.js';\nimport { JobRunner, JobRunnerForms, JobRunnerList } from '@core/models/JobRunner.js';\nimport { MetricsChart, MetricsMiniChartWidget } from '@ext/charts/index.js';\n\n// Import component views\nimport JobStatsView from './JobStatsView.js';\nimport JobHealthView from './JobHealthView.js';\nimport JobDetailsView from './JobDetailsView.js';\n\nclass JobMetricsModalView extends View {\n constructor(options = {}) {\n super({\n className: 'job-metrics-modal-view',\n ...options\n });\n\n this.template = `\n <div data-container=\"job-metrics-modal-chart\" style=\"min-height:320px;\"></div>\n `;\n }\n\n async onInit() {\n this.chart = new MetricsChart({\n containerId: 'job-metrics-modal-chart',\n title: '<i class=\"bi bi-graph-up me-2\"></i> Job Channel Metrics',\n endpoint: '/api/metrics/fetch',\n height: 320,\n granularity: 'hours',\n category: 'jobs_channels',\n account: 'global',\n chartType: 'bar',\n showDateRange: true,\n yAxis: {\n label: 'Count',\n beginAtZero: true\n },\n tooltip: {\n y: 'number:0'\n }\n });\n\n this.addChild(this.chart);\n }\n}\n\nexport default class JobsAdminPage extends Page {\n constructor(options = {}) {\n super({\n title: 'Jobs Management',\n className: 'jobs-admin-page',\n ...options\n });\n\n this.pageTitle = 'Jobs Management';\n this.pageSubtitle = 'Async job monitoring and runner management';\n this.lastUpdated = new Date().toLocaleString();\n this.autoRefreshInterval = null;\n this.refreshRate = 30000; // 30 seconds default\n\n this.template = `\n <div class=\"jobs-admin-container\">\n <!-- Page Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-4\">\n <div>\n <h1 class=\"h3 mb-1\">{{pageTitle}}</h1>\n <p class=\"text-muted mb-0\">{{pageSubtitle}}</p>\n <small class=\"text-info\">\n <i class=\"bi bi-arrow-clockwise me-1\"></i>\n Auto-refresh: {{refreshRateLabel}} | Last updated: {{lastUpdated}}\n </small>\n </div>\n <div class=\"btn-group\" role=\"group\">\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm\"\n data-action=\"refresh-all\" title=\"Refresh All Data\">\n <i class=\"bi bi-arrow-clockwise\"></i> Refresh\n </button>\n <button type=\"button\" class=\"btn btn-outline-primary btn-sm\"\n data-action=\"export-data\" title=\"Export Data\">\n <i class=\"bi bi-download\"></i> Export\n </button>\n <div class=\"dropdown\">\n <button class=\"btn btn-outline-secondary btn-sm dropdown-toggle\"\n type=\"button\" data-bs-toggle=\"dropdown\">\n <i class=\"bi bi-gear\"></i> Settings\n </button>\n <ul class=\"dropdown-menu dropdown-menu-end\">\n <li><h6 class=\"dropdown-header\">Auto Refresh</h6></li>\n <li><button class=\"dropdown-item\" data-action=\"set-refresh-rate\" data-rate=\"5\">5 seconds</button></li>\n <li><button class=\"dropdown-item\" data-action=\"set-refresh-rate\" data-rate=\"10\">10 seconds</button></li>\n <li><button class=\"dropdown-item\" data-action=\"set-refresh-rate\" data-rate=\"30\">30 seconds</button></li>\n <li><button class=\"dropdown-item\" data-action=\"set-refresh-rate\" data-rate=\"0\">Off</button></li>\n <li><hr class=\"dropdown-divider\"></li>\n <li><button class=\"dropdown-item\" data-action=\"runner-broadcast\">Broadcast Command</button></li>\n </ul>\n </div>\n <div class=\"dropdown\">\n <button class=\"btn btn-danger btn-sm dropdown-toggle\"\n type=\"button\" data-bs-toggle=\"dropdown\">\n <i class=\"bi bi-wrench\"></i> Manage\n </button>\n <ul class=\"dropdown-menu dropdown-menu-end\">\n <li><button class=\"dropdown-item\" data-action=\"run-simple-job\"><i class=\"bi bi-play-circle me-2\"></i>Run Simple Job</button></li>\n <li><button class=\"dropdown-item\" data-action=\"run-test-jobs\"><i class=\"bi bi-robot me-2\"></i>Run Test Jobs</button></li>\n <li><hr class=\"dropdown-divider\"></li>\n <li><button class=\"dropdown-item\" data-action=\"clear-stuck\"><i class=\"bi bi-wrench me-2\"></i>Clear Stuck Jobs</button></li>\n <li><button class=\"dropdown-item\" data-action=\"clear-channel\"><i class=\"bi bi-eraser me-2\"></i>Clear Channel</button></li>\n <li><button class=\"dropdown-item\" data-action=\"purge-jobs\"><i class=\"bi bi-trash me-2\"></i>Purge Jobs</button></li>\n <li><button class=\"dropdown-item\" data-action=\"cleanup-consumers\"><i class=\"bi bi-people me-2\"></i>Cleanup Consumers</button></li>\n </ul>\n </div>\n </div>\n </div>\n\n <div data-container=\"job-stats\"></div>\n\n <div class=\"row mb-4 g-3 align-items-stretch\">\n <div class=\"col-lg-6\" data-container=\"jobs-published-chart\"></div>\n <div class=\"col-lg-6 position-relative\">\n <div data-container=\"jobs-failed-chart\"></div>\n <button class=\"btn btn-link btn-sm text-decoration-none position-absolute top-0 end-0\"\n data-action=\"open-job-metrics-modal\">\n <i class=\"bi bi-graph-up\"></i> Channel Metrics\n </button>\n </div>\n </div>\n\n <div class=\"row\">\n <div class=\"col-xxl-6 col-lg-6 mb-4\">\n <div data-container=\"job-health\"></div>\n </div>\n <div class=\"col-xxl-6 col-lg-6 mb-4\">\n <div class=\"card shadow-sm h-100\">\n <div class=\"card-header d-flex justify-content-between align-items-center\">\n <h5 class=\"mb-0\"><i class=\"bi bi-cpu me-2\"></i>Job Runners</h5>\n <small class=\"text-muted\">Heartbeat &amp; status</small>\n </div>\n <div class=\"card-body p-0\" data-container=\"runner-table\"></div>\n </div>\n </div>\n </div>\n\n <div class=\"row\">\n <div class=\"col-xl-6 mb-4\">\n <div class=\"card shadow-sm h-100\">\n <div class=\"card-header d-flex justify-content-between align-items-center\">\n <h5 class=\"mb-0\"><i class=\"bi bi-play-circle me-2\"></i>Running Jobs</h5>\n <small class=\"text-muted\">Currently executing</small>\n </div>\n <div class=\"card-body p-0\" data-container=\"running-jobs-table\"></div>\n </div>\n </div>\n <div class=\"col-xl-6 mb-4\">\n <div class=\"card shadow-sm h-100\">\n <div class=\"card-header d-flex justify-content-between align-items-center\">\n <h5 class=\"mb-0\"><i class=\"bi bi-hourglass-split me-2\"></i>Pending Jobs</h5>\n <small class=\"text-muted\">Waiting in queue</small>\n </div>\n <div class=\"card-body p-0\" data-container=\"pending-jobs-table\"></div>\n </div>\n </div>\n </div>\n\n <div class=\"row\">\n <div class=\"col-xl-6 mb-4\">\n <div class=\"card shadow-sm h-100\">\n <div class=\"card-header\">\n <h5 class=\"mb-0\"><i class=\"bi bi-calendar-event me-2\"></i>Scheduled Jobs</h5>\n </div>\n <div class=\"card-body p-0\" data-container=\"scheduled-jobs-table\"></div>\n </div>\n </div>\n <div class=\"col-xl-6 mb-4\">\n <div class=\"card shadow-sm h-100\">\n <div class=\"card-header d-flex justify-content-between align-items-center\">\n <h5 class=\"mb-0\"><i class=\"bi bi-bug me-2\"></i>Failed Jobs</h5>\n <small class=\"text-muted\">Latest errors</small>\n </div>\n <div class=\"card-body p-0\" data-container=\"failed-jobs-table\"></div>\n </div>\n </div>\n </div>\n\n <div class=\"card shadow-sm mb-5\">\n <div class=\"card-header d-flex justify-content-between align-items-center\">\n <h5 class=\"mb-0\"><i class=\"bi bi-tools me-2\"></i>Operations</h5>\n <button class=\"btn btn-outline-secondary btn-sm\" data-action=\"view-all-jobs\">\n <i class=\"bi bi-table\"></i> View All Jobs\n </button>\n </div>\n <div class=\"card-body\">\n <div class=\"d-flex flex-wrap gap-2\">\n <button class=\"btn btn-outline-primary\" data-action=\"run-simple-job\">\n <i class=\"bi bi-play-circle me-2\"></i>Run Simple Job\n </button>\n <button class=\"btn btn-outline-primary\" data-action=\"run-test-jobs\">\n <i class=\"bi bi-robot me-2\"></i>Run Test Jobs\n </button>\n <button class=\"btn btn-outline-warning\" data-action=\"clear-stuck\">\n <i class=\"bi bi-wrench me-2\"></i>Clear Stuck\n </button>\n <button class=\"btn btn-outline-warning\" data-action=\"clear-channel\">\n <i class=\"bi bi-eraser me-2\"></i>Clear Channel\n </button>\n <button class=\"btn btn-outline-danger\" data-action=\"purge-jobs\">\n <i class=\"bi bi-trash me-2\"></i>Purge Jobs\n </button>\n <button class=\"btn btn-outline-info\" data-action=\"cleanup-consumers\">\n <i class=\"bi bi-people me-2\"></i>Cleanup Consumers\n </button>\n <button class=\"btn btn-outline-secondary\" data-action=\"runner-broadcast\">\n <i class=\"bi bi-wifi me-2\"></i>Broadcast Command\n </button>\n </div>\n </div>\n </div>\n </div>\n `;\n }\n\n async onInit() {\n\n this.jobStats = new JobsEngineStats();\n // Create child views\n this.jobStatsView = new JobStatsView({\n containerId: 'job-stats',\n model: this.jobStats\n });\n this.addChild(this.jobStatsView);\n\n this.jobHealthView = new JobHealthView({\n containerId: 'job-health',\n model: this.jobStats\n });\n this.addChild(this.jobHealthView);\n\n this.jobsPublishedChart = new MetricsMiniChartWidget({\n containerId: 'jobs-published-chart',\n icon: 'bi bi-upload',\n title: 'Jobs Published',\n subtitle: '{{now_value}} {{now_label}}',\n granularity: 'days',\n slugs: ['jobs.published'],\n account: 'global',\n chartType: 'line',\n height: 90,\n showSettings: true,\n showTrending: true,\n showDateRange: false\n });\n this.addChild(this.jobsPublishedChart);\n\n this.jobsFailedChart = new MetricsMiniChartWidget({\n containerId: 'jobs-failed-chart',\n icon: 'bi bi-exclamation-octagon',\n title: 'Jobs Failed',\n subtitle: '{{now_value}} {{now_label}}',\n granularity: 'days',\n slugs: ['jobs.failed'],\n account: 'global',\n chartType: 'line',\n height: 90,\n showSettings: true,\n showTrending: true,\n showDateRange: false\n });\n this.addChild(this.jobsFailedChart);\n\n this.runningJobsTable = new TableView({\n containerId: 'running-jobs-table',\n Collection: JobList,\n collectionParams: {\n size: 5,\n sort: '-created',\n status: 'running'\n },\n searchable: true,\n filterable: false,\n paginated: true,\n itemView: JobDetailsView,\n hideActivePills: ['status'],\n viewDialogOptions: {\n title: 'Job Details',\n size: 'xl',\n scrollable: true\n },\n tableOptions: {\n striped: false,\n hover: true,\n size: 'sm'\n },\n columns: [\n {\n key: 'id',\n label: 'Job',\n template: `\n <div class=\"fw-semibold font-monospace\">{{model.id|truncate_middle(12)}}</div>\n <div class=\"text-muted small\">{{model.channel}} &middot; {{model.func|truncate_middle(28)|default('n/a')}}</div>\n `\n },\n {\n key: 'runner_id',\n label: 'Runner',\n template: `\n <span class=\"font-monospace\">{{model.runner_id|truncate_middle(12)|default('n/a')}}</span>\n `\n },\n {\n key: 'status',\n label: 'State',\n formatter: (value, context) => {\n const job = context.row;\n const badgeClass = job.getStatusBadgeClass ? job.getStatusBadgeClass() : 'bg-secondary';\n const icon = job.getStatusIcon ? job.getStatusIcon() : 'bi-question';\n return `<span class=\"badge ${badgeClass}\"><i class=\"${icon} me-1\"></i>${value.toUpperCase()}</span>`;\n }\n },\n {\n key: 'created',\n label: 'Started',\n formatter: 'datetime'\n }\n ]\n });\n this.addChild(this.runningJobsTable);\n\n this.pendingJobsTable = new TableView({\n containerId: 'pending-jobs-table',\n Collection: JobList,\n collectionParams: {\n size: 5,\n sort: '-created',\n status: 'pending',\n run_at__isnull: true\n },\n searchable: true,\n filterable: false,\n paginated: true,\n selectable: true,\n batchBarLocation: 'top',\n batchActions: [\n {\n icon: 'bi-x-circle-fill',\n label: 'Cancel Jobs',\n action: \"cancel-jobs\"\n }\n ],\n itemView: JobDetailsView,\n hideActivePills: ['status'],\n viewDialogOptions: {\n title: 'Job Details',\n size: 'xl',\n scrollable: true\n },\n tableOptions: {\n striped: false,\n hover: true,\n size: 'sm'\n },\n columns: [\n {\n key: 'id',\n label: 'Job',\n template: `\n <div class=\"fw-semibold font-monospace\">{{model.id|truncate_middle(12)}}</div>\n <div class=\"text-muted small\">{{model.channel}} &middot; {{model.func|truncate_middle(28)|default('n/a')}}</div>\n `\n },\n {\n key: 'priority',\n label: 'Priority',\n formatter: (value = 0) => {\n const badge = value >= 8 ? 'bg-danger' : value >= 5 ? 'bg-warning' : 'bg-secondary';\n return `<span class=\"badge ${badge}\">${value}</span>`;\n }\n },\n {\n key: 'modified',\n label: 'Queued',\n formatter: 'relative'\n }\n ]\n });\n this.pendingJobsTable.on(\"action:batch-cancel-jobs\", async (action, event, element) => {\n const items = this.pendingJobsTable.getSelectedItems();\n await Promise.all(items.map(item => item.model.cancel()));\n this.getApp().toast.success(\"Jobs cancelled successfully\");\n this.pendingJobsTable.collection.fetch();\n });\n this.addChild(this.pendingJobsTable);\n\n this.failedJobsTable = new TableView({\n containerId: 'failed-jobs-table',\n Collection: JobList,\n collectionParams: {\n size: 5,\n sort: '-finished_at',\n status: 'failed'\n },\n searchable: true,\n filterable: false,\n paginated: true,\n itemView: JobDetailsView,\n viewDialogOptions: {\n title: 'Job Details',\n size: 'xl',\n scrollable: true\n },\n hideActivePills: ['status'],\n tableOptions: {\n striped: false,\n hover: true,\n size: 'sm'\n },\n columns: [\n {\n key: 'id',\n label: 'Job',\n template: `\n <div class=\"fw-semibold font-monospace\">{{model.id|truncate_middle(12)}}</div>\n <div class=\"text-muted small\">{{model.channel}} &middot; {{model.func|truncate_middle(28)|default('n/a')}}</div>\n `\n },\n {\n key: 'last_error',\n label: 'Error',\n template: `\n <div class=\"text-danger small\">{{model.last_error|truncate(80)|default('Unknown error')}}</div>\n `\n },\n {\n key: 'modified',\n label: 'Failed',\n formatter: 'relative'\n }\n ]\n });\n this.addChild(this.failedJobsTable);\n\n this.scheduledJobsTable = new TableView({\n containerId: 'scheduled-jobs-table',\n Collection: JobList,\n collectionParams: {\n size: 5,\n sort: 'run_at',\n run_at__isnull: false,\n status: 'pending'\n },\n searchable: true,\n filterable: false,\n paginated: true,\n itemView: JobDetailsView,\n hideActivePills: ['status'],\n viewDialogOptions: {\n title: 'Job Details',\n size: 'xl',\n scrollable: true\n },\n tableOptions: {\n striped: false,\n hover: true,\n size: 'sm'\n },\n columns: [\n {\n key: 'id',\n label: 'Job',\n formatter: 'truncate_middle(12)'\n },\n {\n key: 'run_at',\n label: 'Scheduled For',\n formatter: 'datetime'\n },\n {\n key: 'channel',\n label: 'Channel',\n formatter: 'badge'\n }\n ],\n selectable: true,\n batchBarLocation: 'top',\n batchActions: [\n {\n icon: 'bi-x-circle-fill',\n label: 'Cancel Jobs',\n action: \"cancel-jobs\"\n }\n ],\n });\n this.scheduledJobsTable.on(\"action:batch-cancel-jobs\", async (action, event, element) => {\n const items = this.scheduledJobsTable.getSelectedItems();\n await Promise.all(items.map(item => item.model.cancel()));\n this.getApp().toast.success(\"Jobs cancelled successfully\");\n this.scheduledJobsTable.collection.fetch();\n });\n this.addChild(this.scheduledJobsTable);\n\n this.runnersTable = new TableView({\n containerId: 'runner-table',\n Collection: JobRunnerList,\n searchable: true,\n filterable: false,\n paginated: true,\n tableOptions: {\n striped: false,\n hover: true,\n size: 'sm'\n },\n columns: [\n {\n key: 'runner_id',\n label: 'Runner',\n formatter: 'truncate_middle(16)'\n },\n {\n key: 'alive',\n label: 'Status',\n formatter: (value) => {\n const isAlive = value === true;\n const badgeClass = isAlive ? 'bg-success' : 'bg-danger';\n const icon = isAlive ? 'bi-check-circle-fill' : 'bi-x-octagon-fill';\n const text = isAlive ? 'ALIVE' : 'DEAD';\n return `<span class=\"badge ${badgeClass}\"><i class=\"${icon} me-1\"></i>${text}</span>`;\n }\n },\n {\n key: 'last_heartbeat',\n label: 'Heartbeat',\n formatter: (value) => {\n if (!value) return 'Never';\n const heartbeatTime = new Date(value);\n const now = new Date();\n const diffMs = now - heartbeatTime;\n const diffSeconds = Math.floor(diffMs / 1000);\n if (diffSeconds < 60) return `${diffSeconds}s ago`;\n if (diffSeconds < 3600) return `${Math.floor(diffSeconds / 60)}m ago`;\n return `${Math.floor(diffSeconds / 3600)}h ago`;\n }\n }\n ]\n });\n this.addChild(this.runnersTable);\n\n await this.refreshData();\n\n }\n\n // Auto-refresh management\n startAutoRefresh() {\n if (this.autoRefreshInterval) {\n clearInterval(this.autoRefreshInterval);\n }\n\n if (this.refreshRate > 0) {\n this.autoRefreshInterval = setInterval(async () => {\n await this.refreshData();\n }, this.refreshRate);\n }\n }\n\n async refreshData() {\n try {\n const tasks = [\n this.jobStats.fetch()\n ];\n\n if (this.jobsPublishedChart) {\n tasks.push(this.jobsPublishedChart.refresh());\n }\n if (this.jobsFailedChart) {\n tasks.push(this.jobsFailedChart.refresh());\n }\n if (this.runningJobsTable?.collection?.fetch) {\n tasks.push(this.runningJobsTable.collection.fetch());\n }\n if (this.pendingJobsTable?.collection?.fetch) {\n tasks.push(this.pendingJobsTable.collection.fetch());\n }\n if (this.failedJobsTable?.collection?.fetch) {\n tasks.push(this.failedJobsTable.collection.fetch());\n }\n if (this.scheduledJobsTable?.collection?.fetch) {\n tasks.push(this.scheduledJobsTable.collection.fetch());\n }\n if (this.runnersTable?.collection?.fetch) {\n tasks.push(this.runnersTable.collection.fetch());\n }\n\n await Promise.all(tasks);\n\n this.lastUpdated = new Date().toLocaleString();\n this.updateHeaderTimestamp();\n\n } catch (error) {\n console.error('Failed to refresh jobs dashboard:', error);\n }\n }\n\n updateHeaderTimestamp() {\n const timestampElement = this.element?.querySelector('.text-info');\n if (timestampElement) {\n const rateLabel = this.refreshRate === 0 ? 'Off' : `${this.refreshRate / 1000}s`;\n timestampElement.innerHTML = `\n <i class=\"bi bi-arrow-clockwise me-1\"></i>\n Auto-refresh: ${rateLabel} | Last updated: ${this.lastUpdated}\n `;\n }\n }\n\n get refreshRateSeconds() {\n return this.refreshRate / 1000;\n }\n\n get refreshRateLabel() {\n return this.refreshRate === 0 ? 'Off' : `${this.refreshRateSeconds}s`;\n }\n\n // Action handlers\n async onActionRefreshAll(event, element) {\n try {\n const icon = element.querySelector('i');\n icon?.classList.add('spinning');\n element.disabled = true;\n\n await this.refreshData();\n\n } catch (error) {\n console.error('Failed to refresh jobs dashboard:', error);\n } finally {\n const icon = element.querySelector('i');\n icon?.classList.remove('spinning');\n element.disabled = false;\n }\n }\n\n async onActionSetRefreshRate(event, element) {\n const rate = parseInt(element.getAttribute('data-rate')) * 1000;\n this.refreshRate = rate;\n this.startAutoRefresh();\n\n const rateText = rate === 0 ? 'Off' : `${rate / 1000}s`;\n this.getApp().toast.success(`Auto-refresh set to ${rateText}`);\n }\n\n async onActionExportData() {\n await Dialog.showAlert({\n title: 'Export Data',\n message: 'Data export functionality coming soon!',\n type: 'info'\n });\n }\n\n // Job management actions\n async onActionRunSimpleJob(event, element) {\n const confirmed = await Dialog.showConfirm({\n title: 'Run Simple Job',\n message: 'This will run a simple test job to verify the job system is working correctly.',\n confirmText: 'Run Test',\n confirmClass: 'btn-success'\n });\n\n if (confirmed) {\n await this.executeJobAction(element, () => Job.test(), 'Test job started successfully');\n }\n }\n\n async onActionRunTestJobs(event, element) {\n const confirmed = await Dialog.showConfirm({\n title: 'Run Test Jobs',\n message: 'This will run a suite of test jobs to verify all job functionalities.',\n confirmText: 'Run Tests',\n confirmClass: 'btn-success'\n });\n\n if (confirmed) {\n await this.executeJobAction(element, () => Job.tests(), 'Test suite started successfully');\n }\n }\n\n async onActionClearStuck(event, element) {\n const channels = this.jobHealthView?.health?.channelsArray || [];\n const channelOptions = [\n { value: '', label: 'All Channels' },\n ...channels.map(ch => ({ value: ch.channel, label: ch.channel }))\n ];\n\n const result = await Dialog.showForm({\n title: 'Clear Stuck Jobs',\n formConfig: {\n fields: [\n {\n name: 'channel',\n type: 'select',\n label: 'Channel',\n options: channelOptions,\n value: '',\n help: 'Select specific channel or leave empty for all channels'\n }\n ]\n }\n });\n\n if (result) {\n await this.executeJobAction(\n element,\n () => Job.clearStuck(result.channel || null),\n (response) => {\n const count = response.data.count || 0;\n const channelText = result.channel ? ` from channel \"${result.channel}\"` : '';\n return `Cleared ${count} stuck job${count !== 1 ? 's' : ''}${channelText}`;\n }\n );\n }\n }\n\n async onActionClearChannel(event, element) {\n const channels = this.jobHealthView?.health?.channelsArray || [];\n const channelOptions = channels.map(ch => ({ value: ch.channel, label: ch.channel }));\n\n const result = await Dialog.showForm({\n title: 'Clear Channel',\n formConfig: {\n fields: [\n {\n name: 'channel',\n type: 'select',\n label: 'Channel',\n options: channelOptions,\n required: true,\n help: 'Select the channel to clear.'\n }\n ]\n }\n });\n\n if (result) {\n await this.executeJobAction(\n element,\n () => Job.clearChannel(result.channel),\n `Channel \"${result.channel}\" cleared successfully.`\n );\n }\n }\n\n async onActionPurgeJobs(event, element) {\n const result = await Dialog.showForm({\n title: 'Purge Old Jobs',\n formConfig: {\n fields: [\n {\n name: 'days_old',\n type: 'number',\n label: 'Days Old',\n value: 30,\n required: true,\n help: 'Delete jobs older than this many days.'\n }\n ]\n }\n });\n\n if (result) {\n await this.executeJobAction(\n element,\n () => Job.purgeJobs(result.days_old),\n (response) => {\n const count = response.data.count || 0;\n return `Purged ${count} old job(s).`;\n }\n );\n }\n }\n\n async onActionCleanupConsumers(event, element) {\n const confirmed = await Dialog.showConfirm({\n title: 'Cleanup Consumers',\n message: 'This will remove stale consumer records from the system. This is generally safe.',\n confirmText: 'Cleanup',\n confirmClass: 'btn-warning'\n });\n\n if (confirmed) {\n await this.executeJobAction(\n element,\n () => Job.cleanConsumers(),\n (response) => {\n const count = response.data.count || 0;\n return `Cleaned up ${count} consumer(s).`;\n }\n );\n }\n }\n\n async onActionRunnerBroadcast() {\n const result = await Dialog.showForm({\n title: 'Broadcast Command to All Runners',\n formConfig: JobRunnerForms.broadcast\n });\n\n if (result) {\n try {\n const broadcastResult = await JobRunner.broadcast(\n result.command,\n {},\n result.timeout\n );\n\n if (broadcastResult.success) {\n this.getApp().toast.success(`Broadcast command \"${result.command}\" sent successfully`);\n await this.refreshData();\n } else {\n this.getApp().toast.error(broadcastResult.data?.error || 'Failed to broadcast command');\n }\n } catch (error) {\n console.error('Failed to broadcast command:', error);\n this.getApp().toast.error('Error broadcasting command: ' + error.message);\n }\n }\n }\n\n // Helper method to execute job actions with consistent error handling\n async executeJobAction(element, actionFn, successMessage) {\n try {\n element.disabled = true;\n const icon = element.querySelector('i');\n icon?.classList.add('spinning');\n\n const result = await actionFn();\n if (result.success && result.data?.status) {\n const message = typeof successMessage === 'function'\n ? successMessage(result)\n : successMessage;\n this.getApp().toast.success(message);\n await this.refreshData();\n } else {\n this.getApp().toast.error(result.data?.error || 'Operation failed');\n }\n } catch (error) {\n console.error('Job action failed:', error);\n this.getApp().toast.error('Error: ' + error.message);\n } finally {\n element.disabled = false;\n const icon = element.querySelector('i');\n icon?.classList.remove('spinning');\n }\n }\n\n async onEnter() {\n // Start auto-refresh\n this.startAutoRefresh();\n }\n\n async onExit() {\n if (this.autoRefreshInterval) {\n clearInterval(this.autoRefreshInterval);\n this.autoRefreshInterval = null;\n }\n }\n\n // Public API\n async refreshDashboard() {\n return await this.refreshData();\n }\n\n getStats() {\n return this.jobStatsView?.stats || {};\n }\n\n getHealth() {\n return this.jobHealthView?.health || {};\n }\n\n async onActionOpenJobMetricsModal() {\n const modalView = new JobMetricsModalView();\n await Dialog.showDialog({\n title: '<i class=\"bi bi-graph-up me-2\"></i>Job Channel Metrics',\n body: modalView,\n size: 'xl',\n scrollable: true,\n buttons: [\n { text: 'Close', class: 'btn-secondary', dismiss: true }\n ]\n });\n }\n\n async onActionViewAllJobs() {\n const router = this.getApp()?.router;\n if (router) {\n router.navigateTo('/admin/jobs/table');\n } else {\n this.getApp()?.toast?.info('Router unavailable.');\n }\n }\n}\n","/**\n * TaskDetailsView - Detailed task information view for Dialog display\n * Shows comprehensive task data, logs, metrics, and available actions\n */\n\nimport View from '@core/View.js';\n\nexport default class TaskDetailsView extends View {\n constructor(options = {}) {\n super({\n ...options,\n className: 'mojo-task-details-view'\n });\n\n this.task = options.task || null;\n this.logs = [];\n this.metrics = null;\n }\n\n async getTemplate() {\n return `\n <div class=\"mojo-task-details-container\">\n {{#task}}\n <!-- Task Overview -->\n <div class=\"card border-0 bg-light mb-3\">\n <div class=\"card-body\">\n <div class=\"row\">\n <div class=\"col-md-6\">\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Task ID</label>\n <div class=\"font-monospace\">{{id}}</div>\n </div>\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Function</label>\n <div>{{function}}</div>\n </div>\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Channel</label>\n <div>\n <span class=\"badge bg-primary\">{{channel}}</span>\n </div>\n </div>\n </div>\n <div class=\"col-md-6\">\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Status</label>\n <div>\n <span class=\"badge {{statusBadgeClass}} fs-6\">\n <i class=\"bi {{statusIcon}}\"></i> {{status|uppercase}}\n </span>\n </div>\n </div>\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Created</label>\n <div>{{created|datetime}}</div>\n </div>\n {{#completed_at}}\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Completed</label>\n <div>{{completed_at|datetime}}</div>\n </div>\n {{/completed_at}}\n {{#expires}}\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Expires</label>\n <div class=\"{{expiresClass}}\">{{expires|datetime}}</div>\n </div>\n {{/expires}}\n </div>\n </div>\n </div>\n </div>\n\n <!-- Task Data -->\n {{#data}}\n <div class=\"card mb-3\">\n <div class=\"card-header py-2\">\n <h6 class=\"mb-0\">\n <i class=\"bi bi-database me-2\"></i>Task Data\n </h6>\n </div>\n <div class=\"card-body\">\n <pre class=\"bg-light p-3 rounded mb-0\"><code>{{dataFormatted}}</code></pre>\n </div>\n </div>\n {{/data}}\n\n <!-- Error Information -->\n {{#error}}\n <div class=\"card border-danger mb-3\">\n <div class=\"card-header bg-danger-subtle py-2\">\n <h6 class=\"mb-0 text-danger\">\n <i class=\"bi bi-exclamation-triangle me-2\"></i>Error Details\n </h6>\n </div>\n <div class=\"card-body\">\n <div class=\"alert alert-danger mb-0\">\n <strong>Error:</strong> {{error}}\n </div>\n {{#errorDetails}}\n <div class=\"mt-3\">\n <label class=\"form-label fw-bold small\">Stack Trace:</label>\n <pre class=\"bg-light p-3 rounded small mb-0\"><code>{{errorDetails}}</code></pre>\n </div>\n {{/errorDetails}}\n </div>\n </div>\n {{/error}}\n\n <!-- Performance Metrics -->\n {{#metrics}}\n <div class=\"card mb-3\">\n <div class=\"card-header py-2\">\n <h6 class=\"mb-0\">\n <i class=\"bi bi-speedometer2 me-2\"></i>Performance Metrics\n </h6>\n </div>\n <div class=\"card-body\">\n <div class=\"row\">\n <div class=\"col-md-3 col-6 mb-3\">\n <div class=\"text-center\">\n <div class=\"h5 mb-1 text-primary\">{{executionTime}}ms</div>\n <small class=\"text-muted\">Execution Time</small>\n </div>\n </div>\n <div class=\"col-md-3 col-6 mb-3\">\n <div class=\"text-center\">\n <div class=\"h5 mb-1 text-info\">{{memoryUsage}}MB</div>\n <small class=\"text-muted\">Memory Usage</small>\n </div>\n </div>\n <div class=\"col-md-3 col-6 mb-3\">\n <div class=\"text-center\">\n <div class=\"h5 mb-1 text-warning\">{{cpuUsage}}%</div>\n <small class=\"text-muted\">CPU Usage</small>\n </div>\n </div>\n <div class=\"col-md-3 col-6 mb-3\">\n <div class=\"text-center\">\n <div class=\"h5 mb-1 text-secondary\">{{retryCount}}</div>\n <small class=\"text-muted\">Retry Count</small>\n </div>\n </div>\n </div>\n </div>\n </div>\n {{/metrics}}\n\n <!-- Task Logs -->\n <div class=\"card\">\n <div class=\"card-header py-2 d-flex justify-content-between align-items-center\">\n <h6 class=\"mb-0\">\n <i class=\"bi bi-journal-text me-2\"></i>Task Logs\n </h6>\n <button class=\"btn btn-sm btn-outline-primary\" data-action=\"refresh-logs\">\n <i class=\"bi bi-arrow-clockwise\"></i> Refresh\n </button>\n </div>\n <div class=\"card-body p-0\">\n <div class=\"mojo-task-logs-container\" style=\"max-height: 300px; overflow-y: auto;\">\n {{#logs.length}}\n {{#logs}}\n <div class=\"log-entry p-3 border-bottom\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div class=\"log-message flex-grow-1\">\n <span class=\"badge bg-{{levelClass}} me-2\">{{level|uppercase}}</span>\n {{message}}\n </div>\n <small class=\"text-muted ms-3\">{{timestamp|time}}</small>\n </div>\n </div>\n {{/logs}}\n {{/logs.length}}\n {{^logs.length}}\n <div class=\"text-center text-muted py-5\">\n <i class=\"bi bi-journal fs-1 opacity-50\"></i>\n <p class=\"mb-0 mt-2\">No logs available for this task</p>\n </div>\n {{/logs.length}}\n </div>\n </div>\n </div>\n {{/task}}\n\n {{^task}}\n <div class=\"alert alert-warning\">\n <i class=\"bi bi-exclamation-triangle me-2\"></i>\n No task data available\n </div>\n {{/task}}\n </div>\n `;\n }\n\n async onInit() {\n if (this.task) {\n await this.prepareTaskData();\n await this.loadTaskLogs();\n await this.loadTaskMetrics();\n }\n }\n\n async prepareTaskData() {\n if (!this.task) return;\n\n // Status styling\n this.task.statusBadgeClass = this.getStatusBadgeClass(this.task.status);\n this.task.statusIcon = this.getStatusIcon(this.task.status);\n\n // Format data for display\n if (this.task.data && typeof this.task.data === 'object') {\n this.task.dataFormatted = JSON.stringify(this.task.data, null, 2);\n }\n\n // Check if task has expired\n if (this.task.expires) {\n this.task.expiresClass = this.task.expires * 1000 < Date.now() ? 'text-danger' : 'text-muted';\n }\n }\n\n getStatusBadgeClass(status) {\n const classes = {\n 'pending': 'bg-primary',\n 'running': 'bg-success', \n 'completed': 'bg-info',\n 'error': 'bg-danger',\n 'cancelled': 'bg-secondary',\n 'expired': 'bg-warning'\n };\n return classes[status] || 'bg-secondary';\n }\n\n getStatusIcon(status) {\n const icons = {\n 'pending': 'bi-hourglass',\n 'running': 'bi-arrow-repeat',\n 'completed': 'bi-check-circle',\n 'error': 'bi-x-octagon',\n 'cancelled': 'bi-x-circle',\n 'expired': 'bi-clock'\n };\n return icons[status] || 'bi-question-circle';\n }\n\n getLogLevelClass(level) {\n const classes = {\n 'debug': 'secondary',\n 'info': 'primary',\n 'warning': 'warning',\n 'error': 'danger'\n };\n return classes[level] || 'secondary';\n }\n\n async loadTaskLogs() {\n if (!this.task?.id) return;\n\n try {\n const response = await this.getApp().rest.GET(`/api/tasks/${this.task.id}/logs`);\n if (response.success && response.data.status) {\n this.logs = response.data.data.map(log => ({\n ...log,\n levelClass: this.getLogLevelClass(log.level)\n }));\n } else {\n this.logs = [];\n }\n } catch (error) {\n console.error('Failed to load task logs:', error);\n this.logs = [];\n }\n }\n\n async loadTaskMetrics() {\n if (!this.task?.id) return;\n\n try {\n const response = await this.getApp().rest.GET(`/api/tasks/${this.task.id}/metrics`);\n if (response.success && response.data.status) {\n this.metrics = response.data.data;\n }\n } catch (error) {\n console.error('Failed to load task metrics:', error);\n this.metrics = null;\n }\n }\n\n async setTask(task) {\n this.task = task;\n await this.prepareTaskData();\n await this.loadTaskLogs();\n await this.loadTaskMetrics();\n \n if (this.isMounted()) {\n await this.render();\n }\n }\n\n async onActionRefreshLogs(action, event, element) {\n if (!this.task?.id) return;\n\n try {\n element.disabled = true;\n const icon = element.querySelector('i');\n if (icon) icon.classList.add('spinning');\n\n await this.loadTaskLogs();\n await this.render();\n \n } catch (error) {\n console.error('Failed to refresh logs:', error);\n this.showError('Failed to refresh logs: ' + error.message);\n } finally {\n element.disabled = false;\n const icon = element.querySelector('i');\n if (icon) icon.classList.remove('spinning');\n }\n }\n\n // Static method for easy Dialog integration\n static async show(task, options = {}) {\n const view = new TaskDetailsView({ task });\n await view.onInit();\n\n const buttons = [];\n\n // Add action buttons based on task status\n if (['pending', 'running'].includes(task.status)) {\n buttons.push({\n text: 'Cancel Task',\n class: 'btn-outline-danger',\n action: async () => {\n if (confirm('Are you sure you want to cancel this task?')) {\n try {\n const response = await view.getApp().rest.POST(`/api/tasks/${task.id}/cancel`);\n if (response.success && response.data.status) {\n view.showSuccess('Task cancelled successfully');\n return { action: 'cancelled', task };\n } else {\n view.showError(response.data.error || 'Failed to cancel task');\n }\n } catch (error) {\n view.showError('Failed to cancel task: ' + error.message);\n }\n }\n return null;\n }\n });\n }\n\n if (task.status === 'error') {\n buttons.push({\n text: 'Retry Task',\n class: 'btn-outline-primary',\n action: async () => {\n try {\n const response = await view.getApp().rest.POST(`/api/tasks/${task.id}/retry`);\n if (response.success && response.data.status) {\n view.showSuccess('Task queued for retry');\n return { action: 'retried', task };\n } else {\n view.showError(response.data.error || 'Failed to retry task');\n }\n } catch (error) {\n view.showError('Failed to retry task: ' + error.message);\n }\n return null;\n }\n });\n }\n\n buttons.push({\n text: 'Clone Task',\n class: 'btn-outline-info',\n action: async () => {\n try {\n const response = await view.getApp().rest.POST(`/api/tasks/${task.id}/clone`);\n if (response.success && response.data.status) {\n view.showSuccess('Task cloned successfully');\n return { action: 'cloned', originalTask: task, newTask: response.data.data };\n } else {\n view.showError(response.data.error || 'Failed to clone task');\n }\n } catch (error) {\n view.showError('Failed to clone task: ' + error.message);\n }\n return null;\n }\n });\n\n buttons.push({\n text: 'Export',\n class: 'btn-outline-secondary',\n action: () => {\n try {\n const exportData = {\n task: task,\n logs: view.logs,\n metrics: view.metrics,\n exported_at: new Date().toISOString(),\n exported_by: 'task-management-system'\n };\n\n const blob = new Blob([JSON.stringify(exportData, null, 2)], {\n type: 'application/json'\n });\n \n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `task-${task.id}-${Date.now()}.json`;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n\n view.showSuccess('Task data exported successfully');\n return null;\n } catch (error) {\n view.showError('Failed to export task data');\n return null;\n }\n }\n });\n\n buttons.push({\n text: 'Close',\n class: 'btn-secondary',\n dismiss: true\n });\n\n return await Dialog.showDialog({\n title: `<i class=\"bi bi-info-circle me-2\"></i>Task Details - ${task.id}`,\n body: view,\n size: 'lg',\n scrollable: true,\n buttons: buttons,\n ...options\n });\n }\n}","/**\n * RunnerDetailsView - Detailed runner information view for Dialog display\n * Shows comprehensive runner status, performance, tasks, and management actions\n */\n\nimport View from '@core/View.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\n\nexport default class RunnerDetailsView extends View {\n constructor(options = {}) {\n super({\n ...options,\n className: 'mojo-runner-details-view'\n });\n\n this.runner = options.runner || null;\n this.currentTasks = [];\n this.logs = [];\n this.metrics = null;\n this.config = null;\n }\n\n async getTemplate() {\n return `\n <div class=\"mojo-runner-details-container\">\n {{#runner}}\n <!-- Runner Overview -->\n <div class=\"card border-0 bg-light mb-3\">\n <div class=\"card-body\">\n <div class=\"row\">\n <div class=\"col-md-6\">\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Hostname</label>\n <div class=\"h5 mb-0 font-monospace\">{{hostname}}</div>\n </div>\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Status</label>\n <div>\n <span class=\"badge {{statusBadgeClass}} fs-6\">\n <i class=\"bi {{statusIcon}}\"></i> {{status|uppercase}}\n </span>\n </div>\n </div>\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Workers</label>\n <div class=\"h5 mb-0\">\n {{#metrics.activeWorkers}}{{metrics.activeWorkers}}/{{/metrics.activeWorkers}}{{max_workers}} workers\n {{#metrics.workerUtilization}}\n <div class=\"progress mt-1\" style=\"height: 6px;\">\n <div class=\"progress-bar {{metrics.utilizationClass}}\" style=\"width: {{metrics.workerUtilization}}%\"></div>\n </div>\n {{/metrics.workerUtilization}}\n </div>\n </div>\n </div>\n <div class=\"col-md-6\">\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Started</label>\n <div>{{started_at|datetime}}</div>\n </div>\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Last Ping</label>\n <div class=\"{{pingAgeClass}}\">{{last_ping|datetime}} ({{pingAgeText}})</div>\n </div>\n <div class=\"mb-3\">\n <label class=\"form-label fw-bold text-muted small\">Uptime</label>\n <div>{{uptimeText}}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Channel Assignment -->\n <div class=\"card mb-3\">\n <div class=\"card-header py-2\">\n <h6 class=\"mb-0\">\n <i class=\"bi bi-collection me-2\"></i>Assigned Channels\n </h6>\n </div>\n <div class=\"card-body\">\n {{#channels.length}}\n <div class=\"d-flex flex-wrap gap-2\">\n {{#channels}}\n <span class=\"badge bg-primary-subtle text-primary px-3 py-2\">\n {{.}}\n </span>\n {{/channels}}\n </div>\n {{/channels.length}}\n {{^channels.length}}\n <div class=\"text-center text-muted py-3\">\n <i class=\"bi bi-collection opacity-50\"></i>\n <p class=\"mb-0 mt-2\">No channels assigned</p>\n </div>\n {{/channels.length}}\n </div>\n </div>\n\n <!-- Performance Metrics -->\n {{#metrics}}\n <div class=\"card mb-3\">\n <div class=\"card-header py-2 d-flex justify-content-between align-items-center\">\n <h6 class=\"mb-0\">\n <i class=\"bi bi-speedometer2 me-2\"></i>Performance Metrics\n </h6>\n <button class=\"btn btn-sm btn-outline-primary\" data-action=\"refresh-metrics\">\n <i class=\"bi bi-arrow-clockwise\"></i> Refresh\n </button>\n </div>\n <div class=\"card-body\">\n <div class=\"row\">\n <div class=\"col-lg-3 col-md-6 mb-3\">\n <div class=\"text-center p-3 bg-light rounded\">\n <div class=\"h4 mb-1 text-success\">{{tasksCompleted|number}}</div>\n <small class=\"text-muted\">Tasks Completed</small>\n {{#tasksCompletedToday}}\n <div class=\"small text-success mt-1\">+{{tasksCompletedToday}} today</div>\n {{/tasksCompletedToday}}\n </div>\n </div>\n <div class=\"col-lg-3 col-md-6 mb-3\">\n <div class=\"text-center p-3 bg-light rounded\">\n <div class=\"h4 mb-1 text-info\">{{avgExecutionTime}}ms</div>\n <small class=\"text-muted\">Avg Execution</small>\n <div class=\"small {{performanceTrend.class}} mt-1\">\n <i class=\"bi {{performanceTrend.icon}}\"></i> {{performanceTrend.text}}\n </div>\n </div>\n </div>\n <div class=\"col-lg-3 col-md-6 mb-3\">\n <div class=\"text-center p-3 bg-light rounded\">\n <div class=\"h4 mb-1 text-danger\">{{errorCount|number}}</div>\n <small class=\"text-muted\">Errors</small>\n <div class=\"small text-muted mt-1\">{{errorRate}}% error rate</div>\n </div>\n </div>\n <div class=\"col-lg-3 col-md-6 mb-3\">\n <div class=\"text-center p-3 bg-light rounded\">\n <div class=\"h4 mb-1 text-warning\">{{queueBacklog|number}}</div>\n <small class=\"text-muted\">Queue Backlog</small>\n </div>\n </div>\n </div>\n\n <!-- Resource Usage Bars -->\n <div class=\"row mt-3\">\n <div class=\"col-md-6 mb-2\">\n <label class=\"form-label fw-bold small\">CPU Usage</label>\n <div class=\"progress\" style=\"height: 20px;\">\n <div class=\"progress-bar {{cpu.class}}\" style=\"width: {{cpu.percentage}}%\">\n {{cpu.percentage}}%\n </div>\n </div>\n </div>\n <div class=\"col-md-6 mb-2\">\n <label class=\"form-label fw-bold small\">Memory Usage</label>\n <div class=\"progress\" style=\"height: 20px;\">\n <div class=\"progress-bar {{memory.class}}\" style=\"width: {{memory.percentage}}%\">\n {{memory.used}}MB / {{memory.total}}MB\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n {{/metrics}}\n\n <!-- Current Tasks -->\n <div class=\"card mb-3\">\n <div class=\"card-header py-2 d-flex justify-content-between align-items-center\">\n <h6 class=\"mb-0\">\n <i class=\"bi bi-list-task me-2\"></i>Current Tasks ({{currentTasks.length}})\n </h6>\n <button class=\"btn btn-sm btn-outline-primary\" data-action=\"refresh-tasks\">\n <i class=\"bi bi-arrow-clockwise\"></i> Refresh\n </button>\n </div>\n <div class=\"card-body p-0\">\n {{#currentTasks.length}}\n <div class=\"table-responsive\">\n <table class=\"table table-sm table-hover mb-0\">\n <thead class=\"table-light\">\n <tr>\n <th class=\"border-0\">Task ID</th>\n <th class=\"border-0\">Function</th>\n <th class=\"border-0\">Channel</th>\n <th class=\"border-0\">Started</th>\n <th class=\"border-0\">Duration</th>\n <th class=\"border-0 text-end\">Actions</th>\n </tr>\n </thead>\n <tbody>\n {{#currentTasks}}\n <tr>\n <td class=\"font-monospace small\">{{id|truncate(12)}}</td>\n <td>{{function}}</td>\n <td><span class=\"badge bg-primary-subtle text-primary\">{{channel}}</span></td>\n <td>{{started|time}}</td>\n <td class=\"text-muted\">{{duration}}</td>\n <td class=\"text-end\">\n <div class=\"btn-group btn-group-sm\">\n <button class=\"btn btn-outline-info\" data-action=\"view-task\" data-task-id=\"{{id}}\" title=\"View Details\">\n <i class=\"bi bi-eye\"></i>\n </button>\n <button class=\"btn btn-outline-warning\" data-action=\"cancel-task\" data-task-id=\"{{id}}\" title=\"Cancel\">\n <i class=\"bi bi-x-circle\"></i>\n </button>\n </div>\n </td>\n </tr>\n {{/currentTasks}}\n </tbody>\n </table>\n </div>\n {{/currentTasks.length}}\n {{^currentTasks.length}}\n <div class=\"text-center text-muted py-5\">\n <i class=\"bi bi-list-task fs-1 opacity-50\"></i>\n <p class=\"mb-0 mt-2\">No active tasks</p>\n </div>\n {{/currentTasks.length}}\n </div>\n </div>\n\n <!-- Runner Logs -->\n <div class=\"card\">\n <div class=\"card-header py-2 d-flex justify-content-between align-items-center\">\n <h6 class=\"mb-0\">\n <i class=\"bi bi-journal-text me-2\"></i>Runner Logs\n </h6>\n <div class=\"btn-group btn-group-sm\">\n <button class=\"btn btn-outline-secondary active\" data-action=\"filter-logs\" data-level=\"all\">All</button>\n <button class=\"btn btn-outline-primary\" data-action=\"filter-logs\" data-level=\"info\">Info</button>\n <button class=\"btn btn-outline-warning\" data-action=\"filter-logs\" data-level=\"warning\">Warning</button>\n <button class=\"btn btn-outline-danger\" data-action=\"filter-logs\" data-level=\"error\">Error</button>\n <button class=\"btn btn-outline-primary\" data-action=\"refresh-logs\">\n <i class=\"bi bi-arrow-clockwise\"></i>\n </button>\n </div>\n </div>\n <div class=\"card-body p-0\">\n <div class=\"mojo-runner-logs-container\" style=\"max-height: 300px; overflow-y: auto;\">\n {{#logs.length}}\n {{#logs}}\n <div class=\"log-entry p-3 border-bottom\" data-level=\"{{level}}\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div class=\"log-message flex-grow-1\">\n <span class=\"badge bg-{{levelClass}} me-2\">{{level|uppercase}}</span>\n {{message}}\n </div>\n <small class=\"text-muted ms-3\">{{timestamp|time}}</small>\n </div>\n </div>\n {{/logs}}\n {{/logs.length}}\n {{^logs.length}}\n <div class=\"text-center text-muted py-5\">\n <i class=\"bi bi-journal fs-1 opacity-50\"></i>\n <p class=\"mb-0 mt-2\">No logs available</p>\n </div>\n {{/logs.length}}\n </div>\n </div>\n </div>\n {{/runner}}\n\n {{^runner}}\n <div class=\"alert alert-warning\">\n <i class=\"bi bi-exclamation-triangle me-2\"></i>\n No runner data available\n </div>\n {{/runner}}\n </div>\n `;\n }\n\n async onInit() {\n if (this.runner) {\n await this.prepareRunnerData();\n await this.loadRunnerMetrics();\n await this.loadCurrentTasks();\n await this.loadRunnerLogs();\n }\n }\n\n async prepareRunnerData() {\n if (!this.runner) return;\n\n // Status styling\n this.runner.isActive = this.runner.status === 'active';\n this.runner.statusBadgeClass = this.runner.isActive ? 'bg-success' : 'bg-warning';\n this.runner.statusIcon = this.runner.isActive ? 'bi-check-circle-fill' : 'bi-exclamation-triangle-fill';\n\n // Calculate ping age and uptime\n if (this.runner.ping_age !== undefined) {\n this.runner.pingAgeText = this.formatDuration(this.runner.ping_age);\n this.runner.pingAgeClass = this.runner.ping_age > 300 ? 'text-danger' : 'text-muted';\n }\n\n if (this.runner.started_at) {\n const uptimeSeconds = Date.now() / 1000 - this.runner.started_at;\n this.runner.uptimeText = this.formatUptime(uptimeSeconds);\n }\n }\n\n async loadRunnerMetrics() {\n if (!this.runner?.hostname) return;\n\n try {\n const response = await this.getApp().rest.GET(`/api/runners/${this.runner.hostname}/metrics`);\n if (response.success && response.data.status) {\n const data = response.data.data;\n this.metrics = {\n activeWorkers: data.activeWorkers || 0,\n tasksCompleted: data.tasksCompleted || 0,\n tasksCompletedToday: data.tasksCompletedToday || 0,\n avgExecutionTime: data.avgExecutionTime || 0,\n errorCount: data.errorCount || 0,\n errorRate: data.errorRate || 0,\n queueBacklog: data.queueBacklog || 0,\n workerUtilization: Math.round((data.activeWorkers / this.runner.max_workers) * 100),\n utilizationClass: this.getUtilizationClass(data.activeWorkers / this.runner.max_workers),\n performanceTrend: this.getPerformanceTrend(data.avgExecutionTime || 0),\n cpu: this.getResourceStatus(data.cpuUsage || 0),\n memory: this.getMemoryStatus(data.memoryUsed || 0, data.memoryTotal || 1000)\n };\n }\n } catch (error) {\n console.error('Failed to load runner metrics:', error);\n this.metrics = this.getDefaultMetrics();\n }\n }\n\n async loadCurrentTasks() {\n if (!this.runner?.hostname) return;\n\n try {\n const response = await this.getApp().rest.GET(`/api/runners/${this.runner.hostname}/tasks`);\n if (response.success && response.data.status) {\n this.currentTasks = response.data.data.map(task => ({\n ...task,\n duration: this.formatDuration(Date.now() / 1000 - task.started)\n }));\n } else {\n this.currentTasks = [];\n }\n } catch (error) {\n console.error('Failed to load current tasks:', error);\n this.currentTasks = [];\n }\n }\n\n async loadRunnerLogs() {\n if (!this.runner?.hostname) return;\n\n try {\n const response = await this.getApp().rest.GET(`/api/runners/${this.runner.hostname}/logs?limit=50`);\n if (response.success && response.data.status) {\n this.logs = response.data.data.map(log => ({\n ...log,\n levelClass: this.getLogLevelClass(log.level)\n }));\n } else {\n this.logs = [];\n }\n } catch (error) {\n console.error('Failed to load runner logs:', error);\n this.logs = [];\n }\n }\n\n getDefaultMetrics() {\n return {\n activeWorkers: 0,\n tasksCompleted: 0,\n tasksCompletedToday: 0,\n avgExecutionTime: 0,\n errorCount: 0,\n errorRate: 0,\n queueBacklog: 0,\n workerUtilization: 0,\n utilizationClass: 'bg-secondary',\n performanceTrend: { class: 'text-muted', icon: 'bi-dash', text: 'No data' },\n cpu: { percentage: 0, class: 'bg-secondary' },\n memory: { used: 0, total: 0, percentage: 0, class: 'bg-secondary' }\n };\n }\n\n getUtilizationClass(utilization) {\n if (utilization > 0.9) return 'bg-danger';\n if (utilization > 0.7) return 'bg-warning';\n if (utilization > 0.5) return 'bg-info';\n return 'bg-success';\n }\n\n getPerformanceTrend(avgTime) {\n if (avgTime < 1000) {\n return { class: 'text-success', icon: 'bi-arrow-up', text: 'Excellent' };\n } else if (avgTime < 5000) {\n return { class: 'text-warning', icon: 'bi-arrow-right', text: 'Good' };\n } else {\n return { class: 'text-danger', icon: 'bi-arrow-down', text: 'Slow' };\n }\n }\n\n getResourceStatus(usage) {\n const percentage = Math.round(usage);\n let cssClass = 'bg-success';\n \n if (percentage > 80) cssClass = 'bg-danger';\n else if (percentage > 60) cssClass = 'bg-warning';\n else if (percentage > 40) cssClass = 'bg-info';\n\n return { percentage, class: cssClass };\n }\n\n getMemoryStatus(used, total) {\n const percentage = Math.round((used / total) * 100);\n return {\n used: Math.round(used),\n total: Math.round(total),\n percentage,\n class: this.getResourceStatus(percentage).class\n };\n }\n\n getLogLevelClass(level) {\n const classes = {\n 'debug': 'secondary',\n 'info': 'primary',\n 'warning': 'warning',\n 'error': 'danger'\n };\n return classes[level] || 'secondary';\n }\n\n formatUptime(seconds) {\n const days = Math.floor(seconds / 86400);\n const hours = Math.floor((seconds % 86400) / 3600);\n const minutes = Math.floor((seconds % 3600) / 60);\n \n if (days > 0) return `${days}d ${hours}h ${minutes}m`;\n if (hours > 0) return `${hours}h ${minutes}m`;\n return `${minutes}m`;\n }\n\n formatDuration(seconds) {\n if (seconds < 60) return `${Math.round(seconds)}s`;\n if (seconds < 3600) return `${Math.round(seconds / 60)}m`;\n if (seconds < 86400) return `${Math.round(seconds / 3600)}h`;\n return `${Math.round(seconds / 86400)}d`;\n }\n\n async setRunner(runner) {\n this.runner = runner;\n await this.prepareRunnerData();\n await this.loadRunnerMetrics();\n await this.loadCurrentTasks();\n await this.loadRunnerLogs();\n \n if (this.isMounted()) {\n await this.render();\n }\n }\n\n // Action handlers\n async onActionRefreshMetrics(action, event, element) {\n await this.loadRunnerMetrics();\n await this.render();\n }\n\n async onActionRefreshTasks(action, event, element) {\n await this.loadCurrentTasks();\n await this.render();\n }\n\n async onActionRefreshLogs(action, event, element) {\n await this.loadRunnerLogs();\n await this.render();\n }\n\n async onActionFilterLogs(action, event, element) {\n const level = element.getAttribute('data-level');\n const logEntries = this.element.querySelectorAll('.log-entry');\n \n logEntries.forEach(entry => {\n if (level === 'all' || entry.getAttribute('data-level') === level) {\n entry.style.display = 'block';\n } else {\n entry.style.display = 'none';\n }\n });\n\n // Update active filter button\n this.element.querySelectorAll('[data-action=\"filter-logs\"]').forEach(btn => {\n btn.classList.remove('active');\n });\n element.classList.add('active');\n }\n\n async onActionViewTask(action, event, element) {\n const taskId = element.getAttribute('data-task-id');\n this.emit('task:view', { taskId, runner: this.runner });\n }\n\n async onActionCancelTask(action, event, element) {\n const taskId = element.getAttribute('data-task-id');\n \n if (!confirm('Are you sure you want to cancel this task?')) {\n return;\n }\n\n try {\n const response = await this.getApp().rest.POST(`/api/tasks/${taskId}/cancel`);\n \n if (response.success && response.data.status) {\n this.showSuccess('Task cancelled successfully');\n await this.loadCurrentTasks();\n await this.render();\n this.emit('task:cancelled', { taskId, runner: this.runner });\n } else {\n this.showError(response.data.error || 'Failed to cancel task');\n }\n } catch (error) {\n console.error('Failed to cancel task:', error);\n this.showError('Failed to cancel task: ' + error.message);\n }\n }\n\n // Static method for easy Dialog integration\n static async show(runner, options = {}) {\n const view = new RunnerDetailsView({ runner });\n await view.onInit();\n\n const buttons = [];\n\n // Add action buttons based on runner status\n if (runner.status === 'active') {\n buttons.push({\n text: 'Pause Runner',\n class: 'btn-warning',\n action: async () => {\n if (confirm(`Are you sure you want to pause runner \"${runner.hostname}\"?`)) {\n try {\n const response = await view.getApp().rest.POST(`/api/runners/${runner.hostname}/pause`);\n if (response.success && response.data.status) {\n view.showSuccess('Runner paused successfully');\n return { action: 'paused', runner };\n } else {\n view.showError(response.data.error || 'Failed to pause runner');\n }\n } catch (error) {\n view.showError('Failed to pause runner: ' + error.message);\n }\n }\n return null;\n }\n });\n } else {\n buttons.push({\n text: 'Restart Runner',\n class: 'btn-success',\n action: async () => {\n if (confirm(`Are you sure you want to restart runner \"${runner.hostname}\"?`)) {\n try {\n const response = await view.getApp().rest.POST(`/api/runners/${runner.hostname}/restart`);\n if (response.success && response.data.status) {\n view.showSuccess('Runner restart initiated');\n return { action: 'restarted', runner };\n } else {\n view.showError(response.data.error || 'Failed to restart runner');\n }\n } catch (error) {\n view.showError('Failed to restart runner: ' + error.message);\n }\n }\n return null;\n }\n });\n }\n\n buttons.push({\n text: 'Configure',\n class: 'btn-outline-primary',\n action: () => {\n view.emit('runner:configure', { runner });\n return null;\n }\n });\n\n buttons.push({\n text: 'Remove Runner',\n class: 'btn-outline-danger',\n action: async () => {\n const confirmMessage = `Are you sure you want to remove runner \"${runner.hostname}\"? This action cannot be undone.`;\n if (confirm(confirmMessage)) {\n try {\n const response = await view.getApp().rest.DELETE(`/api/runners/${runner.hostname}`);\n if (response.success && response.data.status) {\n view.showSuccess('Runner removed successfully');\n return { action: 'removed', runner };\n } else {\n view.showError(response.data.error || 'Failed to remove runner');\n }\n } catch (error) {\n view.showError('Failed to remove runner: ' + error.message);\n }\n }\n return null;\n }\n });\n\n buttons.push({\n text: 'Export',\n class: 'btn-outline-secondary',\n action: () => {\n try {\n const exportData = {\n runner: runner,\n metrics: view.metrics,\n currentTasks: view.currentTasks,\n logs: view.logs,\n exported_at: new Date().toISOString(),\n exported_by: 'task-management-system'\n };\n\n const blob = new Blob([JSON.stringify(exportData, null, 2)], {\n type: 'application/json'\n });\n \n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `runner-${runner.hostname}-${Date.now()}.json`;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n\n view.showSuccess('Runner data exported successfully');\n return null;\n } catch (error) {\n view.showError('Failed to export runner data');\n return null;\n }\n }\n });\n\n buttons.push({\n text: 'Close',\n class: 'btn-secondary',\n dismiss: true\n });\n\n return await Dialog.showDialog({\n title: `<i class=\"bi bi-cpu me-2\"></i>Runner Details - ${runner.hostname}`,\n body: view,\n size: 'xl',\n scrollable: true,\n buttons: buttons,\n ...options\n });\n }\n}","/**\n * TaskManagementPage - Async task monitoring and management dashboard\n */\n\nimport Page from '@core/Page.js';\nimport View from '@core/View.js';\nimport Collection from '@core/Collection.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport TableView from '@core/views/table/TableView.js';\nimport { MetricsChart } from '@ext/charts/index.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport TaskDetailsView from './TaskDetailsView.js';\nimport RunnerDetailsView from './RunnerDetailsView.js';\n\n// Task Stats Header View\nclass TaskStatsView extends View {\n constructor(options = {}) {\n super({\n ...options,\n className: 'mojo-task-stats-section'\n });\n\n this.stats = {\n pending: 0,\n running: 0,\n completed: 0,\n errors: 0\n };\n }\n\n async getTemplate() {\n return `\n <div class=\"mojo-task-stats-header mb-4\">\n <div class=\"row\">\n <div class=\"col-xl-3 col-lg-6 col-12 mb-3\">\n <div class=\"card h-100 border-0 shadow-sm\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div>\n <h6 class=\"card-title text-muted mb-2\">Pending Tasks</h6>\n <h3 class=\"mb-1 fw-bold\">{{stats.pending}}</h3>\n <span class=\"badge bg-primary-subtle text-primary\">\n <i class=\"bi bi-clock\"></i> Queued\n </span>\n </div>\n <div class=\"text-primary\">\n <i class=\"bi bi-hourglass fs-2\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"col-xl-3 col-lg-6 col-12 mb-3\">\n <div class=\"card h-100 border-0 shadow-sm\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div>\n <h6 class=\"card-title text-muted mb-2\">Running Tasks</h6>\n <h3 class=\"mb-1 fw-bold\">{{stats.running}}</h3>\n <span class=\"badge bg-success-subtle text-success\">\n <i class=\"bi bi-play-circle\"></i> Active\n </span>\n </div>\n <div class=\"text-success\">\n <i class=\"bi bi-arrow-repeat fs-2\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"col-xl-3 col-lg-6 col-12 mb-3\">\n <div class=\"card h-100 border-0 shadow-sm\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div>\n <h6 class=\"card-title text-muted mb-2\">Completed Tasks</h6>\n <h3 class=\"mb-1 fw-bold\">{{stats.completed}}</h3>\n <span class=\"badge bg-info-subtle text-info\">\n <i class=\"bi bi-check-circle\"></i> Done\n </span>\n </div>\n <div class=\"text-info\">\n <i class=\"bi bi-check-square fs-2\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"col-xl-3 col-lg-6 col-12 mb-3\">\n <div class=\"card h-100 border-0 shadow-sm\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-start\">\n <div>\n <h6 class=\"card-title text-muted mb-2\">Failed Tasks</h6>\n <h3 class=\"mb-1 fw-bold\">{{stats.errors}}</h3>\n <span class=\"badge bg-danger-subtle text-danger\">\n <i class=\"bi bi-exclamation-circle\"></i> Errors\n </span>\n </div>\n <div class=\"text-danger\">\n <i class=\"bi bi-x-octagon fs-2\"></i>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n `;\n }\n\n async loadStats() {\n try {\n const response = await this.getApp().rest.GET('/api/tasks/status');\n if (response.success && response.data.status) {\n this.stats = response.data.data;\n }\n } catch (error) {\n console.error('Failed to load task stats:', error);\n }\n }\n\n async onInit() {\n await this.loadStats();\n }\n}\n\n// Task Runners Status View\nclass TaskRunnersView extends View {\n constructor(options = {}) {\n super({\n ...options,\n className: 'mojo-task-runners-section'\n });\n\n this.runners = [];\n }\n\n async getTemplate() {\n return `\n <div class=\"card border shadow-sm mb-4\">\n <div class=\"card-header d-flex justify-content-between align-items-center\">\n <h5 class=\"card-title mb-0\">\n <i class=\"bi bi-cpu me-2\"></i>Task Runners\n </h5>\n <button class=\"btn btn-sm btn-outline-primary\" data-action=\"refresh-runners\">\n <i class=\"bi bi-arrow-clockwise\"></i> Refresh\n </button>\n </div>\n <div class=\"card-body\">\n {{#runners.length}}\n <div class=\"mojo-task-runner-list\">\n {{#runners}}\n <div class=\"mojo-task-runner-item p-3 mb-2 bg-light rounded\">\n <div class=\"row align-items-center\">\n <div class=\"col-md-8 col-lg-9\">\n <div class=\"d-flex align-items-center\">\n <div class=\"mojo-task-runner-status me-3\">\n <span class=\"badge {{statusBadge}}\">\n <i class=\"bi {{statusIcon}}\"></i> {{status}}\n </span>\n </div>\n <div class=\"mojo-task-runner-info\">\n <div class=\"mojo-task-runner-name\">\n <strong>{{hostname}}</strong>\n {{#max_workers}}<span class=\"text-muted ms-2\">• {{max_workers}} workers</span>{{/max_workers}}\n </div>\n <div class=\"mojo-task-runner-channels\">\n <small class=\"text-muted\">\n {{#channels.length}}Channels: {{#channels}}{{.}}{{^last}}, {{/last}}{{/channels}}{{/channels.length}}\n {{^channels.length}}No channels assigned{{/channels.length}}\n </small>\n </div>\n </div>\n </div>\n </div>\n <div class=\"col-md-4 col-lg-3\">\n <div class=\"mojo-task-runner-actions d-flex justify-content-end align-items-center\">\n <small class=\"text-muted me-2 d-none d-sm-inline\">{{pingAgeText}}</small>\n <div class=\"dropdown\">\n <button class=\"btn btn-sm btn-outline-secondary dropdown-toggle\"\n data-bs-toggle=\"dropdown\" aria-expanded=\"false\">\n <i class=\"bi bi-three-dots-vertical\"></i>\n </button>\n <ul class=\"dropdown-menu dropdown-menu-end\">\n <li><button class=\"dropdown-item\" data-action=\"view-runner-details\" data-runner-id=\"{{id}}\">\n <i class=\"bi bi-info-circle me-2\"></i>View Details\n </button></li>\n {{#isActive}}\n <li><button class=\"dropdown-item text-warning\" data-action=\"pause-runner\" data-runner-id=\"{{id}}\">\n <i class=\"bi bi-pause me-2\"></i>Pause Runner\n </button></li>\n {{/isActive}}\n {{^isActive}}\n <li><button class=\"dropdown-item text-success\" data-action=\"restart-runner\" data-runner-id=\"{{id}}\">\n <i class=\"bi bi-play me-2\"></i>Restart Runner\n </button></li>\n {{/isActive}}\n <li><hr class=\"dropdown-divider\"></li>\n <li><button class=\"dropdown-item text-danger\" data-action=\"remove-runner\" data-runner-id=\"{{id}}\">\n <i class=\"bi bi-trash me-2\"></i>Remove Runner\n </button></li>\n </ul>\n </div>\n </div>\n </div>\n </div>\n <div class=\"row mt-2 d-sm-none\">\n <div class=\"col-12\">\n <small class=\"text-muted\">Last ping: {{pingAgeText}}</small>\n </div>\n </div>\n </div>\n {{/runners}}\n </div>\n {{/runners.length}}\n {{^runners.length}}\n <div class=\"text-center text-muted py-4\">\n <i class=\"bi bi-cpu fs-1\"></i>\n <p class=\"mt-2\">No task runners found</p>\n </div>\n {{/runners.length}}\n </div>\n </div>\n `;\n }\n\n async loadRunners() {\n try {\n const response = await this.getApp().rest.GET('/api/tasks/runners');\n if (response.success && response.data.status) {\n this.runners = response.data.data.map(runner => {\n const isActive = runner.status === 'active';\n const pingAge = runner.ping_age || 0;\n\n return {\n ...runner,\n isActive,\n statusBadge: isActive ? 'bg-success' : 'bg-warning',\n statusIcon: isActive ? 'bi-check-circle-fill' : 'bi-exclamation-triangle-fill',\n pingAgeText: this.formatPingAge(pingAge)\n };\n });\n }\n } catch (error) {\n console.error('Failed to load runners:', error);\n }\n }\n\n formatPingAge(seconds) {\n if (seconds < 60) return `${Math.round(seconds)}s ago`;\n if (seconds < 3600) return `${Math.round(seconds / 60)}m ago`;\n return `${Math.round(seconds / 3600)}h ago`;\n }\n\n async onInit() {\n await this.loadRunners();\n }\n\n async onActionRefreshRunners(event, element) {\n await this.loadRunners();\n }\n\n async onActionViewRunnerDetails(event, element) {\n const runnerId = element.getAttribute('data-runner-id');\n const runner = this.runners.find(r => r.id === runnerId);\n\n if (runner) {\n const result = await RunnerDetailsView.show(runner);\n\n // Refresh runners list if any action was taken\n if (result?.action) {\n await this.loadRunners();\n this.emit('runner:' + result.action, result);\n }\n }\n }\n\n async onActionPauseRunner(event, element) {\n const runnerId = element.getAttribute('data-runner-id');\n const runner = this.runners.find(r => r.id === runnerId);\n\n if (!runner || !confirm(`Are you sure you want to pause runner \"${runner.hostname}\"?`)) {\n return;\n }\n\n try {\n element.disabled = true;\n const response = await this.getApp().rest.POST(`/api/runners/${runnerId}/pause`);\n\n if (response.success && response.data.status) {\n this.showSuccess('Runner paused successfully');\n await this.loadRunners();\n } else {\n this.showError(response.data.error || 'Failed to pause runner');\n }\n } catch (error) {\n console.error('Failed to pause runner:', error);\n this.showError('Failed to pause runner: ' + error.message);\n } finally {\n element.disabled = false;\n }\n }\n\n async onActionRestartRunner(event, element) {\n const runnerId = element.getAttribute('data-runner-id');\n const runner = this.runners.find(r => r.id === runnerId);\n\n if (!runner || !confirm(`Are you sure you want to restart runner \"${runner.hostname}\"?`)) {\n return;\n }\n\n try {\n element.disabled = true;\n const response = await this.getApp().rest.POST(`/api/runners/${runnerId}/restart`);\n\n if (response.success && response.data.status) {\n this.showSuccess('Runner restart initiated');\n await this.loadRunners();\n } else {\n this.showError(response.data.error || 'Failed to restart runner');\n }\n } catch (error) {\n console.error('Failed to restart runner:', error);\n this.showError('Failed to restart runner: ' + error.message);\n } finally {\n element.disabled = false;\n }\n }\n\n async onActionRemoveRunner(event, element) {\n const runnerId = element.getAttribute('data-runner-id');\n const runner = this.runners.find(r => r.id === runnerId);\n\n if (!runner) return;\n\n const confirmMessage = `Are you sure you want to remove runner \"${runner.hostname}\"? This action cannot be undone.`;\n if (!confirm(confirmMessage)) {\n return;\n }\n\n try {\n element.disabled = true;\n const response = await this.getApp().rest.DELETE(`/api/runners/${runnerId}`);\n\n if (response.success && response.data.status) {\n this.showSuccess('Runner removed successfully');\n await this.loadRunners();\n } else {\n this.showError(response.data.error || 'Failed to remove runner');\n }\n } catch (error) {\n console.error('Failed to remove runner:', error);\n this.showError('Failed to remove runner: ' + error.message);\n } finally {\n element.disabled = false;\n }\n }\n}\n\n// Task Charts View\nclass TaskChartsView extends View {\n constructor(options = {}) {\n super({\n ...options,\n className: 'mojo-task-charts-section'\n });\n }\n\n async getTemplate() {\n return `\n <div class=\"row mb-4\">\n <div class=\"col-xl-6 col-lg-12 mb-4\">\n <div class=\"card border shadow-sm\">\n <div class=\"card-body\" style=\"min-height: 300px;\">\n <div data-container=\"task-flow-chart\"></div>\n </div>\n </div>\n </div>\n <div class=\"col-xl-6 col-lg-12 mb-4\">\n <div class=\"card border shadow-sm\">\n <div class=\"card-body\" style=\"min-height: 300px;\">\n <div data-container=\"task-errors-chart\"></div>\n </div>\n </div>\n </div>\n </div>\n `;\n }\n\n async onInit() {\n // Task Flow Chart (Published vs Completed)\n this.taskFlowChart = new MetricsChart({\n title: '<i class=\"bi bi-graph-up me-2\"></i>Task Flow',\n endpoint: '/api/metrics/fetch',\n height: 280,\n granularity: 'hours',\n slugs: ['tasks_pub', 'tasks_completed'],\n account: 'global',\n chartType: 'line',\n showDateRange: false,\n colors: [\n 'rgba(13, 110, 253, 0.8)', // Primary blue for published\n 'rgba(25, 135, 84, 0.8)' // Success green for completed\n ],\n yAxis: {\n label: 'Count',\n beginAtZero: true\n },\n tooltip: {\n y: 'number'\n },\n containerId: 'task-flow-chart'\n });\n this.addChild(this.taskFlowChart);\n\n // Task Errors Chart\n this.taskErrorsChart = new MetricsChart({\n title: '<i class=\"bi bi-exclamation-triangle me-2\"></i>Task Issues',\n endpoint: '/api/metrics/fetch',\n height: 280,\n granularity: 'hours',\n slugs: ['tasks_errors', 'tasks_expired'],\n account: 'global',\n chartType: 'line',\n showDateRange: false,\n colors: [\n 'rgba(220, 53, 69, 0.8)', // Danger red for errors\n 'rgba(255, 193, 7, 0.8)' // Warning yellow for expired\n ],\n yAxis: {\n label: 'Count',\n beginAtZero: true\n },\n tooltip: {\n y: 'number'\n },\n containerId: 'task-errors-chart'\n });\n this.addChild(this.taskErrorsChart);\n }\n}\n\n// Task Table Views\nclass BaseTaskTable extends TableView {\n constructor(options = {}) {\n super({\n showPagination: true,\n showSearch: true,\n showRefresh: true,\n pageSize: 10,\n columns: [\n { key: 'id', label: 'Task ID', sortable: true },\n { key: 'function', label: 'Function', sortable: true },\n { key: 'channel', label: 'Channel', sortable: true },\n { key: 'created', label: 'Created', sortable: true, formatter: 'datetime' },\n { key: 'status', label: 'Status', formatter: 'badge' }\n ],\n ...options\n });\n }\n\n buildActionCell(item) {\n const actions = [];\n\n if (item.status === 'pending') {\n actions.push(`\n <button class=\"btn btn-sm btn-outline-danger\" data-action=\"cancel-task\" data-task-id=\"${item.id}\" title=\"Cancel Task\">\n <i class=\"bi bi-x-circle\"></i>\n </button>\n `);\n }\n\n if (item.status === 'error') {\n actions.push(`\n <button class=\"btn btn-sm btn-outline-primary\" data-action=\"retry-task\" data-task-id=\"${item.id}\" title=\"Retry Task\">\n <i class=\"bi bi-arrow-clockwise\"></i>\n </button>\n `);\n }\n\n actions.push(`\n <button class=\"btn btn-sm btn-outline-info\" data-action=\"view-task-details\" data-task-id=\"${item.id}\" title=\"View Details\">\n <i class=\"bi bi-info-circle\"></i>\n </button>\n `);\n\n return actions.join(' ');\n }\n\n async onActionCancelTask(action, event, element) {\n const taskId = element.getAttribute('data-task-id');\n\n if (!confirm('Are you sure you want to cancel this task?')) {\n return;\n }\n\n try {\n element.disabled = true;\n const response = await this.getApp().rest.POST(`/api/tasks/${taskId}/cancel`);\n\n if (response.success && response.data.status) {\n this.showSuccess('Task cancelled successfully');\n // Refresh the active table\n const activeTab = this.getParentPage()?.taskTablesView?.getActiveTab();\n if (activeTab) {\n const activeTable = this.getParentPage()?.taskTablesView?.getTab(activeTab);\n if (activeTable?.refresh) {\n await activeTable.refresh();\n }\n }\n } else {\n this.showError(response.data.error || 'Failed to cancel task');\n }\n } catch (error) {\n console.error('Failed to cancel task:', error);\n this.showError('Failed to cancel task: ' + error.message);\n } finally {\n element.disabled = false;\n }\n }\n\n async onActionRetryTask(action, event, element) {\n const taskId = element.getAttribute('data-task-id');\n\n try {\n element.disabled = true;\n const response = await this.getApp().rest.POST(`/api/tasks/${taskId}/retry`);\n\n if (response.success && response.data.status) {\n this.showSuccess('Task queued for retry');\n // Refresh the active table\n const activeTab = this.getParentPage()?.taskTablesView?.getActiveTab();\n if (activeTab) {\n const activeTable = this.getParentPage()?.taskTablesView?.getTab(activeTab);\n if (activeTable?.refresh) {\n await activeTable.refresh();\n }\n }\n } else {\n this.showError(response.data.error || 'Failed to retry task');\n }\n } catch (error) {\n console.error('Failed to retry task:', error);\n this.showError('Failed to retry task: ' + error.message);\n } finally {\n element.disabled = false;\n }\n }\n\n async onActionViewTaskDetails(action, event, element) {\n const taskId = element.getAttribute('data-task-id');\n\n try {\n // Fetch detailed task data\n const response = await this.getApp().rest.GET(`/api/tasks/${taskId}/details`);\n\n if (response.success && response.data.status) {\n const task = response.data.data;\n const result = await TaskDetailsView.show(task);\n\n // Refresh tables if any action was taken\n if (result?.action) {\n await this.refreshActiveTables();\n this.emit('task:' + result.action, result);\n }\n } else {\n this.showError(response.data.error || 'Failed to load task details');\n }\n } catch (error) {\n console.error('Failed to load task details:', error);\n this.showError('Failed to load task details: ' + error.message);\n }\n }\n\n getParentPage() {\n // Helper to get the parent TaskManagementPage\n let parent = this.parent;\n while (parent && parent.constructor.name !== 'TaskManagementPage') {\n parent = parent.parent;\n }\n return parent;\n }\n\n async refreshActiveTables() {\n const parentPage = this.getParentPage();\n if (parentPage?.taskTablesView) {\n const activeTab = parentPage.taskTablesView.getActiveTab();\n if (activeTab) {\n const activeTable = parentPage.taskTablesView.getTab(activeTab);\n if (activeTable?.refresh) {\n await activeTable.refresh();\n }\n }\n }\n }\n}\n\nclass PendingTasksTable extends TableView {\n constructor(options = {}) {\n super({\n ...options,\n title: 'Pending Tasks',\n collection: new Collection({\n endpoint: '/api/tasks/pending'\n }),\n columns: [\n { key: 'id', label: 'Task ID', sortable: true },\n { key: 'function', label: 'Function', sortable: true },\n { key: 'channel', label: 'Channel', sortable: true },\n { key: 'created', label: 'Created', sortable: true, formatter: 'datetime' },\n { key: 'status', label: 'Status', formatter: 'badge' }\n ],\n });\n }\n}\n\nclass RunningTasksTable extends TableView {\n constructor(options = {}) {\n super({\n ...options,\n title: 'Running Tasks',\n collection: new Collection({\n endpoint: '/api/tasks/running'\n }),\n columns: [\n { key: 'id', label: 'Task ID', sortable: true },\n { key: 'function', label: 'Function', sortable: true },\n { key: 'channel', label: 'Channel', sortable: true },\n { key: 'created', label: 'Created', sortable: true, formatter: 'datetime' },\n { key: 'status', label: 'Status', formatter: 'badge' }\n ],\n });\n }\n}\n\nclass CompletedTasksTable extends TableView {\n constructor(options = {}) {\n super({\n ...options,\n title: 'Completed Tasks',\n collection: new Collection({\n endpoint: '/api/tasks/completed'\n }),\n columns: [\n { key: 'id', label: 'Task ID', sortable: true },\n { key: 'function', label: 'Function', sortable: true },\n { key: 'channel', label: 'Channel', sortable: true },\n { key: 'created', label: 'Created', sortable: true, formatter: 'datetime' },\n { key: 'completed_at', label: 'Completed', sortable: true, formatter: 'datetime' },\n { key: 'status', label: 'Status', formatter: 'badge' }\n ]\n });\n }\n}\n\nclass ErrorTasksTable extends TableView {\n constructor(options = {}) {\n super({\n ...options,\n title: 'Failed Tasks',\n collection: new Collection({\n endpoint: '/api/tasks/errors'\n }),\n columns: [\n { key: 'id', label: 'Task ID', sortable: true },\n { key: 'function', label: 'Function', sortable: true },\n { key: 'channel', label: 'Channel', sortable: true },\n { key: 'created', label: 'Created', sortable: true, formatter: 'datetime' },\n { key: 'error', label: 'Error', sortable: false },\n { key: 'status', label: 'Status', formatter: 'badge' }\n ]\n });\n }\n}\n\n// Main Task Management Page\nexport default class TaskManagementPage extends Page {\n constructor(options = {}) {\n super({\n ...options,\n title: 'Task Management',\n className: 'mojo-task-management-page'\n });\n\n this.pageTitle = 'Task Management';\n this.pageSubtitle = 'Async task monitoring and runner management';\n this.lastUpdated = new Date().toLocaleString();\n }\n\n async getTemplate() {\n return `\n <div class=\"mojo-task-management-container\">\n <!-- Page Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-4\">\n <div>\n <h1 class=\"h3 mb-1\">{{pageTitle}}</h1>\n <p class=\"text-muted mb-0\">{{pageSubtitle}}</p>\n <small class=\"text-info\">\n <i class=\"bi bi-cpu me-1\"></i>\n Real-time task processing and runner monitoring\n </small>\n </div>\n <div class=\"btn-group\" role=\"group\">\n <button type=\"button\" class=\"btn btn-outline-secondary btn-sm\"\n data-action=\"refresh-all\" title=\"Refresh All Data\">\n <i class=\"bi bi-arrow-clockwise\"></i> Refresh\n </button>\n <button type=\"button\" class=\"btn btn-outline-primary btn-sm\"\n data-action=\"export-tasks\" title=\"Export Task Data\">\n <i class=\"bi bi-download\"></i> Export\n </button>\n <button type=\"button\" class=\"btn btn-outline-success btn-sm\"\n data-action=\"manage-channels\" title=\"Manage Channels\">\n <i class=\"bi bi-collection\"></i> Channels\n </button>\n </div>\n </div>\n\n <!-- Task Stats -->\n <div data-container=\"task-stats\"></div>\n\n <!-- Task Runners -->\n <div data-container=\"task-runners\"></div>\n\n <!-- Task Charts -->\n <div data-container=\"task-charts\"></div>\n\n <!-- Task Tables -->\n <div class=\"card border shadow-sm\">\n <div class=\"card-header\">\n <h5 class=\"card-title mb-0\">\n <i class=\"bi bi-list-task me-2\"></i>Task Management\n </h5>\n </div>\n <div class=\"card-body\">\n <div data-container=\"task-tables\"></div>\n </div>\n </div>\n\n <!-- System Status Footer -->\n <div class=\"row mt-4\">\n <div class=\"col-12\">\n <div class=\"alert alert-info border-0\" role=\"alert\">\n <div class=\"d-flex align-items-center\">\n <i class=\"bi bi-info-circle-fill me-2\"></i>\n <div>\n <strong>Task System Status:</strong> Monitoring active.\n Last updated: <span class=\"text-muted\">{{lastUpdated}}</span>\n </div>\n <div class=\"ms-auto\">\n <button class=\"btn btn-sm btn-outline-info\" data-action=\"view-system-logs\">\n <i class=\"bi bi-journal-text\"></i> View Logs\n </button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n `;\n }\n\n async onInit() {\n // Create Task Stats View\n this.taskStatsView = new TaskStatsView({\n containerId: 'task-stats'\n });\n this.addChild(this.taskStatsView);\n\n // Create Task Runners View\n this.taskRunnersView = new TaskRunnersView({\n containerId: 'task-runners'\n });\n this.addChild(this.taskRunnersView);\n\n // Create Task Charts View\n this.taskChartsView = new TaskChartsView({\n containerId: 'task-charts'\n });\n this.addChild(this.taskChartsView);\n\n // Create Task Tables with TabView\n this.taskTablesView = new TabView({\n containerId: 'task-tables',\n tabs: {\n 'Pending': new PendingTasksTable(),\n 'Running': new RunningTasksTable(),\n 'Completed': new CompletedTasksTable(),\n 'Errors': new ErrorTasksTable()\n },\n activeTab: 'Pending'\n });\n this.addChild(this.taskTablesView);\n }\n\n async onActionRefreshAll(action, event, element) {\n try {\n // Show loading state\n const icon = element.querySelector('i');\n icon?.classList.add('bi-spin');\n element.disabled = true;\n\n // Refresh all components\n const promises = [\n this.taskStatsView?.loadStats(),\n this.taskRunnersView?.loadRunners(),\n this.taskChartsView?.taskFlowChart?.refresh(),\n this.taskChartsView?.taskErrorsChart?.refresh()\n ].filter(Boolean);\n\n // Refresh active table\n const activeTab = this.taskTablesView?.getActiveTab();\n if (activeTab) {\n const activeTable = this.taskTablesView.getTab(activeTab);\n if (activeTable?.refresh) {\n promises.push(activeTable.refresh());\n }\n }\n\n await Promise.allSettled(promises);\n\n // Update timestamp\n this.lastUpdated = new Date().toLocaleString();\n\n // Emit refresh event\n const eventBus = this.getApp()?.events;\n if (eventBus) {\n eventBus.emit('tasks:dashboard-refreshed', {\n page: this,\n timestamp: this.lastUpdated\n });\n }\n\n } catch (error) {\n console.error('Failed to refresh task dashboard:', error);\n } finally {\n // Reset button state\n const icon = element.querySelector('i');\n icon?.classList.remove('bi-spin');\n element.disabled = false;\n }\n }\n\n async onActionExportTasks(action, event, element) {\n try {\n // Export charts\n await this.taskChartsView?.taskFlowChart?.export('png');\n await this.taskChartsView?.taskErrorsChart?.export('png');\n\n // Export active table\n const activeTab = this.taskTablesView?.getActiveTab();\n if (activeTab) {\n const activeTable = this.taskTablesView.getTab(activeTab);\n if (activeTable?.exportToCSV) {\n activeTable.exportToCSV();\n }\n }\n\n } catch (error) {\n console.error('Failed to export task data:', error);\n }\n }\n\n async onActionManageChannels(event, element) {\n try {\n // For now, show a simple info modal with channel information\n const response = await this.getApp().rest.GET('/api/tasks/channels');\n\n if (response.success && response.data.status) {\n const channels = response.data.data;\n\n // Create a simple info alert\n const channelList = channels.map(channel =>\n `${channel.name} (${channel.pending} pending, ${channel.running} running)`\n ).join('\\n');\n\n alert(`Task Channels:\\n\\n${channelList}\\n\\nFull channel management interface coming soon!`);\n } else {\n this.showError('Failed to load channel information');\n }\n } catch (error) {\n console.error('Failed to load channels:', error);\n // Navigate to dedicated channels page as fallback\n const router = this.getApp()?.router;\n if (router) {\n router.navigateTo('/admin/task-channels');\n }\n }\n }\n\n async onActionViewSystemLogs(event, element) {\n try {\n // Try to get recent system logs related to tasks\n const response = await this.getApp().rest.GET('/api/tasks/logs?limit=50');\n\n if (response.success && response.data.status) {\n const logs = response.data.data;\n\n // Create a simple log preview\n const logPreview = logs.slice(0, 10).map(log =>\n `[${new Date(log.timestamp * 1000).toLocaleString()}] ${log.level.toUpperCase()}: ${log.message}`\n ).join('\\n');\n\n const viewFullLogs = confirm(`Recent Task System Logs:\\n\\n${logPreview}\\n\\n... and ${logs.length - 10} more entries.\\n\\nView full logs?`);\n\n if (viewFullLogs) {\n const router = this.getApp()?.router;\n if (router) {\n router.navigateTo('/admin/logs?filter=tasks');\n }\n }\n } else {\n // Navigate to system logs as fallback\n const router = this.getApp()?.router;\n if (router) {\n router.navigateTo('/admin/logs');\n }\n }\n } catch (error) {\n console.error('Failed to load system logs:', error);\n // Navigate to system logs as fallback\n const router = this.getApp()?.router;\n if (router) {\n router.navigateTo('/admin/logs');\n }\n }\n }\n\n // Public API\n async refreshDashboard() {\n return this.onActionRefreshAll(null, null, { disabled: false, querySelector: () => null });\n }\n\n getStats() {\n return this.taskStatsView?.stats || {};\n }\n\n getRunners() {\n return this.taskRunnersView?.runners || [];\n }\n\n getCharts() {\n return {\n taskFlow: this.taskChartsView?.taskFlowChart,\n taskErrors: this.taskChartsView?.taskErrorsChart\n };\n }\n\n\n}\n","/**\n * LogView - Detailed view for a Log record\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport DataView from '@core/views/data/DataView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport { Log } from '@core/models/Log.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport DeviceView from '../account/devices/DeviceView.js';\nimport GeoIPView from '../account/devices/GeoIPView.js';\n\nclass LogView extends View {\n constructor(options = {}) {\n super({\n className: 'log-view',\n ...options\n });\n\n this.model = options.model || new Log(options.data || {});\n this.logIcon = this.getIconForLog(this.model.get('level'));\n\n this.template = `\n <div class=\"log-view-container\">\n <!-- Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-4\">\n <div class=\"d-flex align-items-center gap-3\">\n <div class=\"fs-1 {{logIcon.color}}\">\n <i class=\"bi {{logIcon.icon}}\"></i>\n </div>\n <div>\n <h4 class=\"mb-1\">\n <span class=\"badge bg-secondary\">{{model.method}}</span> {{model.path}}\n </h4>\n <div class=\"text-muted small\">\n {{model.created|datetime}}\n </div>\n </div>\n </div>\n <div data-container=\"log-context-menu\"></div>\n </div>\n\n <!-- Tabs -->\n <div data-container=\"log-tabs\"></div>\n </div>\n `;\n }\n\n getIconForLog(level) {\n const lvl = level?.toLowerCase();\n if (lvl === 'error' || lvl === 'critical') return { icon: 'bi-x-octagon-fill', color: 'text-danger' };\n if (lvl === 'warning') return { icon: 'bi-exclamation-triangle-fill', color: 'text-warning' };\n if (lvl === 'info') return { icon: 'bi-info-circle-fill', color: 'text-info' };\n return { icon: 'bi-journal-text', color: 'text-secondary' };\n }\n\n async onInit() {\n // Overview Tab\n this.overviewView = new DataView({\n model: this.model,\n className: \"p-3\",\n columns: 2,\n fields: [\n { name: 'id', label: 'Log ID' },\n { name: 'level', label: 'Level', format: 'badge' },\n { name: 'kind', label: 'Kind' },\n { name: 'ip', label: 'IP Address', template: '<a href=\"#\" data-action=\"view-ip\">{{model.ip}}</a>' },\n { name: 'uid', label: 'User ID' },\n { name: 'username', label: 'Username' },\n { name: 'duid', label: 'Device ID', template: '<a href=\"#\" data-action=\"view-device\">{{model.duid|truncate_middle(32)}}</a>' },\n { name: 'model_name', label: 'Related Model' },\n { name: 'model_id', label: 'Related Model ID' },\n ]\n });\n\n // Log Content Tab\n const logContent = this.model.get('log');\n let formattedLog = logContent;\n try {\n // Attempt to parse and prettify if it's a JSON string\n const parsed = JSON.parse(logContent);\n formattedLog = JSON.stringify(parsed, null, 2);\n } catch (e) {\n // Not a valid JSON string, display as is\n }\n\n this.logContentView = new View({\n template: `\n <div class=\"position-relative\">\n <button class=\"btn btn-sm btn-outline-secondary position-absolute top-0 end-0 mt-2 me-2\" data-action=\"copy-log\">\n <i class=\"bi bi-clipboard\"></i> Copy\n </button>\n <pre class=\"bg-light p-3 border rounded\" style=\"max-height: 600px; overflow-y: auto;\"><code>${formattedLog}</code></pre>\n </div>\n `,\n onActionCopyLog: () => {\n navigator.clipboard.writeText(formattedLog);\n this.getApp()?.toast?.success('Log content copied to clipboard.');\n }\n });\n\n // TabView\n this.tabView = new TabView({\n containerId: 'log-tabs',\n tabs: {\n 'Overview': this.overviewView,\n 'Log Content': this.logContentView,\n },\n activeTab: 'Overview'\n });\n this.addChild(this.tabView);\n\n // ContextMenu\n const logMenu = new ContextMenu({\n containerId: 'log-context-menu',\n className: \"context-menu-view header-menu-absolute\",\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: [\n { label: 'View User', action: 'view-user', icon: 'bi-person', disabled: !this.model.get('uid') },\n { label: 'View Device', action: 'view-device', icon: 'bi-phone', disabled: !this.model.get('duid') },\n { type: 'divider' },\n { label: 'Delete Log', action: 'delete-log', icon: 'bi-trash', danger: true }\n ]\n }\n });\n this.addChild(logMenu);\n }\n\n async onActionViewIp(event) {\n event.preventDefault();\n const ip = this.model.get('ip');\n if (ip) {\n GeoIPView.show(ip);\n }\n }\n\n async onActionViewDevice(event) {\n event.preventDefault();\n const duid = this.model.get('duid');\n if (duid) {\n DeviceView.show(duid);\n }\n }\n\n async onActionViewUser() {\n console.log(\"TODO: View user\", this.model.get('uid'));\n }\n\n async onActionDeleteLog() {\n const confirmed = await Dialog.confirm(\n `Are you sure you want to delete this log entry? This action cannot be undone.`,\n 'Confirm Deletion',\n { confirmClass: 'btn-danger', confirmText: 'Delete' }\n );\n if (confirmed) {\n const resp = await this.model.destroy();\n if (resp.success) {\n this.emit('log:deleted', { model: this.model });\n }\n }\n }\n}\n\nLog.VIEW_CLASS = LogView;\n\nexport default LogView;\n","/**\n * LogTablePage - Log management using TablePage component\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { LogList } from '@core/models/Log.js';\nimport LogView from './LogView.js';\n\nclass LogTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_logs',\n pageName: 'Manage Logs',\n router: \"admin/logs\",\n Collection: LogList,\n\n itemViewClass: LogView,\n viewDialogOptions: {\n header: false,\n size: 'xl'\n },\n\n // Column definitions\n columns: [\n {\n key: 'created|epoch|datetime',\n label: 'Timestamp',\n sortable: true,\n filter: {\n type: 'daterange',\n }\n },\n {\n key: 'level',\n label: 'Level',\n sortable: true,\n formatter: 'badge',\n filter: {\n type: 'select',\n options: [\n { value: 'info', label: 'Info' },\n { value: 'warning', label: 'Warning' },\n { value: 'error', label: 'Error' }\n ]\n }\n },\n {\n key: 'kind',\n label: 'Kind',\n filter: {\n type: \"text\"\n }\n },\n {\n key: 'method',\n label: 'Method',\n filter: {\n type: \"text\"\n }\n },\n {\n key: 'path',\n label: 'Path',\n filter: {\n type: \"text\"\n }\n },\n {\n key: 'username',\n label: 'User',\n filter: {\n type: \"text\"\n }\n },\n {\n key: 'ip',\n label: 'IP',\n filter: {\n type: \"text\"\n }\n },\n {\n key: 'duid',\n label: 'Browser ID',\n formatter: 'truncate_middle(16)',\n filter: {\n type: \"text\"\n }\n }\n ],\n\n // Default sort by timestamp descending (newest first)\n defaultQuery: {\n sort: '-created'\n },\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: false, // Logs are typically not manually created\n showExport: true,\n\n // Empty state\n emptyMessage: 'No log entries found.',\n\n // Batch actions\n batchBarLocation: 'top',\n batchActions: [\n { label: \"Export\", icon: \"bi bi-download\", action: \"batch-export\" },\n { label: \"Archive\", icon: \"bi bi-archive\", action: \"batch-archive\" },\n { label: \"Delete\", icon: \"bi bi-trash\", action: \"batch-delete\" },\n { label: \"Mark as Reviewed\", icon: \"bi bi-check2\", action: \"batch-reviewed\" }\n ],\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n}\n\nexport default LogTablePage;\n","import View from '@core/View.js';\nimport DataView from '@core/views/data/DataView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport { MetricsPermission, MetricsForms } from '@core/models/Metrics.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\n\nclass MetricsPermissionsView extends View {\n constructor(options = {}) {\n super({\n className: 'metrics-permissions-view',\n ...options\n });\n\n this.model = options.model || new MetricsPermission(options.data || {});\n\n this.template = `\n <div class=\"container p-3\">\n <div class=\"d-flex justify-content-between align-items-center mb-4\">\n <div>\n <h3 class=\"mb-1\">Permissions for {{model.account}}</h3>\n </div>\n <div data-container=\"context-menu\"></div>\n </div>\n <div data-container=\"data-view\"></div>\n </div>\n `;\n }\n\n async onInit() {\n this.dataView = new DataView({\n containerId: 'data-view',\n model: this.model,\n fields: [\n { name: 'view_permissions', label: 'View Permissions', format: 'list|badge' },\n { name: 'write_permissions', label: 'Write Permissions', format: 'list|badge' },\n ]\n });\n this.addChild(this.dataView);\n\n const contextMenu = new ContextMenu({\n containerId: 'context-menu',\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: [\n { label: 'Edit', action: 'edit', icon: 'bi-pencil' },\n { label: 'Delete', action: 'delete', icon: 'bi-trash', danger: true },\n ]\n }\n });\n this.addChild(contextMenu);\n }\n\n async onActionEdit() {\n const resp = await Dialog.showModelForm({\n title: `Edit Permissions for ${this.model.get('account')}`,\n model: this.model,\n formConfig: MetricsForms.edit\n });\n if (resp) {\n this.model.set(resp.data.data);\n this.render();\n }\n }\n\n async onActionDelete() {\n const confirmed = await Dialog.confirm(`Are you sure you want to delete all permissions for ${this.model.get('account')}?`);\n if (confirmed) {\n await this.model.destroy();\n this.emit('deleted', this.model);\n }\n }\n}\n\nMetricsPermission.VIEW_CLASS = MetricsPermissionsView;\n\nexport default MetricsPermissionsView;\n","import TablePage from '@core/pages/TablePage.js';\nimport { MetricsPermissionList, MetricsForms } from '@core/models/Metrics.js';\nimport MetricsPermissionsView from './MetricsPermissionsView.js';\n\nclass MetricsPermissionsTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_metrics_permissions',\n pageName: 'Metrics Permissions',\n router: \"admin/metrics/permissions\",\n Collection: MetricsPermissionList,\n formEdit: MetricsForms.edit,\n itemViewClass: MetricsPermissionsView,\n viewDialogOptions: {\n header: false,\n size: 'lg'\n },\n\n columns: [\n { key: 'account', label: 'Account', sortable: true },\n { key: 'view_permissions', label: 'View Permissions', formatter: 'list|badge' },\n { key: 'write_permissions', label: 'Write Permissions', formatter: 'list|badge' },\n ],\n\n selectable: true,\n searchable: true,\n sortable: true,\n paginated: true,\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n tableOptions: {\n pageSizes: [10, 25, 50],\n defaultPageSize: 25,\n emptyMessage: 'No metrics permissions found.',\n emptyIcon: 'bi-bar-chart-line',\n actions: [\"view\", \"edit\", \"delete\"],\n }\n });\n }\n}\n\nexport default MetricsPermissionsTablePage;\n","/**\n * FileManagerTablePage - File Manager backend management using TablePage component\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { FileManagerList, FileManagerForms } from '@core/models/Files.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\n\nclass FileManagerTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_file_managers',\n pageName: 'Manage Storage Backends',\n router: \"admin/file-managers\",\n Collection: FileManagerList,\n\n formCreate: FileManagerForms.create,\n formEdit: FileManagerForms.edit,\n\n // Column definitions\n columns: [\n {\n key: 'id',\n label: 'ID',\n sortable: true,\n class: 'text-muted'\n },\n {\n key: 'name',\n label: 'Name',\n formatter: \"default('Unnamed Backend')\"\n },\n {\n key: 'backend_url',\n label: 'Backend URL',\n sortable: true\n },\n {\n key: 'is_default',\n label: 'Default',\n formatter: \"boolean|badge\"\n },\n {\n key: 'is_active',\n label: 'Active',\n formatter: \"boolean|badge\"\n },\n {\n key: 'backend_type',\n label: 'Type',\n formatter: \"default('Unknown')\"\n },\n {\n key: 'created',\n label: 'Created',\n formatter: \"epoch|datetime\"\n }\n ],\n\n contextMenu: [\n { icon: 'bi-pencil', action: 'edit', label: 'Edit Name' },\n { icon: 'bi-shield', action: 'edit-credentials', label: 'Edit Credentials' },\n { icon: 'bi-person', action: 'edit-owners', label: 'Edit Owners' },\n { divider: true },\n { icon: 'bi-copy', action: 'clone', label: 'Clone Manager' },\n { divider: true },\n { icon: 'bi-check', action: 'test-connection', label: 'Test Connection' },\n { icon: 'bi-question-circle', action: 'check-cors', label: 'Check CORS' },\n { icon: 'bi-wrench', action: 'fix-cors', label: 'Fix CORS' },\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No storage backends found. Click \"Add Storage Backend\" to configure your first backend.',\n\n // Batch actions\n batchBarLocation: 'top',\n batchActions: [\n { label: \"Delete\", icon: \"bi bi-trash\", action: \"batch-delete\" },\n { label: \"Export\", icon: \"bi bi-download\", action: \"batch-export\" },\n { label: \"Activate\", icon: \"bi bi-check-circle\", action: \"batch-activate\" },\n { label: \"Deactivate\", icon: \"bi bi-x-circle\", action: \"batch-deactivate\" }\n ],\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n\n async onActionEditOwners(event, element) {\n const item = this.collection.get(element.dataset.id);\n const result = await Dialog.showModelForm({\n title: 'Edit Owners',\n model: item,\n fields: FileManagerForms.owners.fields\n });\n if (!result) return true;\n if (result.success) {\n this.getApp().toast.success(\"Owners Updated successfully\");\n } else {\n this.getApp().toast.error(\"Owners update failed\");\n }\n }\n\n async onActionCheckCors(event, element) {\n const item = this.collection.get(element.dataset.id);\n const result = await item.save({check_cors: true});\n if (result.success && result.data.status) {\n // this.getApp().toast.success(result.data.result);\n await Dialog.showData({\n title: `Audit Report - ${item._.name}`,\n data: result.data,\n size: 'lg'\n });\n } else {\n this.getApp().toast.error(\"Connection test failed\");\n }\n return true;\n }\n\n async onActionTestConnection(event, element) {\n const item = this.collection.get(element.dataset.id);\n const result = await item.save({test_connection: true});\n if (result.success && result.data.status) {\n this.getApp().toast.success(\"Connection test successful\");\n } else {\n this.getApp().toast.error(\"Connection test failed\");\n }\n return true;\n }\n\n async onActionEditCredentials(event, element) {\n const item = this.collection.get(element.dataset.id);\n const result = await Dialog.showModelForm({\n title: 'Edit Credentials',\n model: item,\n fields: FileManagerForms.credentials.fields\n });\n\n if (!result) return true;\n\n if (result.success && result.data.status) {\n this.getApp().toast.success(\"Credentials updated successfully\");\n } else {\n this.getApp().toast.error(\"Failed to update credentials\");\n }\n return true;\n }\n\n async onActionClone(event, element) {\n\n const confirmed = await Dialog.showConfirm({\n title: 'Clone File Manager',\n message: 'This will create a clone with the same credentials.',\n });\n\n if (!confirmed) {\n return true;\n }\n\n const item = this.collection.get(element.dataset.id);\n const result = await item.save({clone: true});\n if (result.success && result.data.status) {\n this.getApp().toast.success(\"Connection cloned successfully\");\n this.collection.fetch();\n } else {\n this.getApp().toast.error(\"Failed to clone connection\");\n }\n return true;\n }\n}\n\nexport default FileManagerTablePage;\n","/**\n * FileView - Comprehensive file management interface\n *\n * Features:\n * - Clean header with file thumbnail/icon, name, size, and status\n * - Tabbed interface for Info (metadata) and Renditions\n * - Integrated with DataView, Table, and ContextMenu components\n */\n\nimport View from '@core/View.js';\nimport TabView from '@core/views/navigation/TabView.js';\nimport DataView from '@core/views/data/DataView.js';\nimport TableView from '@core/views/table/TableView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport Collection from '@core/Collection.js';\nimport { File, FileForms } from '@core/models/Files.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport LightboxGallery from '@ext/lightbox/LightboxGallery.js';\nimport PDFViewer from '@ext/lightbox/PDFViewer.js';\n\nclass FileView extends View {\n constructor(options = {}) {\n super({\n className: 'file-view',\n ...options\n });\n\n this.model = options.model || new File(options.data || {});\n this.isImage = this.model.get('category') === 'image';\n\n // Prepare renditions for the table by converting the renditions object to an array\n const renditionsData = this.model.get('renditions') || {};\n this.renditionsCollection = new Collection(Object.values(renditionsData));\n\n this.template = `\n <div class=\"file-view-container\">\n <!-- Header -->\n <div class=\"d-flex justify-content-between align-items-center mb-4\">\n <!-- Left Side: Thumbnail & Info -->\n <div class=\"d-flex align-items-center gap-3\">\n <div class=\"file-thumbnail\" style=\"width: 80px; height: 80px;\">\n {{#isImage}}\n <a href=\"{{model.url}}\" target=\"_blank\" title=\"View original file\">\n <img src=\"{{model.renditions.thumbnail.url|default(model.url)}}\" class=\"img-fluid rounded\" style=\"width: 80px; height: 80px; object-fit: cover;\">\n </a>\n {{/isImage}}\n {{^isImage}}\n <div class=\"avatar-placeholder rounded bg-light d-flex align-items-center justify-content-center\" style=\"width: 80px; height: 80px;\">\n <i class=\"bi bi-file-earmark-text text-secondary\" style=\"font-size: 40px;\"></i>\n </div>\n {{/isImage}}\n </div>\n <div>\n <h3 class=\"mb-1\" style=\"word-break: break-all;\">{{model.filename|truncate(40)}}</h3>\n <div class=\"text-muted small\">\n <span><i class=\"bi bi-hdd\"></i> {{model.file_size|filesize}}</span>\n <span class=\"mx-2\">|</span>\n <span>{{model.content_type}}</span>\n </div>\n <div class=\"text-muted small mt-1\">\n Uploaded: {{model.created|datetime}}\n </div>\n </div>\n </div>\n\n <!-- Right Side: Status & Actions -->\n <div class=\"d-flex align-items-center gap-4\">\n <div class=\"text-end\">\n <div class=\"d-flex align-items-center gap-2 justify-content-end\">\n <span class=\"badge {{model.upload_status|badge}}\">{{model.upload_status|capitalize}}</span>\n </div>\n <div class=\"text-muted small mt-1\">\n Public: {{{model.is_public|yesnoicon}}}\n </div>\n </div>\n <div data-container=\"file-context-menu\"></div>\n </div>\n </div>\n\n <!-- Tab Container -->\n <div data-container=\"file-tabs\"></div>\n </div>\n `;\n }\n\n async onInit() {\n // Info Tab using DataView\n this.infoView = new DataView({\n model: this.model,\n className: \"p-3\",\n showEmptyValues: true,\n emptyValueText: '—',\n columns: 2,\n fields: [\n { name: 'id', label: 'ID' },\n { name: 'filename', label: 'Filename' },\n { name: 'storage_filename', label: 'Storage Filename' },\n { name: 'content_type', label: 'Content Type' },\n { name: 'file_size', label: 'File Size', format: 'filesize' },\n { name: 'category', label: 'Category' },\n { name: 'upload_status', label: 'Status', format: 'badge' },\n { name: 'created', label: 'Created', format: 'datetime' },\n { name: 'modified', label: 'Modified', format: 'datetime' },\n { name: 'user.display_name', label: 'Uploaded By' },\n { name: 'file_manager.name', label: 'Storage Backend' },\n { name: 'storage_file_path', label: 'Storage Path' },\n { name: 'url', label: 'Public URL', format: 'url' },\n { name: 'is_public', label: 'Is Public', format: 'boolean' },\n ]\n });\n\n // Renditions Tab using TableView\n this.renditionsView = new TableView({\n collection: this.renditionsCollection,\n columns: [\n { key: 'role', label: 'Role', formatter: 'badge' },\n { key: 'filename', label: 'Filename', formatter: 'truncate(40)' },\n { key: 'file_size', label: 'Size', formatter: 'filesize' },\n { key: 'content_type', label: 'Content Type' },\n {\n key: 'actions',\n label: 'Actions',\n template: `\n <a href=\"{{url}}\" target=\"_blank\" class=\"btn btn-sm btn-outline-primary\" title=\"View\">\n <i class=\"bi bi-eye\"></i>\n </a>\n <a href=\"{{url}}\" download=\"{{filename}}\" class=\"btn btn-sm btn-outline-secondary\" title=\"Download\">\n <i class=\"bi bi-download\"></i>\n </a>\n `\n }\n ]\n });\n\n // Create TabView, only showing Renditions tab if they exist\n const tabs = { 'Info': this.infoView };\n tabs['Renditions'] = this.renditionsView;\n\n this.tabView = new TabView({\n tabs: tabs,\n activeTab: 'Info',\n containerId: 'file-tabs'\n });\n this.addChild(this.tabView);\n\n // Create ContextMenu\n const fileMenu = new ContextMenu({\n containerId: 'file-context-menu',\n className: \"context-menu-view header-menu-absolute\",\n context: this.model,\n config: {\n icon: 'bi-three-dots-vertical',\n items: [\n { label: 'View', action: 'view-file', icon: 'bi-eye' },\n { label: 'Download', action: 'download-file', icon: 'bi bi-download' },\n { label: 'Edit Details', action: 'edit-file', icon: 'bi bi-pencil' },\n { type: 'divider' },\n this.model.get('is_public')\n ? { label: 'Make Private', action: 'make-private', icon: 'bi bi-lock' }\n : { label: 'Make Public', action: 'make-public', icon: 'bi bi-unlock' },\n { type: 'divider' },\n { label: 'Delete File', action: 'delete-file', icon: 'bi bi-trash', danger: true }\n ]\n }\n });\n this.addChild(fileMenu);\n }\n\n async onActionViewFile() {\n const contentType = this.model.get('content_type');\n const fileUrl = this.model.get('url');\n\n if (contentType.startsWith('image/')) {\n const renditions = this.model.get('renditions') || {};\n const images = [\n { src: fileUrl, alt: 'Original' },\n ...Object.values(renditions).map(r => ({ src: r.url, alt: r.role }))\n ];\n LightboxGallery.show(images, { fitToScreen: false });\n } else if (contentType === 'application/pdf') {\n PDFViewer.showDialog(fileUrl, { title: this.model.get('filename') });\n } else {\n window.open(fileUrl, '_blank');\n }\n }\n\n async onActionDownloadFile() {\n const url = this.model.get('url');\n if (url) {\n const a = document.createElement('a');\n a.href = url;\n a.download = this.model.get('filename');\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n }\n }\n\n async onActionEditFile() {\n const resp = await Dialog.showModelForm({\n title: `Edit File - ${this.model.get('filename')}`,\n model: this.model,\n formConfig: FileForms.edit,\n });\n if (resp) {\n this.render();\n }\n }\n\n async onActionMakePublic() {\n await this.model.save({ is_public: true });\n this.render();\n }\n\n async onActionMakePrivate() {\n await this.model.save({ is_public: false });\n this.render();\n }\n\n async onActionDeleteFile() {\n const confirmed = await Dialog.confirm(\n `Are you sure you want to delete the file \"${this.model.get('filename')}\"? This action cannot be undone.`,\n 'Confirm Deletion',\n { confirmClass: 'btn-danger', confirmText: 'Delete' }\n );\n if (confirmed) {\n const resp = await this.model.destroy();\n if (resp.success) {\n this.emit('file:deleted', { model: this.model });\n }\n }\n }\n}\n\nFile.VIEW_CLASS = FileView;\n\nexport default FileView;\n","/**\n * FileTablePage - File management using TablePage component\n * Clean implementation with file drop support\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { File, FileList, FileForms } from '@core/models/Files.js';\nimport FileView from './FileView.js';\nimport applyFileDropMixin from '@core/mixins/FileDropMixin.js';\n\nclass FileTablePage extends TablePage {\n constructor(options = {}) {\n super({\n name: 'admin_files',\n pageName: 'Manage Files',\n router: \"admin/files\",\n Collection: FileList,\n\n // Don't use formCreate - we have custom file upload handling\n formEdit: FileForms.edit,\n itemViewClass: FileView,\n \n // Custom add handler for file uploads\n onAdd: async (event) => {\n await this.handleFileUpload(event);\n },\n\n viewDialogOptions: {\n header: false,\n size: 'xl'\n },\n\n // Column definitions\n columns: [\n {\n key: 'id',\n label: 'ID',\n sortable: true,\n class: 'text-muted'\n },\n {\n key: 'filename',\n label: 'Filename'\n },\n {\n key: 'content_type',\n label: 'Type',\n formatter: \"default('Unknown')\"\n },\n {\n key: 'file_size',\n label: 'Size',\n formatter: \"filesize\"\n },\n {\n key: 'group.name',\n label: 'Group',\n formatter: \"default('No Group')\"\n },\n {\n key: 'upload_status',\n label: 'Status',\n formatter: \"badge\"\n },\n {\n key: 'created',\n label: 'Uploaded',\n formatter: \"epoch|datetime\"\n }\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No files found. Click \"Add File\" to upload your first file.',\n\n // Batch actions\n batchBarLocation: 'top',\n batchActions: [\n { label: \"Download\", icon: \"bi bi-download\", action: \"batch-download\" },\n { label: \"Delete\", icon: \"bi bi-trash\", action: \"batch-delete\" },\n { label: \"Move to Group\", icon: \"bi bi-folder\", action: \"batch-move\" }\n ],\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n },\n ...options,\n });\n\n // Enable file drop support\n this.enableFileDrop({\n acceptedTypes: ['*/*'],\n maxFileSize: 100 * 1024 * 1024, // 100MB\n multiple: false,\n validateOnDrop: true\n });\n }\n\n /**\n * Handle file upload via file picker\n * Opens native file picker and uses same upload flow as drag-drop\n */\n async handleFileUpload(event) {\n if (event) event.preventDefault();\n\n // Create hidden file input element\n const fileInput = document.createElement('input');\n fileInput.type = 'file';\n fileInput.accept = '*/*';\n fileInput.multiple = false;\n fileInput.style.display = 'none';\n\n // Handle file selection\n fileInput.addEventListener('change', async (e) => {\n const file = e.target.files[0];\n \n if (!file) {\n return;\n }\n\n // Validate file size (same as FileDropMixin config)\n const maxSize = 100 * 1024 * 1024; // 100MB\n if (file.size > maxSize) {\n this.showError(`File size (${this._formatFileSize(file.size)}) exceeds maximum (${this._formatFileSize(maxSize)})`);\n return;\n }\n\n // Use the same upload flow as drag-drop\n try {\n const fileModel = new File();\n let extra = {};\n if (this.options.requiresGroup && this.getApp().activeGroup) {\n extra.group = this.getApp().activeGroup.id;\n }\n\n const upload = fileModel.upload({\n file: file,\n name: file.name,\n description: `File uploaded on ${new Date().toLocaleDateString()}`,\n showToast: true,\n onProgress: (progressInfo) => {\n console.log(`Upload progress: ${progressInfo.percentage}%`);\n },\n onComplete: (result) => {\n console.log('Upload completed:', result);\n this.refresh();\n },\n onError: (error) => {\n console.error('Upload failed:', error);\n this.showError('Upload failed: ' + error.message);\n },\n ...extra\n });\n\n await upload;\n } catch (error) {\n console.error('Error starting file upload:', error);\n this.showError('Failed to start file upload: ' + error.message);\n } finally {\n // Clean up file input\n fileInput.remove();\n }\n });\n\n // Trigger file picker\n document.body.appendChild(fileInput);\n fileInput.click();\n }\n\n /**\n * Format file size for display\n * @param {number} bytes - File size in bytes\n * @returns {string} Formatted file size\n * @private\n */\n _formatFileSize(bytes) {\n if (bytes === 0) return '0 Bytes';\n const k = 1024;\n const sizes = ['Bytes', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];\n }\n\n async onFileDrop(files, event, validation) {\n const file = files[0];\n console.log(`File Dropped: ${file.name} (${file.type}) (${file.size} bytes)`);\n\n try {\n // Create new File model instance\n const fileModel = new File();\n let extra = {};\n if (this.options.requiresGroup && this.getApp().activeGroup) {\n extra.group = this.getApp().activeGroup.id;\n }\n\n // Start upload with progress tracking\n const upload = fileModel.upload({\n file: file,\n name: file.name,\n description: `File uploaded via drag & drop on ${new Date().toLocaleDateString()}`,\n showToast: true,\n onProgress: (progressInfo) => {\n console.log(`Upload progress: ${progressInfo.percentage}%`);\n },\n onComplete: (result) => {\n console.log('Upload completed:', result);\n this.refresh();\n },\n onError: (error) => {\n console.error('Upload failed:', error);\n this.showError('Upload failed: ' + error.message);\n },\n ...extra\n });\n\n await upload;\n } catch (error) {\n console.error('Error starting file upload:', error);\n this.showError('Failed to start file upload: ' + error.message);\n }\n }\n}\n\n// Apply file drop mixin\napplyFileDropMixin(FileTablePage);\n\nexport default FileTablePage;\n","/**\n * S3BucketTablePage - S3 Bucket management using TablePage component\n * Clean implementation using TablePage with minimal overrides\n */\n\nimport TablePage from '@core/pages/TablePage.js';\nimport { S3BucketList, S3BucketForms } from '@core/models/AWS.js';\n\nclass S3BucketTablePage extends TablePage {\n constructor(options = {}) {\n super({\n ...options,\n name: 'admin_s3_buckets',\n pageName: 'Manage S3 Buckets',\n router: \"admin/s3-buckets\",\n Collection: S3BucketList,\n \n formCreate: S3BucketForms.create,\n formEdit: S3BucketForms.edit,\n\n // Column definitions\n columns: [\n {\n key: 'id',\n label: 'ID',\n width: '60px',\n sortable: true,\n class: 'text-muted'\n },\n {\n key: 'name',\n label: 'Bucket Name',\n sortable: true\n },\n {\n key: 'created',\n label: 'Created',\n formatter: \"epoch|datetime\"\n }\n ],\n\n // Table features\n selectable: true,\n searchable: true,\n sortable: true,\n filterable: true,\n paginated: true,\n\n // Toolbar\n showRefresh: true,\n showAdd: true,\n showExport: true,\n\n // Empty state\n emptyMessage: 'No S3 buckets found. Click \"Add S3 Bucket\" to create your first bucket.',\n\n // Batch actions\n batchBarLocation: 'top',\n batchActions: [\n { label: \"Delete\", icon: \"bi bi-trash\", action: \"batch-delete\" },\n { label: \"Export\", icon: \"bi bi-download\", action: \"batch-export\" },\n { label: \"Make Public\", icon: \"bi bi-unlock\", action: \"batch-public\" },\n { label: \"Make Private\", icon: \"bi bi-lock\", action: \"batch-private\" },\n { label: \"Empty Bucket\", icon: \"bi bi-bucket\", action: \"batch-empty\" }\n ],\n\n // Table display options\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false\n }\n });\n }\n}\n\nexport default S3BucketTablePage;","/**\n * MOJO Admin Extension - Entry (2.1.0)\n */\n\n// Bundle admin CSS\nimport '@ext/admin/css/admin.css';\n\n// Admin Pages\nexport { default as AdminDashboardPage } from '@ext/admin/account/AdminDashboardPage.js';\nexport { default as UserTablePage } from '@ext/admin/account/users/UserTablePage.js';\nexport { default as MemberTablePage } from '@ext/admin/account/users/MemberTablePage.js';\nexport { default as GroupTablePage } from '@ext/admin/account/groups/GroupTablePage.js';\nexport { default as UserDeviceTablePage } from '@ext/admin/account/devices/UserDeviceTablePage.js';\nexport { default as UserDeviceLocationTablePage } from '@ext/admin/account/devices/UserDeviceLocationTablePage.js';\nexport { default as GeoLocatedIPTablePage } from '@ext/admin/account/devices/GeoLocatedIPTablePage.js';\n\nexport { default as IncidentDashboardPage } from '@ext/admin/incidents/IncidentDashboardPage.js';\nexport { default as IncidentTablePage } from '@ext/admin/incidents/IncidentTablePage.js';\nexport { default as EventTablePage } from '@ext/admin/incidents/EventTablePage.js';\nexport { default as TicketTablePage } from '@ext/admin/incidents/TicketTablePage.js';\nexport { default as RuleSetTablePage } from '@ext/admin/incidents/RuleSetTablePage.js';\n\nexport { default as EmailDomainTablePage } from '@ext/admin/messaging/email/EmailDomainTablePage.js';\nexport { default as EmailMailboxTablePage } from '@ext/admin/messaging/email/EmailMailboxTablePage.js';\nexport { default as EmailTemplateTablePage } from '@ext/admin/messaging/email/EmailTemplateTablePage.js';\nexport { default as SentMessageTablePage } from '@ext/admin/messaging/email/SentMessageTablePage.js';\nexport { default as PhoneNumberTablePage } from '@ext/admin/messaging/sms/PhoneNumberTablePage.js';\nexport { default as SMSTablePage } from '@ext/admin/messaging/sms/SMSTablePage.js';\nexport { default as PushDashboardPage } from '@ext/admin/messaging/push/PushDashboardPage.js';\nexport { default as PushConfigTablePage } from '@ext/admin/messaging/push/PushConfigTablePage.js';\nexport { default as PushTemplateTablePage } from '@ext/admin/messaging/push/PushTemplateTablePage.js';\nexport { default as PushDeliveryTablePage } from '@ext/admin/messaging/push/PushDeliveryTablePage.js';\nexport { default as PushDeviceTablePage } from '@ext/admin/messaging/push/PushDeviceTablePage.js';\n\nexport { default as JobsAdminPage } from '@ext/admin/jobs/JobsAdminPage.js';\nexport { default as TaskManagementPage } from '@ext/admin/jobs/TaskManagementPage.js';\n\nexport { default as LogTablePage } from '@ext/admin/monitoring/LogTablePage.js';\nexport { default as MetricsPermissionsTablePage } from '@ext/admin/monitoring/MetricsPermissionsTablePage.js';\n\nexport { default as FileManagerTablePage } from '@ext/admin/storage/FileManagerTablePage.js';\nexport { default as FileTablePage } from '@ext/admin/storage/FileTablePage.js';\nexport { default as S3BucketTablePage } from '@ext/admin/storage/S3BucketTablePage.js';\n\n// Admin Views\nexport { default as DeviceView } from '@ext/admin/account/devices/DeviceView.js';\nexport { default as GeoIPView } from '@ext/admin/account/devices/GeoIPView.js';\nexport { default as GroupView } from '@ext/admin/account/groups/GroupView.js';\nexport { default as MemberView } from '@ext/admin/account/users/MemberView.js';\nexport { default as UserView } from '@ext/admin/account/users/UserView.js';\n\nexport { default as IncidentView } from '@ext/admin/incidents/IncidentView.js';\nexport { default as EventView } from '@ext/admin/incidents/EventView.js';\nexport { default as TicketView } from '@ext/admin/incidents/TicketView.js';\nexport { default as RuleSetView } from '@ext/admin/incidents/RuleSetView.js';\n\nexport { default as EmailTemplateView } from '@ext/admin/messaging/email/EmailTemplateView.js';\nexport { default as EmailView } from '@ext/admin/messaging/email/EmailView.js';\nexport { default as PhoneNumberView } from '@ext/admin/messaging/sms/PhoneNumberView.js';\nexport { default as PushDeliveryView } from '@ext/admin/messaging/push/PushDeliveryView.js';\nexport { default as PushDeviceView } from '@ext/admin/messaging/push/PushDeviceView.js';\n\nexport { default as JobDetailsView } from '@ext/admin/jobs/JobDetailsView.js';\nexport { default as JobHealthView } from '@ext/admin/jobs/JobHealthView.js';\nexport { default as JobStatsView } from '@ext/admin/jobs/JobStatsView.js';\n\nexport { default as LogView } from '@ext/admin/monitoring/LogView.js';\nexport { default as MetricsPermissionsView } from '@ext/admin/monitoring/MetricsPermissionsView.js';\n\nexport { default as FileView } from '@ext/admin/storage/FileView.js';\n\n// Admin Components\nexport { default as RunnerDetailsView } from '@ext/admin/jobs/RunnerDetailsView.js';\nexport { default as TaskDetailsView } from '@ext/admin/jobs/TaskDetailsView.js';\n// Convenience\nexport { default as WebApp } from '@core/WebApp.js';\n\n// Version info passthrough\nexport {\n VERSION_INFO,\n VERSION,\n VERSION_MAJOR,\n VERSION_MINOR,\n VERSION_REVISION,\n BUILD_TIME\n} from './version.js';\n\n\n\n// Import all admin page classes for the register function\nimport AdminDashboardPageClass from '@ext/admin/account/AdminDashboardPage.js';\nimport UserTablePageClass from '@ext/admin/account/users/UserTablePage.js';\nimport MemberTablePageClass from '@ext/admin/account/users/MemberTablePage.js';\nimport GroupTablePageClass from '@ext/admin/account/groups/GroupTablePage.js';\nimport UserDeviceTablePageClass from '@ext/admin/account/devices/UserDeviceTablePage.js';\nimport UserDeviceLocationTablePageClass from '@ext/admin/account/devices/UserDeviceLocationTablePage.js';\nimport GeoLocatedIPTablePageClass from '@ext/admin/account/devices/GeoLocatedIPTablePage.js';\n\nimport IncidentDashboardPageClass from '@ext/admin/incidents/IncidentDashboardPage.js';\nimport IncidentTablePageClass from '@ext/admin/incidents/IncidentTablePage.js';\nimport EventTablePageClass from '@ext/admin/incidents/EventTablePage.js';\nimport TicketTablePageClass from '@ext/admin/incidents/TicketTablePage.js';\nimport RuleSetTablePageClass from '@ext/admin/incidents/RuleSetTablePage.js';\n\nimport EmailDomainTablePageClass from '@ext/admin/messaging/email/EmailDomainTablePage.js';\nimport EmailMailboxTablePageClass from '@ext/admin/messaging/email/EmailMailboxTablePage.js';\nimport EmailTemplateTablePageClass from '@ext/admin/messaging/email/EmailTemplateTablePage.js';\nimport SentMessageTablePageClass from '@ext/admin/messaging/email/SentMessageTablePage.js';\nimport PhoneNumberTablePageClass from '@ext/admin/messaging/sms/PhoneNumberTablePage.js';\nimport SMSTablePageClass from '@ext/admin/messaging/sms/SMSTablePage.js';\nimport PushDashboardPageClass from '@ext/admin/messaging/push/PushDashboardPage.js';\nimport PushConfigTablePageClass from '@ext/admin/messaging/push/PushConfigTablePage.js';\nimport PushTemplateTablePageClass from '@ext/admin/messaging/push/PushTemplateTablePage.js';\nimport PushDeliveryTablePageClass from '@ext/admin/messaging/push/PushDeliveryTablePage.js';\nimport PushDeviceTablePageClass from '@ext/admin/messaging/push/PushDeviceTablePage.js';\n\nimport JobsAdminPageClass from '@ext/admin/jobs/JobsAdminPage.js';\nimport TaskManagementPageClass from '@ext/admin/jobs/TaskManagementPage.js';\n\nimport LogTablePageClass from '@ext/admin/monitoring/LogTablePage.js';\nimport MetricsPermissionsTablePageClass from '@ext/admin/monitoring/MetricsPermissionsTablePage.js';\n\nimport FileManagerTablePageClass from '@ext/admin/storage/FileManagerTablePage.js';\nimport FileTablePageClass from '@ext/admin/storage/FileTablePage.js';\nimport S3BucketTablePageClass from '@ext/admin/storage/S3BucketTablePage.js';\n\n/**\n * Register all admin pages to a WebApp instance\n * @param {WebApp} app - The WebApp instance to register pages to\n * @returns {void}\n */\nexport function registerSystemPages(app, addToMenu = true) {\n // Register all admin pages with consistent naming\n app.registerPage('system/dashboard', AdminDashboardPageClass, {permissions: [\"view_admin\"]});\n app.registerPage('system/jobs', JobsAdminPageClass, {permissions: [\"view_jobs\", \"manage_jobs\"]});\n app.registerPage('system/users', UserTablePageClass, {permissions: [\"manage_users\"]});\n app.registerPage('system/groups', GroupTablePageClass, {permissions: [\"manage_groups\"]});\n app.registerPage('system/members', MemberTablePageClass, {permissions: [\"manage_members\"]});\n app.registerPage('system/s3buckets', S3BucketTablePageClass, {permissions: [\"manage_aws\"]});\n app.registerPage('system/filemanagers', FileManagerTablePageClass, {permissions: [\"manage_files\"]});\n app.registerPage('system/files', FileTablePageClass, {permissions: [\"manage_files\"]});\n app.registerPage('system/incidents', IncidentTablePageClass, {permissions: [\"view_incidents\"]});\n app.registerPage('system/events', EventTablePageClass, {permissions: [\"view_incidents\"]});\n app.registerPage('system/logs', LogTablePageClass, {permissions: [\"view_logs\"]});\n app.registerPage('system/user/devices', UserDeviceTablePageClass, {permissions: [\"manage_users\"]});\n app.registerPage('system/user/device-locations', UserDeviceLocationTablePageClass, {permissions: [\"manage_users\"]});\n app.registerPage('system/system/geoip', GeoLocatedIPTablePageClass, {permissions: [\"manage_users\"]});\n app.registerPage('system/email/mailboxes', EmailMailboxTablePageClass, {permissions: [\"manage_aws\"]});\n app.registerPage('system/email/domains', EmailDomainTablePageClass, {permissions: [\"manage_aws\"]});\n app.registerPage('system/email/sent', SentMessageTablePageClass, {permissions: [\"manage_aws\"]});\n app.registerPage('system/email/templates', EmailTemplateTablePageClass, {permissions: [\"manage_aws\"]});\n app.registerPage('system/incident-dashboard', IncidentDashboardPageClass, { permissions: [\"view_incidents\"] });\n app.registerPage('system/rulesets', RuleSetTablePageClass, { permissions: [\"manage_incidents\"] });\n app.registerPage('system/tickets', TicketTablePageClass, { permissions: [\"manage_incidents\"] });\n app.registerPage('system/metrics/permissions', MetricsPermissionsTablePageClass, { permissions: [\"manage_metrics\"] });\n app.registerPage('system/push/dashboard', PushDashboardPageClass, { permissions: [\"manage_users\"] });\n app.registerPage('system/push/configs', PushConfigTablePageClass, { permissions: [\"manage_users\"] });\n app.registerPage('system/push/templates', PushTemplateTablePageClass, { permissions: [\"manage_users\"] });\n app.registerPage('system/push/deliveries', PushDeliveryTablePageClass, { permissions: [\"manage_users\"] });\n app.registerPage('system/push/devices', PushDeviceTablePageClass, { permissions: [\"manage_users\"] });\n app.registerPage('system/phonehub/numbers', PhoneNumberTablePageClass, { permissions: [\"manage_users\"] });\n app.registerPage('system/phonehub/sms', SMSTablePageClass, { permissions: [\"manage_users\"] });\n\n // Check if sidebar exists and has an admin menu config\n if (addToMenu && app.sidebar && app.sidebar.getMenuConfig) {\n const adminMenuConfig = app.sidebar.getMenuConfig('system');\n if (adminMenuConfig && adminMenuConfig.items) {\n // Add admin pages to sidebar menu\n const adminMenuItems = [\n {\n text: 'Dashboard',\n route: '?page=system/dashboard',\n icon: 'bi-speedometer2',\n permissions: [\"view_admin\"]\n },\n {\n text: 'Jobs Management',\n route: '?page=system/jobs',\n icon: 'bi-gear-wide-connected',\n permissions: [\"view_jobs\", \"manage_jobs\"]\n },\n {\n text: 'Users',\n route: '?page=system/users',\n icon: 'bi-people',\n permissions: [\"manage_users\"]\n },\n {\n text: 'Groups',\n route: '?page=system/groups',\n icon: 'bi-diagram-3',\n permissions: [\"manage_groups\"]\n },\n {\n text: 'Incidents & Tickets',\n route: null,\n icon: 'bi-shield-exclamation',\n permissions: [\"view_incidents\"],\n children: [\n {\n text: 'Dashboard',\n route: '?page=system/incident-dashboard',\n icon: 'bi-bar-chart-line',\n permissions: [\"view_incidents\"]\n },\n {\n text: 'Incidents',\n route: '?page=system/incidents',\n icon: 'bi-exclamation-triangle',\n permissions: [\"view_incidents\"]\n },\n {\n text: 'Tickets',\n route: '?page=system/tickets',\n icon: 'bi-ticket-detailed',\n permissions: [\"manage_incidents\"]\n },\n {\n text: 'Events',\n route: '?page=system/events',\n icon: 'bi-bell',\n permissions: [\"view_incidents\"]\n },\n {\n text: 'Rule Engine',\n route: '?page=system/rulesets',\n icon: 'bi-gear-wide-connected',\n permissions: [\"manage_incidents\"]\n },\n ]\n },\n {\n text: 'Security',\n route: null,\n icon: 'bi-shield',\n permissions: [\"manage_groups\"],\n children: [\n {\n text: 'Logs',\n route: '?page=system/logs',\n icon: 'bi-journal-text',\n permissions: [\"view_logs\"]\n },\n {\n text: 'User Devices',\n route: '?page=system/user/devices',\n icon: 'bi-phone',\n permissions: [\"manage_users\"]\n },\n {\n text: 'Device Locations',\n route: '?page=system/user/device-locations',\n icon: 'bi-geo-alt',\n permissions: [\"manage_users\"]\n },\n {\n text: 'GeoIP Cache',\n route: '?page=system/system/geoip',\n icon: 'bi-globe',\n permissions: [\"manage_users\"]\n },\n {\n text: 'Metrics Permissions',\n route: '?page=system/metrics/permissions',\n icon: 'bi-bar-chart-line',\n permissions: [\"manage_metrics\"]\n }\n ]\n },\n {\n text: 'Storage',\n route: null,\n icon: 'bi-folder',\n permissions: [\"manage_files\", \"manage_aws\"],\n children: [\n {\n text: 'S3 Buckets',\n route: '?page=system/s3buckets',\n icon: 'bi-bucket',\n permissions: [\"manage_aws\"]\n },\n {\n text: 'Storage Backends',\n route: '?page=system/filemanagers',\n icon: 'bi-hdd-stack',\n permissions: [\"manage_aws\"]\n },\n {\n text: 'Files',\n route: '?page=system/files',\n icon: 'bi-file-earmark',\n permissions: [\"manage_files\"]\n },\n ]\n },\n {\n text: 'Push Notifications',\n route: null,\n icon: 'bi-broadcast',\n permissions: [\"manage_users\"],\n children: [\n { text: 'Dashboard', route: '?page=system/push/dashboard', icon: 'bi-bar-chart-line' },\n { text: 'Configurations', route: '?page=system/push/configs', icon: 'bi-gear' },\n { text: 'Templates', route: '?page=system/push/templates', icon: 'bi-file-earmark-text' },\n { text: 'Deliveries', route: '?page=system/push/deliveries', icon: 'bi-send' },\n { text: 'Devices', route: '?page=system/push/devices', icon: 'bi-phone' },\n ]\n },\n {\n text: 'Email Admin',\n route: null,\n icon: 'bi-envelope',\n permissions: [\"manage_aws\"],\n children: [\n {\n text: 'Domains',\n route: '?page=system/email/domains',\n icon: 'bi-globe',\n permissions: [\"manage_aws\"]\n },\n {\n text: 'Mailboxes',\n route: '?page=system/email/mailboxes',\n icon: 'bi-inbox',\n permissions: [\"manage_aws\"]\n },\n {\n text: 'Sent',\n route: '?page=system/email/sent',\n icon: 'bi-send-check',\n permissions: [\"manage_aws\"]\n },\n {\n text: 'Templates',\n route: '?page=system/email/templates',\n icon: 'bi-file-text',\n permissions: [\"manage_aws\"]\n }\n ]\n },\n {\n text: 'Phone Hub',\n route: null,\n icon: 'bi-telephone',\n permissions: [\"manage_users\"],\n children: [\n {\n text: 'Numbers',\n route: '?page=system/phonehub/numbers',\n icon: 'bi-collection',\n permissions: [\"manage_users\"]\n },\n {\n text: 'SMS',\n route: '?page=system/phonehub/sms',\n icon: 'bi-chat-dots',\n permissions: [\"manage_users\"]\n }\n ]\n }\n ];\n\n // Add items to existing admin menu\n adminMenuConfig.items.unshift(...adminMenuItems);\n }\n }\n\n}\n\nexport { registerSystemPages as registerAdminPages };\n"],"names":["AdminHeaderView","View","constructor","options","super","title","headerActions","label","icon","action","buttonClass","className","this","stats","user_activity_day","total_users","group_activity_day","total_groups","api_calls","apiChange","incidents","incidentsChange","prepareStatsForTemplate","getTemplate","onInit","userActivity","MetricsMiniChartWidget","subtitle","background","textColor","granularity","trendRange","trendOffset","slugs","account","chartType","showTooltip","showXAxis","height","chartWidth","color","fill","fillColor","smoothing","showTrending","showSettings","showDateRange","containerId","addChild","groupActivity","apiActivity","endpoint","incidentActivity","onBeforeRender","loadValues","response","getApp","rest","GET","success","data","status","Object","assign","header","render","error","console","loadStats","AdminDashboardPage","Page","pageTitle","pageSubtitle","lastUpdated","Date","toLocaleString","headerView","apiMetricsChart","MetricsChart","yAxis","beginAtZero","tooltip","y","onActionRefreshAll","event","element","button","currentTarget","querySelector","classList","add","disabled","promises","refresh","filter","Boolean","Promise","allSettled","eventBus","events","emit","page","timestamp","alert","innerHTML","setTimeout","remove","onActionExportMetrics","export","charts","onActionViewAlerts","router","navigateTo","onActionViewSystemStatus","refreshDashboard","getCharts","apiMetrics","getStats","onAfterRender","UserView","model","User","tabView","profileView","groupsView","eventsView","logsView","template","setModel","DataView","showEmptyValues","fields","UserDataView","profile","permsView","FormView","PERMISSION_FIELDS","autosaveModelField","membersCollection","MemberList","params","user","get","size","TableView","collection","hideActivePillNames","columns","key","formatter","sortable","eventsCollection","IncidentEventList","model_name","model_id","width","userDevices","UserDeviceList","devicesView","userLocations","UserDeviceLocationList","locationsView","pushDevices","PushDeviceList","pushDevicesView","logsCollection","LogList","permissions","name","type","startName","endName","fieldName","format","displayFormat","separator","value","activityCollection","uid","activityView","TabView","tabs","Profile","Permissions","Groups","Events","Logs","Activity","Devices","Locations","activeTab","enableResponsive","dropdownStyle","minWidth","userMenu","ContextMenu","context","config","items","onActionEditUser","frmConfig","UserForms","edit","Dialog","showModelForm","id","modelName","formConfig","onActionResetPassword","onActionDeactivateUser","confirm","save","is_active","toast","onActionActivateUser","onActionViewGroup","getAttribute","onActionRemoveFromGroup","onActionViewEvent","onActionViewLog","showTab","tabName","getActiveTab","_onModelChange","create","VIEW_CLASS","UserTablePage","TablePage","pageName","Collection","UserList","viewDialogOptions","defaultQuery","sort","class","visibility","filters","defaultValue","selectable","searchable","filterable","paginated","showRefresh","showAdd","showExport","emptyMessage","contextMenu","tableOptions","striped","bordered","hover","responsive","onActionEditPermissions","preventDefault","item","dataset","_","username","onActionChangePassword","showForm","attributes","autocomplete","readonly","tabindex","style","passwordUsage","required","showToggle","new_password","MOJOUtils","checkPasswordStrength","score","resp","onPasswordChange","onActionSendInvite","send_invite","MemberView","Member","overviewView","Overview","memberMenu","danger","onActionEditMembership","MemberForms","onActionViewUser","onActionRemoveMember","destroy","MemberTablePage","formEdit","itemViewClass","batchBarLocation","batchActions","GroupView","Group","membersView","childrenView","group","addButtonLabel","onAdd","async","onInviteClick","childrenCollection","GroupList","parent","toolbarButtons","Members","Children","groupMenu","onActionEditGroup","GroupForms","detailed","onActionAddMember","onActionAddChildGroup","onActionDeactivateGroup","onActionActivateGroup","onActionViewParent","parentId","groupId","stopPropagation","app","email","showLoading","POST","hideLoading","fetch","onActionAddMultiple","labelField","itemTemplate","valueField","enableSearch","searchPlaceholder","defaultParams","warn","warning","GroupTablePage","formCreate","GroupKindOptions","onActionMakeActive","setActiveGroup","DeviceView","UserDevice","deviceInfo","deviceIcon","getIconForDevice","os","family","toLowerCase","browser","user_agent","device","includes","infoView","locationsCollection","user_device","Info","deviceMenu","userId","onActionDeleteDevice","show","duid","getByDuid","view","dialog","body","buttons","text","dismiss","document","message","UserDeviceTablePage","UserDeviceLocationTablePage","GeoIPView","GeoLocatedIP","hasCoordinates","detailsView","emptyValueText","cols","networkView","riskView","metadataView","source_ip","ip","Location","Network","Metadata","lat","lng","locationStr","join","mapView","MapView","markers","popup","tileLayer","zoom","menuItems","push","geoIPMenu","window","bootstrap","Tooltip","querySelectorAll","forEach","el","existing","getInstance","dispose","onActionEditLocation","EDIT_LOCATION_FORM","onActionEditSecurity","EDIT_SECURITY_FORM","onActionEditNetwork","EDIT_NETWORK_FORM","onActionRefreshGeoip","info","onActionThreatAnalysis","threat_analysis","onActionViewOnMap","url","open","onActionDeleteGeoip","confirmClass","confirmText","lookup","GeoLocatedIPTablePage","GeoLocatedIPList","itemView","clickAction","tableViewOptions","evt","onLookup","tableView","_onRowView","MapLibreView","center","pitch","bearing","mapStyle","showNavigationControl","autoFitBounds","map","mapMarkers","pendingMarkers","lineSources","pendingLineData","Map","loadMapLibre","initializeMap","maplibregl","link","createElement","rel","href","head","appendChild","resolve","reject","script","src","onload","onerror","getMapStyle","styles","streets","dark","version","sources","osm","tiles","tileSize","attribution","layers","source","glyphs","light","satellite","terrain","mapElement","mapCenter","length","container","addControl","NavigationControl","on","initialMarkers","updateMarkers","addLineSource","clear","addMarkers","Array","isArray","markerData","markerSize","Number","borderRadius","backgroundColor","border","boxShadow","cursor","display","alignItems","justifyContent","marker","Marker","setLngLat","addTo","mapPopup","Popup","offset","setHTML","setPopup","fitBounds","bounds","LngLatBounds","extend","padding","maxZoom","newMarkers","clearMarkers","flyTo","paint","layout","sourceId","getLayer","removeLayer","getSource","removeSource","addSource","addLayer","set","updateLineSource","setData","entries","setPaintProperty","setLayoutProperty","setView","setZoom","setPitch","setBearing","onBeforeDestroy","COUNTRY_CENTROIDS","AD","AE","AF","AG","AI","AL","AM","AO","AQ","AR","AS","AT","AU","AW","AZ","BA","BB","BD","BE","BF","BG","BH","BI","BJ","BL","BM","BN","BO","BQ","BR","BS","BT","BV","BW","BY","BZ","CA","CC","CD","CF","CG","CH","CI","CK","CL","CM","CN","CO","CR","CU","CV","CW","CX","CY","CZ","DE","DJ","DK","DM","DO","DZ","EC","EE","EG","ER","ES","ET","FI","FJ","FK","FM","FO","FR","GA","GB","GD","GE","GF","GG","GH","GI","GL","GM","GN","GP","GQ","GR","GS","GT","GU","GW","GY","HM","HN","HR","HT","HU","ID","IE","IL","IM","IN","IO","IQ","IR","IS","IT","JE","JM","JO","JP","KE","KG","KH","KI","KM","KN","KP","KR","KW","KY","KZ","LA","LB","LC","LI","LK","LR","LS","LT","LU","LV","LY","MA","MC","MD","ME","MF","MG","MH","MK","ML","MM","MN","MP","MQ","MR","MS","MT","MU","MV","MW","MX","MY","MZ","NA","NC","NE","NF","NG","NI","NL","NO","NP","NR","NU","NZ","OM","PA","PE","PF","PG","PH","PK","PL","PM","PN","PR","PS","PT","PW","PY","QA","RE","RO","RS","RU","RW","SA","SB","SC","SD","SE","SG","SH","SI","SJ","SK","SL","SM","SN","SO","SR","SS","ST","SV","SX","SY","SZ","TC","TD","TF","TG","TH","TJ","TK","TL","TM","TN","TO","TR","TT","TV","TZ","UA","UG","UM","US","UY","UZ","VA","VC","VE","VG","VI","VN","VU","WF","WS","YE","YT","ZA","ZM","ZW","MetricsCountryMapView","mapHeight","category","maxCountries","metricLabel","mapOptions","showRoutes","routeOrigin","_refreshing","statusEl","setStatus","metrics","fetchMetrics","applyMetrics","Error","with_labels","slugsArray","labels","iso","series","total","reduce","sum","centroid","toUpperCase","code","values","renderLegend","a","b","topEntries","slice","maxValue","entry","intensity","Math","round","getMarkerColor","renderRoutes","end","mix","idx","legendEl","rows","percent","toFixed","origin","geojson","features","geometry","coordinates","properties","textContent","IncidentDashboardHeader","IncidentStats","IncidentDashboardPage","eventsWidget","defaultDateRange","valueFormat","settingsKey","incidentsWidget","eventsByCountryChart","showMetricsFilter","maxDatasets","colors","incidentsByCountryChart","eventsCountryMap","myTicketsCollection","TicketList","myTicketsTable","newIncidentsCollection","IncidentList","highPriorityIncidentsTable","refreshTasks","then","StackTraceView","stackTrace","formattedStackTrace","formatStackTrace","lines","JSON","stringify","split","html","line","index","trim","escapeHtml","match","funcName","filePath","lineNum","colNum","startsWith","updateStackTrace","newStackTrace","IncidentHistoryAdapter","incidentId","IncidentHistoryList","incident","models","transform","author","avatarUrl","content","attachments","addNote","history","IncidentHistory","note","kind","IncidentView","Incident","incidentIcon","getIconForIncident","state","s","actions","adapter","historyView","ChatView","metadata","stack_trace","stackTraceView","keys","incidentMenu","onActionEditIncident","IncidentForms","onActionResolveIncident","onActionDeleteIncident","IncidentTablePage","onActionBatchResolve","selected","getSelectedItems","all","onActionBatchOpen","onActionBatchPause","onActionBatchIgnore","onActionBatchMerge","_event","_element","result","parentModel","find","merge","mergeIds","EventView","IncidentEvent","eventIcon","getIconForEvent","level","eventMenu","onActionViewIncident","onActionViewModel","onActionDeleteEvent","EventTablePage","IncidentEventForms","category__not","TicketNoteAdapter","ticketId","TicketNoteList","TicketNote","media","files","TicketView","Ticket","chatView","theme","currentUserId","getCurrentUserId","inputPlaceholder","inputButtonText","ticketMenu","currentUser","onActionEditTicket","TicketForms","onActionChangeStatus","currentStatus","replace","onActionSetPriority","currentPriority","p","priority","onActionAssignUser","onActionCloseTicket","TicketTablePage","editable","editableOptions","placeHolder","TicketCategories","RuleSetView","RuleSet","matchByValue","matchByOption","MatchByOptions","opt","matchByLabel","String","bundleByValue","bundleByOption","BundleByOptions","bundleByLabel","configView","rulesCollection","RuleList","rulesView","divider","addFormDefaults","Configuration","Rules","onActionEditRuleset","EDIT_FORM","onActionDisableRuleset","newStatus","showToast","onActionDeleteRuleset","closest","bsModal","Modal","hide","RuleSetTablePage","RuleSetList","v","pageSizes","defaultPageSize","emptyIcon","EmailDomainTablePage","EmailDomainList","EmailDomainForms","onActionEditAwsCreds","credentials","onActionOnboard","EmailDomain","formData","onboard","err","showError","onActionAudit","audit","showDialog","onActionReconcile","reconcile","EmailMailboxTablePage","MailboxList","MailboxForms","onActionSendEmail","from_email","Mailbox","sendEmail","msg","details","onActionSendTemplateEmail","EmailTemplateView","EmailTemplate","hasHtml","hasText","hasMetadata","EmailTemplateTablePage","EmailTemplateList","EmailTemplateForms","scrollable","formDialogConfig","EmailView","SentMessage","hasContext","SentMessageTablePage","SentMessageList","PhoneNumber","Model","normalize","phoneNumber","countryCode","payload","phone_number","country_code","PhoneNumberList","ModelClass","SMS","send","SMSList","PhoneNumberView","carrierView","addressView","Carrier","Address","ctxMenu","onActionRefreshLookup","number","force_refresh","e","onActionDeletePhone","MODEL_CLASS","PhoneNumberTablePage","addButtonIcon","SMSView","messageView","deliveryView","Message","Delivery","onActionRefreshSms","onActionDeleteSms","SMSTablePage","PushDashboardPage","deliveriesChart","statusChart","PieChart","recentDeliveries","PushDeliveryList","_sort","_limit","failedDeliveries","PushConfigTablePage","PushConfigList","PushConfigForms","PushTemplateTablePage","PushTemplateList","PushTemplateForms","PushDeliveryView","PushDeliveryTablePage","PushDeviceView","dataView","PushDeviceTablePage","JobStatsView","pending","running","completed","failed","scheduled","isMounted","totals","JobHealthView","health","runners","active","channels","loadHealth","overall_status","runners_active","scheduler","healthStatusClass","getHealthStatusClass","setupChannelDisplay","setupSchedulerDisplay","channelsArray","channel","channelStatus","totalJobs","queued_count","inflight_count","statusBadgeClass","getChannelBadgeClass","statusIcon","getChannelIcon","queued","inflight","schedulerStatusText","schedulerStatusClass","schedulerIcon","healthy","critical","unknown","onActionRefreshHealth","onActionSystemSettings","showAlert","JobDetailsView","Job","payloadView","autoRefreshInterval","JobEventList","job","JobLogList","Payload","contextMenuItems","canCancel","canRetry","jobMenu","graph","prepareJobData","getStatusBadgeClass","getStatusIcon","formattedDuration","getFormattedDuration","loadJobDetails","getDetailedStatus","onActionRefreshJob","onActionCancelJob","cancel","onActionRetryJob","retryData","JobForms","retry","delay","newJobId","startAutoRefresh","clearInterval","isActive","setInterval","stopAutoRefresh","onDestroy","onHide","JobMetricsModalView","chart","JobsAdminPage","refreshRate","jobStats","JobsEngineStats","jobStatsView","jobHealthView","jobsPublishedChart","jobsFailedChart","runningJobsTable","JobList","collectionParams","hideActivePills","row","pendingJobsTable","run_at__isnull","failedJobsTable","scheduledJobsTable","runnersTable","JobRunnerList","isAlive","heartbeatTime","diffMs","diffSeconds","floor","refreshData","tasks","updateHeaderTimestamp","timestampElement","rateLabel","refreshRateSeconds","refreshRateLabel","onActionSetRefreshRate","rate","parseInt","rateText","onActionExportData","onActionRunSimpleJob","showConfirm","executeJobAction","test","onActionRunTestJobs","tests","onActionClearStuck","channelOptions","ch","help","clearStuck","count","onActionClearChannel","clearChannel","onActionPurgeJobs","purgeJobs","days_old","onActionCleanupConsumers","cleanConsumers","onActionRunnerBroadcast","JobRunnerForms","broadcast","broadcastResult","JobRunner","command","timeout","actionFn","successMessage","onEnter","onExit","getHealth","onActionOpenJobMetricsModal","modalView","onActionViewAllJobs","TaskDetailsView","task","logs","prepareTaskData","loadTaskLogs","loadTaskMetrics","dataFormatted","expires","expiresClass","now","cancelled","expired","getLogLevelClass","debug","log","levelClass","setTask","onActionRefreshLogs","showSuccess","originalTask","newTask","exportData","exported_at","toISOString","exported_by","blob","Blob","URL","createObjectURL","download","click","removeChild","revokeObjectURL","RunnerDetailsView","runner","currentTasks","prepareRunnerData","loadRunnerMetrics","loadCurrentTasks","loadRunnerLogs","ping_age","pingAgeText","formatDuration","pingAgeClass","started_at","uptimeSeconds","uptimeText","formatUptime","hostname","activeWorkers","tasksCompleted","tasksCompletedToday","avgExecutionTime","errorCount","errorRate","queueBacklog","workerUtilization","max_workers","utilizationClass","getUtilizationClass","performanceTrend","getPerformanceTrend","cpu","getResourceStatus","cpuUsage","memory","getMemoryStatus","memoryUsed","memoryTotal","getDefaultMetrics","duration","started","percentage","used","utilization","avgTime","usage","cssClass","seconds","days","hours","minutes","setRunner","onActionRefreshMetrics","onActionRefreshTasks","onActionFilterLogs","btn","onActionViewTask","taskId","onActionCancelTask","confirmMessage","DELETE","TaskStatsView","errors","TaskRunnersView","loadRunners","pingAge","statusBadge","formatPingAge","onActionRefreshRunners","onActionViewRunnerDetails","runnerId","r","onActionPauseRunner","onActionRestartRunner","onActionRemoveRunner","TaskChartsView","taskFlowChart","taskErrorsChart","PendingTasksTable","RunningTasksTable","CompletedTasksTable","ErrorTasksTable","TaskManagementPage","taskStatsView","taskRunnersView","taskChartsView","taskTablesView","Pending","Running","Completed","Errors","activeTable","getTab","onActionExportTasks","exportToCSV","onActionManageChannels","channelList","onActionViewSystemLogs","logPreview","getRunners","taskFlow","taskErrors","LogView","Log","logIcon","getIconForLog","lvl","logContent","formattedLog","parsed","parse","logContentView","onActionCopyLog","navigator","clipboard","writeText","logMenu","onActionViewIp","onActionViewDevice","onActionDeleteLog","LogTablePage","MetricsPermissionsView","MetricsPermission","onActionEdit","MetricsForms","onActionDelete","MetricsPermissionsTablePage","MetricsPermissionList","FileManagerTablePage","FileManagerList","FileManagerForms","onActionEditOwners","owners","onActionCheckCors","check_cors","showData","onActionTestConnection","test_connection","onActionEditCredentials","onActionClone","clone","FileView","File","isImage","renditionsData","renditionsCollection","renditionsView","fileMenu","onActionViewFile","contentType","fileUrl","renditions","images","alt","role","LightboxGallery","fitToScreen","PDFViewer","onActionDownloadFile","onActionEditFile","FileForms","onActionMakePublic","is_public","onActionMakePrivate","onActionDeleteFile","FileTablePage","FileList","handleFileUpload","enableFileDrop","acceptedTypes","maxFileSize","multiple","validateOnDrop","fileInput","accept","addEventListener","file","target","maxSize","_formatFileSize","fileModel","extra","requiresGroup","activeGroup","upload","description","toLocaleDateString","onProgress","progressInfo","onComplete","onError","bytes","i","parseFloat","pow","onFileDrop","validation","applyFileDropMixin","S3BucketTablePage","S3BucketList","S3BucketForms","registerSystemPages","addToMenu","registerPage","AdminDashboardPageClass","JobsAdminPageClass","UserTablePageClass","GroupTablePageClass","MemberTablePageClass","S3BucketTablePageClass","FileManagerTablePageClass","FileTablePageClass","IncidentTablePageClass","EventTablePageClass","LogTablePageClass","UserDeviceTablePageClass","UserDeviceLocationTablePageClass","GeoLocatedIPTablePageClass","EmailMailboxTablePageClass","EmailDomainTablePageClass","SentMessageTablePageClass","EmailTemplateTablePageClass","IncidentDashboardPageClass","RuleSetTablePageClass","TicketTablePageClass","MetricsPermissionsTablePageClass","PushDashboardPageClass","PushConfigTablePageClass","PushTemplateTablePageClass","PushDeliveryTablePageClass","PushDeviceTablePageClass","PhoneNumberTablePageClass","SMSTablePageClass","sidebar","getMenuConfig","adminMenuConfig","adminMenuItems","route","children","unshift"],"mappings":"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"}