129 lines
4.1 KiB
TypeScript
129 lines
4.1 KiB
TypeScript
import type { TicketAnalytics } from "@/components/ticket-agent/analytics/types";
|
|
import type { CommentExtras } from "@/components/ticket-agent/timeline/TimelineCommentRow.vue";
|
|
import { __ } from "@/translation";
|
|
import type {
|
|
DocumentResource,
|
|
RecentSimilarTicket,
|
|
Resource,
|
|
TicketAssignee,
|
|
TicketContact,
|
|
} from "@/types";
|
|
import type { HDTicket } from "@/types/doctypes";
|
|
import { createDocumentResource, createResource, toast } from "frappe-ui";
|
|
import { reactive } from "vue";
|
|
|
|
interface MapValue {
|
|
ticket: DocumentResource<HDTicket>;
|
|
assignees: Resource<TicketAssignee[]>;
|
|
contact: Resource<TicketContact>;
|
|
recentSimilarTickets: Resource<RecentSimilarTicket>;
|
|
analytics: Resource<TicketAnalytics>;
|
|
// shared by every timeline tab instance, fetched once per ticket
|
|
calls: Resource<Record<string, any>[]>;
|
|
commentExtras: Resource<TicketCommentExtras>;
|
|
// lent by the mounted timeline; see registerTicketFeed
|
|
reloadFeed?: () => void;
|
|
}
|
|
|
|
export interface TicketCommentExtras {
|
|
comments: Record<string, CommentExtras>;
|
|
// oldest first, so a pin's position is its "N:" in the pinned bar
|
|
pinned_comments: string[];
|
|
}
|
|
|
|
export interface PinnedComment {
|
|
name: string;
|
|
content: string;
|
|
}
|
|
|
|
const ticketMap: Record<string, MapValue> = reactive({});
|
|
|
|
export const useTicket = (ticketId: string): MapValue => {
|
|
if (!ticketMap[ticketId]) {
|
|
ticketMap[ticketId] = {
|
|
ticket: createDocumentResource<HDTicket>({
|
|
doctype: "HD Ticket",
|
|
name: ticketId,
|
|
whitelistedMethods: {
|
|
markSeen: "mark_seen",
|
|
},
|
|
setValue: {
|
|
onSuccess: () => {
|
|
toast.success(__("Ticket updated successfully."));
|
|
},
|
|
onError: (error) => {
|
|
const msg = error.exc_type
|
|
? (error.messages || error.message || []).join(", ")
|
|
: error.message;
|
|
toast.error(msg);
|
|
},
|
|
},
|
|
}),
|
|
assignees: createResource({
|
|
url: "helpdesk.helpdesk.doctype.hd_ticket.api.get_ticket_assignees",
|
|
params: { ticket: ticketId },
|
|
auto: true,
|
|
}),
|
|
contact: createResource({
|
|
url: "helpdesk.helpdesk.doctype.hd_ticket.api.get_ticket_contact",
|
|
params: { ticket: ticketId },
|
|
auto: true,
|
|
}),
|
|
recentSimilarTickets: createResource({
|
|
url: "helpdesk.helpdesk.doctype.hd_ticket.api.get_recent_similar_tickets",
|
|
params: { ticket: ticketId },
|
|
auto: true,
|
|
}),
|
|
// fetched by the analytics tab, not on ticket open
|
|
analytics: createResource({
|
|
url: "helpdesk.api.ticket_analytics.get_ticket_analytics",
|
|
params: { ticket: ticketId },
|
|
cache: ["Ticket", ticketId, "analytics"],
|
|
}),
|
|
calls: createResource({
|
|
url: "helpdesk.api.timeline.get_ticket_calls",
|
|
params: { ticket: ticketId },
|
|
auto: true,
|
|
}),
|
|
commentExtras: createResource({
|
|
url: "helpdesk.api.timeline.get_comment_extras",
|
|
params: { ticket: ticketId },
|
|
auto: true,
|
|
}),
|
|
};
|
|
}
|
|
|
|
return ticketMap[ticketId];
|
|
};
|
|
|
|
export function reloadTicket(ticketId: string) {
|
|
const ticketData = ticketMap[ticketId];
|
|
if (!ticketData) return;
|
|
ticketData.ticket.reload();
|
|
ticketData.assignees.reload();
|
|
}
|
|
|
|
// The timeline owns its feed through useActivityTimeline, so anything outside it
|
|
// (a saved reply applying actions, say) reloads through the mounted component.
|
|
export function registerTicketFeed(ticketId: string, reload: () => void) {
|
|
const ticketData = ticketMap[ticketId];
|
|
if (!ticketData) return () => {};
|
|
ticketData.reloadFeed = reload;
|
|
// identity check: on ticket switch the new instance mounts before the old unmounts
|
|
return () => {
|
|
if (ticketData.reloadFeed === reload) delete ticketData.reloadFeed;
|
|
};
|
|
}
|
|
|
|
export function reloadTicketFeed(ticketId: string) {
|
|
ticketMap[ticketId]?.reloadFeed?.();
|
|
}
|
|
|
|
// Refresh a ticket that may have gone stale
|
|
export function revalidateTicket(ticketId: string | number) {
|
|
const ticketData = ticketMap[ticketId];
|
|
if (ticketData?.ticket.get.fetched && !ticketData.ticket.get.loading) {
|
|
reloadTicket(ticketId as string);
|
|
}
|
|
}
|