1import { Dayjs } from "dayjs";
2import { Component, ComputedRef, InjectionKey, Ref } from "vue";
3import type { AssignmentRule, ODCustomer, ODTicket } from "./types/doctypes";
4
5interface ResourceOptions<T = any> {
6 method?: string;
7 url: string;
8 initialData?: T;
9 auto?: boolean;
10 cache?: string | string[];
11 debounce?: number;
12 params?: any;
13 makeParams?: (params: any) => any;
14 onFetch?: (params: any) => void;
15 beforeSubmit?: (params: any) => void;
16 validate?: (params: any) => string | void;
17 onError?: (error: Error) => void;
18 onSuccess?: (data: T) => void;
19 onData?: (data: T) => void;
20 transform?: (data: any) => T;
21 resourceFetcher?: (options: any) => Promise<any>;
22}
23
24export interface Resource<T = any> {
25 method: string | undefined;
26 url: string;
27 data: T | null;
28 previousData: T | null;
29 loading: boolean;
30 fetched: boolean;
31 error: Error | null;
32 promise: Promise<T> | null;
33 auto: boolean;
34 params: any;
35 fetch: (
36 params?: any,
37 tempOptions?: Partial<ResourceOptions<T>>
38 ) => Promise<T>;
39 reload: (
40 params?: any,
41 tempOptions?: Partial<ResourceOptions<T>>
42 ) => Promise<T>;
43 submit: (
44 params?: any,
45 tempOptions?: Partial<ResourceOptions<T>>
46 ) => Promise<T>;
47 reset: () => void;
48 update: (options: {
49 method?: string;
50 url?: string;
51 params?: any;
52 auto?: boolean;
53 }) => void;
54 setData: (data: T | ((currentData: T) => T)) => void;
55}
56
57export interface ListResourceOptions {
58 doctype: string;
59 fields?: string[];
60 filters?: Record<string, any>;
61 orFilters?: Record<string, any>;
62 orderBy?: string;
63 start?: number;
64 pageLength?: number;
65 groupBy?: string;
66 parent?: string;
67 debug?: number;
68 auto?: boolean;
69 url?: string;
70 cache?: any;
71 realtime?: boolean;
72 onSuccess?: (data: any) => void;
73 onError?: (error: any) => void;
74 onData?: (data: any) => void;
75 transform?: (data: any) => any;
76 fetchOne?: {
77 onSuccess?: (data: any) => void;
78 onError?: (error: any) => void;
79 };
80 insert?: {
81 onSuccess?: (data: any) => void;
82 onError?: (error: any) => void;
83 };
84 setValue?: {
85 onSuccess?: (data: any) => void;
86 onError?: (error: any) => void;
87 };
88 delete?: {
89 onSuccess?: (data: any) => void;
90 onError?: (error: any) => void;
91 };
92 runDocMethod?: {
93 onSuccess?: (data: any) => void;
94 onError?: (error: any) => void;
95 };
96}
97
98export interface ListResource<T = any> {
99 doctype: string;
100 fields?: string[];
101 filters?: Record<string, any>;
102 orFilters?: Record<string, any>;
103 orderBy?: string;
104 start: number;
105 pageLength: number;
106 groupBy?: string;
107 parent?: string;
108 debug: number;
109 originalData: T[] | null;
110 dataMap: Record<string, T>;
111 data: T[] | null;
112 hasPreviousPage: boolean;
113 hasNextPage: boolean;
114 auto?: boolean;
115 list: Resource<T[]>;
116 fetchOne: Resource<T>;
117 insert: Resource<T>;
118 setValue: Resource<T>;
119 delete: Resource<T>;
120 runDocMethod: Resource<T>;
121 loading: boolean;
122 update: (updatedOptions: Partial<ListResourceOptions>) => void;
123 fetch: () => void;
124 reload: () => Promise<T[]>;
125 setData: (data: T[] | ((data: T[]) => T[])) => void;
126 transform: (data: T[]) => T[];
127 getRow: (name: string) => T;
128 previous: () => void;
129 next: () => void;
130}
131
132export interface Error {
133 exc_type: string;
134 exc: string;
135 response: string;
136 status: string;
137 messages: string;
138 stack: string;
139 message: string;
140}
141
142export interface Comment {
143 commented_by: string;
144 content: string;
145 is_pinned: boolean;
146 name: string;
147 creation: string;
148}
149
150export interface Communication {
151 creation: string;
152 content: string;
153 name: string;
154 sender: string;
155 bcc?: string;
156 cc?: string;
157 sent_or_received?: "Sent" | "Received";
158 email_account?: string;
159 sender_full_name?: string;
160 sender_mail_id?: string;
161}
162
163export interface Activity {
164 action: string;
165 name: string;
166 owner: string;
167 creation: string;
168}
169
170export interface Contact {
171 full_name: string;
172 name: string;
173 company_name?: string;
174 email_id?: string;
175 image?: string;
176 mobile_no?: string;
177 phone?: string;
178}
179
180export interface CustomField {
181 fieldname: string;
182 label: string;
183 value: string;
184 route: string;
185}
186
187export interface ViewLog {
188 name: string;
189 viewed_by: string;
190 creation: string;
191}
192
193export interface Ticket {
194 _assign: string;
195 assignee: UserInfo;
196 agent_group: string;
197 customer: string;
198 modified: string;
199 name: string;
200 priority: string;
201 raised_by: string;
202 resolution_by: string;
203 response_by: string;
204 first_responded_on: string;
205 resolution_date: string;
206 resolution_time: number;
207 status: string;
208 subject: string;
209 ticket_type: string;
210 via_customer_portal: string;
211 agreement_status: string;
212 creation: string;
213 feedback_rating?: number;
214 feedback_extra?: string;
215 contact: Contact;
216 comments: Comment[];
217 communications: Communication[];
218 history: Activity[];
219 template: Template;
220 views: ViewLog[];
221 _customActions: Function[];
222 is_merged?: boolean;
223 status_category: "Open" | "Paused" | "Resolved";
224}
225
226export interface DocField {
227 fieldname: string;
228 fieldtype: string;
229 label: string;
230 name: string;
231 options: string;
232}
233
234export interface Filter {
235 field?: DocField;
236 fieldname: string;
237 operator: string;
238 label: string;
239 value: boolean | number | string;
240}
241
242export interface AutoCompleteItem {
243 label: string;
244 value: string;
245}
246
247export interface Field {
248 fieldname: string;
249 fieldtype: string;
250 label: string;
251 options: string;
252 required: 0 | 1;
253 description?: null;
254 url_method?: string;
255 link_filters?: string;
256 filters?: string;
257 display_via_depends_on?: string;
258 mandatory_via_depends_on?: string;
259 disabled?: boolean;
260 placeholder?: string | null;
261 readonly?: boolean;
262}
263
264export type FieldValue = string | number | boolean | null | undefined | Dayjs;
265
266export interface Template {
267 about: string;
268 fields: Field[];
269}
270
271export type Column = {
272 key: string;
273 label: string;
274 icon?: string;
275 align?: string;
276 width?: string;
277 text?: string;
278};
279
280export type File = {
281 file_name: string;
282 name: string;
283 file_url: string;
284 is_private: boolean;
285 attached_to_doctype?: string;
286 attached_to_field?: string;
287 attached_to_name?: string;
288};
289
290export type UserInfo = {
291 email: string;
292 image: string;
293 name: string;
294};
295
296export interface RenderField {
297 label: string;
298 name: string;
299 type: string;
300 placeholder?: string;
301 description?: string;
302}
303
304export interface EmailService {
305 name: string;
306 icon: string;
307 info: string;
308 link: string;
309 custom: boolean;
310}
311
312export type EmailStep = "email-list" | "email-add" | "email-edit";
313
314export interface EmailAccount {
315 email_account_name: string;
316 email_id: string;
317 service: string;
318 api_key?: string;
319 api_secret?: string;
320 password?: string;
321 frappe_mail_site?: string;
322 enable_outgoing?: boolean;
323 enable_incoming?: boolean;
324 default_outgoing?: boolean;
325 default_incoming?: boolean;
326}
327
328export type TicketTab =
329 | "activity"
330 | "email"
331 | "comment"
332 | "details"
333 | "call"
334 | "analytics";
335
336export interface TabObject {
337 value: TicketTab;
338 label: string;
339 iconLeft: Component;
340 condition?: () => boolean;
341}
342
343export interface RootCategory {
344 category_id: string;
345 category_name: string;
346}
347
348export interface Article {
349 name: string;
350 title: string;
351 title_slug: string;
352 category_name: string;
353 category: string;
354 published_on: string;
355 author: Author;
356 subtitle: string;
357 article_image: string | null;
358 _user_tags: string | null;
359 status: string;
360 visibility: string;
361 creation: string;
362 content: string;
363 modified: string;
364}
365
366export interface Author {
367 name: string;
368 image: string | null;
369 email: string;
370}
371
372export interface Category {
373 categoryName: string;
374 articles: Article[];
375 authors?: {
376 [key: string]: Author;
377 };
378 children?: Article[];
379}
380
381export interface View {
382 filters: string;
383 order_by: string;
384 columns: string;
385 rows: string;
386 dt?: string;
387 type?: string;
388 route_name?: string;
389 user?: string;
390 icon?: string;
391 label?: string;
392 is_default?: boolean;
393 pinned?: boolean;
394 public?: boolean;
395 group_by_field?: string;
396 list_group_by?: string;
397 name: string;
398 is_customer_portal?: boolean;
399}
400
401export interface ViewType {
402 view_type: string;
403 group_by_field: string;
404 name: string;
405}
406
407export interface Breadcrumb {
408 label: string;
409 route?: {
410 name: string;
411 params?: Record<string, string>;
412 };
413}
414
415
416export interface FieldCriteriaState {
417 selectedParentField: string;
418 selectedChildField: string;
419 childFields: any[];
420 parentFieldValues: any[];
421 childFieldValues: any[];
422 currentParentSelection: string;
423 childSelections: any;
424 initialChildSelections: any;
425 parentSearch: string;
426 childSearch: string;
427 enabled: boolean;
428}
429
430interface ResourceBase {
431 data: any;
432 error: any;
433 fetched: boolean;
434 loading: boolean;
435 params: any;
436 previousData: any;
437 promise: Promise<any> | null;
438 submit: (params?: any) => void;
439}
440
441interface DocumentResourceOptions<T = unknown> {
442 doctype: string;
443 name: string;
444 auto?: boolean;
445 whitelistedMethods?: Record<string, string>;
446 onError?: (error: any) => void;
447 onSuccess?: (data: T) => void;
448 transform?: (doc: T) => T;
449 delete?: {
450 onSuccess?: () => void;
451 onError?: (error: any) => void;
452 };
453 setValue?: {
454 onSuccess?: () => void;
455 onError?: (error: any) => void;
456 };
457}
458
459export interface DocumentResource<T = unknown> {
460 // Configuration
461 auto: boolean;
462 doctype: string;
463 name: string;
464 isDirty: boolean;
465 promise: Promise<void> | null;
466
467 // Main document data
468 doc: T;
469 originalDoc: T;
470
471 // Core methods
472 reload(): void;
473 update(options: Partial<DocumentResourceOptions<T>>): void;
474
475 // Sub-resources
476 get: ResourceBase & {
477 data: T;
478 params: {
479 doctype: string;
480 name: string;
481 };
482 };
483
484 setValue: ResourceBase & {
485 submit: (
486 values: Partial<T>,
487 options?: { onSuccess?: () => void; onError?: (error: any) => void }
488 ) => void;
489 };
490
491 setValueDebounced: ResourceBase & {
492 submit: (values: Partial<T>) => void;
493 };
494
495 save: ResourceBase;
496 delete: ResourceBase;
497
498 // Dynamic whitelisted methods
499 [methodName: string]: any;
500}
501
502export interface Customizations {
503 fields: {
504 fieldname: string;
505 required?: number;
506 placeholder?: string;
507 url_method?: string;
508 }[];
509 _form_script: string[];
510 _customActions?: any;
511 _customOnChange?: any;
512}
513
514export interface TicketContact {
515 name: string;
516 email_id: string;
517 phone: string;
518 mobile_no: string;
519 image: string;
520}
521
522// A ticket assignee enriched with their agent status by get_ticket_assignees.
523// Non-agent assignees only carry `name`.
524export interface TicketAssignee {
525 name: string;
526 agent_name?: string;
527 user_image?: string;
528 availability?: string;
529 availability_changed_on?: string;
530}
531
532// A TicketAssignee plus the option fields AssignTo keeps when an agent is
533// picked from the dropdown (image/label come from AgentOption).
534export interface LocalAssignee extends TicketAssignee {
535 image?: string;
536 label?: string;
537}
538
539export type RecentTicket = Record<
540 "subject" | "status" | "priority" | "name" | "creation",
541 string
542>;
543export type SimilarTicket = Record<
544 "subject" | "status" | "priority" | "name" | "creation",
545 string
546>;
547export interface RecentSimilarTicket {
548 recent_tickets: RecentTicket[];
549 similar_tickets: SimilarTicket[];
550}
551
552export interface ODSettings {
553 brandName: string;
554 brandLogo: string;
555 favicon: string;
556 autoCloseAfterDays: string;
557 autoCloseStatus: string;
558 autoCloseTickets: string;
559 assignWithinTeam: boolean;
560 doNotRestrictTicketsWithoutAnAgentGroup: boolean;
561 restrictTicketsByAgentGroup: boolean;
562 updateStatusTo: string;
563 autoUpdateStatus: boolean;
564 isFeedbackMandatory: boolean;
565 allowAnyoneToCreateTickets: boolean;
566 defaultTicketType: string;
567 skipEmailWorkflow: boolean;
568 disableSavedRepliesGlobalScope: boolean;
569 enableOutsideHoursBanner: boolean;
570 outsideWorkingHoursBannerMessage: string;
571}
572
573export interface HolidayList {
574 name: string;
575 description: string;
576}
577
578export interface SlaPolicy {
579 name: string;
580 description: string;
581 default_sla: boolean;
582 enabled: boolean;
583 rank: number;
584 creation: string;
585}
586
587export interface Team {
588 name: string;
589 team: string;
590 disabled: boolean;
591}
592
593export interface SavedReply {
594 name: string;
595 title: string;
596 message: string;
597 scope: string;
598 teams: Team[];
599 /** JSON-encoded SavedReplyAction[] */
600 actions: string;
601 owner: string;
602}
603
604export type SavedReplyActionType =
605 | "Set Status"
606 | "Set Priority"
607 | "Set Team"
608 | "Set Ticket Type"
609 | "Assign Agent"
610 | "Assign to Me"
611 | "Add Tag"
612 | "Remove Tag"
613 | "Add Comment";
614
615export interface SavedReplyAction {
616 action_type: SavedReplyActionType;
617 value: string;
618 label?: string;
619 /** Title of the saved reply that staged this action */
620 source?: string;
621 /** Value the saved reply shipped, so an edited comment can be restored */
622 original_value?: string;
623}
624
625export interface RenderedSavedReply {
626 title: string;
627 message: string;
628 actions: SavedReplyAction[];
629}
630
631export type APIOptions = DropdownOption[] | string[] | [];
632
633export type DropdownOption = {
634 label: string;
635 value: string | number;
636};
637
638export interface AgentOption {
639 value: string;
640 label: string;
641 image?: string;
642 availability?: string;
643 availability_changed_on?: string;
644}
645
646// symbols
647export const TicketSymbol: InjectionKey<
648 ComputedRef<DocumentResource<ODTicket>>
649> = Symbol("ticket");
650export const AssigneeSymbol: InjectionKey<
651 ComputedRef<Resource<Record<"name", string>[]>>
652> = Symbol("assignees");
653
654export const CustomizationSymbol: InjectionKey<
655 ComputedRef<Resource<Customizations>>
656> = Symbol("customizations");
657
658export const TicketContactSymbol: InjectionKey<
659 ComputedRef<Resource<TicketContact>>
660> = Symbol("ticketContact");
661
662export const RecentSimilarTicketsSymbol: InjectionKey<
663 ComputedRef<Resource<RecentSimilarTicket>>
664> = Symbol("recentSimilarTickets");
665
666export const AssignmentRuleListResourceSymbol: InjectionKey<
667 Resource<AssignmentRule[]>
668> = Symbol("assignmentRuleListResource");
669
670export const ODSettingsSymbol: InjectionKey<Ref<ODSettings>> =
671 Symbol("odSettings");
672
673export const HolidayListResourceSymbol: InjectionKey<
674 ListResource<HolidayList>
675> = Symbol("holidayListResource");
676
677export const SlaPolicyListResourceSymbol: InjectionKey<
678 ListResource<SlaPolicy>
679> = Symbol("slaPolicyListResource");
680
681export const TeamListResourceSymbol: InjectionKey<ListResource<Team>> =
682 Symbol("teamListResource");
683
684export const SavedReplyListResourceSymbol: InjectionKey<
685 ListResource<SavedReply>
686> = Symbol("savedReplyListResource");
687
688export const CustomerResourceSymbol: InjectionKey<
689 DocumentResource<ODCustomer>
690> = Symbol("customerResource");
691
692declare global {
693 interface Window {
694 is_fc_site: boolean;
695 date_format: string;
696 time_format: string;
697 session_user: string;
698 timezone: Record<"user" | "system", string>;
699 agent: string | null;
700 default_country: string;
701 apps: string[];
702 telemetry: { enabled: boolean };
703 customer_home_page: string;
704 }
705}
706
707export interface CustomerContact {
708 contact_name: string;
709 is_primary: 0 | 1;
710 is_manager: 0 | 1;
711 email_id: string | null;
712 mobile_no: string | null;
713 image: string | null;
714 last_active: string;
715 modified?: string;
716 ticket_count: number;
717}
718export interface NewContactState {
719 firstName: string;
720 lastName: string;
721 image: string;
722 email: string;
723 phone: string;
724 timezone: string;
725 customer: string;
726 invite: boolean;
727}
728
729export interface ContactEmailEntry {
730 email_id: string;
731 isPrimary: boolean;
732 key: number;
733}
734
735export interface ContactPhoneEntry {
736 phone: string;
737 isPrimary: boolean;
738 key: number;
739}
740
741export interface EditContactState {
742 firstName: string;
743 lastName: string;
744 image: string;
745 emails: ContactEmailEntry[];
746 phones: ContactPhoneEntry[];
747 customers: string[];
748 customer: string;
749 timezone: string;
750}
751
752export interface LineChart {
753 percentage_change: number;
754 total: number;
755 data: { date: string; count: number }[];
756}
757