fix: added lead detail section on right sidebar with phone call button and more
now agent can call the customer using that button
This commit is contained in:
parent
e31b6cdf72
commit
d9bf3388fe
@ -333,8 +333,7 @@ function handleDisconnectedIncomingCall() {
|
|||||||
counterUp.value.stop()
|
counterUp.value.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function makeOutgoingCall(close) {
|
async function makeOutgoingCall() {
|
||||||
close()
|
|
||||||
if (device) {
|
if (device) {
|
||||||
log.value = `Attempting to call +917666980887 ...`
|
log.value = `Attempting to call +917666980887 ...`
|
||||||
|
|
||||||
@ -428,7 +427,7 @@ watch(
|
|||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
provide('showPhoneCall', showPhoneCall)
|
provide('makeOutgoingCall', makeOutgoingCall)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@ -1,18 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="max-w-[81.7%] pl-16 p-4 pt-2">
|
<div class="max-w-[81.7%] pl-16 p-4 pt-2">
|
||||||
<button
|
<button
|
||||||
class="flex gap-2 w-full items-center rounded-lg p-1 bg-gray-100 hover:bg-gray-200"
|
class="flex gap-2 w-full items-center rounded-lg p-2 bg-gray-100 hover:bg-gray-200"
|
||||||
@click="showCommunicationBox = true"
|
@click="showCommunicationBox = true"
|
||||||
v-show="!showCommunicationBox"
|
v-show="!showCommunicationBox"
|
||||||
>
|
>
|
||||||
<UserAvatar class="m-1" :user="getUser().name" size="sm" />
|
<UserAvatar :user="getUser().name" size="sm" />
|
||||||
<div class="flex-1 text-left text-base text-gray-600">Add a reply...</div>
|
<div class="text-base text-gray-600">Add a reply...</div>
|
||||||
<Tooltip text="Make a call..." class="m-1">
|
|
||||||
<PhoneIcon
|
|
||||||
class="bg-gray-900 rounded-full text-white fill-white p-[3px]"
|
|
||||||
@click.stop="showPhoneCall = true"
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</button>
|
</button>
|
||||||
<div
|
<div
|
||||||
v-show="showCommunicationBox"
|
v-show="showCommunicationBox"
|
||||||
@ -55,14 +49,12 @@ import EmailEditor from '@/components/EmailEditor.vue'
|
|||||||
import PhoneIcon from '@/components/Icons/PhoneIcon.vue'
|
import PhoneIcon from '@/components/Icons/PhoneIcon.vue'
|
||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import { Tooltip, call, Button } from 'frappe-ui'
|
import { Tooltip, call, Button } from 'frappe-ui'
|
||||||
import { ref, watch, computed, defineModel, inject } from 'vue'
|
import { ref, watch, computed, defineModel } from 'vue'
|
||||||
|
|
||||||
const modelValue = defineModel()
|
const modelValue = defineModel()
|
||||||
|
|
||||||
const { getUser } = usersStore()
|
const { getUser } = usersStore()
|
||||||
|
|
||||||
let showPhoneCall = inject('showPhoneCall')
|
|
||||||
|
|
||||||
const showCommunicationBox = ref(false)
|
const showCommunicationBox = ref(false)
|
||||||
const newEmail = ref('')
|
const newEmail = ref('')
|
||||||
const newEmailEditor = ref(null)
|
const newEmailEditor = ref(null)
|
||||||
|
|||||||
@ -69,123 +69,148 @@
|
|||||||
<Activities :title="tab.activityTitle" :activities="tab.content" />
|
<Activities :title="tab.activityTitle" :activities="tab.content" />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</TabPanels>
|
</TabPanels>
|
||||||
<CommunicationArea v-if="[0, 1].includes(selectedIndex)" v-model="lead" />
|
<CommunicationArea
|
||||||
|
v-if="[0, 1].includes(selectedIndex)"
|
||||||
|
v-model="lead"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="flex flex-col justify-between border-l w-[360px]">
|
||||||
class="flex flex-col justify-between border-l w-[390px] overflow-hidden"
|
<div
|
||||||
>
|
class="flex flex-col gap-3 pb-4 p-5 items-center justify-center border-b"
|
||||||
<div class="flex flex-col gap-6.5 p-3 overflow-y-auto">
|
>
|
||||||
<div
|
<Avatar
|
||||||
v-for="section in detailSections"
|
size="3xl"
|
||||||
:key="section.label"
|
:label="lead.data.first_name"
|
||||||
class="flex flex-col"
|
:image="lead.data.image"
|
||||||
>
|
/>
|
||||||
<Toggler :is-opened="section.opened" v-slot="{ opened, toggle }">
|
<div class="font-medium text-2xl">{{ lead.data.lead_name }}</div>
|
||||||
<div
|
<div class="flex gap-3">
|
||||||
class="flex items-center gap-2 text-base font-semibold leading-5 pl-2 pr-3 cursor-pointer max-w-fit"
|
<Button class="rounded-full h-8 w-8" @click="makeCall">
|
||||||
@click="toggle()"
|
<PhoneIcon class="h-4 w-4" />
|
||||||
>
|
</Button>
|
||||||
<FeatherIcon
|
<Button class="rounded-full h-8 w-8">
|
||||||
name="chevron-right"
|
<EmailIcon class="h-4 w-4" />
|
||||||
class="h-4 text-gray-600 transition-all duration-300 ease-in-out"
|
</Button>
|
||||||
:class="{ 'rotate-90': opened }"
|
<Button icon="message-square" class="rounded-full h-8 w-8" />
|
||||||
/>
|
<Button icon="more-horizontal" class="rounded-full h-8 w-8" />
|
||||||
{{ section.label }}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<transition
|
<div class="flex-1 flex flex-col justify-between overflow-hidden">
|
||||||
enter-active-class="duration-300 ease-in"
|
<div class="flex flex-col gap-6.5 p-3 overflow-y-auto">
|
||||||
leave-active-class="duration-300 ease-[cubic-bezier(0, 1, 0.5, 1)]"
|
<div
|
||||||
enter-to-class="max-h-[200px] overflow-hidden"
|
v-for="section in detailSections"
|
||||||
leave-from-class="max-h-[200px] overflow-hidden"
|
:key="section.label"
|
||||||
enter-from-class="max-h-0 overflow-hidden"
|
class="flex flex-col"
|
||||||
leave-to-class="max-h-0 overflow-hidden"
|
>
|
||||||
>
|
<Toggler :is-opened="section.opened" v-slot="{ opened, toggle }">
|
||||||
<div v-if="opened" class="flex flex-col gap-3">
|
<div
|
||||||
<div
|
class="flex items-center gap-2 text-base font-semibold leading-5 pl-2 pr-3 cursor-pointer max-w-fit"
|
||||||
v-for="field in section.fields"
|
@click="toggle()"
|
||||||
:key="field.label"
|
>
|
||||||
class="flex items-center px-3 gap-2 text-base leading-5 first:mt-4.5"
|
<FeatherIcon
|
||||||
>
|
name="chevron-right"
|
||||||
<div class="text-gray-600 w-[106px]">{{ field.label }}</div>
|
class="h-4 text-gray-600 transition-all duration-300 ease-in-out"
|
||||||
<div class="flex-1 w-full">
|
:class="{ 'rotate-90': opened }"
|
||||||
<FormControl
|
/>
|
||||||
v-if="field.type === 'select'"
|
{{ section.label }}
|
||||||
type="select"
|
</div>
|
||||||
:options="field.options"
|
<transition
|
||||||
v-model="lead.data[field.name]"
|
enter-active-class="duration-300 ease-in"
|
||||||
>
|
leave-active-class="duration-300 ease-[cubic-bezier(0, 1, 0.5, 1)]"
|
||||||
<template #prefix>
|
enter-to-class="max-h-[200px] overflow-hidden"
|
||||||
<IndicatorIcon
|
leave-from-class="max-h-[200px] overflow-hidden"
|
||||||
:class="indicatorColor[lead.data[field.name]]"
|
enter-from-class="max-h-0 overflow-hidden"
|
||||||
/>
|
leave-to-class="max-h-0 overflow-hidden"
|
||||||
</template>
|
>
|
||||||
</FormControl>
|
<div v-if="opened" class="flex flex-col gap-3">
|
||||||
<FormControl
|
<div
|
||||||
v-else-if="field.type === 'email'"
|
v-for="field in section.fields"
|
||||||
type="email"
|
:key="field.label"
|
||||||
v-model="lead.data[field.name]"
|
class="flex items-center px-3 gap-2 text-base leading-5 first:mt-4.5"
|
||||||
/>
|
>
|
||||||
<Autocomplete
|
<div class="text-gray-600 w-[106px]">
|
||||||
v-else-if="field.type === 'link'"
|
{{ field.label }}
|
||||||
:options="activeAgents"
|
</div>
|
||||||
:value="getUser(lead.data[field.name]).full_name"
|
<div class="flex-1 w-full">
|
||||||
@change="
|
<FormControl
|
||||||
(option) => (lead.data[field.name] = option.email)
|
v-if="field.type === 'select'"
|
||||||
"
|
type="select"
|
||||||
placeholder="Lead owner"
|
:options="field.options"
|
||||||
>
|
v-model="lead.data[field.name]"
|
||||||
<template #prefix>
|
>
|
||||||
<UserAvatar
|
<template #prefix>
|
||||||
class="mr-2"
|
<IndicatorIcon
|
||||||
:user="lead.data[field.name]"
|
:class="indicatorColor[lead.data[field.name]]"
|
||||||
size="sm"
|
/>
|
||||||
/>
|
</template>
|
||||||
</template>
|
</FormControl>
|
||||||
<template #item-prefix="{ option }">
|
<FormControl
|
||||||
<UserAvatar
|
v-else-if="field.type === 'email'"
|
||||||
class="mr-2"
|
type="email"
|
||||||
:user="option.email"
|
v-model="lead.data[field.name]"
|
||||||
size="sm"
|
/>
|
||||||
/>
|
<Autocomplete
|
||||||
</template>
|
v-else-if="field.type === 'link'"
|
||||||
</Autocomplete>
|
:options="activeAgents"
|
||||||
<Dropdown
|
:value="getUser(lead.data[field.name]).full_name"
|
||||||
v-else-if="field.type === 'dropdown'"
|
@change="
|
||||||
:options="statusDropdownOptions"
|
(option) => (lead.data[field.name] = option.email)
|
||||||
class="w-full flex-1"
|
"
|
||||||
>
|
placeholder="Lead owner"
|
||||||
<template #default="{ open }">
|
>
|
||||||
<Button
|
<template #prefix>
|
||||||
:label="lead.data[field.name]"
|
<UserAvatar
|
||||||
class="justify-between w-full"
|
class="mr-2"
|
||||||
>
|
:user="lead.data[field.name]"
|
||||||
<template #prefix>
|
size="sm"
|
||||||
<IndicatorIcon
|
/>
|
||||||
:class="indicatorColor[lead.data[field.name]]"
|
</template>
|
||||||
/>
|
<template #item-prefix="{ option }">
|
||||||
</template>
|
<UserAvatar
|
||||||
<template #default>{{
|
class="mr-2"
|
||||||
lead.data[field.name]
|
:user="option.email"
|
||||||
}}</template>
|
size="sm"
|
||||||
<template #suffix>
|
/>
|
||||||
<FeatherIcon
|
</template>
|
||||||
:name="open ? 'chevron-up' : 'chevron-down'"
|
</Autocomplete>
|
||||||
class="h-4"
|
<Dropdown
|
||||||
/>
|
v-else-if="field.type === 'dropdown'"
|
||||||
</template>
|
:options="statusDropdownOptions"
|
||||||
</Button>
|
class="w-full flex-1"
|
||||||
</template>
|
>
|
||||||
</Dropdown>
|
<template #default="{ open }">
|
||||||
<FormControl
|
<Button
|
||||||
v-else
|
:label="lead.data[field.name]"
|
||||||
type="text"
|
class="justify-between w-full"
|
||||||
v-model="lead.data[field.name]"
|
>
|
||||||
/>
|
<template #prefix>
|
||||||
|
<IndicatorIcon
|
||||||
|
:class="indicatorColor[lead.data[field.name]]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #default>{{
|
||||||
|
lead.data[field.name]
|
||||||
|
}}</template>
|
||||||
|
<template #suffix>
|
||||||
|
<FeatherIcon
|
||||||
|
:name="open ? 'chevron-up' : 'chevron-down'"
|
||||||
|
class="h-4"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
</Dropdown>
|
||||||
|
<FormControl
|
||||||
|
v-else
|
||||||
|
type="text"
|
||||||
|
v-model="lead.data[field.name]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</transition>
|
||||||
</transition>
|
</Toggler>
|
||||||
</Toggler>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@ -217,7 +242,7 @@ import Toggler from '@/components/Toggler.vue'
|
|||||||
import Activities from '@/components/Activities.vue'
|
import Activities from '@/components/Activities.vue'
|
||||||
import Breadcrumbs from '@/components/Breadcrumbs.vue'
|
import Breadcrumbs from '@/components/Breadcrumbs.vue'
|
||||||
import UserAvatar from '@/components/UserAvatar.vue'
|
import UserAvatar from '@/components/UserAvatar.vue'
|
||||||
import CommunicationArea from '../components/CommunicationArea.vue'
|
import CommunicationArea from '@/components/CommunicationArea.vue'
|
||||||
import { TabGroup, TabList, Tab, TabPanels, TabPanel } from '@headlessui/vue'
|
import { TabGroup, TabList, Tab, TabPanels, TabPanel } from '@headlessui/vue'
|
||||||
import { TransitionPresets, useTransition } from '@vueuse/core'
|
import { TransitionPresets, useTransition } from '@vueuse/core'
|
||||||
import { dateFormat, timeAgo, dateTooltipFormat } from '@/utils'
|
import { dateFormat, timeAgo, dateTooltipFormat } from '@/utils'
|
||||||
@ -230,11 +255,14 @@ import {
|
|||||||
FormControl,
|
FormControl,
|
||||||
Dropdown,
|
Dropdown,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
|
Avatar,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { ref, computed, h } from 'vue'
|
import { ref, computed, h, inject } from 'vue'
|
||||||
|
|
||||||
const { getUser, users } = usersStore()
|
const { getUser, users } = usersStore()
|
||||||
|
|
||||||
|
const makeCall = inject('makeOutgoingCall')
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
leadId: {
|
leadId: {
|
||||||
type: String,
|
type: String,
|
||||||
@ -406,9 +434,14 @@ const detailSections = computed(() => {
|
|||||||
opened: true,
|
opened: true,
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
label: 'Name',
|
label: 'First Name',
|
||||||
type: 'data',
|
type: 'data',
|
||||||
name: 'lead_name',
|
name: 'first_name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Last Name',
|
||||||
|
type: 'data',
|
||||||
|
name: 'last_name',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Email',
|
label: 'Email',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user