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