web-mojo 2.4.3 → 2.4.5

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 (144) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/admin-models.cjs.js +1 -1
  3. package/dist/admin-models.cjs.js.map +1 -1
  4. package/dist/admin-models.es.js +1 -1
  5. package/dist/admin-models.es.js.map +1 -1
  6. package/dist/admin.cjs.js +1 -1
  7. package/dist/admin.css +258 -6
  8. package/dist/admin.es.js +1 -1
  9. package/dist/auth.cjs.js +1 -1
  10. package/dist/auth.es.js +1 -1
  11. package/dist/charts.cjs.js +1 -1
  12. package/dist/charts.css +49 -0
  13. package/dist/charts.es.js +1 -1
  14. package/dist/chat.css +5 -0
  15. package/dist/chunks/AssistantPanelView-C4ZyIQoH.js +2 -0
  16. package/dist/chunks/AssistantPanelView-C4ZyIQoH.js.map +1 -0
  17. package/dist/chunks/AssistantPanelView-DlBgWeM_.js +2 -0
  18. package/dist/chunks/AssistantPanelView-DlBgWeM_.js.map +1 -0
  19. package/dist/chunks/{ChatView-B0xHv7Nq.js → ChatView-CcR1GR30.js} +2 -2
  20. package/dist/chunks/{ChatView-CUmRTKna.js.map → ChatView-CcR1GR30.js.map} +1 -1
  21. package/dist/chunks/{ChatView-CUmRTKna.js → ChatView-ChyTcmrd.js} +2 -2
  22. package/dist/chunks/{ChatView-B0xHv7Nq.js.map → ChatView-ChyTcmrd.js.map} +1 -1
  23. package/dist/chunks/{Collection-7F3lsq4z.js → Collection-C0pHSKDH.js} +2 -2
  24. package/dist/chunks/{Collection-7F3lsq4z.js.map → Collection-C0pHSKDH.js.map} +1 -1
  25. package/dist/chunks/{Collection-Cgxbmj8G.js → Collection-DNmr743A.js} +2 -2
  26. package/dist/chunks/{Collection-Cgxbmj8G.js.map → Collection-DNmr743A.js.map} +1 -1
  27. package/dist/chunks/{ContextMenu-afYD0lFk.js → ContextMenu-BeveGkJr.js} +2 -2
  28. package/dist/chunks/{ContextMenu-afYD0lFk.js.map → ContextMenu-BeveGkJr.js.map} +1 -1
  29. package/dist/chunks/{ContextMenu-Ba4fjHxg.js → ContextMenu-T3yDdsIe.js} +2 -2
  30. package/dist/chunks/{ContextMenu-Ba4fjHxg.js.map → ContextMenu-T3yDdsIe.js.map} +1 -1
  31. package/dist/chunks/{DataView-DFGIE3wK.js → DataView-VZIXSsZa.js} +2 -2
  32. package/dist/chunks/{DataView-DFGIE3wK.js.map → DataView-VZIXSsZa.js.map} +1 -1
  33. package/dist/chunks/{DataView-fA6qQbvN.js → DataView-z2rxXk4L.js} +2 -2
  34. package/dist/chunks/{DataView-fA6qQbvN.js.map → DataView-z2rxXk4L.js.map} +1 -1
  35. package/dist/chunks/{FormView-CgnaTPkQ.js → FormView-COIPtbrd.js} +2 -2
  36. package/dist/chunks/{FormView-CgnaTPkQ.js.map → FormView-COIPtbrd.js.map} +1 -1
  37. package/dist/chunks/{FormView-LRb8scDI.js → FormView-DUXQruUZ.js} +2 -2
  38. package/dist/chunks/{FormView-LRb8scDI.js.map → FormView-DUXQruUZ.js.map} +1 -1
  39. package/dist/chunks/{ListView-Dsezts8J.js → ListView-BjsNHuZ1.js} +2 -2
  40. package/dist/chunks/{ListView-Dsezts8J.js.map → ListView-BjsNHuZ1.js.map} +1 -1
  41. package/dist/chunks/{ListView-Cu6iQ0aG.js → ListView-BxcxIwC3.js} +2 -2
  42. package/dist/chunks/{ListView-Cu6iQ0aG.js.map → ListView-BxcxIwC3.js.map} +1 -1
  43. package/dist/chunks/{MetricsCountryMapView-BsJoEsUE.js → MetricsCountryMapView-Bp3qoVHp.js} +2 -2
  44. package/dist/chunks/{MetricsCountryMapView-BsJoEsUE.js.map → MetricsCountryMapView-Bp3qoVHp.js.map} +1 -1
  45. package/dist/chunks/{MetricsCountryMapView-BkLDonK4.js → MetricsCountryMapView-CWjIEBJB.js} +2 -2
  46. package/dist/chunks/{MetricsCountryMapView-BkLDonK4.js.map → MetricsCountryMapView-CWjIEBJB.js.map} +1 -1
  47. package/dist/chunks/Modal-Bm1OQ8Ou.js +3 -0
  48. package/dist/chunks/{Modal-BRKy85bz.js.map → Modal-Bm1OQ8Ou.js.map} +1 -1
  49. package/dist/chunks/Modal-KnJhNZ1E.js +3 -0
  50. package/dist/chunks/{Modal-DKjxtaZI.js.map → Modal-KnJhNZ1E.js.map} +1 -1
  51. package/dist/chunks/Passkeys-DJn9yy-0.js +2 -0
  52. package/dist/chunks/Passkeys-DJn9yy-0.js.map +1 -0
  53. package/dist/chunks/Passkeys-WWAg5tKf.js +2 -0
  54. package/dist/chunks/Passkeys-WWAg5tKf.js.map +1 -0
  55. package/dist/chunks/TicketPanelView-DeeANyKv.js +2 -0
  56. package/dist/chunks/TicketPanelView-DeeANyKv.js.map +1 -0
  57. package/dist/chunks/TicketPanelView-if4ogL_D.js +2 -0
  58. package/dist/chunks/TicketPanelView-if4ogL_D.js.map +1 -0
  59. package/dist/chunks/{TokenManager-CWRL33UM.js → TokenManager-6atX9uKB.js} +2 -2
  60. package/dist/chunks/TokenManager-6atX9uKB.js.map +1 -0
  61. package/dist/chunks/{TokenManager-CVR3ENIS.js → TokenManager-CiNtJclo.js} +2 -2
  62. package/dist/chunks/TokenManager-CiNtJclo.js.map +1 -0
  63. package/dist/chunks/User-9qvKV7G6.js +2 -0
  64. package/dist/chunks/User-9qvKV7G6.js.map +1 -0
  65. package/dist/chunks/{User-BmS8zImI.js → User-BM76Ughk.js} +2 -2
  66. package/dist/chunks/User-BM76Ughk.js.map +1 -0
  67. package/dist/chunks/{UserProfileView-CrmACQjJ.js → UserProfileView-BPjz7uIp.js} +2 -2
  68. package/dist/chunks/{UserProfileView-CrmACQjJ.js.map → UserProfileView-BPjz7uIp.js.map} +1 -1
  69. package/dist/chunks/{UserProfileView-B56WeGL1.js → UserProfileView-udbSWe1S.js} +2 -2
  70. package/dist/chunks/{UserProfileView-B56WeGL1.js.map → UserProfileView-udbSWe1S.js.map} +1 -1
  71. package/dist/chunks/{View-gAghvPMN.js → View-BxlKR1IW.js} +2 -2
  72. package/dist/chunks/{View-gAghvPMN.js.map → View-BxlKR1IW.js.map} +1 -1
  73. package/dist/chunks/{View-IgBQlwd0.js → View-CPWwS19u.js} +2 -2
  74. package/dist/chunks/{View-IgBQlwd0.js.map → View-CPWwS19u.js.map} +1 -1
  75. package/dist/chunks/{WebApp-B6wrmIaj.js → WebApp-BdxhRnnT.js} +2 -2
  76. package/dist/chunks/WebApp-BdxhRnnT.js.map +1 -0
  77. package/dist/chunks/WebApp-Bo_egO0c.js +2 -0
  78. package/dist/chunks/WebApp-Bo_egO0c.js.map +1 -0
  79. package/dist/chunks/admin-C3-HqQvC.js +2 -0
  80. package/dist/chunks/admin-C3-HqQvC.js.map +1 -0
  81. package/dist/chunks/admin-DrFBTXnB.js +2 -0
  82. package/dist/chunks/admin-DrFBTXnB.js.map +1 -0
  83. package/dist/chunks/admin-models-CkHjtMHf.js +2 -0
  84. package/dist/chunks/admin-models-CkHjtMHf.js.map +1 -0
  85. package/dist/chunks/admin-models-DLtFboxy.js +2 -0
  86. package/dist/chunks/admin-models-DLtFboxy.js.map +1 -0
  87. package/dist/chunks/exportChart-BTrEOM9j.js +2 -0
  88. package/dist/chunks/exportChart-BTrEOM9j.js.map +1 -0
  89. package/dist/chunks/exportChart-BfzZUb1j.js +2 -0
  90. package/dist/chunks/exportChart-BfzZUb1j.js.map +1 -0
  91. package/dist/chunks/{index-NXnA6T-5.js → index-Byf1pa3Z.js} +2 -2
  92. package/dist/chunks/{index-NXnA6T-5.js.map → index-Byf1pa3Z.js.map} +1 -1
  93. package/dist/chunks/{index-DTXotoXw.js → index-dFX91Gwz.js} +2 -2
  94. package/dist/chunks/{index-DTXotoXw.js.map → index-dFX91Gwz.js.map} +1 -1
  95. package/dist/chunks/{version-BEKxO75G.js → version-C9vC8_Oy.js} +2 -2
  96. package/dist/chunks/{version-BEKxO75G.js.map → version-C9vC8_Oy.js.map} +1 -1
  97. package/dist/chunks/{version-LBA3E61x.js → version-CjNfmLdK.js} +2 -2
  98. package/dist/chunks/{version-LBA3E61x.js.map → version-CjNfmLdK.js.map} +1 -1
  99. package/dist/css/web-mojo.css +1 -1
  100. package/dist/docit.cjs.js +1 -1
  101. package/dist/docit.es.js +1 -1
  102. package/dist/index.cjs.js +1 -1
  103. package/dist/index.cjs.js.map +1 -1
  104. package/dist/index.es.js +1 -1
  105. package/dist/index.es.js.map +1 -1
  106. package/dist/lightbox.cjs.js +1 -1
  107. package/dist/lightbox.es.js +1 -1
  108. package/dist/map.cjs.js +1 -1
  109. package/dist/map.es.js +1 -1
  110. package/dist/timeline.cjs.js +1 -1
  111. package/dist/timeline.es.js +1 -1
  112. package/dist/user-profile.cjs.js +1 -1
  113. package/dist/user-profile.es.js +1 -1
  114. package/dist/web-mojo.lite.iife.js +20 -4
  115. package/dist/web-mojo.lite.iife.js.map +1 -1
  116. package/dist/web-mojo.lite.iife.min.js +8 -8
  117. package/dist/web-mojo.lite.iife.min.js.map +1 -1
  118. package/package.json +1 -1
  119. package/dist/chunks/AssistantPanelView-BWSQqGV9.js +0 -2
  120. package/dist/chunks/AssistantPanelView-BWSQqGV9.js.map +0 -1
  121. package/dist/chunks/AssistantPanelView-Bipbq_Ob.js +0 -2
  122. package/dist/chunks/AssistantPanelView-Bipbq_Ob.js.map +0 -1
  123. package/dist/chunks/Modal-BRKy85bz.js +0 -3
  124. package/dist/chunks/Modal-DKjxtaZI.js +0 -3
  125. package/dist/chunks/Passkeys-CMh9iSax.js +0 -2
  126. package/dist/chunks/Passkeys-CMh9iSax.js.map +0 -1
  127. package/dist/chunks/Passkeys-DF7mRGYj.js +0 -2
  128. package/dist/chunks/Passkeys-DF7mRGYj.js.map +0 -1
  129. package/dist/chunks/TokenManager-CVR3ENIS.js.map +0 -1
  130. package/dist/chunks/TokenManager-CWRL33UM.js.map +0 -1
  131. package/dist/chunks/User-BmS8zImI.js.map +0 -1
  132. package/dist/chunks/User-CayBjzMG.js +0 -2
  133. package/dist/chunks/User-CayBjzMG.js.map +0 -1
  134. package/dist/chunks/WebApp-B6wrmIaj.js.map +0 -1
  135. package/dist/chunks/WebApp-DeHPnmbD.js +0 -2
  136. package/dist/chunks/WebApp-DeHPnmbD.js.map +0 -1
  137. package/dist/chunks/admin-Cct4cQOQ.js +0 -2
  138. package/dist/chunks/admin-Cct4cQOQ.js.map +0 -1
  139. package/dist/chunks/admin-D8GKQBwN.js +0 -2
  140. package/dist/chunks/admin-D8GKQBwN.js.map +0 -1
  141. package/dist/chunks/exportChart-CF7VJ4fb.js +0 -2
  142. package/dist/chunks/exportChart-CF7VJ4fb.js.map +0 -1
  143. package/dist/chunks/exportChart-DVIvZLoP.js +0 -2
  144. package/dist/chunks/exportChart-DVIvZLoP.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TicketPanelView-DeeANyKv.js","sources":["../../src/extensions/admin/incidents/ActionCardView.js","../../src/extensions/admin/incidents/TicketPanelView.js"],"sourcesContent":["import View from '@core/View.js';\nimport Modal from '@core/views/feedback/Modal.js';\n\nfunction esc(s) {\n if (!s) return '';\n return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\n}\n\nconst ALLOWED_REFS = new Set([\n 'incident.RuleSet', 'incident.Incident', 'incident.Event',\n 'incident.Ticket', 'account.GeoLocatedIP'\n]);\n\nconst HANDLER_COLORS = {\n 'incident.rule_approval': { dot: 'accent', label: 'Rule' },\n 'incident.block_confirm': { dot: 'red', label: 'Block' },\n 'incident.rule_update': { dot: 'green', label: 'Update' },\n 'incident.escalate': { dot: 'amber', label: 'Escalate' }\n};\n\nclass ActionCardView extends View {\n constructor(options = {}) {\n super({\n className: 'action-card-view',\n ...options\n });\n this.action = options.action;\n this.noteId = options.noteId;\n this.ticketStatus = options.ticketStatus;\n }\n\n get isResolved() {\n return !!this.action?.resolved;\n }\n\n get isContext() {\n return this.action?.type === 'context';\n }\n\n get isClosed() {\n return this.ticketStatus === 'closed' || this.ticketStatus === 'resolved';\n }\n\n get handlerConfig() {\n return HANDLER_COLORS[this.action?.handler] || { dot: 'accent', label: 'Action' };\n }\n\n onBeforeRender() {\n const cfg = this.handlerConfig;\n this.dotClass = cfg.dot;\n\n if (this.isContext) {\n this._buildContextTemplate();\n } else if (this.isResolved) {\n this._buildResolvedTemplate();\n } else {\n this._buildPendingTemplate();\n }\n }\n\n onActionToggleCompact() {\n const acEl = this.element?.querySelector('.ac.resolved');\n if (acEl) acEl.classList.toggle('compact');\n }\n\n _buildContextTemplate() {\n const refs = (this.action.references || []).filter(ref => ALLOWED_REFS.has(ref.model));\n const refItems = refs.map(ref => {\n const label = esc(ref.label) || `${esc(ref.model.split('.').pop())} #${esc(ref.pk)}`;\n return `<span class=\"ac-ref\" data-action=\"open-ref\" data-model=\"${esc(ref.model)}\" data-pk=\"${esc(ref.pk)}\"><i class=\"bi bi-box-arrow-up-right\"></i>${label}</span>`;\n }).join('');\n\n this.template = `\n <div class=\"ac ac-context\">\n <div class=\"ac-top\">\n <span class=\"ac-dot context\"></span>\n <span class=\"ac-label\">Referenced models</span>\n </div>\n <div class=\"ac-detail\">${refItems}</div>\n </div>\n `;\n }\n\n _buildResolvedTemplate() {\n const resolution = this.action.resolution || 'approved';\n const badgeClass = resolution === 'approved' ? 'approved' : 'denied';\n const badgeText = resolution === 'approved' ? 'Approved' : 'Denied';\n const target = this.action.context?.target;\n let refHtml = '';\n if (target && ALLOWED_REFS.has(target.model)) {\n const label = esc(this.action.context.label) || `${esc(target.model.split('.').pop())} #${esc(target.pk)}`;\n refHtml = `<div class=\"ac-detail\"><span class=\"ac-ref\" data-action=\"open-ref\" data-model=\"${esc(target.model)}\" data-pk=\"${esc(target.pk)}\"><i class=\"bi bi-box-arrow-up-right\"></i>${label}</span></div>`;\n }\n this.template = `\n <div class=\"ac resolved compact\">\n <div class=\"ac-top\" data-action=\"toggle-compact\" title=\"Click to toggle\">\n <span class=\"ac-dot ${this.dotClass}\"></span>\n <span class=\"ac-label\">${esc(this.action.label) || 'Action'}</span>\n <span class=\"ac-badge ${badgeClass}\">${badgeText}</span>\n <i class=\"bi bi-chevron-down ac-chevron\"></i>\n </div>\n ${refHtml}\n </div>\n `;\n }\n\n _buildPendingTemplate() {\n const target = this.action.context?.target;\n let refHtml = '';\n if (target && ALLOWED_REFS.has(target.model)) {\n const label = esc(this.action.context.label) || `${esc(target.model.split('.').pop())} #${esc(target.pk)}`;\n refHtml = `<br><span class=\"ac-ref\" data-action=\"open-ref\" data-model=\"${esc(target.model)}\" data-pk=\"${esc(target.pk)}\"><i class=\"bi bi-box-arrow-up-right\"></i>${label}</span>`;\n }\n const detail = esc(this.action.context?.detail);\n const disabledAttr = this.isClosed ? ' disabled' : '';\n this.template = `\n <div class=\"ac\">\n <div class=\"ac-top\">\n <span class=\"ac-dot ${this.dotClass}\"></span>\n <span class=\"ac-label\">${esc(this.action.label) || 'Action'}</span>\n </div>\n <div class=\"ac-detail\">${detail}${refHtml}</div>\n <div class=\"ac-foot\">\n <button class=\"btn-approve\" data-action=\"approve\"${disabledAttr}>Approve</button>\n <button class=\"btn-deny\" data-action=\"deny\"${disabledAttr}>Deny</button>\n </div>\n </div>\n `;\n }\n\n async onActionOpenRef(_event, el) {\n const modelRef = el.dataset.model;\n const pk = el.dataset.pk;\n if (!ALLOWED_REFS.has(modelRef) || !/^\\d+$/.test(pk)) return;\n const app = this.getApp();\n const ModelClass = app?.getModelByRef(modelRef);\n if (ModelClass?.VIEW_CLASS) {\n Modal.showModel(new ModelClass({ id: pk }));\n }\n }\n\n onActionApprove() {\n this.emit('action:respond', { noteId: this.noteId, action: 'approve', handler: this.action.handler, context: this.action.context });\n }\n\n onActionDeny() {\n this.emit('action:respond', { noteId: this.noteId, action: 'deny', handler: this.action.handler, context: this.action.context });\n }\n}\n\nexport { HANDLER_COLORS };\nexport default ActionCardView;\n","import View from '@core/View.js';\nimport ChatView from '@core/views/chat/ChatView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport Modal from '@core/views/feedback/Modal.js';\nimport { Ticket, TicketForms, TicketCategories } from '@ext/admin/models/Tickets.js';\nimport { Incident } from '@ext/admin/models/Incident.js';\nimport { GroupList } from '@core/models/Group.js';\nimport { UserList } from '@core/models/User.js';\nimport TicketNoteAdapter from './adapters/TicketNoteAdapter.js';\nimport ActionCardView from './ActionCardView.js';\nimport { openAssistantChat } from '../assistant/AssistantContextChat.js';\nimport rest from '@core/Rest.js';\n\n\nconst STATUS_OPTIONS = ['new', 'open', 'in_progress', 'pending', 'resolved', 'qa', 'closed', 'ignored'];\nconst STATUS_PILL = {\n new: 'pill-new',\n open: 'pill-open',\n in_progress: 'pill-prog',\n pending: 'pill-prog',\n resolved: 'pill-resolved',\n qa: 'pill-open',\n closed: 'pill-closed',\n ignored: 'pill-closed'\n};\nconst PRIORITY_OPTIONS = [\n { value: 10, label: 'P10 — Critical' },\n { value: 9, label: 'P9 — Severe' },\n { value: 8, label: 'P8 — High' },\n { value: 7, label: 'P7 — Elevated' },\n { value: 5, label: 'P5 — Normal' },\n { value: 3, label: 'P3 — Low' },\n { value: 1, label: 'P1 — Info' }\n];\n\nclass TicketPanelView extends View {\n constructor(options = {}) {\n super({\n className: 'ticket-panel-view',\n ...options\n });\n this.model = options.model || new Ticket(options.data || {});\n }\n\n onBeforeRender() {\n const status = this.model.get('status') || 'new';\n this.statusPill = STATUS_PILL[status] || 'pill-closed';\n this.statusLabel = (status || '').replace(/_/g, ' ');\n this.priorityLabel = `P${this.model.get('priority') || 5}`;\n this.assigneeName = this.model.get('assignee.display_name') || this.model.get('assignee') || 'Unassigned';\n this.categoryLabel = this.model.get('category') || 'ticket';\n this.groupName = this.model.get('group.name') || this.model.get('group') || 'None';\n this.hasDescription = !!this.model.get('description');\n this.hasIncident = !!(this.model.get('incident') && typeof this.model.get('incident') === 'object' && this.model.get('incident').id);\n this.priorityColor = (this.model.get('priority') || 5) >= 7 ? 'var(--bs-danger)' : 'var(--bs-secondary-color)';\n\n this.template = `\n <style>\n .ticket-panel-view { height: 100%; display: flex; flex-direction: column; }\n .tp-header { padding: 10px 16px 6px; border-bottom: 1px solid var(--bs-border-color-translucent); flex-shrink: 0; }\n .tp-title-row { display: flex; align-items: flex-start; gap: 6px; }\n .tp-title { font-size: 0.88rem; font-weight: 600; color: var(--bs-emphasis-color); line-height: 1.3; flex: 1; min-width: 0; cursor: ${this.hasDescription ? 'pointer' : 'default'}; transition: color 0.12s; }\n ${this.hasDescription ? '.tp-title:hover { color: var(--bs-primary); }' : ''}\n .tp-title i { font-size: 0.6rem; vertical-align: middle; margin-left: 3px; opacity: 0; transition: opacity 0.12s; }\n .tp-title:hover i { opacity: 0.6; }\n .tp-btns { display: flex; gap: 2px; align-items: center; flex-shrink: 0; }\n .tp-btn { width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; border: none; background: none; color: var(--bs-secondary-color); border-radius: 6px; cursor: pointer; font-size: 0.85rem; transition: all 0.12s; }\n .tp-btn:hover { background: var(--bs-tertiary-bg); color: var(--bs-body-color); }\n .tp-sub { display: flex; align-items: center; gap: 6px; margin-top: 3px; }\n .tp-id { font-family: var(--bs-font-monospace); font-size: 0.7rem; color: var(--bs-secondary-color); }\n .tp-pill { display: inline-block; padding: 1px 7px; border-radius: 10px; font-size: 0.66rem; font-weight: 500; cursor: pointer; transition: filter 0.12s; }\n .tp-pill:hover { filter: brightness(0.9); }\n .tp-pill-new { background: rgba(var(--bs-info-rgb), 0.1); color: var(--bs-info); }\n .tp-pill-open { background: rgba(var(--bs-success-rgb), 0.1); color: var(--bs-success); }\n .tp-pill-prog { background: rgba(var(--bs-warning-rgb), 0.1); color: var(--bs-warning); }\n .tp-pill-closed { background: var(--bs-secondary-bg); color: var(--bs-secondary-color); }\n .tp-pill-resolved { background: rgba(var(--bs-success-rgb), 0.1); color: var(--bs-success); }\n .tp-time { font-size: 0.66rem; color: var(--bs-secondary-color); }\n .tp-desc-chip { display: inline-flex; align-items: center; gap: 4px; font-size: 0.7rem; color: var(--bs-primary); cursor: pointer; padding: 2px 8px; border-radius: 5px; background: rgba(var(--bs-primary-rgb), 0.08); transition: all 0.12s; }\n .tp-desc-chip:hover { background: rgba(var(--bs-primary-rgb), 0.16); }\n .tp-desc-chip i { font-size: 0.7rem; }\n .tp-meta { display: flex; align-items: center; gap: 3px; margin-top: 5px; }\n .tp-fields { display: inline-flex; align-items: center; gap: 2px; flex-wrap: wrap; }\n .tp-field { display: inline-flex; align-items: center; gap: 4px; font-size: 0.72rem; color: var(--bs-secondary-color); padding: 2px 7px; border-radius: 5px; cursor: pointer; transition: all 0.12s; border: 1px solid transparent; }\n .tp-field:hover { background: var(--bs-tertiary-bg); border-color: var(--bs-border-color); color: var(--bs-body-color); }\n .tp-field i { font-size: 0.68rem; }\n .tp-field .caret { font-size: 0.55rem; opacity: 0; transition: opacity 0.12s; margin-left: -1px; }\n .tp-field:hover .caret { opacity: 0.6; }\n .tp-sep { color: var(--bs-secondary-color); font-size: 0.6rem; margin: 0 1px; user-select: none; }\n\n .tp-linked { display: flex; align-items: center; gap: 6px; padding: 7px 16px; border-bottom: 1px solid var(--bs-border-color-translucent); font-size: 0.75rem; color: var(--bs-secondary-color); flex-shrink: 0; }\n .tp-linked i { color: var(--bs-warning); font-size: 0.72rem; }\n .tp-linked a { color: var(--bs-primary); text-decoration: none; font-weight: 500; }\n .tp-linked a:hover { text-decoration: underline; }\n .tp-linked .lpill { font-size: 0.62rem; padding: 0 5px; border-radius: 3px; background: rgba(var(--bs-warning-rgb), 0.1); color: var(--bs-warning); font-weight: 500; }\n\n .tp-conv { flex: 1; overflow-y: auto; min-height: 0; }\n .tp-conv .chat-container { border: none; border-radius: 0; }\n .tp-conv .chat-messages { padding: 6px 0; }\n .tp-conv .chat-input-wrapper { display: none; }\n .tp-conv .message-item { position: relative; }\n .tp-conv .tp-edit-btn { position: absolute; top: 8px; right: 8px; width: 24px; height: 24px; display: none; align-items: center; justify-content: center; border: none; background: var(--bs-tertiary-bg); color: var(--bs-secondary-color); border-radius: 5px; cursor: pointer; font-size: 0.72rem; transition: all 0.12s; }\n .tp-conv .tp-edit-btn:hover { background: var(--bs-secondary-bg); color: var(--bs-body-color); }\n .tp-conv .message-item:hover .tp-edit-btn { display: flex; }\n .tp-conv .message-text { white-space: normal; }\n .tp-conv .message-text p { margin-bottom: 6px; }\n .tp-conv .message-text p:last-child { margin-bottom: 0; }\n .tp-conv .message-text h1,\n .tp-conv .message-text h2,\n .tp-conv .message-text h3,\n .tp-conv .message-text h4,\n .tp-conv .message-text h5,\n .tp-conv .message-text h6 { font-weight: 600; margin-top: 10px; margin-bottom: 4px; line-height: 1.3; }\n .tp-conv .message-text h1 { font-size: 1.05rem; }\n .tp-conv .message-text h2 { font-size: 1rem; }\n .tp-conv .message-text h3 { font-size: 0.95rem; }\n .tp-conv .message-text h4,\n .tp-conv .message-text h5,\n .tp-conv .message-text h6 { font-size: 0.88rem; }\n .tp-conv .message-text h1:first-child,\n .tp-conv .message-text h2:first-child,\n .tp-conv .message-text h3:first-child { margin-top: 0; }\n .tp-conv .message-text hr { margin: 4px 0; opacity: 0.15; }\n .tp-conv .message-text ul,\n .tp-conv .message-text ol { padding-left: 20px; margin-top: 2px; margin-bottom: 6px; }\n .tp-conv .message-text li { margin-bottom: 2px; }\n .tp-conv .message-text pre { background: var(--bs-tertiary-bg); border-radius: 6px; padding: 10px 14px; margin: 8px 0; font-size: 0.8rem; overflow-x: auto; }\n .tp-conv .message-text code { font-size: 0.85em; padding: 1px 5px; background: var(--bs-tertiary-bg); border-radius: 4px; }\n .tp-conv .message-text pre code { padding: 0; background: none; }\n .tp-conv .message-text table { width: 100%; margin: 8px 0; border-collapse: collapse; font-size: 0.82rem; }\n .tp-conv .message-text th,\n .tp-conv .message-text td { padding: 5px 8px; border: 1px solid var(--bs-border-color); text-align: left; }\n .tp-conv .message-text th { background: var(--bs-tertiary-bg); font-weight: 600; }\n .tp-conv .message-text blockquote { margin: 6px 0; padding: 4px 12px; border-left: 3px solid var(--bs-border-color); color: var(--bs-secondary-color); }\n\n .tp-action-area { padding: 0; }\n\n .tp-input { border-top: 1px solid var(--bs-border-color-translucent); padding: 10px 16px; flex-shrink: 0; }\n .tp-input-wrap { display: flex; align-items: flex-end; gap: 8px; }\n .tp-input textarea { flex: 1; font-size: 0.8rem; border: 1px solid var(--bs-border-color); border-radius: 8px; padding: 7px 10px; resize: none; background: var(--bs-body-bg); color: var(--bs-body-color); outline: none; transition: border-color 0.15s; font-family: inherit; max-height: 160px; overflow-y: auto; }\n .tp-input textarea:focus { border-color: var(--bs-primary); }\n .tp-input textarea::placeholder { color: var(--bs-secondary-color); }\n .tp-send-btn { width: 32px; height: 32px; border-radius: 8px; border: none; background: var(--bs-primary); color: #fff; display: flex; align-items: center; justify-content: center; cursor: pointer; flex-shrink: 0; font-size: 0.85rem; transition: filter 0.12s; }\n .tp-send-btn:hover { filter: brightness(1.1); }\n .tp-input-hint { display: flex; align-items: center; gap: 4px; margin-top: 4px; font-size: 0.7rem; color: var(--bs-secondary-color); }\n .tp-input-hint i { font-size: 0.75rem; }\n .tp-input.tp-dragover { background: rgba(var(--bs-primary-rgb), 0.04); }\n .tp-input.tp-dragover textarea { border-color: var(--bs-primary); border-style: dashed; }\n .tp-attachments { display: flex; flex-wrap: wrap; gap: 4px; padding: 4px 0; }\n .tp-attach-chip { display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px; background: var(--bs-tertiary-bg); border: 1px solid var(--bs-border-color); border-radius: 6px; font-size: 0.72rem; color: var(--bs-body-color); }\n .tp-attach-chip i { font-size: 0.68rem; }\n .tp-attach-chip .remove { cursor: pointer; color: var(--bs-secondary-color); margin-left: 2px; }\n .tp-attach-chip .remove:hover { color: var(--bs-danger); }\n .tp-attach-chip.uploading { opacity: 0.6; }\n .tp-conv .message-content.tp-collapsed { max-height: var(--tp-collapse-h, 52px); overflow: hidden; position: relative; -webkit-mask-image: linear-gradient(to bottom, black 60%, transparent 100%); mask-image: linear-gradient(to bottom, black 60%, transparent 100%); }\n .tp-conv .message-item:has(.tp-show-more) { flex-wrap: wrap; }\n .tp-show-more { background: none; border: none; padding: 2px 0; margin: -2px 0 0 48px; font-size: 0.7rem; color: var(--bs-secondary-color); cursor: pointer; text-align: left; width: calc(100% - 48px); }\n .tp-show-more:hover { color: var(--bs-body-color); }\n </style>\n\n <div class=\"tp-header\">\n <div class=\"tp-title-row\">\n <div class=\"tp-title\" ${this.hasDescription ? 'data-action=\"show-description\"' : ''} ${this.hasDescription ? 'title=\"View full description\"' : ''}>\n {{model.title}}${this.hasDescription ? ' <i class=\"bi bi-arrow-up-right-square\"></i>' : ''}\n </div>\n <div class=\"tp-btns\">\n <div data-container=\"panel-menu\"></div>\n </div>\n </div>\n <div class=\"tp-sub\">\n <span class=\"tp-id\">#{{model.id}}</span>\n <span class=\"tp-pill tp-${this.statusPill}\" data-action=\"change-status\" title=\"Change status\">{{statusLabel}} <i class=\"bi bi-chevron-down\" style=\"font-size:0.5rem;\"></i></span>\n <span class=\"tp-time\"><i class=\"bi bi-clock\"></i> {{model.created|relative}}</span>\n <span class=\"tp-desc-chip\" data-action=\"show-description\" title=\"${this.hasDescription ? 'View / edit description' : 'Add description'}\"><i class=\"bi bi-file-text\"></i> ${this.hasDescription ? 'Description' : 'Add description'}</span>\n </div>\n <div class=\"tp-meta\">\n <div class=\"tp-fields\">\n <span class=\"tp-field\" data-action=\"change-priority\" title=\"Change priority\">\n <i class=\"bi bi-flag-fill\" style=\"color:${this.priorityColor};\"></i>{{priorityLabel}}\n <i class=\"bi bi-chevron-down caret\"></i>\n </span>\n <span class=\"tp-sep\">&middot;</span>\n <span class=\"tp-field\" data-action=\"change-assignee\" title=\"Assign\">\n <i class=\"bi bi-person\"></i>{{assigneeName}}\n <i class=\"bi bi-chevron-down caret\"></i>\n </span>\n <span class=\"tp-sep\">&middot;</span>\n <span class=\"tp-field\" data-action=\"change-category\" title=\"Change category\">\n <i class=\"bi bi-tag\"></i>{{categoryLabel}}\n <i class=\"bi bi-chevron-down caret\"></i>\n </span>\n <span class=\"tp-sep\">&middot;</span>\n <span class=\"tp-field\" data-action=\"change-group\" title=\"Change group\">\n <i class=\"bi bi-people\"></i>{{groupName}}\n <i class=\"bi bi-chevron-down caret\"></i>\n </span>\n </div>\n </div>\n </div>\n\n {{#hasIncident|bool}}\n <div class=\"tp-linked\">\n <i class=\"bi bi-exclamation-triangle-fill\"></i>\n <a href=\"#\" data-action=\"view-incident\">Incident #{{model.incident.id}}</a>\n {{#model.incident.status}}<span class=\"lpill\">{{model.incident.status}}</span>{{/model.incident.status}}\n {{#model.incident.event_count}}<span>&middot; {{model.incident.event_count}} events</span>{{/model.incident.event_count}}\n </div>\n {{/hasIncident|bool}}\n\n <div class=\"tp-conv\">\n <div data-container=\"chat-area\"></div>\n </div>\n\n <div class=\"tp-input\" data-ref=\"drop-zone\">\n <div class=\"tp-attachments\" data-ref=\"attachments\"></div>\n <div class=\"tp-input-wrap\">\n <textarea rows=\"2\" placeholder=\"Add a note...\" data-ref=\"note-textarea\"></textarea>\n <button class=\"tp-send-btn\" data-action=\"send-note\" title=\"Send\"><i class=\"bi bi-arrow-up\"></i></button>\n </div>\n <div class=\"tp-input-hint\">\n <i class=\"bi bi-paperclip\"></i> Drag &amp; drop files to attach\n </div>\n </div>\n `;\n }\n\n async onInit() {\n this.adapter = new TicketNoteAdapter(this.model.get('id'));\n\n this.chatView = new ChatView({\n containerId: 'chat-area',\n adapter: this.adapter,\n theme: 'compact',\n currentUserId: this._getCurrentUserId(),\n showInput: false\n });\n this.addChild(this.chatView);\n\n const menu = new ContextMenu({\n containerId: 'panel-menu',\n className: 'context-menu-view header-menu-absolute',\n context: this.model,\n config: {\n icon: 'bi-three-dots',\n btnClass: 'tp-btn',\n items: [\n { label: 'Ask AI', action: 'ask-ai', icon: 'bi-robot' },\n { type: 'divider' },\n { label: 'Edit Ticket', action: 'edit-ticket', icon: 'bi-pencil' },\n { label: 'Refresh Notes', action: 'refresh-notes', icon: 'bi-arrow-clockwise' },\n { type: 'divider' },\n { label: 'Close Window', action: 'close', icon: 'bi-x-lg' }\n ]\n }\n });\n this.addChild(menu);\n }\n\n async onAfterRender() {\n this._setupTextarea();\n this._setupDragDrop();\n // Wait one microtask so child message-view DOM is settled before we\n // place inline action cards and edit buttons.\n await new Promise(r => setTimeout(r, 0));\n await this._loadActionCards();\n this._addEditButtons();\n this._setupCollapsible();\n }\n\n async _loadActionCards() {\n this._cleanupActionCards();\n\n const messages = this.chatView.messages || [];\n if (!messages.length) return;\n\n for (const msg of messages) {\n if (!msg.action || typeof msg.action !== 'object') continue;\n\n const msgView = this.chatView.messageViews.get(msg.id);\n if (!msgView?.element) continue;\n\n const card = new ActionCardView({\n action: msg.action,\n noteId: msg.id,\n ticketStatus: this.model.get('status')\n });\n // Pending approvals get the respond handler\n if (msg.action.type !== 'context' && !msg.action.resolved) {\n card.on('action:respond', (data) => this._handleActionResponse(data));\n }\n this.addChild(card);\n await card.render();\n msgView.element.after(card.element);\n }\n }\n\n _cleanupActionCards() {\n for (const id in this.children) {\n const child = this.children[id];\n if (child instanceof ActionCardView) {\n this.removeChild(child);\n }\n }\n }\n\n async _handleActionResponse(data) {\n const actionNote = { action: { handler: data.handler, context: data.context } };\n const app = this.getApp();\n app?.showLoading();\n try {\n await this.adapter.addActionResponse(actionNote, data.action);\n await this.model.fetch();\n await this.chatView.refresh();\n this.render();\n } finally {\n app?.hideLoading();\n }\n }\n\n _getCurrentUserId() {\n const app = this.getApp();\n return app?.activeUser?.id || app?.getActiveUser?.()?.id || null;\n }\n\n // ── Actions ──\n\n onActionClose() {\n this.emit('panel:close');\n }\n\n async onActionSendNote() {\n const textarea = this.element?.querySelector('[data-ref=\"note-textarea\"]');\n const text = textarea?.value?.trim();\n const files = this._stagedFiles || [];\n if (!text && !files.length) return;\n textarea.value = '';\n textarea.style.height = '';\n this._stagedFiles = [];\n this._renderAttachments();\n await this.adapter.addNote({ text: text || '', files });\n await this.chatView.refresh();\n await this._afterChatRefresh();\n }\n\n _setupTextarea() {\n const textarea = this.element?.querySelector('[data-ref=\"note-textarea\"]');\n if (!textarea) return;\n\n const _insert = (text, cursorOffset = text.length) => {\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n textarea.setRangeText(text, start, end, 'end');\n textarea.selectionStart = textarea.selectionEnd = start + cursorOffset;\n textarea.dispatchEvent(new Event('input'));\n textarea.scrollTop = textarea.scrollHeight;\n };\n\n const _wrapSelection = (wrapper) => {\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n const sel = textarea.value.substring(start, end);\n if (sel.startsWith(wrapper) && sel.endsWith(wrapper)) {\n textarea.setRangeText(sel.slice(wrapper.length, -wrapper.length), start, end, 'end');\n textarea.selectionStart = start;\n textarea.selectionEnd = end - wrapper.length * 2;\n } else {\n textarea.setRangeText(wrapper + sel + wrapper, start, end, 'end');\n textarea.selectionStart = start + wrapper.length;\n textarea.selectionEnd = end + wrapper.length;\n }\n };\n\n const _lineAt = (pos) => {\n const before = textarea.value.substring(0, pos);\n const lineStart = before.lastIndexOf('\\n') + 1;\n return { start: lineStart, text: textarea.value.substring(lineStart, pos) };\n };\n\n const _inCodeFence = (pos) => {\n const before = textarea.value.substring(0, pos);\n const fences = (before.match(/^```/gm) || []).length;\n return fences % 2 === 1;\n };\n\n textarea.addEventListener('keydown', (e) => {\n const mod = e.ctrlKey || e.metaKey;\n\n // Enter → submit (unless Shift held)\n if (e.key === 'Enter' && !e.shiftKey && !mod) {\n e.preventDefault();\n this.onActionSendNote();\n return;\n }\n\n // Shift+Enter → auto-continue list bullets\n if (e.key === 'Enter' && e.shiftKey) {\n const { start, text } = _lineAt(textarea.selectionStart);\n const match = text.match(/^(\\s*)([-*]|\\d+\\.)\\s/);\n if (match) {\n e.preventDefault();\n const indent = match[1];\n const bullet = match[2];\n if (text.trim() === bullet) {\n textarea.setRangeText('', start, textarea.selectionStart, 'end');\n } else {\n const next = /^\\d+\\./.test(bullet) ? `${parseInt(bullet) + 1}.` : bullet;\n _insert(`\\n${indent}${next} `);\n }\n return;\n }\n }\n\n // ``` auto-close: after typing third backtick, insert fence pair\n if (e.key === '`' && !mod) {\n const pos = textarea.selectionStart;\n const before = textarea.value.substring(0, pos);\n if (before.endsWith('``') && !_inCodeFence(pos - 2)) {\n e.preventDefault();\n _insert('`\\n\\n```', 2);\n return;\n }\n }\n\n // Tab indent/dedent inside code fences\n if (e.key === 'Tab' && _inCodeFence(textarea.selectionStart)) {\n e.preventDefault();\n if (e.shiftKey) {\n const { start, text } = _lineAt(textarea.selectionStart);\n const stripped = text.replace(/^ {1,2}/, '');\n textarea.setRangeText(stripped, start, start + text.length, 'end');\n textarea.selectionStart = textarea.selectionEnd = start + stripped.length;\n } else {\n _insert(' ');\n }\n return;\n }\n\n // Ctrl/Cmd+B → bold\n if (mod && e.key === 'b') {\n e.preventDefault();\n _wrapSelection('**');\n return;\n }\n\n // Ctrl/Cmd+I → italic\n if (mod && e.key === 'i') {\n e.preventDefault();\n _wrapSelection('*');\n return;\n }\n });\n\n // Auto-close pairs: ( [ \"\n const PAIRS = { '(': ')', '[': ']', '\"': '\"' };\n textarea.addEventListener('keydown', (e) => {\n if (e.ctrlKey || e.metaKey || e.altKey) return;\n const close = PAIRS[e.key];\n if (!close) return;\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n if (start !== end) {\n e.preventDefault();\n const sel = textarea.value.substring(start, end);\n textarea.setRangeText(e.key + sel + close, start, end, 'end');\n textarea.selectionStart = start + 1;\n textarea.selectionEnd = end + 1;\n } else {\n e.preventDefault();\n _insert(e.key + close, 1);\n }\n });\n\n textarea.addEventListener('input', () => {\n textarea.style.height = '';\n textarea.style.height = Math.min(textarea.scrollHeight, 160) + 'px';\n });\n }\n\n _setupDragDrop() {\n const zone = this.element?.querySelector('[data-ref=\"drop-zone\"]');\n if (!zone) return;\n this._stagedFiles = this._stagedFiles || [];\n let counter = 0;\n zone.addEventListener('dragenter', (e) => { e.preventDefault(); counter++; zone.classList.add('tp-dragover'); });\n zone.addEventListener('dragleave', () => { counter--; if (counter <= 0) { counter = 0; zone.classList.remove('tp-dragover'); } });\n zone.addEventListener('dragover', (e) => e.preventDefault());\n zone.addEventListener('drop', async (e) => {\n e.preventDefault();\n counter = 0;\n zone.classList.remove('tp-dragover');\n const files = Array.from(e.dataTransfer?.files || []);\n if (!files.length) return;\n const { File: FileModel } = await import('@core/models/Files.js');\n for (const file of files) {\n const chipId = Date.now() + Math.random();\n this._addAttachChip(chipId, file.name, true);\n try {\n const fileModel = new FileModel();\n await fileModel.upload({ file, showToast: false });\n this._stagedFiles.push(fileModel);\n this._updateAttachChip(chipId, fileModel.get('name') || file.name, fileModel);\n } catch (err) {\n console.error('File upload failed:', err);\n this._removeAttachChip(chipId);\n this.getApp()?.toast?.error?.('Upload failed: ' + file.name);\n }\n }\n });\n }\n\n _addAttachChip(id, name, uploading) {\n const container = this.element?.querySelector('[data-ref=\"attachments\"]');\n if (!container) return;\n const chip = document.createElement('span');\n chip.className = 'tp-attach-chip' + (uploading ? ' uploading' : '');\n chip.dataset.chipId = id;\n chip.innerHTML = `<i class=\"bi bi-paperclip\"></i>${this._escapeHtml(name)}` +\n (uploading ? '' : '<span class=\"remove\" data-remove=\"1\"><i class=\"bi bi-x\"></i></span>');\n if (!uploading) {\n chip.querySelector('.remove').addEventListener('click', () => {\n this._removeAttachChip(id);\n });\n }\n container.appendChild(chip);\n }\n\n _updateAttachChip(id, name, fileModel) {\n const chip = this.element?.querySelector(`[data-chip-id=\"${id}\"]`);\n if (!chip) return;\n chip.classList.remove('uploading');\n chip.innerHTML = `<i class=\"bi bi-paperclip\"></i>${this._escapeHtml(name)}<span class=\"remove\" data-remove=\"1\"><i class=\"bi bi-x\"></i></span>`;\n chip.querySelector('.remove').addEventListener('click', () => {\n this._stagedFiles = (this._stagedFiles || []).filter(f => f !== fileModel);\n chip.remove();\n });\n }\n\n _removeAttachChip(id) {\n const chip = this.element?.querySelector(`[data-chip-id=\"${id}\"]`);\n if (chip) chip.remove();\n }\n\n _renderAttachments() {\n const container = this.element?.querySelector('[data-ref=\"attachments\"]');\n if (container) container.innerHTML = '';\n }\n\n _escapeHtml(str) {\n const div = document.createElement('div');\n div.textContent = str;\n return div.innerHTML;\n }\n\n _addEditButtons() {\n const userId = this._getCurrentUserId();\n if (!userId || !this.chatView?.messageViews) return;\n const msgById = new Map((this.chatView.messages || []).map(m => [m.id, m]));\n this.chatView.messageViews.forEach((view, msgId) => {\n const msg = msgById.get(msgId);\n const item = view?.element?.querySelector('.message-item');\n if (!item) return;\n item.querySelectorAll('.tp-edit-btn').forEach(b => b.remove());\n if (!msg || msg.author?.id !== userId) return;\n const btn = document.createElement('button');\n btn.className = 'tp-edit-btn';\n btn.title = 'Edit note';\n btn.innerHTML = '<i class=\"bi bi-pencil\"></i>';\n btn.addEventListener('click', (e) => { e.stopPropagation(); this._editNote(msg); });\n item.appendChild(btn);\n });\n }\n\n _setupCollapsible() {\n const container = this.element?.querySelector('[data-container=\"chat-area\"]');\n if (!container) return;\n container.querySelectorAll('.tp-show-more').forEach(b => b.remove());\n container.querySelectorAll('.tp-collapsed').forEach(el => el.classList.remove('tp-collapsed'));\n setTimeout(() => {\n const MAX = 52;\n container.querySelectorAll('.message-content').forEach(body => {\n if (body.scrollHeight <= MAX) return;\n body.classList.add('tp-collapsed');\n body.style.setProperty('--tp-collapse-h', MAX + 'px');\n const btn = document.createElement('button');\n btn.className = 'tp-show-more';\n btn.textContent = 'Show more';\n btn.addEventListener('click', () => {\n const collapsed = body.classList.toggle('tp-collapsed');\n btn.textContent = collapsed ? 'Show more' : 'Show less';\n });\n body.after(btn);\n });\n }, 150);\n }\n\n async _editNote(msg) {\n const metaJson = Object.keys(msg._metadata || {}).length\n ? JSON.stringify(msg._metadata, null, 2) : '';\n const data = await Modal.form({\n title: 'Edit Note',\n icon: 'bi-pencil',\n size: 'lg',\n fields: [{\n type: 'tabset',\n tabs: [\n {\n label: 'Note',\n fields: [{\n name: 'note', type: 'textarea', label: 'Note',\n required: true, cols: 12, rows: 8,\n value: msg._rawContent || msg.content\n }]\n },\n {\n label: 'Metadata',\n fields: [{\n name: 'metadata_json', type: 'json', label: 'Metadata (JSON)',\n cols: 12, rows: 10,\n value: metaJson,\n help: 'Action metadata — e.g. { \"action\": { \"handler\": \"incident.rule_approval\", \"label\": \"...\", \"context\": { ... } } }'\n }]\n }\n ]\n }]\n });\n if (!data) return;\n const { TicketNote } = await import('@ext/admin/models/Tickets.js');\n const note = new TicketNote({ id: msg.id });\n const payload = { note: data.note };\n if (data.metadata_json) {\n payload.metadata = typeof data.metadata_json === 'string'\n ? JSON.parse(data.metadata_json) : data.metadata_json;\n }\n await note.save(payload);\n await this.chatView.refresh();\n await this._afterChatRefresh();\n }\n\n async _saveAndSync(patch) {\n // Some endpoints don't echo the updated record on PUT — Model.save() only\n // merges response.data.data into local state. Re-fetch so the panel and\n // the shared list collection both see the new values. Also refresh notes\n // since the backend may create a status_change note as a side effect.\n await this.model.save(patch);\n await this.model.fetch();\n if (this.chatView) {\n await this.chatView.refresh();\n await this._afterChatRefresh();\n }\n }\n\n async onActionChangeStatus(event) {\n const items = STATUS_OPTIONS.map(s => ({\n label: s.replace(/_/g, ' '),\n value: s,\n active: s === this.model.get('status')\n }));\n const result = await this._showInlineSelect(items, event);\n if (!result) return;\n await this._saveAndSync({ status: result });\n this.render();\n }\n\n async onActionChangePriority(event) {\n const items = PRIORITY_OPTIONS.map(p => ({\n label: p.label,\n value: p.value,\n active: p.value === this.model.get('priority')\n }));\n const result = await this._showInlineSelect(items, event);\n if (!result) return;\n await this._saveAndSync({ priority: parseInt(result) });\n this.render();\n }\n\n async onActionChangeAssignee() {\n const data = await Modal.form({\n title: 'Assign User',\n icon: 'bi-person-plus',\n size: 'sm',\n fields: [{\n name: 'assignee', type: 'collection', label: 'User',\n Collection: UserList, labelField: 'display_name', valueField: 'id',\n required: true, cols: 12,\n value: this.model.get('assignee')\n }]\n });\n if (!data) return;\n await this._saveAndSync({ assignee: data.assignee });\n this.render();\n }\n\n async onActionChangeCategory(event) {\n const items = Object.entries(TicketCategories).map(([key, label]) => ({\n label,\n value: key,\n active: key === this.model.get('category')\n }));\n const result = await this._showInlineSelect(items, event);\n if (!result) return;\n await this._saveAndSync({ category: result });\n this.render();\n }\n\n async onActionChangeGroup() {\n const data = await Modal.form({\n title: 'Change Group',\n icon: 'bi-people',\n size: 'sm',\n fields: [{\n name: 'group', type: 'collection', label: 'Group',\n Collection: GroupList, labelField: 'name', valueField: 'id',\n required: false, cols: 12,\n value: this.model.get('group')\n }]\n });\n if (!data) return;\n await this._saveAndSync({ group: data.group });\n this.render();\n }\n\n async onActionShowDescription() {\n const raw = this.model.get('description') || '';\n if (!raw) {\n // No description yet → jump straight to edit\n return this._editDescription();\n }\n let rendered = false;\n let html = '';\n try {\n const resp = await rest.post('/api/docit/render', { markdown: raw });\n html = resp?.data?.data?.html || resp?.data?.html || '';\n rendered = !!html;\n } catch (_e) { /* fallback to escaped text */ }\n if (!rendered) {\n const div = document.createElement('div');\n div.textContent = raw;\n html = `<pre style=\"white-space:pre-wrap;\">${div.innerHTML}</pre>`;\n }\n const choice = await Modal.dialog({\n title: `Ticket #${this.model.get('id')} — Description`,\n body: `<div style=\"font-size:0.85rem; line-height:1.65;\">${html}</div>`,\n size: 'lg',\n buttons: [\n { text: 'Edit', class: 'btn-primary', value: 'edit' },\n { text: 'Close', class: 'btn-secondary', value: 'close' }\n ]\n });\n if (choice === 'edit') await this._editDescription();\n }\n\n async _editDescription() {\n const id = this.model.get('id');\n const current = this.model.get('description') || '';\n const escaped = current\n .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n const body = `\n <div class=\"tp-desc-edit\">\n <textarea data-ref=\"desc-textarea\" rows=\"16\" placeholder=\"Description (markdown supported)...\"\n style=\"width:100%; font-family: var(--bs-font-monospace); font-size: 0.85rem; padding: 10px 12px; border: 1px solid var(--bs-border-color); border-radius: 8px; background: var(--bs-body-bg); color: var(--bs-body-color); resize: vertical; outline: none;\">${escaped}</textarea>\n <div class=\"text-muted small mt-1\">\n Markdown supported. Cmd/Ctrl+B = bold · Cmd/Ctrl+I = italic · Shift+Enter continues lists · \\`\\`\\` opens a code block\n </div>\n </div>\n `;\n // Modal.dialog only mounts strings or Views — pass HTML string and find\n // the textarea after the modal renders, then wire shortcuts.\n const wireUp = async () => {\n for (let i = 0; i < 20; i++) {\n await new Promise(r => setTimeout(r, 50));\n const ta = document.querySelector('.modal.show [data-ref=\"desc-textarea\"]');\n if (ta) { this._wireMarkdownTextarea(ta); return ta; }\n }\n return null;\n };\n const wirePromise = wireUp();\n const choice = await Modal.dialog({\n title: `Ticket #${id} — Edit Description`,\n body,\n size: 'lg',\n buttons: [\n { text: 'Cancel', class: 'btn-secondary', value: null },\n {\n text: 'Save', class: 'btn-primary',\n handler: () => {\n const ta = document.querySelector('.modal.show [data-ref=\"desc-textarea\"]');\n return ta ? ta.value : null;\n }\n }\n ]\n });\n await wirePromise; // ensure cleanup of the polling\n if (choice === null || choice === undefined) return;\n await this._saveAndSync({ description: choice });\n this.render();\n }\n\n _wireMarkdownTextarea(textarea) {\n const _insert = (text, cursorOffset = text.length) => {\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n textarea.setRangeText(text, start, end, 'end');\n textarea.selectionStart = textarea.selectionEnd = start + cursorOffset;\n textarea.dispatchEvent(new Event('input'));\n };\n const _wrapSelection = (wrapper) => {\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n const sel = textarea.value.substring(start, end);\n if (sel.startsWith(wrapper) && sel.endsWith(wrapper)) {\n textarea.setRangeText(sel.slice(wrapper.length, -wrapper.length), start, end, 'end');\n textarea.selectionStart = start;\n textarea.selectionEnd = end - wrapper.length * 2;\n } else {\n textarea.setRangeText(wrapper + sel + wrapper, start, end, 'end');\n textarea.selectionStart = start + wrapper.length;\n textarea.selectionEnd = end + wrapper.length;\n }\n };\n const _lineAt = (pos) => {\n const before = textarea.value.substring(0, pos);\n const lineStart = before.lastIndexOf('\\n') + 1;\n return { start: lineStart, text: textarea.value.substring(lineStart, pos) };\n };\n const _inCodeFence = (pos) => {\n const before = textarea.value.substring(0, pos);\n const fences = (before.match(/^```/gm) || []).length;\n return fences % 2 === 1;\n };\n textarea.addEventListener('keydown', (e) => {\n const mod = e.ctrlKey || e.metaKey;\n // Shift+Enter → auto-continue list bullets (plain Enter saves the form,\n // so list continuation moves to Shift+Enter here too)\n if (e.key === 'Enter' && e.shiftKey) {\n const { start, text } = _lineAt(textarea.selectionStart);\n const match = text.match(/^(\\s*)([-*]|\\d+\\.)\\s/);\n if (match) {\n e.preventDefault();\n const indent = match[1];\n const bullet = match[2];\n if (text.trim() === bullet) {\n textarea.setRangeText('', start, textarea.selectionStart, 'end');\n } else {\n const next = /^\\d+\\./.test(bullet) ? `${parseInt(bullet) + 1}.` : bullet;\n _insert(`\\n${indent}${next} `);\n }\n return;\n }\n }\n if (e.key === '`' && !mod) {\n const pos = textarea.selectionStart;\n const before = textarea.value.substring(0, pos);\n if (before.endsWith('``') && !_inCodeFence(pos - 2)) {\n e.preventDefault();\n _insert('`\\n\\n```', 2);\n return;\n }\n }\n if (e.key === 'Tab' && _inCodeFence(textarea.selectionStart)) {\n e.preventDefault();\n if (e.shiftKey) {\n const { start, text } = _lineAt(textarea.selectionStart);\n const stripped = text.replace(/^ {1,2}/, '');\n textarea.setRangeText(stripped, start, start + text.length, 'end');\n textarea.selectionStart = textarea.selectionEnd = start + stripped.length;\n } else {\n _insert(' ');\n }\n return;\n }\n if (mod && e.key === 'b') { e.preventDefault(); _wrapSelection('**'); return; }\n if (mod && e.key === 'i') { e.preventDefault(); _wrapSelection('*'); return; }\n });\n const PAIRS = { '(': ')', '[': ']', '\"': '\"' };\n textarea.addEventListener('keydown', (e) => {\n if (e.ctrlKey || e.metaKey || e.altKey) return;\n const close = PAIRS[e.key];\n if (!close) return;\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n if (start !== end) {\n e.preventDefault();\n const sel = textarea.value.substring(start, end);\n textarea.setRangeText(e.key + sel + close, start, end, 'end');\n textarea.selectionStart = start + 1;\n textarea.selectionEnd = end + 1;\n } else {\n e.preventDefault();\n _insert(e.key + close, 1);\n }\n });\n }\n\n async onActionViewIncident() {\n const incident = this.model.get('incident');\n if (incident?.id) {\n Modal.showModel(new Incident({ id: incident.id }));\n }\n }\n\n async onActionEditTicket() {\n const resp = await Modal.modelForm({\n title: `Edit Ticket #${this.model.get('id')}`,\n model: this.model,\n size: 'lg',\n fields: TicketForms.edit.fields\n });\n if (resp) this.render();\n }\n\n async onActionRefreshNotes() {\n await this.chatView.refresh();\n await this._afterChatRefresh();\n this.getApp()?.toast?.success('Notes refreshed');\n }\n\n async onActionAskAi() {\n await openAssistantChat(this, 'incident.Ticket');\n }\n\n async _showInlineSelect(items, event) {\n return new Promise((resolve) => {\n let picked = false;\n const menu = new ContextMenu({\n config: {\n // Each item needs a unique action — ContextMenu identifies the\n // clicked item by data-item-action, and find() returns the FIRST\n // match, so reusing one action name maps every click to item[0].\n items: items.map((item, i) => ({\n label: item.label,\n action: `pick-${i}`,\n class: item.active ? 'fw-bold' : '',\n handler: () => {\n picked = true;\n this.removeChild(menu);\n resolve(item.value);\n }\n }))\n }\n });\n const origClose = menu.closeDropdown.bind(menu);\n menu.closeDropdown = () => {\n origClose();\n if (!picked) {\n this.removeChild(menu);\n resolve(null);\n }\n };\n this.addChild(menu);\n menu.openAt(event.clientX, event.clientY);\n });\n }\n\n async _afterChatRefresh() {\n // ChatView.refresh() doesn't await each message view's render — its inner\n // _renderChildren calls messageView.render(false) without await. Wait one\n // microtask so the message-item DOM is in place before we touch it.\n await new Promise(r => setTimeout(r, 0));\n await this._loadActionCards();\n this._addEditButtons();\n this._setupCollapsible();\n }\n\n async setTicket(ticket) {\n this.model = ticket;\n this.adapter = new TicketNoteAdapter(ticket.get('id'));\n this.chatView.adapter = this.adapter;\n this.chatView.clearMessages();\n await this.render();\n await this.chatView.refresh();\n await this._afterChatRefresh();\n }\n}\n\nexport default TicketPanelView;\n"],"names":["esc","s","String","replace","ALLOWED_REFS","Set","HANDLER_COLORS","dot","label","ActionCardView","View","constructor","options","super","className","this","action","noteId","ticketStatus","isResolved","resolved","isContext","type","isClosed","handlerConfig","handler","onBeforeRender","cfg","dotClass","_buildContextTemplate","_buildResolvedTemplate","_buildPendingTemplate","onActionToggleCompact","acEl","element","querySelector","classList","toggle","refItems","references","filter","ref","has","model","map","split","pop","pk","join","template","resolution","badgeClass","badgeText","target","context","refHtml","detail","disabledAttr","onActionOpenRef","_event","el","modelRef","dataset","test","app","getApp","ModelClass","getModelByRef","VIEW_CLASS","Modal","showModel","id","onActionApprove","emit","onActionDeny","STATUS_OPTIONS","STATUS_PILL","new","open","in_progress","pending","qa","closed","ignored","PRIORITY_OPTIONS","value","TicketPanelView","Ticket","data","status","get","statusPill","statusLabel","priorityLabel","assigneeName","categoryLabel","groupName","hasDescription","hasIncident","priorityColor","onInit","adapter","TicketNoteAdapter","chatView","ChatView","containerId","theme","currentUserId","_getCurrentUserId","showInput","addChild","menu","ContextMenu","config","icon","btnClass","items","onAfterRender","_setupTextarea","_setupDragDrop","Promise","r","setTimeout","_loadActionCards","_addEditButtons","_setupCollapsible","_cleanupActionCards","messages","length","msg","msgView","messageViews","card","on","_handleActionResponse","render","after","children","child","removeChild","actionNote","showLoading","addActionResponse","fetch","refresh","hideLoading","activeUser","getActiveUser","onActionClose","onActionSendNote","textarea","text","trim","files","_stagedFiles","style","height","_renderAttachments","addNote","_afterChatRefresh","_insert","cursorOffset","start","selectionStart","end","selectionEnd","setRangeText","dispatchEvent","Event","scrollTop","scrollHeight","_wrapSelection","wrapper","sel","substring","startsWith","endsWith","slice","_lineAt","pos","lineStart","lastIndexOf","_inCodeFence","match","addEventListener","e","mod","ctrlKey","metaKey","key","shiftKey","preventDefault","indent","bullet","next","parseInt","stripped","PAIRS","altKey","close","Math","min","zone","counter","add","remove","async","Array","from","dataTransfer","File","FileModel","import","then","n","h","file","chipId","Date","now","random","_addAttachChip","name","fileModel","upload","showToast","push","_updateAttachChip","err","console","error","_removeAttachChip","toast","uploading","container","chip","document","createElement","innerHTML","_escapeHtml","appendChild","f","str","div","textContent","userId","msgById","Map","m","forEach","view","msgId","item","querySelectorAll","b","author","btn","title","stopPropagation","_editNote","body","setProperty","MAX","collapsed","metaJson","Object","keys","_metadata","JSON","stringify","form","size","fields","tabs","required","cols","rows","_rawContent","content","help","TicketNote","aQ","note","payload","metadata_json","metadata","parse","save","_saveAndSync","patch","onActionChangeStatus","event","active","result","_showInlineSelect","onActionChangePriority","p","priority","onActionChangeAssignee","Collection","UserList","labelField","valueField","assignee","onActionChangeCategory","entries","TicketCategories","category","onActionChangeGroup","GroupList","group","onActionShowDescription","raw","_editDescription","rendered","html","resp","rest","post","markdown","_e","dialog","buttons","class","wirePromise","i","ta","_wireMarkdownTextarea","wireUp","choice","description","onActionViewIncident","incident","Incident","onActionEditTicket","modelForm","TicketForms","edit","onActionRefreshNotes","success","onActionAskAi","openAssistantChat","resolve","picked","origClose","closeDropdown","bind","openAt","clientX","clientY","setTicket","ticket","clearMessages"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./View-CPWwS19u.js"),t=require("./ChatView-ChyTcmrd.js"),n=require("./ContextMenu-BeveGkJr.js"),i=require("./Modal-Bm1OQ8Ou.js"),a=require("./admin-models-CkHjtMHf.js"),s=require("./User-9qvKV7G6.js"),o=require("./admin-C3-HqQvC.js"),r=require("./Collection-C0pHSKDH.js");function l(e){return e?String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;"):""}const c=/* @__PURE__ */new Set(["incident.RuleSet","incident.Incident","incident.Event","incident.Ticket","account.GeoLocatedIP"]),d={"incident.rule_approval":{dot:"accent",label:"Rule"},"incident.block_confirm":{dot:"red",label:"Block"},"incident.rule_update":{dot:"green",label:"Update"},"incident.escalate":{dot:"amber",label:"Escalate"}};class ActionCardView extends e.View{constructor(e={}){super({className:"action-card-view",...e}),this.action=e.action,this.noteId=e.noteId,this.ticketStatus=e.ticketStatus}get isResolved(){return!!this.action?.resolved}get isContext(){return"context"===this.action?.type}get isClosed(){return"closed"===this.ticketStatus||"resolved"===this.ticketStatus}get handlerConfig(){return d[this.action?.handler]||{dot:"accent",label:"Action"}}onBeforeRender(){const e=this.handlerConfig;this.dotClass=e.dot,this.isContext?this._buildContextTemplate():this.isResolved?this._buildResolvedTemplate():this._buildPendingTemplate()}onActionToggleCompact(){const e=this.element?.querySelector(".ac.resolved");e&&e.classList.toggle("compact")}_buildContextTemplate(){const e=(this.action.references||[]).filter(e=>c.has(e.model)).map(e=>{const t=l(e.label)||`${l(e.model.split(".").pop())} #${l(e.pk)}`;return`<span class="ac-ref" data-action="open-ref" data-model="${l(e.model)}" data-pk="${l(e.pk)}"><i class="bi bi-box-arrow-up-right"></i>${t}</span>`}).join("");this.template=`\n <div class="ac ac-context">\n <div class="ac-top">\n <span class="ac-dot context"></span>\n <span class="ac-label">Referenced models</span>\n </div>\n <div class="ac-detail">${e}</div>\n </div>\n `}_buildResolvedTemplate(){const e=this.action.resolution||"approved",t="approved"===e?"approved":"denied",n="approved"===e?"Approved":"Denied",i=this.action.context?.target;let a="";if(i&&c.has(i.model)){const e=l(this.action.context.label)||`${l(i.model.split(".").pop())} #${l(i.pk)}`;a=`<div class="ac-detail"><span class="ac-ref" data-action="open-ref" data-model="${l(i.model)}" data-pk="${l(i.pk)}"><i class="bi bi-box-arrow-up-right"></i>${e}</span></div>`}this.template=`\n <div class="ac resolved compact">\n <div class="ac-top" data-action="toggle-compact" title="Click to toggle">\n <span class="ac-dot ${this.dotClass}"></span>\n <span class="ac-label">${l(this.action.label)||"Action"}</span>\n <span class="ac-badge ${t}">${n}</span>\n <i class="bi bi-chevron-down ac-chevron"></i>\n </div>\n ${a}\n </div>\n `}_buildPendingTemplate(){const e=this.action.context?.target;let t="";if(e&&c.has(e.model)){const n=l(this.action.context.label)||`${l(e.model.split(".").pop())} #${l(e.pk)}`;t=`<br><span class="ac-ref" data-action="open-ref" data-model="${l(e.model)}" data-pk="${l(e.pk)}"><i class="bi bi-box-arrow-up-right"></i>${n}</span>`}const n=l(this.action.context?.detail),i=this.isClosed?" disabled":"";this.template=`\n <div class="ac">\n <div class="ac-top">\n <span class="ac-dot ${this.dotClass}"></span>\n <span class="ac-label">${l(this.action.label)||"Action"}</span>\n </div>\n <div class="ac-detail">${n}${t}</div>\n <div class="ac-foot">\n <button class="btn-approve" data-action="approve"${i}>Approve</button>\n <button class="btn-deny" data-action="deny"${i}>Deny</button>\n </div>\n </div>\n `}async onActionOpenRef(e,t){const n=t.dataset.model,a=t.dataset.pk;if(!c.has(n)||!/^\d+$/.test(a))return;const s=this.getApp(),o=s?.getModelByRef(n);o?.VIEW_CLASS&&i.Modal.showModel(new o({id:a}))}onActionApprove(){this.emit("action:respond",{noteId:this.noteId,action:"approve",handler:this.action.handler,context:this.action.context})}onActionDeny(){this.emit("action:respond",{noteId:this.noteId,action:"deny",handler:this.action.handler,context:this.action.context})}}const p=["new","open","in_progress","pending","resolved","qa","closed","ignored"],h={new:"pill-new",open:"pill-open",in_progress:"pill-prog",pending:"pill-prog",resolved:"pill-resolved",qa:"pill-open",closed:"pill-closed",ignored:"pill-closed"},g=[{value:10,label:"P10 — Critical"},{value:9,label:"P9 — Severe"},{value:8,label:"P8 — High"},{value:7,label:"P7 — Elevated"},{value:5,label:"P5 — Normal"},{value:3,label:"P3 — Low"},{value:1,label:"P1 — Info"}];class TicketPanelView extends e.View{constructor(e={}){super({className:"ticket-panel-view",...e}),this.model=e.model||new a.Ticket(e.data||{})}onBeforeRender(){const e=this.model.get("status")||"new";this.statusPill=h[e]||"pill-closed",this.statusLabel=e.replace(/_/g," "),this.priorityLabel=`P${this.model.get("priority")||5}`,this.assigneeName=this.model.get("assignee.display_name")||this.model.get("assignee")||"Unassigned",this.categoryLabel=this.model.get("category")||"ticket",this.groupName=this.model.get("group.name")||this.model.get("group")||"None",this.hasDescription=!!this.model.get("description"),this.hasIncident=!(!this.model.get("incident")||"object"!=typeof this.model.get("incident")||!this.model.get("incident").id),this.priorityColor=(this.model.get("priority")||5)>=7?"var(--bs-danger)":"var(--bs-secondary-color)",this.template=`\n <style>\n .ticket-panel-view { height: 100%; display: flex; flex-direction: column; }\n .tp-header { padding: 10px 16px 6px; border-bottom: 1px solid var(--bs-border-color-translucent); flex-shrink: 0; }\n .tp-title-row { display: flex; align-items: flex-start; gap: 6px; }\n .tp-title { font-size: 0.88rem; font-weight: 600; color: var(--bs-emphasis-color); line-height: 1.3; flex: 1; min-width: 0; cursor: ${this.hasDescription?"pointer":"default"}; transition: color 0.12s; }\n ${this.hasDescription?".tp-title:hover { color: var(--bs-primary); }":""}\n .tp-title i { font-size: 0.6rem; vertical-align: middle; margin-left: 3px; opacity: 0; transition: opacity 0.12s; }\n .tp-title:hover i { opacity: 0.6; }\n .tp-btns { display: flex; gap: 2px; align-items: center; flex-shrink: 0; }\n .tp-btn { width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; border: none; background: none; color: var(--bs-secondary-color); border-radius: 6px; cursor: pointer; font-size: 0.85rem; transition: all 0.12s; }\n .tp-btn:hover { background: var(--bs-tertiary-bg); color: var(--bs-body-color); }\n .tp-sub { display: flex; align-items: center; gap: 6px; margin-top: 3px; }\n .tp-id { font-family: var(--bs-font-monospace); font-size: 0.7rem; color: var(--bs-secondary-color); }\n .tp-pill { display: inline-block; padding: 1px 7px; border-radius: 10px; font-size: 0.66rem; font-weight: 500; cursor: pointer; transition: filter 0.12s; }\n .tp-pill:hover { filter: brightness(0.9); }\n .tp-pill-new { background: rgba(var(--bs-info-rgb), 0.1); color: var(--bs-info); }\n .tp-pill-open { background: rgba(var(--bs-success-rgb), 0.1); color: var(--bs-success); }\n .tp-pill-prog { background: rgba(var(--bs-warning-rgb), 0.1); color: var(--bs-warning); }\n .tp-pill-closed { background: var(--bs-secondary-bg); color: var(--bs-secondary-color); }\n .tp-pill-resolved { background: rgba(var(--bs-success-rgb), 0.1); color: var(--bs-success); }\n .tp-time { font-size: 0.66rem; color: var(--bs-secondary-color); }\n .tp-desc-chip { display: inline-flex; align-items: center; gap: 4px; font-size: 0.7rem; color: var(--bs-primary); cursor: pointer; padding: 2px 8px; border-radius: 5px; background: rgba(var(--bs-primary-rgb), 0.08); transition: all 0.12s; }\n .tp-desc-chip:hover { background: rgba(var(--bs-primary-rgb), 0.16); }\n .tp-desc-chip i { font-size: 0.7rem; }\n .tp-meta { display: flex; align-items: center; gap: 3px; margin-top: 5px; }\n .tp-fields { display: inline-flex; align-items: center; gap: 2px; flex-wrap: wrap; }\n .tp-field { display: inline-flex; align-items: center; gap: 4px; font-size: 0.72rem; color: var(--bs-secondary-color); padding: 2px 7px; border-radius: 5px; cursor: pointer; transition: all 0.12s; border: 1px solid transparent; }\n .tp-field:hover { background: var(--bs-tertiary-bg); border-color: var(--bs-border-color); color: var(--bs-body-color); }\n .tp-field i { font-size: 0.68rem; }\n .tp-field .caret { font-size: 0.55rem; opacity: 0; transition: opacity 0.12s; margin-left: -1px; }\n .tp-field:hover .caret { opacity: 0.6; }\n .tp-sep { color: var(--bs-secondary-color); font-size: 0.6rem; margin: 0 1px; user-select: none; }\n\n .tp-linked { display: flex; align-items: center; gap: 6px; padding: 7px 16px; border-bottom: 1px solid var(--bs-border-color-translucent); font-size: 0.75rem; color: var(--bs-secondary-color); flex-shrink: 0; }\n .tp-linked i { color: var(--bs-warning); font-size: 0.72rem; }\n .tp-linked a { color: var(--bs-primary); text-decoration: none; font-weight: 500; }\n .tp-linked a:hover { text-decoration: underline; }\n .tp-linked .lpill { font-size: 0.62rem; padding: 0 5px; border-radius: 3px; background: rgba(var(--bs-warning-rgb), 0.1); color: var(--bs-warning); font-weight: 500; }\n\n .tp-conv { flex: 1; overflow-y: auto; min-height: 0; }\n .tp-conv .chat-container { border: none; border-radius: 0; }\n .tp-conv .chat-messages { padding: 6px 0; }\n .tp-conv .chat-input-wrapper { display: none; }\n .tp-conv .message-item { position: relative; }\n .tp-conv .tp-edit-btn { position: absolute; top: 8px; right: 8px; width: 24px; height: 24px; display: none; align-items: center; justify-content: center; border: none; background: var(--bs-tertiary-bg); color: var(--bs-secondary-color); border-radius: 5px; cursor: pointer; font-size: 0.72rem; transition: all 0.12s; }\n .tp-conv .tp-edit-btn:hover { background: var(--bs-secondary-bg); color: var(--bs-body-color); }\n .tp-conv .message-item:hover .tp-edit-btn { display: flex; }\n .tp-conv .message-text { white-space: normal; }\n .tp-conv .message-text p { margin-bottom: 6px; }\n .tp-conv .message-text p:last-child { margin-bottom: 0; }\n .tp-conv .message-text h1,\n .tp-conv .message-text h2,\n .tp-conv .message-text h3,\n .tp-conv .message-text h4,\n .tp-conv .message-text h5,\n .tp-conv .message-text h6 { font-weight: 600; margin-top: 10px; margin-bottom: 4px; line-height: 1.3; }\n .tp-conv .message-text h1 { font-size: 1.05rem; }\n .tp-conv .message-text h2 { font-size: 1rem; }\n .tp-conv .message-text h3 { font-size: 0.95rem; }\n .tp-conv .message-text h4,\n .tp-conv .message-text h5,\n .tp-conv .message-text h6 { font-size: 0.88rem; }\n .tp-conv .message-text h1:first-child,\n .tp-conv .message-text h2:first-child,\n .tp-conv .message-text h3:first-child { margin-top: 0; }\n .tp-conv .message-text hr { margin: 4px 0; opacity: 0.15; }\n .tp-conv .message-text ul,\n .tp-conv .message-text ol { padding-left: 20px; margin-top: 2px; margin-bottom: 6px; }\n .tp-conv .message-text li { margin-bottom: 2px; }\n .tp-conv .message-text pre { background: var(--bs-tertiary-bg); border-radius: 6px; padding: 10px 14px; margin: 8px 0; font-size: 0.8rem; overflow-x: auto; }\n .tp-conv .message-text code { font-size: 0.85em; padding: 1px 5px; background: var(--bs-tertiary-bg); border-radius: 4px; }\n .tp-conv .message-text pre code { padding: 0; background: none; }\n .tp-conv .message-text table { width: 100%; margin: 8px 0; border-collapse: collapse; font-size: 0.82rem; }\n .tp-conv .message-text th,\n .tp-conv .message-text td { padding: 5px 8px; border: 1px solid var(--bs-border-color); text-align: left; }\n .tp-conv .message-text th { background: var(--bs-tertiary-bg); font-weight: 600; }\n .tp-conv .message-text blockquote { margin: 6px 0; padding: 4px 12px; border-left: 3px solid var(--bs-border-color); color: var(--bs-secondary-color); }\n\n .tp-action-area { padding: 0; }\n\n .tp-input { border-top: 1px solid var(--bs-border-color-translucent); padding: 10px 16px; flex-shrink: 0; }\n .tp-input-wrap { display: flex; align-items: flex-end; gap: 8px; }\n .tp-input textarea { flex: 1; font-size: 0.8rem; border: 1px solid var(--bs-border-color); border-radius: 8px; padding: 7px 10px; resize: none; background: var(--bs-body-bg); color: var(--bs-body-color); outline: none; transition: border-color 0.15s; font-family: inherit; max-height: 160px; overflow-y: auto; }\n .tp-input textarea:focus { border-color: var(--bs-primary); }\n .tp-input textarea::placeholder { color: var(--bs-secondary-color); }\n .tp-send-btn { width: 32px; height: 32px; border-radius: 8px; border: none; background: var(--bs-primary); color: #fff; display: flex; align-items: center; justify-content: center; cursor: pointer; flex-shrink: 0; font-size: 0.85rem; transition: filter 0.12s; }\n .tp-send-btn:hover { filter: brightness(1.1); }\n .tp-input-hint { display: flex; align-items: center; gap: 4px; margin-top: 4px; font-size: 0.7rem; color: var(--bs-secondary-color); }\n .tp-input-hint i { font-size: 0.75rem; }\n .tp-input.tp-dragover { background: rgba(var(--bs-primary-rgb), 0.04); }\n .tp-input.tp-dragover textarea { border-color: var(--bs-primary); border-style: dashed; }\n .tp-attachments { display: flex; flex-wrap: wrap; gap: 4px; padding: 4px 0; }\n .tp-attach-chip { display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px; background: var(--bs-tertiary-bg); border: 1px solid var(--bs-border-color); border-radius: 6px; font-size: 0.72rem; color: var(--bs-body-color); }\n .tp-attach-chip i { font-size: 0.68rem; }\n .tp-attach-chip .remove { cursor: pointer; color: var(--bs-secondary-color); margin-left: 2px; }\n .tp-attach-chip .remove:hover { color: var(--bs-danger); }\n .tp-attach-chip.uploading { opacity: 0.6; }\n .tp-conv .message-content.tp-collapsed { max-height: var(--tp-collapse-h, 52px); overflow: hidden; position: relative; -webkit-mask-image: linear-gradient(to bottom, black 60%, transparent 100%); mask-image: linear-gradient(to bottom, black 60%, transparent 100%); }\n .tp-conv .message-item:has(.tp-show-more) { flex-wrap: wrap; }\n .tp-show-more { background: none; border: none; padding: 2px 0; margin: -2px 0 0 48px; font-size: 0.7rem; color: var(--bs-secondary-color); cursor: pointer; text-align: left; width: calc(100% - 48px); }\n .tp-show-more:hover { color: var(--bs-body-color); }\n </style>\n\n <div class="tp-header">\n <div class="tp-title-row">\n <div class="tp-title" ${this.hasDescription?'data-action="show-description"':""} ${this.hasDescription?'title="View full description"':""}>\n {{model.title}}${this.hasDescription?' <i class="bi bi-arrow-up-right-square"></i>':""}\n </div>\n <div class="tp-btns">\n <div data-container="panel-menu"></div>\n </div>\n </div>\n <div class="tp-sub">\n <span class="tp-id">#{{model.id}}</span>\n <span class="tp-pill tp-${this.statusPill}" data-action="change-status" title="Change status">{{statusLabel}} <i class="bi bi-chevron-down" style="font-size:0.5rem;"></i></span>\n <span class="tp-time"><i class="bi bi-clock"></i> {{model.created|relative}}</span>\n <span class="tp-desc-chip" data-action="show-description" title="${this.hasDescription?"View / edit description":"Add description"}"><i class="bi bi-file-text"></i> ${this.hasDescription?"Description":"Add description"}</span>\n </div>\n <div class="tp-meta">\n <div class="tp-fields">\n <span class="tp-field" data-action="change-priority" title="Change priority">\n <i class="bi bi-flag-fill" style="color:${this.priorityColor};"></i>{{priorityLabel}}\n <i class="bi bi-chevron-down caret"></i>\n </span>\n <span class="tp-sep">&middot;</span>\n <span class="tp-field" data-action="change-assignee" title="Assign">\n <i class="bi bi-person"></i>{{assigneeName}}\n <i class="bi bi-chevron-down caret"></i>\n </span>\n <span class="tp-sep">&middot;</span>\n <span class="tp-field" data-action="change-category" title="Change category">\n <i class="bi bi-tag"></i>{{categoryLabel}}\n <i class="bi bi-chevron-down caret"></i>\n </span>\n <span class="tp-sep">&middot;</span>\n <span class="tp-field" data-action="change-group" title="Change group">\n <i class="bi bi-people"></i>{{groupName}}\n <i class="bi bi-chevron-down caret"></i>\n </span>\n </div>\n </div>\n </div>\n\n {{#hasIncident|bool}}\n <div class="tp-linked">\n <i class="bi bi-exclamation-triangle-fill"></i>\n <a href="#" data-action="view-incident">Incident #{{model.incident.id}}</a>\n {{#model.incident.status}}<span class="lpill">{{model.incident.status}}</span>{{/model.incident.status}}\n {{#model.incident.event_count}}<span>&middot; {{model.incident.event_count}} events</span>{{/model.incident.event_count}}\n </div>\n {{/hasIncident|bool}}\n\n <div class="tp-conv">\n <div data-container="chat-area"></div>\n </div>\n\n <div class="tp-input" data-ref="drop-zone">\n <div class="tp-attachments" data-ref="attachments"></div>\n <div class="tp-input-wrap">\n <textarea rows="2" placeholder="Add a note..." data-ref="note-textarea"></textarea>\n <button class="tp-send-btn" data-action="send-note" title="Send"><i class="bi bi-arrow-up"></i></button>\n </div>\n <div class="tp-input-hint">\n <i class="bi bi-paperclip"></i> Drag &amp; drop files to attach\n </div>\n </div>\n `}async onInit(){this.adapter=new o.TicketNoteAdapter(this.model.get("id")),this.chatView=new t.ChatView({containerId:"chat-area",adapter:this.adapter,theme:"compact",currentUserId:this._getCurrentUserId(),showInput:!1}),this.addChild(this.chatView);const e=new n.ContextMenu({containerId:"panel-menu",className:"context-menu-view header-menu-absolute",context:this.model,config:{icon:"bi-three-dots",btnClass:"tp-btn",items:[{label:"Ask AI",action:"ask-ai",icon:"bi-robot"},{type:"divider"},{label:"Edit Ticket",action:"edit-ticket",icon:"bi-pencil"},{label:"Refresh Notes",action:"refresh-notes",icon:"bi-arrow-clockwise"},{type:"divider"},{label:"Close Window",action:"close",icon:"bi-x-lg"}]}});this.addChild(e)}async onAfterRender(){this._setupTextarea(),this._setupDragDrop(),await new Promise(e=>setTimeout(e,0)),await this._loadActionCards(),this._addEditButtons(),this._setupCollapsible()}async _loadActionCards(){this._cleanupActionCards();const e=this.chatView.messages||[];if(e.length)for(const t of e){if(!t.action||"object"!=typeof t.action)continue;const e=this.chatView.messageViews.get(t.id);if(!e?.element)continue;const n=new ActionCardView({action:t.action,noteId:t.id,ticketStatus:this.model.get("status")});"context"===t.action.type||t.action.resolved||n.on("action:respond",e=>this._handleActionResponse(e)),this.addChild(n),await n.render(),e.element.after(n.element)}}_cleanupActionCards(){for(const e in this.children){const t=this.children[e];t instanceof ActionCardView&&this.removeChild(t)}}async _handleActionResponse(e){const t={action:{handler:e.handler,context:e.context}},n=this.getApp();n?.showLoading();try{await this.adapter.addActionResponse(t,e.action),await this.model.fetch(),await this.chatView.refresh(),this.render()}finally{n?.hideLoading()}}_getCurrentUserId(){const e=this.getApp();return e?.activeUser?.id||e?.getActiveUser?.()?.id||null}onActionClose(){this.emit("panel:close")}async onActionSendNote(){const e=this.element?.querySelector('[data-ref="note-textarea"]'),t=e?.value?.trim(),n=this._stagedFiles||[];(t||n.length)&&(e.value="",e.style.height="",this._stagedFiles=[],this._renderAttachments(),await this.adapter.addNote({text:t||"",files:n}),await this.chatView.refresh(),await this._afterChatRefresh())}_setupTextarea(){const e=this.element?.querySelector('[data-ref="note-textarea"]');if(!e)return;const t=(t,n=t.length)=>{const i=e.selectionStart,a=e.selectionEnd;e.setRangeText(t,i,a,"end"),e.selectionStart=e.selectionEnd=i+n,e.dispatchEvent(new Event("input")),e.scrollTop=e.scrollHeight},n=t=>{const n=e.selectionStart,i=e.selectionEnd,a=e.value.substring(n,i);a.startsWith(t)&&a.endsWith(t)?(e.setRangeText(a.slice(t.length,-t.length),n,i,"end"),e.selectionStart=n,e.selectionEnd=i-2*t.length):(e.setRangeText(t+a+t,n,i,"end"),e.selectionStart=n+t.length,e.selectionEnd=i+t.length)},i=t=>{const n=e.value.substring(0,t).lastIndexOf("\n")+1;return{start:n,text:e.value.substring(n,t)}},a=t=>(e.value.substring(0,t).match(/^```/gm)||[]).length%2==1;e.addEventListener("keydown",s=>{const o=s.ctrlKey||s.metaKey;if("Enter"===s.key&&!s.shiftKey&&!o)return s.preventDefault(),void this.onActionSendNote();if("Enter"===s.key&&s.shiftKey){const{start:n,text:a}=i(e.selectionStart),o=a.match(/^(\s*)([-*]|\d+\.)\s/);if(o){s.preventDefault();const i=o[1],r=o[2];if(a.trim()===r)e.setRangeText("",n,e.selectionStart,"end");else{const e=/^\d+\./.test(r)?`${parseInt(r)+1}.`:r;t(`\n${i}${e} `)}return}}if("`"===s.key&&!o){const n=e.selectionStart;if(e.value.substring(0,n).endsWith("``")&&!a(n-2))return s.preventDefault(),void t("`\n\n```",2)}if("Tab"!==s.key||!a(e.selectionStart))return o&&"b"===s.key?(s.preventDefault(),void n("**")):o&&"i"===s.key?(s.preventDefault(),void n("*")):void 0;if(s.preventDefault(),s.shiftKey){const{start:t,text:n}=i(e.selectionStart),a=n.replace(/^ {1,2}/,"");e.setRangeText(a,t,t+n.length,"end"),e.selectionStart=e.selectionEnd=t+a.length}else t(" ")});const s={"(":")","[":"]",'"':'"'};e.addEventListener("keydown",n=>{if(n.ctrlKey||n.metaKey||n.altKey)return;const i=s[n.key];if(!i)return;const a=e.selectionStart,o=e.selectionEnd;if(a!==o){n.preventDefault();const t=e.value.substring(a,o);e.setRangeText(n.key+t+i,a,o,"end"),e.selectionStart=a+1,e.selectionEnd=o+1}else n.preventDefault(),t(n.key+i,1)}),e.addEventListener("input",()=>{e.style.height="",e.style.height=Math.min(e.scrollHeight,160)+"px"})}_setupDragDrop(){const e=this.element?.querySelector('[data-ref="drop-zone"]');if(!e)return;this._stagedFiles=this._stagedFiles||[];let t=0;e.addEventListener("dragenter",n=>{n.preventDefault(),t++,e.classList.add("tp-dragover")}),e.addEventListener("dragleave",()=>{t--,t<=0&&(t=0,e.classList.remove("tp-dragover"))}),e.addEventListener("dragover",e=>e.preventDefault()),e.addEventListener("drop",async n=>{n.preventDefault(),t=0,e.classList.remove("tp-dragover");const i=Array.from(n.dataTransfer?.files||[]);if(!i.length)return;const{File:a}=await Promise.resolve().then(()=>require("./Modal-Bm1OQ8Ou.js")).then(e=>e.Files);for(const e of i){const t=Date.now()+Math.random();this._addAttachChip(t,e.name,!0);try{const n=new a;await n.upload({file:e,showToast:!1}),this._stagedFiles.push(n),this._updateAttachChip(t,n.get("name")||e.name,n)}catch(s){console.error("File upload failed:",s),this._removeAttachChip(t),this.getApp()?.toast?.error?.("Upload failed: "+e.name)}}})}_addAttachChip(e,t,n){const i=this.element?.querySelector('[data-ref="attachments"]');if(!i)return;const a=document.createElement("span");a.className="tp-attach-chip"+(n?" uploading":""),a.dataset.chipId=e,a.innerHTML=`<i class="bi bi-paperclip"></i>${this._escapeHtml(t)}`+(n?"":'<span class="remove" data-remove="1"><i class="bi bi-x"></i></span>'),n||a.querySelector(".remove").addEventListener("click",()=>{this._removeAttachChip(e)}),i.appendChild(a)}_updateAttachChip(e,t,n){const i=this.element?.querySelector(`[data-chip-id="${e}"]`);i&&(i.classList.remove("uploading"),i.innerHTML=`<i class="bi bi-paperclip"></i>${this._escapeHtml(t)}<span class="remove" data-remove="1"><i class="bi bi-x"></i></span>`,i.querySelector(".remove").addEventListener("click",()=>{this._stagedFiles=(this._stagedFiles||[]).filter(e=>e!==n),i.remove()}))}_removeAttachChip(e){const t=this.element?.querySelector(`[data-chip-id="${e}"]`);t&&t.remove()}_renderAttachments(){const e=this.element?.querySelector('[data-ref="attachments"]');e&&(e.innerHTML="")}_escapeHtml(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}_addEditButtons(){const e=this._getCurrentUserId();if(!e||!this.chatView?.messageViews)return;const t=new Map((this.chatView.messages||[]).map(e=>[e.id,e]));this.chatView.messageViews.forEach((n,i)=>{const a=t.get(i),s=n?.element?.querySelector(".message-item");if(!s)return;if(s.querySelectorAll(".tp-edit-btn").forEach(e=>e.remove()),!a||a.author?.id!==e)return;const o=document.createElement("button");o.className="tp-edit-btn",o.title="Edit note",o.innerHTML='<i class="bi bi-pencil"></i>',o.addEventListener("click",e=>{e.stopPropagation(),this._editNote(a)}),s.appendChild(o)})}_setupCollapsible(){const e=this.element?.querySelector('[data-container="chat-area"]');e&&(e.querySelectorAll(".tp-show-more").forEach(e=>e.remove()),e.querySelectorAll(".tp-collapsed").forEach(e=>e.classList.remove("tp-collapsed")),setTimeout(()=>{e.querySelectorAll(".message-content").forEach(e=>{if(e.scrollHeight<=52)return;e.classList.add("tp-collapsed"),e.style.setProperty("--tp-collapse-h","52px");const t=document.createElement("button");t.className="tp-show-more",t.textContent="Show more",t.addEventListener("click",()=>{const n=e.classList.toggle("tp-collapsed");t.textContent=n?"Show more":"Show less"}),e.after(t)})},150))}async _editNote(e){const t=Object.keys(e._metadata||{}).length?JSON.stringify(e._metadata,null,2):"",n=await i.Modal.form({title:"Edit Note",icon:"bi-pencil",size:"lg",fields:[{type:"tabset",tabs:[{label:"Note",fields:[{name:"note",type:"textarea",label:"Note",required:!0,cols:12,rows:8,value:e._rawContent||e.content}]},{label:"Metadata",fields:[{name:"metadata_json",type:"json",label:"Metadata (JSON)",cols:12,rows:10,value:t,help:'Action metadata — e.g. { "action": { "handler": "incident.rule_approval", "label": "...", "context": { ... } } }'}]}]}]});if(!n)return;const{TicketNote:a}=await Promise.resolve().then(()=>require("./admin-models-CkHjtMHf.js")).then(e=>e.Tickets),s=new a({id:e.id}),o={note:n.note};n.metadata_json&&(o.metadata="string"==typeof n.metadata_json?JSON.parse(n.metadata_json):n.metadata_json),await s.save(o),await this.chatView.refresh(),await this._afterChatRefresh()}async _saveAndSync(e){await this.model.save(e),await this.model.fetch(),this.chatView&&(await this.chatView.refresh(),await this._afterChatRefresh())}async onActionChangeStatus(e){const t=p.map(e=>({label:e.replace(/_/g," "),value:e,active:e===this.model.get("status")})),n=await this._showInlineSelect(t,e);n&&(await this._saveAndSync({status:n}),this.render())}async onActionChangePriority(e){const t=g.map(e=>({label:e.label,value:e.value,active:e.value===this.model.get("priority")})),n=await this._showInlineSelect(t,e);n&&(await this._saveAndSync({priority:parseInt(n)}),this.render())}async onActionChangeAssignee(){const e=await i.Modal.form({title:"Assign User",icon:"bi-person-plus",size:"sm",fields:[{name:"assignee",type:"collection",label:"User",Collection:s.UserList,labelField:"display_name",valueField:"id",required:!0,cols:12,value:this.model.get("assignee")}]});e&&(await this._saveAndSync({assignee:e.assignee}),this.render())}async onActionChangeCategory(e){const t=Object.entries(a.TicketCategories).map(([e,t])=>({label:t,value:e,active:e===this.model.get("category")})),n=await this._showInlineSelect(t,e);n&&(await this._saveAndSync({category:n}),this.render())}async onActionChangeGroup(){const e=await i.Modal.form({title:"Change Group",icon:"bi-people",size:"sm",fields:[{name:"group",type:"collection",label:"Group",Collection:s.GroupList,labelField:"name",valueField:"id",required:!1,cols:12,value:this.model.get("group")}]});e&&(await this._saveAndSync({group:e.group}),this.render())}async onActionShowDescription(){const e=this.model.get("description")||"";if(!e)return this._editDescription();let t=!1,n="";try{const i=await r.rest.post("/api/docit/render",{markdown:e});n=i?.data?.data?.html||i?.data?.html||"",t=!!n}catch(a){}if(!t){const t=document.createElement("div");t.textContent=e,n=`<pre style="white-space:pre-wrap;">${t.innerHTML}</pre>`}"edit"===await i.Modal.dialog({title:`Ticket #${this.model.get("id")} — Description`,body:`<div style="font-size:0.85rem; line-height:1.65;">${n}</div>`,size:"lg",buttons:[{text:"Edit",class:"btn-primary",value:"edit"},{text:"Close",class:"btn-secondary",value:"close"}]})&&await this._editDescription()}async _editDescription(){const e=this.model.get("id"),t=`\n <div class="tp-desc-edit">\n <textarea data-ref="desc-textarea" rows="16" placeholder="Description (markdown supported)..."\n style="width:100%; font-family: var(--bs-font-monospace); font-size: 0.85rem; padding: 10px 12px; border: 1px solid var(--bs-border-color); border-radius: 8px; background: var(--bs-body-bg); color: var(--bs-body-color); resize: vertical; outline: none;">${(this.model.get("description")||"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}</textarea>\n <div class="text-muted small mt-1">\n Markdown supported. Cmd/Ctrl+B = bold · Cmd/Ctrl+I = italic · Shift+Enter continues lists · \`\`\` opens a code block\n </div>\n </div>\n `,n=(async()=>{for(let e=0;e<20;e++){await new Promise(e=>setTimeout(e,50));const e=document.querySelector('.modal.show [data-ref="desc-textarea"]');if(e)return this._wireMarkdownTextarea(e),e}return null})(),a=await i.Modal.dialog({title:`Ticket #${e} — Edit Description`,body:t,size:"lg",buttons:[{text:"Cancel",class:"btn-secondary",value:null},{text:"Save",class:"btn-primary",handler:()=>{const e=document.querySelector('.modal.show [data-ref="desc-textarea"]');return e?e.value:null}}]});await n,null!=a&&(await this._saveAndSync({description:a}),this.render())}_wireMarkdownTextarea(e){const t=(t,n=t.length)=>{const i=e.selectionStart,a=e.selectionEnd;e.setRangeText(t,i,a,"end"),e.selectionStart=e.selectionEnd=i+n,e.dispatchEvent(new Event("input"))},n=t=>{const n=e.selectionStart,i=e.selectionEnd,a=e.value.substring(n,i);a.startsWith(t)&&a.endsWith(t)?(e.setRangeText(a.slice(t.length,-t.length),n,i,"end"),e.selectionStart=n,e.selectionEnd=i-2*t.length):(e.setRangeText(t+a+t,n,i,"end"),e.selectionStart=n+t.length,e.selectionEnd=i+t.length)},i=t=>{const n=e.value.substring(0,t).lastIndexOf("\n")+1;return{start:n,text:e.value.substring(n,t)}},a=t=>(e.value.substring(0,t).match(/^```/gm)||[]).length%2==1;e.addEventListener("keydown",s=>{const o=s.ctrlKey||s.metaKey;if("Enter"===s.key&&s.shiftKey){const{start:n,text:a}=i(e.selectionStart),o=a.match(/^(\s*)([-*]|\d+\.)\s/);if(o){s.preventDefault();const i=o[1],r=o[2];if(a.trim()===r)e.setRangeText("",n,e.selectionStart,"end");else{const e=/^\d+\./.test(r)?`${parseInt(r)+1}.`:r;t(`\n${i}${e} `)}return}}if("`"===s.key&&!o){const n=e.selectionStart;if(e.value.substring(0,n).endsWith("``")&&!a(n-2))return s.preventDefault(),void t("`\n\n```",2)}if("Tab"!==s.key||!a(e.selectionStart))return o&&"b"===s.key?(s.preventDefault(),void n("**")):o&&"i"===s.key?(s.preventDefault(),void n("*")):void 0;if(s.preventDefault(),s.shiftKey){const{start:t,text:n}=i(e.selectionStart),a=n.replace(/^ {1,2}/,"");e.setRangeText(a,t,t+n.length,"end"),e.selectionStart=e.selectionEnd=t+a.length}else t(" ")});const s={"(":")","[":"]",'"':'"'};e.addEventListener("keydown",n=>{if(n.ctrlKey||n.metaKey||n.altKey)return;const i=s[n.key];if(!i)return;const a=e.selectionStart,o=e.selectionEnd;if(a!==o){n.preventDefault();const t=e.value.substring(a,o);e.setRangeText(n.key+t+i,a,o,"end"),e.selectionStart=a+1,e.selectionEnd=o+1}else n.preventDefault(),t(n.key+i,1)})}async onActionViewIncident(){const e=this.model.get("incident");e?.id&&i.Modal.showModel(new a.Incident({id:e.id}))}async onActionEditTicket(){await i.Modal.modelForm({title:`Edit Ticket #${this.model.get("id")}`,model:this.model,size:"lg",fields:a.TicketForms.edit.fields})&&this.render()}async onActionRefreshNotes(){await this.chatView.refresh(),await this._afterChatRefresh(),this.getApp()?.toast?.success("Notes refreshed")}async onActionAskAi(){await o.openAssistantChat(this,"incident.Ticket")}async _showInlineSelect(e,t){return new Promise(i=>{let a=!1;const s=new n.ContextMenu({config:{items:e.map((e,t)=>({label:e.label,action:`pick-${t}`,class:e.active?"fw-bold":"",handler:()=>{a=!0,this.removeChild(s),i(e.value)}}))}}),o=s.closeDropdown.bind(s);s.closeDropdown=()=>{o(),a||(this.removeChild(s),i(null))},this.addChild(s),s.openAt(t.clientX,t.clientY)})}async _afterChatRefresh(){await new Promise(e=>setTimeout(e,0)),await this._loadActionCards(),this._addEditButtons(),this._setupCollapsible()}async setTicket(e){this.model=e,this.adapter=new o.TicketNoteAdapter(e.get("id")),this.chatView.adapter=this.adapter,this.chatView.clearMessages(),await this.render(),await this.chatView.refresh(),await this._afterChatRefresh()}}exports.default=TicketPanelView;
2
+ //# sourceMappingURL=TicketPanelView-if4ogL_D.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TicketPanelView-if4ogL_D.js","sources":["../../src/extensions/admin/incidents/ActionCardView.js","../../src/extensions/admin/incidents/TicketPanelView.js"],"sourcesContent":["import View from '@core/View.js';\nimport Modal from '@core/views/feedback/Modal.js';\n\nfunction esc(s) {\n if (!s) return '';\n return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\n}\n\nconst ALLOWED_REFS = new Set([\n 'incident.RuleSet', 'incident.Incident', 'incident.Event',\n 'incident.Ticket', 'account.GeoLocatedIP'\n]);\n\nconst HANDLER_COLORS = {\n 'incident.rule_approval': { dot: 'accent', label: 'Rule' },\n 'incident.block_confirm': { dot: 'red', label: 'Block' },\n 'incident.rule_update': { dot: 'green', label: 'Update' },\n 'incident.escalate': { dot: 'amber', label: 'Escalate' }\n};\n\nclass ActionCardView extends View {\n constructor(options = {}) {\n super({\n className: 'action-card-view',\n ...options\n });\n this.action = options.action;\n this.noteId = options.noteId;\n this.ticketStatus = options.ticketStatus;\n }\n\n get isResolved() {\n return !!this.action?.resolved;\n }\n\n get isContext() {\n return this.action?.type === 'context';\n }\n\n get isClosed() {\n return this.ticketStatus === 'closed' || this.ticketStatus === 'resolved';\n }\n\n get handlerConfig() {\n return HANDLER_COLORS[this.action?.handler] || { dot: 'accent', label: 'Action' };\n }\n\n onBeforeRender() {\n const cfg = this.handlerConfig;\n this.dotClass = cfg.dot;\n\n if (this.isContext) {\n this._buildContextTemplate();\n } else if (this.isResolved) {\n this._buildResolvedTemplate();\n } else {\n this._buildPendingTemplate();\n }\n }\n\n onActionToggleCompact() {\n const acEl = this.element?.querySelector('.ac.resolved');\n if (acEl) acEl.classList.toggle('compact');\n }\n\n _buildContextTemplate() {\n const refs = (this.action.references || []).filter(ref => ALLOWED_REFS.has(ref.model));\n const refItems = refs.map(ref => {\n const label = esc(ref.label) || `${esc(ref.model.split('.').pop())} #${esc(ref.pk)}`;\n return `<span class=\"ac-ref\" data-action=\"open-ref\" data-model=\"${esc(ref.model)}\" data-pk=\"${esc(ref.pk)}\"><i class=\"bi bi-box-arrow-up-right\"></i>${label}</span>`;\n }).join('');\n\n this.template = `\n <div class=\"ac ac-context\">\n <div class=\"ac-top\">\n <span class=\"ac-dot context\"></span>\n <span class=\"ac-label\">Referenced models</span>\n </div>\n <div class=\"ac-detail\">${refItems}</div>\n </div>\n `;\n }\n\n _buildResolvedTemplate() {\n const resolution = this.action.resolution || 'approved';\n const badgeClass = resolution === 'approved' ? 'approved' : 'denied';\n const badgeText = resolution === 'approved' ? 'Approved' : 'Denied';\n const target = this.action.context?.target;\n let refHtml = '';\n if (target && ALLOWED_REFS.has(target.model)) {\n const label = esc(this.action.context.label) || `${esc(target.model.split('.').pop())} #${esc(target.pk)}`;\n refHtml = `<div class=\"ac-detail\"><span class=\"ac-ref\" data-action=\"open-ref\" data-model=\"${esc(target.model)}\" data-pk=\"${esc(target.pk)}\"><i class=\"bi bi-box-arrow-up-right\"></i>${label}</span></div>`;\n }\n this.template = `\n <div class=\"ac resolved compact\">\n <div class=\"ac-top\" data-action=\"toggle-compact\" title=\"Click to toggle\">\n <span class=\"ac-dot ${this.dotClass}\"></span>\n <span class=\"ac-label\">${esc(this.action.label) || 'Action'}</span>\n <span class=\"ac-badge ${badgeClass}\">${badgeText}</span>\n <i class=\"bi bi-chevron-down ac-chevron\"></i>\n </div>\n ${refHtml}\n </div>\n `;\n }\n\n _buildPendingTemplate() {\n const target = this.action.context?.target;\n let refHtml = '';\n if (target && ALLOWED_REFS.has(target.model)) {\n const label = esc(this.action.context.label) || `${esc(target.model.split('.').pop())} #${esc(target.pk)}`;\n refHtml = `<br><span class=\"ac-ref\" data-action=\"open-ref\" data-model=\"${esc(target.model)}\" data-pk=\"${esc(target.pk)}\"><i class=\"bi bi-box-arrow-up-right\"></i>${label}</span>`;\n }\n const detail = esc(this.action.context?.detail);\n const disabledAttr = this.isClosed ? ' disabled' : '';\n this.template = `\n <div class=\"ac\">\n <div class=\"ac-top\">\n <span class=\"ac-dot ${this.dotClass}\"></span>\n <span class=\"ac-label\">${esc(this.action.label) || 'Action'}</span>\n </div>\n <div class=\"ac-detail\">${detail}${refHtml}</div>\n <div class=\"ac-foot\">\n <button class=\"btn-approve\" data-action=\"approve\"${disabledAttr}>Approve</button>\n <button class=\"btn-deny\" data-action=\"deny\"${disabledAttr}>Deny</button>\n </div>\n </div>\n `;\n }\n\n async onActionOpenRef(_event, el) {\n const modelRef = el.dataset.model;\n const pk = el.dataset.pk;\n if (!ALLOWED_REFS.has(modelRef) || !/^\\d+$/.test(pk)) return;\n const app = this.getApp();\n const ModelClass = app?.getModelByRef(modelRef);\n if (ModelClass?.VIEW_CLASS) {\n Modal.showModel(new ModelClass({ id: pk }));\n }\n }\n\n onActionApprove() {\n this.emit('action:respond', { noteId: this.noteId, action: 'approve', handler: this.action.handler, context: this.action.context });\n }\n\n onActionDeny() {\n this.emit('action:respond', { noteId: this.noteId, action: 'deny', handler: this.action.handler, context: this.action.context });\n }\n}\n\nexport { HANDLER_COLORS };\nexport default ActionCardView;\n","import View from '@core/View.js';\nimport ChatView from '@core/views/chat/ChatView.js';\nimport ContextMenu from '@core/views/feedback/ContextMenu.js';\nimport Modal from '@core/views/feedback/Modal.js';\nimport { Ticket, TicketForms, TicketCategories } from '@ext/admin/models/Tickets.js';\nimport { Incident } from '@ext/admin/models/Incident.js';\nimport { GroupList } from '@core/models/Group.js';\nimport { UserList } from '@core/models/User.js';\nimport TicketNoteAdapter from './adapters/TicketNoteAdapter.js';\nimport ActionCardView from './ActionCardView.js';\nimport { openAssistantChat } from '../assistant/AssistantContextChat.js';\nimport rest from '@core/Rest.js';\n\n\nconst STATUS_OPTIONS = ['new', 'open', 'in_progress', 'pending', 'resolved', 'qa', 'closed', 'ignored'];\nconst STATUS_PILL = {\n new: 'pill-new',\n open: 'pill-open',\n in_progress: 'pill-prog',\n pending: 'pill-prog',\n resolved: 'pill-resolved',\n qa: 'pill-open',\n closed: 'pill-closed',\n ignored: 'pill-closed'\n};\nconst PRIORITY_OPTIONS = [\n { value: 10, label: 'P10 — Critical' },\n { value: 9, label: 'P9 — Severe' },\n { value: 8, label: 'P8 — High' },\n { value: 7, label: 'P7 — Elevated' },\n { value: 5, label: 'P5 — Normal' },\n { value: 3, label: 'P3 — Low' },\n { value: 1, label: 'P1 — Info' }\n];\n\nclass TicketPanelView extends View {\n constructor(options = {}) {\n super({\n className: 'ticket-panel-view',\n ...options\n });\n this.model = options.model || new Ticket(options.data || {});\n }\n\n onBeforeRender() {\n const status = this.model.get('status') || 'new';\n this.statusPill = STATUS_PILL[status] || 'pill-closed';\n this.statusLabel = (status || '').replace(/_/g, ' ');\n this.priorityLabel = `P${this.model.get('priority') || 5}`;\n this.assigneeName = this.model.get('assignee.display_name') || this.model.get('assignee') || 'Unassigned';\n this.categoryLabel = this.model.get('category') || 'ticket';\n this.groupName = this.model.get('group.name') || this.model.get('group') || 'None';\n this.hasDescription = !!this.model.get('description');\n this.hasIncident = !!(this.model.get('incident') && typeof this.model.get('incident') === 'object' && this.model.get('incident').id);\n this.priorityColor = (this.model.get('priority') || 5) >= 7 ? 'var(--bs-danger)' : 'var(--bs-secondary-color)';\n\n this.template = `\n <style>\n .ticket-panel-view { height: 100%; display: flex; flex-direction: column; }\n .tp-header { padding: 10px 16px 6px; border-bottom: 1px solid var(--bs-border-color-translucent); flex-shrink: 0; }\n .tp-title-row { display: flex; align-items: flex-start; gap: 6px; }\n .tp-title { font-size: 0.88rem; font-weight: 600; color: var(--bs-emphasis-color); line-height: 1.3; flex: 1; min-width: 0; cursor: ${this.hasDescription ? 'pointer' : 'default'}; transition: color 0.12s; }\n ${this.hasDescription ? '.tp-title:hover { color: var(--bs-primary); }' : ''}\n .tp-title i { font-size: 0.6rem; vertical-align: middle; margin-left: 3px; opacity: 0; transition: opacity 0.12s; }\n .tp-title:hover i { opacity: 0.6; }\n .tp-btns { display: flex; gap: 2px; align-items: center; flex-shrink: 0; }\n .tp-btn { width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; border: none; background: none; color: var(--bs-secondary-color); border-radius: 6px; cursor: pointer; font-size: 0.85rem; transition: all 0.12s; }\n .tp-btn:hover { background: var(--bs-tertiary-bg); color: var(--bs-body-color); }\n .tp-sub { display: flex; align-items: center; gap: 6px; margin-top: 3px; }\n .tp-id { font-family: var(--bs-font-monospace); font-size: 0.7rem; color: var(--bs-secondary-color); }\n .tp-pill { display: inline-block; padding: 1px 7px; border-radius: 10px; font-size: 0.66rem; font-weight: 500; cursor: pointer; transition: filter 0.12s; }\n .tp-pill:hover { filter: brightness(0.9); }\n .tp-pill-new { background: rgba(var(--bs-info-rgb), 0.1); color: var(--bs-info); }\n .tp-pill-open { background: rgba(var(--bs-success-rgb), 0.1); color: var(--bs-success); }\n .tp-pill-prog { background: rgba(var(--bs-warning-rgb), 0.1); color: var(--bs-warning); }\n .tp-pill-closed { background: var(--bs-secondary-bg); color: var(--bs-secondary-color); }\n .tp-pill-resolved { background: rgba(var(--bs-success-rgb), 0.1); color: var(--bs-success); }\n .tp-time { font-size: 0.66rem; color: var(--bs-secondary-color); }\n .tp-desc-chip { display: inline-flex; align-items: center; gap: 4px; font-size: 0.7rem; color: var(--bs-primary); cursor: pointer; padding: 2px 8px; border-radius: 5px; background: rgba(var(--bs-primary-rgb), 0.08); transition: all 0.12s; }\n .tp-desc-chip:hover { background: rgba(var(--bs-primary-rgb), 0.16); }\n .tp-desc-chip i { font-size: 0.7rem; }\n .tp-meta { display: flex; align-items: center; gap: 3px; margin-top: 5px; }\n .tp-fields { display: inline-flex; align-items: center; gap: 2px; flex-wrap: wrap; }\n .tp-field { display: inline-flex; align-items: center; gap: 4px; font-size: 0.72rem; color: var(--bs-secondary-color); padding: 2px 7px; border-radius: 5px; cursor: pointer; transition: all 0.12s; border: 1px solid transparent; }\n .tp-field:hover { background: var(--bs-tertiary-bg); border-color: var(--bs-border-color); color: var(--bs-body-color); }\n .tp-field i { font-size: 0.68rem; }\n .tp-field .caret { font-size: 0.55rem; opacity: 0; transition: opacity 0.12s; margin-left: -1px; }\n .tp-field:hover .caret { opacity: 0.6; }\n .tp-sep { color: var(--bs-secondary-color); font-size: 0.6rem; margin: 0 1px; user-select: none; }\n\n .tp-linked { display: flex; align-items: center; gap: 6px; padding: 7px 16px; border-bottom: 1px solid var(--bs-border-color-translucent); font-size: 0.75rem; color: var(--bs-secondary-color); flex-shrink: 0; }\n .tp-linked i { color: var(--bs-warning); font-size: 0.72rem; }\n .tp-linked a { color: var(--bs-primary); text-decoration: none; font-weight: 500; }\n .tp-linked a:hover { text-decoration: underline; }\n .tp-linked .lpill { font-size: 0.62rem; padding: 0 5px; border-radius: 3px; background: rgba(var(--bs-warning-rgb), 0.1); color: var(--bs-warning); font-weight: 500; }\n\n .tp-conv { flex: 1; overflow-y: auto; min-height: 0; }\n .tp-conv .chat-container { border: none; border-radius: 0; }\n .tp-conv .chat-messages { padding: 6px 0; }\n .tp-conv .chat-input-wrapper { display: none; }\n .tp-conv .message-item { position: relative; }\n .tp-conv .tp-edit-btn { position: absolute; top: 8px; right: 8px; width: 24px; height: 24px; display: none; align-items: center; justify-content: center; border: none; background: var(--bs-tertiary-bg); color: var(--bs-secondary-color); border-radius: 5px; cursor: pointer; font-size: 0.72rem; transition: all 0.12s; }\n .tp-conv .tp-edit-btn:hover { background: var(--bs-secondary-bg); color: var(--bs-body-color); }\n .tp-conv .message-item:hover .tp-edit-btn { display: flex; }\n .tp-conv .message-text { white-space: normal; }\n .tp-conv .message-text p { margin-bottom: 6px; }\n .tp-conv .message-text p:last-child { margin-bottom: 0; }\n .tp-conv .message-text h1,\n .tp-conv .message-text h2,\n .tp-conv .message-text h3,\n .tp-conv .message-text h4,\n .tp-conv .message-text h5,\n .tp-conv .message-text h6 { font-weight: 600; margin-top: 10px; margin-bottom: 4px; line-height: 1.3; }\n .tp-conv .message-text h1 { font-size: 1.05rem; }\n .tp-conv .message-text h2 { font-size: 1rem; }\n .tp-conv .message-text h3 { font-size: 0.95rem; }\n .tp-conv .message-text h4,\n .tp-conv .message-text h5,\n .tp-conv .message-text h6 { font-size: 0.88rem; }\n .tp-conv .message-text h1:first-child,\n .tp-conv .message-text h2:first-child,\n .tp-conv .message-text h3:first-child { margin-top: 0; }\n .tp-conv .message-text hr { margin: 4px 0; opacity: 0.15; }\n .tp-conv .message-text ul,\n .tp-conv .message-text ol { padding-left: 20px; margin-top: 2px; margin-bottom: 6px; }\n .tp-conv .message-text li { margin-bottom: 2px; }\n .tp-conv .message-text pre { background: var(--bs-tertiary-bg); border-radius: 6px; padding: 10px 14px; margin: 8px 0; font-size: 0.8rem; overflow-x: auto; }\n .tp-conv .message-text code { font-size: 0.85em; padding: 1px 5px; background: var(--bs-tertiary-bg); border-radius: 4px; }\n .tp-conv .message-text pre code { padding: 0; background: none; }\n .tp-conv .message-text table { width: 100%; margin: 8px 0; border-collapse: collapse; font-size: 0.82rem; }\n .tp-conv .message-text th,\n .tp-conv .message-text td { padding: 5px 8px; border: 1px solid var(--bs-border-color); text-align: left; }\n .tp-conv .message-text th { background: var(--bs-tertiary-bg); font-weight: 600; }\n .tp-conv .message-text blockquote { margin: 6px 0; padding: 4px 12px; border-left: 3px solid var(--bs-border-color); color: var(--bs-secondary-color); }\n\n .tp-action-area { padding: 0; }\n\n .tp-input { border-top: 1px solid var(--bs-border-color-translucent); padding: 10px 16px; flex-shrink: 0; }\n .tp-input-wrap { display: flex; align-items: flex-end; gap: 8px; }\n .tp-input textarea { flex: 1; font-size: 0.8rem; border: 1px solid var(--bs-border-color); border-radius: 8px; padding: 7px 10px; resize: none; background: var(--bs-body-bg); color: var(--bs-body-color); outline: none; transition: border-color 0.15s; font-family: inherit; max-height: 160px; overflow-y: auto; }\n .tp-input textarea:focus { border-color: var(--bs-primary); }\n .tp-input textarea::placeholder { color: var(--bs-secondary-color); }\n .tp-send-btn { width: 32px; height: 32px; border-radius: 8px; border: none; background: var(--bs-primary); color: #fff; display: flex; align-items: center; justify-content: center; cursor: pointer; flex-shrink: 0; font-size: 0.85rem; transition: filter 0.12s; }\n .tp-send-btn:hover { filter: brightness(1.1); }\n .tp-input-hint { display: flex; align-items: center; gap: 4px; margin-top: 4px; font-size: 0.7rem; color: var(--bs-secondary-color); }\n .tp-input-hint i { font-size: 0.75rem; }\n .tp-input.tp-dragover { background: rgba(var(--bs-primary-rgb), 0.04); }\n .tp-input.tp-dragover textarea { border-color: var(--bs-primary); border-style: dashed; }\n .tp-attachments { display: flex; flex-wrap: wrap; gap: 4px; padding: 4px 0; }\n .tp-attach-chip { display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px; background: var(--bs-tertiary-bg); border: 1px solid var(--bs-border-color); border-radius: 6px; font-size: 0.72rem; color: var(--bs-body-color); }\n .tp-attach-chip i { font-size: 0.68rem; }\n .tp-attach-chip .remove { cursor: pointer; color: var(--bs-secondary-color); margin-left: 2px; }\n .tp-attach-chip .remove:hover { color: var(--bs-danger); }\n .tp-attach-chip.uploading { opacity: 0.6; }\n .tp-conv .message-content.tp-collapsed { max-height: var(--tp-collapse-h, 52px); overflow: hidden; position: relative; -webkit-mask-image: linear-gradient(to bottom, black 60%, transparent 100%); mask-image: linear-gradient(to bottom, black 60%, transparent 100%); }\n .tp-conv .message-item:has(.tp-show-more) { flex-wrap: wrap; }\n .tp-show-more { background: none; border: none; padding: 2px 0; margin: -2px 0 0 48px; font-size: 0.7rem; color: var(--bs-secondary-color); cursor: pointer; text-align: left; width: calc(100% - 48px); }\n .tp-show-more:hover { color: var(--bs-body-color); }\n </style>\n\n <div class=\"tp-header\">\n <div class=\"tp-title-row\">\n <div class=\"tp-title\" ${this.hasDescription ? 'data-action=\"show-description\"' : ''} ${this.hasDescription ? 'title=\"View full description\"' : ''}>\n {{model.title}}${this.hasDescription ? ' <i class=\"bi bi-arrow-up-right-square\"></i>' : ''}\n </div>\n <div class=\"tp-btns\">\n <div data-container=\"panel-menu\"></div>\n </div>\n </div>\n <div class=\"tp-sub\">\n <span class=\"tp-id\">#{{model.id}}</span>\n <span class=\"tp-pill tp-${this.statusPill}\" data-action=\"change-status\" title=\"Change status\">{{statusLabel}} <i class=\"bi bi-chevron-down\" style=\"font-size:0.5rem;\"></i></span>\n <span class=\"tp-time\"><i class=\"bi bi-clock\"></i> {{model.created|relative}}</span>\n <span class=\"tp-desc-chip\" data-action=\"show-description\" title=\"${this.hasDescription ? 'View / edit description' : 'Add description'}\"><i class=\"bi bi-file-text\"></i> ${this.hasDescription ? 'Description' : 'Add description'}</span>\n </div>\n <div class=\"tp-meta\">\n <div class=\"tp-fields\">\n <span class=\"tp-field\" data-action=\"change-priority\" title=\"Change priority\">\n <i class=\"bi bi-flag-fill\" style=\"color:${this.priorityColor};\"></i>{{priorityLabel}}\n <i class=\"bi bi-chevron-down caret\"></i>\n </span>\n <span class=\"tp-sep\">&middot;</span>\n <span class=\"tp-field\" data-action=\"change-assignee\" title=\"Assign\">\n <i class=\"bi bi-person\"></i>{{assigneeName}}\n <i class=\"bi bi-chevron-down caret\"></i>\n </span>\n <span class=\"tp-sep\">&middot;</span>\n <span class=\"tp-field\" data-action=\"change-category\" title=\"Change category\">\n <i class=\"bi bi-tag\"></i>{{categoryLabel}}\n <i class=\"bi bi-chevron-down caret\"></i>\n </span>\n <span class=\"tp-sep\">&middot;</span>\n <span class=\"tp-field\" data-action=\"change-group\" title=\"Change group\">\n <i class=\"bi bi-people\"></i>{{groupName}}\n <i class=\"bi bi-chevron-down caret\"></i>\n </span>\n </div>\n </div>\n </div>\n\n {{#hasIncident|bool}}\n <div class=\"tp-linked\">\n <i class=\"bi bi-exclamation-triangle-fill\"></i>\n <a href=\"#\" data-action=\"view-incident\">Incident #{{model.incident.id}}</a>\n {{#model.incident.status}}<span class=\"lpill\">{{model.incident.status}}</span>{{/model.incident.status}}\n {{#model.incident.event_count}}<span>&middot; {{model.incident.event_count}} events</span>{{/model.incident.event_count}}\n </div>\n {{/hasIncident|bool}}\n\n <div class=\"tp-conv\">\n <div data-container=\"chat-area\"></div>\n </div>\n\n <div class=\"tp-input\" data-ref=\"drop-zone\">\n <div class=\"tp-attachments\" data-ref=\"attachments\"></div>\n <div class=\"tp-input-wrap\">\n <textarea rows=\"2\" placeholder=\"Add a note...\" data-ref=\"note-textarea\"></textarea>\n <button class=\"tp-send-btn\" data-action=\"send-note\" title=\"Send\"><i class=\"bi bi-arrow-up\"></i></button>\n </div>\n <div class=\"tp-input-hint\">\n <i class=\"bi bi-paperclip\"></i> Drag &amp; drop files to attach\n </div>\n </div>\n `;\n }\n\n async onInit() {\n this.adapter = new TicketNoteAdapter(this.model.get('id'));\n\n this.chatView = new ChatView({\n containerId: 'chat-area',\n adapter: this.adapter,\n theme: 'compact',\n currentUserId: this._getCurrentUserId(),\n showInput: false\n });\n this.addChild(this.chatView);\n\n const menu = new ContextMenu({\n containerId: 'panel-menu',\n className: 'context-menu-view header-menu-absolute',\n context: this.model,\n config: {\n icon: 'bi-three-dots',\n btnClass: 'tp-btn',\n items: [\n { label: 'Ask AI', action: 'ask-ai', icon: 'bi-robot' },\n { type: 'divider' },\n { label: 'Edit Ticket', action: 'edit-ticket', icon: 'bi-pencil' },\n { label: 'Refresh Notes', action: 'refresh-notes', icon: 'bi-arrow-clockwise' },\n { type: 'divider' },\n { label: 'Close Window', action: 'close', icon: 'bi-x-lg' }\n ]\n }\n });\n this.addChild(menu);\n }\n\n async onAfterRender() {\n this._setupTextarea();\n this._setupDragDrop();\n // Wait one microtask so child message-view DOM is settled before we\n // place inline action cards and edit buttons.\n await new Promise(r => setTimeout(r, 0));\n await this._loadActionCards();\n this._addEditButtons();\n this._setupCollapsible();\n }\n\n async _loadActionCards() {\n this._cleanupActionCards();\n\n const messages = this.chatView.messages || [];\n if (!messages.length) return;\n\n for (const msg of messages) {\n if (!msg.action || typeof msg.action !== 'object') continue;\n\n const msgView = this.chatView.messageViews.get(msg.id);\n if (!msgView?.element) continue;\n\n const card = new ActionCardView({\n action: msg.action,\n noteId: msg.id,\n ticketStatus: this.model.get('status')\n });\n // Pending approvals get the respond handler\n if (msg.action.type !== 'context' && !msg.action.resolved) {\n card.on('action:respond', (data) => this._handleActionResponse(data));\n }\n this.addChild(card);\n await card.render();\n msgView.element.after(card.element);\n }\n }\n\n _cleanupActionCards() {\n for (const id in this.children) {\n const child = this.children[id];\n if (child instanceof ActionCardView) {\n this.removeChild(child);\n }\n }\n }\n\n async _handleActionResponse(data) {\n const actionNote = { action: { handler: data.handler, context: data.context } };\n const app = this.getApp();\n app?.showLoading();\n try {\n await this.adapter.addActionResponse(actionNote, data.action);\n await this.model.fetch();\n await this.chatView.refresh();\n this.render();\n } finally {\n app?.hideLoading();\n }\n }\n\n _getCurrentUserId() {\n const app = this.getApp();\n return app?.activeUser?.id || app?.getActiveUser?.()?.id || null;\n }\n\n // ── Actions ──\n\n onActionClose() {\n this.emit('panel:close');\n }\n\n async onActionSendNote() {\n const textarea = this.element?.querySelector('[data-ref=\"note-textarea\"]');\n const text = textarea?.value?.trim();\n const files = this._stagedFiles || [];\n if (!text && !files.length) return;\n textarea.value = '';\n textarea.style.height = '';\n this._stagedFiles = [];\n this._renderAttachments();\n await this.adapter.addNote({ text: text || '', files });\n await this.chatView.refresh();\n await this._afterChatRefresh();\n }\n\n _setupTextarea() {\n const textarea = this.element?.querySelector('[data-ref=\"note-textarea\"]');\n if (!textarea) return;\n\n const _insert = (text, cursorOffset = text.length) => {\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n textarea.setRangeText(text, start, end, 'end');\n textarea.selectionStart = textarea.selectionEnd = start + cursorOffset;\n textarea.dispatchEvent(new Event('input'));\n textarea.scrollTop = textarea.scrollHeight;\n };\n\n const _wrapSelection = (wrapper) => {\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n const sel = textarea.value.substring(start, end);\n if (sel.startsWith(wrapper) && sel.endsWith(wrapper)) {\n textarea.setRangeText(sel.slice(wrapper.length, -wrapper.length), start, end, 'end');\n textarea.selectionStart = start;\n textarea.selectionEnd = end - wrapper.length * 2;\n } else {\n textarea.setRangeText(wrapper + sel + wrapper, start, end, 'end');\n textarea.selectionStart = start + wrapper.length;\n textarea.selectionEnd = end + wrapper.length;\n }\n };\n\n const _lineAt = (pos) => {\n const before = textarea.value.substring(0, pos);\n const lineStart = before.lastIndexOf('\\n') + 1;\n return { start: lineStart, text: textarea.value.substring(lineStart, pos) };\n };\n\n const _inCodeFence = (pos) => {\n const before = textarea.value.substring(0, pos);\n const fences = (before.match(/^```/gm) || []).length;\n return fences % 2 === 1;\n };\n\n textarea.addEventListener('keydown', (e) => {\n const mod = e.ctrlKey || e.metaKey;\n\n // Enter → submit (unless Shift held)\n if (e.key === 'Enter' && !e.shiftKey && !mod) {\n e.preventDefault();\n this.onActionSendNote();\n return;\n }\n\n // Shift+Enter → auto-continue list bullets\n if (e.key === 'Enter' && e.shiftKey) {\n const { start, text } = _lineAt(textarea.selectionStart);\n const match = text.match(/^(\\s*)([-*]|\\d+\\.)\\s/);\n if (match) {\n e.preventDefault();\n const indent = match[1];\n const bullet = match[2];\n if (text.trim() === bullet) {\n textarea.setRangeText('', start, textarea.selectionStart, 'end');\n } else {\n const next = /^\\d+\\./.test(bullet) ? `${parseInt(bullet) + 1}.` : bullet;\n _insert(`\\n${indent}${next} `);\n }\n return;\n }\n }\n\n // ``` auto-close: after typing third backtick, insert fence pair\n if (e.key === '`' && !mod) {\n const pos = textarea.selectionStart;\n const before = textarea.value.substring(0, pos);\n if (before.endsWith('``') && !_inCodeFence(pos - 2)) {\n e.preventDefault();\n _insert('`\\n\\n```', 2);\n return;\n }\n }\n\n // Tab indent/dedent inside code fences\n if (e.key === 'Tab' && _inCodeFence(textarea.selectionStart)) {\n e.preventDefault();\n if (e.shiftKey) {\n const { start, text } = _lineAt(textarea.selectionStart);\n const stripped = text.replace(/^ {1,2}/, '');\n textarea.setRangeText(stripped, start, start + text.length, 'end');\n textarea.selectionStart = textarea.selectionEnd = start + stripped.length;\n } else {\n _insert(' ');\n }\n return;\n }\n\n // Ctrl/Cmd+B → bold\n if (mod && e.key === 'b') {\n e.preventDefault();\n _wrapSelection('**');\n return;\n }\n\n // Ctrl/Cmd+I → italic\n if (mod && e.key === 'i') {\n e.preventDefault();\n _wrapSelection('*');\n return;\n }\n });\n\n // Auto-close pairs: ( [ \"\n const PAIRS = { '(': ')', '[': ']', '\"': '\"' };\n textarea.addEventListener('keydown', (e) => {\n if (e.ctrlKey || e.metaKey || e.altKey) return;\n const close = PAIRS[e.key];\n if (!close) return;\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n if (start !== end) {\n e.preventDefault();\n const sel = textarea.value.substring(start, end);\n textarea.setRangeText(e.key + sel + close, start, end, 'end');\n textarea.selectionStart = start + 1;\n textarea.selectionEnd = end + 1;\n } else {\n e.preventDefault();\n _insert(e.key + close, 1);\n }\n });\n\n textarea.addEventListener('input', () => {\n textarea.style.height = '';\n textarea.style.height = Math.min(textarea.scrollHeight, 160) + 'px';\n });\n }\n\n _setupDragDrop() {\n const zone = this.element?.querySelector('[data-ref=\"drop-zone\"]');\n if (!zone) return;\n this._stagedFiles = this._stagedFiles || [];\n let counter = 0;\n zone.addEventListener('dragenter', (e) => { e.preventDefault(); counter++; zone.classList.add('tp-dragover'); });\n zone.addEventListener('dragleave', () => { counter--; if (counter <= 0) { counter = 0; zone.classList.remove('tp-dragover'); } });\n zone.addEventListener('dragover', (e) => e.preventDefault());\n zone.addEventListener('drop', async (e) => {\n e.preventDefault();\n counter = 0;\n zone.classList.remove('tp-dragover');\n const files = Array.from(e.dataTransfer?.files || []);\n if (!files.length) return;\n const { File: FileModel } = await import('@core/models/Files.js');\n for (const file of files) {\n const chipId = Date.now() + Math.random();\n this._addAttachChip(chipId, file.name, true);\n try {\n const fileModel = new FileModel();\n await fileModel.upload({ file, showToast: false });\n this._stagedFiles.push(fileModel);\n this._updateAttachChip(chipId, fileModel.get('name') || file.name, fileModel);\n } catch (err) {\n console.error('File upload failed:', err);\n this._removeAttachChip(chipId);\n this.getApp()?.toast?.error?.('Upload failed: ' + file.name);\n }\n }\n });\n }\n\n _addAttachChip(id, name, uploading) {\n const container = this.element?.querySelector('[data-ref=\"attachments\"]');\n if (!container) return;\n const chip = document.createElement('span');\n chip.className = 'tp-attach-chip' + (uploading ? ' uploading' : '');\n chip.dataset.chipId = id;\n chip.innerHTML = `<i class=\"bi bi-paperclip\"></i>${this._escapeHtml(name)}` +\n (uploading ? '' : '<span class=\"remove\" data-remove=\"1\"><i class=\"bi bi-x\"></i></span>');\n if (!uploading) {\n chip.querySelector('.remove').addEventListener('click', () => {\n this._removeAttachChip(id);\n });\n }\n container.appendChild(chip);\n }\n\n _updateAttachChip(id, name, fileModel) {\n const chip = this.element?.querySelector(`[data-chip-id=\"${id}\"]`);\n if (!chip) return;\n chip.classList.remove('uploading');\n chip.innerHTML = `<i class=\"bi bi-paperclip\"></i>${this._escapeHtml(name)}<span class=\"remove\" data-remove=\"1\"><i class=\"bi bi-x\"></i></span>`;\n chip.querySelector('.remove').addEventListener('click', () => {\n this._stagedFiles = (this._stagedFiles || []).filter(f => f !== fileModel);\n chip.remove();\n });\n }\n\n _removeAttachChip(id) {\n const chip = this.element?.querySelector(`[data-chip-id=\"${id}\"]`);\n if (chip) chip.remove();\n }\n\n _renderAttachments() {\n const container = this.element?.querySelector('[data-ref=\"attachments\"]');\n if (container) container.innerHTML = '';\n }\n\n _escapeHtml(str) {\n const div = document.createElement('div');\n div.textContent = str;\n return div.innerHTML;\n }\n\n _addEditButtons() {\n const userId = this._getCurrentUserId();\n if (!userId || !this.chatView?.messageViews) return;\n const msgById = new Map((this.chatView.messages || []).map(m => [m.id, m]));\n this.chatView.messageViews.forEach((view, msgId) => {\n const msg = msgById.get(msgId);\n const item = view?.element?.querySelector('.message-item');\n if (!item) return;\n item.querySelectorAll('.tp-edit-btn').forEach(b => b.remove());\n if (!msg || msg.author?.id !== userId) return;\n const btn = document.createElement('button');\n btn.className = 'tp-edit-btn';\n btn.title = 'Edit note';\n btn.innerHTML = '<i class=\"bi bi-pencil\"></i>';\n btn.addEventListener('click', (e) => { e.stopPropagation(); this._editNote(msg); });\n item.appendChild(btn);\n });\n }\n\n _setupCollapsible() {\n const container = this.element?.querySelector('[data-container=\"chat-area\"]');\n if (!container) return;\n container.querySelectorAll('.tp-show-more').forEach(b => b.remove());\n container.querySelectorAll('.tp-collapsed').forEach(el => el.classList.remove('tp-collapsed'));\n setTimeout(() => {\n const MAX = 52;\n container.querySelectorAll('.message-content').forEach(body => {\n if (body.scrollHeight <= MAX) return;\n body.classList.add('tp-collapsed');\n body.style.setProperty('--tp-collapse-h', MAX + 'px');\n const btn = document.createElement('button');\n btn.className = 'tp-show-more';\n btn.textContent = 'Show more';\n btn.addEventListener('click', () => {\n const collapsed = body.classList.toggle('tp-collapsed');\n btn.textContent = collapsed ? 'Show more' : 'Show less';\n });\n body.after(btn);\n });\n }, 150);\n }\n\n async _editNote(msg) {\n const metaJson = Object.keys(msg._metadata || {}).length\n ? JSON.stringify(msg._metadata, null, 2) : '';\n const data = await Modal.form({\n title: 'Edit Note',\n icon: 'bi-pencil',\n size: 'lg',\n fields: [{\n type: 'tabset',\n tabs: [\n {\n label: 'Note',\n fields: [{\n name: 'note', type: 'textarea', label: 'Note',\n required: true, cols: 12, rows: 8,\n value: msg._rawContent || msg.content\n }]\n },\n {\n label: 'Metadata',\n fields: [{\n name: 'metadata_json', type: 'json', label: 'Metadata (JSON)',\n cols: 12, rows: 10,\n value: metaJson,\n help: 'Action metadata — e.g. { \"action\": { \"handler\": \"incident.rule_approval\", \"label\": \"...\", \"context\": { ... } } }'\n }]\n }\n ]\n }]\n });\n if (!data) return;\n const { TicketNote } = await import('@ext/admin/models/Tickets.js');\n const note = new TicketNote({ id: msg.id });\n const payload = { note: data.note };\n if (data.metadata_json) {\n payload.metadata = typeof data.metadata_json === 'string'\n ? JSON.parse(data.metadata_json) : data.metadata_json;\n }\n await note.save(payload);\n await this.chatView.refresh();\n await this._afterChatRefresh();\n }\n\n async _saveAndSync(patch) {\n // Some endpoints don't echo the updated record on PUT — Model.save() only\n // merges response.data.data into local state. Re-fetch so the panel and\n // the shared list collection both see the new values. Also refresh notes\n // since the backend may create a status_change note as a side effect.\n await this.model.save(patch);\n await this.model.fetch();\n if (this.chatView) {\n await this.chatView.refresh();\n await this._afterChatRefresh();\n }\n }\n\n async onActionChangeStatus(event) {\n const items = STATUS_OPTIONS.map(s => ({\n label: s.replace(/_/g, ' '),\n value: s,\n active: s === this.model.get('status')\n }));\n const result = await this._showInlineSelect(items, event);\n if (!result) return;\n await this._saveAndSync({ status: result });\n this.render();\n }\n\n async onActionChangePriority(event) {\n const items = PRIORITY_OPTIONS.map(p => ({\n label: p.label,\n value: p.value,\n active: p.value === this.model.get('priority')\n }));\n const result = await this._showInlineSelect(items, event);\n if (!result) return;\n await this._saveAndSync({ priority: parseInt(result) });\n this.render();\n }\n\n async onActionChangeAssignee() {\n const data = await Modal.form({\n title: 'Assign User',\n icon: 'bi-person-plus',\n size: 'sm',\n fields: [{\n name: 'assignee', type: 'collection', label: 'User',\n Collection: UserList, labelField: 'display_name', valueField: 'id',\n required: true, cols: 12,\n value: this.model.get('assignee')\n }]\n });\n if (!data) return;\n await this._saveAndSync({ assignee: data.assignee });\n this.render();\n }\n\n async onActionChangeCategory(event) {\n const items = Object.entries(TicketCategories).map(([key, label]) => ({\n label,\n value: key,\n active: key === this.model.get('category')\n }));\n const result = await this._showInlineSelect(items, event);\n if (!result) return;\n await this._saveAndSync({ category: result });\n this.render();\n }\n\n async onActionChangeGroup() {\n const data = await Modal.form({\n title: 'Change Group',\n icon: 'bi-people',\n size: 'sm',\n fields: [{\n name: 'group', type: 'collection', label: 'Group',\n Collection: GroupList, labelField: 'name', valueField: 'id',\n required: false, cols: 12,\n value: this.model.get('group')\n }]\n });\n if (!data) return;\n await this._saveAndSync({ group: data.group });\n this.render();\n }\n\n async onActionShowDescription() {\n const raw = this.model.get('description') || '';\n if (!raw) {\n // No description yet → jump straight to edit\n return this._editDescription();\n }\n let rendered = false;\n let html = '';\n try {\n const resp = await rest.post('/api/docit/render', { markdown: raw });\n html = resp?.data?.data?.html || resp?.data?.html || '';\n rendered = !!html;\n } catch (_e) { /* fallback to escaped text */ }\n if (!rendered) {\n const div = document.createElement('div');\n div.textContent = raw;\n html = `<pre style=\"white-space:pre-wrap;\">${div.innerHTML}</pre>`;\n }\n const choice = await Modal.dialog({\n title: `Ticket #${this.model.get('id')} — Description`,\n body: `<div style=\"font-size:0.85rem; line-height:1.65;\">${html}</div>`,\n size: 'lg',\n buttons: [\n { text: 'Edit', class: 'btn-primary', value: 'edit' },\n { text: 'Close', class: 'btn-secondary', value: 'close' }\n ]\n });\n if (choice === 'edit') await this._editDescription();\n }\n\n async _editDescription() {\n const id = this.model.get('id');\n const current = this.model.get('description') || '';\n const escaped = current\n .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n const body = `\n <div class=\"tp-desc-edit\">\n <textarea data-ref=\"desc-textarea\" rows=\"16\" placeholder=\"Description (markdown supported)...\"\n style=\"width:100%; font-family: var(--bs-font-monospace); font-size: 0.85rem; padding: 10px 12px; border: 1px solid var(--bs-border-color); border-radius: 8px; background: var(--bs-body-bg); color: var(--bs-body-color); resize: vertical; outline: none;\">${escaped}</textarea>\n <div class=\"text-muted small mt-1\">\n Markdown supported. Cmd/Ctrl+B = bold · Cmd/Ctrl+I = italic · Shift+Enter continues lists · \\`\\`\\` opens a code block\n </div>\n </div>\n `;\n // Modal.dialog only mounts strings or Views — pass HTML string and find\n // the textarea after the modal renders, then wire shortcuts.\n const wireUp = async () => {\n for (let i = 0; i < 20; i++) {\n await new Promise(r => setTimeout(r, 50));\n const ta = document.querySelector('.modal.show [data-ref=\"desc-textarea\"]');\n if (ta) { this._wireMarkdownTextarea(ta); return ta; }\n }\n return null;\n };\n const wirePromise = wireUp();\n const choice = await Modal.dialog({\n title: `Ticket #${id} — Edit Description`,\n body,\n size: 'lg',\n buttons: [\n { text: 'Cancel', class: 'btn-secondary', value: null },\n {\n text: 'Save', class: 'btn-primary',\n handler: () => {\n const ta = document.querySelector('.modal.show [data-ref=\"desc-textarea\"]');\n return ta ? ta.value : null;\n }\n }\n ]\n });\n await wirePromise; // ensure cleanup of the polling\n if (choice === null || choice === undefined) return;\n await this._saveAndSync({ description: choice });\n this.render();\n }\n\n _wireMarkdownTextarea(textarea) {\n const _insert = (text, cursorOffset = text.length) => {\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n textarea.setRangeText(text, start, end, 'end');\n textarea.selectionStart = textarea.selectionEnd = start + cursorOffset;\n textarea.dispatchEvent(new Event('input'));\n };\n const _wrapSelection = (wrapper) => {\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n const sel = textarea.value.substring(start, end);\n if (sel.startsWith(wrapper) && sel.endsWith(wrapper)) {\n textarea.setRangeText(sel.slice(wrapper.length, -wrapper.length), start, end, 'end');\n textarea.selectionStart = start;\n textarea.selectionEnd = end - wrapper.length * 2;\n } else {\n textarea.setRangeText(wrapper + sel + wrapper, start, end, 'end');\n textarea.selectionStart = start + wrapper.length;\n textarea.selectionEnd = end + wrapper.length;\n }\n };\n const _lineAt = (pos) => {\n const before = textarea.value.substring(0, pos);\n const lineStart = before.lastIndexOf('\\n') + 1;\n return { start: lineStart, text: textarea.value.substring(lineStart, pos) };\n };\n const _inCodeFence = (pos) => {\n const before = textarea.value.substring(0, pos);\n const fences = (before.match(/^```/gm) || []).length;\n return fences % 2 === 1;\n };\n textarea.addEventListener('keydown', (e) => {\n const mod = e.ctrlKey || e.metaKey;\n // Shift+Enter → auto-continue list bullets (plain Enter saves the form,\n // so list continuation moves to Shift+Enter here too)\n if (e.key === 'Enter' && e.shiftKey) {\n const { start, text } = _lineAt(textarea.selectionStart);\n const match = text.match(/^(\\s*)([-*]|\\d+\\.)\\s/);\n if (match) {\n e.preventDefault();\n const indent = match[1];\n const bullet = match[2];\n if (text.trim() === bullet) {\n textarea.setRangeText('', start, textarea.selectionStart, 'end');\n } else {\n const next = /^\\d+\\./.test(bullet) ? `${parseInt(bullet) + 1}.` : bullet;\n _insert(`\\n${indent}${next} `);\n }\n return;\n }\n }\n if (e.key === '`' && !mod) {\n const pos = textarea.selectionStart;\n const before = textarea.value.substring(0, pos);\n if (before.endsWith('``') && !_inCodeFence(pos - 2)) {\n e.preventDefault();\n _insert('`\\n\\n```', 2);\n return;\n }\n }\n if (e.key === 'Tab' && _inCodeFence(textarea.selectionStart)) {\n e.preventDefault();\n if (e.shiftKey) {\n const { start, text } = _lineAt(textarea.selectionStart);\n const stripped = text.replace(/^ {1,2}/, '');\n textarea.setRangeText(stripped, start, start + text.length, 'end');\n textarea.selectionStart = textarea.selectionEnd = start + stripped.length;\n } else {\n _insert(' ');\n }\n return;\n }\n if (mod && e.key === 'b') { e.preventDefault(); _wrapSelection('**'); return; }\n if (mod && e.key === 'i') { e.preventDefault(); _wrapSelection('*'); return; }\n });\n const PAIRS = { '(': ')', '[': ']', '\"': '\"' };\n textarea.addEventListener('keydown', (e) => {\n if (e.ctrlKey || e.metaKey || e.altKey) return;\n const close = PAIRS[e.key];\n if (!close) return;\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n if (start !== end) {\n e.preventDefault();\n const sel = textarea.value.substring(start, end);\n textarea.setRangeText(e.key + sel + close, start, end, 'end');\n textarea.selectionStart = start + 1;\n textarea.selectionEnd = end + 1;\n } else {\n e.preventDefault();\n _insert(e.key + close, 1);\n }\n });\n }\n\n async onActionViewIncident() {\n const incident = this.model.get('incident');\n if (incident?.id) {\n Modal.showModel(new Incident({ id: incident.id }));\n }\n }\n\n async onActionEditTicket() {\n const resp = await Modal.modelForm({\n title: `Edit Ticket #${this.model.get('id')}`,\n model: this.model,\n size: 'lg',\n fields: TicketForms.edit.fields\n });\n if (resp) this.render();\n }\n\n async onActionRefreshNotes() {\n await this.chatView.refresh();\n await this._afterChatRefresh();\n this.getApp()?.toast?.success('Notes refreshed');\n }\n\n async onActionAskAi() {\n await openAssistantChat(this, 'incident.Ticket');\n }\n\n async _showInlineSelect(items, event) {\n return new Promise((resolve) => {\n let picked = false;\n const menu = new ContextMenu({\n config: {\n // Each item needs a unique action — ContextMenu identifies the\n // clicked item by data-item-action, and find() returns the FIRST\n // match, so reusing one action name maps every click to item[0].\n items: items.map((item, i) => ({\n label: item.label,\n action: `pick-${i}`,\n class: item.active ? 'fw-bold' : '',\n handler: () => {\n picked = true;\n this.removeChild(menu);\n resolve(item.value);\n }\n }))\n }\n });\n const origClose = menu.closeDropdown.bind(menu);\n menu.closeDropdown = () => {\n origClose();\n if (!picked) {\n this.removeChild(menu);\n resolve(null);\n }\n };\n this.addChild(menu);\n menu.openAt(event.clientX, event.clientY);\n });\n }\n\n async _afterChatRefresh() {\n // ChatView.refresh() doesn't await each message view's render — its inner\n // _renderChildren calls messageView.render(false) without await. Wait one\n // microtask so the message-item DOM is in place before we touch it.\n await new Promise(r => setTimeout(r, 0));\n await this._loadActionCards();\n this._addEditButtons();\n this._setupCollapsible();\n }\n\n async setTicket(ticket) {\n this.model = ticket;\n this.adapter = new TicketNoteAdapter(ticket.get('id'));\n this.chatView.adapter = this.adapter;\n this.chatView.clearMessages();\n await this.render();\n await this.chatView.refresh();\n await this._afterChatRefresh();\n }\n}\n\nexport default TicketPanelView;\n"],"names":["esc","s","String","replace","ALLOWED_REFS","Set","HANDLER_COLORS","dot","label","ActionCardView","View","constructor","options","super","className","this","action","noteId","ticketStatus","isResolved","resolved","isContext","type","isClosed","handlerConfig","handler","onBeforeRender","cfg","dotClass","_buildContextTemplate","_buildResolvedTemplate","_buildPendingTemplate","onActionToggleCompact","acEl","element","querySelector","classList","toggle","refItems","references","filter","ref","has","model","map","split","pop","pk","join","template","resolution","badgeClass","badgeText","target","context","refHtml","detail","disabledAttr","onActionOpenRef","_event","el","modelRef","dataset","test","app","getApp","ModelClass","getModelByRef","VIEW_CLASS","Modal","showModel","id","onActionApprove","emit","onActionDeny","STATUS_OPTIONS","STATUS_PILL","new","open","in_progress","pending","qa","closed","ignored","PRIORITY_OPTIONS","value","TicketPanelView","Ticket","data","status","get","statusPill","statusLabel","priorityLabel","assigneeName","categoryLabel","groupName","hasDescription","hasIncident","priorityColor","onInit","adapter","TicketNoteAdapter","chatView","ChatView","containerId","theme","currentUserId","_getCurrentUserId","showInput","addChild","menu","ContextMenu","config","icon","btnClass","items","onAfterRender","_setupTextarea","_setupDragDrop","Promise","r","setTimeout","_loadActionCards","_addEditButtons","_setupCollapsible","_cleanupActionCards","messages","length","msg","msgView","messageViews","card","on","_handleActionResponse","render","after","children","child","removeChild","actionNote","showLoading","addActionResponse","fetch","refresh","hideLoading","activeUser","getActiveUser","onActionClose","onActionSendNote","textarea","text","trim","files","_stagedFiles","style","height","_renderAttachments","addNote","_afterChatRefresh","_insert","cursorOffset","start","selectionStart","end","selectionEnd","setRangeText","dispatchEvent","Event","scrollTop","scrollHeight","_wrapSelection","wrapper","sel","substring","startsWith","endsWith","slice","_lineAt","pos","lineStart","lastIndexOf","_inCodeFence","match","addEventListener","e","mod","ctrlKey","metaKey","key","shiftKey","preventDefault","indent","bullet","next","parseInt","stripped","PAIRS","altKey","close","Math","min","zone","counter","add","remove","async","Array","from","dataTransfer","File","FileModel","resolve","then","require","n","Files","file","chipId","Date","now","random","_addAttachChip","name","fileModel","upload","showToast","push","_updateAttachChip","err","console","error","_removeAttachChip","toast","uploading","container","chip","document","createElement","innerHTML","_escapeHtml","appendChild","f","str","div","textContent","userId","msgById","Map","m","forEach","view","msgId","item","querySelectorAll","b","author","btn","title","stopPropagation","_editNote","body","setProperty","MAX","collapsed","metaJson","Object","keys","_metadata","JSON","stringify","form","size","fields","tabs","required","cols","rows","_rawContent","content","help","TicketNote","Tickets","note","payload","metadata_json","metadata","parse","save","_saveAndSync","patch","onActionChangeStatus","event","active","result","_showInlineSelect","onActionChangePriority","p","priority","onActionChangeAssignee","Collection","UserList","labelField","valueField","assignee","onActionChangeCategory","entries","TicketCategories","category","onActionChangeGroup","GroupList","group","onActionShowDescription","raw","_editDescription","rendered","html","resp","rest","post","markdown","_e","dialog","buttons","class","wirePromise","i","ta","_wireMarkdownTextarea","wireUp","choice","description","onActionViewIncident","incident","Incident","onActionEditTicket","modelForm","TicketForms","edit","onActionRefreshNotes","success","onActionAskAi","openAssistantChat","picked","origClose","closeDropdown","bind","openAt","clientX","clientY","setTicket","ticket","clearMessages"],"mappings":"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"}