@dragonmastery/dragoncore-vue 0.0.3 → 0.0.4

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 (186) hide show
  1. package/LICENSE +2 -2
  2. package/dist/{ChangePasswordPage-Btu5lf-r.js → ChangePasswordPage-DCews8GU.js} +2 -2
  3. package/dist/{ChangePasswordPage-Btu5lf-r.js.map → ChangePasswordPage-DCews8GU.js.map} +1 -1
  4. package/dist/ChangePasswordPage-Dm5vW0nl.js +6 -0
  5. package/dist/CreateTeamForm-zVlGgmL9.js +27 -0
  6. package/dist/CreateTeamMemberForm-DkCbsJDn.js +27 -0
  7. package/dist/{CreateUserPage-Cmx8xjjv.js → CreateUserPage-B8qeBZij.js} +2 -2
  8. package/dist/{CreateUserPage-Cmx8xjjv.js.map → CreateUserPage-B8qeBZij.js.map} +1 -1
  9. package/dist/CreateUserPage-WjYDkwpb.js +6 -0
  10. package/dist/CreditBalanceDashboard-BTW4IK66.js +27 -0
  11. package/dist/CreditManagement-0JxmCIAd.js +27 -0
  12. package/dist/CustomerCreateSupportTicketForm-CQcv4vrX.js +27 -0
  13. package/dist/CustomerSupportTicketDetailPage-DQa_Zvfe.js +703 -0
  14. package/dist/CustomerSupportTicketDetailPage-DQa_Zvfe.js.map +1 -0
  15. package/dist/CustomerSupportTicketList-CMPRQ_7O.js +27 -0
  16. package/dist/CustomerSupportTicketParent-BOYIren9.js +7 -0
  17. package/dist/{CustomerSupportTicketParent-2mONd9kL.js → CustomerSupportTicketParent-sT8hpgrA.js} +6 -6
  18. package/dist/CustomerSupportTicketParent-sT8hpgrA.js.map +1 -0
  19. package/dist/CustomerSupportTicketSuccess-CnRWm6gX.js +27 -0
  20. package/dist/EditTeamForm-BM90JTjr.js +27 -0
  21. package/dist/EditTeamMemberForm-B8-pI6Xm.js +6 -0
  22. package/dist/{EditTeamMemberForm-ru4WgLz-.js → EditTeamMemberForm-CKbKomrL.js} +27 -5
  23. package/dist/EditTeamMemberForm-CKbKomrL.js.map +1 -0
  24. package/dist/EditUserPage-BG-Fkx_c.js +7 -0
  25. package/dist/{EditUserPage-BxJ5QvIM.js → EditUserPage-XqF25iwz.js} +4 -4
  26. package/dist/{EditUserPage-BxJ5QvIM.js.map → EditUserPage-XqF25iwz.js.map} +1 -1
  27. package/dist/ForgotPassword-CjWv2V7p.js +7 -0
  28. package/dist/{ForgotPassword-CqhenzUG.js → ForgotPassword-D3bjL48L.js} +2 -2
  29. package/dist/{ForgotPassword-CqhenzUG.js.map → ForgotPassword-D3bjL48L.js.map} +1 -1
  30. package/dist/LoginForm--br4Il85.js +7 -0
  31. package/dist/{LoginForm-_PZ51Uwe.js → LoginForm-C85U2E2r.js} +3 -3
  32. package/dist/{LoginForm-_PZ51Uwe.js.map → LoginForm-C85U2E2r.js.map} +1 -1
  33. package/dist/Logout-DHT-5Qz3.js +6 -0
  34. package/dist/{Logout-BMjiqHnS.js → Logout-DZuWLh0O.js} +3 -3
  35. package/dist/{Logout-BMjiqHnS.js.map → Logout-DZuWLh0O.js.map} +1 -1
  36. package/dist/ResetPassword-DAn7dYAp.js +27 -0
  37. package/dist/SavedFiltersPage-BNasEKOY.js +391 -0
  38. package/dist/SavedFiltersPage-BNasEKOY.js.map +1 -0
  39. package/dist/Signup-VZa7U-Ur.js +7 -0
  40. package/dist/{Signup-c2-_yMOM.js → Signup-hpV8J5cM.js} +3 -3
  41. package/dist/{Signup-c2-_yMOM.js.map → Signup-hpV8J5cM.js.map} +1 -1
  42. package/dist/StaffCreateSupportTicketForm-DoHCw60c.js +27 -0
  43. package/dist/StaffSupportTicketDetailPage-D49ibqrO.js +1884 -0
  44. package/dist/StaffSupportTicketDetailPage-D49ibqrO.js.map +1 -0
  45. package/dist/StaffSupportTicketList-BgCIa_9v.js +27 -0
  46. package/dist/StaffSupportTicketParent-C7Mm7W_0.js +7 -0
  47. package/dist/{StaffSupportTicketParent-Cx1buQZw.js → StaffSupportTicketParent-CxrPxXSH.js} +6 -6
  48. package/dist/StaffSupportTicketParent-CxrPxXSH.js.map +1 -0
  49. package/dist/StaffSupportTicketSuccess-DZF2WpZc.js +27 -0
  50. package/dist/SupportStaffPage-nd0HowtH.js +156 -0
  51. package/dist/SupportStaffPage-nd0HowtH.js.map +1 -0
  52. package/dist/SupportTicketDevLifecycleBadge-Cl4y47Sy.js +116 -0
  53. package/dist/SupportTicketDevLifecycleBadge-Cl4y47Sy.js.map +1 -0
  54. package/dist/SupportTicketMaintenancePage-rcJ7EfDj.js +56 -0
  55. package/dist/SupportTicketMaintenancePage-rcJ7EfDj.js.map +1 -0
  56. package/dist/TeamAttachmentsTab-BoOIuTU1.js +27 -0
  57. package/dist/{TeamHistoryTab-gB3H2KZv.js → TeamHistoryTab-CNelXR3Q.js} +19 -6
  58. package/dist/TeamHistoryTab-CNelXR3Q.js.map +1 -0
  59. package/dist/TeamHistoryTab-siesF93u.js +4 -0
  60. package/dist/TeamList-TpS3BhPd.js +27 -0
  61. package/dist/TeamMemberList-CQTxcWNS.js +27 -0
  62. package/dist/TeamMemberParent-Bt0kbyKQ.js +27 -0
  63. package/dist/{NoteList-C0hRPNMO.js → TeamNotesTab-BhVRLG8h.js} +30 -69
  64. package/dist/TeamNotesTab-BhVRLG8h.js.map +1 -0
  65. package/dist/TeamNotesTab-Crp-afAe.js +7 -0
  66. package/dist/TeamParent-BvLiiJq6.js +27 -0
  67. package/dist/TimelineNoteInput-BRsQ2QTz.js +490 -0
  68. package/dist/TimelineNoteInput-BRsQ2QTz.js.map +1 -0
  69. package/dist/{InlineAttachments-I39rOvip.js → TimelineSystemEvent-B69B3eeL.js} +589 -126
  70. package/dist/TimelineSystemEvent-B69B3eeL.js.map +1 -0
  71. package/dist/UserListPage-D68AjrjM.js +4 -0
  72. package/dist/{UserListPage-WU56KiWj.js → UserListPage-OGYOLwlw.js} +3 -3
  73. package/dist/{UserListPage-WU56KiWj.js.map → UserListPage-OGYOLwlw.js.map} +1 -1
  74. package/dist/UserProfilePage-Q68NAGQQ.js +7 -0
  75. package/dist/{UserProfilePage-BtLUY1kt.js → UserProfilePage-uAIfC_NW.js} +4 -4
  76. package/dist/{UserProfilePage-BtLUY1kt.js.map → UserProfilePage-uAIfC_NW.js.map} +1 -1
  77. package/dist/ViewTeam-Bb1WH_Us.js +27 -0
  78. package/dist/ViewTeamMember-CBTAnAhS.js +27 -0
  79. package/dist/{convertToLocalDateTime-D4IoNvRj.js → convertToLocalDateTime-DOSGtMn8.js} +13 -3
  80. package/dist/convertToLocalDateTime-DOSGtMn8.js.map +1 -0
  81. package/dist/{displayIdFormatter-Dz900Awr.js → displayIdFormatter-BoKcrgF5.js} +1 -1
  82. package/dist/{displayIdFormatter-Dz900Awr.js.map → displayIdFormatter-BoKcrgF5.js.map} +1 -1
  83. package/dist/extractRpcErrorMessage-C_UbKgHL.js +20 -0
  84. package/dist/extractRpcErrorMessage-C_UbKgHL.js.map +1 -0
  85. package/dist/index.d.ts +1658 -1384
  86. package/dist/index.js +25 -40
  87. package/dist/{src-o5fMIo5_.js → src-ChwBeNHB.js} +4006 -1522
  88. package/dist/src-ChwBeNHB.js.map +1 -0
  89. package/dist/{useMutation-CFwe7H9j.js → useMutation-B4_S4Xoa.js} +3 -3
  90. package/dist/{useMutation-CFwe7H9j.js.map → useMutation-B4_S4Xoa.js.map} +1 -1
  91. package/dist/{useQuery-p7oJO7OD.js → useQuery-B7ndu5_P.js} +3 -3
  92. package/dist/{useQuery-p7oJO7OD.js.map → useQuery-B7ndu5_P.js.map} +1 -1
  93. package/dist/{useQueryCache-ByayvZgZ.js → useQueryCache-DqcDMsxb.js} +2 -2
  94. package/dist/{useQueryCache-ByayvZgZ.js.map → useQueryCache-DqcDMsxb.js.map} +1 -1
  95. package/dist/{useRpcAuth-BLlRSHy8.js → useRpcAuth-Dp2sec-X.js} +13 -4
  96. package/dist/useRpcAuth-Dp2sec-X.js.map +1 -0
  97. package/package.json +3 -3
  98. package/dist/ChangePasswordPage-mBBuQMkT.js +0 -6
  99. package/dist/CreateTeamForm-n2ut93vM.js +0 -43
  100. package/dist/CreateTeamMemberForm-CcH3AxNL.js +0 -43
  101. package/dist/CreateUserPage-CDrGuW9B.js +0 -6
  102. package/dist/CreditBalanceDashboard-DLz0ioP3.js +0 -43
  103. package/dist/CreditManagement-D3q5S-qc.js +0 -43
  104. package/dist/CustomerCreateSupportTicketForm-Ci7QYkG-.js +0 -43
  105. package/dist/CustomerEditSupportTicketForm-Dd5ZB74k.js +0 -159
  106. package/dist/CustomerEditSupportTicketForm-Dd5ZB74k.js.map +0 -1
  107. package/dist/CustomerEditSupportTicketForm-lLchVjnw.js +0 -9
  108. package/dist/CustomerSupportTicketAttachmentsTab-gBrVO97t.js +0 -43
  109. package/dist/CustomerSupportTicketCustomerNotesTab-D0jhzbOY.js +0 -8
  110. package/dist/CustomerSupportTicketCustomerNotesTab-D1aa9It7.js +0 -23
  111. package/dist/CustomerSupportTicketCustomerNotesTab-D1aa9It7.js.map +0 -1
  112. package/dist/CustomerSupportTicketHistoryTab-BNTf8EZq.js +0 -6
  113. package/dist/CustomerSupportTicketHistoryTab-CFYN_Sa4.js +0 -17
  114. package/dist/CustomerSupportTicketHistoryTab-CFYN_Sa4.js.map +0 -1
  115. package/dist/CustomerSupportTicketList-BkOzFxMP.js +0 -6
  116. package/dist/CustomerSupportTicketList-C2nUPawb.js +0 -166
  117. package/dist/CustomerSupportTicketList-C2nUPawb.js.map +0 -1
  118. package/dist/CustomerSupportTicketParent-2mONd9kL.js.map +0 -1
  119. package/dist/CustomerSupportTicketParent-N8ko1yFE.js +0 -7
  120. package/dist/CustomerSupportTicketSuccess-w_-9NXT4.js +0 -43
  121. package/dist/CustomerViewSupportTicket-CVwNH0lS.js +0 -11
  122. package/dist/CustomerViewSupportTicket-tZkxragu.js +0 -363
  123. package/dist/CustomerViewSupportTicket-tZkxragu.js.map +0 -1
  124. package/dist/EditTeamForm-BioqiTWE.js +0 -43
  125. package/dist/EditTeamMemberForm-DCq0Gsn_.js +0 -7
  126. package/dist/EditTeamMemberForm-ru4WgLz-.js.map +0 -1
  127. package/dist/EditUserPage-XOBuxUxd.js +0 -7
  128. package/dist/FieldsetSection-CsHN38_o.js +0 -27
  129. package/dist/FieldsetSection-CsHN38_o.js.map +0 -1
  130. package/dist/ForgotPassword-CpqvcSFg.js +0 -7
  131. package/dist/InlineAttachments-I39rOvip.js.map +0 -1
  132. package/dist/LoginForm-AM0qkfbU.js +0 -7
  133. package/dist/Logout-BfiBjlaH.js +0 -6
  134. package/dist/NoteList-C0hRPNMO.js.map +0 -1
  135. package/dist/NotificationEmailsPage-BjRqtW95.js +0 -141
  136. package/dist/NotificationEmailsPage-BjRqtW95.js.map +0 -1
  137. package/dist/NotificationEmailsPage-bx-9rg3x.js +0 -7
  138. package/dist/ResetPassword-BQLkR9TZ.js +0 -43
  139. package/dist/Signup-CnCcQlB8.js +0 -7
  140. package/dist/StaffCreateSupportTicketForm-ChVFDJdA.js +0 -43
  141. package/dist/StaffEditSupportTicketForm-DY1Zkf5k.js +0 -9
  142. package/dist/StaffEditSupportTicketForm-DuUKuIGg.js +0 -263
  143. package/dist/StaffEditSupportTicketForm-DuUKuIGg.js.map +0 -1
  144. package/dist/StaffSupportTicketAttachmentsTab-DpDXsHXP.js +0 -43
  145. package/dist/StaffSupportTicketCustomerNotesTab-CusqQV2-.js +0 -23
  146. package/dist/StaffSupportTicketCustomerNotesTab-CusqQV2-.js.map +0 -1
  147. package/dist/StaffSupportTicketCustomerNotesTab-rbJHJ0_V.js +0 -8
  148. package/dist/StaffSupportTicketHistoryTab-D24myEm3.js +0 -17
  149. package/dist/StaffSupportTicketHistoryTab-D24myEm3.js.map +0 -1
  150. package/dist/StaffSupportTicketHistoryTab-nmVma5vp.js +0 -6
  151. package/dist/StaffSupportTicketInternalNotesTab-D8HM--dp.js +0 -23
  152. package/dist/StaffSupportTicketInternalNotesTab-D8HM--dp.js.map +0 -1
  153. package/dist/StaffSupportTicketInternalNotesTab-DihYd5XI.js +0 -8
  154. package/dist/StaffSupportTicketList-DelptSmK.js +0 -43
  155. package/dist/StaffSupportTicketParent-BCrj3ckV.js +0 -7
  156. package/dist/StaffSupportTicketParent-Cx1buQZw.js.map +0 -1
  157. package/dist/StaffSupportTicketSuccess-BYxtY5wZ.js +0 -43
  158. package/dist/StaffSupportTicketWorkflowTab-BrDDBeK9.js +0 -9
  159. package/dist/StaffSupportTicketWorkflowTab-DmVTPzxS.js +0 -1234
  160. package/dist/StaffSupportTicketWorkflowTab-DmVTPzxS.js.map +0 -1
  161. package/dist/SupportTicketHistoryTab-CLMopA7a.js +0 -220
  162. package/dist/SupportTicketHistoryTab-CLMopA7a.js.map +0 -1
  163. package/dist/SupportTicketStatusBadge-YdZzjvkh.js +0 -163
  164. package/dist/SupportTicketStatusBadge-YdZzjvkh.js.map +0 -1
  165. package/dist/TeamAttachmentsTab-BxUpTWYh.js +0 -43
  166. package/dist/TeamHistoryTab-CUCT9MRG.js +0 -5
  167. package/dist/TeamHistoryTab-gB3H2KZv.js.map +0 -1
  168. package/dist/TeamList-By6pzWm5.js +0 -43
  169. package/dist/TeamMemberList-CYV9fWEb.js +0 -43
  170. package/dist/TeamMemberParent-CVvGqpxD.js +0 -43
  171. package/dist/TeamNotesTab-pfXTDhg6.js +0 -23
  172. package/dist/TeamNotesTab-pfXTDhg6.js.map +0 -1
  173. package/dist/TeamNotesTab-u4cDC67X.js +0 -8
  174. package/dist/TeamParent-BxT1KubK.js +0 -43
  175. package/dist/UserListPage-DsQdH2Sm.js +0 -4
  176. package/dist/UserProfilePage-B73JhjUu.js +0 -7
  177. package/dist/ViewTeam-DzX-obEl.js +0 -43
  178. package/dist/ViewTeamMember-PF6S_4Pb.js +0 -43
  179. package/dist/ZiniaContainer-C7c7Vwkh.js +0 -18
  180. package/dist/ZiniaContainer-C7c7Vwkh.js.map +0 -1
  181. package/dist/convertToLocalDateTime-D4IoNvRj.js.map +0 -1
  182. package/dist/creditValueFormatter-DftEzu8d.js +0 -128
  183. package/dist/creditValueFormatter-DftEzu8d.js.map +0 -1
  184. package/dist/src-o5fMIo5_.js.map +0 -1
  185. package/dist/useRpcAuth-BLlRSHy8.js.map +0 -1
  186. /package/dist/{TeamMembersTab-CpE9BaCi.js → TeamMembersTab-DTJxmb-M.js} +0 -0
