From 0b63dbc636326a32b75fe3056220a9afa03da2d8 Mon Sep 17 00:00:00 2001 From: fadilsid Date: Wed, 18 Jan 2023 10:21:07 +0000 Subject: [PATCH 01/32] feat: update status while sending message --- desk/src/pages/desk/Ticket.vue | 145 +++++++++++++++++++++++++-------- frappedesk/api/ticket.py | 9 +- 2 files changed, 117 insertions(+), 37 deletions(-) diff --git a/desk/src/pages/desk/Ticket.vue b/desk/src/pages/desk/Ticket.vue index 71d832cc0..b4f41f38e 100644 --- a/desk/src/pages/desk/Ticket.vue +++ b/desk/src/pages/desk/Ticket.vue @@ -207,29 +207,85 @@ class="pt-2 select-none flex flex-row items-center space-x-2 gap-2" v-if="$refs.replyEditor" > - +
+ + +
@@ -384,7 +440,7 @@ import CannedResponsesDialog from "@/components/desk/global/CannedResponsesDialo import ArticleResponseDialog from "@/components/desk/global/ArticleResponseDialog.vue" import { inject, ref } from "vue" import TicketStatus from "@/components/global/ticket_list_item/TicketStatus.vue" - +import { color } from "echarts" export default { name: "Ticket", props: ["ticketId"], @@ -419,17 +475,13 @@ export default { const user = inject("user") const agents = inject("agents") const attachments = ref([]) - const editingType = ref("") - + const replied = ref("Replied") const tempTextEditorData = ref({}) - const showCannedResponsesDialog = ref(false) const tempMessage = ref("") - const showArticleResponseDialog = ref(false) const tempContent = ref("") - return { showTextFormattingMenu, viewportWidth, @@ -442,6 +494,7 @@ export default { tempMessage, showArticleResponseDialog, tempContent, + replied, } }, resources: { @@ -452,6 +505,14 @@ export default { name: this.ticketId, } }, + submitAndUpdateTicketStatus() { + return { + method: "frappedesk.api.ticket.update_ticket_status", + onSuccess: (val) => { + console.log(val) + }, + } + }, submitConversation() { return { method: "frappedesk.api.ticket.submit_conversation_via_agent", @@ -463,7 +524,6 @@ export default { text: "No default outgoing email available", }, }[res.error_code] - this.$toast({ fixed: true, title: error.title, @@ -620,7 +680,6 @@ export default { function delay(time) { return new Promise((resolve) => setTimeout(resolve, time)) } - delay(400).then(() => (this.scrollConversationsToBottom = true)) delay(1000).then(() => (this.scrollConversationsToBottom = false)) }, @@ -649,13 +708,30 @@ export default { this.tempTextEditorData.content = this.content this.tempTextEditorData.attachments = this.attachments const content = `
${this.content}
` - this.$resources.submitConversation.submit({ ticket_id: this.ticketId, message: content, attachments: this.attachments.map((x) => x.name), }) - + this.content = "" + this.attachments = [] + }, + submitAndUpdateTicketStatus(status) { + this.$resources.submitAndUpdateTicketStatus.submit({ + ticket_id: this.ticketId, + status: status, + }) + }, + submitResolvedTicket() { + this.tempTextEditorData.content = this.content + this.tempTextEditorData.attachments = this.attachments + const content = `
${this.content}
` + this.$resources.submitConversation.submit({ + ticket_id: this.ticketId, + message: content, + status: "Resolved", + attachments: this.attachments.map((x) => x.name), + }) this.content = "" this.attachments = [] }, @@ -663,7 +739,6 @@ export default { this.tempTextEditorData.attachments = this.attachments this.tempTextEditorData.content = this.content const content = `
${this.content}
` - this.$resources.submitComment.submit({ doc: { doctype: "Frappe Desk Comment", @@ -672,13 +747,11 @@ export default { commented_by: this.user.user, }, }) - this.content = "" this.attachments = [] }, getNextTicket() {}, getPreviousTicket() {}, - getMessage(message) { this.tempMessage = message this.content = this.tempMessage diff --git a/frappedesk/api/ticket.py b/frappedesk/api/ticket.py index 722669c37..9e83fa5be 100644 --- a/frappedesk/api/ticket.py +++ b/frappedesk/api/ticket.py @@ -379,6 +379,13 @@ def assign_ticket_status(ticket_id, status): log_ticket_activity(ticket_id, f"status set to {status}") return ticket_doc +@frappe.whitelist() +def update_ticket_status(ticket_id,status): + frappe.db.set_value('Ticket', ticket_id, 'status', status,update_modified=False) + + doc = frappe.get_doc('Ticket',ticket_id) + + return doc @frappe.whitelist() @@ -569,4 +576,4 @@ def get_custom_fields(view="Customer Portal"): @frappe.whitelist() def get_assignee(ticket_id): - return frappe.get_doc("Ticket", ticket_id).get_assigned_agent() + return frappe.get_doc("Ticket", ticket_id).get_assigned_agent() \ No newline at end of file From 3e7d9fc3118fc8aec37791846c995970dbde1751 Mon Sep 17 00:00:00 2001 From: Sabu Siyad Date: Fri, 10 Mar 2023 15:50:55 +0530 Subject: [PATCH 02/32] refactor: re-arrange button placement --- desk/src/pages/desk/Ticket.vue | 590 ++++++++++++++------------------- 1 file changed, 245 insertions(+), 345 deletions(-) diff --git a/desk/src/pages/desk/Ticket.vue b/desk/src/pages/desk/Ticket.vue index 991c5f2b8..4d308478f 100644 --- a/desk/src/pages/desk/Ticket.vue +++ b/desk/src/pages/desk/Ticket.vue @@ -1,23 +1,20 @@ - - diff --git a/frappedesk/frappedesk/doctype/frappe_desk_comment/frappe_desk_comment.py b/frappedesk/frappedesk/doctype/frappe_desk_comment/frappe_desk_comment.py index f31a70e71..26d13c50b 100644 --- a/frappedesk/frappedesk/doctype/frappe_desk_comment/frappe_desk_comment.py +++ b/frappedesk/frappedesk/doctype/frappe_desk_comment/frappe_desk_comment.py @@ -9,9 +9,8 @@ class FrappeDeskComment(Document): def on_change(self): - print(f"\n\nFrappe Desk Comment created : {self.name}\n\n") mentions = extract_mentions(self.content) - print(f"\n\nMentions : {mentions}\n\n") + for mention in mentions: values = frappe._dict( from_user=self.commented_by, @@ -19,11 +18,18 @@ def on_change(self): ticket=self.reference_ticket, comment=self.name, ) + if frappe.db.exists("Frappe Desk Notification", values): continue + notification = frappe.get_doc(doctype="Frappe Desk Notification") notification.message = ( f"{get_fullname(self.owner)} mentioned you in Ticket #{self.reference_ticket}", ) notification.update(values) notification.insert(ignore_permissions=True) + + def after_insert(self): + frappe.publish_realtime( + "new_frappedesk_comment", {"ticket_id": self.reference_ticket} + ) diff --git a/frappedesk/frappedesk/hooks/communication.py b/frappedesk/frappedesk/hooks/communication.py new file mode 100644 index 000000000..0b29d5760 --- /dev/null +++ b/frappedesk/frappedesk/hooks/communication.py @@ -0,0 +1,19 @@ +import frappe + + +def after_insert(c, method=None): + # DocType against which the communication is created. We only want those + # which related to Frappe Desk + if not c.reference_doctype == "Ticket": + return + + # Skip if doc is not mentioned + if not c.reference_name: + return + + frappe.publish_realtime( + "new_frappedesk_communication", + { + "ticket_id": c.reference_name, + }, + ) diff --git a/frappedesk/hooks.py b/frappedesk/hooks.py index 933b14d86..6273fab73 100644 --- a/frappedesk/hooks.py +++ b/frappedesk/hooks.py @@ -24,6 +24,7 @@ ], "after_insert": [ "frappedesk.frappedesk.doctype.ticket.ticket.set_descritption_from_communication", + "frappedesk.frappedesk.hooks.communication.after_insert", ], }, "Contact": { From 6a0ed7da4722fc7c85be0e91de348ccc13293a49 Mon Sep 17 00:00:00 2001 From: Sabu Siyad Date: Mon, 13 Mar 2023 18:16:17 +0530 Subject: [PATCH 09/32] feat: form in toast --- desk/src/components/global/Toast.vue | 51 +++++++++++++++++----------- desk/src/pages/desk/Desk.vue | 32 +++++++++-------- 2 files changed, 50 insertions(+), 33 deletions(-) diff --git a/desk/src/components/global/Toast.vue b/desk/src/components/global/Toast.vue index e254b0bcb..d513a0ae2 100644 --- a/desk/src/components/global/Toast.vue +++ b/desk/src/components/global/Toast.vue @@ -29,13 +29,27 @@ {{ b.title }}
+
+
+ + +
+
@@ -85,29 +99,28 @@ export default { type: Array, default: [], }, + form: { + type: Object, + }, + actionOnClose: { + type: Function, + } }, emits: ["close"], - // data() { - // return { - // buttons: [ - // { - // title: 'Setup Now', - // appearance: 'primary', - // onClick: () => console.log('foo'), - // }, - // { - // title: "Don't remind again!", - // onClick: () => console.log('bar'), - // }, - // ] - // } - // }, mounted() { if (this.timeout > 0) { - setTimeout(() => { - this.$emit("close"); - }, this.timeout * 1000); + setTimeout(this.onToastClose, this.timeout * 1000); } }, + methods: { + onToastClose() { + if (this.actionOnClose instanceof Function) this.actionOnClose(); + this.$emit('close'); + }, + submitForm(form, event) { + if (form.onSubmit instanceof Function) form.onSubmit(event); + this.$emit("close"); + }, + }, }; diff --git a/desk/src/pages/desk/Desk.vue b/desk/src/pages/desk/Desk.vue index 2cb7917c9..fc5784805 100644 --- a/desk/src/pages/desk/Desk.vue +++ b/desk/src/pages/desk/Desk.vue @@ -209,29 +209,33 @@ export default { } }, showHelpdeskNameSetupToast() { - console.log("showing helpdesk name setup toast"); this.$toast({ - title: "Setup Helpdesk Name", + title: "Set a name", + text: "What would you like to name your helpdesk?", + timeout: 0, + icon: "edit", + iconClasses: "text-blue-500", form: { + classes: "flex gap-1", inputs: [ { type: "text", - fieldname: "helpdesk_name", - placeholder: "eg: FDESK", + fieldname: "helpdeskName", + placeholder: "Frappe Helpdesk", }, ], onSubmit: (values) => { - if (values.helpdesk_name) { - this.$resources.setHelpdeskName.submit({ - name: values.helpdesk_name, - }); - } + const inputs = values.target?.elements; + const name = inputs?.helpdeskName?.value; + + if (!name) return; + + this.$resources.setHelpdeskName.submit({ + name, + }); }, }, - fixed: true, - appearance: "info", - position: "bottom-right", - onClose: () => { + actionOnClose: () => { this.$resources.skipHelpdeskNameSetup.submit(); }, }); @@ -271,7 +275,7 @@ export default { onClick: () => { this.$router.push({ name: "Agents" }); }, - } + }, ], }); }, From 60da5fde40b1a0a9c3dbc057ad348d2a39d2f6f9 Mon Sep 17 00:00:00 2001 From: Sabu Siyad Date: Mon, 13 Mar 2023 22:17:34 +0530 Subject: [PATCH 10/32] fix(ui): remove custom icon from toasts --- .../components/desk/contacts/ContactInfo.vue | 4 ++-- .../desk/contacts/ContactRelatedInfo.vue | 16 ++++++------- .../desk/customers/CustomerInfo.vue | 16 ++++++------- .../desk/global/AddNewAgentsDialog.vue | 13 +++++----- .../global/AddNewCannedResponsesDialog.vue | 4 ++-- .../desk/settings/agents/AgentInfo.vue | 4 ++-- .../canned_responses/CannedResponseInfo.vue | 8 +++---- .../desk/settings/teams/TeamInfo.vue | 16 ++++++------- .../settings/ticket_types/TicketTypeInfo.vue | 8 +++---- .../components/global/SaveFiltersDialog.vue | 8 +++---- desk/src/components/global/TicketField.vue | 22 ++++++++--------- desk/src/components/global/Toast.vue | 2 +- .../components/global/kb/ArticleMiniList.vue | 8 +++---- .../components/global/kb/CategoryCardList.vue | 12 +++++----- desk/src/pages/common/kb/Article.vue | 12 +++++----- desk/src/pages/common/kb/Category.vue | 4 ++-- desk/src/pages/desk/Desk.vue | 24 +++++++++---------- desk/src/pages/desk/Setup.vue | 12 +++++----- desk/src/pages/desk/Ticket.vue | 4 ++-- desk/src/pages/desk/Tickets.vue | 18 +++++++------- desk/src/pages/desk/settings/agent/Agents.vue | 4 ++-- .../canned_response/CannedResponses.vue | 4 ++-- .../desk/settings/email/EmailAccount.vue | 24 +++++++++---------- .../src/pages/desk/settings/sla/SlaPolicy.vue | 4 ++-- desk/src/pages/portal/ticketing/Ticketing.vue | 4 ++-- 25 files changed, 127 insertions(+), 128 deletions(-) diff --git a/desk/src/components/desk/contacts/ContactInfo.vue b/desk/src/components/desk/contacts/ContactInfo.vue index 9b5cb7cdb..59200d62b 100644 --- a/desk/src/components/desk/contacts/ContactInfo.vue +++ b/desk/src/components/desk/contacts/ContactInfo.vue @@ -235,8 +235,8 @@ export default { onSuccess: (res) => { this.$toast({ title: "Contact Updated.", - customIcon: "circle-check", - appearance: "success", + icon: "check", + iconClasses: "text-green-500" }) this.$router.go() }, diff --git a/desk/src/components/desk/contacts/ContactRelatedInfo.vue b/desk/src/components/desk/contacts/ContactRelatedInfo.vue index 5412b0037..f1c97136c 100644 --- a/desk/src/components/desk/contacts/ContactRelatedInfo.vue +++ b/desk/src/components/desk/contacts/ContactRelatedInfo.vue @@ -239,8 +239,8 @@ export default { this.$toast({ title: `Tickets marked as ${res.status}.`, - customIcon: "circle-check", - appearance: "success", + icon: "check", + iconClasses: "text-green-500", }) this.$event.emit("update_ticket_list") @@ -248,8 +248,8 @@ export default { onError: () => { this.$toast({ title: "Unable to mark tickets as closed.", - customIcon: "circle-fail", - appearance: "danger", + icon: "x", + iconClasses: "text-red-500", }) }, } @@ -263,8 +263,8 @@ export default { this.$toast({ title: "Tickets assigned to agent.", - customIcon: "circle-check", - appearance: "success", + icon: "check", + iconClasses: "text-green-500", }) this.$event.emit("update_ticket_list") @@ -272,8 +272,8 @@ export default { onError: () => { this.$toast({ title: "Unable to assign tickets to agent.", - customIcon: "circle-fail", - appearance: "danger", + icon: "x", + iconClasses: "text-red-500", }) }, } diff --git a/desk/src/components/desk/customers/CustomerInfo.vue b/desk/src/components/desk/customers/CustomerInfo.vue index e2ad4bdf4..a39fc9028 100644 --- a/desk/src/components/desk/customers/CustomerInfo.vue +++ b/desk/src/components/desk/customers/CustomerInfo.vue @@ -399,8 +399,8 @@ export default { onSuccess() { this.$toast({ title: "Customer updated successfully.", - appearance: "success", - customIcon: "circle-check", + icon: "check", + iconClasses: "text-green-500", }) }, }, @@ -437,8 +437,8 @@ export default { onSuccess: () => { this.$toast({ title: "Customer deleted", - customIcon: "circle-check", - appearance: "success", + icon: "check", + iconClasses: "text-green-500", }) this.resetForm() }, @@ -447,8 +447,8 @@ export default { this.$toast({ title: "Cannot delete customer", text: e, - customIcon: "circle-fail", - appearance: "danger", + icon: "x", + iconClasses: "text-red-500", }) }, }) @@ -468,8 +468,8 @@ export default { } else { this.$toast({ title: "Please fill all the fields", - customIcon: "circle-fail", - appearance: "danger", + icon: "x", + iconClasses: "text-red-500", }) } }, diff --git a/desk/src/components/desk/global/AddNewAgentsDialog.vue b/desk/src/components/desk/global/AddNewAgentsDialog.vue index c1df3386c..98ace8314 100644 --- a/desk/src/components/desk/global/AddNewAgentsDialog.vue +++ b/desk/src/components/desk/global/AddNewAgentsDialog.vue @@ -184,8 +184,8 @@ export default { this.$clearToasts() this.$toast({ title: "Invites Sent Successfully!", - customIcon: "circle-check", - appearance: "success", + icon: "check", + iconClasses: "text-green-500" }) this.close() @@ -195,15 +195,14 @@ export default { this.$toast({ title: "Paywall Reached!", text: "You have reached the maximum number of agents you can add. Please upgrade your plan to add more agents.", - customIcon: "circle-fail", - appearance: "danger", - fixed: true, + icon: "x", + iconClasses: "text-red-500", }) } else { this.$toast({ title: "Error Sending Invites!", - customIcon: "circle-fail", - appearance: "danger", + icon: "x", + iconClasses: "text-red-500", }) } }, diff --git a/desk/src/components/desk/global/AddNewCannedResponsesDialog.vue b/desk/src/components/desk/global/AddNewCannedResponsesDialog.vue index c127d2e9b..3e5fca00d 100644 --- a/desk/src/components/desk/global/AddNewCannedResponsesDialog.vue +++ b/desk/src/components/desk/global/AddNewCannedResponsesDialog.vue @@ -212,8 +212,8 @@ export default { this.$toast({ title: "Error while creating canned response", text: err, - customIcon: "circle-fail", - appearance: "danger", + icon: "x", + iconClasses: "text-red-500", }) }, } diff --git a/desk/src/components/desk/settings/agents/AgentInfo.vue b/desk/src/components/desk/settings/agents/AgentInfo.vue index a80b7ee70..3c0579190 100644 --- a/desk/src/components/desk/settings/agents/AgentInfo.vue +++ b/desk/src/components/desk/settings/agents/AgentInfo.vue @@ -196,8 +196,8 @@ export default { this.$toast({ title: "Agent Updated.", - customIcon: "circle-check", - appearance: "success", + icon: "check", + iconClasses: "text-green-500" }) this.$router.go() // TODO: fix this }, diff --git a/desk/src/components/desk/settings/canned_responses/CannedResponseInfo.vue b/desk/src/components/desk/settings/canned_responses/CannedResponseInfo.vue index 50b1ab6fd..91ccb254f 100644 --- a/desk/src/components/desk/settings/canned_responses/CannedResponseInfo.vue +++ b/desk/src/components/desk/settings/canned_responses/CannedResponseInfo.vue @@ -121,16 +121,16 @@ export default { onSuccess: () => { this.$toast({ title: "Canned Response Updated.", - customIcon: "circle-check", - appearance: "success", + icon: "check", + iconClasses: "text-green-500" }) }, onError: (err) => { this.$toast({ title: "Error while updating canned response", text: err, - customIcon: "circle-fail", - appearance: "danger", + icon: "x", + iconClasses: "text-red-500", }) }, }, diff --git a/desk/src/components/desk/settings/teams/TeamInfo.vue b/desk/src/components/desk/settings/teams/TeamInfo.vue index 5780dbb8e..a12ff4823 100644 --- a/desk/src/components/desk/settings/teams/TeamInfo.vue +++ b/desk/src/components/desk/settings/teams/TeamInfo.vue @@ -166,24 +166,24 @@ export default { this.$toast({ title: "Error Fetching Team", text: err, - customIcon: "circle-fail", - appearance: "danger", + icon: "x", + iconClasses: "text-red-500", }); }, setValue: { onSuccess: () => { this.$toast({ title: "Team Updated", - customIcon: "circle-check", - appearance: "success", + icon: "check", + iconClasses: "text-green-500" }); }, onError: (err) => { this.$toast({ title: "Error While Updating Ticket Type", text: err, - customIcon: "circle-fail", - appearance: "danger", + icon: "x", + iconClasses: "text-red-500", }); }, }, @@ -199,8 +199,8 @@ export default { this.$toast({ title: "New Team Created", - customIcon: "circle-check", - appearance: "success", + icon: "check", + iconClasses: "text-green-500", }); }, }; diff --git a/desk/src/components/desk/settings/ticket_types/TicketTypeInfo.vue b/desk/src/components/desk/settings/ticket_types/TicketTypeInfo.vue index edff49fc0..6e99bcfab 100644 --- a/desk/src/components/desk/settings/ticket_types/TicketTypeInfo.vue +++ b/desk/src/components/desk/settings/ticket_types/TicketTypeInfo.vue @@ -164,16 +164,16 @@ export default { onSuccess: () => { this.$toast({ title: "Ticket Type Updated.", - customIcon: "circle-check", - appearance: "success", + icon: "check", + iconClasses: "text-green-500", }) }, onError: (err) => { this.$toast({ title: "Error while updating ticket type", text: err, - customIcon: "circle-fail", - appearance: "danger", + icon: "x", + iconClasses: "text-red-500", }) }, }, diff --git a/desk/src/components/global/SaveFiltersDialog.vue b/desk/src/components/global/SaveFiltersDialog.vue index ee34b36cf..5fa69ad15 100644 --- a/desk/src/components/global/SaveFiltersDialog.vue +++ b/desk/src/components/global/SaveFiltersDialog.vue @@ -109,8 +109,8 @@ export default { onSuccess: (res) => { this.$toast({ title: "Filter Saved!", - customIcon: "circle-check", - appearance: "success", + icon: "check", + iconClasses: "text-green-500", }); this.close(); @@ -119,8 +119,8 @@ export default { this.$toast({ title: "Error Sending Invites!", text: err, - customIcon: "circle-fail", - appearance: "danger", + icon: "x", + iconClasses: "text-red-500", }); this.close(); diff --git a/desk/src/components/global/TicketField.vue b/desk/src/components/global/TicketField.vue index 7d5d57e4d..261088cdb 100644 --- a/desk/src/components/global/TicketField.vue +++ b/desk/src/components/global/TicketField.vue @@ -106,16 +106,16 @@ export default { onSuccess() { this.$toast({ title: "Ticket updated successfully.", - appearance: "success", - customIcon: "circle-check", + icon: "check", + iconClasses: "text-green-500", }) }, onError(err) { this.$toast({ title: "Error while updating ticket", text: err, - customIcon: "circle-fail", - appearance: "danger", + icon: "x", + iconClasses: "text-red-500", }) }, }, @@ -150,17 +150,17 @@ export default { url: "frappedesk.api.ticket.assign_ticket_to_agent", onSuccess: () => { this.$toast({ - title: "Agent assigned successfully.", - appearance: "success", - customIcon: "circle-check", + title: "Agent assigned successfully", + icon: "check", + iconClasses: "text-green-500", }) }, onError: (res) => { this.$toast({ title: "Error while assigning agent", text: res, - customIcon: "circle-fail", - appearance: "danger", + icon: "x", + iconClasses: "text-red-500", }) }, } @@ -182,8 +182,8 @@ export default { } else { this.$toast({ title: "Please fill all mandatory fields.", - customIcon: "circle-fail", - appearance: "danger", + icon: "x", + iconClasses: "text-red-500", }) } }, diff --git a/desk/src/components/global/Toast.vue b/desk/src/components/global/Toast.vue index d513a0ae2..d9ca2448e 100644 --- a/desk/src/components/global/Toast.vue +++ b/desk/src/components/global/Toast.vue @@ -18,7 +18,7 @@

{{ text }}

-
+