From 61c8b16ff1f7d9fc63c4d043f1cdcfd92ba9751d Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 17 Apr 2024 15:24:55 +0530 Subject: [PATCH 01/45] feat: added whatsapp messages on lead/deal page in whatsapp tab --- crm/api/activities.py | 9 +++++ frontend/src/components/Activities.vue | 36 +++++++++++++++++ frontend/src/components/Icons/CheckIcon.vue | 16 ++++++++ .../src/components/Icons/DoubleCheckIcon.vue | 17 ++++++++ .../src/components/Icons/WhatsAppIcon.vue | 28 +++++++++++++ frontend/src/components/WhatsAppArea.vue | 40 +++++++++++++++++++ frontend/src/pages/Deal.vue | 6 +++ frontend/src/pages/Lead.vue | 6 +++ 8 files changed, 158 insertions(+) create mode 100644 frontend/src/components/Icons/CheckIcon.vue create mode 100644 frontend/src/components/Icons/DoubleCheckIcon.vue create mode 100644 frontend/src/components/Icons/WhatsAppIcon.vue create mode 100644 frontend/src/components/WhatsAppArea.vue diff --git a/crm/api/activities.py b/crm/api/activities.py index d04678d2..d3a3a63a 100644 --- a/crm/api/activities.py +++ b/crm/api/activities.py @@ -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 [] \ No newline at end of file diff --git a/frontend/src/components/Activities.vue b/frontend/src/components/Activities.vue index 8075a792..829468a4 100644 --- a/frontend/src/components/Activities.vue +++ b/frontend/src/components/Activities.vue @@ -35,6 +35,16 @@ {{ __('New Task') }} + @@ -338,6 +353,11 @@ +
{ } 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' }) }) diff --git a/frontend/src/components/Icons/CheckIcon.vue b/frontend/src/components/Icons/CheckIcon.vue new file mode 100644 index 00000000..ad94e5df --- /dev/null +++ b/frontend/src/components/Icons/CheckIcon.vue @@ -0,0 +1,16 @@ + diff --git a/frontend/src/components/Icons/DoubleCheckIcon.vue b/frontend/src/components/Icons/DoubleCheckIcon.vue new file mode 100644 index 00000000..77b7a478 --- /dev/null +++ b/frontend/src/components/Icons/DoubleCheckIcon.vue @@ -0,0 +1,17 @@ + diff --git a/frontend/src/components/Icons/WhatsAppIcon.vue b/frontend/src/components/Icons/WhatsAppIcon.vue new file mode 100644 index 00000000..1b8cdcfe --- /dev/null +++ b/frontend/src/components/Icons/WhatsAppIcon.vue @@ -0,0 +1,28 @@ + diff --git a/frontend/src/components/WhatsAppArea.vue b/frontend/src/components/WhatsAppArea.vue new file mode 100644 index 00000000..8b70a49e --- /dev/null +++ b/frontend/src/components/WhatsAppArea.vue @@ -0,0 +1,40 @@ + + + diff --git a/frontend/src/pages/Deal.vue b/frontend/src/pages/Deal.vue index 2d2345df..607b770f 100644 --- a/frontend/src/pages/Deal.vue +++ b/frontend/src/pages/Deal.vue @@ -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(() => { diff --git a/frontend/src/pages/Lead.vue b/frontend/src/pages/Lead.vue index cf22dc8c..aa743872 100644 --- a/frontend/src/pages/Lead.vue +++ b/frontend/src/pages/Lead.vue @@ -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) { From 11770489dc378399e833997d2a029810586ebbb2 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 18 Apr 2024 13:09:17 +0530 Subject: [PATCH 02/45] fix: send message from whatsapp tab --- crm/api/activities.py | 17 ++++++- frontend/src/components/Activities.vue | 58 +++++++++++++++-------- frontend/src/components/WhatsAppArea.vue | 41 +++++++++++++--- frontend/src/components/WhatsAppBox.vue | 59 ++++++++++++++++++++++++ 4 files changed, 148 insertions(+), 27 deletions(-) create mode 100644 frontend/src/components/WhatsAppBox.vue diff --git a/crm/api/activities.py b/crm/api/activities.py index d3a3a63a..e2aa7ec6 100644 --- a/crm/api/activities.py +++ b/crm/api/activities.py @@ -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 [] \ No newline at end of file + 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 \ No newline at end of file diff --git a/frontend/src/components/Activities.vue b/frontend/src/components/Activities.vue index 829468a4..ef228ebe 100644 --- a/frontend/src/components/Activities.vue +++ b/frontend/src/components/Activities.vue @@ -35,16 +35,23 @@ {{ __('New Task') }} - +
+ + +
{{ __('Loading...') }}
-
+
+ +
+
-
+ 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) diff --git a/frontend/src/components/WhatsAppArea.vue b/frontend/src/components/WhatsAppArea.vue index 8b70a49e..8104fcbe 100644 --- a/frontend/src/components/WhatsAppArea.vue +++ b/frontend/src/components/WhatsAppArea.vue @@ -3,19 +3,24 @@
-
{{ whatsapp.message }}
-
+
+
-
{{ dateFormat(whatsapp.creation, 'hh:mm a') }}
+
+ {{ dateFormat(whatsapp.creation, 'hh:mm a') }} +
- + $1') + // if message contains *text*, make it bold + message = message.replace(/\*(.*?)\*/g, '$1') + // if message contains ~text~, make it strikethrough + message = message.replace(/~(.*?)~/g, '$1') + // if message contains ```text```, make it monospace + message = message.replace(/```(.*?)```/g, '$1') + // if message contains `text`, make it inline code + message = message.replace(/`(.*?)`/g, '$1') + // if message contains > text, make it a blockquote + message = message.replace(/^> (.*)$/gm, '
$1
') + // if contain /n, make it a new line + message = message.replace(/\n/g, '
') + // if contains *text, make it a bullet point + message = message.replace(/\* (.*?)(?=\s*\*|$)/g, '
  • $1
  • ') + message = message.replace(/- (.*?)(?=\s*-|$)/g, '
  • $1
  • ') + message = message.replace(/(\d+)\. (.*?)(?=\s*(\d+)\.|$)/g, '
  • $2
  • ') + + return message + +} diff --git a/frontend/src/components/WhatsAppBox.vue b/frontend/src/components/WhatsAppBox.vue new file mode 100644 index 00000000..e73da3d9 --- /dev/null +++ b/frontend/src/components/WhatsAppBox.vue @@ -0,0 +1,59 @@ +