web-mojo 2.4.5 → 2.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/dist/admin.cjs.js +1 -1
- package/dist/admin.es.js +1 -1
- package/dist/auth.cjs.js +1 -1
- package/dist/auth.es.js +1 -1
- package/dist/charts.cjs.js +1 -1
- package/dist/charts.es.js +1 -1
- package/dist/chunks/{AssistantPanelView-C4ZyIQoH.js → AssistantPanelView-CkQEcaFk.js} +2 -2
- package/dist/chunks/{AssistantPanelView-C4ZyIQoH.js.map → AssistantPanelView-CkQEcaFk.js.map} +1 -1
- package/dist/chunks/{AssistantPanelView-DlBgWeM_.js → AssistantPanelView-D1wEbgtM.js} +2 -2
- package/dist/chunks/{AssistantPanelView-DlBgWeM_.js.map → AssistantPanelView-D1wEbgtM.js.map} +1 -1
- package/dist/chunks/ChatView-D2WOSxPu.js +2 -0
- package/dist/chunks/ChatView-D2WOSxPu.js.map +1 -0
- package/dist/chunks/ChatView-kWguc444.js +2 -0
- package/dist/chunks/ChatView-kWguc444.js.map +1 -0
- package/dist/chunks/Passkeys-B1-Z4-16.js +2 -0
- package/dist/chunks/Passkeys-B1-Z4-16.js.map +1 -0
- package/dist/chunks/Passkeys-BlHx11-5.js +2 -0
- package/dist/chunks/Passkeys-BlHx11-5.js.map +1 -0
- package/dist/chunks/{TicketPanelView-DeeANyKv.js → TicketPanelView-DVePzWyJ.js} +2 -2
- package/dist/chunks/{TicketPanelView-DeeANyKv.js.map → TicketPanelView-DVePzWyJ.js.map} +1 -1
- package/dist/chunks/{TicketPanelView-if4ogL_D.js → TicketPanelView-TYh5iZiR.js} +2 -2
- package/dist/chunks/{TicketPanelView-if4ogL_D.js.map → TicketPanelView-TYh5iZiR.js.map} +1 -1
- package/dist/chunks/{UserProfileView-BPjz7uIp.js → UserProfileView-DDflzpTa.js} +2 -2
- package/dist/chunks/{UserProfileView-BPjz7uIp.js.map → UserProfileView-DDflzpTa.js.map} +1 -1
- package/dist/chunks/{UserProfileView-udbSWe1S.js → UserProfileView-cUF8ED9n.js} +2 -2
- package/dist/chunks/{UserProfileView-udbSWe1S.js.map → UserProfileView-cUF8ED9n.js.map} +1 -1
- package/dist/chunks/{admin-C3-HqQvC.js → admin-CHPo4iDn.js} +2 -2
- package/dist/chunks/{admin-C3-HqQvC.js.map → admin-CHPo4iDn.js.map} +1 -1
- package/dist/chunks/{admin-DrFBTXnB.js → admin-CucHFXfD.js} +2 -2
- package/dist/chunks/{admin-DrFBTXnB.js.map → admin-CucHFXfD.js.map} +1 -1
- package/dist/chunks/{index-dFX91Gwz.js → index-Cxffar1o.js} +2 -2
- package/dist/chunks/{index-dFX91Gwz.js.map → index-Cxffar1o.js.map} +1 -1
- package/dist/chunks/{index-Byf1pa3Z.js → index-DsID1QpB.js} +2 -2
- package/dist/chunks/{index-Byf1pa3Z.js.map → index-DsID1QpB.js.map} +1 -1
- package/dist/chunks/{version-C9vC8_Oy.js → version-BYy2UAT0.js} +2 -2
- package/dist/chunks/{version-C9vC8_Oy.js.map → version-BYy2UAT0.js.map} +1 -1
- package/dist/chunks/{version-CjNfmLdK.js → version-BxLEknar.js} +2 -2
- package/dist/chunks/{version-CjNfmLdK.js.map → version-BxLEknar.js.map} +1 -1
- package/dist/docit.cjs.js +1 -1
- package/dist/docit.es.js +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.es.js +1 -1
- package/dist/lightbox.cjs.js +1 -1
- package/dist/lightbox.es.js +1 -1
- package/dist/user-profile.cjs.js +1 -1
- package/dist/user-profile.es.js +1 -1
- package/dist/web-mojo.lite.iife.js +13 -0
- package/dist/web-mojo.lite.iife.js.map +1 -1
- package/dist/web-mojo.lite.iife.min.js +44 -44
- package/dist/web-mojo.lite.iife.min.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunks/ChatView-CcR1GR30.js +0 -2
- package/dist/chunks/ChatView-CcR1GR30.js.map +0 -1
- package/dist/chunks/ChatView-ChyTcmrd.js +0 -2
- package/dist/chunks/ChatView-ChyTcmrd.js.map +0 -1
- package/dist/chunks/Passkeys-DJn9yy-0.js +0 -2
- package/dist/chunks/Passkeys-DJn9yy-0.js.map +0 -1
- package/dist/chunks/Passkeys-WWAg5tKf.js +0 -2
- package/dist/chunks/Passkeys-WWAg5tKf.js.map +0 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{V as e}from"./View-BxlKR1IW.js";import{C as t}from"./ChatView-CcR1GR30.js";import{C as n}from"./ContextMenu-T3yDdsIe.js";import{M as a}from"./Modal-KnJhNZ1E.js";import{T as i,b as s,I as o,c as r}from"./admin-models-DLtFboxy.js";import{U as l,G as c}from"./User-BM76Ughk.js";import{T as d,o as p}from"./admin-DrFBTXnB.js";import{r as h}from"./Collection-DNmr743A.js";function g(e){return e?String(e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,"""):""}const m=/* @__PURE__ */new Set(["incident.RuleSet","incident.Incident","incident.Event","incident.Ticket","account.GeoLocatedIP"]),b={"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{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 b[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=>m.has(e.model)).map(e=>{const t=g(e.label)||`${g(e.model.split(".").pop())} #${g(e.pk)}`;return`<span class="ac-ref" data-action="open-ref" data-model="${g(e.model)}" data-pk="${g(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",a=this.action.context?.target;let i="";if(a&&m.has(a.model)){const e=g(this.action.context.label)||`${g(a.model.split(".").pop())} #${g(a.pk)}`;i=`<div class="ac-detail"><span class="ac-ref" data-action="open-ref" data-model="${g(a.model)}" data-pk="${g(a.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">${g(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 ${i}\n </div>\n `}_buildPendingTemplate(){const e=this.action.context?.target;let t="";if(e&&m.has(e.model)){const n=g(this.action.context.label)||`${g(e.model.split(".").pop())} #${g(e.pk)}`;t=`<br><span class="ac-ref" data-action="open-ref" data-model="${g(e.model)}" data-pk="${g(e.pk)}"><i class="bi bi-box-arrow-up-right"></i>${n}</span>`}const n=g(this.action.context?.detail),a=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">${g(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"${a}>Approve</button>\n <button class="btn-deny" data-action="deny"${a}>Deny</button>\n </div>\n </div>\n `}async onActionOpenRef(e,t){const n=t.dataset.model,i=t.dataset.pk;if(!m.has(n)||!/^\d+$/.test(i))return;const s=this.getApp(),o=s?.getModelByRef(n);o?.VIEW_CLASS&&a.showModel(new o({id:i}))}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 v=["new","open","in_progress","pending","resolved","qa","closed","ignored"],u={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"},f=[{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{constructor(e={}){super({className:"ticket-panel-view",...e}),this.model=e.model||new i(e.data||{})}onBeforeRender(){const e=this.model.get("status")||"new";this.statusPill=u[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">·</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">·</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">·</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>· {{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 & drop files to attach\n </div>\n </div>\n `}async onInit(){this.adapter=new d(this.model.get("id")),this.chatView=new t({containerId:"chat-area",adapter:this.adapter,theme:"compact",currentUserId:this._getCurrentUserId(),showInput:!1}),this.addChild(this.chatView);const e=new n({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 a=e.selectionStart,i=e.selectionEnd;e.setRangeText(t,a,i,"end"),e.selectionStart=e.selectionEnd=a+n,e.dispatchEvent(new Event("input")),e.scrollTop=e.scrollHeight},n=t=>{const n=e.selectionStart,a=e.selectionEnd,i=e.value.substring(n,a);i.startsWith(t)&&i.endsWith(t)?(e.setRangeText(i.slice(t.length,-t.length),n,a,"end"),e.selectionStart=n,e.selectionEnd=a-2*t.length):(e.setRangeText(t+i+t,n,a,"end"),e.selectionStart=n+t.length,e.selectionEnd=a+t.length)},a=t=>{const n=e.value.substring(0,t).lastIndexOf("\n")+1;return{start:n,text:e.value.substring(n,t)}},i=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:i}=a(e.selectionStart),o=i.match(/^(\s*)([-*]|\d+\.)\s/);if(o){s.preventDefault();const a=o[1],r=o[2];if(i.trim()===r)e.setRangeText("",n,e.selectionStart,"end");else{const e=/^\d+\./.test(r)?`${parseInt(r)+1}.`:r;t(`\n${a}${e} `)}return}}if("`"===s.key&&!o){const n=e.selectionStart;if(e.value.substring(0,n).endsWith("``")&&!i(n-2))return s.preventDefault(),void t("`\n\n```",2)}if("Tab"!==s.key||!i(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}=a(e.selectionStart),i=n.replace(/^ {1,2}/,"");e.setRangeText(i,t,t+n.length,"end"),e.selectionStart=e.selectionEnd=t+i.length}else t(" ")});const s={"(":")","[":"]",'"':'"'};e.addEventListener("keydown",n=>{if(n.ctrlKey||n.metaKey||n.altKey)return;const a=s[n.key];if(!a)return;const i=e.selectionStart,o=e.selectionEnd;if(i!==o){n.preventDefault();const t=e.value.substring(i,o);e.setRangeText(n.key+t+a,i,o,"end"),e.selectionStart=i+1,e.selectionEnd=o+1}else n.preventDefault(),t(n.key+a,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 a=Array.from(n.dataTransfer?.files||[]);if(!a.length)return;const{File:i}=await import("./Modal-KnJhNZ1E.js").then(e=>e.h);for(const e of a){const t=Date.now()+Math.random();this._addAttachChip(t,e.name,!0);try{const n=new i;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 a=this.element?.querySelector('[data-ref="attachments"]');if(!a)return;const i=document.createElement("span");i.className="tp-attach-chip"+(n?" uploading":""),i.dataset.chipId=e,i.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||i.querySelector(".remove").addEventListener("click",()=>{this._removeAttachChip(e)}),a.appendChild(i)}_updateAttachChip(e,t,n){const a=this.element?.querySelector(`[data-chip-id="${e}"]`);a&&(a.classList.remove("uploading"),a.innerHTML=`<i class="bi bi-paperclip"></i>${this._escapeHtml(t)}<span class="remove" data-remove="1"><i class="bi bi-x"></i></span>`,a.querySelector(".remove").addEventListener("click",()=>{this._stagedFiles=(this._stagedFiles||[]).filter(e=>e!==n),a.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,a)=>{const i=t.get(a),s=n?.element?.querySelector(".message-item");if(!s)return;if(s.querySelectorAll(".tp-edit-btn").forEach(e=>e.remove()),!i||i.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(i)}),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 a.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:i}=await import("./admin-models-DLtFboxy.js").then(e=>e.aQ),s=new i({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=v.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=f.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 a.form({title:"Assign User",icon:"bi-person-plus",size:"sm",fields:[{name:"assignee",type:"collection",label:"User",Collection:l,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(s).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 a.form({title:"Change Group",icon:"bi-people",size:"sm",fields:[{name:"group",type:"collection",label:"Group",Collection:c,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 a=await h.post("/api/docit/render",{markdown:e});n=a?.data?.data?.html||a?.data?.html||"",t=!!n}catch(i){}if(!t){const t=document.createElement("div");t.textContent=e,n=`<pre style="white-space:pre-wrap;">${t.innerHTML}</pre>`}"edit"===await a.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,"&").replace(/</g,"<").replace(/>/g,">")}</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})(),i=await a.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!=i&&(await this._saveAndSync({description:i}),this.render())}_wireMarkdownTextarea(e){const t=(t,n=t.length)=>{const a=e.selectionStart,i=e.selectionEnd;e.setRangeText(t,a,i,"end"),e.selectionStart=e.selectionEnd=a+n,e.dispatchEvent(new Event("input"))},n=t=>{const n=e.selectionStart,a=e.selectionEnd,i=e.value.substring(n,a);i.startsWith(t)&&i.endsWith(t)?(e.setRangeText(i.slice(t.length,-t.length),n,a,"end"),e.selectionStart=n,e.selectionEnd=a-2*t.length):(e.setRangeText(t+i+t,n,a,"end"),e.selectionStart=n+t.length,e.selectionEnd=a+t.length)},a=t=>{const n=e.value.substring(0,t).lastIndexOf("\n")+1;return{start:n,text:e.value.substring(n,t)}},i=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:i}=a(e.selectionStart),o=i.match(/^(\s*)([-*]|\d+\.)\s/);if(o){s.preventDefault();const a=o[1],r=o[2];if(i.trim()===r)e.setRangeText("",n,e.selectionStart,"end");else{const e=/^\d+\./.test(r)?`${parseInt(r)+1}.`:r;t(`\n${a}${e} `)}return}}if("`"===s.key&&!o){const n=e.selectionStart;if(e.value.substring(0,n).endsWith("``")&&!i(n-2))return s.preventDefault(),void t("`\n\n```",2)}if("Tab"!==s.key||!i(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}=a(e.selectionStart),i=n.replace(/^ {1,2}/,"");e.setRangeText(i,t,t+n.length,"end"),e.selectionStart=e.selectionEnd=t+i.length}else t(" ")});const s={"(":")","[":"]",'"':'"'};e.addEventListener("keydown",n=>{if(n.ctrlKey||n.metaKey||n.altKey)return;const a=s[n.key];if(!a)return;const i=e.selectionStart,o=e.selectionEnd;if(i!==o){n.preventDefault();const t=e.value.substring(i,o);e.setRangeText(n.key+t+a,i,o,"end"),e.selectionStart=i+1,e.selectionEnd=o+1}else n.preventDefault(),t(n.key+a,1)})}async onActionViewIncident(){const e=this.model.get("incident");e?.id&&a.showModel(new o({id:e.id}))}async onActionEditTicket(){await a.modelForm({title:`Edit Ticket #${this.model.get("id")}`,model:this.model,size:"lg",fields:r.edit.fields})&&this.render()}async onActionRefreshNotes(){await this.chatView.refresh(),await this._afterChatRefresh(),this.getApp()?.toast?.success("Notes refreshed")}async onActionAskAi(){await p(this,"incident.Ticket")}async _showInlineSelect(e,t){return new Promise(a=>{let i=!1;const s=new n({config:{items:e.map((e,t)=>({label:e.label,action:`pick-${t}`,class:e.active?"fw-bold":"",handler:()=>{i=!0,this.removeChild(s),a(e.value)}}))}}),o=s.closeDropdown.bind(s);s.closeDropdown=()=>{o(),i||(this.removeChild(s),a(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 d(e.get("id")),this.chatView.adapter=this.adapter,this.chatView.clearMessages(),await this.render(),await this.chatView.refresh(),await this._afterChatRefresh()}}export{TicketPanelView as default};
|
|
2
|
-
//# sourceMappingURL=TicketPanelView-
|
|
1
|
+
import{V as e}from"./View-BxlKR1IW.js";import{C as t}from"./ChatView-D2WOSxPu.js";import{C as n}from"./ContextMenu-T3yDdsIe.js";import{M as a}from"./Modal-KnJhNZ1E.js";import{T as i,b as s,I as o,c as r}from"./admin-models-DLtFboxy.js";import{U as l,G as c}from"./User-BM76Ughk.js";import{T as d,o as p}from"./admin-CucHFXfD.js";import{r as h}from"./Collection-DNmr743A.js";function g(e){return e?String(e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,"""):""}const m=/* @__PURE__ */new Set(["incident.RuleSet","incident.Incident","incident.Event","incident.Ticket","account.GeoLocatedIP"]),b={"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{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 b[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=>m.has(e.model)).map(e=>{const t=g(e.label)||`${g(e.model.split(".").pop())} #${g(e.pk)}`;return`<span class="ac-ref" data-action="open-ref" data-model="${g(e.model)}" data-pk="${g(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",a=this.action.context?.target;let i="";if(a&&m.has(a.model)){const e=g(this.action.context.label)||`${g(a.model.split(".").pop())} #${g(a.pk)}`;i=`<div class="ac-detail"><span class="ac-ref" data-action="open-ref" data-model="${g(a.model)}" data-pk="${g(a.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">${g(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 ${i}\n </div>\n `}_buildPendingTemplate(){const e=this.action.context?.target;let t="";if(e&&m.has(e.model)){const n=g(this.action.context.label)||`${g(e.model.split(".").pop())} #${g(e.pk)}`;t=`<br><span class="ac-ref" data-action="open-ref" data-model="${g(e.model)}" data-pk="${g(e.pk)}"><i class="bi bi-box-arrow-up-right"></i>${n}</span>`}const n=g(this.action.context?.detail),a=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">${g(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"${a}>Approve</button>\n <button class="btn-deny" data-action="deny"${a}>Deny</button>\n </div>\n </div>\n `}async onActionOpenRef(e,t){const n=t.dataset.model,i=t.dataset.pk;if(!m.has(n)||!/^\d+$/.test(i))return;const s=this.getApp(),o=s?.getModelByRef(n);o?.VIEW_CLASS&&a.showModel(new o({id:i}))}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 v=["new","open","in_progress","pending","resolved","qa","closed","ignored"],u={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"},f=[{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{constructor(e={}){super({className:"ticket-panel-view",...e}),this.model=e.model||new i(e.data||{})}onBeforeRender(){const e=this.model.get("status")||"new";this.statusPill=u[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">·</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">·</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">·</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>· {{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 & drop files to attach\n </div>\n </div>\n `}async onInit(){this.adapter=new d(this.model.get("id")),this.chatView=new t({containerId:"chat-area",adapter:this.adapter,theme:"compact",currentUserId:this._getCurrentUserId(),showInput:!1}),this.addChild(this.chatView);const e=new n({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 a=e.selectionStart,i=e.selectionEnd;e.setRangeText(t,a,i,"end"),e.selectionStart=e.selectionEnd=a+n,e.dispatchEvent(new Event("input")),e.scrollTop=e.scrollHeight},n=t=>{const n=e.selectionStart,a=e.selectionEnd,i=e.value.substring(n,a);i.startsWith(t)&&i.endsWith(t)?(e.setRangeText(i.slice(t.length,-t.length),n,a,"end"),e.selectionStart=n,e.selectionEnd=a-2*t.length):(e.setRangeText(t+i+t,n,a,"end"),e.selectionStart=n+t.length,e.selectionEnd=a+t.length)},a=t=>{const n=e.value.substring(0,t).lastIndexOf("\n")+1;return{start:n,text:e.value.substring(n,t)}},i=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:i}=a(e.selectionStart),o=i.match(/^(\s*)([-*]|\d+\.)\s/);if(o){s.preventDefault();const a=o[1],r=o[2];if(i.trim()===r)e.setRangeText("",n,e.selectionStart,"end");else{const e=/^\d+\./.test(r)?`${parseInt(r)+1}.`:r;t(`\n${a}${e} `)}return}}if("`"===s.key&&!o){const n=e.selectionStart;if(e.value.substring(0,n).endsWith("``")&&!i(n-2))return s.preventDefault(),void t("`\n\n```",2)}if("Tab"!==s.key||!i(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}=a(e.selectionStart),i=n.replace(/^ {1,2}/,"");e.setRangeText(i,t,t+n.length,"end"),e.selectionStart=e.selectionEnd=t+i.length}else t(" ")});const s={"(":")","[":"]",'"':'"'};e.addEventListener("keydown",n=>{if(n.ctrlKey||n.metaKey||n.altKey)return;const a=s[n.key];if(!a)return;const i=e.selectionStart,o=e.selectionEnd;if(i!==o){n.preventDefault();const t=e.value.substring(i,o);e.setRangeText(n.key+t+a,i,o,"end"),e.selectionStart=i+1,e.selectionEnd=o+1}else n.preventDefault(),t(n.key+a,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 a=Array.from(n.dataTransfer?.files||[]);if(!a.length)return;const{File:i}=await import("./Modal-KnJhNZ1E.js").then(e=>e.h);for(const e of a){const t=Date.now()+Math.random();this._addAttachChip(t,e.name,!0);try{const n=new i;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 a=this.element?.querySelector('[data-ref="attachments"]');if(!a)return;const i=document.createElement("span");i.className="tp-attach-chip"+(n?" uploading":""),i.dataset.chipId=e,i.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||i.querySelector(".remove").addEventListener("click",()=>{this._removeAttachChip(e)}),a.appendChild(i)}_updateAttachChip(e,t,n){const a=this.element?.querySelector(`[data-chip-id="${e}"]`);a&&(a.classList.remove("uploading"),a.innerHTML=`<i class="bi bi-paperclip"></i>${this._escapeHtml(t)}<span class="remove" data-remove="1"><i class="bi bi-x"></i></span>`,a.querySelector(".remove").addEventListener("click",()=>{this._stagedFiles=(this._stagedFiles||[]).filter(e=>e!==n),a.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,a)=>{const i=t.get(a),s=n?.element?.querySelector(".message-item");if(!s)return;if(s.querySelectorAll(".tp-edit-btn").forEach(e=>e.remove()),!i||i.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(i)}),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 a.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:i}=await import("./admin-models-DLtFboxy.js").then(e=>e.aQ),s=new i({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=v.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=f.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 a.form({title:"Assign User",icon:"bi-person-plus",size:"sm",fields:[{name:"assignee",type:"collection",label:"User",Collection:l,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(s).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 a.form({title:"Change Group",icon:"bi-people",size:"sm",fields:[{name:"group",type:"collection",label:"Group",Collection:c,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 a=await h.post("/api/docit/render",{markdown:e});n=a?.data?.data?.html||a?.data?.html||"",t=!!n}catch(i){}if(!t){const t=document.createElement("div");t.textContent=e,n=`<pre style="white-space:pre-wrap;">${t.innerHTML}</pre>`}"edit"===await a.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,"&").replace(/</g,"<").replace(/>/g,">")}</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})(),i=await a.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!=i&&(await this._saveAndSync({description:i}),this.render())}_wireMarkdownTextarea(e){const t=(t,n=t.length)=>{const a=e.selectionStart,i=e.selectionEnd;e.setRangeText(t,a,i,"end"),e.selectionStart=e.selectionEnd=a+n,e.dispatchEvent(new Event("input"))},n=t=>{const n=e.selectionStart,a=e.selectionEnd,i=e.value.substring(n,a);i.startsWith(t)&&i.endsWith(t)?(e.setRangeText(i.slice(t.length,-t.length),n,a,"end"),e.selectionStart=n,e.selectionEnd=a-2*t.length):(e.setRangeText(t+i+t,n,a,"end"),e.selectionStart=n+t.length,e.selectionEnd=a+t.length)},a=t=>{const n=e.value.substring(0,t).lastIndexOf("\n")+1;return{start:n,text:e.value.substring(n,t)}},i=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:i}=a(e.selectionStart),o=i.match(/^(\s*)([-*]|\d+\.)\s/);if(o){s.preventDefault();const a=o[1],r=o[2];if(i.trim()===r)e.setRangeText("",n,e.selectionStart,"end");else{const e=/^\d+\./.test(r)?`${parseInt(r)+1}.`:r;t(`\n${a}${e} `)}return}}if("`"===s.key&&!o){const n=e.selectionStart;if(e.value.substring(0,n).endsWith("``")&&!i(n-2))return s.preventDefault(),void t("`\n\n```",2)}if("Tab"!==s.key||!i(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}=a(e.selectionStart),i=n.replace(/^ {1,2}/,"");e.setRangeText(i,t,t+n.length,"end"),e.selectionStart=e.selectionEnd=t+i.length}else t(" ")});const s={"(":")","[":"]",'"':'"'};e.addEventListener("keydown",n=>{if(n.ctrlKey||n.metaKey||n.altKey)return;const a=s[n.key];if(!a)return;const i=e.selectionStart,o=e.selectionEnd;if(i!==o){n.preventDefault();const t=e.value.substring(i,o);e.setRangeText(n.key+t+a,i,o,"end"),e.selectionStart=i+1,e.selectionEnd=o+1}else n.preventDefault(),t(n.key+a,1)})}async onActionViewIncident(){const e=this.model.get("incident");e?.id&&a.showModel(new o({id:e.id}))}async onActionEditTicket(){await a.modelForm({title:`Edit Ticket #${this.model.get("id")}`,model:this.model,size:"lg",fields:r.edit.fields})&&this.render()}async onActionRefreshNotes(){await this.chatView.refresh(),await this._afterChatRefresh(),this.getApp()?.toast?.success("Notes refreshed")}async onActionAskAi(){await p(this,"incident.Ticket")}async _showInlineSelect(e,t){return new Promise(a=>{let i=!1;const s=new n({config:{items:e.map((e,t)=>({label:e.label,action:`pick-${t}`,class:e.active?"fw-bold":"",handler:()=>{i=!0,this.removeChild(s),a(e.value)}}))}}),o=s.closeDropdown.bind(s);s.closeDropdown=()=>{o(),i||(this.removeChild(s),a(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 d(e.get("id")),this.chatView.adapter=this.adapter,this.chatView.clearMessages(),await this.render(),await this.chatView.refresh(),await this._afterChatRefresh()}}export{TicketPanelView as default};
|
|
2
|
+
//# sourceMappingURL=TicketPanelView-DVePzWyJ.js.map
|