feat: added whatsapp messages on lead/deal page in whatsapp tab

This commit is contained in:
Shariq Ansari 2024-04-17 15:24:55 +05:30
parent e04a4c6b4c
commit 61c8b16ff1
8 changed files with 158 additions and 0 deletions

View File

@ -336,3 +336,12 @@ def get_linked_tasks(name):
],
)
return tasks or []
@frappe.whitelist()
def get_whatsapp_messages(name):
whatsapp_messages = frappe.db.get_all(
"WhatsApp Message",
filters={"reference_doctype": "CRM Lead", "reference_name": name, "status": ("not in", ["failed", "Success"])},
fields=["name", "type", "to", "from", "content_type", "creation", "message", "status"],
)
return whatsapp_messages or []

View File

@ -35,6 +35,16 @@
</template>
<span>{{ __('New Task') }}</span>
</Button>
<Button
v-else-if="title == 'WhatsApp'"
variant="solid"
@click="$refs.whatsappBox.show = true"
>
<template #prefix>
<FeatherIcon name="plus" class="h-4 w-4" />
</template>
<span>{{ __('New WhatsApp Message') }}</span>
</Button>
<Dropdown
v-else
:options="[
@ -58,6 +68,11 @@
label: __('New Task'),
onClick: () => showTask(),
},
{
icon: h(WhatsAppIcon, { class: 'h-4 w-4' }),
label: __('New WhatsApp Message'),
onClick: () => ($refs.emailBox.show = true),
},
]"
@click.stop
>
@ -338,6 +353,11 @@
</div>
</div>
</div>
<WhatsAppArea
v-else-if="title == 'WhatsApp'"
class="px-10"
:messages="activities"
/>
<div v-else v-for="(activity, i) in activities" class="activity">
<div class="grid grid-cols-[30px_minmax(auto,_1fr)] gap-4 px-10">
<div
@ -768,6 +788,8 @@ import EmailIcon from '@/components/Icons/EmailIcon.vue'
import PhoneIcon from '@/components/Icons/PhoneIcon.vue'
import NoteIcon from '@/components/Icons/NoteIcon.vue'
import TaskIcon from '@/components/Icons/TaskIcon.vue'
import WhatsAppIcon from '@/components/Icons/WhatsAppIcon.vue'
import WhatsAppArea from '@/components/WhatsAppArea.vue'
import LoadingIndicator from '@/components/Icons/LoadingIndicator.vue'
import DurationIcon from '@/components/Icons/DurationIcon.vue'
import CalendarIcon from '@/components/Icons/CalendarIcon.vue'
@ -875,6 +897,13 @@ const all_activities = createResource({
},
})
const whatsappMessages = createResource({
url: 'crm.api.activities.get_whatsapp_messages',
params: { name: doc.value.data.name },
cache: ['whatsapp', doc.value.data.name],
auto: true,
})
function get_activities() {
if (!all_activities.data?.versions) return []
if (!all_activities.data?.calls.length)
@ -900,6 +929,9 @@ const activities = computed(() => {
} else if (props.title == 'Notes') {
if (!all_activities.data?.notes) return []
return sortByCreation(all_activities.data.notes)
} else if (props.title == 'WhatsApp') {
if (!whatsappMessages.data) return []
return sortByCreation(whatsappMessages.data)
}
activities.forEach((activity) => {
activity.icon = timelineIcon(activity.activity_type, activity.is_lead)
@ -958,6 +990,8 @@ const emptyText = computed(() => {
text = 'No Notes'
} else if (props.title == 'Tasks') {
text = 'No Tasks'
} else if (props.title == 'WhatsApp') {
text = 'No WhatsApp Messages'
}
return text
})
@ -972,6 +1006,8 @@ const emptyTextIcon = computed(() => {
icon = NoteIcon
} else if (props.title == 'Tasks') {
icon = TaskIcon
} else if (props.title == 'WhatsApp') {
icon = WhatsAppIcon
}
return h(icon, { class: 'text-gray-500' })
})

View File

@ -0,0 +1,16 @@
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-check"
>
<path d="M20 6 9 17l-5-5" />
</svg>
</template>

View File

@ -0,0 +1,17 @@
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-check-check"
>
<path d="M18 6 7 17l-5-5" />
<path d="m22 10-7.5 7.5L13 16" />
</svg>
</template>

View File

@ -0,0 +1,28 @@
<template>
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 15.5C7.5 16.5 10.3367 17.2101 12.0757 16.7964C13.8147 16.3826 15.3488 15.3614 16.4015 13.9167C17.4541 12.472 17.9562 10.6988 17.8172 8.91668C17.6781 7.13456 16.9072 5.46069 15.6432 4.19671C14.3792 2.93273 12.7053 2.16176 10.9232 2.02273C9.14108 1.8837 7.36789 2.38575 5.92318 3.43842C4.47847 4.49109 3.45724 6.02514 3.04352 7.76414C2.62979 9.50314 3 12 4 13.5L2.5 17L6 15.5Z"
stroke="currentColor"
stroke-width="1"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M11.3355 12.3844L12 11.5C12.4877 11.0123 12.775 11.209 13.4481 11.4793C13.4824 11.4931 13.515 11.5112 13.5445 11.5333L13.8563 11.7664C14.0434 11.8987 14.0487 12.1733 13.8668 12.3126L13.2106 12.8153C12.9462 13.0178 12.5923 13.0604 12.2947 12.91C11.5548 12.536 10.1264 11.74 9.11332 10.7151C8.13237 9.72267 7.42487 8.40546 7.07965 7.68204C6.92664 7.3614 7.0003 6.98556 7.24735 6.72951L7.85039 6.10451C8.00431 5.94497 8.26796 5.97191 8.38609 6.15923L8.79347 6.72951C9.11332 7.24052 8.65816 7.67639 8.38609 7.85843L7.5 8.49417"
fill="currentColor"
/>
<path
d="M11.3355 12.3844L12 11.5C12.4877 11.0123 12.775 11.209 13.4481 11.4793C13.4824 11.4931 13.515 11.5112 13.5445 11.5333L13.8563 11.7664C14.0434 11.8987 14.0487 12.1733 13.8668 12.3126L13.2106 12.8153C12.9462 13.0178 12.5923 13.0604 12.2947 12.91C11.5548 12.536 10.1264 11.74 9.11332 10.7151C8.13237 9.72267 7.42487 8.40546 7.07965 7.68204C6.92664 7.3614 7.0003 6.98556 7.24735 6.72951L7.85039 6.10451C8.00431 5.94497 8.26796 5.97191 8.38609 6.15923L8.79347 6.72951C9.11332 7.24052 8.65816 7.67639 8.38609 7.85843L7.5 8.49417"
stroke="currentColor"
stroke-linejoin="round"
/>
</svg>
</template>

View File

@ -0,0 +1,40 @@
<template>
<div>
<div
v-for="whatsapp in messages"
:key="whatsapp.name"
class="flex"
:class="{ 'justify-end': whatsapp.type == 'Outgoing' }"
>
<div
class="mb-3 inline-flex max-w-[90%] gap-2 rounded-md bg-gray-50 p-2 text-base shadow-sm"
>
<div>{{ whatsapp.message }}</div>
<div class="-mb-1 flex items-end gap-1 text-gray-600 shrink-0">
<Tooltip :text="dateFormat(whatsapp.creation, 'ddd, MMM D, YYYY')">
<div class="text-2xs">{{ dateFormat(whatsapp.creation, 'hh:mm a') }}</div>
</Tooltip>
<div v-if="whatsapp.type == 'Outgoing'">
<CheckIcon v-if="whatsapp.status == 'sent'" class="size-4" />
<DoubleCheckIcon
v-else-if="['read', 'delivered'].includes(whatsapp.status)"
class="size-4"
:class="{ 'text-blue-500': whatsapp.status == 'read' }"
/>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import CheckIcon from '@/components/Icons/CheckIcon.vue'
import DoubleCheckIcon from '@/components/Icons/DoubleCheckIcon.vue'
import { Tooltip } from 'frappe-ui'
import { dateFormat } from '@/utils'
const props = defineProps({
messages: Array,
})
</script>

View File

@ -293,6 +293,7 @@ import EmailIcon from '@/components/Icons/EmailIcon.vue'
import PhoneIcon from '@/components/Icons/PhoneIcon.vue'
import TaskIcon from '@/components/Icons/TaskIcon.vue'
import NoteIcon from '@/components/Icons/NoteIcon.vue'
import WhatsAppIcon from '@/components/Icons/WhatsAppIcon.vue'
import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue'
import LinkIcon from '@/components/Icons/LinkIcon.vue'
import ArrowUpRightIcon from '@/components/Icons/ArrowUpRightIcon.vue'
@ -459,6 +460,11 @@ const tabs = [
label: __('Notes'),
icon: NoteIcon,
},
{
name: 'WhatsApp',
label: __('WhatsApp'),
icon: WhatsAppIcon,
},
]
const detailSections = computed(() => {

View File

@ -260,6 +260,7 @@ import EmailIcon from '@/components/Icons/EmailIcon.vue'
import PhoneIcon from '@/components/Icons/PhoneIcon.vue'
import TaskIcon from '@/components/Icons/TaskIcon.vue'
import NoteIcon from '@/components/Icons/NoteIcon.vue'
import WhatsAppIcon from '@/components/Icons/WhatsAppIcon.vue'
import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue'
import CameraIcon from '@/components/Icons/CameraIcon.vue'
import LinkIcon from '@/components/Icons/LinkIcon.vue'
@ -423,6 +424,11 @@ const tabs = [
label: __('Notes'),
icon: NoteIcon,
},
{
name: 'WhatsApp',
label: __('WhatsApp'),
icon: WhatsAppIcon,
},
]
function validateFile(file) {