1<template>
2 <div class="flex items-center gap-3">
3 <Avatar
4 :label="contact.data?.name ?? ''"
5 :image="contactImage"
6 size="3xl"
7 />
8 <div class="flex min-w-0 flex-1 flex-col gap-1.5">
9 <Tooltip :text="contact.data?.name || contact.data?.email_id">
10 <div class="flex min-w-0 items-center gap-1.5 w-fit max-w-[65%]">
11 <p
12 class="min-h-[1lh] cursor-pointer truncate text-md font-medium text-ink-gray-7 hover:text-ink-gray-9"
13 @click="openContact(contact.data?.name)"
14 >
15 {{ contact.data?.name || contact.data?.email_id }}
16 </p>
17 </div>
18 </Tooltip>
19 <div class="flex items-center gap-1 text-p-sm text-ink-gray-6">
20 <p
21 class="cursor-copy transition-colors hover:text-ink-gray-8"
22 @click="
23 copyToClipboard(
24 ticket.doc.name,
25 `Ticket #${ticket.doc.name} copied`
26 )
27 "
28 >
29 #{{ ticket.doc.name }}
30 </p>
31 <div class="flex items-center">
32 <span>{{
33 ticket.doc.via_customer_portal ? __("via Portal") : __("via Email")
34 }}</span>
35 </div>
36 </div>
37 </div>
38 <Tooltip v-if="isCallingEnabled" :text="__('Call contact')">
39 <Button variant="ghost" @click="callContact">
40 <template #icon>
41 <PhoneIcon class="size-4" />
42 </template>
43 </Button>
44 </Tooltip>
45 <SetContactPhoneModal
46 v-model="showPhoneModal"
47 :name="contact.data?.name ?? ''"
48 @onUpdate="contact.reload"
49 />
50 </div>
51</template>
52
53<script setup lang="ts">
54import { useShortcut } from "@opendesk/shared/shortcuts";
55import { useTelephonyStore } from "@/stores/telephony";
56import { useUserStore } from "@/stores/user";
57import { TicketContactSymbol, TicketSymbol } from "@/types";
58import { openContact } from "@/utils";
59import { copyToClipboard } from "@opendesk/shared/utils";
60import { Avatar, Button, Tooltip } from "frappe-ui";
61import { storeToRefs } from "pinia";
62import { computed, inject, ref } from "vue";
63import PhoneIcon from "../icons/PhoneIcon.vue";
64import SetContactPhoneModal from "../ticket/SetContactPhoneModal.vue";
65
66const telephonyStore = useTelephonyStore();
67const { getUser } = useUserStore();
68const { isCallingEnabled } = storeToRefs(telephonyStore);
69const showPhoneModal = ref(false);
70
71const ticket = inject(TicketSymbol)!;
72
73const contact = inject(TicketContactSymbol)!;
74const contactImage = computed(() => {
75 if (!contact.value?.data) return "";
76 const email = contact.value?.data?.email_id ?? "";
77 return (
78 contact.value?.data?.image || (email && getUser(email)?.user_image) || ""
79 );
80});
81
82const callContact = () => {
83 if (!contact.value.data.mobile_no && !contact.value.data.phone) {
84 showPhoneModal.value = true;
85 return;
86 }
87 telephonyStore.makeCall({
88 number: contact.value.data.mobile_no || contact.value.data.phone,
89 doctype: "OD Ticket",
90 docname: ticket.value.name,
91 });
92};
93
94useShortcut({ meta: true, shift: true, key: "." }, () => {
95 copyToClipboard(window.location.href, `Ticket URL copied`);
96});
97
98useShortcut({ meta: true, key: "." }, () => {
99 copyToClipboard(
100 ticket.value.doc.name,
101 `Ticket #${ticket.value.doc.name} copied`
102 );
103});
104</script>
105