1
0
forked from test/crm

fix: send message from whatsapp tab

This commit is contained in:
Shariq Ansari 2024-04-18 13:09:17 +05:30
parent 61c8b16ff1
commit 11770489dc
4 changed files with 148 additions and 27 deletions

View File

@ -341,7 +341,20 @@ def get_linked_tasks(name):
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"])},
filters={"reference_doctype": "CRM Lead", "reference_name": name, "status": ("not in", ["failed"])},
fields=["name", "type", "to", "from", "content_type", "creation", "message", "status"],
)
return whatsapp_messages or []
return whatsapp_messages or []
@frappe.whitelist()
def create_whatsapp_message(reference_doctype, reference_name, to, message, content_type="text"):
doc = frappe.new_doc("WhatsApp Message")
doc.update({
"reference_doctype": reference_doctype,
"reference_name": reference_name,
"to": to,
"message": message,
"content_type": content_type,
})
doc.insert(ignore_permissions=True)
return doc.name

View File

@ -35,16 +35,23 @@
</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>
<div class="flex gap-2" v-else-if="title == 'WhatsApp'">
<Button
:label="__('Refresh')"
@click="whatsappMessages.reload()"
:loading="whatsappMessages.loading"
>
<template #icon>
<RefreshIcon class="h-4 w-4" />
</template>
</Button>
<Button variant="solid" @click="$refs.whatsappBox.show = true">
<template #prefix>
<FeatherIcon name="plus" class="h-4 w-4" />
</template>
<span>{{ __('New WhatsApp Message') }}</span>
</Button>
</div>
<Dropdown
v-else
:options="[
@ -99,7 +106,16 @@
<LoadingIndicator class="h-6 w-6" />
<span>{{ __('Loading...') }}</span>
</div>
<div v-else-if="activities?.length" class="activities flex-1 overflow-y-auto">
<div
v-else-if="title == 'WhatsApp' && whatsappMessages.data?.length"
class="activities flex-1 overflow-y-auto"
>
<WhatsAppArea class="px-10" :messages="whatsappMessages.data" />
</div>
<div
v-else-if="activities?.length"
class="activities flex-1 overflow-y-auto"
>
<div
v-if="title == 'Notes'"
class="activity grid grid-cols-1 gap-4 px-10 pb-5 lg:grid-cols-2 xl:grid-cols-3"
@ -353,11 +369,6 @@
</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
@ -766,6 +777,14 @@
:doctype="doctype"
@scroll="scroll"
/>
<WhatsAppBox
ref="whatsappBox"
v-if="title == 'WhatsApp'"
v-model="doc"
v-model:whatsapp="whatsappMessages"
:doctype="doctype"
@scroll="scroll"
/>
<NoteModal
v-model="showNoteModal"
v-model:reloadNotes="all_activities"
@ -790,6 +809,8 @@ 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 WhatsAppBox from '@/components/WhatsAppBox.vue'
import RefreshIcon from '@/components/Icons/RefreshIcon.vue'
import LoadingIndicator from '@/components/Icons/LoadingIndicator.vue'
import DurationIcon from '@/components/Icons/DurationIcon.vue'
import CalendarIcon from '@/components/Icons/CalendarIcon.vue'
@ -902,6 +923,7 @@ const whatsappMessages = createResource({
params: { name: doc.value.data.name },
cache: ['whatsapp', doc.value.data.name],
auto: true,
onSuccess: () => nextTick(() => scroll()),
})
function get_activities() {
@ -929,10 +951,8 @@ 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)

View File

@ -3,19 +3,24 @@
<div
v-for="whatsapp in messages"
:key="whatsapp.name"
class="flex"
class="activity 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"
class="mb-3 inline-flex max-w-[90%] gap-2 rounded-md bg-gray-50 p-1.5 pl-2 text-base shadow-sm"
>
<div>{{ whatsapp.message }}</div>
<div class="-mb-1 flex items-end gap-1 text-gray-600 shrink-0">
<div v-html="formatWhatsAppMessage(whatsapp.message)"></div>
<div class="-mb-1 flex shrink-0 items-end gap-1 text-gray-600">
<Tooltip :text="dateFormat(whatsapp.creation, 'ddd, MMM D, YYYY')">
<div class="text-2xs">{{ dateFormat(whatsapp.creation, 'hh:mm a') }}</div>
<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" />
<CheckIcon
v-if="['sent', 'Success'].includes(whatsapp.status)"
class="size-4"
/>
<DoubleCheckIcon
v-else-if="['read', 'delivered'].includes(whatsapp.status)"
class="size-4"
@ -37,4 +42,28 @@ import { dateFormat } from '@/utils'
const props = defineProps({
messages: Array,
})
function formatWhatsAppMessage(message) {
// if message contains _text_, make it italic
message = message.replace(/_(.*?)_/g, '<i>$1</i>')
// if message contains *text*, make it bold
message = message.replace(/\*(.*?)\*/g, '<b>$1</b>')
// if message contains ~text~, make it strikethrough
message = message.replace(/~(.*?)~/g, '<s>$1</s>')
// if message contains ```text```, make it monospace
message = message.replace(/```(.*?)```/g, '<code>$1</code>')
// if message contains `text`, make it inline code
message = message.replace(/`(.*?)`/g, '<code>$1</code>')
// if message contains > text, make it a blockquote
message = message.replace(/^> (.*)$/gm, '<blockquote>$1</blockquote>')
// if contain /n, make it a new line
message = message.replace(/\n/g, '<br>')
// if contains *<space>text, make it a bullet point
message = message.replace(/\* (.*?)(?=\s*\*|$)/g, '<li>$1</li>')
message = message.replace(/- (.*?)(?=\s*-|$)/g, '<li>$1</li>')
message = message.replace(/(\d+)\. (.*?)(?=\s*(\d+)\.|$)/g, '<li>$2</li>')
return message
}
</script>

View File

@ -0,0 +1,59 @@
<template>
<div class="flex items-end gap-2 px-10 py-2.5">
<Textarea
type="textarea"
class="min-h-8 w-full"
:rows="rows"
v-model="content"
:placeholder="placeholder"
@focus="rows = 6"
@blur="rows = 1"
/>
<div class="flex justify-end gap-2">
<Button
class="min-h-8"
variant="solid"
:label="__('Send')"
@click="sendWhatsAppMessage"
:disabled="isEmpty"
/>
</div>
</div>
</template>
<script setup>
import { createResource, Textarea } from 'frappe-ui'
import { ref, computed, nextTick } from 'vue'
const props = defineProps({
doctype: String,
})
const doc = defineModel()
const whatsapp = defineModel('whatsapp')
const rows = ref(1)
const content = ref('')
const placeholder = ref(__('Type your message here...'))
const isEmpty = computed(() => {
return !content.value || content.value === '<p></p>'
})
async function sendWhatsAppMessage() {
let args = {
reference_doctype: props.doctype,
reference_name: doc.value.data.name,
message: content.value,
to: doc.value.data.mobile_no,
content_type: 'text',
}
content.value = ''
createResource({
url: 'crm.api.activities.create_whatsapp_message',
params: args,
auto: true,
onSuccess: () => nextTick(() => whatsapp.value?.reload()),
})
}
</script>