@dragonmastery/dragoncore-vue 0.0.8 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -13
- package/dist/AppLink-CHMMrSFI.js +0 -54
- package/dist/AppLink-CHMMrSFI.js.map +0 -1
- package/dist/Appearance-BfPdKMXw.js +0 -70
- package/dist/Appearance-BfPdKMXw.js.map +0 -1
- package/dist/Appearance-C3WguxT-.js +0 -3
- package/dist/ChangePasswordPage-DCews8GU.js +0 -86
- package/dist/ChangePasswordPage-DCews8GU.js.map +0 -1
- package/dist/ChangePasswordPage-Dm5vW0nl.js +0 -6
- package/dist/CreateTeamForm-Cg4sD65k.js +0 -27
- package/dist/CreateTeamMemberForm-CiG-fCJD.js +0 -27
- package/dist/CreateUserPage-B8qeBZij.js +0 -76
- package/dist/CreateUserPage-B8qeBZij.js.map +0 -1
- package/dist/CreateUserPage-WjYDkwpb.js +0 -6
- package/dist/CreditBalanceDashboard-BUdKWieE.js +0 -27
- package/dist/CreditManagement-BcyUY_J0.js +0 -27
- package/dist/CustomerCreateSupportTicketForm-BplS0xSi.js +0 -27
- package/dist/CustomerSupportTicketDetailPage-DZQCplSM.js +0 -717
- package/dist/CustomerSupportTicketDetailPage-DZQCplSM.js.map +0 -1
- package/dist/CustomerSupportTicketList-DGwy4Wje.js +0 -27
- package/dist/CustomerSupportTicketParent-BnmTFigo.js +0 -7
- package/dist/CustomerSupportTicketParent-BzY4pmBk.js +0 -66
- package/dist/CustomerSupportTicketParent-BzY4pmBk.js.map +0 -1
- package/dist/CustomerSupportTicketSuccess-DC1jJG1E.js +0 -27
- package/dist/EditTeamForm-BnPwhv5B.js +0 -27
- package/dist/EditTeamMemberForm-B8-pI6Xm.js +0 -6
- package/dist/EditTeamMemberForm-CKbKomrL.js +0 -191
- package/dist/EditTeamMemberForm-CKbKomrL.js.map +0 -1
- package/dist/EditUserPage-BG-Fkx_c.js +0 -7
- package/dist/EditUserPage-XqF25iwz.js +0 -112
- package/dist/EditUserPage-XqF25iwz.js.map +0 -1
- package/dist/ForgotPassword-CjWv2V7p.js +0 -7
- package/dist/ForgotPassword-D3bjL48L.js +0 -73
- package/dist/ForgotPassword-D3bjL48L.js.map +0 -1
- package/dist/LoginForm--br4Il85.js +0 -7
- package/dist/LoginForm-C85U2E2r.js +0 -116
- package/dist/LoginForm-C85U2E2r.js.map +0 -1
- package/dist/Logout-DHT-5Qz3.js +0 -6
- package/dist/Logout-DZuWLh0O.js +0 -38
- package/dist/Logout-DZuWLh0O.js.map +0 -1
- package/dist/ResetPassword-M6mvTS24.js +0 -27
- package/dist/SavedFiltersPage-D3vJrfzt.js +0 -419
- package/dist/SavedFiltersPage-D3vJrfzt.js.map +0 -1
- package/dist/Signup-VZa7U-Ur.js +0 -7
- package/dist/Signup-hpV8J5cM.js +0 -106
- package/dist/Signup-hpV8J5cM.js.map +0 -1
- package/dist/StaffCreateSupportTicketForm-Bc7UnK0Q.js +0 -27
- package/dist/StaffSupportTicketDetailPage-DY07Ez0R.js +0 -1928
- package/dist/StaffSupportTicketDetailPage-DY07Ez0R.js.map +0 -1
- package/dist/StaffSupportTicketList-ChJP_67k.js +0 -27
- package/dist/StaffSupportTicketParent-CWWhaM37.js +0 -66
- package/dist/StaffSupportTicketParent-CWWhaM37.js.map +0 -1
- package/dist/StaffSupportTicketParent-Dp1G85wc.js +0 -7
- package/dist/StaffSupportTicketSuccess-B6X_dP4f.js +0 -27
- package/dist/SupportStaffPage-nd0HowtH.js +0 -156
- package/dist/SupportStaffPage-nd0HowtH.js.map +0 -1
- package/dist/SupportTicketDevLifecycleBadge-Ba-Rm6QW.js +0 -116
- package/dist/SupportTicketDevLifecycleBadge-Ba-Rm6QW.js.map +0 -1
- package/dist/SupportTicketMaintenancePage-rcJ7EfDj.js +0 -56
- package/dist/SupportTicketMaintenancePage-rcJ7EfDj.js.map +0 -1
- package/dist/TeamAttachmentsTab-Dk_Bnk-1.js +0 -27
- package/dist/TeamHistoryTab-CNelXR3Q.js +0 -232
- package/dist/TeamHistoryTab-CNelXR3Q.js.map +0 -1
- package/dist/TeamHistoryTab-siesF93u.js +0 -4
- package/dist/TeamList-BtLzbjls.js +0 -27
- package/dist/TeamMemberList-EoDXIr0w.js +0 -27
- package/dist/TeamMemberParent-DZ5YVyi6.js +0 -27
- package/dist/TeamMembersTab-4gmnP9sD.js +0 -21
- package/dist/TeamMembersTab-4gmnP9sD.js.map +0 -1
- package/dist/TeamMembersTab-DTJxmb-M.js +0 -3
- package/dist/TeamNotesTab-BhVRLG8h.js +0 -458
- package/dist/TeamNotesTab-BhVRLG8h.js.map +0 -1
- package/dist/TeamNotesTab-Crp-afAe.js +0 -7
- package/dist/TeamParent-CbrXXzAr.js +0 -27
- package/dist/TimelineNoteInput-BVqF4MtZ.js +0 -513
- package/dist/TimelineNoteInput-BVqF4MtZ.js.map +0 -1
- package/dist/TimelineSystemEvent-D58zN850.js +0 -1897
- package/dist/TimelineSystemEvent-D58zN850.js.map +0 -1
- package/dist/UserListPage-D68AjrjM.js +0 -4
- package/dist/UserListPage-OGYOLwlw.js +0 -153
- package/dist/UserListPage-OGYOLwlw.js.map +0 -1
- package/dist/UserProfilePage-Q68NAGQQ.js +0 -7
- package/dist/UserProfilePage-uAIfC_NW.js +0 -125
- package/dist/UserProfilePage-uAIfC_NW.js.map +0 -1
- package/dist/ViewTeam-DpE_NfRq.js +0 -27
- package/dist/ViewTeamMember-BdBwkuXC.js +0 -27
- package/dist/convertToLocalDateTime-DOSGtMn8.js +0 -121
- package/dist/convertToLocalDateTime-DOSGtMn8.js.map +0 -1
- package/dist/displayIdFormatter-B1ZKgofu.js +0 -13
- package/dist/displayIdFormatter-B1ZKgofu.js.map +0 -1
- package/dist/extractRpcErrorMessage-C_UbKgHL.js +0 -20
- package/dist/extractRpcErrorMessage-C_UbKgHL.js.map +0 -1
- package/dist/index.d.ts +0 -6366
- package/dist/index.js +0 -30
- package/dist/src-CEBiyg_f.css +0 -13
- package/dist/src-CEBiyg_f.css.map +0 -1
- package/dist/src-CHw8DdkR.js +0 -9200
- package/dist/src-CHw8DdkR.js.map +0 -1
- package/dist/useBreadcrumbs-DmgSucoe.js +0 -41
- package/dist/useBreadcrumbs-DmgSucoe.js.map +0 -1
- package/dist/useMutation-B4_S4Xoa.js +0 -50
- package/dist/useMutation-B4_S4Xoa.js.map +0 -1
- package/dist/useQuery-B7ndu5_P.js +0 -107
- package/dist/useQuery-B7ndu5_P.js.map +0 -1
- package/dist/useQueryCache-DqcDMsxb.js +0 -254
- package/dist/useQueryCache-DqcDMsxb.js.map +0 -1
- package/dist/useRpcAuth-Dp2sec-X.js +0 -731
- package/dist/useRpcAuth-Dp2sec-X.js.map +0 -1
|
@@ -1,1928 +0,0 @@
|
|
|
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 { a as SupportTicketTypeBadge_default, i as SupportTicketApprovalBadge_default, l as formatStaffCreditValue, n as TimelineItem_default, o as SupportTicketPriorityBadge_default, r as formatTicketDate, t as TimelineSystemEvent_default } from "./TimelineSystemEvent-D58zN850.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-B1ZKgofu.js";
|
|
9
|
-
import { t as SupportTicketDevLifecycleBadge_default } from "./SupportTicketDevLifecycleBadge-Ba-Rm6QW.js";
|
|
10
|
-
import { a as SupportTicketAttachmentsCollapsible_default, i as parseRecordVersions, n as MetadataField_default, r as ActionBannerAlert_default, t as TimelineNoteInput_default } from "./TimelineNoteInput-BVqF4MtZ.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_DEV_LIFECYCLE_FILTER_OPTIONS, SUPPORT_TICKET_PRIORITY_NUMBER_TO_LABEL, StaffSupportTicketUpdateSchema, SupportTicketDevLifecycleUpdateEnum, 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 = ["value", "disabled"];
|
|
750
|
-
const _hoisted_4$3 = ["value"];
|
|
751
|
-
const _hoisted_5$3 = {
|
|
752
|
-
key: 2,
|
|
753
|
-
class: "text-base-content/50 italic text-sm"
|
|
754
|
-
};
|
|
755
|
-
const _hoisted_6$3 = { key: 0 };
|
|
756
|
-
const _hoisted_7$3 = {
|
|
757
|
-
key: 1,
|
|
758
|
-
class: "text-base-content/50 italic text-sm"
|
|
759
|
-
};
|
|
760
|
-
const _hoisted_8$3 = { class: "md:col-span-2" };
|
|
761
|
-
const _hoisted_9$3 = {
|
|
762
|
-
key: 0,
|
|
763
|
-
class: "whitespace-pre-wrap text-sm"
|
|
764
|
-
};
|
|
765
|
-
const _hoisted_10$3 = {
|
|
766
|
-
key: 0,
|
|
767
|
-
class: "text-base-content/50"
|
|
768
|
-
};
|
|
769
|
-
const _hoisted_11$3 = { class: "text-base-content/50" };
|
|
770
|
-
const _hoisted_12$3 = {
|
|
771
|
-
key: 1,
|
|
772
|
-
class: "text-base-content/50 italic text-sm"
|
|
773
|
-
};
|
|
774
|
-
const _hoisted_13$3 = { class: "text-base-content/50" };
|
|
775
|
-
const _hoisted_14$3 = { class: "text-base-content/50" };
|
|
776
|
-
const _hoisted_15$3 = { class: "inline-flex items-center gap-1" };
|
|
777
|
-
const _hoisted_16$3 = {
|
|
778
|
-
key: 0,
|
|
779
|
-
"aria-hidden": "true"
|
|
780
|
-
};
|
|
781
|
-
const _hoisted_17$3 = {
|
|
782
|
-
key: 0,
|
|
783
|
-
class: "flex justify-end gap-2 mt-4"
|
|
784
|
-
};
|
|
785
|
-
const _sfc_main$4 = /* @__PURE__ */ defineComponent({
|
|
786
|
-
__name: "StaffMetadataCard",
|
|
787
|
-
props: { ticket: {} },
|
|
788
|
-
emits: ["edit"],
|
|
789
|
-
setup(__props) {
|
|
790
|
-
const props = __props;
|
|
791
|
-
const refreshTicket = inject("refreshTicket");
|
|
792
|
-
const devLifecycleUpdateOptions = SUPPORT_TICKET_DEV_LIFECYCLE_FILTER_OPTIONS.filter((opt) => SupportTicketDevLifecycleUpdateEnum.includes(opt.value));
|
|
793
|
-
const updatingDevLifecycle = ref(false);
|
|
794
|
-
const { mutate: updateTicket } = useMutation((api, input) => api.supportTickets.staffUpdateTicket(input), { invalidate: /^support-tickets?:/ });
|
|
795
|
-
const canEditDevLifecycle = computed(() => {
|
|
796
|
-
const t = props.ticket;
|
|
797
|
-
if (!t.approval_status) return false;
|
|
798
|
-
if (!["APPROVED", "INTERNAL"].includes(t.approval_status)) return false;
|
|
799
|
-
const dl = t.dev_lifecycle;
|
|
800
|
-
if (dl === "DEPLOYED" || dl === "CANCELLED") return false;
|
|
801
|
-
return true;
|
|
802
|
-
});
|
|
803
|
-
const effectiveDevLifecycleForSelect = computed(() => {
|
|
804
|
-
const dl = props.ticket.dev_lifecycle;
|
|
805
|
-
if (dl && SupportTicketDevLifecycleUpdateEnum.includes(dl)) return dl;
|
|
806
|
-
return "BACKLOG";
|
|
807
|
-
});
|
|
808
|
-
async function handleDevLifecycleChange(newDevLifecycle) {
|
|
809
|
-
if (!SupportTicketDevLifecycleUpdateEnum.includes(newDevLifecycle)) return;
|
|
810
|
-
if (!refreshTicket) return;
|
|
811
|
-
const t = props.ticket;
|
|
812
|
-
updatingDevLifecycle.value = true;
|
|
813
|
-
try {
|
|
814
|
-
const priority = typeof t.priority === "number" ? t.priority : supportTicketPriorityToNumber(t.priority);
|
|
815
|
-
await updateTicket({
|
|
816
|
-
id: t.id,
|
|
817
|
-
title: t.title,
|
|
818
|
-
type: t.type,
|
|
819
|
-
priority,
|
|
820
|
-
dev_lifecycle: newDevLifecycle
|
|
821
|
-
});
|
|
822
|
-
await refreshTicket();
|
|
823
|
-
} finally {
|
|
824
|
-
updatingDevLifecycle.value = false;
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
const assigneeDisplay = computed(() => {
|
|
828
|
-
const assigneeId = props.ticket?.assigned_to;
|
|
829
|
-
if (!assigneeId) return null;
|
|
830
|
-
return props.ticket.assigned_to_display_name ?? assigneeId;
|
|
831
|
-
});
|
|
832
|
-
const ticketDisplayId = computed(() => formatTicketDisplayId(props.ticket.display_id, props.ticket.display_id_prefix, props.ticket.id));
|
|
833
|
-
const createdFormatted = computed(() => {
|
|
834
|
-
const at = props.ticket.created_at;
|
|
835
|
-
return at ? formatTicketDate(at).formatted : "";
|
|
836
|
-
});
|
|
837
|
-
const createdRelative = computed(() => {
|
|
838
|
-
const at = props.ticket.created_at;
|
|
839
|
-
return at ? formatTicketDate(at).relative : "";
|
|
840
|
-
});
|
|
841
|
-
const startFormatted = computed(() => props.ticket.start_at ? formatTicketDate(props.ticket.start_at).formatted : "");
|
|
842
|
-
const startRelative = computed(() => props.ticket.start_at ? formatTicketDate(props.ticket.start_at).relative : "");
|
|
843
|
-
const targetFormatted = computed(() => props.ticket.target_at ? formatTicketDate(props.ticket.target_at).formatted : "");
|
|
844
|
-
const targetRelative = computed(() => props.ticket.target_at ? formatTicketDate(props.ticket.target_at).relative : "");
|
|
845
|
-
const completedFormatted = computed(() => props.ticket.completed_at ? formatTicketDate(props.ticket.completed_at).formatted : "");
|
|
846
|
-
const completedRelative = computed(() => props.ticket.completed_at ? formatTicketDate(props.ticket.completed_at).relative : "");
|
|
847
|
-
const isTargetOverdue = computed(() => {
|
|
848
|
-
if (!props.ticket.target_at || props.ticket.completed_at) return false;
|
|
849
|
-
return new Date(props.ticket.target_at) < /* @__PURE__ */ new Date() && !props.ticket.completed_at;
|
|
850
|
-
});
|
|
851
|
-
const creditDisplay = computed(() => formatStaffCreditValue(props.ticket.credit_value, props.ticket.approval_status));
|
|
852
|
-
return (_ctx, _cache) => {
|
|
853
|
-
return openBlock(), createElementBlock("div", _hoisted_1$4, [createElementVNode("div", _hoisted_2$4, [
|
|
854
|
-
createVNode(MetadataField_default, { label: "Status" }, {
|
|
855
|
-
default: withCtx(() => [createVNode(SupportTicketApprovalBadge_default, {
|
|
856
|
-
"approval-status": __props.ticket.approval_status,
|
|
857
|
-
size: "sm"
|
|
858
|
-
}, null, 8, ["approval-status"])]),
|
|
859
|
-
_: 1
|
|
860
|
-
}),
|
|
861
|
-
createVNode(MetadataField_default, { label: "Dev Lifecycle" }, {
|
|
862
|
-
default: withCtx(() => [canEditDevLifecycle.value ? (openBlock(), createElementBlock("select", {
|
|
863
|
-
key: 0,
|
|
864
|
-
value: effectiveDevLifecycleForSelect.value,
|
|
865
|
-
class: "select select-sm select-bordered min-w-[7rem]",
|
|
866
|
-
disabled: updatingDevLifecycle.value,
|
|
867
|
-
onChange: _cache[0] || (_cache[0] = ($event) => handleDevLifecycleChange($event.target.value))
|
|
868
|
-
}, [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(devLifecycleUpdateOptions), (opt) => {
|
|
869
|
-
return openBlock(), createElementBlock("option", {
|
|
870
|
-
key: opt.value,
|
|
871
|
-
value: opt.value
|
|
872
|
-
}, toDisplayString(opt.label), 9, _hoisted_4$3);
|
|
873
|
-
}), 128))], 40, _hoisted_3$3)) : __props.ticket.dev_lifecycle ? (openBlock(), createBlock(SupportTicketDevLifecycleBadge_default, {
|
|
874
|
-
key: 1,
|
|
875
|
-
"dev-lifecycle": __props.ticket.dev_lifecycle,
|
|
876
|
-
size: "sm"
|
|
877
|
-
}, null, 8, ["dev-lifecycle"])) : (openBlock(), createElementBlock("span", _hoisted_5$3, "Not started"))]),
|
|
878
|
-
_: 1
|
|
879
|
-
}),
|
|
880
|
-
createVNode(MetadataField_default, { label: "Type" }, {
|
|
881
|
-
default: withCtx(() => [createVNode(SupportTicketTypeBadge_default, {
|
|
882
|
-
type: __props.ticket.type,
|
|
883
|
-
size: "sm"
|
|
884
|
-
}, null, 8, ["type"])]),
|
|
885
|
-
_: 1
|
|
886
|
-
}),
|
|
887
|
-
createVNode(MetadataField_default, { label: "Priority" }, {
|
|
888
|
-
default: withCtx(() => [createVNode(SupportTicketPriorityBadge_default, {
|
|
889
|
-
priority: __props.ticket.priority,
|
|
890
|
-
size: "sm"
|
|
891
|
-
}, null, 8, ["priority"])]),
|
|
892
|
-
_: 1
|
|
893
|
-
}),
|
|
894
|
-
createVNode(MetadataField_default, { label: "Assignee" }, {
|
|
895
|
-
default: withCtx(() => [assigneeDisplay.value ? (openBlock(), createElementBlock("span", _hoisted_6$3, toDisplayString(assigneeDisplay.value), 1)) : (openBlock(), createElementBlock("span", _hoisted_7$3, "Unassigned"))]),
|
|
896
|
-
_: 1
|
|
897
|
-
}),
|
|
898
|
-
createVNode(MetadataField_default, { label: "Requester" }, {
|
|
899
|
-
default: withCtx(() => [createTextVNode(toDisplayString(__props.ticket.created_by_display_name), 1)]),
|
|
900
|
-
_: 1
|
|
901
|
-
}),
|
|
902
|
-
createElementVNode("div", _hoisted_8$3, [createVNode(MetadataField_default, { label: "Description" }, {
|
|
903
|
-
empty: withCtx(() => [..._cache[2] || (_cache[2] = [createElementVNode("span", { class: "text-base-content/50 italic text-sm" }, "No description provided", -1)])]),
|
|
904
|
-
default: withCtx(() => [__props.ticket.description?.trim() ? (openBlock(), createElementBlock("p", _hoisted_9$3, toDisplayString(__props.ticket.description), 1)) : createCommentVNode("v-if", true)]),
|
|
905
|
-
_: 1
|
|
906
|
-
})]),
|
|
907
|
-
createVNode(MetadataField_default, {
|
|
908
|
-
label: "Ticket ID",
|
|
909
|
-
copyable: true
|
|
910
|
-
}, {
|
|
911
|
-
default: withCtx(() => [createTextVNode(toDisplayString(ticketDisplayId.value), 1)]),
|
|
912
|
-
_: 1
|
|
913
|
-
}),
|
|
914
|
-
createVNode(MetadataField_default, { label: "Created" }, {
|
|
915
|
-
default: withCtx(() => [createTextVNode(toDisplayString(createdFormatted.value) + " ", 1), createdRelative.value ? (openBlock(), createElementBlock("span", _hoisted_10$3, "· " + toDisplayString(createdRelative.value), 1)) : createCommentVNode("v-if", true)]),
|
|
916
|
-
_: 1
|
|
917
|
-
}),
|
|
918
|
-
createVNode(MetadataField_default, { label: "Started" }, {
|
|
919
|
-
default: withCtx(() => [__props.ticket.start_at ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode(toDisplayString(startFormatted.value) + " ", 1), createElementVNode("span", _hoisted_11$3, "· " + toDisplayString(startRelative.value), 1)], 64)) : (openBlock(), createElementBlock("span", _hoisted_12$3, "Not set"))]),
|
|
920
|
-
_: 1
|
|
921
|
-
}),
|
|
922
|
-
createVNode(MetadataField_default, { label: "Target" }, {
|
|
923
|
-
empty: withCtx(() => [..._cache[3] || (_cache[3] = [createElementVNode("span", { class: "text-base-content/50 italic text-sm" }, "Not set", -1)])]),
|
|
924
|
-
default: withCtx(() => [__props.ticket.target_at ? (openBlock(), createElementBlock("span", {
|
|
925
|
-
key: 0,
|
|
926
|
-
class: normalizeClass({ "text-error": isTargetOverdue.value })
|
|
927
|
-
}, [createTextVNode(toDisplayString(targetFormatted.value) + " ", 1), createElementVNode("span", _hoisted_13$3, "· " + toDisplayString(targetRelative.value), 1)], 2)) : createCommentVNode("v-if", true)]),
|
|
928
|
-
_: 1
|
|
929
|
-
}),
|
|
930
|
-
createVNode(MetadataField_default, { label: "Completed" }, {
|
|
931
|
-
empty: withCtx(() => [..._cache[4] || (_cache[4] = [createElementVNode("span", { class: "text-base-content/50 italic text-sm" }, "Not set", -1)])]),
|
|
932
|
-
default: withCtx(() => [__props.ticket.completed_at ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode(toDisplayString(completedFormatted.value) + " ", 1), createElementVNode("span", _hoisted_14$3, "· " + toDisplayString(completedRelative.value), 1)], 64)) : createCommentVNode("v-if", true)]),
|
|
933
|
-
_: 1
|
|
934
|
-
}),
|
|
935
|
-
createVNode(MetadataField_default, { label: "Credits" }, {
|
|
936
|
-
default: withCtx(() => [createElementVNode("span", _hoisted_15$3, [__props.ticket.is_locked ? (openBlock(), createElementBlock("span", _hoisted_16$3, "🔒")) : createCommentVNode("v-if", true), createTextVNode(" " + toDisplayString(creditDisplay.value), 1)])]),
|
|
937
|
-
_: 1
|
|
938
|
-
}),
|
|
939
|
-
createVNode(MetadataField_default, { label: "Delivered Value" }, {
|
|
940
|
-
empty: withCtx(() => [..._cache[5] || (_cache[5] = [createElementVNode("span", { class: "text-base-content/50 italic text-sm" }, "Not set", -1)])]),
|
|
941
|
-
default: withCtx(() => [__props.ticket.delivered_value?.trim() ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode(toDisplayString(__props.ticket.delivered_value), 1)], 64)) : createCommentVNode("v-if", true)]),
|
|
942
|
-
_: 1
|
|
943
|
-
})
|
|
944
|
-
]), !__props.ticket.archived_at ? (openBlock(), createElementBlock("div", _hoisted_17$3, [createElementVNode("button", {
|
|
945
|
-
type: "button",
|
|
946
|
-
class: "btn btn-sm",
|
|
947
|
-
onClick: _cache[1] || (_cache[1] = ($event) => _ctx.$emit("edit"))
|
|
948
|
-
}, [..._cache[6] || (_cache[6] = [createElementVNode("svg", {
|
|
949
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
950
|
-
fill: "none",
|
|
951
|
-
viewBox: "0 0 24 24",
|
|
952
|
-
"stroke-width": "1.5",
|
|
953
|
-
stroke: "currentColor",
|
|
954
|
-
class: "w-4 h-4 mr-2"
|
|
955
|
-
}, [createElementVNode("path", {
|
|
956
|
-
"stroke-linecap": "round",
|
|
957
|
-
"stroke-linejoin": "round",
|
|
958
|
-
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"
|
|
959
|
-
})], -1), createTextVNode(" Edit ", -1)])])])) : createCommentVNode("v-if", true)]);
|
|
960
|
-
};
|
|
961
|
-
}
|
|
962
|
-
});
|
|
963
|
-
var StaffMetadataCard_default = _sfc_main$4;
|
|
964
|
-
|
|
965
|
-
//#endregion
|
|
966
|
-
//#region src/slices/support_ticket/staff/staffMetadataCardEditFormMetadata.ts
|
|
967
|
-
const staffMetadataCardEditFormMetadata = withMetadata(StaffSupportTicketUpdateSchema, "staffMetadataCardEditForm", {
|
|
968
|
-
title: {
|
|
969
|
-
label: "Title",
|
|
970
|
-
placeholder: "Enter ticket title"
|
|
971
|
-
},
|
|
972
|
-
description: {
|
|
973
|
-
label: "Description",
|
|
974
|
-
inputType: "textarea",
|
|
975
|
-
placeholder: "Describe the ticket in detail"
|
|
976
|
-
},
|
|
977
|
-
type: { label: "Type" },
|
|
978
|
-
priority: {
|
|
979
|
-
label: "Priority",
|
|
980
|
-
inputType: "select",
|
|
981
|
-
valueToLabel: SUPPORT_TICKET_PRIORITY_NUMBER_TO_LABEL,
|
|
982
|
-
valueType: "number"
|
|
983
|
-
},
|
|
984
|
-
dev_lifecycle: { label: "Development Lifecycle" },
|
|
985
|
-
credit_value: {
|
|
986
|
-
label: "Credit Value",
|
|
987
|
-
inputType: "currency",
|
|
988
|
-
step: .01,
|
|
989
|
-
placeholder: "0.00"
|
|
990
|
-
},
|
|
991
|
-
delivered_value: {
|
|
992
|
-
label: "Delivered Value",
|
|
993
|
-
inputType: "currency",
|
|
994
|
-
step: .01,
|
|
995
|
-
placeholder: "0.00"
|
|
996
|
-
},
|
|
997
|
-
start_at: {
|
|
998
|
-
label: "Start At",
|
|
999
|
-
inputType: "date"
|
|
1000
|
-
},
|
|
1001
|
-
target_at: {
|
|
1002
|
-
label: "Target At",
|
|
1003
|
-
inputType: "date"
|
|
1004
|
-
},
|
|
1005
|
-
completed_at: {
|
|
1006
|
-
label: "Completed At",
|
|
1007
|
-
inputType: "date"
|
|
1008
|
-
}
|
|
1009
|
-
});
|
|
1010
|
-
|
|
1011
|
-
//#endregion
|
|
1012
|
-
//#region src/slices/support_ticket/staff/components/StaffMetadataCardEdit.vue
|
|
1013
|
-
const _hoisted_1$3 = { class: "grid grid-cols-1 md:grid-cols-2 gap-4" };
|
|
1014
|
-
const _hoisted_2$3 = { class: "form-control md:col-span-2" };
|
|
1015
|
-
const _hoisted_3$2 = { class: "form-control md:col-span-2" };
|
|
1016
|
-
const _hoisted_4$2 = { class: "form-control" };
|
|
1017
|
-
const _hoisted_5$2 = {
|
|
1018
|
-
key: 0,
|
|
1019
|
-
class: "form-control"
|
|
1020
|
-
};
|
|
1021
|
-
const _hoisted_6$2 = ["disabled"];
|
|
1022
|
-
const _hoisted_7$2 = ["value"];
|
|
1023
|
-
const _hoisted_8$2 = { class: "form-control" };
|
|
1024
|
-
const _hoisted_9$2 = {
|
|
1025
|
-
key: 1,
|
|
1026
|
-
class: "form-control"
|
|
1027
|
-
};
|
|
1028
|
-
const _hoisted_10$2 = {
|
|
1029
|
-
key: 2,
|
|
1030
|
-
class: "form-control"
|
|
1031
|
-
};
|
|
1032
|
-
const _hoisted_11$2 = {
|
|
1033
|
-
key: 3,
|
|
1034
|
-
class: "form-control"
|
|
1035
|
-
};
|
|
1036
|
-
const _hoisted_12$2 = {
|
|
1037
|
-
key: 4,
|
|
1038
|
-
class: "form-control"
|
|
1039
|
-
};
|
|
1040
|
-
const _hoisted_13$2 = {
|
|
1041
|
-
key: 5,
|
|
1042
|
-
class: "form-control"
|
|
1043
|
-
};
|
|
1044
|
-
const _hoisted_14$2 = {
|
|
1045
|
-
key: 6,
|
|
1046
|
-
class: "form-control"
|
|
1047
|
-
};
|
|
1048
|
-
const _hoisted_15$2 = {
|
|
1049
|
-
key: 0,
|
|
1050
|
-
class: "alert alert-error py-2 mt-4"
|
|
1051
|
-
};
|
|
1052
|
-
const _hoisted_16$2 = { class: "text-sm" };
|
|
1053
|
-
const _hoisted_17$2 = { class: "flex justify-end gap-2 mt-4" };
|
|
1054
|
-
const _hoisted_18$2 = ["disabled"];
|
|
1055
|
-
const _sfc_main$3 = /* @__PURE__ */ defineComponent({
|
|
1056
|
-
__name: "StaffMetadataCardEdit",
|
|
1057
|
-
props: {
|
|
1058
|
-
ticket: {},
|
|
1059
|
-
saving: { type: Boolean }
|
|
1060
|
-
},
|
|
1061
|
-
emits: ["success", "cancel"],
|
|
1062
|
-
setup(__props, { emit: __emit }) {
|
|
1063
|
-
const props = __props;
|
|
1064
|
-
const assigneeId = ref(props.ticket.assigned_to ?? "");
|
|
1065
|
-
watch(() => props.ticket.assigned_to, (v) => {
|
|
1066
|
-
assigneeId.value = v ?? "";
|
|
1067
|
-
});
|
|
1068
|
-
const { data: triageUsers } = useQuery((api) => api.users.getTriageUsers(), {
|
|
1069
|
-
cacheKey: "triage-users",
|
|
1070
|
-
staleTime: 3600 * 1e3
|
|
1071
|
-
});
|
|
1072
|
-
const emit = __emit;
|
|
1073
|
-
const { mutate: updateTicket, loading: isSaving } = useMutation((api, input) => api.supportTickets.staffUpdateTicket(input), { invalidate: /^support-tickets?:/ });
|
|
1074
|
-
const saving = computed(() => props.saving ?? isSaving.value);
|
|
1075
|
-
function formatDateForInput(dateString) {
|
|
1076
|
-
if (!dateString) return null;
|
|
1077
|
-
try {
|
|
1078
|
-
return new Date(dateString).toISOString().split("T")[0] || null;
|
|
1079
|
-
} catch {
|
|
1080
|
-
return null;
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
function formatDateForOutput(dateString) {
|
|
1084
|
-
if (!dateString || dateString.trim() === "") return null;
|
|
1085
|
-
try {
|
|
1086
|
-
const date = /* @__PURE__ */ new Date(dateString + "T00:00:00");
|
|
1087
|
-
if (isNaN(date.getTime())) return null;
|
|
1088
|
-
return date.toISOString();
|
|
1089
|
-
} catch {
|
|
1090
|
-
return null;
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
const { form, zinia, ZiniaForm, ZiniaSubmitButton, ZiniaFormErrorsSummary } = useForm(staffMetadataCardEditFormMetadata, {
|
|
1094
|
-
storeName: `staff-metadata-card-edit-${props.ticket.id}`,
|
|
1095
|
-
persistToLocalStorage: false,
|
|
1096
|
-
renderStyle: "daisy_ui",
|
|
1097
|
-
fetchData: async () => {
|
|
1098
|
-
const t = props.ticket;
|
|
1099
|
-
const devLifecycle = t.dev_lifecycle && [
|
|
1100
|
-
"BACKLOG",
|
|
1101
|
-
"PLANNING",
|
|
1102
|
-
"DEVELOPMENT",
|
|
1103
|
-
"CODE_REVIEW",
|
|
1104
|
-
"TESTING",
|
|
1105
|
-
"STAGING",
|
|
1106
|
-
"PO_APPROVAL",
|
|
1107
|
-
"VERIFICATION"
|
|
1108
|
-
].includes(t.dev_lifecycle) ? t.dev_lifecycle : void 0;
|
|
1109
|
-
return {
|
|
1110
|
-
id: t.id,
|
|
1111
|
-
title: t.title || "",
|
|
1112
|
-
description: t.description ?? "",
|
|
1113
|
-
type: t.type,
|
|
1114
|
-
priority: supportTicketPriorityToNumber(t.priority),
|
|
1115
|
-
dev_lifecycle: devLifecycle,
|
|
1116
|
-
credit_value: t.credit_value?.trim() || null,
|
|
1117
|
-
delivered_value: t.delivered_value?.trim() || null,
|
|
1118
|
-
start_at: formatDateForInput(t.start_at) ?? null,
|
|
1119
|
-
target_at: formatDateForInput(t.target_at) ?? null,
|
|
1120
|
-
completed_at: formatDateForInput(t.completed_at) ?? null
|
|
1121
|
-
};
|
|
1122
|
-
}
|
|
1123
|
-
});
|
|
1124
|
-
function buildPayload(formData) {
|
|
1125
|
-
const t = props.ticket;
|
|
1126
|
-
const payload = {
|
|
1127
|
-
id: t.id,
|
|
1128
|
-
title: String(formData.title ?? "").trim(),
|
|
1129
|
-
description: formData.description || "",
|
|
1130
|
-
type: formData.type,
|
|
1131
|
-
priority: formData.priority
|
|
1132
|
-
};
|
|
1133
|
-
if (["APPROVED", "INTERNAL"].includes(t.approval_status || "") && t.dev_lifecycle !== "DEPLOYED" && t.dev_lifecycle !== "CANCELLED") {
|
|
1134
|
-
const dl = formData.dev_lifecycle;
|
|
1135
|
-
if (dl !== null && dl !== void 0 && dl !== "PENDING" && dl !== "DEPLOYED" && dl !== "CANCELLED") payload.dev_lifecycle = dl;
|
|
1136
|
-
}
|
|
1137
|
-
if (t.approval_status === "PENDING") {
|
|
1138
|
-
const cv = formData.credit_value;
|
|
1139
|
-
if (cv != null && cv !== "") payload.credit_value = String(cv).trim();
|
|
1140
|
-
}
|
|
1141
|
-
if (t.dev_lifecycle === "DEPLOYED") {
|
|
1142
|
-
const dv = formData.delivered_value;
|
|
1143
|
-
if (dv != null && dv !== "") payload.delivered_value = String(dv).trim();
|
|
1144
|
-
}
|
|
1145
|
-
if ([
|
|
1146
|
-
"PENDING",
|
|
1147
|
-
"APPROVED",
|
|
1148
|
-
"INTERNAL"
|
|
1149
|
-
].includes(t.approval_status || "") && t.dev_lifecycle !== "CANCELLED" && t.dev_lifecycle !== "DEPLOYED") {
|
|
1150
|
-
const startAt = formatDateForOutput(formData.start_at);
|
|
1151
|
-
if (startAt !== null) payload.start_at = startAt;
|
|
1152
|
-
const targetAt = formatDateForOutput(formData.target_at);
|
|
1153
|
-
if (targetAt !== null) payload.target_at = targetAt;
|
|
1154
|
-
}
|
|
1155
|
-
if (t.dev_lifecycle === "DEPLOYED" || t.dev_lifecycle === "CANCELLED") {
|
|
1156
|
-
const completedAt = formatDateForOutput(formData.completed_at);
|
|
1157
|
-
if (completedAt !== null) payload.completed_at = completedAt;
|
|
1158
|
-
}
|
|
1159
|
-
payload.assigned_to = assigneeId.value || null;
|
|
1160
|
-
return payload;
|
|
1161
|
-
}
|
|
1162
|
-
async function handleSubmit(formData) {
|
|
1163
|
-
await updateTicket(buildPayload(formData));
|
|
1164
|
-
}
|
|
1165
|
-
function handleSuccess() {
|
|
1166
|
-
emit("success");
|
|
1167
|
-
}
|
|
1168
|
-
function handleError(error) {
|
|
1169
|
-
const message = extractRpcErrorMessage(error, "Failed to update ticket");
|
|
1170
|
-
form.setSubmitError(message);
|
|
1171
|
-
}
|
|
1172
|
-
function handleCancel() {
|
|
1173
|
-
emit("cancel");
|
|
1174
|
-
}
|
|
1175
|
-
return (_ctx, _cache) => {
|
|
1176
|
-
return openBlock(), createBlock(unref(ZiniaForm), {
|
|
1177
|
-
onHandleSubmit: handleSubmit,
|
|
1178
|
-
onSuccess: handleSuccess,
|
|
1179
|
-
onError: handleError,
|
|
1180
|
-
title: "",
|
|
1181
|
-
subtitle: ""
|
|
1182
|
-
}, {
|
|
1183
|
-
default: withCtx(() => [
|
|
1184
|
-
createElementVNode("div", _hoisted_1$3, [
|
|
1185
|
-
createCommentVNode(" Title - full width "),
|
|
1186
|
-
createElementVNode("div", _hoisted_2$3, [createVNode(unref(zinia).TitleField, { placeholder: "Enter ticket title" })]),
|
|
1187
|
-
createCommentVNode(" Description - full width "),
|
|
1188
|
-
createElementVNode("div", _hoisted_3$2, [createVNode(unref(zinia).DescriptionField, {
|
|
1189
|
-
class: "w-full",
|
|
1190
|
-
placeholder: "Describe the ticket in detail"
|
|
1191
|
-
})]),
|
|
1192
|
-
createCommentVNode(" Type "),
|
|
1193
|
-
createElementVNode("div", _hoisted_4$2, [createVNode(unref(zinia).TypeField)]),
|
|
1194
|
-
createCommentVNode(" Assignee - only when not archived "),
|
|
1195
|
-
!__props.ticket.archived_at ? (openBlock(), createElementBlock("div", _hoisted_5$2, [_cache[2] || (_cache[2] = createElementVNode("label", { class: "label" }, [createElementVNode("span", { class: "label-text" }, "Assignee")], -1)), withDirectives(createElementVNode("select", {
|
|
1196
|
-
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => assigneeId.value = $event),
|
|
1197
|
-
class: "select select-bordered w-full",
|
|
1198
|
-
disabled: saving.value
|
|
1199
|
-
}, [_cache[1] || (_cache[1] = createElementVNode("option", { value: "" }, "Unassigned", -1)), (openBlock(true), createElementBlock(Fragment, null, renderList(unref(triageUsers) ?? [], (u) => {
|
|
1200
|
-
return openBlock(), createElementBlock("option", {
|
|
1201
|
-
key: u.id,
|
|
1202
|
-
value: u.id
|
|
1203
|
-
}, toDisplayString(u.email), 9, _hoisted_7$2);
|
|
1204
|
-
}), 128))], 8, _hoisted_6$2), [[vModelSelect, assigneeId.value]])])) : createCommentVNode("v-if", true),
|
|
1205
|
-
createCommentVNode(" Priority "),
|
|
1206
|
-
createElementVNode("div", _hoisted_8$2, [createVNode(unref(zinia).PriorityField)]),
|
|
1207
|
-
createCommentVNode(" Development Lifecycle - Only editable when APPROVED or INTERNAL (not terminal states) "),
|
|
1208
|
-
["APPROVED", "INTERNAL"].includes(__props.ticket.approval_status || "") && __props.ticket.dev_lifecycle !== "DEPLOYED" && __props.ticket.dev_lifecycle !== "CANCELLED" ? (openBlock(), createElementBlock("div", _hoisted_9$2, [createVNode(unref(zinia).DevLifecycleField)])) : createCommentVNode("v-if", true),
|
|
1209
|
-
createCommentVNode(" Credit Value - Only editable when PENDING "),
|
|
1210
|
-
__props.ticket.approval_status === "PENDING" ? (openBlock(), createElementBlock("div", _hoisted_10$2, [createVNode(unref(zinia).CreditValueField, { placeholder: "0.00" })])) : createCommentVNode("v-if", true),
|
|
1211
|
-
createCommentVNode(" Delivered Value - Only editable when DEPLOYED "),
|
|
1212
|
-
__props.ticket.dev_lifecycle === "DEPLOYED" ? (openBlock(), createElementBlock("div", _hoisted_11$2, [createVNode(unref(zinia).DeliveredValueField, { placeholder: "0.00" })])) : createCommentVNode("v-if", true),
|
|
1213
|
-
createCommentVNode(" Start At - Only editable when PENDING, APPROVED, or INTERNAL (not terminal) "),
|
|
1214
|
-
[
|
|
1215
|
-
"PENDING",
|
|
1216
|
-
"APPROVED",
|
|
1217
|
-
"INTERNAL"
|
|
1218
|
-
].includes(__props.ticket.approval_status || "") && __props.ticket.dev_lifecycle !== "CANCELLED" && __props.ticket.dev_lifecycle !== "DEPLOYED" ? (openBlock(), createElementBlock("div", _hoisted_12$2, [createVNode(unref(zinia).StartAtField, { formatter: unref(formatToISODate) }, null, 8, ["formatter"])])) : createCommentVNode("v-if", true),
|
|
1219
|
-
createCommentVNode(" Target At - Only editable when PENDING, APPROVED, or INTERNAL (not terminal) "),
|
|
1220
|
-
[
|
|
1221
|
-
"PENDING",
|
|
1222
|
-
"APPROVED",
|
|
1223
|
-
"INTERNAL"
|
|
1224
|
-
].includes(__props.ticket.approval_status || "") && __props.ticket.dev_lifecycle !== "CANCELLED" && __props.ticket.dev_lifecycle !== "DEPLOYED" ? (openBlock(), createElementBlock("div", _hoisted_13$2, [createVNode(unref(zinia).TargetAtField, { formatter: unref(formatToISODate) }, null, 8, ["formatter"])])) : createCommentVNode("v-if", true),
|
|
1225
|
-
createCommentVNode(" Completed At - Only editable when DEPLOYED or CANCELLED "),
|
|
1226
|
-
__props.ticket.dev_lifecycle === "DEPLOYED" || __props.ticket.dev_lifecycle === "CANCELLED" ? (openBlock(), createElementBlock("div", _hoisted_14$2, [createVNode(unref(zinia).CompletedAtField, { formatter: unref(formatToISODate) }, null, 8, ["formatter"])])) : createCommentVNode("v-if", true)
|
|
1227
|
-
]),
|
|
1228
|
-
unref(form).submitError ? (openBlock(), createElementBlock("div", _hoisted_15$2, [createElementVNode("span", _hoisted_16$2, toDisplayString(unref(form).submitError), 1)])) : createCommentVNode("v-if", true),
|
|
1229
|
-
createVNode(unref(ZiniaFormErrorsSummary), { title: "Please fix the following errors:" }),
|
|
1230
|
-
createCommentVNode(" Action Buttons "),
|
|
1231
|
-
createElementVNode("div", _hoisted_17$2, [createElementVNode("button", {
|
|
1232
|
-
type: "button",
|
|
1233
|
-
class: "btn btn-ghost",
|
|
1234
|
-
onClick: handleCancel,
|
|
1235
|
-
disabled: saving.value
|
|
1236
|
-
}, " Cancel ", 8, _hoisted_18$2), createVNode(unref(ZiniaSubmitButton), {
|
|
1237
|
-
submitText: "Save",
|
|
1238
|
-
submittingText: "Saving..."
|
|
1239
|
-
})])
|
|
1240
|
-
]),
|
|
1241
|
-
_: 1
|
|
1242
|
-
});
|
|
1243
|
-
};
|
|
1244
|
-
}
|
|
1245
|
-
});
|
|
1246
|
-
var StaffMetadataCardEdit_default = _sfc_main$3;
|
|
1247
|
-
|
|
1248
|
-
//#endregion
|
|
1249
|
-
//#region src/slices/support_ticket/staff/components/StaffTimeline.vue
|
|
1250
|
-
const _hoisted_1$2 = { class: "flex flex-col gap-4 w-full" };
|
|
1251
|
-
const _hoisted_2$2 = {
|
|
1252
|
-
key: 0,
|
|
1253
|
-
class: "text-base-content/50 text-sm text-center py-8"
|
|
1254
|
-
};
|
|
1255
|
-
const _sfc_main$2 = /* @__PURE__ */ defineComponent({
|
|
1256
|
-
__name: "StaffTimeline",
|
|
1257
|
-
props: {
|
|
1258
|
-
ticket: {},
|
|
1259
|
-
customerNotes: {},
|
|
1260
|
-
internalNotes: {},
|
|
1261
|
-
systemEvents: {},
|
|
1262
|
-
onAddNote: { type: Function }
|
|
1263
|
-
},
|
|
1264
|
-
setup(__props, { expose: __expose }) {
|
|
1265
|
-
const props = __props;
|
|
1266
|
-
const route = useRoute();
|
|
1267
|
-
const router = useRouter();
|
|
1268
|
-
const noteTypeFromQuery = computed(() => {
|
|
1269
|
-
return route.query.noteType === "internal" ? "internal" : "customer";
|
|
1270
|
-
});
|
|
1271
|
-
function updateNoteTypeQuery(value) {
|
|
1272
|
-
router.replace({
|
|
1273
|
-
path: route.path,
|
|
1274
|
-
query: {
|
|
1275
|
-
...route.query,
|
|
1276
|
-
noteType: value === "internal" ? "internal" : void 0
|
|
1277
|
-
}
|
|
1278
|
-
});
|
|
1279
|
-
}
|
|
1280
|
-
const noteInputRef = ref(null);
|
|
1281
|
-
const mergedTimelineItems = computed(() => {
|
|
1282
|
-
const items = [];
|
|
1283
|
-
for (const note of props.customerNotes ?? []) items.push({
|
|
1284
|
-
type: "customer-note",
|
|
1285
|
-
timestamp: note.createdAt,
|
|
1286
|
-
id: `customer-note-${note.createdAt}-${note.authorName}`,
|
|
1287
|
-
data: note
|
|
1288
|
-
});
|
|
1289
|
-
for (const note of props.internalNotes ?? []) items.push({
|
|
1290
|
-
type: "internal-note",
|
|
1291
|
-
timestamp: note.createdAt,
|
|
1292
|
-
id: `internal-note-${note.createdAt}-${note.authorName}`,
|
|
1293
|
-
data: note
|
|
1294
|
-
});
|
|
1295
|
-
for (const event of props.systemEvents ?? []) items.push({
|
|
1296
|
-
type: "system-event",
|
|
1297
|
-
timestamp: event.timestamp,
|
|
1298
|
-
id: event.id,
|
|
1299
|
-
data: event
|
|
1300
|
-
});
|
|
1301
|
-
items.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
1302
|
-
return items;
|
|
1303
|
-
});
|
|
1304
|
-
function resetInput() {
|
|
1305
|
-
noteInputRef.value?.reset();
|
|
1306
|
-
}
|
|
1307
|
-
__expose({ resetInput });
|
|
1308
|
-
return (_ctx, _cache) => {
|
|
1309
|
-
return openBlock(), createElementBlock("div", _hoisted_1$2, [
|
|
1310
|
-
createVNode(TimelineNoteInput_default, {
|
|
1311
|
-
ref_key: "noteInputRef",
|
|
1312
|
-
ref: noteInputRef,
|
|
1313
|
-
"show-type-toggle": true,
|
|
1314
|
-
disabled: !!__props.ticket.archived_at,
|
|
1315
|
-
"note-type": noteTypeFromQuery.value,
|
|
1316
|
-
"on-submit": __props.onAddNote,
|
|
1317
|
-
"onUpdate:noteType": updateNoteTypeQuery
|
|
1318
|
-
}, null, 8, [
|
|
1319
|
-
"disabled",
|
|
1320
|
-
"note-type",
|
|
1321
|
-
"on-submit"
|
|
1322
|
-
]),
|
|
1323
|
-
mergedTimelineItems.value.length === 0 ? (openBlock(), createElementBlock("p", _hoisted_2$2, " No activity yet. ")) : createCommentVNode("v-if", true),
|
|
1324
|
-
(openBlock(true), createElementBlock(Fragment, null, renderList(mergedTimelineItems.value, (item) => {
|
|
1325
|
-
return openBlock(), createElementBlock(Fragment, { key: item.id }, [item.type === "customer-note" || item.type === "internal-note" ? (openBlock(), createBlock(TimelineItem_default, {
|
|
1326
|
-
key: 0,
|
|
1327
|
-
"author-name": item.data.authorName,
|
|
1328
|
-
"created-at": item.data.createdAt,
|
|
1329
|
-
variant: item.type === "customer-note" ? "customer" : "internal"
|
|
1330
|
-
}, {
|
|
1331
|
-
default: withCtx(() => [createTextVNode(toDisplayString(item.data.body ?? ""), 1)]),
|
|
1332
|
-
_: 2
|
|
1333
|
-
}, 1032, [
|
|
1334
|
-
"author-name",
|
|
1335
|
-
"created-at",
|
|
1336
|
-
"variant"
|
|
1337
|
-
])) : item.type === "system-event" ? (openBlock(), createBlock(TimelineSystemEvent_default, {
|
|
1338
|
-
key: 1,
|
|
1339
|
-
author: item.data.author,
|
|
1340
|
-
message: item.data.message,
|
|
1341
|
-
timestamp: item.data.timestamp,
|
|
1342
|
-
action: item.data.action,
|
|
1343
|
-
type: item.data.type,
|
|
1344
|
-
details: item.data.details,
|
|
1345
|
-
"old-value": item.data.oldValue,
|
|
1346
|
-
"new-value": item.data.newValue,
|
|
1347
|
-
changes: item.data.changes
|
|
1348
|
-
}, null, 8, [
|
|
1349
|
-
"author",
|
|
1350
|
-
"message",
|
|
1351
|
-
"timestamp",
|
|
1352
|
-
"action",
|
|
1353
|
-
"type",
|
|
1354
|
-
"details",
|
|
1355
|
-
"old-value",
|
|
1356
|
-
"new-value",
|
|
1357
|
-
"changes"
|
|
1358
|
-
])) : createCommentVNode("v-if", true)], 64);
|
|
1359
|
-
}), 128))
|
|
1360
|
-
]);
|
|
1361
|
-
};
|
|
1362
|
-
}
|
|
1363
|
-
});
|
|
1364
|
-
var StaffTimeline_default = _sfc_main$2;
|
|
1365
|
-
|
|
1366
|
-
//#endregion
|
|
1367
|
-
//#region src/slices/support_ticket/staff/components/SupportTicketSubscribersCollapsible.vue
|
|
1368
|
-
const _hoisted_1$1 = {
|
|
1369
|
-
key: 0,
|
|
1370
|
-
class: "w-full"
|
|
1371
|
-
};
|
|
1372
|
-
const _hoisted_2$1 = { class: "collapse collapse-arrow bg-base-200" };
|
|
1373
|
-
const _hoisted_3$1 = { class: "collapse-title text-sm font-medium px-4 py-2 min-h-0" };
|
|
1374
|
-
const _hoisted_4$1 = { class: "flex items-center gap-2" };
|
|
1375
|
-
const _hoisted_5$1 = { class: "collapse-content px-0" };
|
|
1376
|
-
const _hoisted_6$1 = { class: "px-4 pb-4 space-y-4" };
|
|
1377
|
-
const _hoisted_7$1 = {
|
|
1378
|
-
key: 0,
|
|
1379
|
-
class: "flex flex-wrap items-end gap-2"
|
|
1380
|
-
};
|
|
1381
|
-
const _hoisted_8$1 = { class: "form-control flex-1 min-w-[200px]" };
|
|
1382
|
-
const _hoisted_9$1 = ["disabled"];
|
|
1383
|
-
const _hoisted_10$1 = ["value"];
|
|
1384
|
-
const _hoisted_11$1 = ["disabled"];
|
|
1385
|
-
const _hoisted_12$1 = {
|
|
1386
|
-
key: 0,
|
|
1387
|
-
class: "loading loading-spinner loading-xs"
|
|
1388
|
-
};
|
|
1389
|
-
const _hoisted_13$1 = { key: 1 };
|
|
1390
|
-
const _hoisted_14$1 = {
|
|
1391
|
-
key: 1,
|
|
1392
|
-
class: "flex justify-center py-4"
|
|
1393
|
-
};
|
|
1394
|
-
const _hoisted_15$1 = {
|
|
1395
|
-
key: 2,
|
|
1396
|
-
class: "text-sm text-base-content/50 italic"
|
|
1397
|
-
};
|
|
1398
|
-
const _hoisted_16$1 = {
|
|
1399
|
-
key: 3,
|
|
1400
|
-
class: "space-y-2"
|
|
1401
|
-
};
|
|
1402
|
-
const _hoisted_17$1 = { class: "text-sm truncate" };
|
|
1403
|
-
const _hoisted_18$1 = ["onClick", "disabled"];
|
|
1404
|
-
const _hoisted_19$1 = {
|
|
1405
|
-
key: 0,
|
|
1406
|
-
class: "loading loading-spinner loading-xs"
|
|
1407
|
-
};
|
|
1408
|
-
const _hoisted_20$1 = {
|
|
1409
|
-
key: 1,
|
|
1410
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
1411
|
-
class: "h-4 w-4",
|
|
1412
|
-
fill: "none",
|
|
1413
|
-
viewBox: "0 0 24 24",
|
|
1414
|
-
stroke: "currentColor"
|
|
1415
|
-
};
|
|
1416
|
-
const _sfc_main$1 = /* @__PURE__ */ defineComponent({
|
|
1417
|
-
__name: "SupportTicketSubscribersCollapsible",
|
|
1418
|
-
props: {
|
|
1419
|
-
supportTicketId: {},
|
|
1420
|
-
locked: { type: Boolean }
|
|
1421
|
-
},
|
|
1422
|
-
setup(__props) {
|
|
1423
|
-
const props = __props;
|
|
1424
|
-
const expanded = ref(false);
|
|
1425
|
-
const selectedUserId = ref("");
|
|
1426
|
-
const { data: subscribersData, loading: subscribersLoading, refetch: refetchSubscribers } = useQuery((api) => api.supportTickets.staffListSubscribers(props.supportTicketId), {
|
|
1427
|
-
enabled: computed(() => !!props.supportTicketId),
|
|
1428
|
-
cacheKey: `support-ticket-subscribers-${props.supportTicketId}`
|
|
1429
|
-
});
|
|
1430
|
-
const subscribers = computed(() => {
|
|
1431
|
-
return subscribersData.value ?? [];
|
|
1432
|
-
});
|
|
1433
|
-
const { data: usersData } = useQuery((api) => api.users.getUsersForSelection(), { staleTime: 600 * 1e3 });
|
|
1434
|
-
const availableUsers = computed(() => {
|
|
1435
|
-
const users = usersData.value ?? [];
|
|
1436
|
-
const existingIds = new Set(subscribers.value.map((s) => s.user_id));
|
|
1437
|
-
return users.filter((u) => !existingIds.has(u.id));
|
|
1438
|
-
});
|
|
1439
|
-
const { mutate: addSubscriber, loading: isAddLoading } = useMutation((api, input) => api.supportTickets.staffAddSubscriber(input), { invalidate: /^support-ticket-subscribers/ });
|
|
1440
|
-
const { mutate: removeSubscriber } = useMutation((api, input) => api.supportTickets.staffRemoveSubscriber(input), { invalidate: /^support-ticket-subscribers/ });
|
|
1441
|
-
const isRemoveLoading = ref(null);
|
|
1442
|
-
async function handleAddSubscriber() {
|
|
1443
|
-
if (!selectedUserId.value) return;
|
|
1444
|
-
try {
|
|
1445
|
-
await addSubscriber({
|
|
1446
|
-
support_ticket_id: props.supportTicketId,
|
|
1447
|
-
user_id: selectedUserId.value
|
|
1448
|
-
});
|
|
1449
|
-
toast.success("Subscriber added");
|
|
1450
|
-
selectedUserId.value = "";
|
|
1451
|
-
await refetchSubscribers();
|
|
1452
|
-
} catch (e) {
|
|
1453
|
-
toast.error(extractRpcErrorMessage(e, "Failed to add subscriber"));
|
|
1454
|
-
}
|
|
1455
|
-
}
|
|
1456
|
-
async function handleRemoveSubscriber(subscriberId) {
|
|
1457
|
-
try {
|
|
1458
|
-
isRemoveLoading.value = subscriberId;
|
|
1459
|
-
await removeSubscriber({
|
|
1460
|
-
supportTicketId: props.supportTicketId,
|
|
1461
|
-
subscriberId
|
|
1462
|
-
});
|
|
1463
|
-
toast.success("Subscriber removed");
|
|
1464
|
-
await refetchSubscribers();
|
|
1465
|
-
} catch (e) {
|
|
1466
|
-
toast.error(extractRpcErrorMessage(e, "Failed to remove subscriber"));
|
|
1467
|
-
} finally {
|
|
1468
|
-
isRemoveLoading.value = null;
|
|
1469
|
-
}
|
|
1470
|
-
}
|
|
1471
|
-
return (_ctx, _cache) => {
|
|
1472
|
-
return __props.supportTicketId ? (openBlock(), createElementBlock("div", _hoisted_1$1, [createElementVNode("div", _hoisted_2$1, [
|
|
1473
|
-
withDirectives(createElementVNode("input", {
|
|
1474
|
-
type: "checkbox",
|
|
1475
|
-
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => expanded.value = $event)
|
|
1476
|
-
}, null, 512), [[vModelCheckbox, expanded.value]]),
|
|
1477
|
-
createElementVNode("div", _hoisted_3$1, [createElementVNode("div", _hoisted_4$1, [_cache[2] || (_cache[2] = createElementVNode("svg", {
|
|
1478
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
1479
|
-
class: "h-4 w-4",
|
|
1480
|
-
fill: "none",
|
|
1481
|
-
viewBox: "0 0 24 24",
|
|
1482
|
-
stroke: "currentColor"
|
|
1483
|
-
}, [createElementVNode("path", {
|
|
1484
|
-
"stroke-linecap": "round",
|
|
1485
|
-
"stroke-linejoin": "round",
|
|
1486
|
-
"stroke-width": "2",
|
|
1487
|
-
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"
|
|
1488
|
-
})], -1)), createElementVNode("span", null, "Subscribers (" + toDisplayString(subscribers.value.length) + ")", 1)])]),
|
|
1489
|
-
createElementVNode("div", _hoisted_5$1, [createElementVNode("div", _hoisted_6$1, [
|
|
1490
|
-
createCommentVNode(" Add subscriber "),
|
|
1491
|
-
!__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", {
|
|
1492
|
-
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => selectedUserId.value = $event),
|
|
1493
|
-
class: "select select-bordered select-sm w-full",
|
|
1494
|
-
disabled: unref(isAddLoading)
|
|
1495
|
-
}, [_cache[3] || (_cache[3] = createElementVNode("option", { value: "" }, "Select user...", -1)), (openBlock(true), createElementBlock(Fragment, null, renderList(availableUsers.value, (user) => {
|
|
1496
|
-
return openBlock(), createElementBlock("option", {
|
|
1497
|
-
key: user.id,
|
|
1498
|
-
value: user.id
|
|
1499
|
-
}, toDisplayString(user.email), 9, _hoisted_10$1);
|
|
1500
|
-
}), 128))], 8, _hoisted_9$1), [[vModelSelect, selectedUserId.value]])]), createElementVNode("button", {
|
|
1501
|
-
class: "btn btn-sm btn-primary",
|
|
1502
|
-
disabled: !selectedUserId.value || unref(isAddLoading),
|
|
1503
|
-
onClick: handleAddSubscriber
|
|
1504
|
-
}, [unref(isAddLoading) ? (openBlock(), createElementBlock("span", _hoisted_12$1)) : (openBlock(), createElementBlock("span", _hoisted_13$1, "Add"))], 8, _hoisted_11$1)])) : createCommentVNode("v-if", true),
|
|
1505
|
-
createCommentVNode(" Subscriber list "),
|
|
1506
|
-
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) => {
|
|
1507
|
-
return openBlock(), createElementBlock("li", {
|
|
1508
|
-
key: sub.id,
|
|
1509
|
-
class: "flex items-center justify-between gap-2 py-2 px-3 rounded bg-base-100 border border-base-300"
|
|
1510
|
-
}, [createElementVNode("span", _hoisted_17$1, toDisplayString(sub.user_id_display_name ?? sub.user_id), 1), !__props.locked ? (openBlock(), createElementBlock("button", {
|
|
1511
|
-
key: 0,
|
|
1512
|
-
type: "button",
|
|
1513
|
-
class: "btn btn-ghost btn-xs text-error",
|
|
1514
|
-
"aria-label": "Remove subscriber",
|
|
1515
|
-
onClick: ($event) => handleRemoveSubscriber(sub.id),
|
|
1516
|
-
disabled: isRemoveLoading.value === sub.id
|
|
1517
|
-
}, [isRemoveLoading.value === sub.id ? (openBlock(), createElementBlock("span", _hoisted_19$1)) : (openBlock(), createElementBlock("svg", _hoisted_20$1, [..._cache[6] || (_cache[6] = [createElementVNode("path", {
|
|
1518
|
-
"stroke-linecap": "round",
|
|
1519
|
-
"stroke-linejoin": "round",
|
|
1520
|
-
"stroke-width": "2",
|
|
1521
|
-
d: "M6 18L18 6M6 6l12 12"
|
|
1522
|
-
}, null, -1)])]))], 8, _hoisted_18$1)) : createCommentVNode("v-if", true)]);
|
|
1523
|
-
}), 128))]))
|
|
1524
|
-
])])
|
|
1525
|
-
])])) : createCommentVNode("v-if", true);
|
|
1526
|
-
};
|
|
1527
|
-
}
|
|
1528
|
-
});
|
|
1529
|
-
var SupportTicketSubscribersCollapsible_default = _sfc_main$1;
|
|
1530
|
-
|
|
1531
|
-
//#endregion
|
|
1532
|
-
//#region src/slices/support_ticket/staff/StaffSupportTicketDetailPage.vue
|
|
1533
|
-
const _hoisted_1 = {
|
|
1534
|
-
key: 0,
|
|
1535
|
-
class: "flex justify-center items-center p-8"
|
|
1536
|
-
};
|
|
1537
|
-
const _hoisted_2 = { class: "alert alert-error mb-4 max-w-4xl mx-auto px-4" };
|
|
1538
|
-
const _hoisted_3 = { class: "flex-1" };
|
|
1539
|
-
const _hoisted_4 = { class: "mt-2" };
|
|
1540
|
-
const _hoisted_5 = { class: "px-4 py-6 max-w-4xl mx-auto" };
|
|
1541
|
-
const _hoisted_6 = { class: "mb-6" };
|
|
1542
|
-
const _hoisted_7 = { class: "text-2xl font-bold text-base-content break-words leading-tight mt-2" };
|
|
1543
|
-
const _hoisted_8 = { class: "mb-6" };
|
|
1544
|
-
const _hoisted_9 = { class: "modal-box" };
|
|
1545
|
-
const _hoisted_10 = { class: "form-control py-4" };
|
|
1546
|
-
const _hoisted_11 = ["disabled"];
|
|
1547
|
-
const _hoisted_12 = {
|
|
1548
|
-
key: 0,
|
|
1549
|
-
class: "alert alert-error py-2 mb-4"
|
|
1550
|
-
};
|
|
1551
|
-
const _hoisted_13 = { class: "text-sm" };
|
|
1552
|
-
const _hoisted_14 = { class: "modal-action" };
|
|
1553
|
-
const _hoisted_15 = ["disabled"];
|
|
1554
|
-
const _hoisted_16 = ["disabled"];
|
|
1555
|
-
const _hoisted_17 = {
|
|
1556
|
-
key: 0,
|
|
1557
|
-
class: "loading loading-spinner loading-xs mr-2"
|
|
1558
|
-
};
|
|
1559
|
-
const _hoisted_18 = { class: "mb-8" };
|
|
1560
|
-
const _hoisted_19 = { class: "mb-8" };
|
|
1561
|
-
const _hoisted_20 = { class: "mb-8" };
|
|
1562
|
-
const _hoisted_21 = {
|
|
1563
|
-
key: 1,
|
|
1564
|
-
class: "flex justify-center items-center py-8"
|
|
1565
|
-
};
|
|
1566
|
-
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
1567
|
-
__name: "StaffSupportTicketDetailPage",
|
|
1568
|
-
props: {
|
|
1569
|
-
ticket: {},
|
|
1570
|
-
isLoading: { type: Boolean },
|
|
1571
|
-
error: {}
|
|
1572
|
-
},
|
|
1573
|
-
setup(__props) {
|
|
1574
|
-
const props = __props;
|
|
1575
|
-
const route = useRoute();
|
|
1576
|
-
const router = useRouter();
|
|
1577
|
-
const support_ticket_id = route.params.id;
|
|
1578
|
-
const isApproveModalOpen = ref(false);
|
|
1579
|
-
const isRevertModalOpen = ref(false);
|
|
1580
|
-
const isDeleteModalOpen = ref(false);
|
|
1581
|
-
const isConvertToInternalModalOpen = ref(false);
|
|
1582
|
-
const isCompleteModalOpen = ref(false);
|
|
1583
|
-
const isArchiveModalOpen = ref(false);
|
|
1584
|
-
const rejectModal = ref(null);
|
|
1585
|
-
const rejectReason = ref("");
|
|
1586
|
-
const rejectError = ref(null);
|
|
1587
|
-
const isEditMode = computed(() => route.query.mode === "edit");
|
|
1588
|
-
const refreshTicket = inject("refreshTicket");
|
|
1589
|
-
const { data: timelineData, refetch: refetchTimeline } = useQuery(async (api) => {
|
|
1590
|
-
const [customerNotes, internalNotes, recordVersions] = await Promise.all([
|
|
1591
|
-
api.notes.getNotes({
|
|
1592
|
-
record_id: {
|
|
1593
|
-
operator: OPERATORS.EQUALS,
|
|
1594
|
-
value: support_ticket_id
|
|
1595
|
-
},
|
|
1596
|
-
record_type: {
|
|
1597
|
-
operator: OPERATORS.EQUALS,
|
|
1598
|
-
value: "support_ticket"
|
|
1599
|
-
},
|
|
1600
|
-
is_internal: {
|
|
1601
|
-
operator: OPERATORS.EQUALS,
|
|
1602
|
-
value: false
|
|
1603
|
-
},
|
|
1604
|
-
first: 100,
|
|
1605
|
-
sortBy: "created_at",
|
|
1606
|
-
sortDirection: "asc"
|
|
1607
|
-
}),
|
|
1608
|
-
api.notes.getNotes({
|
|
1609
|
-
record_id: {
|
|
1610
|
-
operator: OPERATORS.EQUALS,
|
|
1611
|
-
value: support_ticket_id
|
|
1612
|
-
},
|
|
1613
|
-
record_type: {
|
|
1614
|
-
operator: OPERATORS.EQUALS,
|
|
1615
|
-
value: "support_ticket"
|
|
1616
|
-
},
|
|
1617
|
-
is_internal: {
|
|
1618
|
-
operator: OPERATORS.EQUALS,
|
|
1619
|
-
value: true
|
|
1620
|
-
},
|
|
1621
|
-
first: 100,
|
|
1622
|
-
sortBy: "created_at",
|
|
1623
|
-
sortDirection: "asc"
|
|
1624
|
-
}),
|
|
1625
|
-
api.recordVersions.listRecordVersionsPaginated(support_ticket_id, RecordConst.SUPPORT_TICKET, {
|
|
1626
|
-
record_types: [RecordConst.SUPPORT_TICKET, RecordConst.SUPPORT_TICKET_ACTIVITY],
|
|
1627
|
-
first: 100,
|
|
1628
|
-
sortBy: "recorded_at",
|
|
1629
|
-
sortDirection: "asc"
|
|
1630
|
-
})
|
|
1631
|
-
]);
|
|
1632
|
-
return {
|
|
1633
|
-
customerNotes,
|
|
1634
|
-
internalNotes,
|
|
1635
|
-
recordVersions
|
|
1636
|
-
};
|
|
1637
|
-
}, {
|
|
1638
|
-
enabled: !!support_ticket_id && !!props.ticket,
|
|
1639
|
-
batchMode: BATCH_MODE.batch,
|
|
1640
|
-
trackedSegment: "support-ticket-timeline"
|
|
1641
|
-
});
|
|
1642
|
-
const customerNotesForTimeline = computed(() => {
|
|
1643
|
-
return (timelineData.value?.customerNotes?.items ?? []).map((n) => ({
|
|
1644
|
-
authorName: n.created_by_display_name ?? n.created_by ?? "(unknown)",
|
|
1645
|
-
createdAt: n.created_at,
|
|
1646
|
-
body: n.body ?? ""
|
|
1647
|
-
}));
|
|
1648
|
-
});
|
|
1649
|
-
const internalNotesForTimeline = computed(() => {
|
|
1650
|
-
return (timelineData.value?.internalNotes?.items ?? []).map((n) => ({
|
|
1651
|
-
authorName: n.created_by_display_name ?? n.created_by ?? "(unknown)",
|
|
1652
|
-
createdAt: n.created_at,
|
|
1653
|
-
body: n.body ?? ""
|
|
1654
|
-
}));
|
|
1655
|
-
});
|
|
1656
|
-
const systemEventsForTimeline = computed(() => {
|
|
1657
|
-
const data = timelineData.value?.recordVersions;
|
|
1658
|
-
const versions = data?.items ?? [];
|
|
1659
|
-
const userDisplayMap = data?.user_display_map;
|
|
1660
|
-
return parseRecordVersions(versions, userDisplayMap ? new Map(Object.entries(userDisplayMap)) : void 0);
|
|
1661
|
-
});
|
|
1662
|
-
const { mutate: createNote } = useMutation((api, input) => api.notes.createNote(input), { invalidate: /^notes?:/ });
|
|
1663
|
-
const { mutate: rejectSupportTicket, loading: isRejecting } = useMutation((api, input) => api.supportTickets.rejectTicket(input), { invalidate: /^support-tickets?:/ });
|
|
1664
|
-
async function handleAddNote(payload) {
|
|
1665
|
-
try {
|
|
1666
|
-
await createNote({
|
|
1667
|
-
record_id: support_ticket_id,
|
|
1668
|
-
record_type: "support_ticket",
|
|
1669
|
-
body: payload.content,
|
|
1670
|
-
tag: null,
|
|
1671
|
-
is_internal: payload.noteType === "internal"
|
|
1672
|
-
});
|
|
1673
|
-
await refetchTimeline();
|
|
1674
|
-
toast.success(payload.noteType === "internal" ? "Internal note added" : "Note added");
|
|
1675
|
-
} catch {
|
|
1676
|
-
toast.error("Failed to add note");
|
|
1677
|
-
throw new Error("Failed to add note");
|
|
1678
|
-
}
|
|
1679
|
-
}
|
|
1680
|
-
function showApproveModal() {
|
|
1681
|
-
if (!props.ticket) return;
|
|
1682
|
-
isApproveModalOpen.value = true;
|
|
1683
|
-
}
|
|
1684
|
-
function showRevertModal() {
|
|
1685
|
-
if (!props.ticket) return;
|
|
1686
|
-
isRevertModalOpen.value = true;
|
|
1687
|
-
}
|
|
1688
|
-
function showDeleteModal() {
|
|
1689
|
-
if (!props.ticket) return;
|
|
1690
|
-
isDeleteModalOpen.value = true;
|
|
1691
|
-
}
|
|
1692
|
-
function showConvertToInternalModal() {
|
|
1693
|
-
if (!props.ticket) return;
|
|
1694
|
-
isConvertToInternalModalOpen.value = true;
|
|
1695
|
-
}
|
|
1696
|
-
function showCompleteModal() {
|
|
1697
|
-
if (!props.ticket) return;
|
|
1698
|
-
isCompleteModalOpen.value = true;
|
|
1699
|
-
}
|
|
1700
|
-
function showArchiveModal() {
|
|
1701
|
-
if (!props.ticket) return;
|
|
1702
|
-
isArchiveModalOpen.value = true;
|
|
1703
|
-
}
|
|
1704
|
-
function handleDeleteSuccess() {
|
|
1705
|
-
router.push({ name: "StaffSupportTicketList" });
|
|
1706
|
-
}
|
|
1707
|
-
function showRejectModal() {
|
|
1708
|
-
rejectReason.value = "";
|
|
1709
|
-
rejectError.value = null;
|
|
1710
|
-
rejectModal.value?.showModal();
|
|
1711
|
-
}
|
|
1712
|
-
function closeRejectModal() {
|
|
1713
|
-
rejectModal.value?.close();
|
|
1714
|
-
rejectReason.value = "";
|
|
1715
|
-
rejectError.value = null;
|
|
1716
|
-
}
|
|
1717
|
-
async function handleApproveSuccess() {
|
|
1718
|
-
await handleTicketUpdate();
|
|
1719
|
-
}
|
|
1720
|
-
async function handleRejectWithReason() {
|
|
1721
|
-
if (!props.ticket) return;
|
|
1722
|
-
rejectError.value = null;
|
|
1723
|
-
try {
|
|
1724
|
-
const reasonTrimmed = rejectReason.value.trim();
|
|
1725
|
-
if (reasonTrimmed) await createNote({
|
|
1726
|
-
record_id: props.ticket.id,
|
|
1727
|
-
record_type: "support_ticket",
|
|
1728
|
-
body: reasonTrimmed,
|
|
1729
|
-
tag: null,
|
|
1730
|
-
is_internal: false
|
|
1731
|
-
});
|
|
1732
|
-
await rejectSupportTicket({ id: props.ticket.id });
|
|
1733
|
-
toast.info("Support Ticket rejected.");
|
|
1734
|
-
closeRejectModal();
|
|
1735
|
-
await handleTicketUpdate();
|
|
1736
|
-
} catch (e) {
|
|
1737
|
-
const errorMessage = extractRpcErrorMessage(e, "Failed to reject ticket");
|
|
1738
|
-
rejectError.value = errorMessage;
|
|
1739
|
-
toast.error(errorMessage);
|
|
1740
|
-
}
|
|
1741
|
-
}
|
|
1742
|
-
async function handleTicketUpdate() {
|
|
1743
|
-
if (refreshTicket) await refreshTicket();
|
|
1744
|
-
await refetchTimeline();
|
|
1745
|
-
}
|
|
1746
|
-
function enterEditMode() {
|
|
1747
|
-
router.push({
|
|
1748
|
-
name: route.name || "StaffViewSupportTicket",
|
|
1749
|
-
params: route.params,
|
|
1750
|
-
query: {
|
|
1751
|
-
...route.query,
|
|
1752
|
-
mode: "edit"
|
|
1753
|
-
}
|
|
1754
|
-
});
|
|
1755
|
-
}
|
|
1756
|
-
async function exitEditMode() {
|
|
1757
|
-
await router.push({
|
|
1758
|
-
name: route.name || "StaffViewSupportTicket",
|
|
1759
|
-
params: route.params,
|
|
1760
|
-
query: {
|
|
1761
|
-
...route.query,
|
|
1762
|
-
mode: void 0
|
|
1763
|
-
}
|
|
1764
|
-
});
|
|
1765
|
-
}
|
|
1766
|
-
async function handleMetadataSaveSuccess() {
|
|
1767
|
-
await exitEditMode();
|
|
1768
|
-
await nextTick();
|
|
1769
|
-
toast.success("Ticket updated successfully");
|
|
1770
|
-
if (refreshTicket) await refreshTicket();
|
|
1771
|
-
await refetchTimeline();
|
|
1772
|
-
}
|
|
1773
|
-
return (_ctx, _cache) => {
|
|
1774
|
-
const _component_router_link = resolveComponent("router-link");
|
|
1775
|
-
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", {
|
|
1776
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
1777
|
-
class: "stroke-current shrink-0 h-6 w-6",
|
|
1778
|
-
fill: "none",
|
|
1779
|
-
viewBox: "0 0 24 24"
|
|
1780
|
-
}, [createElementVNode("path", {
|
|
1781
|
-
"stroke-linecap": "round",
|
|
1782
|
-
"stroke-linejoin": "round",
|
|
1783
|
-
"stroke-width": "2",
|
|
1784
|
-
d: "M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
1785
|
-
})], -1)), createElementVNode("div", _hoisted_3, [createElementVNode("span", null, toDisplayString(__props.error.message || "An error occurred"), 1), createElementVNode("div", _hoisted_4, [createVNode(_component_router_link, {
|
|
1786
|
-
to: { name: "StaffSupportTicketList" },
|
|
1787
|
-
class: "link link-primary"
|
|
1788
|
-
}, {
|
|
1789
|
-
default: withCtx(() => [..._cache[8] || (_cache[8] = [createTextVNode(" Go back to ticket list ", -1)])]),
|
|
1790
|
-
_: 1
|
|
1791
|
-
})])])])], 2112)) : __props.ticket ? (openBlock(), createElementBlock(Fragment, { key: 2 }, [createCommentVNode(" Main Content "), createElementVNode("div", _hoisted_5, [
|
|
1792
|
-
createCommentVNode(" Header "),
|
|
1793
|
-
createElementVNode("div", _hoisted_6, [createElementVNode("h1", _hoisted_7, toDisplayString(__props.ticket.title), 1)]),
|
|
1794
|
-
createCommentVNode(" Action Banner (buttons only; sections in dialogs) "),
|
|
1795
|
-
createElementVNode("div", _hoisted_8, [createVNode(StaffActionBanner_default, {
|
|
1796
|
-
ticket: __props.ticket,
|
|
1797
|
-
onApprove: showApproveModal,
|
|
1798
|
-
onReject: showRejectModal,
|
|
1799
|
-
onRevert: showRevertModal,
|
|
1800
|
-
onConvertToInternal: showConvertToInternalModal,
|
|
1801
|
-
onDelete: showDeleteModal,
|
|
1802
|
-
onComplete: showCompleteModal,
|
|
1803
|
-
onArchive: showArchiveModal
|
|
1804
|
-
}, null, 8, ["ticket"])]),
|
|
1805
|
-
createCommentVNode(" Approve Modal "),
|
|
1806
|
-
createVNode(ApproveSupportTicketModal_default, {
|
|
1807
|
-
ticket: __props.ticket,
|
|
1808
|
-
"is-open": isApproveModalOpen.value,
|
|
1809
|
-
onClose: _cache[0] || (_cache[0] = ($event) => isApproveModalOpen.value = false),
|
|
1810
|
-
onSuccess: handleApproveSuccess
|
|
1811
|
-
}, null, 8, ["ticket", "is-open"]),
|
|
1812
|
-
createCommentVNode(" Revert Modal "),
|
|
1813
|
-
createVNode(RevertSupportTicketModal_default, {
|
|
1814
|
-
ticket: __props.ticket,
|
|
1815
|
-
"is-open": isRevertModalOpen.value,
|
|
1816
|
-
onClose: _cache[1] || (_cache[1] = ($event) => isRevertModalOpen.value = false),
|
|
1817
|
-
onSuccess: handleTicketUpdate
|
|
1818
|
-
}, null, 8, ["ticket", "is-open"]),
|
|
1819
|
-
createCommentVNode(" Delete Modal "),
|
|
1820
|
-
createVNode(DeleteSupportTicketModal_default, {
|
|
1821
|
-
ticket: __props.ticket,
|
|
1822
|
-
"is-open": isDeleteModalOpen.value,
|
|
1823
|
-
onClose: _cache[2] || (_cache[2] = ($event) => isDeleteModalOpen.value = false),
|
|
1824
|
-
onSuccess: handleDeleteSuccess
|
|
1825
|
-
}, null, 8, ["ticket", "is-open"]),
|
|
1826
|
-
createCommentVNode(" Convert to Internal Modal "),
|
|
1827
|
-
createVNode(ConvertToInternalModal_default, {
|
|
1828
|
-
ticket: __props.ticket,
|
|
1829
|
-
"is-open": isConvertToInternalModalOpen.value,
|
|
1830
|
-
onClose: _cache[3] || (_cache[3] = ($event) => isConvertToInternalModalOpen.value = false),
|
|
1831
|
-
onSuccess: handleTicketUpdate
|
|
1832
|
-
}, null, 8, ["ticket", "is-open"]),
|
|
1833
|
-
createCommentVNode(" Complete Modal "),
|
|
1834
|
-
createVNode(CompleteSupportTicketModal_default, {
|
|
1835
|
-
ticket: __props.ticket,
|
|
1836
|
-
"is-open": isCompleteModalOpen.value,
|
|
1837
|
-
onClose: _cache[4] || (_cache[4] = ($event) => isCompleteModalOpen.value = false),
|
|
1838
|
-
onSuccess: handleTicketUpdate
|
|
1839
|
-
}, null, 8, ["ticket", "is-open"]),
|
|
1840
|
-
createCommentVNode(" Archive Modal "),
|
|
1841
|
-
createVNode(ArchiveSupportTicketModal_default, {
|
|
1842
|
-
ticket: __props.ticket,
|
|
1843
|
-
"is-open": isArchiveModalOpen.value,
|
|
1844
|
-
onClose: _cache[5] || (_cache[5] = ($event) => isArchiveModalOpen.value = false),
|
|
1845
|
-
onSuccess: handleTicketUpdate
|
|
1846
|
-
}, null, 8, ["ticket", "is-open"]),
|
|
1847
|
-
createCommentVNode(" Reject Modal with Reason "),
|
|
1848
|
-
createElementVNode("dialog", {
|
|
1849
|
-
ref_key: "rejectModal",
|
|
1850
|
-
ref: rejectModal,
|
|
1851
|
-
class: "modal"
|
|
1852
|
-
}, [createElementVNode("div", _hoisted_9, [
|
|
1853
|
-
_cache[11] || (_cache[11] = createElementVNode("h3", { class: "font-bold text-lg" }, "Reject Ticket", -1)),
|
|
1854
|
-
_cache[12] || (_cache[12] = createElementVNode("p", { class: "text-sm text-base-content/70 py-2" }, " This will lock the ticket and notify the requester. ", -1)),
|
|
1855
|
-
createElementVNode("div", _hoisted_10, [_cache[10] || (_cache[10] = createElementVNode("label", { class: "label" }, [createElementVNode("span", { class: "label-text text-sm font-medium" }, "Reason (optional)")], -1)), withDirectives(createElementVNode("textarea", {
|
|
1856
|
-
"onUpdate:modelValue": _cache[6] || (_cache[6] = ($event) => rejectReason.value = $event),
|
|
1857
|
-
class: "textarea textarea-bordered w-full",
|
|
1858
|
-
rows: "3",
|
|
1859
|
-
placeholder: "Why is this ticket being rejected?",
|
|
1860
|
-
disabled: unref(isRejecting)
|
|
1861
|
-
}, null, 8, _hoisted_11), [[vModelText, rejectReason.value]])]),
|
|
1862
|
-
rejectError.value ? (openBlock(), createElementBlock("div", _hoisted_12, [createElementVNode("span", _hoisted_13, toDisplayString(rejectError.value), 1)])) : createCommentVNode("v-if", true),
|
|
1863
|
-
createElementVNode("div", _hoisted_14, [createElementVNode("button", {
|
|
1864
|
-
class: "btn btn-ghost",
|
|
1865
|
-
onClick: closeRejectModal,
|
|
1866
|
-
disabled: unref(isRejecting)
|
|
1867
|
-
}, " Cancel ", 8, _hoisted_15), createElementVNode("button", {
|
|
1868
|
-
class: "btn btn-error",
|
|
1869
|
-
onClick: handleRejectWithReason,
|
|
1870
|
-
disabled: unref(isRejecting)
|
|
1871
|
-
}, [unref(isRejecting) ? (openBlock(), createElementBlock("span", _hoisted_17)) : createCommentVNode("v-if", true), createTextVNode(" " + toDisplayString(unref(isRejecting) ? "Rejecting..." : "Reject Ticket"), 1)], 8, _hoisted_16)])
|
|
1872
|
-
]), createElementVNode("form", {
|
|
1873
|
-
method: "dialog",
|
|
1874
|
-
class: "modal-backdrop"
|
|
1875
|
-
}, [createElementVNode("button", { onClick: closeRejectModal }, "close")])], 512),
|
|
1876
|
-
createCommentVNode(" Metadata Card (Display or Edit) "),
|
|
1877
|
-
createElementVNode("div", _hoisted_18, [!isEditMode.value ? (openBlock(), createBlock(StaffMetadataCard_default, {
|
|
1878
|
-
key: 0,
|
|
1879
|
-
ticket: __props.ticket,
|
|
1880
|
-
onEdit: enterEditMode
|
|
1881
|
-
}, null, 8, ["ticket"])) : (openBlock(), createBlock(StaffMetadataCardEdit_default, {
|
|
1882
|
-
key: 1,
|
|
1883
|
-
ticket: __props.ticket,
|
|
1884
|
-
onSuccess: handleMetadataSaveSuccess,
|
|
1885
|
-
onCancel: exitEditMode
|
|
1886
|
-
}, null, 8, ["ticket"]))]),
|
|
1887
|
-
createCommentVNode(" Attachments (separate from timeline so adding a comment doesn't cause refetch) "),
|
|
1888
|
-
createElementVNode("div", _hoisted_19, [createVNode(SupportTicketAttachmentsCollapsible_default, {
|
|
1889
|
-
"record-id": __props.ticket.id,
|
|
1890
|
-
locked: !!__props.ticket.archived_at,
|
|
1891
|
-
editable: !__props.ticket.archived_at,
|
|
1892
|
-
onUploaded: unref(refetchTimeline),
|
|
1893
|
-
onDeleted: unref(refetchTimeline)
|
|
1894
|
-
}, null, 8, [
|
|
1895
|
-
"record-id",
|
|
1896
|
-
"locked",
|
|
1897
|
-
"editable",
|
|
1898
|
-
"onUploaded",
|
|
1899
|
-
"onDeleted"
|
|
1900
|
-
])]),
|
|
1901
|
-
createCommentVNode(" Subscribers "),
|
|
1902
|
-
createElementVNode("div", _hoisted_20, [createVNode(SupportTicketSubscribersCollapsible_default, {
|
|
1903
|
-
"support-ticket-id": __props.ticket.id,
|
|
1904
|
-
locked: !!__props.ticket.archived_at
|
|
1905
|
-
}, null, 8, ["support-ticket-id", "locked"])]),
|
|
1906
|
-
createCommentVNode(" Timeline: stay mounted once we have data so adding a comment doesn't unmount/remount "),
|
|
1907
|
-
createElementVNode("div", null, [unref(timelineData) != null ? (openBlock(), createBlock(StaffTimeline_default, {
|
|
1908
|
-
key: 0,
|
|
1909
|
-
ticket: __props.ticket,
|
|
1910
|
-
"customer-notes": customerNotesForTimeline.value,
|
|
1911
|
-
"internal-notes": internalNotesForTimeline.value,
|
|
1912
|
-
"system-events": systemEventsForTimeline.value,
|
|
1913
|
-
"on-add-note": handleAddNote
|
|
1914
|
-
}, null, 8, [
|
|
1915
|
-
"ticket",
|
|
1916
|
-
"customer-notes",
|
|
1917
|
-
"internal-notes",
|
|
1918
|
-
"system-events"
|
|
1919
|
-
])) : (openBlock(), createElementBlock("div", _hoisted_21, [..._cache[13] || (_cache[13] = [createElementVNode("span", { class: "loading loading-spinner loading-lg" }, null, -1)])]))])
|
|
1920
|
-
])], 2112)) : createCommentVNode("v-if", true)], 2112);
|
|
1921
|
-
};
|
|
1922
|
-
}
|
|
1923
|
-
});
|
|
1924
|
-
var StaffSupportTicketDetailPage_default = _sfc_main;
|
|
1925
|
-
|
|
1926
|
-
//#endregion
|
|
1927
|
-
export { StaffSupportTicketDetailPage_default as default };
|
|
1928
|
-
//# sourceMappingURL=StaffSupportTicketDetailPage-DY07Ez0R.js.map
|