fix: send message from whatsapp tab
This commit is contained in:
parent
61c8b16ff1
commit
11770489dc
@ -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
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
59
frontend/src/components/WhatsAppBox.vue
Normal file
59
frontend/src/components/WhatsAppBox.vue
Normal 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>
|
||||
Loading…
x
Reference in New Issue
Block a user