@@ -0,0 +1,1884 @@
1
+ import { t as BATCH_MODE } from "./useRpcAuth-Dp2sec-X.js";
2
+ import "./useQueryCache-DqcDMsxb.js";
3
+ import { t as useMutation } from "./useMutation-B4_S4Xoa.js";
4
+ import { t as useQuery } from "./useQuery-B7ndu5_P.js";
5
+ import { c as SupportTicketTypeBadge_default, i as SupportTicketApprovalBadge_default, l as SupportTicketPriorityBadge_default, n as TimelineItem_default, r as formatTicketDate, s as formatStaffCreditValue, t as TimelineSystemEvent_default } from "./TimelineSystemEvent-B69B3eeL.js";
6
+ import { n as formatToISODate } from "./convertToLocalDateTime-DOSGtMn8.js";
7
+ import { t as extractRpcErrorMessage } from "./extractRpcErrorMessage-C_UbKgHL.js";
8
+ import { t as formatTicketDisplayId } from "./displayIdFormatter-BoKcrgF5.js";
9
+ import { t as SupportTicketDevLifecycleBadge_default } from "./SupportTicketDevLifecycleBadge-Cl4y47Sy.js";
10
+ import { a as SupportTicketAttachmentsCollapsible_default, i as parseRecordVersions, n as MetadataField_default, r as ActionBannerAlert_default, t as TimelineNoteInput_default } from "./TimelineNoteInput-BRsQ2QTz.js";
11
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, inject, nextTick, normalizeClass, openBlock, ref, renderList, resolveComponent, toDisplayString, unref, vModelCheckbox, vModelSelect, vModelText, watch, withCtx, withDirectives } from "vue";
12
+ import { useRoute, useRouter } from "vue-router";
13
+ import { ApproveSupportTicketSchema, CompleteSupportTicketSchema, OPERATORS, RecordConst, SUPPORT_TICKET_PRIORITY_NUMBER_TO_LABEL, StaffSupportTicketUpdateSchema, supportTicketPriorityToNumber } from "@dragonmastery/dragoncore-shared";
14
+ import { useForm, withMetadata } from "@dragonmastery/zinia-forms-core";
15
+ import { toast } from "vue3-toastify";
16
+
17
+ //#region src/slices/support_ticket/staff/approveSupportTicketFormMetadata.ts
18
+ /**
19
+ * Form schema reuses approve workflow schema (credit_value only).
20
+ */
21
+ const ApproveSupportTicketFormSchema = ApproveSupportTicketSchema.pick({ credit_value: true });
22
+ const approveSupportTicketFormMetadata = withMetadata(ApproveSupportTicketFormSchema, "approveSupportTicketForm", { credit_value: {
23
+ label: "Credit value to deduct",
24
+ inputType: "currency",
25
+ step: .01,
26
+ placeholder: "0.00",
27
+ helpText: "Enter the credit amount to deduct from the customer"
28
+ } });
29
+
30
+ //#endregion
31
+ //#region src/slices/support_ticket/staff/components/ApproveSupportTicketModal.vue
32
+ const _hoisted_1$10 = { class: "modal-box" };
33
+ const _hoisted_2$10 = { class: "py-2 whitespace-pre-line text-sm text-base-content/80" };
34
+ const _hoisted_3$9 = {
35
+ key: 0,
36
+ class: "alert alert-error py-2 my-2"
37
+ };
38
+ const _hoisted_4$9 = { class: "text-sm" };
39
+ const _hoisted_5$9 = { class: "py-2 text-sm text-base-content/70 whitespace-pre-line" };
40
+ const _hoisted_6$9 = { class: "modal-action" };
41
+ const _hoisted_7$9 = ["disabled"];
42
+ const APPROVE_MODAL_INTRO = "This will:\n\n• Deduct credits from customer (amount set below)\n• Lock the support ticket item\n• Start development workflow\n";
43
+ const APPROVE_MODAL_FOOTER = `This action cannot be undone.`;
44
+ const _sfc_main$11 = /* @__PURE__ */ defineComponent({
45
+ __name: "ApproveSupportTicketModal",
46
+ props: {
47
+ ticket: {},
48
+ isOpen: { type: Boolean }
49
+ },
50
+ emits: ["close", "success"],
51
+ setup(__props, { emit: __emit }) {
52
+ const props = __props;
53
+ const emit = __emit;
54
+ const modalRef = ref(null);
55
+ const approveModalIntro = APPROVE_MODAL_INTRO;
56
+ const approveModalFooter = APPROVE_MODAL_FOOTER;
57
+ const { form, zinia, ZiniaForm, ZiniaSubmitButton, ZiniaFormErrorsSummary, refreshFormData } = useForm(approveSupportTicketFormMetadata, {
58
+ storeName: "approve-support-ticket-form",
59
+ persistToLocalStorage: false,
60
+ renderStyle: "daisy_ui",
61
+ fetchData: async () => {
62
+ const raw = props.ticket?.credit_value?.trim();
63
+ return { credit_value: raw && raw !== "" ? raw : "0" };
64
+ }
65
+ });
66
+ const { mutate: approveSupportTicket, loading: isApproving } = useMutation((api, input) => api.supportTickets.approveTicket(input), { invalidate: /^support-tickets?:/ });
67
+ watch(() => props.isOpen, (isOpen) => {
68
+ if (isOpen) {
69
+ refreshFormData();
70
+ modalRef.value?.showModal();
71
+ } else modalRef.value?.close();
72
+ }, { immediate: true });
73
+ function close() {
74
+ modalRef.value?.close();
75
+ emit("close");
76
+ }
77
+ async function handleSubmit(formData) {
78
+ if (!props.ticket) return;
79
+ await approveSupportTicket({
80
+ id: props.ticket.id,
81
+ credit_value: formData.credit_value
82
+ });
83
+ }
84
+ function handleSuccess() {
85
+ close();
86
+ toast.success(`Support Ticket approved! ${form.values.credit_value} credits deducted.`);
87
+ emit("success");
88
+ }
89
+ function handleError(error) {
90
+ const message = extractRpcErrorMessage(error, "Failed to approve");
91
+ form.setSubmitError(message);
92
+ toast.error(message);
93
+ }
94
+ return (_ctx, _cache) => {
95
+ return openBlock(), createElementBlock("dialog", {
96
+ ref_key: "modalRef",
97
+ ref: modalRef,
98
+ class: "modal"
99
+ }, [createElementVNode("div", _hoisted_1$10, [
100
+ _cache[0] || (_cache[0] = createElementVNode("h3", { class: "font-bold text-lg" }, "Approve Support Ticket?", -1)),
101
+ createElementVNode("p", _hoisted_2$10, toDisplayString(unref(approveModalIntro)), 1),
102
+ createVNode(unref(ZiniaForm), {
103
+ onHandleSubmit: handleSubmit,
104
+ onSuccess: handleSuccess,
105
+ onError: handleError,
106
+ title: "",
107
+ subtitle: ""
108
+ }, {
109
+ default: withCtx(() => [
110
+ createVNode(unref(zinia).CreditValueField),
111
+ unref(form).submitError ? (openBlock(), createElementBlock("div", _hoisted_3$9, [createElementVNode("span", _hoisted_4$9, toDisplayString(unref(form).submitError), 1)])) : createCommentVNode("v-if", true),
112
+ createElementVNode("p", _hoisted_5$9, toDisplayString(unref(approveModalFooter)), 1),
113
+ createElementVNode("div", _hoisted_6$9, [createElementVNode("button", {
114
+ type: "button",
115
+ class: "btn btn-ghost",
116
+ onClick: close,
117
+ disabled: unref(isApproving)
118
+ }, " Cancel ", 8, _hoisted_7$9), createVNode(unref(ZiniaSubmitButton), {
119
+ submitText: "Approve & Deduct",
120
+ submittingText: "Approving..."
121
+ })]),
122
+ createVNode(unref(ZiniaFormErrorsSummary), { title: "Please fix the following errors:" })
123
+ ]),
124
+ _: 1
125
+ })
126
+ ]), createElementVNode("form", {
127
+ method: "dialog",
128
+ class: "modal-backdrop"
129
+ }, [createElementVNode("button", {
130
+ type: "button",
131
+ onClick: close
132
+ }, "close")])], 512);
133
+ };
134
+ }
135
+ });
136
+ var ApproveSupportTicketModal_default = _sfc_main$11;
137
+
138
+ //#endregion
139
+ //#region src/slices/support_ticket/staff/components/ArchiveSupportTicketModal.vue
140
+ const _hoisted_1$9 = { class: "modal-box" };
141
+ const _hoisted_2$9 = { class: "font-bold text-lg" };
142
+ const _hoisted_3$8 = { class: "py-4 text-sm text-base-content/80" };
143
+ const _hoisted_4$8 = {
144
+ key: 0,
145
+ class: "alert alert-error py-2 mb-4"
146
+ };
147
+ const _hoisted_5$8 = { class: "text-sm" };
148
+ const _hoisted_6$8 = { class: "modal-action" };
149
+ const _hoisted_7$8 = ["disabled"];
150
+ const _hoisted_8$6 = ["disabled"];
151
+ const _hoisted_9$4 = {
152
+ key: 0,
153
+ class: "loading loading-spinner loading-sm mr-2"
154
+ };
155
+ const _sfc_main$10 = /* @__PURE__ */ defineComponent({
156
+ __name: "ArchiveSupportTicketModal",
157
+ props: {
158
+ ticket: {},
159
+ isOpen: { type: Boolean }
160
+ },
161
+ emits: ["close", "success"],
162
+ setup(__props, { emit: __emit }) {
163
+ const props = __props;
164
+ const emit = __emit;
165
+ const isArchived = computed(() => !!props.ticket?.archived_at);
166
+ const modalRef = ref(null);
167
+ const archiveError = ref(null);
168
+ const { mutate: archiveTicket, loading: isArchiving } = useMutation((api, input) => api.supportTickets.archiveTicket(input), { invalidate: /^support-tickets?:/ });
169
+ watch(() => props.isOpen, (isOpen) => {
170
+ if (isOpen) {
171
+ archiveError.value = null;
172
+ modalRef.value?.showModal();
173
+ } else modalRef.value?.close();
174
+ }, { immediate: true });
175
+ function close() {
176
+ modalRef.value?.close();
177
+ emit("close");
178
+ }
179
+ async function handleToggle() {
180
+ if (!props.ticket) return;
181
+ archiveError.value = null;
182
+ try {
183
+ await archiveTicket({ id: props.ticket.id });
184
+ toast.success(isArchived.value ? "Ticket unarchived" : "Ticket archived");
185
+ close();
186
+ emit("success");
187
+ } catch (e) {
188
+ const message = extractRpcErrorMessage(e, isArchived.value ? "Failed to unarchive" : "Failed to archive");
189
+ archiveError.value = message;
190
+ toast.error(message);
191
+ }
192
+ }
193
+ return (_ctx, _cache) => {
194
+ return openBlock(), createElementBlock("dialog", {
195
+ ref_key: "modalRef",
196
+ ref: modalRef,
197
+ class: "modal"
198
+ }, [createElementVNode("div", _hoisted_1$9, [
199
+ createElementVNode("h3", _hoisted_2$9, toDisplayString(isArchived.value ? "Unarchive Support Ticket?" : "Archive Support Ticket?"), 1),
200
+ createElementVNode("p", _hoisted_3$8, [isArchived.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode(" This will unarchive the ticket. Comments, edits, and attachments will be unlocked for customer and staff. ")], 64)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(" This will archive the ticket. Once archived, no one (customer or staff) can add or edit comments, edit the ticket, or add/delete attachments. ")], 64))]),
201
+ archiveError.value ? (openBlock(), createElementBlock("div", _hoisted_4$8, [createElementVNode("span", _hoisted_5$8, toDisplayString(archiveError.value), 1)])) : createCommentVNode("v-if", true),
202
+ createElementVNode("div", _hoisted_6$8, [createElementVNode("button", {
203
+ type: "button",
204
+ class: "btn btn-ghost",
205
+ onClick: close,
206
+ disabled: unref(isArchiving)
207
+ }, " Cancel ", 8, _hoisted_7$8), createElementVNode("button", {
208
+ type: "button",
209
+ class: normalizeClass(isArchived.value ? "btn btn-primary" : "btn btn-warning"),
210
+ onClick: handleToggle,
211
+ disabled: unref(isArchiving)
212
+ }, [unref(isArchiving) ? (openBlock(), createElementBlock("span", _hoisted_9$4)) : createCommentVNode("v-if", true), createTextVNode(" " + toDisplayString(unref(isArchiving) ? isArchived.value ? "Unarchiving..." : "Archiving..." : isArchived.value ? "Unarchive" : "Archive"), 1)], 10, _hoisted_8$6)])
213
+ ]), createElementVNode("form", {
214
+ method: "dialog",
215
+ class: "modal-backdrop"
216
+ }, [createElementVNode("button", {
217
+ type: "button",
218
+ onClick: close
219
+ }, "close")])], 512);
220
+ };
221
+ }
222
+ });
223
+ var ArchiveSupportTicketModal_default = _sfc_main$10;
224
+
225
+ //#endregion
226
+ //#region src/slices/support_ticket/staff/completeSupportTicketFormMetadata.ts
227
+ const CompleteSupportTicketFormSchema = CompleteSupportTicketSchema.pick({ delivered_value: true });
228
+ const completeSupportTicketFormMetadata = withMetadata(CompleteSupportTicketFormSchema, "completeSupportTicketForm", { delivered_value: {
229
+ label: "Actual Effort Delivered",
230
+ inputType: "currency",
231
+ step: .01,
232
+ placeholder: "100.00",
233
+ helpText: "How much effort did it actually take?"
234
+ } });
235
+
236
+ //#endregion
237
+ //#region src/slices/support_ticket/staff/components/CompleteSupportTicketModal.vue
238
+ const _hoisted_1$8 = { class: "modal-box" };
239
+ const _hoisted_2$8 = { class: "py-2 text-sm text-base-content/80" };
240
+ const _hoisted_3$7 = { class: "font-semibold" };
241
+ const _hoisted_4$7 = {
242
+ key: 0,
243
+ class: "alert alert-error py-2 my-2"
244
+ };
245
+ const _hoisted_5$7 = { class: "text-sm" };
246
+ const _hoisted_6$7 = { class: "modal-action" };
247
+ const _hoisted_7$7 = ["disabled"];
248
+ const _sfc_main$9 = /* @__PURE__ */ defineComponent({
249
+ __name: "CompleteSupportTicketModal",
250
+ props: {
251
+ ticket: {},
252
+ isOpen: { type: Boolean }
253
+ },
254
+ emits: ["close", "success"],
255
+ setup(__props, { emit: __emit }) {
256
+ const props = __props;
257
+ const emit = __emit;
258
+ const modalRef = ref(null);
259
+ const creditDisplay = computed(() => props.ticket ? formatStaffCreditValue(props.ticket.credit_value, props.ticket.approval_status) : "0");
260
+ const { form, zinia, ZiniaForm, ZiniaSubmitButton, ZiniaFormErrorsSummary, refreshFormData } = useForm(completeSupportTicketFormMetadata, {
261
+ storeName: "complete-support-ticket-form",
262
+ persistToLocalStorage: false,
263
+ renderStyle: "daisy_ui",
264
+ fetchData: async () => {
265
+ const display = creditDisplay.value;
266
+ return { delivered_value: display === "TBD" || display === "N/A" ? "0.00" : display };
267
+ }
268
+ });
269
+ const { mutate: completeSupportTicket, loading: isCompleting } = useMutation((api, input) => api.supportTickets.completeTicket(input), { invalidate: /^support-tickets?:/ });
270
+ watch(() => props.isOpen, (isOpen) => {
271
+ if (isOpen) {
272
+ refreshFormData();
273
+ modalRef.value?.showModal();
274
+ } else modalRef.value?.close();
275
+ }, { immediate: true });
276
+ function close() {
277
+ modalRef.value?.close();
278
+ emit("close");
279
+ }
280
+ async function handleSubmit(formData) {
281
+ if (!props.ticket) return;
282
+ await completeSupportTicket({
283
+ id: props.ticket.id,
284
+ delivered_value: formData.delivered_value
285
+ });
286
+ }
287
+ function handleSuccess() {
288
+ close();
289
+ toast.success("Support ticket marked as completed!");
290
+ emit("success");
291
+ }
292
+ function handleError(error) {
293
+ const message = extractRpcErrorMessage(error, "Failed to complete");
294
+ form.setSubmitError(message);
295
+ toast.error(message);
296
+ }
297
+ return (_ctx, _cache) => {
298
+ return openBlock(), createElementBlock("dialog", {
299
+ ref_key: "modalRef",
300
+ ref: modalRef,
301
+ class: "modal"
302
+ }, [createElementVNode("div", _hoisted_1$8, [
303
+ _cache[2] || (_cache[2] = createElementVNode("h3", { class: "font-bold text-lg" }, "Complete Support Ticket", -1)),
304
+ createElementVNode("p", _hoisted_2$8, [
305
+ _cache[0] || (_cache[0] = createTextVNode(" Customer was charged ", -1)),
306
+ createElementVNode("span", _hoisted_3$7, toDisplayString(creditDisplay.value), 1),
307
+ _cache[1] || (_cache[1] = createTextVNode(" credits. Enter the actual effort delivered: ", -1))
308
+ ]),
309
+ createVNode(unref(ZiniaForm), {
310
+ onHandleSubmit: handleSubmit,
311
+ onSuccess: handleSuccess,
312
+ onError: handleError,
313
+ title: "",
314
+ subtitle: ""
315
+ }, {
316
+ default: withCtx(() => [
317
+ createVNode(unref(zinia).DeliveredValueField, { placeholder: "100.00" }),
318
+ unref(form).submitError ? (openBlock(), createElementBlock("div", _hoisted_4$7, [createElementVNode("span", _hoisted_5$7, toDisplayString(unref(form).submitError), 1)])) : createCommentVNode("v-if", true),
319
+ createElementVNode("div", _hoisted_6$7, [createElementVNode("button", {
320
+ type: "button",
321
+ class: "btn btn-ghost",
322
+ onClick: close,
323
+ disabled: unref(isCompleting)
324
+ }, " Cancel ", 8, _hoisted_7$7), createVNode(unref(ZiniaSubmitButton), {
325
+ submitText: "Mark as Completed",
326
+ submittingText: "Completing..."
327
+ })]),
328
+ createVNode(unref(ZiniaFormErrorsSummary), { title: "Please fix the following errors:" })
329
+ ]),
330
+ _: 1
331
+ })
332
+ ]), createElementVNode("form", {
333
+ method: "dialog",
334
+ class: "modal-backdrop"
335
+ }, [createElementVNode("button", {
336
+ type: "button",
337
+ onClick: close
338
+ }, "close")])], 512);
339
+ };
340
+ }
341
+ });
342
+ var CompleteSupportTicketModal_default = _sfc_main$9;
343
+
344
+ //#endregion
345
+ //#region src/slices/support_ticket/staff/components/ConvertToInternalModal.vue
346
+ const _hoisted_1$7 = { class: "modal-box" };
347
+ const _hoisted_2$7 = { class: "py-4 whitespace-pre-line text-sm text-base-content/80" };
348
+ const _hoisted_3$6 = {
349
+ key: 0,
350
+ class: "alert alert-error py-2 mb-4"
351
+ };
352
+ const _hoisted_4$6 = { class: "text-sm" };
353
+ const _hoisted_5$6 = { class: "modal-action" };
354
+ const _hoisted_6$6 = ["disabled"];
355
+ const _hoisted_7$6 = ["disabled"];
356
+ const _hoisted_8$5 = {
357
+ key: 0,
358
+ class: "loading loading-spinner loading-sm mr-2"
359
+ };
360
+ const _sfc_main$8 = /* @__PURE__ */ defineComponent({
361
+ __name: "ConvertToInternalModal",
362
+ props: {
363
+ ticket: {},
364
+ isOpen: { type: Boolean }
365
+ },
366
+ emits: ["close", "success"],
367
+ setup(__props, { emit: __emit }) {
368
+ const props = __props;
369
+ const emit = __emit;
370
+ const modalRef = ref(null);
371
+ const convertError = ref(null);
372
+ const convertMessage = ref("");
373
+ const { mutate: convertToInternal, loading: isConverting } = useMutation((api, id) => api.supportTickets.convertToInternal(id), { invalidate: /^support-tickets?:/ });
374
+ watch([() => props.isOpen, () => props.ticket], ([isOpen, ticket]) => {
375
+ if (isOpen && ticket) {
376
+ convertMessage.value = "This will:\n\n• Change status to INTERNAL\n• Bypass customer approval workflow\n" + (ticket.credit_value && parseFloat(ticket.credit_value) > 0 ? `• Clear ${ticket.credit_value} credit estimate\n` : "") + "• Set dev lifecycle to BACKLOG\n• Allow immediate development start\n\nThis change can be useful for converting customer ideas into internal improvements.";
377
+ convertError.value = null;
378
+ modalRef.value?.showModal();
379
+ } else modalRef.value?.close();
380
+ }, { immediate: true });
381
+ function close() {
382
+ modalRef.value?.close();
383
+ emit("close");
384
+ }
385
+ async function handleConvert() {
386
+ if (!props.ticket) return;
387
+ convertError.value = null;
388
+ try {
389
+ await convertToInternal(props.ticket.id);
390
+ toast.success("Converted to internal task successfully");
391
+ close();
392
+ emit("success");
393
+ } catch (e) {
394
+ const message = extractRpcErrorMessage(e, "Failed to convert");
395
+ convertError.value = message;
396
+ toast.error(message);
397
+ }
398
+ }
399
+ return (_ctx, _cache) => {
400
+ return openBlock(), createElementBlock("dialog", {
401
+ ref_key: "modalRef",
402
+ ref: modalRef,
403
+ class: "modal"
404
+ }, [createElementVNode("div", _hoisted_1$7, [
405
+ _cache[0] || (_cache[0] = createElementVNode("h3", { class: "font-bold text-lg" }, "Convert to Internal Task?", -1)),
406
+ createElementVNode("p", _hoisted_2$7, toDisplayString(convertMessage.value), 1),
407
+ convertError.value ? (openBlock(), createElementBlock("div", _hoisted_3$6, [createElementVNode("span", _hoisted_4$6, toDisplayString(convertError.value), 1)])) : createCommentVNode("v-if", true),
408
+ createElementVNode("div", _hoisted_5$6, [createElementVNode("button", {
409
+ type: "button",
410
+ class: "btn btn-ghost",
411
+ onClick: close,
412
+ disabled: unref(isConverting)
413
+ }, " Cancel ", 8, _hoisted_6$6), createElementVNode("button", {
414
+ type: "button",
415
+ class: "btn btn-primary",
416
+ onClick: handleConvert,
417
+ disabled: unref(isConverting)
418
+ }, [unref(isConverting) ? (openBlock(), createElementBlock("span", _hoisted_8$5)) : createCommentVNode("v-if", true), createTextVNode(" " + toDisplayString(unref(isConverting) ? "Converting..." : "Convert to Internal"), 1)], 8, _hoisted_7$6)])
419
+ ]), createElementVNode("form", {
420
+ method: "dialog",
421
+ class: "modal-backdrop"
422
+ }, [createElementVNode("button", {
423
+ type: "button",
424
+ onClick: close
425
+ }, "close")])], 512);
426
+ };
427
+ }
428
+ });
429
+ var ConvertToInternalModal_default = _sfc_main$8;
430
+
431
+ //#endregion
432
+ //#region src/slices/support_ticket/staff/components/DeleteSupportTicketModal.vue
433
+ const _hoisted_1$6 = { class: "modal-box" };
434
+ const _hoisted_2$6 = {
435
+ key: 0,
436
+ class: "alert alert-error py-2 mb-4"
437
+ };
438
+ const _hoisted_3$5 = { class: "text-sm" };
439
+ const _hoisted_4$5 = { class: "modal-action" };
440
+ const _hoisted_5$5 = ["disabled"];
441
+ const _hoisted_6$5 = ["disabled"];
442
+ const _hoisted_7$5 = {
443
+ key: 0,
444
+ class: "loading loading-spinner loading-sm mr-2"
445
+ };
446
+ const _sfc_main$7 = /* @__PURE__ */ defineComponent({
447
+ __name: "DeleteSupportTicketModal",
448
+ props: {
449
+ ticket: {},
450
+ isOpen: { type: Boolean }
451
+ },
452
+ emits: ["close", "success"],
453
+ setup(__props, { emit: __emit }) {
454
+ const props = __props;
455
+ const emit = __emit;
456
+ const modalRef = ref(null);
457
+ const deleteError = ref(null);
458
+ const { mutate: deleteTicket, loading: isDeleting } = useMutation((api, id) => api.supportTickets.deleteTicket(id), { invalidate: /^support-tickets?:/ });
459
+ watch(() => props.isOpen, (isOpen) => {
460
+ if (isOpen) {
461
+ deleteError.value = null;
462
+ modalRef.value?.showModal();
463
+ } else modalRef.value?.close();
464
+ }, { immediate: true });
465
+ function close() {
466
+ modalRef.value?.close();
467
+ emit("close");
468
+ }
469
+ async function handleDelete() {
470
+ if (!props.ticket) return;
471
+ deleteError.value = null;
472
+ try {
473
+ await deleteTicket(props.ticket.id);
474
+ toast.success("Ticket deleted");
475
+ close();
476
+ emit("success");
477
+ } catch (e) {
478
+ const message = extractRpcErrorMessage(e, "Failed to delete");
479
+ deleteError.value = message;
480
+ toast.error(message);
481
+ }
482
+ }
483
+ return (_ctx, _cache) => {
484
+ return openBlock(), createElementBlock("dialog", {
485
+ ref_key: "modalRef",
486
+ ref: modalRef,
487
+ class: "modal"
488
+ }, [createElementVNode("div", _hoisted_1$6, [
489
+ _cache[0] || (_cache[0] = createElementVNode("h3", { class: "font-bold text-lg" }, "Delete Support Ticket?", -1)),
490
+ _cache[1] || (_cache[1] = createElementVNode("p", { class: "py-4 text-sm text-base-content/80" }, " This will soft-delete the ticket. It can be restored if needed. This action cannot be undone for approved tickets (you cannot delete approved tickets). ", -1)),
491
+ deleteError.value ? (openBlock(), createElementBlock("div", _hoisted_2$6, [createElementVNode("span", _hoisted_3$5, toDisplayString(deleteError.value), 1)])) : createCommentVNode("v-if", true),
492
+ createElementVNode("div", _hoisted_4$5, [createElementVNode("button", {
493
+ type: "button",
494
+ class: "btn btn-ghost",
495
+ onClick: close,
496
+ disabled: unref(isDeleting)
497
+ }, " Cancel ", 8, _hoisted_5$5), createElementVNode("button", {
498
+ type: "button",
499
+ class: "btn btn-error",
500
+ onClick: handleDelete,
501
+ disabled: unref(isDeleting)
502
+ }, [unref(isDeleting) ? (openBlock(), createElementBlock("span", _hoisted_7$5)) : createCommentVNode("v-if", true), createTextVNode(" " + toDisplayString(unref(isDeleting) ? "Deleting..." : "Delete"), 1)], 8, _hoisted_6$5)])
503
+ ]), createElementVNode("form", {
504
+ method: "dialog",
505
+ class: "modal-backdrop"
506
+ }, [createElementVNode("button", {
507
+ type: "button",
508
+ onClick: close
509
+ }, "close")])], 512);
510
+ };
511
+ }
512
+ });
513
+ var DeleteSupportTicketModal_default = _sfc_main$7;
514
+
515
+ //#endregion
516
+ //#region src/slices/support_ticket/staff/components/RevertSupportTicketModal.vue
517
+ const _hoisted_1$5 = { class: "modal-box" };
518
+ const _hoisted_2$5 = { class: "py-4 whitespace-pre-line text-sm text-base-content/80" };
519
+ const _hoisted_3$4 = {
520
+ key: 0,
521
+ class: "alert alert-error py-2 mb-4"
522
+ };
523
+ const _hoisted_4$4 = { class: "text-sm" };
524
+ const _hoisted_5$4 = { class: "modal-action" };
525
+ const _hoisted_6$4 = ["disabled"];
526
+ const _hoisted_7$4 = ["disabled"];
527
+ const _hoisted_8$4 = {
528
+ key: 0,
529
+ class: "loading loading-spinner loading-sm mr-2"
530
+ };
531
+ const _sfc_main$6 = /* @__PURE__ */ defineComponent({
532
+ __name: "RevertSupportTicketModal",
533
+ props: {
534
+ ticket: {},
535
+ isOpen: { type: Boolean }
536
+ },
537
+ emits: ["close", "success"],
538
+ setup(__props, { emit: __emit }) {
539
+ const props = __props;
540
+ const emit = __emit;
541
+ const modalRef = ref(null);
542
+ const revertError = ref(null);
543
+ const revertMessage = ref("");
544
+ const { mutate: revertSupportTicket, loading: isReverting } = useMutation((api, input) => api.supportTickets.revertTicket(input), { invalidate: /^support-tickets?:/ });
545
+ watch([() => props.isOpen, () => props.ticket], ([isOpen, ticket]) => {
546
+ if (isOpen && ticket) {
547
+ const creditDisplay = formatStaffCreditValue(ticket.credit_value, ticket.approval_status);
548
+ revertMessage.value = "This will:\n\n• Change status back to PENDING\n• Unlock the support ticket item\n" + (creditDisplay !== "TBD" && creditDisplay !== "N/A" ? `• Refund ${creditDisplay} credits to customer\n` : "") + "• Allow the customer to edit again\n\nAre you sure you want to revert this decision?";
549
+ revertError.value = null;
550
+ modalRef.value?.showModal();
551
+ } else modalRef.value?.close();
552
+ }, { immediate: true });
553
+ function close() {
554
+ modalRef.value?.close();
555
+ emit("close");
556
+ }
557
+ async function handleRevert() {
558
+ if (!props.ticket) return;
559
+ revertError.value = null;
560
+ try {
561
+ await revertSupportTicket({ id: props.ticket.id });
562
+ const creditDisplay = formatStaffCreditValue(props.ticket.credit_value, props.ticket.approval_status);
563
+ const refundMessage = creditDisplay !== "TBD" && creditDisplay !== "N/A" ? ` ${creditDisplay} credits refunded.` : "";
564
+ toast.success(`Support Ticket reverted to PENDING.${refundMessage}`);
565
+ close();
566
+ emit("success");
567
+ } catch (e) {
568
+ const message = extractRpcErrorMessage(e, "Failed to revert");
569
+ revertError.value = message;
570
+ toast.error(message);
571
+ }
572
+ }
573
+ return (_ctx, _cache) => {
574
+ return openBlock(), createElementBlock("dialog", {
575
+ ref_key: "modalRef",
576
+ ref: modalRef,
577
+ class: "modal"
578
+ }, [createElementVNode("div", _hoisted_1$5, [
579
+ _cache[0] || (_cache[0] = createElementVNode("h3", { class: "font-bold text-lg" }, "Revert Support Ticket to PENDING?", -1)),
580
+ createElementVNode("p", _hoisted_2$5, toDisplayString(revertMessage.value), 1),
581
+ revertError.value ? (openBlock(), createElementBlock("div", _hoisted_3$4, [createElementVNode("span", _hoisted_4$4, toDisplayString(revertError.value), 1)])) : createCommentVNode("v-if", true),
582
+ createElementVNode("div", _hoisted_5$4, [createElementVNode("button", {
583
+ type: "button",
584
+ class: "btn btn-ghost",
585
+ onClick: close,
586
+ disabled: unref(isReverting)
587
+ }, " Cancel ", 8, _hoisted_6$4), createElementVNode("button", {
588
+ type: "button",
589
+ class: "btn btn-warning",
590
+ onClick: handleRevert,
591
+ disabled: unref(isReverting)
592
+ }, [unref(isReverting) ? (openBlock(), createElementBlock("span", _hoisted_8$4)) : createCommentVNode("v-if", true), createTextVNode(" " + toDisplayString(unref(isReverting) ? "Reverting..." : "Revert to PENDING"), 1)], 8, _hoisted_7$4)])
593
+ ]), createElementVNode("form", {
594
+ method: "dialog",
595
+ class: "modal-backdrop"
596
+ }, [createElementVNode("button", {
597
+ type: "button",
598
+ onClick: close
599
+ }, "close")])], 512);
600
+ };
601
+ }
602
+ });
603
+ var RevertSupportTicketModal_default = _sfc_main$6;
604
+
605
+ //#endregion
606
+ //#region src/slices/support_ticket/staff/components/StaffActionBanner.vue
607
+ const _sfc_main$5 = /* @__PURE__ */ defineComponent({
608
+ __name: "StaffActionBanner",
609
+ props: { ticket: {} },
610
+ emits: [
611
+ "approve",
612
+ "reject",
613
+ "revert",
614
+ "convertToInternal",
615
+ "delete",
616
+ "complete",
617
+ "archive"
618
+ ],
619
+ setup(__props, { emit: __emit }) {
620
+ const props = __props;
621
+ const emit = __emit;
622
+ const lockedDateFormatted = computed(() => {
623
+ const at = props.ticket.locked_approval_at;
624
+ return at ? formatTicketDate(at).formatted : "";
625
+ });
626
+ const isCompleted = computed(() => props.ticket.status === "COMPLETED");
627
+ const isArchived = computed(() => !!props.ticket.archived_at);
628
+ return (_ctx, _cache) => {
629
+ return isArchived.value ? (openBlock(), createBlock(ActionBannerAlert_default, {
630
+ key: 0,
631
+ variant: "neutral",
632
+ icon: "archive"
633
+ }, {
634
+ actions: withCtx(() => [createElementVNode("button", {
635
+ type: "button",
636
+ class: "btn btn-ghost btn-sm",
637
+ onClick: _cache[0] || (_cache[0] = ($event) => emit("archive"))
638
+ }, " Unarchive ")]),
639
+ default: withCtx(() => [_cache[14] || (_cache[14] = createTextVNode(" This ticket is archived. Comments, edits, and attachments are locked for both customer and staff. ", -1))]),
640
+ _: 1
641
+ })) : __props.ticket.approval_status === "PENDING" ? (openBlock(), createBlock(ActionBannerAlert_default, {
642
+ key: 1,
643
+ variant: "warning",
644
+ icon: "clock"
645
+ }, {
646
+ actions: withCtx(() => [
647
+ createElementVNode("button", {
648
+ type: "button",
649
+ class: "btn btn-success btn-sm",
650
+ onClick: _cache[1] || (_cache[1] = ($event) => emit("approve"))
651
+ }, " Approve "),
652
+ createElementVNode("button", {
653
+ type: "button",
654
+ class: "btn btn-error btn-sm",
655
+ onClick: _cache[2] || (_cache[2] = ($event) => emit("reject"))
656
+ }, " Reject "),
657
+ createElementVNode("button", {
658
+ type: "button",
659
+ class: "btn btn-primary btn-sm",
660
+ onClick: _cache[3] || (_cache[3] = ($event) => emit("convertToInternal"))
661
+ }, " Convert to Internal "),
662
+ createElementVNode("button", {
663
+ type: "button",
664
+ class: "btn btn-ghost btn-sm",
665
+ onClick: _cache[4] || (_cache[4] = ($event) => emit("delete"))
666
+ }, " Delete ")
667
+ ]),
668
+ default: withCtx(() => [_cache[15] || (_cache[15] = createTextVNode(" This ticket is awaiting approval. ", -1))]),
669
+ _: 1
670
+ })) : __props.ticket.approval_status === "APPROVED" && __props.ticket.locked_approval_at ? (openBlock(), createBlock(ActionBannerAlert_default, {
671
+ key: 2,
672
+ variant: "success",
673
+ icon: "lock"
674
+ }, {
675
+ actions: withCtx(() => [createElementVNode("button", {
676
+ type: "button",
677
+ class: "btn btn-warning btn-sm",
678
+ onClick: _cache[5] || (_cache[5] = ($event) => emit("revert"))
679
+ }, " Revert "), !isCompleted.value ? (openBlock(), createElementBlock("button", {
680
+ key: 0,
681
+ type: "button",
682
+ class: "btn btn-primary btn-sm",
683
+ onClick: _cache[6] || (_cache[6] = ($event) => emit("complete"))
684
+ }, " Complete ")) : !isArchived.value ? (openBlock(), createElementBlock("button", {
685
+ key: 1,
686
+ type: "button",
687
+ class: "btn btn-ghost btn-sm",
688
+ onClick: _cache[7] || (_cache[7] = ($event) => emit("archive"))
689
+ }, " Archive ")) : createCommentVNode("v-if", true)]),
690
+ default: withCtx(() => [createTextVNode(" This ticket was approved on " + toDisplayString(lockedDateFormatted.value) + ". ", 1)]),
691
+ _: 1
692
+ })) : __props.ticket.approval_status === "REJECTED" && __props.ticket.locked_approval_at ? (openBlock(), createBlock(ActionBannerAlert_default, {
693
+ key: 3,
694
+ variant: "error",
695
+ icon: "lock"
696
+ }, {
697
+ actions: withCtx(() => [
698
+ createElementVNode("button", {
699
+ type: "button",
700
+ class: "btn btn-warning btn-sm",
701
+ onClick: _cache[8] || (_cache[8] = ($event) => emit("revert"))
702
+ }, " Revert "),
703
+ !isArchived.value ? (openBlock(), createElementBlock("button", {
704
+ key: 0,
705
+ type: "button",
706
+ class: "btn btn-ghost btn-sm",
707
+ onClick: _cache[9] || (_cache[9] = ($event) => emit("archive"))
708
+ }, " Archive ")) : createCommentVNode("v-if", true),
709
+ createElementVNode("button", {
710
+ type: "button",
711
+ class: "btn btn-ghost btn-sm",
712
+ onClick: _cache[10] || (_cache[10] = ($event) => emit("delete"))
713
+ }, " Delete ")
714
+ ]),
715
+ default: withCtx(() => [createTextVNode(" This ticket was rejected on " + toDisplayString(lockedDateFormatted.value) + ". ", 1)]),
716
+ _: 1
717
+ })) : __props.ticket.approval_status === "INTERNAL" ? (openBlock(), createBlock(ActionBannerAlert_default, {
718
+ key: 4,
719
+ variant: "info",
720
+ icon: "info"
721
+ }, {
722
+ actions: withCtx(() => [!isCompleted.value ? (openBlock(), createElementBlock("button", {
723
+ key: 0,
724
+ type: "button",
725
+ class: "btn btn-primary btn-sm",
726
+ onClick: _cache[11] || (_cache[11] = ($event) => emit("complete"))
727
+ }, " Complete ")) : !isArchived.value ? (openBlock(), createElementBlock("button", {
728
+ key: 1,
729
+ type: "button",
730
+ class: "btn btn-ghost btn-sm",
731
+ onClick: _cache[12] || (_cache[12] = ($event) => emit("archive"))
732
+ }, " Archive ")) : createCommentVNode("v-if", true), createElementVNode("button", {
733
+ type: "button",
734
+ class: "btn btn-ghost btn-sm",
735
+ onClick: _cache[13] || (_cache[13] = ($event) => emit("delete"))
736
+ }, " Delete ")]),
737
+ default: withCtx(() => [_cache[16] || (_cache[16] = createTextVNode(" Internal ticket. ", -1))]),
738
+ _: 1
739
+ })) : createCommentVNode("v-if", true);
740
+ };
741
+ }
742
+ });
743
+ var StaffActionBanner_default = _sfc_main$5;
744
+
745
+ //#endregion
746
+ //#region src/slices/support_ticket/staff/components/StaffMetadataCard.vue
747
+ const _hoisted_1$4 = { class: "border border-base-200 rounded-lg p-4 md:p-6" };
748
+ const _hoisted_2$4 = { class: "grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-3" };
749
+ const _hoisted_3$3 = { key: 0 };
750
+ const _hoisted_4$3 = {
751
+ key: 1,
752
+ class: "text-base-content/50 italic text-sm"
753
+ };
754
+ const _hoisted_5$3 = { class: "md:col-span-2" };
755
+ const _hoisted_6$3 = {
756
+ key: 0,
757
+ class: "whitespace-pre-wrap text-sm"
758
+ };
759
+ const _hoisted_7$3 = {
760
+ key: 0,
761
+ class: "text-base-content/50"
762
+ };
763
+ const _hoisted_8$3 = { class: "text-base-content/50" };
764
+ const _hoisted_9$3 = {
765
+ key: 1,
766
+ class: "text-base-content/50 italic text-sm"
767
+ };
768
+ const _hoisted_10$3 = { class: "text-base-content/50" };
769
+ const _hoisted_11$3 = { class: "text-base-content/50" };
770
+ const _hoisted_12$3 = { class: "inline-flex items-center gap-1" };
771
+ const _hoisted_13$3 = {
772
+ key: 0,
773
+ "aria-hidden": "true"
774
+ };
775
+ const _sfc_main$4 = /* @__PURE__ */ defineComponent({
776
+ __name: "StaffMetadataCard",
777
+ props: { ticket: {} },
778
+ setup(__props) {
779
+ const props = __props;
780
+ const assigneeDisplay = computed(() => {
781
+ const assigneeId = props.ticket?.assigned_to;
782
+ if (!assigneeId) return null;
783
+ return props.ticket.assigned_to_display_name ?? assigneeId;
784
+ });
785
+ const ticketDisplayId = computed(() => formatTicketDisplayId(props.ticket.display_id, props.ticket.display_id_prefix, props.ticket.id));
786
+ const createdFormatted = computed(() => {
787
+ const at = props.ticket.created_at;
788
+ return at ? formatTicketDate(at).formatted : "";
789
+ });
790
+ const createdRelative = computed(() => {
791
+ const at = props.ticket.created_at;
792
+ return at ? formatTicketDate(at).relative : "";
793
+ });
794
+ const startFormatted = computed(() => props.ticket.start_at ? formatTicketDate(props.ticket.start_at).formatted : "");
795
+ const startRelative = computed(() => props.ticket.start_at ? formatTicketDate(props.ticket.start_at).relative : "");
796
+ const targetFormatted = computed(() => props.ticket.target_at ? formatTicketDate(props.ticket.target_at).formatted : "");
797
+ const targetRelative = computed(() => props.ticket.target_at ? formatTicketDate(props.ticket.target_at).relative : "");
798
+ const completedFormatted = computed(() => props.ticket.completed_at ? formatTicketDate(props.ticket.completed_at).formatted : "");
799
+ const completedRelative = computed(() => props.ticket.completed_at ? formatTicketDate(props.ticket.completed_at).relative : "");
800
+ const isTargetOverdue = computed(() => {
801
+ if (!props.ticket.target_at || props.ticket.completed_at) return false;
802
+ return new Date(props.ticket.target_at) < /* @__PURE__ */ new Date() && !props.ticket.completed_at;
803
+ });
804
+ const creditDisplay = computed(() => formatStaffCreditValue(props.ticket.credit_value, props.ticket.approval_status));
805
+ return (_ctx, _cache) => {
806
+ return openBlock(), createElementBlock("div", _hoisted_1$4, [createElementVNode("div", _hoisted_2$4, [
807
+ createVNode(MetadataField_default, { label: "Status" }, {
808
+ default: withCtx(() => [createVNode(SupportTicketApprovalBadge_default, {
809
+ "approval-status": __props.ticket.approval_status,
810
+ size: "sm"
811
+ }, null, 8, ["approval-status"])]),
812
+ _: 1
813
+ }),
814
+ createVNode(MetadataField_default, { label: "Dev Lifecycle" }, {
815
+ empty: withCtx(() => [..._cache[0] || (_cache[0] = [createElementVNode("span", { class: "text-base-content/50 italic text-sm" }, "Not started", -1)])]),
816
+ default: withCtx(() => [__props.ticket.dev_lifecycle ? (openBlock(), createBlock(SupportTicketDevLifecycleBadge_default, {
817
+ key: 0,
818
+ "dev-lifecycle": __props.ticket.dev_lifecycle,
819
+ size: "sm"
820
+ }, null, 8, ["dev-lifecycle"])) : createCommentVNode("v-if", true)]),
821
+ _: 1
822
+ }),
823
+ createVNode(MetadataField_default, { label: "Type" }, {
824
+ default: withCtx(() => [createVNode(SupportTicketTypeBadge_default, {
825
+ type: __props.ticket.type,
826
+ size: "sm"
827
+ }, null, 8, ["type"])]),
828
+ _: 1
829
+ }),
830
+ createVNode(MetadataField_default, { label: "Priority" }, {
831
+ default: withCtx(() => [createVNode(SupportTicketPriorityBadge_default, {
832
+ priority: __props.ticket.priority,
833
+ size: "sm"
834
+ }, null, 8, ["priority"])]),
835
+ _: 1
836
+ }),
837
+ createVNode(MetadataField_default, { label: "Assignee" }, {
838
+ default: withCtx(() => [assigneeDisplay.value ? (openBlock(), createElementBlock("span", _hoisted_3$3, toDisplayString(assigneeDisplay.value), 1)) : (openBlock(), createElementBlock("span", _hoisted_4$3, "Unassigned"))]),
839
+ _: 1
840
+ }),
841
+ createVNode(MetadataField_default, { label: "Requester" }, {
842
+ default: withCtx(() => [createTextVNode(toDisplayString(__props.ticket.created_by_display_name), 1)]),
843
+ _: 1
844
+ }),
845
+ createElementVNode("div", _hoisted_5$3, [createVNode(MetadataField_default, { label: "Description" }, {
846
+ empty: withCtx(() => [..._cache[1] || (_cache[1] = [createElementVNode("span", { class: "text-base-content/50 italic text-sm" }, "No description provided", -1)])]),
847
+ default: withCtx(() => [__props.ticket.description?.trim() ? (openBlock(), createElementBlock("p", _hoisted_6$3, toDisplayString(__props.ticket.description), 1)) : createCommentVNode("v-if", true)]),
848
+ _: 1
849
+ })]),
850
+ createVNode(MetadataField_default, {
851
+ label: "Ticket ID",
852
+ copyable: true
853
+ }, {
854
+ default: withCtx(() => [createTextVNode(toDisplayString(ticketDisplayId.value), 1)]),
855
+ _: 1
856
+ }),
857
+ createVNode(MetadataField_default, { label: "Created" }, {
858
+ default: withCtx(() => [createTextVNode(toDisplayString(createdFormatted.value) + " ", 1), createdRelative.value ? (openBlock(), createElementBlock("span", _hoisted_7$3, "· " + toDisplayString(createdRelative.value), 1)) : createCommentVNode("v-if", true)]),
859
+ _: 1
860
+ }),
861
+ createVNode(MetadataField_default, { label: "Started" }, {
862
+ default: withCtx(() => [__props.ticket.start_at ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode(toDisplayString(startFormatted.value) + " ", 1), createElementVNode("span", _hoisted_8$3, "· " + toDisplayString(startRelative.value), 1)], 64)) : (openBlock(), createElementBlock("span", _hoisted_9$3, "Not set"))]),
863
+ _: 1
864
+ }),
865
+ createVNode(MetadataField_default, { label: "Target" }, {
866
+ empty: withCtx(() => [..._cache[2] || (_cache[2] = [createElementVNode("span", { class: "text-base-content/50 italic text-sm" }, "Not set", -1)])]),
867
+ default: withCtx(() => [__props.ticket.target_at ? (openBlock(), createElementBlock("span", {
868
+ key: 0,
869
+ class: normalizeClass({ "text-error": isTargetOverdue.value })
870
+ }, [createTextVNode(toDisplayString(targetFormatted.value) + " ", 1), createElementVNode("span", _hoisted_10$3, "· " + toDisplayString(targetRelative.value), 1)], 2)) : createCommentVNode("v-if", true)]),
871
+ _: 1
872
+ }),
873
+ createVNode(MetadataField_default, { label: "Completed" }, {
874
+ empty: withCtx(() => [..._cache[3] || (_cache[3] = [createElementVNode("span", { class: "text-base-content/50 italic text-sm" }, "Not set", -1)])]),
875
+ default: withCtx(() => [__props.ticket.completed_at ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode(toDisplayString(completedFormatted.value) + " ", 1), createElementVNode("span", _hoisted_11$3, "· " + toDisplayString(completedRelative.value), 1)], 64)) : createCommentVNode("v-if", true)]),
876
+ _: 1
877
+ }),
878
+ createVNode(MetadataField_default, { label: "Credits" }, {
879
+ default: withCtx(() => [createElementVNode("span", _hoisted_12$3, [__props.ticket.is_locked ? (openBlock(), createElementBlock("span", _hoisted_13$3, "🔒")) : createCommentVNode("v-if", true), createTextVNode(" " + toDisplayString(creditDisplay.value), 1)])]),
880
+ _: 1
881
+ }),
882
+ createVNode(MetadataField_default, { label: "Delivered Value" }, {
883
+ empty: withCtx(() => [..._cache[4] || (_cache[4] = [createElementVNode("span", { class: "text-base-content/50 italic text-sm" }, "Not set", -1)])]),
884
+ default: withCtx(() => [__props.ticket.delivered_value?.trim() ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode(toDisplayString(__props.ticket.delivered_value), 1)], 64)) : createCommentVNode("v-if", true)]),
885
+ _: 1
886
+ })
887
+ ])]);
888
+ };
889
+ }
890
+ });
891
+ var StaffMetadataCard_default = _sfc_main$4;
892
+
893
+ //#endregion
894
+ //#region src/slices/support_ticket/staff/staffMetadataCardEditFormMetadata.ts
895
+ const staffMetadataCardEditFormMetadata = withMetadata(StaffSupportTicketUpdateSchema, "staffMetadataCardEditForm", {
896
+ title: {
897
+ label: "Title",
898
+ placeholder: "Enter ticket title"
899
+ },
900
+ description: {
901
+ label: "Description",
902
+ inputType: "textarea",
903
+ placeholder: "Describe the ticket in detail"
904
+ },
905
+ type: { label: "Type" },
906
+ priority: {
907
+ label: "Priority",
908
+ inputType: "select",
909
+ valueToLabel: SUPPORT_TICKET_PRIORITY_NUMBER_TO_LABEL,
910
+ valueType: "number"
911
+ },
912
+ dev_lifecycle: { label: "Development Lifecycle" },
913
+ credit_value: {
914
+ label: "Credit Value",
915
+ inputType: "currency",
916
+ step: .01,
917
+ placeholder: "0.00"
918
+ },
919
+ delivered_value: {
920
+ label: "Delivered Value",
921
+ inputType: "currency",
922
+ step: .01,
923
+ placeholder: "0.00"
924
+ },
925
+ start_at: {
926
+ label: "Start At",
927
+ inputType: "date"
928
+ },
929
+ target_at: {
930
+ label: "Target At",
931
+ inputType: "date"
932
+ },
933
+ completed_at: {
934
+ label: "Completed At",
935
+ inputType: "date"
936
+ }
937
+ });
938
+
939
+ //#endregion
940
+ //#region src/slices/support_ticket/staff/components/StaffMetadataCardEdit.vue
941
+ const _hoisted_1$3 = { class: "border border-base-200 rounded-lg p-4 md:p-6" };
942
+ const _hoisted_2$3 = { class: "grid grid-cols-1 md:grid-cols-2 gap-4" };
943
+ const _hoisted_3$2 = { class: "form-control md:col-span-2" };
944
+ const _hoisted_4$2 = { class: "form-control md:col-span-2" };
945
+ const _hoisted_5$2 = { class: "form-control" };
946
+ const _hoisted_6$2 = {
947
+ key: 0,
948
+ class: "form-control"
949
+ };
950
+ const _hoisted_7$2 = ["disabled"];
951
+ const _hoisted_8$2 = ["value"];
952
+ const _hoisted_9$2 = { class: "form-control" };
953
+ const _hoisted_10$2 = {
954
+ key: 1,
955
+ class: "form-control"
956
+ };
957
+ const _hoisted_11$2 = {
958
+ key: 2,
959
+ class: "form-control"
960
+ };
961
+ const _hoisted_12$2 = {
962
+ key: 3,
963
+ class: "form-control"
964
+ };
965
+ const _hoisted_13$2 = {
966
+ key: 4,
967
+ class: "form-control"
968
+ };
969
+ const _hoisted_14$2 = {
970
+ key: 5,
971
+ class: "form-control"
972
+ };
973
+ const _hoisted_15$2 = {
974
+ key: 6,
975
+ class: "form-control"
976
+ };
977
+ const _hoisted_16$2 = {
978
+ key: 0,
979
+ class: "alert alert-error py-2 mt-4"
980
+ };
981
+ const _hoisted_17$2 = { class: "text-sm" };
982
+ const _hoisted_18$2 = { class: "flex justify-end gap-2 mt-4" };
983
+ const _hoisted_19$2 = ["disabled"];
984
+ const _sfc_main$3 = /* @__PURE__ */ defineComponent({
985
+ __name: "StaffMetadataCardEdit",
986
+ props: {
987
+ ticket: {},
988
+ saving: { type: Boolean }
989
+ },
990
+ emits: ["success", "cancel"],
991
+ setup(__props, { emit: __emit }) {
992
+ const props = __props;
993
+ const assigneeId = ref(props.ticket.assigned_to ?? "");
994
+ watch(() => props.ticket.assigned_to, (v) => {
995
+ assigneeId.value = v ?? "";
996
+ });
997
+ const { data: triageUsers } = useQuery((api) => api.users.getTriageUsers(), {
998
+ cacheKey: "triage-users",
999
+ staleTime: 3600 * 1e3
1000
+ });
1001
+ const emit = __emit;
1002
+ const { mutate: updateTicket, loading: isSaving } = useMutation((api, input) => api.supportTickets.staffUpdateTicket(input), { invalidate: /^support-tickets?:/ });
1003
+ const saving = computed(() => props.saving ?? isSaving.value);
1004
+ function formatDateForInput(dateString) {
1005
+ if (!dateString) return null;
1006
+ try {
1007
+ return new Date(dateString).toISOString().split("T")[0] || null;
1008
+ } catch {
1009
+ return null;
1010
+ }
1011
+ }
1012
+ function formatDateForOutput(dateString) {
1013
+ if (!dateString || dateString.trim() === "") return null;
1014
+ try {
1015
+ const date = /* @__PURE__ */ new Date(dateString + "T00:00:00");
1016
+ if (isNaN(date.getTime())) return null;
1017
+ return date.toISOString();
1018
+ } catch {
1019
+ return null;
1020
+ }
1021
+ }
1022
+ const { form, zinia, ZiniaForm, ZiniaSubmitButton, ZiniaFormErrorsSummary } = useForm(staffMetadataCardEditFormMetadata, {
1023
+ storeName: `staff-metadata-card-edit-${props.ticket.id}`,
1024
+ persistToLocalStorage: false,
1025
+ renderStyle: "daisy_ui",
1026
+ fetchData: async () => {
1027
+ const t = props.ticket;
1028
+ const devLifecycle = t.dev_lifecycle && [
1029
+ "BACKLOG",
1030
+ "PLANNING",
1031
+ "DEVELOPMENT",
1032
+ "CODE_REVIEW",
1033
+ "TESTING",
1034
+ "STAGING",
1035
+ "PO_APPROVAL",
1036
+ "VERIFICATION"
1037
+ ].includes(t.dev_lifecycle) ? t.dev_lifecycle : void 0;
1038
+ return {
1039
+ id: t.id,
1040
+ title: t.title || "",
1041
+ description: t.description ?? "",
1042
+ type: t.type,
1043
+ priority: supportTicketPriorityToNumber(t.priority),
1044
+ dev_lifecycle: devLifecycle,
1045
+ credit_value: t.credit_value?.trim() || null,
1046
+ delivered_value: t.delivered_value?.trim() || null,
1047
+ start_at: formatDateForInput(t.start_at) ?? null,
1048
+ target_at: formatDateForInput(t.target_at) ?? null,
1049
+ completed_at: formatDateForInput(t.completed_at) ?? null
1050
+ };
1051
+ }
1052
+ });
1053
+ function buildPayload(formData) {
1054
+ const t = props.ticket;
1055
+ const payload = {
1056
+ id: t.id,
1057
+ title: String(formData.title ?? "").trim(),
1058
+ description: formData.description || "",
1059
+ type: formData.type,
1060
+ priority: formData.priority
1061
+ };
1062
+ if (["APPROVED", "INTERNAL"].includes(t.approval_status || "") && t.dev_lifecycle !== "DEPLOYED" && t.dev_lifecycle !== "CANCELLED") {
1063
+ const dl = formData.dev_lifecycle;
1064
+ if (dl !== null && dl !== void 0 && dl !== "PENDING" && dl !== "DEPLOYED" && dl !== "CANCELLED") payload.dev_lifecycle = dl;
1065
+ }
1066
+ if (t.approval_status === "PENDING") {
1067
+ const cv = formData.credit_value;
1068
+ if (cv != null && cv !== "") payload.credit_value = String(cv).trim();
1069
+ }
1070
+ if (t.dev_lifecycle === "DEPLOYED") {
1071
+ const dv = formData.delivered_value;
1072
+ if (dv != null && dv !== "") payload.delivered_value = String(dv).trim();
1073
+ }
1074
+ if ([
1075
+ "PENDING",
1076
+ "APPROVED",
1077
+ "INTERNAL"
1078
+ ].includes(t.approval_status || "") && t.dev_lifecycle !== "CANCELLED" && t.dev_lifecycle !== "DEPLOYED") {
1079
+ const startAt = formatDateForOutput(formData.start_at);
1080
+ if (startAt !== null) payload.start_at = startAt;
1081
+ const targetAt = formatDateForOutput(formData.target_at);
1082
+ if (targetAt !== null) payload.target_at = targetAt;
1083
+ }
1084
+ if (t.dev_lifecycle === "DEPLOYED" || t.dev_lifecycle === "CANCELLED") {
1085
+ const completedAt = formatDateForOutput(formData.completed_at);
1086
+ if (completedAt !== null) payload.completed_at = completedAt;
1087
+ }
1088
+ payload.assigned_to = assigneeId.value || null;
1089
+ return payload;
1090
+ }
1091
+ async function handleSubmit(formData) {
1092
+ await updateTicket(buildPayload(formData));
1093
+ }
1094
+ function handleSuccess() {
1095
+ emit("success");
1096
+ }
1097
+ function handleError(error) {
1098
+ const message = extractRpcErrorMessage(error, "Failed to update ticket");
1099
+ form.setSubmitError(message);
1100
+ }
1101
+ function handleCancel() {
1102
+ emit("cancel");
1103
+ }
1104
+ return (_ctx, _cache) => {
1105
+ return openBlock(), createElementBlock("div", _hoisted_1$3, [createVNode(unref(ZiniaForm), {
1106
+ onHandleSubmit: handleSubmit,
1107
+ onSuccess: handleSuccess,
1108
+ onError: handleError,
1109
+ title: "",
1110
+ subtitle: ""
1111
+ }, {
1112
+ default: withCtx(() => [
1113
+ createElementVNode("div", _hoisted_2$3, [
1114
+ createCommentVNode(" Title - full width "),
1115
+ createElementVNode("div", _hoisted_3$2, [createVNode(unref(zinia).TitleField, { placeholder: "Enter ticket title" })]),
1116
+ createCommentVNode(" Description - full width "),
1117
+ createElementVNode("div", _hoisted_4$2, [createVNode(unref(zinia).DescriptionField, {
1118
+ class: "w-full",
1119
+ placeholder: "Describe the ticket in detail"
1120
+ })]),
1121
+ createCommentVNode(" Type "),
1122
+ createElementVNode("div", _hoisted_5$2, [createVNode(unref(zinia).TypeField)]),
1123
+ createCommentVNode(" Assignee - only when not archived "),
1124
+ !__props.ticket.archived_at ? (openBlock(), createElementBlock("div", _hoisted_6$2, [_cache[2] || (_cache[2] = createElementVNode("label", { class: "label" }, [createElementVNode("span", { class: "label-text" }, "Assignee")], -1)), withDirectives(createElementVNode("select", {
1125
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => assigneeId.value = $event),
1126
+ class: "select select-bordered w-full",
1127
+ disabled: saving.value
1128
+ }, [_cache[1] || (_cache[1] = createElementVNode("option", { value: "" }, "Unassigned", -1)), (openBlock(true), createElementBlock(Fragment, null, renderList(unref(triageUsers) ?? [], (u) => {
1129
+ return openBlock(), createElementBlock("option", {
1130
+ key: u.id,
1131
+ value: u.id
1132
+ }, toDisplayString(u.email), 9, _hoisted_8$2);
1133
+ }), 128))], 8, _hoisted_7$2), [[vModelSelect, assigneeId.value]])])) : createCommentVNode("v-if", true),
1134
+ createCommentVNode(" Priority "),
1135
+ createElementVNode("div", _hoisted_9$2, [createVNode(unref(zinia).PriorityField)]),
1136
+ createCommentVNode(" Development Lifecycle - Only editable when APPROVED or INTERNAL (not terminal states) "),
1137
+ ["APPROVED", "INTERNAL"].includes(__props.ticket.approval_status || "") && __props.ticket.dev_lifecycle !== "DEPLOYED" && __props.ticket.dev_lifecycle !== "CANCELLED" ? (openBlock(), createElementBlock("div", _hoisted_10$2, [createVNode(unref(zinia).DevLifecycleField)])) : createCommentVNode("v-if", true),
1138
+ createCommentVNode(" Credit Value - Only editable when PENDING "),
1139
+ __props.ticket.approval_status === "PENDING" ? (openBlock(), createElementBlock("div", _hoisted_11$2, [createVNode(unref(zinia).CreditValueField, { placeholder: "0.00" })])) : createCommentVNode("v-if", true),
1140
+ createCommentVNode(" Delivered Value - Only editable when DEPLOYED "),
1141
+ __props.ticket.dev_lifecycle === "DEPLOYED" ? (openBlock(), createElementBlock("div", _hoisted_12$2, [createVNode(unref(zinia).DeliveredValueField, { placeholder: "0.00" })])) : createCommentVNode("v-if", true),
1142
+ createCommentVNode(" Start At - Only editable when PENDING, APPROVED, or INTERNAL (not terminal) "),
1143
+ [
1144
+ "PENDING",
1145
+ "APPROVED",
1146
+ "INTERNAL"
1147
+ ].includes(__props.ticket.approval_status || "") && __props.ticket.dev_lifecycle !== "CANCELLED" && __props.ticket.dev_lifecycle !== "DEPLOYED" ? (openBlock(), createElementBlock("div", _hoisted_13$2, [createVNode(unref(zinia).StartAtField, { formatter: unref(formatToISODate) }, null, 8, ["formatter"])])) : createCommentVNode("v-if", true),
1148
+ createCommentVNode(" Target At - Only editable when PENDING, APPROVED, or INTERNAL (not terminal) "),
1149
+ [
1150
+ "PENDING",
1151
+ "APPROVED",
1152
+ "INTERNAL"
1153
+ ].includes(__props.ticket.approval_status || "") && __props.ticket.dev_lifecycle !== "CANCELLED" && __props.ticket.dev_lifecycle !== "DEPLOYED" ? (openBlock(), createElementBlock("div", _hoisted_14$2, [createVNode(unref(zinia).TargetAtField, { formatter: unref(formatToISODate) }, null, 8, ["formatter"])])) : createCommentVNode("v-if", true),
1154
+ createCommentVNode(" Completed At - Only editable when DEPLOYED or CANCELLED "),
1155
+ __props.ticket.dev_lifecycle === "DEPLOYED" || __props.ticket.dev_lifecycle === "CANCELLED" ? (openBlock(), createElementBlock("div", _hoisted_15$2, [createVNode(unref(zinia).CompletedAtField, { formatter: unref(formatToISODate) }, null, 8, ["formatter"])])) : createCommentVNode("v-if", true)
1156
+ ]),
1157
+ unref(form).submitError ? (openBlock(), createElementBlock("div", _hoisted_16$2, [createElementVNode("span", _hoisted_17$2, toDisplayString(unref(form).submitError), 1)])) : createCommentVNode("v-if", true),
1158
+ createVNode(unref(ZiniaFormErrorsSummary), { title: "Please fix the following errors:" }),
1159
+ createCommentVNode(" Action Buttons "),
1160
+ createElementVNode("div", _hoisted_18$2, [createElementVNode("button", {
1161
+ type: "button",
1162
+ class: "btn btn-ghost",
1163
+ onClick: handleCancel,
1164
+ disabled: saving.value
1165
+ }, " Cancel ", 8, _hoisted_19$2), createVNode(unref(ZiniaSubmitButton), {
1166
+ submitText: "Save",
1167
+ submittingText: "Saving..."
1168
+ })])
1169
+ ]),
1170
+ _: 1
1171
+ })]);
1172
+ };
1173
+ }
1174
+ });
1175
+ var StaffMetadataCardEdit_default = _sfc_main$3;
1176
+
1177
+ //#endregion
1178
+ //#region src/slices/support_ticket/staff/components/StaffTimeline.vue
1179
+ const _hoisted_1$2 = { class: "flex flex-col gap-4 w-full" };
1180
+ const _hoisted_2$2 = {
1181
+ key: 0,
1182
+ class: "text-base-content/50 text-sm text-center py-8"
1183
+ };
1184
+ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
1185
+ __name: "StaffTimeline",
1186
+ props: {
1187
+ ticket: {},
1188
+ customerNotes: {},
1189
+ internalNotes: {},
1190
+ systemEvents: {},
1191
+ onAddNote: { type: Function }
1192
+ },
1193
+ setup(__props, { expose: __expose }) {
1194
+ const props = __props;
1195
+ const route = useRoute();
1196
+ const router = useRouter();
1197
+ const noteTypeFromQuery = computed(() => {
1198
+ return route.query.noteType === "internal" ? "internal" : "customer";
1199
+ });
1200
+ function updateNoteTypeQuery(value) {
1201
+ router.replace({
1202
+ path: route.path,
1203
+ query: {
1204
+ ...route.query,
1205
+ noteType: value === "internal" ? "internal" : void 0
1206
+ }
1207
+ });
1208
+ }
1209
+ const noteInputRef = ref(null);
1210
+ const mergedTimelineItems = computed(() => {
1211
+ const items = [];
1212
+ for (const note of props.customerNotes ?? []) items.push({
1213
+ type: "customer-note",
1214
+ timestamp: note.createdAt,
1215
+ id: `customer-note-${note.createdAt}-${note.authorName}`,
1216
+ data: note
1217
+ });
1218
+ for (const note of props.internalNotes ?? []) items.push({
1219
+ type: "internal-note",
1220
+ timestamp: note.createdAt,
1221
+ id: `internal-note-${note.createdAt}-${note.authorName}`,
1222
+ data: note
1223
+ });
1224
+ for (const event of props.systemEvents ?? []) items.push({
1225
+ type: "system-event",
1226
+ timestamp: event.timestamp,
1227
+ id: event.id,
1228
+ data: event
1229
+ });
1230
+ items.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
1231
+ return items;
1232
+ });
1233
+ function resetInput() {
1234
+ noteInputRef.value?.reset();
1235
+ }
1236
+ __expose({ resetInput });
1237
+ return (_ctx, _cache) => {
1238
+ return openBlock(), createElementBlock("div", _hoisted_1$2, [
1239
+ createVNode(TimelineNoteInput_default, {
1240
+ ref_key: "noteInputRef",
1241
+ ref: noteInputRef,
1242
+ "show-type-toggle": true,
1243
+ disabled: !!__props.ticket.archived_at,
1244
+ "note-type": noteTypeFromQuery.value,
1245
+ "on-submit": __props.onAddNote,
1246
+ "onUpdate:noteType": updateNoteTypeQuery
1247
+ }, null, 8, [
1248
+ "disabled",
1249
+ "note-type",
1250
+ "on-submit"
1251
+ ]),
1252
+ mergedTimelineItems.value.length === 0 ? (openBlock(), createElementBlock("p", _hoisted_2$2, " No activity yet. ")) : createCommentVNode("v-if", true),
1253
+ (openBlock(true), createElementBlock(Fragment, null, renderList(mergedTimelineItems.value, (item) => {
1254
+ return openBlock(), createElementBlock(Fragment, { key: item.id }, [item.type === "customer-note" || item.type === "internal-note" ? (openBlock(), createBlock(TimelineItem_default, {
1255
+ key: 0,
1256
+ "author-name": item.data.authorName,
1257
+ "created-at": item.data.createdAt,
1258
+ variant: item.type === "customer-note" ? "customer" : "internal"
1259
+ }, {
1260
+ default: withCtx(() => [createTextVNode(toDisplayString(item.data.body ?? ""), 1)]),
1261
+ _: 2
1262
+ }, 1032, [
1263
+ "author-name",
1264
+ "created-at",
1265
+ "variant"
1266
+ ])) : item.type === "system-event" ? (openBlock(), createBlock(TimelineSystemEvent_default, {
1267
+ key: 1,
1268
+ author: item.data.author,
1269
+ message: item.data.message,
1270
+ timestamp: item.data.timestamp,
1271
+ "old-value": item.data.oldValue,
1272
+ "new-value": item.data.newValue
1273
+ }, null, 8, [
1274
+ "author",
1275
+ "message",
1276
+ "timestamp",
1277
+ "old-value",
1278
+ "new-value"
1279
+ ])) : createCommentVNode("v-if", true)], 64);
1280
+ }), 128))
1281
+ ]);
1282
+ };
1283
+ }
1284
+ });
1285
+ var StaffTimeline_default = _sfc_main$2;
1286
+
1287
+ //#endregion
1288
+ //#region src/slices/support_ticket/staff/components/SupportTicketSubscribersCollapsible.vue
1289
+ const _hoisted_1$1 = {
1290
+ key: 0,
1291
+ class: "w-full"
1292
+ };
1293
+ const _hoisted_2$1 = { class: "collapse collapse-arrow bg-base-200" };
1294
+ const _hoisted_3$1 = { class: "collapse-title text-sm font-medium px-4 py-2 min-h-0" };
1295
+ const _hoisted_4$1 = { class: "flex items-center gap-2" };
1296
+ const _hoisted_5$1 = { class: "collapse-content px-0" };
1297
+ const _hoisted_6$1 = { class: "px-4 pb-4 space-y-4" };
1298
+ const _hoisted_7$1 = {
1299
+ key: 0,
1300
+ class: "flex flex-wrap items-end gap-2"
1301
+ };
1302
+ const _hoisted_8$1 = { class: "form-control flex-1 min-w-[200px]" };
1303
+ const _hoisted_9$1 = ["disabled"];
1304
+ const _hoisted_10$1 = ["value"];
1305
+ const _hoisted_11$1 = ["disabled"];
1306
+ const _hoisted_12$1 = {
1307
+ key: 0,
1308
+ class: "loading loading-spinner loading-xs"
1309
+ };
1310
+ const _hoisted_13$1 = { key: 1 };
1311
+ const _hoisted_14$1 = {
1312
+ key: 1,
1313
+ class: "flex justify-center py-4"
1314
+ };
1315
+ const _hoisted_15$1 = {
1316
+ key: 2,
1317
+ class: "text-sm text-base-content/50 italic"
1318
+ };
1319
+ const _hoisted_16$1 = {
1320
+ key: 3,
1321
+ class: "space-y-2"
1322
+ };
1323
+ const _hoisted_17$1 = { class: "text-sm truncate" };
1324
+ const _hoisted_18$1 = ["onClick", "disabled"];
1325
+ const _hoisted_19$1 = {
1326
+ key: 0,
1327
+ class: "loading loading-spinner loading-xs"
1328
+ };
1329
+ const _hoisted_20$1 = {
1330
+ key: 1,
1331
+ xmlns: "http://www.w3.org/2000/svg",
1332
+ class: "h-4 w-4",
1333
+ fill: "none",
1334
+ viewBox: "0 0 24 24",
1335
+ stroke: "currentColor"
1336
+ };
1337
+ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
1338
+ __name: "SupportTicketSubscribersCollapsible",
1339
+ props: {
1340
+ supportTicketId: {},
1341
+ locked: { type: Boolean }
1342
+ },
1343
+ setup(__props) {
1344
+ const props = __props;
1345
+ const expanded = ref(false);
1346
+ const selectedUserId = ref("");
1347
+ const { data: subscribersData, loading: subscribersLoading, refetch: refetchSubscribers } = useQuery((api) => api.supportTickets.staffListSubscribers(props.supportTicketId), {
1348
+ enabled: computed(() => !!props.supportTicketId),
1349
+ cacheKey: `support-ticket-subscribers-${props.supportTicketId}`
1350
+ });
1351
+ const subscribers = computed(() => {
1352
+ return subscribersData.value ?? [];
1353
+ });
1354
+ const { data: usersData } = useQuery((api) => api.users.getUsersForSelection(), { staleTime: 600 * 1e3 });
1355
+ const availableUsers = computed(() => {
1356
+ const users = usersData.value ?? [];
1357
+ const existingIds = new Set(subscribers.value.map((s) => s.user_id));
1358
+ return users.filter((u) => !existingIds.has(u.id));
1359
+ });
1360
+ const { mutate: addSubscriber, loading: isAddLoading } = useMutation((api, input) => api.supportTickets.staffAddSubscriber(input), { invalidate: /^support-ticket-subscribers/ });
1361
+ const { mutate: removeSubscriber } = useMutation((api, input) => api.supportTickets.staffRemoveSubscriber(input), { invalidate: /^support-ticket-subscribers/ });
1362
+ const isRemoveLoading = ref(null);
1363
+ async function handleAddSubscriber() {
1364
+ if (!selectedUserId.value) return;
1365
+ try {
1366
+ await addSubscriber({
1367
+ support_ticket_id: props.supportTicketId,
1368
+ user_id: selectedUserId.value
1369
+ });
1370
+ toast.success("Subscriber added");
1371
+ selectedUserId.value = "";
1372
+ await refetchSubscribers();
1373
+ } catch (e) {
1374
+ toast.error(extractRpcErrorMessage(e, "Failed to add subscriber"));
1375
+ }
1376
+ }
1377
+ async function handleRemoveSubscriber(subscriberId) {
1378
+ try {
1379
+ isRemoveLoading.value = subscriberId;
1380
+ await removeSubscriber({
1381
+ supportTicketId: props.supportTicketId,
1382
+ subscriberId
1383
+ });
1384
+ toast.success("Subscriber removed");
1385
+ await refetchSubscribers();
1386
+ } catch (e) {
1387
+ toast.error(extractRpcErrorMessage(e, "Failed to remove subscriber"));
1388
+ } finally {
1389
+ isRemoveLoading.value = null;
1390
+ }
1391
+ }
1392
+ return (_ctx, _cache) => {
1393
+ return __props.supportTicketId ? (openBlock(), createElementBlock("div", _hoisted_1$1, [createElementVNode("div", _hoisted_2$1, [
1394
+ withDirectives(createElementVNode("input", {
1395
+ type: "checkbox",
1396
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => expanded.value = $event)
1397
+ }, null, 512), [[vModelCheckbox, expanded.value]]),
1398
+ createElementVNode("div", _hoisted_3$1, [createElementVNode("div", _hoisted_4$1, [_cache[2] || (_cache[2] = createElementVNode("svg", {
1399
+ xmlns: "http://www.w3.org/2000/svg",
1400
+ class: "h-4 w-4",
1401
+ fill: "none",
1402
+ viewBox: "0 0 24 24",
1403
+ stroke: "currentColor"
1404
+ }, [createElementVNode("path", {
1405
+ "stroke-linecap": "round",
1406
+ "stroke-linejoin": "round",
1407
+ "stroke-width": "2",
1408
+ d: "M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"
1409
+ })], -1)), createElementVNode("span", null, "Subscribers (" + toDisplayString(subscribers.value.length) + ")", 1)])]),
1410
+ createElementVNode("div", _hoisted_5$1, [createElementVNode("div", _hoisted_6$1, [
1411
+ createCommentVNode(" Add subscriber "),
1412
+ !__props.locked ? (openBlock(), createElementBlock("div", _hoisted_7$1, [createElementVNode("div", _hoisted_8$1, [_cache[4] || (_cache[4] = createElementVNode("label", { class: "label py-0" }, [createElementVNode("span", { class: "label-text text-sm" }, "Add subscriber")], -1)), withDirectives(createElementVNode("select", {
1413
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => selectedUserId.value = $event),
1414
+ class: "select select-bordered select-sm w-full",
1415
+ disabled: unref(isAddLoading)
1416
+ }, [_cache[3] || (_cache[3] = createElementVNode("option", { value: "" }, "Select user...", -1)), (openBlock(true), createElementBlock(Fragment, null, renderList(availableUsers.value, (user) => {
1417
+ return openBlock(), createElementBlock("option", {
1418
+ key: user.id,
1419
+ value: user.id
1420
+ }, toDisplayString(user.email), 9, _hoisted_10$1);
1421
+ }), 128))], 8, _hoisted_9$1), [[vModelSelect, selectedUserId.value]])]), createElementVNode("button", {
1422
+ class: "btn btn-sm btn-primary",
1423
+ disabled: !selectedUserId.value || unref(isAddLoading),
1424
+ onClick: handleAddSubscriber
1425
+ }, [unref(isAddLoading) ? (openBlock(), createElementBlock("span", _hoisted_12$1)) : (openBlock(), createElementBlock("span", _hoisted_13$1, "Add"))], 8, _hoisted_11$1)])) : createCommentVNode("v-if", true),
1426
+ createCommentVNode(" Subscriber list "),
1427
+ unref(subscribersLoading) ? (openBlock(), createElementBlock("div", _hoisted_14$1, [..._cache[5] || (_cache[5] = [createElementVNode("span", { class: "loading loading-spinner loading-sm" }, null, -1)])])) : subscribers.value.length === 0 ? (openBlock(), createElementBlock("ul", _hoisted_15$1, " No subscribers yet ")) : (openBlock(), createElementBlock("ul", _hoisted_16$1, [(openBlock(true), createElementBlock(Fragment, null, renderList(subscribers.value, (sub) => {
1428
+ return openBlock(), createElementBlock("li", {
1429
+ key: sub.id,
1430
+ class: "flex items-center justify-between gap-2 py-2 px-3 rounded bg-base-100 border border-base-300"
1431
+ }, [createElementVNode("span", _hoisted_17$1, toDisplayString(sub.user_id_display_name ?? sub.user_id), 1), !__props.locked ? (openBlock(), createElementBlock("button", {
1432
+ key: 0,
1433
+ type: "button",
1434
+ class: "btn btn-ghost btn-xs text-error",
1435
+ "aria-label": "Remove subscriber",
1436
+ onClick: ($event) => handleRemoveSubscriber(sub.id),
1437
+ disabled: isRemoveLoading.value === sub.id
1438
+ }, [isRemoveLoading.value === sub.id ? (openBlock(), createElementBlock("span", _hoisted_19$1)) : (openBlock(), createElementBlock("svg", _hoisted_20$1, [..._cache[6] || (_cache[6] = [createElementVNode("path", {
1439
+ "stroke-linecap": "round",
1440
+ "stroke-linejoin": "round",
1441
+ "stroke-width": "2",
1442
+ d: "M6 18L18 6M6 6l12 12"
1443
+ }, null, -1)])]))], 8, _hoisted_18$1)) : createCommentVNode("v-if", true)]);
1444
+ }), 128))]))
1445
+ ])])
1446
+ ])])) : createCommentVNode("v-if", true);
1447
+ };
1448
+ }
1449
+ });
1450
+ var SupportTicketSubscribersCollapsible_default = _sfc_main$1;
1451
+
1452
+ //#endregion
1453
+ //#region src/slices/support_ticket/staff/StaffSupportTicketDetailPage.vue
1454
+ const _hoisted_1 = {
1455
+ key: 0,
1456
+ class: "flex justify-center items-center p-8"
1457
+ };
1458
+ const _hoisted_2 = { class: "alert alert-error mb-4 max-w-4xl mx-auto px-4" };
1459
+ const _hoisted_3 = { class: "flex-1" };
1460
+ const _hoisted_4 = { class: "mt-2" };
1461
+ const _hoisted_5 = { class: "px-4 py-6 max-w-4xl mx-auto" };
1462
+ const _hoisted_6 = { class: "mb-6" };
1463
+ const _hoisted_7 = { class: "text-2xl font-bold text-base-content break-words leading-tight mt-2" };
1464
+ const _hoisted_8 = { class: "mb-6" };
1465
+ const _hoisted_9 = {
1466
+ key: 0,
1467
+ class: "flex gap-2 mb-6"
1468
+ };
1469
+ const _hoisted_10 = { class: "modal-box" };
1470
+ const _hoisted_11 = { class: "form-control py-4" };
1471
+ const _hoisted_12 = ["disabled"];
1472
+ const _hoisted_13 = {
1473
+ key: 0,
1474
+ class: "alert alert-error py-2 mb-4"
1475
+ };
1476
+ const _hoisted_14 = { class: "text-sm" };
1477
+ const _hoisted_15 = { class: "modal-action" };
1478
+ const _hoisted_16 = ["disabled"];
1479
+ const _hoisted_17 = ["disabled"];
1480
+ const _hoisted_18 = {
1481
+ key: 0,
1482
+ class: "loading loading-spinner loading-xs mr-2"
1483
+ };
1484
+ const _hoisted_19 = { class: "mb-8" };
1485
+ const _hoisted_20 = { class: "mb-8" };
1486
+ const _hoisted_21 = { class: "mb-8" };
1487
+ const _hoisted_22 = {
1488
+ key: 1,
1489
+ class: "flex justify-center items-center py-8"
1490
+ };
1491
+ const _sfc_main = /* @__PURE__ */ defineComponent({
1492
+ __name: "StaffSupportTicketDetailPage",
1493
+ props: {
1494
+ ticket: {},
1495
+ isLoading: { type: Boolean },
1496
+ error: {}
1497
+ },
1498
+ setup(__props) {
1499
+ const props = __props;
1500
+ const route = useRoute();
1501
+ const router = useRouter();
1502
+ const support_ticket_id = route.params.id;
1503
+ const isApproveModalOpen = ref(false);
1504
+ const isRevertModalOpen = ref(false);
1505
+ const isDeleteModalOpen = ref(false);
1506
+ const isConvertToInternalModalOpen = ref(false);
1507
+ const isCompleteModalOpen = ref(false);
1508
+ const isArchiveModalOpen = ref(false);
1509
+ const rejectModal = ref(null);
1510
+ const rejectReason = ref("");
1511
+ const rejectError = ref(null);
1512
+ const isEditMode = computed(() => route.query.mode === "edit");
1513
+ const refreshTicket = inject("refreshTicket");
1514
+ const { data: timelineData, refetch: refetchTimeline } = useQuery(async (api) => {
1515
+ const [customerNotes, internalNotes, recordVersions] = await Promise.all([
1516
+ api.notes.getNotes({
1517
+ record_id: {
1518
+ operator: OPERATORS.EQUALS,
1519
+ value: support_ticket_id
1520
+ },
1521
+ record_type: {
1522
+ operator: OPERATORS.EQUALS,
1523
+ value: "support_ticket"
1524
+ },
1525
+ is_internal: {
1526
+ operator: OPERATORS.EQUALS,
1527
+ value: false
1528
+ },
1529
+ first: 100,
1530
+ sortBy: "created_at",
1531
+ sortDirection: "asc"
1532
+ }),
1533
+ api.notes.getNotes({
1534
+ record_id: {
1535
+ operator: OPERATORS.EQUALS,
1536
+ value: support_ticket_id
1537
+ },
1538
+ record_type: {
1539
+ operator: OPERATORS.EQUALS,
1540
+ value: "support_ticket"
1541
+ },
1542
+ is_internal: {
1543
+ operator: OPERATORS.EQUALS,
1544
+ value: true
1545
+ },
1546
+ first: 100,
1547
+ sortBy: "created_at",
1548
+ sortDirection: "asc"
1549
+ }),
1550
+ api.recordVersions.listRecordVersionsPaginated(support_ticket_id, RecordConst.SUPPORT_TICKET, {
1551
+ record_types: [RecordConst.SUPPORT_TICKET, RecordConst.SUPPORT_TICKET_ACTIVITY],
1552
+ first: 100,
1553
+ sortBy: "recorded_at",
1554
+ sortDirection: "asc"
1555
+ })
1556
+ ]);
1557
+ return {
1558
+ customerNotes,
1559
+ internalNotes,
1560
+ recordVersions
1561
+ };
1562
+ }, {
1563
+ enabled: !!support_ticket_id && !!props.ticket,
1564
+ batchMode: BATCH_MODE.batch,
1565
+ trackedSegment: "support-ticket-timeline"
1566
+ });
1567
+ const customerNotesForTimeline = computed(() => {
1568
+ return (timelineData.value?.customerNotes?.items ?? []).map((n) => ({
1569
+ authorName: n.created_by_display_name ?? n.created_by ?? "(unknown)",
1570
+ createdAt: n.created_at,
1571
+ body: n.body ?? ""
1572
+ }));
1573
+ });
1574
+ const internalNotesForTimeline = computed(() => {
1575
+ return (timelineData.value?.internalNotes?.items ?? []).map((n) => ({
1576
+ authorName: n.created_by_display_name ?? n.created_by ?? "(unknown)",
1577
+ createdAt: n.created_at,
1578
+ body: n.body ?? ""
1579
+ }));
1580
+ });
1581
+ const systemEventsForTimeline = computed(() => {
1582
+ const data = timelineData.value?.recordVersions;
1583
+ const versions = data?.items ?? [];
1584
+ const userDisplayMap = data?.user_display_map;
1585
+ return parseRecordVersions(versions, userDisplayMap ? new Map(Object.entries(userDisplayMap)) : void 0);
1586
+ });
1587
+ const { mutate: createNote } = useMutation((api, input) => api.notes.createNote(input), { invalidate: /^notes?:/ });
1588
+ const { mutate: rejectSupportTicket, loading: isRejecting } = useMutation((api, input) => api.supportTickets.rejectTicket(input), { invalidate: /^support-tickets?:/ });
1589
+ async function handleAddNote(payload) {
1590
+ try {
1591
+ await createNote({
1592
+ record_id: support_ticket_id,
1593
+ record_type: "support_ticket",
1594
+ body: payload.content,
1595
+ tag: null,
1596
+ is_internal: payload.noteType === "internal"
1597
+ });
1598
+ await refetchTimeline();
1599
+ toast.success(payload.noteType === "internal" ? "Internal note added" : "Note added");
1600
+ } catch {
1601
+ toast.error("Failed to add note");
1602
+ throw new Error("Failed to add note");
1603
+ }
1604
+ }
1605
+ function showApproveModal() {
1606
+ if (!props.ticket) return;
1607
+ isApproveModalOpen.value = true;
1608
+ }
1609
+ function showRevertModal() {
1610
+ if (!props.ticket) return;
1611
+ isRevertModalOpen.value = true;
1612
+ }
1613
+ function showDeleteModal() {
1614
+ if (!props.ticket) return;
1615
+ isDeleteModalOpen.value = true;
1616
+ }
1617
+ function showConvertToInternalModal() {
1618
+ if (!props.ticket) return;
1619
+ isConvertToInternalModalOpen.value = true;
1620
+ }
1621
+ function showCompleteModal() {
1622
+ if (!props.ticket) return;
1623
+ isCompleteModalOpen.value = true;
1624
+ }
1625
+ function showArchiveModal() {
1626
+ if (!props.ticket) return;
1627
+ isArchiveModalOpen.value = true;
1628
+ }
1629
+ function handleDeleteSuccess() {
1630
+ router.push({ name: "StaffSupportTicketList" });
1631
+ }
1632
+ function showRejectModal() {
1633
+ rejectReason.value = "";
1634
+ rejectError.value = null;
1635
+ rejectModal.value?.showModal();
1636
+ }
1637
+ function closeRejectModal() {
1638
+ rejectModal.value?.close();
1639
+ rejectReason.value = "";
1640
+ rejectError.value = null;
1641
+ }
1642
+ async function handleApproveSuccess() {
1643
+ await handleTicketUpdate();
1644
+ }
1645
+ async function handleRejectWithReason() {
1646
+ if (!props.ticket) return;
1647
+ rejectError.value = null;
1648
+ try {
1649
+ const reasonTrimmed = rejectReason.value.trim();
1650
+ if (reasonTrimmed) await createNote({
1651
+ record_id: props.ticket.id,
1652
+ record_type: "support_ticket",
1653
+ body: reasonTrimmed,
1654
+ tag: null,
1655
+ is_internal: false
1656
+ });
1657
+ await rejectSupportTicket({ id: props.ticket.id });
1658
+ toast.info("Support Ticket rejected.");
1659
+ closeRejectModal();
1660
+ await handleTicketUpdate();
1661
+ } catch (e) {
1662
+ const errorMessage = extractRpcErrorMessage(e, "Failed to reject ticket");
1663
+ rejectError.value = errorMessage;
1664
+ toast.error(errorMessage);
1665
+ }
1666
+ }
1667
+ async function handleTicketUpdate() {
1668
+ if (refreshTicket) await refreshTicket();
1669
+ await refetchTimeline();
1670
+ }
1671
+ function enterEditMode() {
1672
+ router.push({
1673
+ name: route.name || "StaffViewSupportTicket",
1674
+ params: route.params,
1675
+ query: {
1676
+ ...route.query,
1677
+ mode: "edit"
1678
+ }
1679
+ });
1680
+ }
1681
+ async function exitEditMode() {
1682
+ await router.push({
1683
+ name: route.name || "StaffViewSupportTicket",
1684
+ params: route.params,
1685
+ query: {
1686
+ ...route.query,
1687
+ mode: void 0
1688
+ }
1689
+ });
1690
+ }
1691
+ async function handleMetadataSaveSuccess() {
1692
+ await exitEditMode();
1693
+ await nextTick();
1694
+ toast.success("Ticket updated successfully");
1695
+ if (refreshTicket) await refreshTicket();
1696
+ await refetchTimeline();
1697
+ }
1698
+ return (_ctx, _cache) => {
1699
+ const _component_router_link = resolveComponent("router-link");
1700
+ return openBlock(), createElementBlock(Fragment, null, [createCommentVNode(" Loading State (only on initial load; refetch keeps content visible) "), __props.isLoading && !__props.ticket ? (openBlock(), createElementBlock("div", _hoisted_1, [..._cache[7] || (_cache[7] = [createElementVNode("span", { class: "loading loading-spinner loading-lg" }, null, -1)])])) : __props.error ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [createCommentVNode(" Error State "), createElementVNode("div", _hoisted_2, [_cache[9] || (_cache[9] = createElementVNode("svg", {
1701
+ xmlns: "http://www.w3.org/2000/svg",
1702
+ class: "stroke-current shrink-0 h-6 w-6",
1703
+ fill: "none",
1704
+ viewBox: "0 0 24 24"
1705
+ }, [createElementVNode("path", {
1706
+ "stroke-linecap": "round",
1707
+ "stroke-linejoin": "round",
1708
+ "stroke-width": "2",
1709
+ d: "M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
1710
+ })], -1)), createElementVNode("div", _hoisted_3, [createElementVNode("span", null, toDisplayString(__props.error.message || "An error occurred"), 1), createElementVNode("div", _hoisted_4, [createVNode(_component_router_link, {
1711
+ to: { name: "StaffSupportTicketList" },
1712
+ class: "link link-primary"
1713
+ }, {
1714
+ default: withCtx(() => [..._cache[8] || (_cache[8] = [createTextVNode(" Go back to ticket list ", -1)])]),
1715
+ _: 1
1716
+ })])])])], 2112)) : __props.ticket ? (openBlock(), createElementBlock(Fragment, { key: 2 }, [createCommentVNode(" Main Content "), createElementVNode("div", _hoisted_5, [
1717
+ createCommentVNode(" Header "),
1718
+ createElementVNode("div", _hoisted_6, [createElementVNode("h1", _hoisted_7, toDisplayString(__props.ticket.title), 1)]),
1719
+ createCommentVNode(" Action Banner (buttons only; sections in dialogs) "),
1720
+ createElementVNode("div", _hoisted_8, [createVNode(StaffActionBanner_default, {
1721
+ ticket: __props.ticket,
1722
+ onApprove: showApproveModal,
1723
+ onReject: showRejectModal,
1724
+ onRevert: showRevertModal,
1725
+ onConvertToInternal: showConvertToInternalModal,
1726
+ onDelete: showDeleteModal,
1727
+ onComplete: showCompleteModal,
1728
+ onArchive: showArchiveModal
1729
+ }, null, 8, ["ticket"])]),
1730
+ createCommentVNode(" Edit / Cancel "),
1731
+ !__props.ticket.archived_at ? (openBlock(), createElementBlock("div", _hoisted_9, [!isEditMode.value ? (openBlock(), createElementBlock("button", {
1732
+ key: 0,
1733
+ onClick: enterEditMode,
1734
+ class: "btn btn-sm"
1735
+ }, [..._cache[10] || (_cache[10] = [createElementVNode("svg", {
1736
+ xmlns: "http://www.w3.org/2000/svg",
1737
+ fill: "none",
1738
+ viewBox: "0 0 24 24",
1739
+ "stroke-width": "1.5",
1740
+ stroke: "currentColor",
1741
+ class: "w-4 h-4 mr-2"
1742
+ }, [createElementVNode("path", {
1743
+ "stroke-linecap": "round",
1744
+ "stroke-linejoin": "round",
1745
+ d: "M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10"
1746
+ })], -1), createTextVNode(" Edit ", -1)])])) : isEditMode.value ? (openBlock(), createElementBlock("button", {
1747
+ key: 1,
1748
+ onClick: exitEditMode,
1749
+ class: "btn btn-sm"
1750
+ }, [..._cache[11] || (_cache[11] = [createElementVNode("svg", {
1751
+ xmlns: "http://www.w3.org/2000/svg",
1752
+ fill: "none",
1753
+ viewBox: "0 0 24 24",
1754
+ "stroke-width": "1.5",
1755
+ stroke: "currentColor",
1756
+ class: "w-4 h-4 mr-2"
1757
+ }, [createElementVNode("path", {
1758
+ "stroke-linecap": "round",
1759
+ "stroke-linejoin": "round",
1760
+ d: "M6 18L18 6M6 6l12 12"
1761
+ })], -1), createTextVNode(" Cancel ", -1)])])) : createCommentVNode("v-if", true)])) : createCommentVNode("v-if", true),
1762
+ createCommentVNode(" Approve Modal "),
1763
+ createVNode(ApproveSupportTicketModal_default, {
1764
+ ticket: __props.ticket,
1765
+ "is-open": isApproveModalOpen.value,
1766
+ onClose: _cache[0] || (_cache[0] = ($event) => isApproveModalOpen.value = false),
1767
+ onSuccess: handleApproveSuccess
1768
+ }, null, 8, ["ticket", "is-open"]),
1769
+ createCommentVNode(" Revert Modal "),
1770
+ createVNode(RevertSupportTicketModal_default, {
1771
+ ticket: __props.ticket,
1772
+ "is-open": isRevertModalOpen.value,
1773
+ onClose: _cache[1] || (_cache[1] = ($event) => isRevertModalOpen.value = false),
1774
+ onSuccess: handleTicketUpdate
1775
+ }, null, 8, ["ticket", "is-open"]),
1776
+ createCommentVNode(" Delete Modal "),
1777
+ createVNode(DeleteSupportTicketModal_default, {
1778
+ ticket: __props.ticket,
1779
+ "is-open": isDeleteModalOpen.value,
1780
+ onClose: _cache[2] || (_cache[2] = ($event) => isDeleteModalOpen.value = false),
1781
+ onSuccess: handleDeleteSuccess
1782
+ }, null, 8, ["ticket", "is-open"]),
1783
+ createCommentVNode(" Convert to Internal Modal "),
1784
+ createVNode(ConvertToInternalModal_default, {
1785
+ ticket: __props.ticket,
1786
+ "is-open": isConvertToInternalModalOpen.value,
1787
+ onClose: _cache[3] || (_cache[3] = ($event) => isConvertToInternalModalOpen.value = false),
1788
+ onSuccess: handleTicketUpdate
1789
+ }, null, 8, ["ticket", "is-open"]),
1790
+ createCommentVNode(" Complete Modal "),
1791
+ createVNode(CompleteSupportTicketModal_default, {
1792
+ ticket: __props.ticket,
1793
+ "is-open": isCompleteModalOpen.value,
1794
+ onClose: _cache[4] || (_cache[4] = ($event) => isCompleteModalOpen.value = false),
1795
+ onSuccess: handleTicketUpdate
1796
+ }, null, 8, ["ticket", "is-open"]),
1797
+ createCommentVNode(" Archive Modal "),
1798
+ createVNode(ArchiveSupportTicketModal_default, {
1799
+ ticket: __props.ticket,
1800
+ "is-open": isArchiveModalOpen.value,
1801
+ onClose: _cache[5] || (_cache[5] = ($event) => isArchiveModalOpen.value = false),
1802
+ onSuccess: handleTicketUpdate
1803
+ }, null, 8, ["ticket", "is-open"]),
1804
+ createCommentVNode(" Reject Modal with Reason "),
1805
+ createElementVNode("dialog", {
1806
+ ref_key: "rejectModal",
1807
+ ref: rejectModal,
1808
+ class: "modal"
1809
+ }, [createElementVNode("div", _hoisted_10, [
1810
+ _cache[13] || (_cache[13] = createElementVNode("h3", { class: "font-bold text-lg" }, "Reject Ticket", -1)),
1811
+ _cache[14] || (_cache[14] = createElementVNode("p", { class: "text-sm text-base-content/70 py-2" }, " This will lock the ticket and notify the requester. ", -1)),
1812
+ createElementVNode("div", _hoisted_11, [_cache[12] || (_cache[12] = createElementVNode("label", { class: "label" }, [createElementVNode("span", { class: "label-text text-sm font-medium" }, "Reason (optional)")], -1)), withDirectives(createElementVNode("textarea", {
1813
+ "onUpdate:modelValue": _cache[6] || (_cache[6] = ($event) => rejectReason.value = $event),
1814
+ class: "textarea textarea-bordered w-full",
1815
+ rows: "3",
1816
+ placeholder: "Why is this ticket being rejected?",
1817
+ disabled: unref(isRejecting)
1818
+ }, null, 8, _hoisted_12), [[vModelText, rejectReason.value]])]),
1819
+ rejectError.value ? (openBlock(), createElementBlock("div", _hoisted_13, [createElementVNode("span", _hoisted_14, toDisplayString(rejectError.value), 1)])) : createCommentVNode("v-if", true),
1820
+ createElementVNode("div", _hoisted_15, [createElementVNode("button", {
1821
+ class: "btn btn-ghost",
1822
+ onClick: closeRejectModal,
1823
+ disabled: unref(isRejecting)
1824
+ }, " Cancel ", 8, _hoisted_16), createElementVNode("button", {
1825
+ class: "btn btn-error",
1826
+ onClick: handleRejectWithReason,
1827
+ disabled: unref(isRejecting)
1828
+ }, [unref(isRejecting) ? (openBlock(), createElementBlock("span", _hoisted_18)) : createCommentVNode("v-if", true), createTextVNode(" " + toDisplayString(unref(isRejecting) ? "Rejecting..." : "Reject Ticket"), 1)], 8, _hoisted_17)])
1829
+ ]), createElementVNode("form", {
1830
+ method: "dialog",
1831
+ class: "modal-backdrop"
1832
+ }, [createElementVNode("button", { onClick: closeRejectModal }, "close")])], 512),
1833
+ createCommentVNode(" Metadata Card (Display or Edit) "),
1834
+ createElementVNode("div", _hoisted_19, [!isEditMode.value ? (openBlock(), createBlock(StaffMetadataCard_default, {
1835
+ key: 0,
1836
+ ticket: __props.ticket
1837
+ }, null, 8, ["ticket"])) : (openBlock(), createBlock(StaffMetadataCardEdit_default, {
1838
+ key: 1,
1839
+ ticket: __props.ticket,
1840
+ onSuccess: handleMetadataSaveSuccess,
1841
+ onCancel: exitEditMode
1842
+ }, null, 8, ["ticket"]))]),
1843
+ createCommentVNode(" Attachments (separate from timeline so adding a comment doesn't cause refetch) "),
1844
+ createElementVNode("div", _hoisted_20, [createVNode(SupportTicketAttachmentsCollapsible_default, {
1845
+ "record-id": __props.ticket.id,
1846
+ locked: !!__props.ticket.archived_at,
1847
+ editable: !__props.ticket.archived_at,
1848
+ onUploaded: unref(refetchTimeline),
1849
+ onDeleted: unref(refetchTimeline)
1850
+ }, null, 8, [
1851
+ "record-id",
1852
+ "locked",
1853
+ "editable",
1854
+ "onUploaded",
1855
+ "onDeleted"
1856
+ ])]),
1857
+ createCommentVNode(" Subscribers "),
1858
+ createElementVNode("div", _hoisted_21, [createVNode(SupportTicketSubscribersCollapsible_default, {
1859
+ "support-ticket-id": __props.ticket.id,
1860
+ locked: !!__props.ticket.archived_at
1861
+ }, null, 8, ["support-ticket-id", "locked"])]),
1862
+ createCommentVNode(" Timeline: stay mounted once we have data so adding a comment doesn't unmount/remount "),
1863
+ createElementVNode("div", null, [unref(timelineData) != null ? (openBlock(), createBlock(StaffTimeline_default, {
1864
+ key: 0,
1865
+ ticket: __props.ticket,
1866
+ "customer-notes": customerNotesForTimeline.value,
1867
+ "internal-notes": internalNotesForTimeline.value,
1868
+ "system-events": systemEventsForTimeline.value,
1869
+ "on-add-note": handleAddNote
1870
+ }, null, 8, [
1871
+ "ticket",
1872
+ "customer-notes",
1873
+ "internal-notes",
1874
+ "system-events"
1875
+ ])) : (openBlock(), createElementBlock("div", _hoisted_22, [..._cache[15] || (_cache[15] = [createElementVNode("span", { class: "loading loading-spinner loading-lg" }, null, -1)])]))])
1876
+ ])], 2112)) : createCommentVNode("v-if", true)], 2112);
1877
+ };
1878
+ }
1879
+ });
1880
+ var StaffSupportTicketDetailPage_default = _sfc_main;
1881
+
1882
+ //#endregion
1883
+ export { StaffSupportTicketDetailPage_default as default };
1884
+ //# sourceMappingURL=StaffSupportTicketDetailPage-D49ibqrO.js.map