web-mojo 2.2.68 → 2.2.69

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 (96) hide show
  1. package/CHANGELOG.md +25 -9
  2. package/dist/admin.cjs.js +1 -1
  3. package/dist/admin.cjs.js.map +1 -1
  4. package/dist/admin.es.js +1 -1
  5. package/dist/admin.es.js.map +1 -1
  6. package/dist/auth.cjs.js +1 -1
  7. package/dist/auth.es.js +1 -1
  8. package/dist/charts.cjs.js +1 -1
  9. package/dist/charts.es.js +1 -1
  10. package/dist/chunks/ChatView-CZ3Key2k.js +2 -0
  11. package/dist/chunks/ChatView-CZ3Key2k.js.map +1 -0
  12. package/dist/chunks/ChatView-Dw-iVmht.js +2 -0
  13. package/dist/chunks/ChatView-Dw-iVmht.js.map +1 -0
  14. package/dist/chunks/{Dialog-DW7PHzUc.js → Dialog-Dhqtd9Yz.js} +2 -2
  15. package/dist/chunks/{Dialog-DW7PHzUc.js.map → Dialog-Dhqtd9Yz.js.map} +1 -1
  16. package/dist/chunks/{Dialog-jfBsXy5X.js → Dialog-t_9l2Mou.js} +2 -2
  17. package/dist/chunks/{Dialog-jfBsXy5X.js.map → Dialog-t_9l2Mou.js.map} +1 -1
  18. package/dist/chunks/Files-6eRT5k3r.js +2 -0
  19. package/dist/chunks/{Files-C-ChBvr5.js.map → Files-6eRT5k3r.js.map} +1 -1
  20. package/dist/chunks/Files-Dh_5PFBn.js +2 -0
  21. package/dist/chunks/{Files-DNbHDy43.js.map → Files-Dh_5PFBn.js.map} +1 -1
  22. package/dist/chunks/{FormView-EoB_ZdIB.js → FormView-B1CXO2t8.js} +2 -2
  23. package/dist/chunks/{FormView-EoB_ZdIB.js.map → FormView-B1CXO2t8.js.map} +1 -1
  24. package/dist/chunks/{FormView-Q_lFA0nr.js → FormView-BRHAIawp.js} +2 -2
  25. package/dist/chunks/{FormView-Q_lFA0nr.js.map → FormView-BRHAIawp.js.map} +1 -1
  26. package/dist/chunks/{MetricsMiniChartWidget-lzq4lSTF.js → MetricsMiniChartWidget-D1w608Jy.js} +2 -2
  27. package/dist/chunks/{MetricsMiniChartWidget-lzq4lSTF.js.map → MetricsMiniChartWidget-D1w608Jy.js.map} +1 -1
  28. package/dist/chunks/{MetricsMiniChartWidget-ukn-NRMR.js → MetricsMiniChartWidget-Dg1e6EQJ.js} +2 -2
  29. package/dist/chunks/{MetricsMiniChartWidget-ukn-NRMR.js.map → MetricsMiniChartWidget-Dg1e6EQJ.js.map} +1 -1
  30. package/dist/chunks/{PDFViewer-iOqYpg-6.js → PDFViewer-CDeV9OBs.js} +2 -2
  31. package/dist/chunks/{PDFViewer-iOqYpg-6.js.map → PDFViewer-CDeV9OBs.js.map} +1 -1
  32. package/dist/chunks/{PDFViewer-sFoyopz3.js → PDFViewer-D_3V8QJe.js} +2 -2
  33. package/dist/chunks/{PDFViewer-sFoyopz3.js.map → PDFViewer-D_3V8QJe.js.map} +1 -1
  34. package/dist/chunks/TableView-CI_7a-kD.js +2 -0
  35. package/dist/chunks/TableView-CI_7a-kD.js.map +1 -0
  36. package/dist/chunks/TableView-CWk5k4LQ.js +2 -0
  37. package/dist/chunks/TableView-CWk5k4LQ.js.map +1 -0
  38. package/dist/chunks/ToastService-C2tTooFn.js +3 -0
  39. package/dist/chunks/ToastService-C2tTooFn.js.map +1 -0
  40. package/dist/chunks/ToastService-nUaGVpSl.js +3 -0
  41. package/dist/chunks/ToastService-nUaGVpSl.js.map +1 -0
  42. package/dist/chunks/{TokenManager-ChNOca0K.js → TokenManager-ien2XzwO.js} +2 -2
  43. package/dist/chunks/{TokenManager-ChNOca0K.js.map → TokenManager-ien2XzwO.js.map} +1 -1
  44. package/dist/chunks/{TokenManager-DKzxBt6g.js → TokenManager-sZgt--C9.js} +2 -2
  45. package/dist/chunks/{TokenManager-DKzxBt6g.js.map → TokenManager-sZgt--C9.js.map} +1 -1
  46. package/dist/chunks/User-BL9M_PWB.js +2 -0
  47. package/dist/chunks/User-BL9M_PWB.js.map +1 -0
  48. package/dist/chunks/{User-BnlvMG5J.js → User-DqHG5Gr1.js} +2 -3
  49. package/dist/chunks/User-DqHG5Gr1.js.map +1 -0
  50. package/dist/chunks/UserProfileView-DnVMHcLH.js +2 -0
  51. package/dist/chunks/UserProfileView-DnVMHcLH.js.map +1 -0
  52. package/dist/chunks/UserProfileView-kupeq2rN.js +2 -0
  53. package/dist/chunks/UserProfileView-kupeq2rN.js.map +1 -0
  54. package/dist/chunks/{WebApp-Bsic6FPo.js → WebApp-Bti0Gqqo.js} +2 -2
  55. package/dist/chunks/{WebApp-Bsic6FPo.js.map → WebApp-Bti0Gqqo.js.map} +1 -1
  56. package/dist/chunks/{WebApp-B0m6JCjO.js → WebApp-CcVF73yg.js} +2 -2
  57. package/dist/chunks/{WebApp-B0m6JCjO.js.map → WebApp-CcVF73yg.js.map} +1 -1
  58. package/dist/chunks/index-Aq9ke4vg.js +2 -0
  59. package/dist/chunks/index-Aq9ke4vg.js.map +1 -0
  60. package/dist/chunks/index-Da9sT-tE.js +2 -0
  61. package/dist/chunks/index-Da9sT-tE.js.map +1 -0
  62. package/dist/chunks/{version-BmVUtM_7.js → version-D8JjsPW0.js} +2 -2
  63. package/dist/chunks/{version-BmVUtM_7.js.map → version-D8JjsPW0.js.map} +1 -1
  64. package/dist/chunks/{version-i7K_82Qy.js → version-XmirKYWA.js} +2 -2
  65. package/dist/chunks/{version-i7K_82Qy.js.map → version-XmirKYWA.js.map} +1 -1
  66. package/dist/css/web-mojo.css +1 -1
  67. package/dist/docit.cjs.js +1 -1
  68. package/dist/docit.cjs.js.map +1 -1
  69. package/dist/docit.es.js +1 -1
  70. package/dist/docit.es.js.map +1 -1
  71. package/dist/index.cjs.js +1 -1
  72. package/dist/index.cjs.js.map +1 -1
  73. package/dist/index.es.js +1 -1
  74. package/dist/index.es.js.map +1 -1
  75. package/dist/lightbox.cjs.js +1 -1
  76. package/dist/lightbox.es.js +1 -1
  77. package/dist/map.cjs.js +1 -1
  78. package/dist/map.es.js +1 -1
  79. package/dist/user-profile.cjs.js +2 -0
  80. package/dist/user-profile.cjs.js.map +1 -0
  81. package/dist/user-profile.es.js +2 -0
  82. package/dist/user-profile.es.js.map +1 -0
  83. package/dist/web-mojo.lite.iife.js +9 -6
  84. package/dist/web-mojo.lite.iife.js.map +1 -1
  85. package/dist/web-mojo.lite.iife.min.js +11 -11
  86. package/dist/web-mojo.lite.iife.min.js.map +1 -1
  87. package/package.json +5 -1
  88. package/dist/chunks/ChatView-Cfe0ZGvr.js +0 -2
  89. package/dist/chunks/ChatView-Cfe0ZGvr.js.map +0 -1
  90. package/dist/chunks/ChatView-DuQVFrCY.js +0 -2
  91. package/dist/chunks/ChatView-DuQVFrCY.js.map +0 -1
  92. package/dist/chunks/Files-C-ChBvr5.js +0 -2
  93. package/dist/chunks/Files-DNbHDy43.js +0 -2
  94. package/dist/chunks/User-BnlvMG5J.js.map +0 -1
  95. package/dist/chunks/User-DSqcOwPL.js +0 -3
  96. package/dist/chunks/User-DSqcOwPL.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatView-Dw-iVmht.js","sources":["../../src/core/models/AWS.js","../../src/core/models/Email.js","../../src/core/models/Incident.js","../../src/core/models/Job.js","../../src/core/models/JobRunner.js","../../src/core/models/Metrics.js","../../src/core/models/Push.js","../../src/core/models/System.js","../../src/core/models/Tickets.js","../../src/core/pages/TablePage.js","../../src/core/views/navigation/TabView.js","../../src/core/views/data/FilePreviewView.js","../../src/core/views/chat/ChatMessageView.js","../../src/core/views/chat/ChatInputView.js","../../src/core/views/chat/ChatView.js"],"sourcesContent":["\nimport Collection from '@core/Collection.js';\nimport Model from '@core/Model.js';\n\n/* =========================\n * Model\n * ========================= */\nclass S3Bucket extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/aws/s3/bucket',\n });\n }\n}\n\n/* =========================\n * Collection\n * ========================= */\nclass S3BucketList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: S3Bucket,\n endpoint: '/api/aws/s3/bucket',\n size: 10,\n ...options,\n });\n }\n}\n\n/* =========================\n * Forms\n * ========================= */\nconst S3BucketForms = {\n create: {\n title: 'Add S3 Bucket',\n fields: [\n {\n name: 'bucket_name',\n type: 'text',\n label: 'Name',\n placeholder: 'bucket name',\n help: 'Enter a universally unique name for the bucket',\n required: true,\n cols: 12,\n },\n {\n name: 'is_public',\n type: 'switch',\n label: 'Is Public',\n cols: 12,\n },\n ],\n },\n\n // Provide an edit form even though legacy only had ADD_FORM\n edit: {\n title: 'Edit S3 Bucket',\n fields: [\n {\n name: 'bucket_name',\n type: 'text',\n label: 'Name',\n placeholder: 'bucket name',\n help: 'Enter a universally unique name for the bucket',\n required: true,\n cols: 12,\n },\n {\n name: 'is_public',\n type: 'switch',\n label: 'Is Public',\n cols: 12,\n },\n ],\n },\n};\n\nexport { S3Bucket, S3BucketList, S3BucketForms };\n","import Collection from '@core/Collection.js';\nimport Model from '@core/Model.js';\nimport rest from '@core/Rest.js';\n\n\n/**\n * EmailDomain - SES/SNS/S3-backed email domain model\n * Maps to REST endpoints under /api/aws/email/domain\n *\n * Key operations:\n * - Create/Update/Delete domains\n * - Onboard: DNS records + SNS + optional receiving\n * - Audit: Drift report (verification/DKIM, topics, receipt rules)\n * - Reconcile: Safe, idempotent fixes (no DNS writes)\n *\n * Notes:\n * - Management endpoints require \"manage_aws\" permission server-side.\n * - Error handling follows the MOJO Rest response contract.\n */\nclass EmailDomain extends Model {\n constructor(data = {}, options = {}) {\n super(data, {\n endpoint: '/api/aws/email/domain',\n ...options\n });\n }\n\n /**\n * Onboard the domain (DNS/SNS/receiving orchestration).\n * POST /api/aws/email/domain/<id>/onboard\n *\n * @param {object} data\n * receiving_enabled?: boolean\n * s3_inbound_bucket?: string\n * s3_inbound_prefix?: string\n * ensure_mail_from?: boolean\n * mail_from_subdomain?: string\n * dns_mode?: \"manual\" | \"godaddy\"\n * godaddy_key?: string\n * godaddy_secret?: string\n * endpoints?: { bounce, complaint, delivery, inbound }\n * @param {object} options - Optional { params?: object }\n * @returns {Promise<object>} REST response\n */\n async onboard(data = {}, options = {}) {\n if (!this.id) {\n await this.showError('Cannot onboard domain without ID');\n return {\n success: false,\n status: 400,\n error: 'Missing domain id'\n };\n }\n\n try {\n const url = `${this.buildUrl(this.id)}/onboard`;\n const response = await this.rest.POST(url, data, options.params);\n\n // No guaranteed shape to merge; response contains DNS records, topics, notes, etc.\n // If backend returns updated domain fields, you can call this.set(response.data.data).\n return response;\n } catch (err) {\n return {\n success: false,\n status: err?.status || 500,\n error: err?.message || 'Failed to onboard domain'\n };\n }\n }\n\n /**\n * Audit domain configuration for drift.\n * GET or POST /api/aws/email/domain/<id>/audit\n *\n * @param {object} options\n * method?: 'GET'|'POST' (default 'GET')\n * data?: object (when method is POST)\n * params?: object (query params for GET)\n * @returns {Promise<object>} REST response\n */\n async audit(options = {}) {\n if (!this.id) {\n await this.showError('Cannot audit domain without ID');\n return {\n success: false,\n status: 400,\n error: 'Missing domain id'\n };\n }\n\n const method = (options.method || 'GET').toUpperCase();\n const url = `${this.buildUrl(this.id)}/audit`;\n\n try {\n if (method === 'POST') {\n return await this.rest.POST(url, options.data || {}, options.params);\n }\n return await this.rest.GET(url, options.params);\n } catch (err) {\n return {\n success: false,\n status: err?.status || 500,\n error: err?.message || 'Failed to audit domain'\n };\n }\n }\n\n /**\n * Reconcile domain configuration (safe fixes; no DNS writes).\n * POST /api/aws/email/domain/<id>/reconcile\n *\n * @param {object} data - Optional payload (usually none required)\n * @param {object} options - Optional { params?: object }\n * @returns {Promise<object>} REST response\n */\n async reconcile(data = {}, options = {}) {\n if (!this.id) {\n await this.showError('Cannot reconcile domain without ID');\n return {\n success: false,\n status: 400,\n error: 'Missing domain id'\n };\n }\n\n try {\n const url = `${this.buildUrl(this.id)}/reconcile`;\n return await this.rest.POST(url, data, options.params);\n } catch (err) {\n return {\n success: false,\n status: err?.status || 500,\n error: err?.message || 'Failed to reconcile domain'\n };\n }\n }\n\n /**\n * Convenience: Onboard by id without creating a separate instance.\n * @param {string|number} id\n * @param {object} data\n * @param {object} options\n */\n static async onboardById(id, data = {}, options = {}) {\n const model = new EmailDomain({ id }, options);\n return await model.onboard(data, options);\n }\n\n /**\n * Convenience: Audit by id without creating a separate instance.\n * @param {string|number} id\n * @param {object} options - see audit()\n */\n static async auditById(id, options = {}) {\n const model = new EmailDomain({ id }, options);\n return await model.audit(options);\n }\n\n /**\n * Convenience: Reconcile by id without creating a separate instance.\n * @param {string|number} id\n * @param {object} data\n * @param {object} options\n */\n static async reconcileById(id, data = {}, options = {}) {\n const model = new EmailDomain({ id }, options);\n return await model.reconcile(data, options);\n }\n}\n\n/**\n * EmailDomainList - Collection of EmailDomain\n * Supports standard MOJO list/search/sort/pagination patterns\n */\nclass EmailDomainList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: EmailDomain,\n endpoint: '/api/aws/email/domain',\n size: 10,\n ...options\n });\n }\n}\n\n/**\n * Forms configuration for EmailDomain (for TablePage/Dialog integration)\n * NOTE: Conditional requirements (e.g., s3_inbound_bucket when receiving_enabled)\n * should be validated server-side; help text guides the admin.\n */\nconst EmailDomainForms = {\n create: {\n title: 'Add Email Domain',\n fields: [\n {\n name: 'name',\n type: 'text',\n label: 'Domain Name',\n placeholder: 'example.com',\n required: true,\n columns: 12,\n help: 'Enter the root domain to verify with SES (no protocol).'\n },\n {\n name: 'region',\n type: 'text',\n label: 'AWS Region (optional)',\n placeholder: 'us-east-1',\n columns: 12,\n help: 'Optional. Defaults to project AWS_REGION if omitted.'\n },\n {\n name: 'aws_key',\n type: 'text',\n label: 'AWS Key (optional)',\n placeholder: 'enter your AWS Key with SES permissions',\n columns: 12,\n help: 'Optional, AWS Key with SES permissions'\n },\n {\n name: 'aws_secret',\n type: 'text',\n label: 'AWS Secret (optional)',\n placeholder: 'enter your AWS Secret with SES permissions',\n columns: 12,\n help: 'Optional, AWS Secret with SES permissions'\n },\n {\n name: 'receiving_enabled',\n type: 'switch',\n label: 'Enable Inbound Receiving',\n columns: 12,\n help: 'Catch-all SES receipt rule to S3 + SNS; routing is done in-app.'\n }\n ]\n },\n\n edit: {\n title: 'Edit Email Domain',\n fields: [\n {\n name: 'name',\n type: 'text',\n label: 'Domain Name',\n placeholder: 'example.com',\n required: true,\n columns: 12,\n readonly: true,\n help: 'Domain name cannot be changed after creation.'\n },\n {\n name: 'region',\n type: 'text',\n label: 'AWS Region',\n placeholder: 'us-east-1',\n columns: 12\n },\n {\n name: 'receiving_enabled',\n type: 'switch',\n label: 'Enable Inbound Receiving',\n columns: 12\n },\n {\n name: 's3_inbound_bucket',\n type: 'text',\n label: 'Inbound S3 Bucket',\n placeholder: 'my-inbound-bucket',\n columns: 12\n },\n {\n name: 's3_inbound_prefix',\n type: 'text',\n label: 'Inbound S3 Prefix',\n placeholder: 'inbound/example.com/',\n columns: 12\n },\n {\n name: 'dns_mode',\n type: 'select',\n label: 'DNS Mode',\n options: [\n { value: 'manual', text: 'Manual (show records)' },\n { value: 'godaddy', text: 'GoDaddy (apply via API)' }\n ],\n columns: 12\n }\n ],\n },\n\n credentials: {\n fields: [\n {\n name: 'region',\n type: 'select',\n label: 'AWS Region (optional)',\n placeholder: 'us-east-1',\n options: [\n { value: 'us-east-1', text: 'US East (N. Virginia)' },\n { value: 'us-east-2', text: 'US East (Ohio)' },\n { value: 'us-west-1', text: 'US West (N. California)' },\n { value: 'us-west-2', text: 'US West (Oregon)' },\n { value: 'ca-central-1', text: 'Canada (Central)' },\n { value: 'eu-west-1', text: 'Europe (Ireland)' },\n { value: 'eu-west-2', text: 'Europe (London)' },\n { value: 'eu-west-3', text: 'Europe (Paris)' },\n { value: 'eu-central-1', text: 'Europe (Frankfurt)' },\n { value: 'eu-north-1', text: 'Europe (Stockholm)' },\n { value: 'eu-south-1', text: 'Europe (Milan)' },\n { value: 'ap-southeast-2', text: 'Asia Pacific (Sydney)' }\n ],\n columns: 12,\n help: 'Optional. Defaults to project AWS_REGION if omitted.'\n },\n {\n name: 'aws_key',\n type: 'text',\n label: 'AWS Key (optional)',\n placeholder: 'enter your AWS Key with SES permissions',\n columns: 12,\n help: 'Optional, AWS Key with SES permissions'\n },\n {\n name: 'aws_secret',\n type: 'text',\n label: 'AWS Secret (optional)',\n placeholder: 'enter your AWS Secret with SES permissions',\n columns: 12,\n help: 'Optional, AWS Secret with SES permissions'\n },\n ]\n},\n\n onboard: {\n title: 'Onboard Domain',\n fields: [\n {\n type: 'header',\n text: 'Receiving',\n level: 6,\n className: 'mt-2'\n },\n {\n name: 'receiving_enabled',\n type: 'switch',\n label: 'Enable Inbound Receiving',\n columns: 12\n },\n {\n name: 's3_inbound_bucket',\n type: 'text',\n label: 'Inbound S3 Bucket',\n placeholder: 'my-inbound-bucket',\n columns: 12,\n help: 'Required if receiving is enabled.'\n },\n {\n name: 's3_inbound_prefix',\n type: 'text',\n label: 'Inbound S3 Prefix',\n placeholder: 'inbound/example.com/',\n columns: 12\n },\n\n {\n type: 'header',\n text: 'MAIL FROM (optional)',\n level: 6,\n className: 'mt-3'\n },\n {\n name: 'ensure_mail_from',\n type: 'switch',\n label: 'Ensure MAIL FROM Setup',\n columns: 12\n },\n {\n name: 'mail_from_subdomain',\n type: 'text',\n label: 'MAIL FROM Subdomain',\n placeholder: 'feedback',\n columns: 12\n },\n\n {\n type: 'header',\n text: 'DNS',\n level: 6,\n className: 'mt-3'\n },\n {\n name: 'dns_mode',\n type: 'select',\n label: 'DNS Mode',\n options: [\n { value: 'manual', text: 'Manual (show records)' },\n { value: 'godaddy', text: 'GoDaddy (apply via API)' }\n ],\n value: 'manual',\n columns: 12\n },\n {\n name: 'godaddy_key',\n type: 'text',\n label: 'GoDaddy API Key',\n columns: 12,\n help: 'Required when DNS Mode = GoDaddy.'\n },\n {\n name: 'godaddy_secret',\n type: 'password',\n label: 'GoDaddy API Secret',\n columns: 12\n },\n\n {\n type: 'header',\n text: 'Webhook Endpoints',\n level: 6,\n className: 'mt-3'\n },\n {\n name: 'endpoints.bounce',\n type: 'text',\n label: 'Bounce Endpoint',\n placeholder: 'https://portal.example.com/api/aws/sns/bounce',\n columns: 12\n },\n {\n name: 'endpoints.complaint',\n type: 'text',\n label: 'Complaint Endpoint',\n placeholder: 'https://portal.example.com/api/aws/sns/complaint',\n columns: 12\n },\n {\n name: 'endpoints.delivery',\n type: 'text',\n label: 'Delivery Endpoint',\n placeholder: 'https://portal.example.com/api/aws/sns/delivery',\n columns: 12\n },\n {\n name: 'endpoints.inbound',\n type: 'text',\n label: 'Inbound Endpoint',\n placeholder: 'https://portal.example.com/api/aws/sns/inbound',\n columns: 12\n }\n ]\n }\n};\n\n/**\n * Mailbox - Represents a single email address within a domain.\n * Endpoint: /api/aws/email/mailbox\n * Fields (typical):\n * - domain: FK (domain id) or name (server may accept name)\n * - email: full address\n * - allow_inbound: boolean\n * - allow_outbound: boolean\n * - async_handler: \"package.module:function\" for task dispatch on inbound\n */\nclass Mailbox extends Model {\n constructor(data = {}, options = {}) {\n super(data, {\n endpoint: '/api/aws/email/mailbox',\n ...options\n });\n }\n}\n\nMailbox.sendEmail = async function(data) {\n return await rest.POST('/api/aws/email/send', data);\n};\n\n/**\n * MailboxList - Collection of Mailboxes\n */\nclass MailboxList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: Mailbox,\n endpoint: '/api/aws/email/mailbox',\n size: 10,\n ...options\n });\n }\n}\n\n/**\n * Forms configuration for Mailbox CRUD\n */\nconst MailboxForms = {\n create: {\n title: 'Add Mailbox',\n fields: [\n {\n type: 'collection',\n name: 'domain',\n label: 'Domain',\n Collection: EmailDomainList, // Collection class\n labelField: 'name', // Field to display in dropdown\n valueField: 'id', // Field to use as value\n maxItems: 10, // Max items to show in dropdown\n placeholder: 'Search domains...',\n emptyFetch: false,\n required: true,\n debounceMs: 300, // Search debounce delay\n columns: 12\n },\n {\n name: 'email',\n type: 'email',\n label: 'Email Address',\n placeholder: 'support@example.com',\n required: true,\n columns: 12\n },\n {\n name: 'allow_inbound',\n type: 'switch',\n label: 'Allow Inbound',\n columns: 6\n },\n {\n name: 'allow_outbound',\n type: 'switch',\n label: 'Allow Outbound',\n defaultValue: true,\n columns: 6\n },\n {\n name: 'is_system_default',\n type: 'switch',\n label: 'System Default',\n columns: 6\n },\n {\n name: 'is_domain_default',\n type: 'switch',\n label: 'Domain Default',\n columns: 6\n },\n {\n name: 'async_handler',\n type: 'text',\n label: 'Async Handler (optional)',\n placeholder: 'myapp.handlers.process_support',\n columns: 12,\n help: 'Module:function to process inbound messages via task system'\n }\n ]\n },\n\n edit: {\n title: 'Edit Mailbox',\n fields: [\n {\n name: 'email',\n type: 'email',\n label: 'Email Address',\n required: true,\n columns: 12\n },\n {\n name: 'allow_inbound',\n type: 'switch',\n label: 'Allow Inbound',\n columns: 6\n },\n {\n name: 'allow_outbound',\n type: 'switch',\n label: 'Allow Outbound',\n columns: 6\n },\n {\n name: 'is_system_default',\n type: 'switch',\n label: 'System Default',\n columns: 6\n },\n {\n name: 'is_domain_default',\n type: 'switch',\n label: 'Domain Default',\n columns: 6\n },\n {\n name: 'async_handler',\n type: 'text',\n label: 'Async Handler (optional)',\n placeholder: 'myapp.handlers.process_support',\n columns: 12\n }\n ]\n }\n};\n\n/**\n * SentMessage - Outbound messages sent via SES; read-only in UI\n * Endpoint: /api/aws/email/sent\n */\nclass SentMessage extends Model {\n constructor(data = {}, options = {}) {\n super(data, {\n endpoint: '/api/aws/email/sent',\n ...options\n });\n }\n}\n\n/**\n * SentMessageList - Collection of SentMessage\n */\nclass SentMessageList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: SentMessage,\n endpoint: '/api/aws/email/sent',\n size: 10,\n ...options\n });\n }\n}\n\n/**\n * Forms for SentMessage (read-only details)\n */\nconst SentMessageForms = {\n view: {\n title: 'Sent Message Details',\n fields: [\n { name: 'id', type: 'text', label: 'ID', readonly: true, cols: 6 },\n { name: 'ses_message_id', type: 'text', label: 'SES Message ID', readonly: true, cols: 6 },\n { name: 'from_email', type: 'text', label: 'From', readonly: true, cols: 12 },\n { name: 'to', type: 'textarea', label: 'To', readonly: true, rows: 2, cols: 12 },\n { name: 'cc', type: 'textarea', label: 'CC', readonly: true, rows: 2, cols: 12 },\n { name: 'bcc', type: 'textarea', label: 'BCC', readonly: true, rows: 2, cols: 12 },\n { name: 'subject', type: 'text', label: 'Subject', readonly: true, cols: 12 },\n { name: 'status', type: 'text', label: 'Status', readonly: true, cols: 6 },\n { name: 'status_reason', type: 'textarea', label: 'Status Reason', readonly: true, rows: 3, cols: 12 },\n { name: 'created', type: 'text', label: 'Created', readonly: true, cols: 6 }\n ]\n }\n};\n\n/**\n * EmailTemplate - DB template entries (Django templated), CRUD supported\n * Endpoint: /api/aws/email/template\n */\nclass EmailTemplate extends Model {\n constructor(data = {}, options = {}) {\n super(data, {\n endpoint: '/api/aws/email/template',\n ...options\n });\n }\n}\n\n/**\n * EmailTemplateList - Collection of EmailTemplate\n */\nclass EmailTemplateList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: EmailTemplate,\n endpoint: '/api/aws/email/template',\n size: 10,\n ...options\n });\n }\n}\n\n/**\n * Forms configuration for EmailTemplate CRUD\n */\nconst EmailTemplateForms = {\n create: {\n title: 'Add Email Template',\n fields: [\n { name: 'name', type: 'text', label: 'Name', required: true, cols: 12 },\n { name: 'subject_template', type: 'text', label: 'Subject Template', cols: 12 },\n {\n type: 'tabset',\n name: 'settingsTabs',\n tabs: [\n {\n label: 'HTML',\n fields: [\n { name: 'html_template', type: 'htmlpreview', label: 'HTML Template', rows: 16, cols: 12 },\n ],\n },\n {\n label: 'TEXT',\n fields: [\n { name: 'text_template', type: 'textarea', label: 'Text Template', rows: 16, cols: 12 }\n ]\n }\n ]\n }\n ]\n },\n edit: {\n title: 'Edit Email Template',\n fields: [\n { name: 'name', type: 'text', label: 'Name', required: true, cols: 12 },\n { name: 'subject_template', type: 'text', label: 'Subject Template', cols: 12 },\n {\n type: 'tabset',\n name: 'settingsTabs',\n tabs: [\n {\n label: 'HTML',\n fields: [\n { name: 'html_template', type: 'textarea', label: 'HTML Template', rows: 16, cols: 12 },\n ],\n },\n {\n label: 'TEXT',\n fields: [\n { name: 'text_template', type: 'textarea', label: 'Text Template', rows: 16, cols: 12 }\n ]\n }\n ]\n }\n ]\n }\n};\n\nexport {\n EmailDomain,\n EmailDomainList,\n EmailDomainForms,\n Mailbox,\n MailboxList,\n MailboxForms,\n SentMessage,\n SentMessageList,\n SentMessageForms,\n EmailTemplate,\n EmailTemplateList,\n EmailTemplateForms\n};\n","\nimport Collection from '@core/Collection.js';\nimport Model from '@core/Model.js';\n\n/* =========================\n * IncidentEvent\n * ========================= */\n class IncidentStats extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/incident/stats',\n requiresId: false\n });\n }\n }\n\n\nclass IncidentEvent extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/incident/event',\n });\n }\n}\n\nclass IncidentEventList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: IncidentEvent,\n endpoint: '/api/incident/event',\n size: 10,\n ...options,\n });\n }\n}\n\nconst IncidentEventForms = {\n edit: {\n title: 'Edit Incident Event',\n fields: [\n {\n name: 'category',\n type: 'select',\n label: 'Category',\n placeholder: 'Select category',\n options: () => Incident.COMPONENTS,\n editable: true,\n force_top: true,\n cols: 6,\n },\n {\n name: 'incident',\n type: 'text',\n label: 'Incident',\n placeholder: 'Enter Incident ID',\n cols: 6,\n },\n {\n name: 'description',\n type: 'textarea',\n label: 'Description',\n placeholder: 'Enter Description',\n cols: 12,\n },\n {\n name: 'details',\n type: 'textarea',\n label: 'Details',\n placeholder: 'Enter Details',\n cols: 12,\n },\n {\n name: 'component',\n type: 'text',\n label: 'Component',\n placeholder: 'Enter Component',\n cols: 8,\n },\n {\n name: 'component_id',\n type: 'text',\n label: 'Component ID',\n placeholder: 'Enter Component ID',\n cols: 4,\n },\n ],\n },\n};\n\n/* =========================\n * Incident\n * ========================= */\nclass Incident extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/incident/incident',\n });\n }\n}\n\nclass IncidentList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: Incident,\n endpoint: '/api/incident/incident',\n size: 10,\n ...options,\n });\n }\n}\n\nconst IncidentForms = {\n create: {\n title: 'Create Incident',\n fields: [\n {\n type: 'tabset',\n name: 'settingsTabs',\n tabs: [\n {\n label: 'General',\n fields: [\n {\n name: 'title',\n type: 'text',\n label: 'Title',\n required: true,\n columns: 12\n },\n {\n name: 'details',\n type: 'textarea',\n label: 'Details',\n required: true,\n columns: 12\n },\n\n ]\n },\n {\n label: 'Advanced',\n fields: [\n {\n name: 'priority',\n type: 'select',\n label: 'Priority',\n options: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'],\n value: 5,\n columns: 6\n },\n {\n name: 'status',\n type: 'select',\n label: 'Status',\n value: 'open',\n options: ['open', 'investigating', 'resolved', 'closed', \"paused\", \"ignored\"],\n columns: 6\n },\n {\n name: 'category',\n type: 'text',\n label: 'Category',\n value: 'manual',\n columns: 6\n },\n ]\n },\n {\n label: 'Metadata',\n fields: [\n {\n name: 'metadata',\n type: 'json',\n label: 'Metadata',\n value: { \"example\": \"hello world\" },\n rows: 15,\n columns: 12\n }\n ]\n }\n ]\n },\n\n ]\n },\n edit: {\n title: 'Edit Incident',\n fields: [\n {\n name: 'category',\n type: 'text',\n label: 'Category',\n cols: 6,\n },\n {\n name: 'state',\n type: 'select',\n label: 'State',\n placeholder: 'Select State',\n options: ['new', 'opened', 'paused', 'ignore', 'resolved'],\n cols: 3,\n },\n {\n name: 'priority',\n type: 'text',\n label: 'Priority',\n },\n {\n name: 'details',\n type: 'textarea',\n label: 'Description',\n placeholder: 'Enter Name',\n cols: 12,\n },\n {\n name: 'model_name',\n type: 'text',\n label: 'Model',\n placeholder: 'Enter Model',\n cols: 8,\n },\n {\n name: 'model_id',\n type: 'text',\n label: 'Model ID',\n placeholder: 'Enter Model ID',\n cols: 4,\n },\n ],\n },\n};\n\n/* =========================\n * RuleSet / Rule\n * ========================= */\nclass IncidentRuleSet extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/incident/event/ruleset',\n });\n }\n}\n\nclass IncidentRuleSetList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: IncidentRuleSet,\n endpoint: '/api/incident/event/ruleset',\n size: 10,\n ...options,\n });\n }\n}\n\nclass IncidentRule extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/incident/event/ruleset/rule',\n });\n }\n}\n\nclass IncidentRuleList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: IncidentRule,\n endpoint: '/api/incident/event/ruleset/rule',\n size: 10,\n ...options,\n });\n }\n}\n\n/* =========================\n * IncidentHistory\n * ========================= */\nclass IncidentHistory extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/incident/incident/history',\n });\n }\n}\n\nclass IncidentHistoryList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: IncidentHistory,\n endpoint: '/api/incident/incident/history',\n size: 10,\n ...options,\n });\n }\n}\n\n\nexport {\n IncidentEvent,\n IncidentEventList,\n IncidentEventForms,\n Incident,\n IncidentList,\n IncidentForms,\n IncidentRuleSet,\n IncidentRuleSetList,\n IncidentRule,\n IncidentRuleList,\n IncidentHistory,\n IncidentHistoryList\n};\n\n/* =========================\n * RuleSet\n * ========================= */\n\n// Bundle By Options\nconst BundleByOptions = [\n { value: 0, label: 'No Bundling' },\n { value: 1, label: 'By Hostname' },\n { value: 2, label: 'By Model Name' },\n { value: 3, label: 'By Model Name + ID' },\n { value: 4, label: 'By Source IP' },\n { value: 5, label: 'By Hostname + Model Name' },\n { value: 6, label: 'By Hostname + Model Name + ID' },\n { value: 7, label: 'By Source IP + Model Name' },\n { value: 8, label: 'By Source IP + Model Name + ID' },\n { value: 9, label: 'By Source IP + Hostname' }\n];\n\n// Match By Options\nconst MatchByOptions = [\n { value: 0, label: 'ALL (must match all rules)' },\n { value: 1, label: 'ANY (match any rule)' }\n];\n\n// Comparator Options\nconst ComparatorOptions = [\n { value: '==', label: 'Equal (==)' },\n { value: 'eq', label: 'Equal (eq)' },\n { value: '>', label: 'Greater Than (>)' },\n { value: '>=', label: 'Greater Than or Equal (>=)' },\n { value: '<', label: 'Less Than (<)' },\n { value: '<=', label: 'Less Than or Equal (<=)' },\n { value: 'contains', label: 'Contains' },\n { value: 'regex', label: 'Regular Expression' }\n];\n\n// Value Type Options\nconst ValueTypeOptions = [\n { value: 'str', label: 'String' },\n { value: 'int', label: 'Integer' },\n { value: 'float', label: 'Float' },\n { value: 'bool', label: 'Boolean' }\n];\n\n// Common Event Fields for ComboInput\nconst CommonEventFields = [\n { value: 'level', label: 'Level', description: 'Event level (error, warning, info, debug)', meta: { type: 'str' } },\n { value: 'source_ip', label: 'Source IP Address', description: 'IP address of the event source', meta: { type: 'str' } },\n { value: 'rule_id', label: 'Rule ID', description: 'Numeric rule identifier', meta: { type: 'int' } },\n { value: 'hostname', label: 'Hostname', description: 'Hostname where event occurred', meta: { type: 'str' } },\n { value: 'component', label: 'Component', description: 'System component name', meta: { type: 'str' } },\n { value: 'component_id', label: 'Component ID', description: 'Component identifier', meta: { type: 'str' } },\n { value: 'category', label: 'Category', description: 'Event category (ossec, auth, api_error, etc.)', meta: { type: 'str' } },\n { value: 'description', label: 'Description', description: 'Event description text', meta: { type: 'str' } },\n { value: 'details', label: 'Details', description: 'Additional event details', meta: { type: 'str' } },\n { value: 'alert_id', label: 'Alert ID', description: 'Numeric alert identifier', meta: { type: 'int' } },\n { value: 'severity', label: 'Severity', description: 'Numeric severity level', meta: { type: 'int' } },\n { value: 'user', label: 'User', description: 'Username associated with event', meta: { type: 'str' } },\n { value: 'action', label: 'Action', description: 'Action that triggered the event', meta: { type: 'str' } },\n { value: 'status', label: 'Status', description: 'Status value or code', meta: { type: 'str' } },\n { value: 'status_code', label: 'Status Code', description: 'Numeric status code (e.g., HTTP status)', meta: { type: 'int' } },\n { value: 'message', label: 'Message', description: 'Event message text', meta: { type: 'str' } },\n { value: 'path', label: 'Path', description: 'File path or URL path', meta: { type: 'str' } },\n { value: 'title', label: 'Title', description: 'OSSEC Title', meta: { type: 'str' } },\n { value: 'country_code', label: 'Country Code', description: 'Country code associated with event', meta: { type: 'str' } },\n { value: 'region', label: 'Region', description: 'Region associated with event', meta: { type: 'str' } },\n { value: 'city', label: 'City', description: 'City associated with event', meta: { type: 'str' } },\n { value: 'http_user_agent', label: 'HTTP User Agent', description: 'User agent string associated with event', meta: { type: 'str' } },\n { value: 'request_path', label: 'Request Path', description: 'Request path associated with event', meta: { type: 'str' } },\n { value: 'method', label: 'Method', description: 'HTTP method or function name', meta: { type: 'str' } }\n];\n\nconst CommonScopeOptions = [\n { value: 'global', label: 'Global' },\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: 'realtime', label: 'Realtime' },\n { value: 'aws', label: 'AWS' }\n];\n\nclass RuleSet extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/incident/event/ruleset',\n });\n }\n}\n\nclass RuleSetList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: RuleSet,\n endpoint: '/api/incident/event/ruleset',\n ...options,\n });\n }\n}\n\nconst RuleSetForms = {\n create: {\n title: 'Create RuleSet',\n size: 'lg',\n fields: [\n {\n name: 'name',\n type: 'text',\n label: 'Name',\n required: true,\n placeholder: 'Enter ruleset name',\n cols: 12\n },\n {\n name: 'category',\n type: 'combo',\n label: 'Scope',\n required: true,\n options: CommonScopeOptions,\n placeholder: 'e.g., ossec, auth, api_error',\n cols: 6\n },\n {\n name: 'priority',\n type: 'number',\n label: 'Priority',\n value: 10,\n required: true,\n cols: 6\n },\n {\n name: 'match_by',\n type: 'select',\n label: 'Match Logic',\n value: 0,\n options: MatchByOptions,\n cols: 12\n },\n {\n name: 'bundle_by',\n type: 'select',\n label: 'Bundle By',\n value: 0,\n options: BundleByOptions,\n cols: 12\n },\n {\n name: 'bundle_minutes',\n type: 'number',\n label: 'Bundle Minutes',\n value: 10,\n placeholder: 'Time window for bundling',\n cols: 12\n },\n {\n name: 'handler',\n type: 'text',\n label: 'Handler',\n placeholder: 'e.g., email://user@company.com, task://name',\n cols: 12\n },\n {\n name: 'is_active',\n type: 'switch',\n label: 'Is Active',\n value: true,\n cols: 6\n },\n {\n name: 'bundle_by_rule_set',\n type: 'switch',\n label: 'Bundle by Rule Set',\n value: true,\n cols: 6\n }\n ]\n },\n edit: {\n title: 'Edit RuleSet',\n size: 'lg',\n fields: [\n {\n name: 'name',\n type: 'text',\n label: 'Name',\n required: true,\n placeholder: 'Enter ruleset name',\n cols: 12\n },\n {\n name: 'category',\n type: 'combo',\n label: 'Scope',\n options: CommonScopeOptions,\n required: true,\n placeholder: 'e.g., ossec, auth, api_error',\n cols: 6\n },\n {\n name: 'priority',\n type: 'number',\n label: 'Priority',\n required: true,\n cols: 6\n },\n {\n name: 'match_by',\n type: 'select',\n label: 'Match Logic',\n options: MatchByOptions,\n cols: 12\n },\n {\n name: 'bundle_by',\n type: 'select',\n label: 'Bundle By',\n options: BundleByOptions,\n cols: 12\n },\n {\n name: 'bundle_minutes',\n type: 'select',\n label: 'Bundle Minutes',\n placeholder: 'Time window for bundling',\n \"options\": [\n {\"value\": 0, \"label\": \"Disabled - don't bundle by time\"},\n {\"value\": 5, \"label\": \"5 minutes\"},\n {\"value\": 10, \"label\": \"10 minutes\"},\n {\"value\": 15, \"label\": \"15 minutes\"},\n {\"value\": 30, \"label\": \"30 minutes\"},\n {\"value\": 60, \"label\": \"1 hour\"},\n {\"value\": 120, \"label\": \"2 hours\"},\n {\"value\": 360, \"label\": \"6 hours\"},\n {\"value\": 720, \"label\": \"12 hours\"},\n {\"value\": 1440, \"label\": \"1 day\"},\n {\"value\": null, \"label\": \"No limit - bundle forever\"}\n ],\n cols: 12\n },\n {\n name: 'handler',\n type: 'text',\n label: 'Handler',\n placeholder: 'e.g., email://user@company.com, task://name',\n cols: 12\n },\n {\n name: 'is_active',\n type: 'switch',\n label: 'Is Active',\n value: true,\n cols: 6\n },\n {\n name: 'bundle_by_rule_set',\n type: 'switch',\n label: 'Bundle by Rule Set',\n value: true,\n cols: 6\n }\n ]\n }\n};\n\n/* =========================\n * Rule\n * ========================= */\nclass Rule extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/incident/event/ruleset/rule',\n });\n }\n}\n\nclass RuleList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: Rule,\n endpoint: '/api/incident/event/ruleset/rule',\n ...options,\n });\n }\n}\n\nconst RuleForms = {\n create: {\n title: 'Create Rule',\n fields: [\n {\n name: 'name',\n type: 'text',\n label: 'Name',\n required: true,\n placeholder: 'Enter rule name',\n cols: 12\n },\n {\n name: 'field_name',\n type: 'combo',\n label: 'Field Name',\n required: true,\n placeholder: 'Select or enter field name...',\n options: CommonEventFields,\n allowCustom: true,\n showDescription: true,\n help: 'Select a common field or type a custom field name',\n cols: 8\n },\n {\n name: 'index',\n type: 'select',\n label: 'Index',\n required: true,\n start: 0,\n end: 14,\n step: 1,\n cols: 4\n },\n {\n name: 'comparator',\n type: 'select',\n label: 'Comparator',\n required: true,\n options: ComparatorOptions,\n cols: 6\n },\n {\n name: 'value_type',\n type: 'select',\n label: 'Value Type',\n required: true,\n options: ValueTypeOptions,\n value: 'str',\n cols: 6\n },\n {\n name: 'value',\n type: 'textarea',\n label: 'Value',\n required: true,\n placeholder: 'Enter comparison value',\n cols: 12\n }\n ]\n },\n edit: {\n title: 'Edit Rule',\n fields: [\n {\n name: 'name',\n type: 'text',\n label: 'Name',\n required: true,\n placeholder: 'Enter rule name',\n cols: 12\n },\n {\n name: 'field_name',\n type: 'combo',\n label: 'Field Name',\n required: true,\n placeholder: 'Select or enter field name...',\n options: CommonEventFields,\n allowCustom: true,\n showDescription: true,\n help: 'Select a common field or type a custom field name',\n cols: 12\n },\n {\n name: 'comparator',\n type: 'select',\n label: 'Comparator',\n required: true,\n options: ComparatorOptions,\n cols: 6\n },\n {\n name: 'value_type',\n type: 'select',\n label: 'Value Type',\n required: true,\n options: ValueTypeOptions,\n cols: 6\n },\n {\n name: 'value',\n type: 'text',\n label: 'Value',\n required: true,\n placeholder: 'Enter comparison value',\n cols: 12\n }\n ]\n }\n};\n\n// Attach forms to models\nRuleSet.EDIT_FORM = RuleSetForms.edit;\nRuleSet.ADD_FORM = RuleSetForms.create;\nRuleSet.CREATE_FORM = RuleSetForms.create; // Alias for compatibility\nRuleSet.BundleByOptions = BundleByOptions;\nRuleSet.MatchByOptions = MatchByOptions;\n\nRule.EDIT_FORM = RuleForms.edit;\nRule.ADD_FORM = RuleForms.create;\nRule.CREATE_FORM = RuleForms.create; // Alias for compatibility\nRule.ComparatorOptions = ComparatorOptions;\nRule.ValueTypeOptions = ValueTypeOptions;\n\nexport {\n RuleSet,\n RuleSetList,\n RuleSetForms,\n Rule,\n RuleList,\n RuleForms,\n IncidentStats,\n BundleByOptions,\n MatchByOptions,\n ComparatorOptions,\n ValueTypeOptions,\n CommonEventFields,\n CommonScopeOptions\n};\n","/**\n * Job Model - Async Job Engine management\n * Supports object-oriented POST_SAVE_ACTIONS pattern from Jobs API\n */\n\nimport Collection from '@core/Collection.js';\nimport Model from '@core/Model.js';\nimport rest from '@core/Rest.js';\n\nclass Job extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/jobs/job'\n });\n }\n\n // Object-oriented actions using POST_SAVE_ACTIONS pattern\n async cancel() {\n const response = await this.save({ cancel_request: true });\n if (response.success && response.data.status) {\n // Update local status if successful\n this.set('cancel_requested', true);\n }\n return response;\n }\n\n async retry(delay = null) {\n const data = delay ?\n { retry_request: { retry: true, delay } } :\n { retry_request: true };\n\n const response = await this.save(data);\n if (response.success && response.data.status && response.data.new_job_id) {\n // Return new job ID if created\n return {\n ...response,\n newJobId: response.data.new_job_id,\n originalJobId: response.data.original_job_id\n };\n }\n return response;\n }\n\n async getDetailedStatus() {\n const response = await this.save({ get_status: true });\n if (response.success && response.data.status) {\n // Update local data with detailed status\n this.set(response.data.data);\n }\n return response;\n }\n\n async cloneJob(newPayload = {}) {\n const publishData = {\n publish_job: {\n payload: newPayload,\n ...newPayload\n }\n };\n\n const response = await this.save(publishData);\n if (response.success && response.data.status && response.data.job_id) {\n return {\n ...response,\n newJobId: response.data.job_id,\n templateJobId: response.data.template_job_id\n };\n }\n return response;\n }\n\n // Status helper methods\n isActive() {\n const status = this.get('status');\n return ['pending', 'running'].includes(status);\n }\n\n isTerminal() {\n const status = this.get('status');\n return ['completed', 'failed', 'canceled', 'expired'].includes(status);\n }\n\n canRetry() {\n const status = this.get('status');\n return ['failed', 'canceled', 'expired'].includes(status) && this.get('is_retriable') !== false;\n }\n\n canCancel() {\n const status = this.get('status');\n return ['pending', 'running'].includes(status) && !this.get('cancel_requested');\n }\n\n // Get status badge class for UI\n getStatusBadgeClass() {\n const status = this.get('status');\n const classes = {\n 'pending': 'bg-primary',\n 'running': 'bg-success',\n 'completed': 'bg-info',\n 'failed': 'bg-danger',\n 'canceled': 'bg-secondary',\n 'expired': 'bg-warning'\n };\n return classes[status] || 'bg-secondary';\n }\n\n // Get status icon\n getStatusIcon() {\n const status = this.get('status');\n const icons = {\n 'pending': 'bi-hourglass',\n 'running': 'bi-arrow-repeat',\n 'completed': 'bi-check-circle',\n 'failed': 'bi-x-octagon',\n 'canceled': 'bi-x-circle',\n 'expired': 'bi-clock'\n };\n return icons[status] || 'bi-question-circle';\n }\n\n // Get recent events for timeline\n getEvents() {\n return this.get('recent_events') || [];\n }\n\n // Get formatted duration\n getFormattedDuration() {\n const duration = this.get('duration_ms');\n if (!duration || duration === 0) return 'N/A';\n\n if (duration < 1000) return `${duration}ms`;\n if (duration < 60000) return `${(duration / 1000).toFixed(1)}s`;\n if (duration < 3600000) return `${(duration / 60000).toFixed(1)}m`;\n return `${(duration / 3600000).toFixed(1)}h`;\n }\n\n // Get queue position if available\n getQueuePosition() {\n return this.get('queue_position');\n }\n\n // Check if job has expired\n hasExpired() {\n const expiresAt = this.get('expires_at');\n if (!expiresAt) return false;\n return new Date(expiresAt) < new Date();\n }\n\n // Get runner information\n getRunnerId() {\n return this.get('runner_id');\n }\n\n // Get payload data\n getPayload() {\n return this.get('payload') || {};\n }\n\n // Get metadata\n getMetadata() {\n return this.get('metadata') || {};\n }\n}\n\nclass JobList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: Job,\n endpoint: '/api/jobs/job',\n ...options\n });\n }\n\n // Filter by status\n async fetchByStatus(status, params = {}) {\n return this.fetch({ status, ...params });\n }\n\n // Filter by channel\n async fetchByChannel(channel, params = {}) {\n return this.fetch({ channel, ...params });\n }\n\n // Get pending jobs\n async fetchPending(params = {}) {\n return this.fetchByStatus('pending', params);\n }\n\n // Get running jobs\n async fetchRunning(params = {}) {\n return this.fetchByStatus('running', params);\n }\n\n // Get completed jobs\n async fetchCompleted(params = {}) {\n return this.fetchByStatus('completed', params);\n }\n\n // Get failed jobs\n async fetchFailed(params = {}) {\n return this.fetchByStatus('failed', params);\n }\n\n // Get scheduled jobs (those with run_at)\n async fetchScheduled(params = {}) {\n return this.fetch({\n scheduled: true, // This may need to be adjusted based on actual API\n ...params\n });\n }\n}\n\n// Form configurations for job management\nconst JobForms = {\n publish: {\n title: 'Publish New Job',\n fields: [\n {\n name: 'func',\n type: 'text',\n label: 'Function',\n required: true,\n placeholder: 'myapp.jobs.send_email',\n help: 'Module path to job function'\n },\n {\n name: 'channel',\n type: 'text',\n label: 'Channel',\n value: 'default',\n help: 'Queue channel (default: \"default\")'\n },\n {\n name: 'payload',\n type: 'textarea',\n label: 'Payload (JSON)',\n required: true,\n rows: 8,\n placeholder: '{\\n \"key\": \"value\"\\n}',\n help: 'JSON data passed to the job function'\n },\n {\n name: 'delay',\n type: 'number',\n label: 'Delay (seconds)',\n min: 0,\n help: 'Delay execution by specified seconds'\n },\n {\n name: 'run_at',\n type: 'datetime-local',\n label: 'Run At',\n help: 'Schedule for specific date/time'\n },\n {\n name: 'max_retries',\n type: 'number',\n label: 'Max Retries',\n value: 3,\n min: 0,\n max: 10\n },\n {\n name: 'expires_in',\n type: 'number',\n label: 'Expires In (seconds)',\n value: 900,\n min: 60,\n help: 'Job will expire if not completed in this time'\n },\n {\n name: 'broadcast',\n type: 'switch',\n label: 'Broadcast to All Workers',\n help: 'Execute on all available workers'\n }\n ]\n },\n\n retry: {\n title: 'Retry Job',\n fields: [\n {\n name: 'delay',\n type: 'number',\n label: 'Delay (seconds)',\n value: 0,\n min: 0,\n help: 'Delay before retry (0 = immediate)'\n }\n ]\n },\n\n clone: {\n title: 'Clone Job',\n fields: [\n {\n name: 'channel',\n type: 'text',\n label: 'Channel',\n help: 'Override channel for cloned job'\n },\n {\n name: 'payload',\n type: 'textarea',\n label: 'Modified Payload (JSON)',\n rows: 8,\n help: 'Modified payload for cloned job'\n },\n {\n name: 'delay',\n type: 'number',\n label: 'Delay (seconds)',\n min: 0\n }\n ]\n }\n};\n\n// Static method to publish new job directly\nJob.publish = async function(jobData) {\n return await rest.POST('/api/jobs/publish', jobData);\n};\n\n// Static method to get system stats\nJob.getStats = async function() {\n const response = await rest.GET('/api/jobs/stats');\n return response.success ? response.data : null;\n};\n\n// Static method to get system health\nJob.getHealth = async function(channel = null) {\n const endpoint = channel ? `/api/jobs/health/${channel}` : '/api/jobs/health';\n const response = rest.GET(endpoint);\n return response.success ? response.data : null;\n};\n\n// Static method to run test job\nJob.test = async function() {\n return await rest.POST('/api/jobs/test', {});\n};\n\n// Static method to run test jobs\nJob.tests = async function() {\n return await rest.POST('/api/jobs/tests', {});\n};\n\nJob.clearStuck = async function(channel = null) {\n const payload = channel ? { channel } : {};\n return await rest.POST('/api/jobs/control/clear-stuck', payload);\n};\n\nJob.clearChannel = async function(channel) {\n return await rest.POST('/api/jobs/control/clear-queue', { channel, confirm:\"yes\" });\n};\n\nJob.cleanConsumers = async function() {\n return await rest.POST('/api/jobs/control/cleanup-consumers');\n};\n\nJob.purgeJobs = async function(days_old) {\n return await rest.POST('/api/jobs/control/purge', { days_old });\n};\n\n\nclass JobLog extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/jobs/logs'\n });\n }\n}\n\nclass JobLogList extends Collection {\n constructor(options = {}) {\n super({\n endpoint: '/api/jobs/logs',\n model: JobLog,\n ...options\n });\n }\n}\n\n\nclass JobEvent extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/jobs/event'\n });\n }\n}\n\nclass JobEventList extends Collection {\n constructor(options = {}) {\n super({\n endpoint: '/api/jobs/event',\n model: JobEvent,\n ...options\n });\n }\n}\n\nclass JobsEngineStats extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/jobs/stats',\n requiresId: false\n });\n }\n}\n\nexport {\n Job, JobList, JobForms,\n JobLog, JobLogList, JobEvent,\n JobEventList, JobsEngineStats };\n","/**\n * JobRunner Model - Job Engine Runner/Worker management\n * Manages worker processes that execute jobs\n */\n\nimport Collection from '@core/Collection.js';\nimport Model from '@core/Model.js';\n\nclass JobRunner extends Model {\n constructor(data = {}) {\n // Map runner_id to id for proper Model handling\n if (data.runner_id && !data.id) {\n data.id = data.runner_id;\n }\n \n super(data, {\n endpoint: '/api/jobs/runners',\n idAttribute: 'runner_id' // Tell the model to use runner_id as the ID field\n });\n }\n\n // Ping this runner to test connectivity\n async ping(timeout = 2.0) {\n const app = this.getApp();\n if (!app || !app.rest) {\n throw new Error('App or REST client not available');\n }\n\n const response = await app.rest.POST('/api/jobs/runners/ping', {\n runner_id: this.get('runner_id'),\n timeout: timeout\n });\n\n if (response.success && response.data.status) {\n // Update ping status\n this.set('last_heartbeat', new Date().toISOString());\n this.set('ping_status', response.data.ping_status || 'success');\n }\n\n return response;\n }\n\n // Shutdown this runner gracefully or forcefully\n async shutdown(graceful = true) {\n const app = this.getApp();\n if (!app || !app.rest) {\n throw new Error('App or REST client not available');\n }\n\n const response = await app.rest.POST('/api/jobs/runners/shutdown', {\n runner_id: this.get('runner_id'),\n graceful: graceful\n });\n\n if (response.success && response.data.status) {\n // Update status to shutting down (alive becomes false)\n this.set('alive', false);\n }\n\n return response;\n }\n\n // Get channels handled by this runner\n getChannels() {\n return this.get('channels') || [];\n }\n\n // Get runner status with appropriate styling based on alive field\n getStatusBadgeClass() {\n const alive = this.get('alive');\n return alive ? 'bg-success' : 'bg-danger';\n }\n\n // Get status icon based on alive field\n getStatusIcon() {\n const alive = this.get('alive');\n return alive ? 'bi-check-circle-fill' : 'bi-x-octagon-fill';\n }\n\n // Check if runner is active (alive)\n isActive() {\n return this.get('alive') === true;\n }\n\n // Check if runner is healthy (active and recent heartbeat)\n isHealthy() {\n if (!this.isActive()) return false;\n \n const lastHeartbeat = this.get('last_heartbeat');\n if (!lastHeartbeat) return false;\n \n // Consider healthy if heartbeat within last 2 minutes\n const heartbeatAge = (Date.now() - new Date(lastHeartbeat).getTime()) / 1000;\n return heartbeatAge < 120;\n }\n\n // Get formatted heartbeat age\n getFormattedHeartbeatAge() {\n const lastHeartbeat = this.get('last_heartbeat');\n if (!lastHeartbeat) return 'Never';\n \n const heartbeatAge = (Date.now() - new Date(lastHeartbeat).getTime()) / 1000;\n \n if (heartbeatAge < 60) return `${Math.round(heartbeatAge)}s ago`;\n if (heartbeatAge < 3600) return `${Math.round(heartbeatAge / 60)}m ago`;\n return `${Math.round(heartbeatAge / 3600)}h ago`;\n }\n\n // Get current utilization (from jobs processed vs capacity)\n getUtilization() {\n const processed = this.get('jobs_processed') || 0;\n const failed = this.get('jobs_failed') || 0;\n const total = processed + failed;\n // Since we don't have max capacity from API, return based on recent activity\n return total > 10 ? 100 : Math.min(total * 10, 100);\n }\n\n // Get formatted uptime based on started timestamp\n getFormattedUptime() {\n const started = this.get('started');\n if (!started) return 'Unknown';\n \n const startTime = new Date(started);\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\n // Get worker capacity info\n getWorkerInfo() {\n const processed = this.get('jobs_processed') || 0;\n const failed = this.get('jobs_failed') || 0;\n return {\n processed,\n failed,\n total: processed + failed,\n alive: this.get('alive'),\n utilization: this.getUtilization()\n };\n }\n\n // Get display name for runner\n getDisplayName() {\n const runnerId = this.get('runner_id');\n if (!runnerId) return 'Unknown Runner';\n \n // Extract hostname-like portion if it exists\n const parts = runnerId.split('-');\n return parts.length > 1 ? parts[0] : runnerId;\n }\n\n // Check if runner can be controlled\n canControl() {\n return this.get('alive') === true;\n }\n}\n\nclass JobRunnerList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: JobRunner,\n endpoint: '/api/jobs/runners',\n ...options\n });\n }\n\n // Get only active runners\n getActive() {\n return this.where(runner => runner.isActive());\n }\n\n // Get runners by channel\n getByChannel(channel) {\n return this.where(runner => runner.getChannels().includes(channel));\n }\n\n // Get healthy runners\n getHealthy() {\n return this.where(runner => runner.isHealthy());\n }\n\n // Get total jobs processed across all runners\n getTotalProcessed() {\n return this.models.reduce((total, runner) => {\n return total + (runner.get('jobs_processed') || 0);\n }, 0);\n }\n\n // Get total jobs failed across all runners\n getTotalFailed() {\n return this.models.reduce((total, runner) => {\n return total + (runner.get('jobs_failed') || 0);\n }, 0);\n }\n\n // Get overall system health percentage (alive runners / total runners)\n getSystemHealth() {\n if (this.models.length === 0) return 0;\n const aliveRunners = this.models.filter(runner => runner.get('alive')).length;\n return Math.round((aliveRunners / this.models.length) * 100);\n }\n\n // Get unique channels across all runners\n getAllChannels() {\n const channels = new Set();\n this.models.forEach(runner => {\n runner.getChannels().forEach(channel => channels.add(channel));\n });\n return Array.from(channels).sort();\n }\n}\n\n// Static methods for system-wide runner management\nJobRunner.ping = async function(runnerId, timeout = 2.0) {\n const app = typeof window !== 'undefined' && window.app;\n if (!app || !app.rest) {\n throw new Error('App or REST client not available');\n }\n \n return await app.rest.POST('/api/jobs/runners/ping', {\n runner_id: runnerId,\n timeout\n });\n};\n\nJobRunner.shutdown = async function(runnerId, graceful = true) {\n const app = typeof window !== 'undefined' && window.app;\n if (!app || !app.rest) {\n throw new Error('App or REST client not available');\n }\n \n return await app.rest.POST('/api/jobs/runners/shutdown', {\n runner_id: runnerId,\n graceful\n });\n};\n\nJobRunner.broadcast = async function(command, data = {}, timeout = 2.0) {\n const app = typeof window !== 'undefined' && window.app;\n if (!app || !app.rest) {\n throw new Error('App or REST client not available');\n }\n \n return await app.rest.POST('/api/jobs/runners/broadcast', {\n command,\n data,\n timeout\n });\n};\n\n// Broadcast commands\nJobRunner.broadcastStatus = async function(timeout = 2.0) {\n return JobRunner.broadcast('status', {}, timeout);\n};\n\nJobRunner.broadcastShutdown = async function(timeout = 2.0) {\n return JobRunner.broadcast('shutdown', {}, timeout);\n};\n\nJobRunner.broadcastPause = async function(timeout = 2.0) {\n return JobRunner.broadcast('pause', {}, timeout);\n};\n\nJobRunner.broadcastResume = async function(timeout = 2.0) {\n return JobRunner.broadcast('resume', {}, timeout);\n};\n\nJobRunner.broadcastReload = async function(timeout = 2.0) {\n return JobRunner.broadcast('reload', {}, timeout);\n};\n\nconst JobRunnerForms = {\n broadcast: {\n title: 'Broadcast Command',\n fields: [\n {\n name: 'command',\n type: 'select',\n label: 'Command',\n required: true,\n options: [\n { value: 'status', label: 'Status Check' },\n { value: 'pause', label: 'Pause Processing' },\n { value: 'resume', label: 'Resume Processing' },\n { value: 'reload', label: 'Reload Configuration' },\n { value: 'shutdown', label: 'Shutdown All Runners' }\n ],\n help: 'Command to send to all runners'\n },\n {\n name: 'timeout',\n type: 'number',\n label: 'Timeout (seconds)',\n value: 2.0,\n min: 0.5,\n max: 10.0,\n step: 0.5,\n help: 'How long to wait for responses'\n }\n ]\n }\n};\n\nexport { JobRunner, JobRunnerList, JobRunnerForms };","import Collection from '@core/Collection.js';\nimport Model from '@core/Model.js';\n\nclass MetricsPermission extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/metrics/permissions',\n id_key: 'account'\n });\n }\n}\n\nclass MetricsPermissionList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: MetricsPermission,\n endpoint: '/api/metrics/permissions',\n ...options,\n });\n }\n\n}\n\nconst MetricsForms = {\n edit: {\n title: 'Edit Metrics Permissions',\n fields: [\n { name: 'account', type: 'text', label: 'Account', columns:12 },\n { name: 'view_permissions', type: 'tags', label: 'View Permissions', help: 'Enter permissions or \"public\"', columns:12 },\n { name: 'write_permissions', type: 'tags', label: 'Write Permissions', help: 'Enter permissions', columns:12 },\n ]\n }\n};\n\nexport { MetricsPermission, MetricsPermissionList, MetricsForms };\n","import Collection from '@core/Collection.js';\nimport Model from '@core/Model.js';\nimport { GroupList } from './Group.js';\n\n// =========================\n// PushDevice\n// =========================\nclass PushDevice extends Model {\n constructor(data = {}) {\n super(data, { endpoint: '/api/account/devices/push' });\n }\n}\n\nclass PushDeviceList extends Collection {\n constructor(options = {}) {\n super({ ModelClass: PushDevice, endpoint: '/api/account/devices/push', ...options });\n }\n}\n\n// =========================\n// PushTemplate\n// =========================\nclass PushTemplate extends Model {\n constructor(data = {}) {\n super(data, { endpoint: '/api/account/devices/push/templates' });\n }\n}\n\nclass PushTemplateList extends Collection {\n constructor(options = {}) {\n super({ ModelClass: PushTemplate, endpoint: '/api/account/devices/push/templates', ...options });\n }\n}\n\n// =========================\n// PushConfig\n// =========================\nclass PushConfig extends Model {\n constructor(data = {}) {\n super(data, { endpoint: '/api/account/devices/push/config' });\n }\n}\n\nclass PushConfigList extends Collection {\n constructor(options = {}) {\n super({ ModelClass: PushConfig, endpoint: '/api/account/devices/push/config', ...options });\n }\n}\n\n// =========================\n// PushDelivery\n// =========================\nclass PushDelivery extends Model {\n constructor(data = {}) {\n super(data, { endpoint: '/api/account/devices/push/deliveries' });\n }\n}\n\nclass PushDeliveryList extends Collection {\n constructor(options = {}) {\n super({ ModelClass: PushDelivery, endpoint: '/api/account/devices/push/deliveries', ...options });\n }\n}\n\n// =========================\n// Forms\n// =========================\nconst PushConfigForms = {\n create: {\n title: 'Create Push Configuration',\n fields: [\n { name: 'name', label: 'Name', required: true },\n { type: 'collection', name: 'group', label: 'Group (optional)', Collection: GroupList, labelField: 'name', valueField: 'id' },\n { name: 'fcm_service_account', label: 'Service Account', type: \"textarea\", rows: 10},\n ]\n },\n edit: {\n title: 'Edit Push Configuration',\n fields: [\n { name: 'name', label: 'Name', required: true },\n { type: 'collection', name: 'group', label: 'Group (optional)', Collection: GroupList, labelField: 'name', valueField: 'id' },\n { name: 'fcm_service_account', label: 'Service Account', type: \"textarea\", rows: 10},\n { name: 'is_active', label: 'Is Active', type: 'switch', value: true },\n ]\n }\n};\n\nconst PushTemplateForms = {\n edit: {\n title: 'Edit Push Template',\n fields: [\n { name: 'name', label: 'Name', required: true },\n { name: 'category', label: 'Category', required: true },\n { \n type: 'collection', \n name: 'group', \n label: 'Group (optional)', \n Collection: GroupList, \n labelField: 'name', \n valueField: 'id',\n defaultParams: { is_active: true } // Example: filter to active groups only\n },\n { name: 'title_template', label: 'Title Template', required: true },\n { name: 'body_template', label: 'Body Template', type: 'textarea', required: true },\n { name: 'action_url', label: 'Action URL' },\n { name: 'priority', label: 'Priority', type: 'select', options: ['high', 'normal'] },\n { name: 'variables', label: 'Variables', type: 'json', help: 'JSON format' },\n { name: 'is_active', label: 'Is Active', type: 'switch' },\n ]\n }\n};\n\nPushConfigForms.create = PushConfigForms.edit;\nPushTemplateForms.create = PushTemplateForms.edit;\n\n\nexport {\n PushDevice, PushDeviceList,\n PushTemplate, PushTemplateList,\n PushConfig, PushConfigList,\n PushDelivery, PushDeliveryList,\n PushConfigForms, PushTemplateForms\n};\n","import Collection from '@core/Collection.js';\nimport Model from '@core/Model.js';\n\n/* =========================\n * GeoLocatedIP\n * ========================= */\n\nconst GeoIPForms = {\n editLocation: {\n title: 'Edit Location',\n size: 'lg',\n fields: [\n { name: 'ip_address', label: 'IP Address', type: 'text', required: true, readonly: true, cols: 6 },\n { name: 'subnet', label: 'Subnet', type: 'text', cols: 6 },\n { name: 'country_name', label: 'Country', type: 'text', cols: 6 },\n { name: 'country_code', label: 'Country Code', type: 'text', cols: 6 },\n { name: 'region', label: 'Region', type: 'text', cols: 6 },\n { name: 'city', label: 'City', type: 'text', cols: 6 },\n { name: 'postal_code', label: 'Postal Code', type: 'text', cols: 6 },\n { name: 'timezone', label: 'Timezone', type: 'text', cols: 6 },\n { name: 'latitude', label: 'Latitude', type: 'number', step: 'any', cols: 6 },\n { name: 'longitude', label: 'Longitude', type: 'number', step: 'any', cols: 6 },\n ]\n },\n editSecurity: {\n title: 'Edit Security',\n size: 'md',\n fields: [\n { \n name: 'threat_level', \n label: 'Threat Level', \n type: 'select', \n cols: 12,\n options: [\n { value: '', label: 'None' },\n { value: 'low', label: 'Low' },\n { value: 'medium', label: 'Medium' },\n { value: 'high', label: 'High' },\n { value: 'critical', label: 'Critical' }\n ]\n },\n { name: 'is_threat', label: 'Threat', type: 'switch', cols: 6 },\n { name: 'is_suspicious', label: 'Suspicious', type: 'switch', cols: 6 },\n { name: 'is_known_attacker', label: 'Known Attacker', type: 'switch', cols: 6 },\n { name: 'is_known_abuser', label: 'Known Abuser', type: 'switch', cols: 6 },\n { name: 'risk_score', label: 'Risk Score', type: 'number', cols: 6 },\n { name: 'is_tor', label: 'TOR Exit Node', type: 'switch', cols: 6 },\n { name: 'is_vpn', label: 'VPN', type: 'switch', cols: 6 },\n { name: 'is_proxy', label: 'Proxy', type: 'switch', cols: 6 },\n { name: 'is_cloud', label: 'Cloud Provider', type: 'switch', cols: 6 },\n { name: 'is_datacenter', label: 'Datacenter', type: 'switch', cols: 6 }\n ]\n },\n editNetwork: {\n title: 'Edit Network',\n size: 'md',\n fields: [\n { name: 'asn', label: 'ASN', type: 'text', cols: 6 },\n { name: 'asn_org', label: 'ASN Organization', type: 'text', cols: 6 },\n { name: 'isp', label: 'ISP', type: 'text', cols: 12 },\n { name: 'connection_type', label: 'Connection Type', type: 'text', cols: 6 },\n { name: 'provider', label: 'Provider', type: 'text', cols: 6 },\n { name: 'is_mobile', label: 'Mobile Connection', type: 'switch', cols: 6 },\n { name: 'mobile_carrier', label: 'Mobile Carrier', type: 'text', cols: 6 },\n { name: 'last_seen', label: 'Last Seen', type: 'datetime', cols: 12 }\n ]\n }\n};\n\nclass GeoLocatedIP extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/system/geoip',\n });\n }\n\n static async lookup(ip) {\n const model = new GeoLocatedIP();\n const resp = await model.rest.GET('/api/system/geoip/lookup', { ip });\n if (resp.success && resp.data && resp.data.data) {\n return new GeoLocatedIP(resp.data.data);\n }\n return null;\n }\n}\n\n// Attach forms to model (use Location as default EDIT_FORM for TableView)\nGeoLocatedIP.EDIT_FORM = GeoIPForms.editLocation;\nGeoLocatedIP.EDIT_LOCATION_FORM = GeoIPForms.editLocation;\nGeoLocatedIP.EDIT_SECURITY_FORM = GeoIPForms.editSecurity;\nGeoLocatedIP.EDIT_NETWORK_FORM = GeoIPForms.editNetwork;\n\nclass GeoLocatedIPList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: GeoLocatedIP,\n endpoint: '/api/system/geoip',\n ...options,\n });\n }\n}\n\nexport { GeoLocatedIP, GeoLocatedIPList };\n","import Collection from '@core/Collection.js';\nimport Model from '@core/Model.js';\nimport { UserList } from './User.js';\nimport { IncidentList } from './Incident.js';\n\n/* =========================\n * Ticket\n * ========================= */\nclass Ticket extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/incident/ticket',\n });\n }\n}\n\nclass TicketList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: Ticket,\n endpoint: '/api/incident/ticket',\n ...options,\n });\n }\n}\n\n\nconst TicketCategories = {\n 'ticket': 'Ticket',\n 'bug': 'Bug',\n 'feature': 'Feature Request',\n 'incident': 'Incident',\n 'security': 'Security Incident',\n 'fulfillment': 'Fulfillment',\n 'new_user': 'New User',\n 'new_group': 'New Group',\n 'qa': 'Quality Assurance'\n};\n\n// Convert TicketCategories to select options\nconst TicketCategoriesOptions = Object.entries(TicketCategories).map(([key, label]) => ({\n value: key,\n label: label\n}));\n\n\nconst TicketForms = {\n create: {\n title: 'Create Ticket',\n fields: [\n {\n name: 'title', type: 'text',\n label: 'Title', required: true, cols: 12\n },\n { name: 'description', type: 'textarea', label: 'Description', required: false, cols: 12 },\n {\n name: \"category\", type: \"select\", label: \"Category\",\n options: TicketCategoriesOptions, cols: 12, value: 'ticket' },\n {\n name: 'priority', type: 'number',\n label: 'Priority', value: 5, cols: 6\n },\n {\n name: 'status', type: 'select', label: \"Status\",\n options: [\"new\", \"open\", \"paused\", \"resolved\", \"qa\", \"ignored\"],\n cols: 6, value: 'new'\n },\n {\n type: 'collection', name: 'assignee',\n label: 'Assignee', Collection: UserList,\n labelField: 'display_name',\n valueField: 'id',\n cols: 12\n },\n {\n type: 'collection', name: 'incident',\n label: 'Incident', Collection: IncidentList,\n labelField: 'title',\n valueField: 'id',\n cols: 12\n },\n ]\n },\n edit: {\n title: 'Edit Ticket',\n fields: [\n { name: 'title', type: 'text', label: 'Title', required: true, cols: 12 },\n { name: 'description', type: 'textarea', label: 'Description', required: false, cols: 12 },\n { name: \"category\", type: \"select\", label: \"Category\", options: TicketCategoriesOptions, cols: 12 },\n { name: 'priority', type: 'number', label: 'Priority', cols: 6 },\n { name: 'status', type: 'select', label: 'Status', options: [\"new\", \"open\", \"paused\", \"resolved\", \"qa\", \"ignored\"], cols: 6 },\n { type: 'collection', name: 'assignee', label: 'Assignee', Collection: UserList, labelField: 'display_name', valueField: 'id', cols: 12 },\n { type: 'collection', name: 'incident', label: 'Incident', Collection: IncidentList, labelField: 'title', valueField: 'id', cols: 12 },\n ]\n }\n};\n\n/* =========================\n * TicketNote\n * ========================= */\nclass TicketNote extends Model {\n constructor(data = {}) {\n super(data, {\n endpoint: '/api/incident/ticket/note',\n });\n }\n}\n\nclass TicketNoteList extends Collection {\n constructor(options = {}) {\n super({\n ModelClass: TicketNote,\n endpoint: '/api/incident/ticket/note',\n ...options,\n });\n }\n}\n\nexport { Ticket, TicketList, TicketNote, TicketNoteList, TicketForms, TicketCategories };\n","/**\n * TablePage - Page component that manages a TableView with URL parameter synchronization\n *\n * A clean, simplified implementation using the new TableView component.\n * Automatically syncs pagination, sorting, and filtering with URL parameters.\n *\n * @example\n * const usersPage = new TablePage({\n * pageName: 'users',\n * title: 'User Management',\n * collection: userCollection,\n * columns: [\n * { key: 'name', label: 'Name', sortable: true },\n * { key: 'email', label: 'Email' },\n * { key: 'role', label: 'Role', type: 'badge' }\n * ],\n * actions: ['view', 'edit', 'delete']\n * });\n */\n\nimport Page from '@core/Page.js';\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport TableView from '@core/views/table/TableView.js';\nimport Collection from '@core/Collection.js';\nimport { parseFilterKey } from '@core/utils/DjangoLookups.js';\n\nclass TablePage extends Page {\n constructor(options = {}) {\n super({\n ...options,\n pageName: options.pageName || options.name || 'table'\n });\n\n // Page configuration\n this.title = options.title || this.pageName;\n this.description = options.description || '';\n\n // Collection setup\n this.Collection = options.Collection || null;\n this.collection = options.collection || null;\n\n // our default collection query\n this.defaultQuery = options.defaultQuery || {};\n\n // Group field configuration - defaults to \"group\"\n this.groupField = options.groupField || 'group';\n\n // Store configuration for TableView\n // Map legacy property names to new ones\n this.tableViewConfig = {\n // Core table properties\n columns: options.columns || [],\n actions: options.actions || null,\n contextMenu: options.contextMenu || null,\n batchActions: options.batchActions || null,\n batchBarLocation: options.batchBarLocation || 'top',\n clickAction: options.clickAction || 'view',\n // Map legacy form properties to new names\n addForm: options.addForm || options.formFields || options.formCreate,\n editForm: options.editForm || options.formEdit || options.formFields,\n\n // Model operation configurations\n itemView: options.itemView,\n deleteTemplate: options.deleteTemplate,\n formDialogConfig: options.formDialogConfig,\n viewDialogOptions: options.viewDialogOptions,\n\n // Features\n searchable: options.searchable !== false,\n sortable: options.sortable !== false,\n filterable: options.filterable !== false,\n paginated: options.paginated !== false,\n\n // Selection mode\n selectionMode: options.selectionMode || (options.selectable ? 'multiple' : 'none'),\n\n // Filter configuration\n filters: options.filters || options.additionalFilters || [],\n hideActivePills: options.hideActivePills || false,\n hideActivePillNames: options.hideActivePillNames || [],\n searchPlacement: options.searchPlacement || 'toolbar',\n\n // Display options for the HTML table element\n tableOptions: {\n striped: true,\n bordered: false,\n hover: true,\n responsive: false,\n ...options.tableOptions\n },\n\n // Additional options\n emptyMessage: options.emptyMessage || 'No data available',\n searchPlaceholder: options.searchPlaceholder || 'Search...',\n showAdd: options.showAdd !== false,\n showExport: options.showExport !== false,\n\n // Custom handlers\n onItemView: options.onItemView,\n onItemEdit: options.onItemEdit,\n onItemDelete: options.onItemDelete,\n onAdd: options.onAdd,\n onExport: options.onExport,\n\n // Override with tableViewOptions if provided\n ...options.tableViewOptions\n };\n\n // URL synchronization\n this.urlSyncEnabled = options.urlSyncEnabled !== false;\n\n // Status tracking\n this.lastUpdated = null;\n this.isLoading = false;\n\n // Set up template\n this.template = options.template || this.buildTemplate();\n }\n\n /**\n * Build the page template\n */\n buildTemplate() {\n return `\n <div class=\"table-page-container\">\n\n <div class=\"table-container\" data-container=\"table\"></div>\n\n {{#showStatus}}\n <div class=\"table-status-bar table-status-top\">\n <div class=\"status-info\">\n <div class=\"d-flex justify-content-between w-100\">\n <span class=\"text-muted\">\n <i class=\"bi bi-clock me-1\"></i>\n Last updated: <span data-status=\"last-updated\">{{lastUpdated}}</span>\n </span>\n <span class=\"text-muted\">\n <i class=\"bi bi-list-ol me-1\"></i>\n Total records: <span data-status=\"record-count\">0</span>\n </span>\n </div>\n </div>\n </div>\n {{/showStatus}}\n\n </div>\n `;\n }\n\n /**\n * Initialize the page\n */\n async onInit() {\n await super.onInit();\n\n // Create collection if needed\n if (!this.collection) {\n if (this.Collection) {\n this.collection = new this.Collection();\n } else {\n this.collection = new Collection();\n }\n }\n\n // Apply URL query parameters to collection\n this.applyQueryToCollection();\n\n // Create TableView instance with all configuration\n this.tableView = new TableView({\n collection: this.collection,\n containerId: 'table',\n fetchOnMount: true,\n ...this.tableViewConfig\n });\n\n // Add as child view\n this.addChild(this.tableView);\n\n // Set up event listeners\n this.setupEventListeners();\n }\n\n /**\n * Set up event listeners\n */\n setupEventListeners() {\n // Listen for collection changes to sync URL\n if (this.urlSyncEnabled && this.collection) {\n // Sync URL when collection params change\n this.collection.on('fetch:start', () => {\n this.isLoading = true;\n });\n\n this.collection.on('fetch:end', () => {\n this.isLoading = false;\n this.lastUpdated = new Date().toLocaleTimeString();\n this.updateStatusDisplay();\n });\n }\n\n // Listen for params-changed event from TableView to sync URL\n this.tableView.on('params-changed', () => {\n if (this.urlSyncEnabled) {\n this.syncUrl();\n }\n });\n\n // // Listen for table events (these also emit params-changed, but keep for backwards compatibility)\n // this.tableView.on('table:search', ({ searchTerm }) => {\n // // params-changed will handle URL sync\n // });\n\n // this.tableView.on('table:sort', ({ field }) => {\n // // params-changed will handle URL sync\n // });\n\n // this.tableView.on('table:page', ({ page }) => {\n // // params-changed will handle URL sync\n // });\n\n // Filter events - params-changed will handle URL sync\n this.tableView.on('filter:edit', async ({ key }) => {\n await this.handleFilterEdit(key);\n });\n\n // Row action events\n this.tableView.on('row:view', async ({ model }) => {\n if (this.onItemView) {\n await this.onItemView(model);\n }\n });\n\n this.tableView.on('row:edit', async ({ model }) => {\n if (this.onItemEdit) {\n await this.onItemEdit(model);\n }\n });\n\n this.tableView.on('row:delete', async ({ model }) => {\n if (this.onItemDelete) {\n await this.onItemDelete(model);\n }\n });\n\n // Table action events\n // Note: TableView will call options.onAdd if provided, but we still listen to the event\n // for backwards compatibility and to support event-based patterns\n this.tableView.on('table:add', async ({ event }) => {\n // The handler was already called by TableView if options.onAdd is set,\n // but we keep this listener for external code that might listen to 'table:add'\n // We don't call this.tableViewConfig.onAdd here to avoid double execution\n });\n\n this.tableView.on('table:export', async ({ data }) => {\n if (this.tableViewConfig.onExport) {\n await this.tableViewConfig.onExport(data);\n }\n });\n }\n\n /**\n * Apply URL query parameters to collection\n */\n applyQueryToCollection() {\n const params = {};\n const query = { ...this.defaultQuery, ...this.query };\n if (!query || Object.keys(query).length === 0) {\n return;\n }\n // Pagination\n if (query.start !== undefined) params.start = parseInt(query.start) || 0;\n if (query.size !== undefined) params.size = parseInt(query.size) || 10;\n\n // Sorting\n if (query.sort !== undefined) params.sort = query.sort;\n\n // Search\n if (query.search !== undefined) params.search = query.search;\n\n // Process all other params as potential filters\n const reservedParams = ['start', 'size', 'sort', 'search', 'page'];\n Object.entries(query).forEach(([key, value]) => {\n if (!reservedParams.includes(key) && value !== undefined && value !== '') {\n // Parse value if it looks like JSON\n if (typeof value === 'string' && (value.startsWith('{') || value.startsWith('['))) {\n try {\n params[key] = JSON.parse(value);\n } catch (e) {\n params[key] = value;\n }\n } else {\n params[key] = value;\n }\n }\n });\n\n // Update collection params\n if (Object.keys(params).length > 0) {\n // Deduplicate simple + __in filters (prefer __in / not_in when both are present)\n Object.keys(params).forEach(key => {\n const { field, lookup } = parseFilterKey(key);\n if ((lookup === 'in' || lookup === 'not_in') && params.hasOwnProperty(field)) {\n delete params[field];\n }\n });\n\n this.collection.setParams({\n ...this.collection.params,\n ...params\n });\n }\n }\n\n /**\n * Sync URL with current table state\n */\n syncUrl(force = true) {\n if (!this.urlSyncEnabled || !this.collection || !this.getApp()?.router) {\n return;\n }\n\n // Get current URL params\n const currentUrl = new URL(window.location);\n const currentParams = {};\n for (const [key, value] of currentUrl.searchParams) {\n if (key !== 'page') {\n currentParams[key] = value;\n }\n }\n\n // Get desired params from collection\n const desiredParams = {};\n const collectionParams = this.collection.params || {};\n\n // Only include non-default values\n if (collectionParams.start) {\n desiredParams.start = collectionParams.start;\n }\n if (collectionParams.size) {\n desiredParams.size = collectionParams.size;\n }\n if (collectionParams.sort) {\n desiredParams.sort = collectionParams.sort;\n }\n if (collectionParams.search) {\n desiredParams.search = collectionParams.search;\n }\n\n // Include other filters\n Object.entries(collectionParams).forEach(([key, value]) => {\n if (!['start', 'size', 'sort', 'search'].includes(key) && value !== undefined && value !== '') {\n // Stringify complex values for URL\n if (typeof value === 'object') {\n desiredParams[key] = JSON.stringify(value);\n } else {\n desiredParams[key] = value;\n }\n }\n });\n\n // Check if there are any changes\n const hasChanges =\n Object.keys(desiredParams).some(key =>\n String(currentParams[key] || '') !== String(desiredParams[key] || '')\n ) ||\n Object.keys(currentParams).some(key =>\n !(key in desiredParams)\n );\n\n this.query = desiredParams;\n if (!hasChanges && !force) return;\n\n // Update URL\n this.updateBrowserUrl(desiredParams, true, false);\n }\n\n /**\n * Update status display\n */\n updateStatusDisplay() {\n if (!this.element) return;\n\n // Update last updated time\n const updatedElement = this.element.querySelector('[data-status=\"last-updated\"]');\n if (updatedElement) {\n updatedElement.textContent = this.lastUpdated || 'Never';\n }\n\n // Update record count\n const countElement = this.element.querySelector('[data-status=\"record-count\"]');\n if (countElement && this.collection) {\n const count = this.collection.meta?.count || this.collection.length();\n countElement.textContent = count;\n }\n }\n\n /**\n * Called when entering this page\n */\n async onEnter() {\n await super.onEnter();\n\n if (this.options.requiresGroup && !this.query[this.groupField] && this.getApp().activeGroup) {\n this.query[this.groupField] = this.getApp().activeGroup.id;\n }\n\n this.applyQueryToCollection();\n\n // Ensure filter pills are shown if there are active filters from URL\n if (this.tableView && this.tableView.element) {\n setTimeout(() => {\n this.tableView.updateFilterPills();\n this.tableView.updateSortIcons();\n }, 100);\n }\n }\n\n /**\n * Public method to refresh the table\n */\n async refresh() {\n await this.tableView.refresh();\n }\n\n /**\n * Public method to get selected items\n */\n getSelectedItems() {\n return this.tableView.getSelectedItems();\n }\n\n /**\n * Public method to clear selection\n */\n clearSelection() {\n this.tableView.clearSelection();\n }\n\n /**\n * Handle filter edit dialog\n */\n async handleFilterEdit(filterKey) {\n // Using statically imported Dialog\n const filterConfig = this.tableView.getAllAvailableFilters().find(f => f.key === filterKey);\n const currentValue = this.collection.params[filterKey];\n\n if (!filterConfig) return;\n\n // Build form field for the filter\n const field = {\n name: 'filter_value',\n label: filterConfig.label || filterKey,\n value: currentValue,\n ...filterConfig.config\n };\n\n const result = await Dialog.showForm({\n title: `Edit ${field.label} Filter`,\n size: 'md',\n fields: [field]\n });\n\n if (result && result.filter_value !== undefined) {\n this.tableView.setFilter(filterKey, result.filter_value);\n\n if (this.collection.restEnabled) {\n this.collection.fetch();\n }\n await this.tableView.render();\n this.syncUrl();\n }\n }\n\n /**\n * Clear all filters\n */\n clearAllFilters() {\n if (!this.collection) return;\n\n // Keep only pagination and sort params\n const { start, size, sort } = this.collection.params;\n this.collection.params = { start, size };\n if (sort) this.collection.params.sort = sort;\n\n this.syncUrl();\n\n if (this.collection.restEnabled) {\n this.collection.fetch();\n } else {\n this.tableView.render();\n }\n }\n\n async onGroupChange(group) {\n if (!group || !this.collection || !this.options.requiresGroup) return;\n this.query[this.groupField] = group.id;\n this.applyQueryToCollection();\n if (this.collection && this.collection.restEnabled) {\n this.collection.fetch();\n }\n }\n\n /**\n * Cleanup on destroy\n */\n async onBeforeDestroy() {\n // Remove event listeners\n if (this.collection) {\n this.collection.off('fetch:start');\n this.collection.off('fetch:end');\n }\n\n if (this.tableView) {\n this.tableView.off('params-changed');\n this.tableView.off('table:search');\n this.tableView.off('table:sort');\n this.tableView.off('table:page');\n this.tableView.off('filter:edit');\n this.tableView.off('row:view');\n this.tableView.off('row:edit');\n this.tableView.off('row:delete');\n this.tableView.off('table:add');\n this.tableView.off('table:export');\n }\n\n await super.onBeforeDestroy();\n }\n\n /**\n * Show/hide status display\n */\n get showStatus() {\n return this.options.showStatus === true;\n }\n\n /**\n * Static factory method\n */\n static create(options = {}) {\n return new this(options);\n }\n}\n\nexport default TablePage;\n","/**\n * TabView - Responsive tabbed interface component with smooth transitions\n * Displays multiple views in a tabbed interface with automatic responsive adaptation\n *\n * Features:\n * - Responsive tab navigation with automatic dropdown mode\n * - Smooth fade transitions between tabs (Bootstrap compatible)\n * - Child view management with proper mounting/unmounting\n * - Bootstrap 5 styling\n * - Keyboard navigation support\n * - Event-driven tab switching\n * - ResizeObserver for efficient responsive detection\n *\n * Example Usage:\n * ```javascript\n * const tabView = new TabView({\n * tabs: {\n * 'Profile': new UserProfileView({ data: userData }),\n * 'Settings': new UserSettingsView({ data: settings }),\n * 'Activity': new UserActivityView({ data: activity })\n * },\n * activeTab: 'Profile', // Optional: set initial active tab\n * enableTransitions: true, // Optional: enable fade transitions (default: true)\n * transitionDuration: 150, // Optional: transition duration in ms (default: 150)\n * enableResponsive: true, // Optional: enable responsive mode (default: true)\n * dropdownStyle: 'select' // Optional: 'select' or 'button' (default: 'select')\n * });\n * ```\n */\n\nimport View from '@core/View.js';\n\nclass TabView extends View {\n constructor(options = {}) {\n const {\n tabs,\n activeTab,\n tabsClass,\n contentClass,\n minWidth,\n enableResponsive,\n tabPadding,\n dropdownStyle, // 'button' (default) or 'select'\n enableTransitions, // Enable fade transitions (default: true)\n transitionDuration, // Transition duration in ms (default: 150, Bootstrap default)\n ...viewOptions\n } = options;\n\n super({\n tagName: 'div',\n className: 'tab-view',\n ...viewOptions\n });\n\n // Tab configuration\n this.tabs = {};\n this.tabLabels = Object.keys(this.tabs);\n this.activeTab = activeTab || this.tabLabels[0] || null;\n\n // CSS classes\n this.tabsClass = tabsClass || 'nav nav-tabs mb-3';\n this.contentClass = contentClass || 'tab-content';\n\n // Transition configuration\n this.enableTransitions = enableTransitions !== false; // Default to enabled\n this.transitionDuration = transitionDuration || 150; // Bootstrap default\n\n // Responsive configuration\n this.dropdownStyle = dropdownStyle || 'select'; // 'button' or 'select'\n this.minWidth = minWidth || 300; // Minimum width before switching to dropdown\n this.enableResponsive = enableResponsive !== false; // Default to enabled\n this.tabPadding = tabPadding || 80; // Estimated padding per tab (16px * 2)\n this.currentMode = 'tabs'; // 'tabs' or 'dropdown'\n\n // Width calculation cache\n this.tabWidthCache = new Map();\n this.lastContainerWidth = 0;\n this.resizeObserver = null;\n this._measurementSpan = null; // Reusable measurement element\n this._tabComputedStyle = null; // To store computed styles\n\n\n\n // State tracking\n this.isMobileMode = false;\n this.hasOverflow = false;\n\n // Initialize tabs by adding them as child views\n for (const [label, view] of Object.entries(tabs)) {\n this.addTab(label, view);\n }\n\n // Bind resize handler\n this.handleResize = this.handleResize.bind(this);\n }\n\n /**\n * Render the tab navigation and content areas\n */\n async renderTemplate() {\n const tabNavigation = this.buildTabNavigation();\n const tabContent = this.buildTabContent();\n\n return `\n <div class=\"tab-view-container\">\n ${tabNavigation}\n ${tabContent}\n </div>\n `;\n }\n\n /**\n * Build the tab navigation HTML - supports both tab and dropdown modes\n * @returns {string} Tab navigation HTML\n */\n buildTabNavigation() {\n if (this.tabLabels.length === 0) {\n return '';\n }\n\n if (this.currentMode === 'dropdown') {\n return this.buildDropdownNavigation();\n } else {\n return this.buildTabsNavigation();\n }\n }\n\n /**\n * Build traditional tab navigation\n * @returns {string} Tab navigation HTML\n */\n buildTabsNavigation() {\n const tabItems = this.tabLabels.map(label => {\n const isActive = label === this.activeTab;\n const tabId = this.getTabId(label);\n\n return `\n <li class=\"nav-item\" role=\"presentation\">\n <button class=\"nav-link ${isActive ? 'active' : ''}\"\n id=\"${tabId}-tab\"\n data-action=\"show-tab\"\n data-tab-label=\"${this.escapeHtml(label)}\"\n type=\"button\"\n role=\"tab\"\n aria-controls=\"${tabId}\"\n aria-selected=\"${isActive}\">\n ${this.escapeHtml(label)}\n </button>\n </li>\n `;\n }).join('');\n\n return `\n <ul class=\"${this.tabsClass}\" role=\"tablist\" data-tab-mode=\"tabs\">\n ${tabItems}\n </ul>\n `;\n }\n\n /**\n * Build dropdown navigation for mobile/narrow screens\n * @returns {string} Dropdown navigation HTML\n */\n buildDropdownNavigation() {\n const activeLabel = this.activeTab || this.tabLabels[0];\n const dropdownItems = this.tabLabels.map(label => {\n const isActive = label === this.activeTab;\n return `\n <li>\n <button class=\"dropdown-item ${isActive ? 'active' : ''}\"\n data-action=\"show-tab\"\n data-tab-label=\"${this.escapeHtml(label)}\"\n type=\"button\">\n ${this.escapeHtml(label)}\n ${isActive ? '<i class=\"bi bi-check-lg ms-2\"></i>' : ''}\n </button>\n </li>\n `;\n }).join('');\n\n let buttonHtml;\n if (this.dropdownStyle === 'select') {\n // New \"select\" style button\n buttonHtml = `\n <button class=\"btn tab-view-select-style dropdown-toggle\"\n type=\"button\"\n data-bs-toggle=\"dropdown\"\n aria-expanded=\"false\"\n id=\"tab-dropdown-${this.id}\">\n <span class=\"tab-view-select-label\">${this.escapeHtml(activeLabel)}</span>\n </button>\n `;\n } else {\n // Original \"button\" style\n buttonHtml = `\n <button class=\"btn btn-outline-secondary dropdown-toggle w-100 w-sm-auto\"\n type=\"button\"\n data-bs-toggle=\"dropdown\"\n aria-expanded=\"false\"\n id=\"tab-dropdown-${this.id}\">\n <i class=\"bi bi-list me-2\"></i>\n ${this.escapeHtml(activeLabel)}\n </button>\n `;\n }\n\n return `\n <div class=\"dropdown mb-3\" data-tab-mode=\"dropdown\">\n ${buttonHtml}\n <ul class=\"dropdown-menu\" aria-labelledby=\"tab-dropdown-${this.id}\">\n ${dropdownItems}\n </ul>\n </div>\n `;\n }\n\n /**\n * Build mobile dropdown navigation\n * @returns {string} Mobile dropdown HTML\n */\n buildMobileDropdownNavigation() {\n const activeLabel = this.activeTab || this.tabLabels[0];\n const dropdownItems = this.tabLabels.map(label => {\n const isActive = label === this.activeTab;\n return `\n <li>\n <button class=\"dropdown-item ${isActive ? 'active' : ''}\"\n data-action=\"show-tab\"\n data-tab-label=\"${this.escapeHtml(label)}\"\n type=\"button\">\n ${this.escapeHtml(label)}\n ${isActive ? '<i class=\"bi bi-check ms-2\"></i>' : ''}\n </button>\n </li>\n `;\n }).join('');\n\n return `\n <div class=\"dropdown mb-3\" data-tab-navigation=\"mobile\">\n <button class=\"btn btn-outline-secondary dropdown-toggle w-100 text-start\"\n type=\"button\"\n data-bs-toggle=\"dropdown\"\n aria-expanded=\"false\">\n <i class=\"bi bi-list me-2\"></i>\n ${this.escapeHtml(activeLabel)}\n </button>\n <ul class=\"dropdown-menu w-100\">\n ${dropdownItems}\n </ul>\n </div>\n `;\n }\n\n /**\n * Determine if mobile dropdown should be used\n * @returns {boolean} True if mobile dropdown should be used\n */\n shouldUseMobileDropdown() {\n if (!this.enableMobileDropdown) return false;\n\n // Check viewport width\n const viewportWidth = window.innerWidth;\n if (viewportWidth < this.mobileBreakpoint) {\n return true;\n }\n\n // Check for overflow in desktop mode\n return this.hasOverflow && viewportWidth < 992; // Bootstrap lg breakpoint\n }\n\n /**\n * Build the tab content area HTML\n * @returns {string} Tab content HTML\n */\n buildTabContent() {\n if (this.tabLabels.length === 0) {\n return '<div class=\"alert alert-info\">No tabs to display</div>';\n }\n\n const tabPanes = this.tabLabels.map(label => {\n const isActive = label === this.activeTab;\n const tabId = this.getTabId(label);\n\n return `\n <div class=\"tab-pane fade ${isActive ? 'show active' : ''}\"\n id=\"${tabId}\"\n role=\"tabpanel\"\n aria-labelledby=\"${tabId}-tab\"\n data-tab-label=\"${this.escapeHtml(label)}\">\n <div data-container=\"${tabId}-content\"></div>\n </div>\n `;\n }).join('');\n\n return `\n <div class=\"${this.contentClass}\">\n ${tabPanes}\n </div>\n `;\n }\n\n /**\n * Generate a safe DOM ID for a tab\n * @param {string} label - Tab label\n * @returns {string} Safe DOM ID\n */\n getTabId(label) {\n return `tab-${label.toLowerCase().replace(/[^a-z0-9]/g, '-')}-${this.id}`;\n }\n\n /**\n * Show a specific tab\n * @param {string} tabLabel - Label of the tab to show\n * @returns {Promise<boolean>} True if tab was shown successfully\n */\n async showTab(tabLabel, options = {}) {\n const { force = false } = options;\n\n // Validate tab exists\n if (!this.tabs[tabLabel]) {\n console.warn(`TabView: Tab \"${tabLabel}\" does not exist`);\n return false;\n }\n\n // Skip if already active and not being forced\n // This prevents re-rendering the same tab on repeated clicks, but allows\n // for forced re-mounting after a parent view render.\n if (this.activeTab === tabLabel && !force) {\n // As a safeguard, ensure the view is actually in the DOM.\n // If not, proceed to re-mount it.\n const activeView = this.tabs[tabLabel];\n if (activeView && activeView.isMounted() && this.element.contains(activeView.element)) {\n return true;\n }\n }\n\n const previousTab = this.activeTab;\n this.activeTab = tabLabel;\n\n try {\n // Update tab navigation\n await this.updateTabNavigation(tabLabel, previousTab);\n\n // Update tab content\n await this.updateTabContent(tabLabel, previousTab);\n\n // Emit tab change event\n this.emit('tab:changed', {\n activeTab: tabLabel,\n previousTab: previousTab\n });\n\n return true;\n } catch (error) {\n console.error('TabView: Error showing tab:', error);\n this.activeTab = previousTab; // Revert on error\n return false;\n }\n }\n\n /**\n * Update tab navigation visual state\n * @param {string} activeTabLabel - New active tab\n * @param {string} previousTabLabel - Previous active tab\n */\n async updateTabNavigation(activeTabLabel, previousTabLabel) {\n if (!this.element) return;\n\n // In dropdown mode, re-render the navigation to update the button label and items\n if (this.currentMode === 'dropdown') {\n await this.reRenderNavigation();\n return; // Re-rendering handles all visual updates for navigation\n }\n\n // In tabs mode, just toggle classes for efficiency\n if (previousTabLabel) {\n const prevTabButton = this.element.querySelector(`[data-tab-label=\"${previousTabLabel}\"]`);\n if (prevTabButton) {\n prevTabButton.classList.remove('active');\n prevTabButton.setAttribute('aria-selected', 'false');\n }\n }\n\n // Add active state to new tab\n const activeTabButton = this.element.querySelector(`[data-tab-label=\"${activeTabLabel}\"]`);\n if (activeTabButton) {\n activeTabButton.classList.add('active');\n activeTabButton.setAttribute('aria-selected', 'true');\n }\n }\n\n /**\n * Update tab content area and manage child views\n * @param {string} activeTabLabel - New active tab\n * @param {string} previousTabLabel - Previous active tab\n */\n async updateTabContent(activeTabLabel, previousTabLabel) {\n if (!this.element) return;\n\n const activeTabId = this.getTabId(activeTabLabel);\n const previousTabId = previousTabLabel ? this.getTabId(previousTabLabel) : null;\n\n const activePane = this.element.querySelector(`#${activeTabId}`);\n const prevPane = previousTabId ? this.element.querySelector(`#${previousTabId}`) : null;\n\n // Get the active view once at the top\n const activeView = this.tabs[activeTabLabel];\n\n // Handle transitions if enabled\n if (this.enableTransitions) {\n // Start fade-out transition for previous pane\n if (prevPane && prevPane.classList.contains('show')) {\n // Remove 'show' to trigger fade-out (but keep 'active' so it stays visible during transition)\n prevPane.classList.remove('show');\n \n // Wait for fade-out transition to complete\n await new Promise(resolve => {\n const duration = this._getTransitionDuration(prevPane) || this.transitionDuration;\n setTimeout(resolve, duration);\n });\n \n // Now remove 'active' to fully hide the pane\n prevPane.classList.remove('active');\n }\n\n // Mount the active tab's view before showing it (prevents flash of empty content)\n if (activeView) {\n const container = this.element.querySelector(`[data-container=\"${activeTabId}-content\"]`);\n if (container && !activeView.isMounted()) {\n await activeView.render(true, container);\n }\n }\n\n // Start fade-in transition for active pane\n if (activePane) {\n // Add 'active' to make it the active pane (but not visible yet)\n activePane.classList.add('active');\n \n // Force reflow to ensure the 'active' class is applied before 'show'\n // This is necessary for CSS transitions to work properly\n void activePane.offsetHeight;\n \n // Add 'show' to trigger fade-in transition\n activePane.classList.add('show');\n }\n } else {\n // No transitions - instant switch\n if (prevPane) {\n prevPane.classList.remove('show', 'active');\n }\n\n // Mount the active tab's view\n if (activeView) {\n const container = this.element.querySelector(`[data-container=\"${activeTabId}-content\"]`);\n if (container && !activeView.isMounted()) {\n await activeView.render(true, container);\n }\n }\n\n if (activePane) {\n activePane.classList.add('show', 'active');\n }\n }\n\n // Call onTabActivated hook after the view is mounted and visible\n if (activeView && activeView.onTabActivated) {\n await activeView.onTabActivated();\n }\n }\n\n /**\n * Get CSS transition duration for an element\n * @param {HTMLElement} element - Element to check\n * @returns {number} Transition duration in milliseconds\n * @private\n */\n _getTransitionDuration(element) {\n if (!element || typeof window === 'undefined' || !window.getComputedStyle) {\n return 0;\n }\n\n const computedStyle = window.getComputedStyle(element);\n const duration = computedStyle.transitionDuration || '0s';\n \n // Parse duration (can be in seconds or milliseconds)\n const matches = duration.match(/^([0-9.]+)(m?s)$/);\n if (!matches) return 0;\n \n const value = parseFloat(matches[1]);\n const unit = matches[2];\n \n // Convert to milliseconds\n return unit === 's' ? value * 1000 : value;\n }\n\n /**\n * Handle tab click events\n * @param {Event} event - Click event\n * @param {HTMLElement} element - Clicked element\n */\n async onActionShowTab(event, element) {\n const tabLabel = element.getAttribute('data-tab-label');\n if (tabLabel) {\n await this.showTab(tabLabel);\n }\n }\n\n /**\n * Initialize measurement styles by reading computed styles from a rendered tab\n */\n _initializeMeasurementStyles() {\n if (!this.element || this._tabComputedStyle) return;\n\n const tabButton = this.element.querySelector('.nav-link');\n if (tabButton && typeof window.getComputedStyle === 'function') {\n const style = window.getComputedStyle(tabButton);\n this._tabComputedStyle = {\n font: style.font,\n letterSpacing: style.letterSpacing,\n };\n\n // Calculate horizontal padding from computed styles\n const paddingLeft = parseFloat(style.paddingLeft) || 0;\n const paddingRight = parseFloat(style.paddingRight) || 0;\n this.tabPadding = paddingLeft + paddingRight + 12; // Update with real value\n }\n }\n\n /**\n * Initialize active tab after rendering\n */\n async onAfterRender() {\n await super.onAfterRender();\n\n // Initialize styles for accurate width calculation before setting up responsive handling\n this._initializeMeasurementStyles();\n\n // Set up responsive behavior\n if (this.enableResponsive) {\n this.setupResponsiveHandling();\n }\n\n // Show the active tab after initial render. Forcing ensures the content is\n // correctly re-mounted if the TabView itself was re-rendered.\n if (this.activeTab && this.tabs[this.activeTab]) {\n await this.showTab(this.activeTab, { force: true });\n }\n }\n\n /**\n * Mount child views after tab view is mounted\n */\n async onAfterMount() {\n await super.onAfterMount();\n\n // The active tab's content is now mounted via the onAfterRender -> showTab flow,\n // which correctly handles both initial renders and subsequent re-renders.\n // The logic previously here was redundant.\n }\n\n /**\n * Clean up child views when destroying\n */\n async onBeforeDestroy() {\n await super.onBeforeDestroy();\n\n // Clean up resize observer\n if (this.resizeObserver) {\n this.resizeObserver.disconnect();\n this.resizeObserver = null;\n }\n\n // Remove window resize listener\n if (typeof window !== 'undefined') {\n window.removeEventListener('resize', this.handleResize);\n }\n\n // Remove measurement span if it exists\n if (this._measurementSpan && this._measurementSpan.parentElement) {\n this._measurementSpan.parentElement.removeChild(this._measurementSpan);\n }\n this._measurementSpan = null;\n\n // Destroy all child tab views\n for (const [label, view] of Object.entries(this.tabs)) {\n if (view && typeof view.destroy === 'function') {\n await view.destroy();\n }\n }\n }\n\n /**\n * Get the currently active tab label\n * @returns {string|null} Active tab label\n */\n getActiveTab() {\n return this.activeTab;\n }\n\n /**\n * Get all tab labels\n * @returns {string[]} Array of tab labels\n */\n getTabLabels() {\n return [...this.tabLabels];\n }\n\n /**\n * Get a specific tab's view by label\n * @param {string} label - Tab label\n * @returns {View|null} The tab's view instance or null if not found\n */\n getTab(label) {\n return this.tabs[label] || null;\n }\n\n /**\n * Add a new tab\n * @param {string} label - Tab label\n * @param {View} view - View instance for tab content\n * @param {boolean} makeActive - Whether to make this tab active\n * @returns {Promise<boolean>} True if tab was added successfully\n */\n async addTab(label, view, makeActive = false) {\n if (this.tabs[label]) {\n console.warn(`TabView: Tab \"${label}\" already exists`);\n return false;\n }\n\n if (view.options.permissions && !this.getApp().activeUser.hasPerm(view.options.permissions)) {\n return false;\n }\n\n this.tabs[label] = view;\n // this.addChild(view);\n view.containerId = this.getTabId(label);\n view.parent = this;\n this.tabLabels = Object.keys(this.tabs);\n\n // Set as active tab if it's the first tab or explicitly requested\n if (!this.activeTab || makeActive) {\n this.activeTab = label;\n }\n\n // Re-render if mounted\n if (this.isMounted()) {\n await this.render();\n\n if (makeActive || this.activeTab === label) {\n await this.showTab(label);\n }\n }\n\n this.emit('tab:added', { label, view });\n return true;\n }\n\n /**\n * Remove a tab\n * @param {string} label - Tab label to remove\n * @returns {Promise<boolean>} True if tab was removed successfully\n */\n async removeTab(label) {\n if (!this.tabs[label]) {\n console.warn(`TabView: Tab \"${label}\" does not exist`);\n return false;\n }\n\n const view = this.tabs[label];\n\n // Destroy the view\n if (view && typeof view.destroy === 'function') {\n await view.destroy();\n }\n\n // Remove from tabs\n delete this.tabs[label];\n this.tabLabels = Object.keys(this.tabs);\n\n // Handle active tab removal\n if (this.activeTab === label) {\n this.activeTab = this.tabLabels[0] || null;\n }\n\n // Re-render if mounted\n if (this.isMounted()) {\n await this.render();\n\n if (this.activeTab) {\n await this.showTab(this.activeTab);\n }\n }\n\n this.emit('tab:removed', { label, view });\n return true;\n }\n\n /**\n * Calculate estimated width needed for a tab label\n * @param {string} label - Tab label text\n * @returns {number} Estimated width in pixels\n */\n calculateTabWidth(label) {\n if (this.tabWidthCache.has(label)) {\n return this.tabWidthCache.get(label);\n }\n\n // Fallback for non-browser environments\n if (typeof document === 'undefined') {\n const estimatedWidth = label.length * 8 + this.tabPadding; // Original fallback\n this.tabWidthCache.set(label, estimatedWidth);\n return estimatedWidth;\n }\n\n // Create a reusable measurement span if it doesn't exist\n if (!this._measurementSpan) {\n this._measurementSpan = document.createElement('span');\n this._measurementSpan.style.visibility = 'hidden';\n this._measurementSpan.style.position = 'absolute';\n this._measurementSpan.style.whiteSpace = 'nowrap';\n }\n\n const span = this._measurementSpan;\n\n // Apply computed styles if available, otherwise use defaults\n if (this._tabComputedStyle) {\n span.style.font = this._tabComputedStyle.font;\n span.style.letterSpacing = this._tabComputedStyle.letterSpacing;\n } else {\n // Fallback to original hardcoded styles if computed style not yet available\n span.style.fontSize = '14px';\n span.style.fontFamily = 'system-ui, -apple-system, sans-serif';\n }\n\n span.textContent = label;\n document.body.appendChild(span);\n const width = span.offsetWidth + this.tabPadding;\n document.body.removeChild(span);\n\n this.tabWidthCache.set(label, width);\n return width;\n }\n\n /**\n * Calculate total width needed for all tabs\n * @returns {number} Total width in pixels\n */\n getTotalTabWidth() {\n return this.tabLabels.reduce((total, label) => {\n return total + this.calculateTabWidth(label);\n }, 0);\n }\n\n /**\n * Get current container width\n * @returns {number} Container width in pixels\n */\n getContainerWidth() {\n if (!this.element) {\n return this.minWidth;\n }\n\n const container = this.element.parentElement || this.element;\n return container.offsetWidth || this.minWidth;\n }\n\n /**\n * Determine if dropdown mode should be used\n * @returns {boolean} True if dropdown mode should be used\n */\n shouldUseDropdown() {\n if (!this.enableResponsive) {\n return false;\n }\n\n const containerWidth = this.getContainerWidth();\n const totalTabWidth = this.getTotalTabWidth();\n\n // Use dropdown if tabs would overflow or container is too narrow\n return containerWidth < Math.max(totalTabWidth, this.minWidth);\n }\n\n /**\n * Setup responsive handling with ResizeObserver\n */\n setupResponsiveHandling() {\n if (!this.element || !this.enableResponsive) {\n return;\n }\n\n // Set initial mode\n this.updateNavigationMode();\n\n // Use ResizeObserver for better performance\n if (typeof ResizeObserver !== 'undefined') {\n this.resizeObserver = new ResizeObserver(() => {\n this.handleResize();\n });\n\n const container = this.element.parentElement || this.element;\n this.resizeObserver.observe(container);\n } else {\n // Fallback to window resize\n window.addEventListener('resize', this.handleResize);\n }\n }\n\n /**\n * Handle resize events\n */\n async handleResize() {\n const containerWidth = this.getContainerWidth();\n\n // Only update if width changed significantly (avoid excessive updates)\n if (Math.abs(containerWidth - this.lastContainerWidth) > 50) {\n this.lastContainerWidth = containerWidth;\n await this.updateNavigationMode();\n }\n }\n\n /**\n * Update navigation mode based on current space\n */\n async updateNavigationMode() {\n const shouldUseDropdown = this.shouldUseDropdown();\n const newMode = shouldUseDropdown ? 'dropdown' : 'tabs';\n\n if (newMode !== this.currentMode) {\n this.currentMode = newMode;\n\n // Re-render navigation if mounted\n if (this.isMounted()) {\n await this.reRenderNavigation();\n }\n\n // Emit mode change event\n this.emit('navigation:modeChanged', {\n mode: this.currentMode,\n containerWidth: this.getContainerWidth(),\n totalTabWidth: this.getTotalTabWidth()\n });\n }\n }\n\n /**\n * Re-render just the navigation part\n */\n async reRenderNavigation() {\n if (!this.element) return;\n\n const navigationContainer = this.element.querySelector('[data-tab-mode]');\n if (navigationContainer) {\n const newNavigation = this.buildTabNavigation();\n navigationContainer.outerHTML = newNavigation;\n }\n }\n\n /**\n * Get current navigation mode\n * @returns {string} Current mode ('tabs' or 'dropdown')\n */\n getNavigationMode() {\n return this.currentMode;\n }\n\n /**\n * Force a specific navigation mode\n * @param {string} mode - 'tabs' or 'dropdown'\n */\n async setNavigationMode(mode) {\n if (mode !== 'tabs' && mode !== 'dropdown') {\n console.warn('TabView: Invalid navigation mode. Use \"tabs\" or \"dropdown\"');\n return;\n }\n\n this.currentMode = mode;\n\n if (this.isMounted()) {\n await this.reRenderNavigation();\n }\n }\n\n /**\n * Clear the tab width cache (useful when tab labels change)\n */\n clearWidthCache() {\n this.tabWidthCache.clear();\n }\n\n /**\n * Static factory method\n * @param {object} options - TabView options\n * @returns {TabView} New TabView instance\n */\n static create(options = {}) {\n return new TabView(options);\n }\n}\n\nexport default TabView;\n","import View from '@core/View.js';\n\nclass FilePreviewView extends View {\n constructor(options = {}) {\n super({\n className: 'file-preview',\n ...options\n });\n this.file = options.file || {};\n this.isImage = this.file.content_type?.startsWith('image/');\n this.isPdf = this.file.content_type === 'application/pdf';\n }\n\n getTemplate() {\n return `\n <div class=\"file-preview-item card card-body p-2 mt-2\">\n <div class=\"d-flex align-items-center\">\n <div class=\"flex-shrink-0\">\n ${this.isImage ? `<img src=\"${this.file.thumbnailUrl || this.file.url}\" class=\"rounded\" style=\"width: 40px; height: 40px; object-fit: cover;\">` : `<i class=\"bi bi-file-earmark-text fs-2 text-secondary\"></i>`}\n </div>\n <div class=\"flex-grow-1 ms-3\">\n <div class=\"fw-bold text-truncate\">{{file.filename}}</div>\n <div class=\"small text-muted\">{{file.file_size|filesize}}</div>\n </div>\n <div class=\"flex-shrink-0\">\n <button class=\"btn btn-sm btn-outline-primary\" data-action=\"view-file\">View</button>\n </div>\n </div>\n </div>\n `;\n }\n\n async onActionViewFile() {\n if (this.isImage) {\n // Check if lightbox extension is available\n const LightboxGallery = window.MOJO?.plugins?.LightboxGallery;\n \n if (LightboxGallery) {\n LightboxGallery.show({ src: this.file.url, alt: this.file.filename });\n } else {\n // Fallback: open in new tab\n window.open(this.file.url, '_blank');\n }\n } else if (this.isPdf) {\n // Check if lightbox extension is available\n const PDFViewer = window.MOJO?.plugins?.PDFViewer;\n \n if (PDFViewer) {\n PDFViewer.showDialog(this.file.url, { title: this.file.filename });\n } else {\n // Fallback: open in new tab\n window.open(this.file.url, '_blank');\n }\n } else {\n window.open(this.file.url, '_blank');\n }\n }\n}\n\nexport default FilePreviewView;\n","import View from '@core/View.js';\nimport FilePreviewView from '@core/views/data/FilePreviewView.js';\n\n/**\n * ChatMessageView - Individual message display with theme support\n * \n * Supports two themes:\n * - 'compact': List-based admin/activity feed style\n * - 'bubbles': Chat bubble style with left/right positioning\n */\nclass ChatMessageView extends View {\n constructor(options = {}) {\n super({\n className: 'chat-message',\n ...options\n });\n \n this.message = options.message || {};\n this.theme = options.theme || 'compact';\n this.isCurrentUser = options.isCurrentUser || false;\n \n // Add theme-specific class\n if (this.theme === 'bubbles') {\n this.className += this.isCurrentUser ? ' message-right' : ' message-left';\n }\n }\n\n getTemplate() {\n // System event messages (same for both themes)\n if (this.message.type === 'system_event') {\n return `\n <div class=\"chat-message-system text-center text-muted small py-2\">\n <i class=\"bi bi-info-circle me-1\"></i>\n {{message.content}} on {{message.timestamp|datetime}}\n </div>\n `;\n }\n\n // Theme-specific templates\n if (this.theme === 'bubbles') {\n return this.getBubblesTemplate();\n } else {\n return this.getCompactTemplate();\n }\n }\n\n /**\n * Get compact theme template (Option 4 - Admin/Activity Feed Style)\n */\n getCompactTemplate() {\n const userClass = this.isCurrentUser ? 'bg-primary' : 'bg-secondary';\n \n return `\n <div class=\"message-item\">\n <div class=\"message-avatar ${userClass}\">\n {{#message.author.avatarUrl}}\n <img src=\"{{message.author.avatarUrl}}\" alt=\"{{message.author.name}}\" class=\"w-100 h-100 rounded-circle\">\n {{/message.author.avatarUrl}}\n {{^message.author.avatarUrl}}\n {{message.author.name|initials}}\n {{/message.author.avatarUrl}}\n </div>\n <div class=\"message-content\">\n <div class=\"message-header\">\n <div class=\"message-author\">\n {{message.author.name}}\n {{#isCurrentUser}}\n <span class=\"badge bg-primary badge-sm ms-1\">You</span>\n {{/isCurrentUser}}\n </div>\n <div class=\"message-time text-muted\">{{message.timestamp|relative}}</div>\n </div>\n <div class=\"message-text\">{{{message.content}}}</div>\n <div data-container=\"attachments\"></div>\n </div>\n </div>\n `;\n }\n\n /**\n * Get bubbles theme template (Option 1 - Modern Chat Bubbles)\n */\n getBubblesTemplate() {\n return `\n <div class=\"message-bubble-wrapper\">\n <div class=\"message-meta\">\n <strong>{{message.author.name}}</strong>\n <span class=\"text-muted\">· {{message.timestamp|relative}}</span>\n </div>\n <div class=\"message-bubble\">\n <div class=\"message-text\">{{{message.content}}}</div>\n <div data-container=\"attachments\"></div>\n </div>\n </div>\n `;\n }\n\n async onAfterRender() {\n // Render attachments if any\n if (this.message.attachments && this.message.attachments.length > 0) {\n const attachmentsContainer = this.element.querySelector('[data-container=\"attachments\"]');\n if (attachmentsContainer) {\n this.message.attachments.forEach(file => {\n const filePreview = new FilePreviewView({ file });\n this.addChild(filePreview);\n filePreview.render(true, attachmentsContainer);\n });\n }\n }\n }\n}\n\nexport default ChatMessageView;\n","import View from '@core/View.js';\nimport applyFileDropMixin from '@core/mixins/FileDropMixin.js';\nimport { File } from '@core/models/Files.js';\n\n/**\n * ChatInputView - Input area with file drop support and attachment preview\n */\nclass ChatInputView extends View {\n constructor(options = {}) {\n super({\n className: 'chat-input-view',\n ...options\n });\n\n this.placeholder = options.placeholder || 'Type a message...';\n this.buttonText = options.buttonText || 'Send';\n this.attachments = []; // Array of uploaded file data\n this.pendingUploads = new Map(); // Track in-progress uploads\n }\n\n getTemplate() {\n return `\n <div class=\"chat-input-container\">\n <div class=\"chat-input-attachments\" data-container=\"attachments\"></div>\n <div class=\"chat-input-wrapper\">\n <textarea\n class=\"chat-input form-control\"\n placeholder=\"${this.placeholder}\"\n rows=\"2\"></textarea>\n <button class=\"chat-send-btn btn btn-primary\" data-action=\"send-message\" type=\"button\">\n <i class=\"bi bi-send-fill\"></i>\n <span class=\"spinner-border spinner-border-sm d-none\" role=\"status\" aria-hidden=\"true\"></span>\n </button>\n </div>\n <div class=\"chat-input-footer\">\n <small class=\"text-muted\">\n <i class=\"bi bi-paperclip\"></i>\n Drag & drop files to attach\n </small>\n </div>\n </div>\n `;\n }\n\n async onAfterRender() {\n // Enable file drop on the entire input container\n this.enableFileDrop({\n dropZoneSelector: '.chat-input-container',\n multiple: true,\n acceptedTypes: ['*/*'], // Accept all file types\n visualFeedback: true,\n dragOverClass: 'drag-over',\n dragActiveClass: 'drag-active'\n });\n\n // Auto-resize textarea as user types and handle Enter key\n const textarea = this.element.querySelector('.chat-input');\n if (textarea) {\n textarea.addEventListener('input', () => this.autoResizeTextarea(textarea));\n textarea.addEventListener('keydown', (e) => this.handleKeydown(e));\n }\n }\n\n /**\n * Handle textarea keydown (send on Enter without Shift)\n */\n handleKeydown(event) {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n this.onActionSendMessage(event, event.target);\n }\n }\n\n /**\n * Handle file drop\n * @param {File[]} files - Dropped files\n */\n async onFileDrop(files) {\n for (const file of files) {\n await this.uploadFile(file);\n }\n }\n\n /**\n * Upload a file\n * @param {File} file - File to upload\n */\n async uploadFile(file) {\n const fileModel = new File();\n const uploadId = Date.now() + Math.random();\n\n // Add preview immediately\n this.addFilePreview(uploadId, file, 0);\n this.pendingUploads.set(uploadId, { file, fileModel });\n\n try {\n const result = await fileModel.upload({\n file: file,\n onProgress: (progress) => {\n this.updateFileProgress(uploadId, progress);\n },\n onComplete: (uploadResult) => {\n this.handleUploadComplete(uploadId, fileModel);\n }\n });\n\n } catch (error) {\n console.error('File upload failed:', error);\n this.handleUploadError(uploadId, error);\n }\n }\n\n /**\n * Add file preview to UI\n * @param {string} uploadId - Unique upload ID\n * @param {File} file - File object\n * @param {number} progress - Upload progress (0-100)\n */\n addFilePreview(uploadId, file, progress) {\n const container = this.element.querySelector('[data-container=\"attachments\"]');\n if (!container) return;\n\n const preview = document.createElement('div');\n preview.className = 'attachment-preview';\n preview.dataset.uploadId = uploadId;\n preview.innerHTML = `\n <div class=\"attachment-info\">\n <i class=\"bi bi-file-earmark\"></i>\n <span class=\"attachment-name\">${this.escapeHtml(file.name)}</span>\n <span class=\"attachment-size\">(${this.formatFileSize(file.size)})</span>\n </div>\n <div class=\"attachment-progress\">\n <div class=\"progress\" style=\"height: 4px;\">\n <div class=\"progress-bar\" role=\"progressbar\" style=\"width: ${progress}%\"></div>\n </div>\n </div>\n <button class=\"attachment-remove btn btn-sm btn-link text-danger\" data-action=\"remove-attachment\" data-upload-id=\"${uploadId}\" type=\"button\">\n <i class=\"bi bi-x\"></i>\n </button>\n `;\n\n container.appendChild(preview);\n }\n\n /**\n * Update file upload progress\n * @param {string} uploadId - Upload ID\n * @param {number} progress - Progress (0-100)\n */\n updateFileProgress(uploadId, progress) {\n const preview = this.element.querySelector(`[data-upload-id=\"${uploadId}\"]`);\n if (preview) {\n const progressBar = preview.querySelector('.progress-bar');\n if (progressBar) {\n progressBar.style.width = `${progress}%`;\n }\n }\n }\n\n /**\n * Handle upload completion\n * @param {string} uploadId - Upload ID\n * @param {Object} result - Upload result data (contains file.id)\n */\n handleUploadComplete(uploadId, fileModel) {\n // Store the file data with its ID\n this.attachments.push({\n id: fileModel.id,\n name: fileModel.get(\"name\"),\n uploadId: uploadId\n });\n this.pendingUploads.delete(uploadId);\n\n const preview = this.element.querySelector(`[data-upload-id=\"${uploadId}\"]`);\n if (preview) {\n preview.classList.add('upload-complete');\n const progressContainer = preview.querySelector('.attachment-progress');\n if (progressContainer) {\n progressContainer.remove();\n }\n }\n }\n\n /**\n * Handle upload error\n * @param {string} uploadId - Upload ID\n * @param {Error} error - Error object\n */\n handleUploadError(uploadId, error) {\n this.pendingUploads.delete(uploadId);\n\n const preview = this.element.querySelector(`[data-upload-id=\"${uploadId}\"]`);\n if (preview) {\n preview.classList.add('upload-error');\n preview.querySelector('.attachment-info').innerHTML +=\n `<span class=\"text-danger ms-2\">Upload failed</span>`;\n }\n }\n\n /**\n * Remove attachment\n */\n async onActionRemoveAttachment(event, element) {\n const uploadId = element.dataset.uploadId;\n\n // Remove from pending uploads\n this.pendingUploads.delete(uploadId);\n\n // Remove from completed attachments\n const preview = this.element.querySelector(`[data-upload-id=\"${uploadId}\"]`);\n if (preview) {\n // TODO: Get the file ID from the preview and remove from attachments array\n preview.remove();\n }\n }\n\n\n\n /**\n * Send message\n */\n async onActionSendMessage(event, element) {\n const textarea = this.element.querySelector('.chat-input');\n const text = textarea.value.trim();\n\n // Don't send if empty and no attachments\n if (!text && this.attachments.length === 0) {\n return;\n }\n\n // Don't send if uploads are pending\n if (this.pendingUploads.size > 0) {\n // TODO: Show message that uploads are in progress\n return;\n }\n\n // Show busy state\n this.setBusy(true);\n\n // Emit event with message data\n this.emit('message:send', {\n text: text,\n files: this.attachments\n });\n\n // Note: Don't clear here - let the parent ChatView call clearInput() after successful send\n }\n\n /**\n * Set busy state (show/hide spinner)\n * @param {boolean} busy - Whether to show busy state\n */\n setBusy(busy) {\n const button = this.element.querySelector('.chat-send-btn');\n const icon = button.querySelector('.bi-send-fill');\n const spinner = button.querySelector('.spinner-border');\n\n if (busy) {\n button.disabled = true;\n icon.classList.add('d-none');\n spinner.classList.remove('d-none');\n } else {\n button.disabled = false;\n icon.classList.remove('d-none');\n spinner.classList.add('d-none');\n }\n }\n\n /**\n * Clear input and attachments\n */\n clearInput() {\n const textarea = this.element.querySelector('.chat-input');\n if (textarea) {\n textarea.value = '';\n textarea.style.height = 'auto';\n }\n\n const container = this.element.querySelector('[data-container=\"attachments\"]');\n if (container) {\n container.innerHTML = '';\n }\n\n this.attachments = [];\n this.pendingUploads.clear();\n \n // Reset busy state\n this.setBusy(false);\n }\n\n /**\n * Auto-resize textarea based on content\n * @param {HTMLTextAreaElement} textarea\n */\n autoResizeTextarea(textarea) {\n textarea.style.height = 'auto';\n textarea.style.height = Math.min(textarea.scrollHeight, 150) + 'px';\n }\n\n /**\n * Format file size for display\n * @param {number} bytes\n * @returns {string}\n */\n formatFileSize(bytes) {\n if (bytes === 0) return '0 B';\n const k = 1024;\n const sizes = ['B', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];\n }\n\n /**\n * Escape HTML to prevent XSS\n * @param {string} text\n * @returns {string}\n */\n escapeHtml(text) {\n const div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML;\n }\n}\n\n// Apply FileDropMixin\napplyFileDropMixin(ChatInputView);\n\nexport default ChatInputView;\n","import View from '@core/View.js';\nimport ChatMessageView from './ChatMessageView.js';\nimport ChatInputView from './ChatInputView.js';\n\n/**\n * ChatView - Modern chat interface with theme support\n * \n * Themes:\n * - 'compact' (default): Admin/activity feed style, list-based layout\n * - 'bubbles': Modern chat bubbles with left/right positioning\n * \n * Usage:\n * const chat = new ChatView({\n * adapter: myAdapter,\n * theme: 'compact', // or 'bubbles'\n * currentUserId: 123,\n * inputPlaceholder: 'Add a comment...',\n * inputButtonText: 'Send'\n * });\n */\nclass ChatView extends View {\n constructor(options = {}) {\n super({\n className: 'chat-view',\n ...options\n });\n \n this.adapter = options.adapter;\n this.theme = options.theme || 'compact'; // 'compact' or 'bubbles'\n this.currentUserId = options.currentUserId;\n this.inputPlaceholder = options.inputPlaceholder || 'Type a message...';\n this.inputButtonText = options.inputButtonText || 'Send';\n this.messages = [];\n this.messageViews = new Map(); // Track message views by ID\n }\n\n getTemplate() {\n return `\n <div class=\"chat-container chat-theme-${this.theme}\">\n <div class=\"chat-messages\" data-container=\"messages\"></div>\n <div class=\"chat-input-wrapper\" data-container=\"input\"></div>\n </div>\n `;\n }\n\n async onInit() {\n // Initial fetch of messages\n this.messages = await this.adapter.fetch();\n \n // Create input view\n this.inputView = new ChatInputView({\n containerId: 'input',\n placeholder: this.inputPlaceholder,\n buttonText: this.inputButtonText\n });\n this.addChild(this.inputView);\n \n // Listen for new messages\n this.inputView.on('message:send', async (data) => {\n await this.handleSendMessage(data);\n });\n }\n\n async onAfterRender() {\n // Build message views\n this._buildMessageViews();\n \n // Render children (like ListView does)\n await this._renderChildren();\n \n // Scroll to bottom\n this.scrollToBottom();\n }\n\n /**\n * Render child message views (similar to ListView._renderChildren)\n * @private\n */\n async _renderChildren() {\n await super._renderChildren();\n const messagesContainer = this.element.querySelector('[data-container=\"messages\"]');\n if (!messagesContainer) {\n console.error('ChatView: messages container not found');\n return;\n }\n \n // Append each message view to the container and render it\n this.messageViews.forEach((messageView) => {\n messagesContainer.appendChild(messageView.element);\n messageView.render(false);\n });\n }\n\n /**\n * Build message views for all messages (similar to ListView._buildItems)\n * @private\n */\n _buildMessageViews() {\n if (!this.messages || this.messages.length === 0) return;\n \n this.messages.forEach(message => {\n if (!this.messageViews.has(message.id)) {\n this._createMessageView(message);\n }\n });\n }\n\n /**\n * Create a message view (similar to ListView._createItemView)\n * @private\n */\n _createMessageView(message) {\n if (this.messageViews.has(message.id)) return;\n \n const isCurrentUser = message.author && message.author.id === this.currentUserId;\n \n const messageView = new ChatMessageView({\n message: message,\n theme: this.theme,\n isCurrentUser: isCurrentUser\n });\n \n this.addChild(messageView);\n this.messageViews.set(message.id, messageView);\n \n return messageView;\n }\n\n /**\n * Add a new message to the chat (for real-time updates)\n * @param {Object} message - Message data\n * @param {boolean} scroll - Whether to scroll to bottom after adding\n */\n addMessage(message, scroll = true) {\n if (this.messageViews.has(message.id)) return;\n \n const messageView = this._createMessageView(message);\n \n // If already rendered, append to DOM immediately\n if (this.isMounted()) {\n const messagesContainer = this.element.querySelector('[data-container=\"messages\"]');\n if (messagesContainer) {\n messagesContainer.appendChild(messageView.element);\n messageView.render(false);\n }\n }\n \n if (scroll) {\n this.scrollToBottom();\n }\n }\n\n /**\n * Handle sending a new message\n * @param {Object} data - Message data {text, files}\n * @private\n */\n async handleSendMessage(data) {\n try {\n // If there's text, send it as a note\n if (data.text && data.text.trim()) {\n const result = await this.adapter.addNote({\n text: data.text,\n files: data.files && data.files.length > 0 ? [data.files[0]] : []\n });\n \n if (!result.success) {\n throw new Error('Failed to send message');\n }\n }\n \n // If there are multiple files, or files without text, create a note for each\n const startIndex = (data.text && data.text.trim() && data.files.length > 0) ? 1 : 0;\n \n for (let i = startIndex; i < (data.files?.length || 0); i++) {\n const file = data.files[i];\n const result = await this.adapter.addNote({\n text: '', // Empty text, just the file\n files: [file]\n });\n \n if (!result.success) {\n console.error('Failed to upload file:', file);\n }\n }\n \n // Fetch updated messages\n this.messages = await this.adapter.fetch();\n \n // Find the new message(s) and add them\n this.messages.forEach(message => {\n if (!this.messageViews.has(message.id)) {\n this.addMessage(message, true);\n }\n });\n \n // Clear input (this also resets busy state)\n this.inputView.clearInput();\n \n } catch (error) {\n console.error('Failed to send message:', error);\n // Reset busy state on error\n this.inputView.setBusy(false);\n // TODO: Show error feedback to user\n }\n }\n\n /**\n * Scroll chat to bottom\n */\n scrollToBottom() {\n const container = this.element.querySelector('.chat-messages');\n if (container) {\n requestAnimationFrame(() => {\n container.scrollTop = container.scrollHeight;\n });\n }\n }\n\n /**\n * Clear all messages\n */\n clearMessages() {\n this.messageViews.forEach(view => view.destroy());\n this.messageViews.clear();\n this.messages = [];\n \n const container = this.element.querySelector('[data-container=\"messages\"]');\n if (container) {\n container.innerHTML = '';\n }\n }\n\n /**\n * Refresh messages from adapter\n */\n async refresh() {\n this.clearMessages();\n this.messages = await this.adapter.fetch();\n this._buildMessageViews();\n \n if (this.isMounted()) {\n await this._renderChildren();\n this.scrollToBottom();\n }\n }\n}\n\nexport default ChatView;\n"],"names":["S3Bucket","Model","constructor","data","super","endpoint","S3BucketList","Collection","options","ModelClass","size","S3BucketForms","create","title","fields","name","type","label","placeholder","help","required","cols","edit","EmailDomain","onboard","this","id","showError","success","status","error","url","buildUrl","rest","POST","params","err","message","audit","method","toUpperCase","GET","reconcile","onboardById","model","auditById","reconcileById","EmailDomainList","EmailDomainForms","columns","readonly","value","text","credentials","level","className","Mailbox","sendEmail","async","MailboxList","MailboxForms","labelField","valueField","maxItems","emptyFetch","debounceMs","defaultValue","SentMessage","SentMessageList","SentMessageForms","view","rows","EmailTemplate","EmailTemplateList","EmailTemplateForms","tabs","IncidentStats","requiresId","IncidentEvent","IncidentEventList","IncidentEventForms","Incident","COMPONENTS","editable","force_top","IncidentList","IncidentForms","example","IncidentRuleSet","IncidentRuleSetList","IncidentRule","IncidentRuleList","IncidentHistory","IncidentHistoryList","BundleByOptions","MatchByOptions","ComparatorOptions","ValueTypeOptions","CommonEventFields","description","meta","CommonScopeOptions","RuleSet","RuleSetList","RuleSetForms","Rule","RuleList","RuleForms","allowCustom","showDescription","start","end","step","EDIT_FORM","ADD_FORM","CREATE_FORM","Job","cancel","response","save","cancel_request","set","retry","delay","retry_request","new_job_id","newJobId","originalJobId","original_job_id","getDetailedStatus","get_status","cloneJob","newPayload","publishData","publish_job","payload","job_id","templateJobId","template_job_id","isActive","get","includes","isTerminal","canRetry","canCancel","getStatusBadgeClass","pending","running","completed","failed","canceled","expired","getStatusIcon","getEvents","getFormattedDuration","duration","toFixed","getQueuePosition","hasExpired","expiresAt","Date","getRunnerId","getPayload","getMetadata","JobList","fetchByStatus","fetch","fetchByChannel","channel","fetchPending","fetchRunning","fetchCompleted","fetchFailed","fetchScheduled","scheduled","JobForms","publish","min","max","clone","jobData","getStats","getHealth","test","tests","clearStuck","clearChannel","confirm","cleanConsumers","purgeJobs","days_old","JobLog","JobLogList","JobEvent","JobEventList","JobsEngineStats","JobRunner","runner_id","idAttribute","ping","timeout","app","getApp","Error","toISOString","ping_status","shutdown","graceful","getChannels","isHealthy","lastHeartbeat","now","getTime","getFormattedHeartbeatAge","heartbeatAge","Math","round","getUtilization","total","getFormattedUptime","started","startTime","diffMs","diffSeconds","floor","getWorkerInfo","processed","alive","utilization","getDisplayName","runnerId","parts","split","length","canControl","JobRunnerList","getActive","where","runner","getByChannel","getHealthy","getTotalProcessed","models","reduce","getTotalFailed","getSystemHealth","aliveRunners","filter","getAllChannels","channels","Set","forEach","add","Array","from","sort","window","broadcast","command","broadcastStatus","broadcastShutdown","broadcastPause","broadcastResume","broadcastReload","JobRunnerForms","MetricsPermission","id_key","MetricsPermissionList","MetricsForms","PushDevice","PushDeviceList","PushTemplate","PushTemplateList","PushConfig","PushConfigList","PushDelivery","PushDeliveryList","PushConfigForms","GroupList","PushTemplateForms","defaultParams","is_active","GeoIPForms","GeoLocatedIP","lookup","ip","resp","EDIT_LOCATION_FORM","EDIT_SECURITY_FORM","EDIT_NETWORK_FORM","GeoLocatedIPList","Ticket","TicketList","TicketCategories","ticket","bug","feature","incident","security","fulfillment","new_user","new_group","qa","TicketCategoriesOptions","Object","entries","map","key","TicketForms","UserList","TicketNote","TicketNoteList","TablePage","Page","pageName","collection","defaultQuery","groupField","tableViewConfig","actions","contextMenu","batchActions","batchBarLocation","clickAction","addForm","formFields","formCreate","editForm","formEdit","itemView","deleteTemplate","formDialogConfig","viewDialogOptions","searchable","sortable","filterable","paginated","selectionMode","selectable","filters","additionalFilters","hideActivePills","hideActivePillNames","searchPlacement","tableOptions","striped","bordered","hover","responsive","emptyMessage","searchPlaceholder","showAdd","showExport","onItemView","onItemEdit","onItemDelete","onAdd","onExport","tableViewOptions","urlSyncEnabled","lastUpdated","isLoading","template","buildTemplate","onInit","applyQueryToCollection","tableView","TableView","containerId","fetchOnMount","addChild","setupEventListeners","on","toLocaleTimeString","updateStatusDisplay","syncUrl","handleFilterEdit","event","query","keys","parseInt","search","reservedParams","startsWith","JSON","parse","e","field","parseFilterKey","hasOwnProperty","setParams","force","router","currentUrl","URL","location","currentParams","searchParams","desiredParams","collectionParams","stringify","hasChanges","some","String","updateBrowserUrl","element","updatedElement","querySelector","textContent","countElement","count","onEnter","requiresGroup","activeGroup","setTimeout","updateFilterPills","updateSortIcons","refresh","getSelectedItems","clearSelection","filterKey","filterConfig","getAllAvailableFilters","find","f","currentValue","config","result","Dialog","showForm","filter_value","setFilter","restEnabled","render","clearAllFilters","onGroupChange","group","onBeforeDestroy","off","showStatus","TabView","View","activeTab","tabsClass","contentClass","minWidth","enableResponsive","tabPadding","dropdownStyle","enableTransitions","transitionDuration","viewOptions","tagName","tabLabels","currentMode","tabWidthCache","Map","lastContainerWidth","resizeObserver","_measurementSpan","_tabComputedStyle","isMobileMode","hasOverflow","addTab","handleResize","bind","renderTemplate","buildTabNavigation","buildTabContent","buildDropdownNavigation","buildTabsNavigation","tabItems","tabId","getTabId","escapeHtml","join","activeLabel","dropdownItems","buttonHtml","buildMobileDropdownNavigation","shouldUseMobileDropdown","enableMobileDropdown","viewportWidth","innerWidth","mobileBreakpoint","tabPanes","toLowerCase","replace","showTab","tabLabel","console","warn","activeView","isMounted","contains","previousTab","updateTabNavigation","updateTabContent","emit","activeTabLabel","previousTabLabel","reRenderNavigation","prevTabButton","classList","remove","setAttribute","activeTabButton","activeTabId","previousTabId","activePane","prevPane","Promise","resolve","_getTransitionDuration","container","offsetHeight","onTabActivated","getComputedStyle","matches","match","parseFloat","onActionShowTab","getAttribute","_initializeMeasurementStyles","tabButton","style","font","letterSpacing","paddingLeft","paddingRight","onAfterRender","setupResponsiveHandling","onAfterMount","disconnect","removeEventListener","parentElement","removeChild","destroy","getActiveTab","getTabLabels","getTab","makeActive","permissions","activeUser","hasPerm","parent","removeTab","calculateTabWidth","has","document","estimatedWidth","createElement","visibility","position","whiteSpace","span","fontSize","fontFamily","body","appendChild","width","offsetWidth","getTotalTabWidth","getContainerWidth","shouldUseDropdown","containerWidth","totalTabWidth","updateNavigationMode","ResizeObserver","observe","addEventListener","abs","newMode","mode","navigationContainer","newNavigation","outerHTML","getNavigationMode","setNavigationMode","clearWidthCache","clear","FilePreviewView","file","isImage","content_type","isPdf","getTemplate","thumbnailUrl","onActionViewFile","LightboxGallery","MOJO","plugins","show","src","alt","filename","open","PDFViewer","showDialog","ChatMessageView","theme","isCurrentUser","getBubblesTemplate","getCompactTemplate","attachments","attachmentsContainer","filePreview","ChatInputView","buttonText","pendingUploads","enableFileDrop","dropZoneSelector","multiple","acceptedTypes","visualFeedback","dragOverClass","dragActiveClass","textarea","autoResizeTextarea","handleKeydown","shiftKey","preventDefault","onActionSendMessage","target","onFileDrop","files","uploadFile","fileModel","File","uploadId","random","addFilePreview","upload","onProgress","progress","updateFileProgress","onComplete","uploadResult","handleUploadComplete","handleUploadError","preview","dataset","innerHTML","formatFileSize","progressBar","push","delete","progressContainer","onActionRemoveAttachment","trim","setBusy","busy","button","icon","spinner","disabled","clearInput","height","scrollHeight","bytes","i","log","pow","div","applyFileDropMixin","ChatView","adapter","currentUserId","inputPlaceholder","inputButtonText","messages","messageViews","inputView","handleSendMessage","_buildMessageViews","_renderChildren","scrollToBottom","messagesContainer","messageView","_createMessageView","author","addMessage","scroll","addNote","requestAnimationFrame","scrollTop","clearMessages"],"mappings":"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"}