From ac6584f3ac2eae5cbbc8f9e86ed2add21618939f Mon Sep 17 00:00:00 2001 From: RitvikSardana Date: Fri, 22 Nov 2024 19:49:46 +0530 Subject: [PATCH 01/22] fix: by default new settings should be enabled --- helpdesk/helpdesk/doctype/hd_settings/hd_settings.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/helpdesk/helpdesk/doctype/hd_settings/hd_settings.json b/helpdesk/helpdesk/doctype/hd_settings/hd_settings.json index 5bf9407ef..c7c53fef9 100644 --- a/helpdesk/helpdesk/doctype/hd_settings/hd_settings.json +++ b/helpdesk/helpdesk/doctype/hd_settings/hd_settings.json @@ -304,7 +304,7 @@ "fieldtype": "Section Break" }, { - "default": "0", + "default": "1", "description": "When enabled, the ticket status will automatically change to \"Replied\" whenever the agent responds to a ticket.\n", "fieldname": "auto_update_status", "fieldtype": "Check", @@ -316,7 +316,7 @@ "label": "Feedback" }, { - "default": "0", + "default": "1", "description": "If enabled, the feedback dialog will be shown, when a user tries to close a ticket. \nNote: User can't close a ticket without giving a feedback.", "fieldname": "is_feedback_mandatory", "fieldtype": "Check", @@ -325,7 +325,7 @@ ], "issingle": 1, "links": [], - "modified": "2024-11-22 17:25:40.112881", + "modified": "2024-11-22 19:47:41.543679", "modified_by": "Administrator", "module": "Helpdesk", "name": "HD Settings", From 4786399cc838c39209f327411dd438a436c53c0c Mon Sep 17 00:00:00 2001 From: RitvikSardana Date: Mon, 25 Nov 2024 09:24:39 +0530 Subject: [PATCH 02/22] feat: user feedback on article --- desk/src/pages/KnowledgeBaseArticle.vue | 138 +++++++++++++++--- desk/src/types.ts | 8 + helpdesk/helpdesk/doctype/hd_article/api.py | 19 +++ .../doctype/hd_article/hd_article.json | 17 ++- .../helpdesk/doctype/hd_article/hd_article.py | 15 ++ .../hd_article_feedback.js | 7 - .../hd_article_feedback.json | 50 ++----- .../test_hd_article_feedback.py | 9 -- 8 files changed, 183 insertions(+), 80 deletions(-) delete mode 100644 helpdesk/helpdesk/doctype/hd_article_feedback/hd_article_feedback.js delete mode 100644 helpdesk/helpdesk/doctype/hd_article_feedback/test_hd_article_feedback.py diff --git a/desk/src/pages/KnowledgeBaseArticle.vue b/desk/src/pages/KnowledgeBaseArticle.vue index 4165cc881..63b6d4b99 100644 --- a/desk/src/pages/KnowledgeBaseArticle.vue +++ b/desk/src/pages/KnowledgeBaseArticle.vue @@ -47,27 +47,64 @@ /> - - - - + + + + +
+ +
+ Did this article solve your issue? +
+
+ + {{ + articleFeedback?.total_likes + }} +
+
+ + {{ + articleFeedback?.total_dislikes + }} +
+
+
+
+ @@ -102,7 +139,8 @@ import KnowledgeBaseArticleTopEdit from "./knowledge-base/KnowledgeBaseArticleTo import KnowledgeBaseArticleTopNew from "./knowledge-base/KnowledgeBaseArticleTopNew.vue"; import KnowledgeBaseArticleTopView from "./knowledge-base/KnowledgeBaseArticleTopView.vue"; import { PreserveIds } from "@/tiptap-extensions"; - +import { Icon } from "@iconify/vue"; +import { ArticleFeedback, Feedback } from "@/types"; const props = defineProps({ articleId: { type: String, @@ -146,7 +184,7 @@ const breadcrumbs = computed(() => { }, }, ]; - // if (options__.value.subCategoryId !== options__.value.categoryId) { + agentPortalItems.push({ label: options__.value.subCategoryName, route: { @@ -158,7 +196,6 @@ const breadcrumbs = computed(() => { }, }, }); - // } if (!isNew) { agentPortalItems.push({ @@ -224,6 +261,7 @@ const topComponent = computed(() => { return KnowledgeBaseArticleTopView; }); +const articleFeedback = ref(null); const article = createResource({ url: "helpdesk.helpdesk.doctype.hd_article.api.get_article", params: { @@ -231,6 +269,7 @@ const article = createResource({ }, onSuccess(data) { articleTitle.value = data.title; + articleFeedback.value = data.feedbacks; capture("article_viewed", { data: { user: authStore.userId, @@ -242,6 +281,59 @@ const article = createResource({ auto: !isNew, }); +const setFeedback = createResource({ + url: "run_doc_method", + debounce: 300, + makeParams: () => ({ + dt: "HD Article", + dn: props.articleId, + method: "set_feedback", + args: { + value: userAction.value, + }, + }), + onSuccess: () => { + article.reload(); + }, +}); + +const userAction = ref(""); +function handleFeedbackClick(action: Feedback) { + if ( + articleFeedback.value.user_feedback && + articleFeedback.value.user_feedback === action + ) { + articleFeedback.value.user_feedback = null; + + userAction.value = ""; + } else { + articleFeedback.value.user_feedback = action; + userAction.value = action; + } + handleOptimisticUpdate(action); + + setFeedback.submit(); +} + +function handleOptimisticUpdate(action: Feedback) { + if (action === "Like" && action === articleFeedback.value.user_feedback) { + articleFeedback.value.total_likes++; + articleFeedback.value.total_dislikes > 0 && + articleFeedback.value.total_dislikes--; + } else if ( + action === "Dislike" && + action === articleFeedback.value.user_feedback + ) { + articleFeedback.value.total_dislikes++; + articleFeedback.value.total_likes > 0 && + articleFeedback.value.total_likes--; + } else if (action == "Like" && !articleFeedback.value.user_feedback) { + articleFeedback.value.total_likes--; + } else if (action == "Dislike" && !articleFeedback.value.user_feedback) { + articleFeedback.value.total_dislikes--; + } +} + const category = createDocumentResource({ doctype: "HD Article Category", name: categoryId.value, diff --git a/desk/src/types.ts b/desk/src/types.ts index 97d40f0fc..d428705da 100644 --- a/desk/src/types.ts +++ b/desk/src/types.ts @@ -246,3 +246,11 @@ export interface Category { }; children?: (Article | SubCategory)[]; } + +export interface ArticleFeedback { + user_feedback: string; + total_likes: number; + total_dislikes: number; +} + +export type Feedback = "Like" | "Dislike" | ""; diff --git a/helpdesk/helpdesk/doctype/hd_article/api.py b/helpdesk/helpdesk/doctype/hd_article/api.py index 84be868b1..5e07733f3 100644 --- a/helpdesk/helpdesk/doctype/hd_article/api.py +++ b/helpdesk/helpdesk/doctype/hd_article/api.py @@ -17,9 +17,28 @@ def get_article(name: str): "HD Article Category", sub_category.parent_category or article["category"] ) + user = frappe.session.user + # TODO: views count increment with views field in HD Article + # if not is_agent() and user != author.name: + # frappe.db.set_value("HD Article", name, "views", article["views"] + 1) + + feedbacks = { + "user_feedback": frappe.db.get_value( + "HD Article Feedback", {"user": user, "parent": name}, "feedback" + ) + or None, + "total_likes": frappe.db.count( + "HD Article Feedback", {"parent": name, "feedback": "Like"} + ), + "total_dislikes": frappe.db.count( + "HD Article Feedback", {"parent": name, "feedback": "Dislike"} + ), + } + return { **article, "author": author, "category": category, "sub_category": sub_category, + "feedbacks": feedbacks, } diff --git a/helpdesk/helpdesk/doctype/hd_article/hd_article.json b/helpdesk/helpdesk/doctype/hd_article/hd_article.json index d8b2db488..d86c6d703 100644 --- a/helpdesk/helpdesk/doctype/hd_article/hd_article.json +++ b/helpdesk/helpdesk/doctype/hd_article/hd_article.json @@ -17,7 +17,9 @@ "subtitle", "article_image", "content_section", - "content" + "content", + "feedbacks_section", + "feedbacks" ], "fields": [ { @@ -97,11 +99,22 @@ "fieldname": "subtitle", "fieldtype": "Small Text", "label": "Subtitle" + }, + { + "fieldname": "feedbacks_section", + "fieldtype": "Section Break", + "label": "Feedbacks" + }, + { + "fieldname": "feedbacks", + "fieldtype": "Table", + "label": "feedbacks", + "options": "HD Article Feedback" } ], "links": [], "make_attachments_public": 1, - "modified": "2024-10-01 21:39:58.189965", + "modified": "2024-11-24 22:54:55.477514", "modified_by": "Administrator", "module": "Helpdesk", "name": "HD Article", diff --git a/helpdesk/helpdesk/doctype/hd_article/hd_article.py b/helpdesk/helpdesk/doctype/hd_article/hd_article.py index c0e296c6c..b29b3c6d1 100644 --- a/helpdesk/helpdesk/doctype/hd_article/hd_article.py +++ b/helpdesk/helpdesk/doctype/hd_article/hd_article.py @@ -52,6 +52,21 @@ def before_save(self): ) ) + @frappe.whitelist() + def set_feedback(self, value): + user = frappe.session.user + feedback_exists = frappe.db.exists( + "HD Article Feedback", {"user": user, "parent": self.name} + ) + if feedback_exists: + frappe.db.set_value( + "HD Article Feedback", feedback_exists, "feedback", value + ) + return + + self.append("feedbacks", {"user": user, "feedback": value}) + self.save() + @property def title_slug(self) -> str: """ diff --git a/helpdesk/helpdesk/doctype/hd_article_feedback/hd_article_feedback.js b/helpdesk/helpdesk/doctype/hd_article_feedback/hd_article_feedback.js deleted file mode 100644 index ddb38d38d..000000000 --- a/helpdesk/helpdesk/doctype/hd_article_feedback/hd_article_feedback.js +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) 2022, Frappe Technologies and contributors -// For license information, please see license.txt - -frappe.ui.form.on("HD Article Feedback", { - // refresh: function(frm) { - // } -}); diff --git a/helpdesk/helpdesk/doctype/hd_article_feedback/hd_article_feedback.json b/helpdesk/helpdesk/doctype/hd_article_feedback/hd_article_feedback.json index 1114e3462..6e10b653e 100644 --- a/helpdesk/helpdesk/doctype/hd_article_feedback/hd_article_feedback.json +++ b/helpdesk/helpdesk/doctype/hd_article_feedback/hd_article_feedback.json @@ -1,67 +1,39 @@ { "actions": [], "allow_rename": 1, - "creation": "2022-10-18 23:01:11.913095", + "creation": "2024-11-24 18:33:16.603719", "doctype": "DocType", - "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "article", "user", "feedback" ], "fields": [ - { - "fieldname": "article", - "fieldtype": "Link", - "label": "Article", - "options": "HD Article" - }, { "fieldname": "user", "fieldtype": "Link", + "in_list_view": 1, "label": "User", "options": "User" }, { "fieldname": "feedback", - "fieldtype": "Int", - "label": "Feedback" + "fieldtype": "Select", + "in_list_view": 1, + "label": "Feedback", + "options": "\nLike\nDislike" } ], "index_web_pages_for_search": 1, + "istable": 1, "links": [], - "modified": "2023-03-26 23:06:42.029313", + "modified": "2024-11-24 18:46:11.731463", "modified_by": "Administrator", "module": "Helpdesk", "name": "HD Article Feedback", "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - }, - { - "email": 1, - "export": 1, - "if_owner": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "All", - "share": 1, - "write": 1 - } - ], - "sort_field": "modified", + "permissions": [], + "sort_field": "creation", "sort_order": "DESC", "states": [] -} +} \ No newline at end of file diff --git a/helpdesk/helpdesk/doctype/hd_article_feedback/test_hd_article_feedback.py b/helpdesk/helpdesk/doctype/hd_article_feedback/test_hd_article_feedback.py deleted file mode 100644 index 4083e9b31..000000000 --- a/helpdesk/helpdesk/doctype/hd_article_feedback/test_hd_article_feedback.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2022, Frappe Technologies and Contributors -# See license.txt - -# import frappe -from frappe.tests.utils import FrappeTestCase - - -class TestHDArticleFeedback(FrappeTestCase): - pass From ef8da5f8e41139647192b712b923f779eae5116b Mon Sep 17 00:00:00 2001 From: RitvikSardana Date: Mon, 25 Nov 2024 11:38:05 +0530 Subject: [PATCH 03/22] chore: rename feedbacks variable to feedback --- helpdesk/helpdesk/doctype/hd_article/api.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/helpdesk/helpdesk/doctype/hd_article/api.py b/helpdesk/helpdesk/doctype/hd_article/api.py index 5e07733f3..0547c5cac 100644 --- a/helpdesk/helpdesk/doctype/hd_article/api.py +++ b/helpdesk/helpdesk/doctype/hd_article/api.py @@ -22,7 +22,7 @@ def get_article(name: str): # if not is_agent() and user != author.name: # frappe.db.set_value("HD Article", name, "views", article["views"] + 1) - feedbacks = { + feedback = { "user_feedback": frappe.db.get_value( "HD Article Feedback", {"user": user, "parent": name}, "feedback" ) @@ -34,11 +34,12 @@ def get_article(name: str): "HD Article Feedback", {"parent": name, "feedback": "Dislike"} ), } + article["feedbacks"] = None return { **article, "author": author, "category": category, "sub_category": sub_category, - "feedbacks": feedbacks, + "feedback": feedback, } From 5a75099232b11df1d98f7aff5335702da0848a77 Mon Sep 17 00:00:00 2001 From: RitvikSardana Date: Mon, 25 Nov 2024 11:38:28 +0530 Subject: [PATCH 04/22] fix: show article's heading --- desk/src/pages/KnowledgeBaseArticle.vue | 9 ++++----- .../pages/knowledge-base/KnowledgeBaseArticleTopView.vue | 7 ------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/desk/src/pages/KnowledgeBaseArticle.vue b/desk/src/pages/KnowledgeBaseArticle.vue index 63b6d4b99..4d1805ec9 100644 --- a/desk/src/pages/KnowledgeBaseArticle.vue +++ b/desk/src/pages/KnowledgeBaseArticle.vue @@ -33,13 +33,12 @@ editor-class="prose-f" @change="articleContent = $event" > -