11<template >
2- <div class =" flex-col text-base" >
2+ <div class =" flex-col text-base flex-1 " ref = " commentBoxRef " >
33 <div class =" mb-1 ml-0.5 flex items-center justify-between" >
44 <div class =" text-gray-600 flex items-center gap-2" >
55 <Avatar
1717 </span >
1818 </p >
1919 </div >
20- <div class =" flex items-center" >
20+ <div class =" flex items-center gap-1 " >
2121 <Tooltip :text =" dateFormat (creation , dateTooltipFormat )" >
2222 <span class =" pl-0.5 text-sm text-gray-600" >
2323 {{ timeAgo(creation) }}
2424 </span >
2525 </Tooltip >
26- <div v-if =" authStore.userId === commentedBy" >
26+ <div v-if =" authStore.userId === commentedBy && !editable " >
2727 <Dropdown
28- :options =" [{ label: ' Delete' , onClick : () => (showDialog = true ) }] "
28+ :options =" [
29+ {
30+ label: ' Edit' ,
31+ onClick : () => handleEditMode (),
32+ icon: ' edit-2' ,
33+ },
34+ {
35+ label: ' Delete' ,
36+ onClick : () => (showDialog = true ),
37+ icon: ' trash-2' ,
38+ },
39+ ] "
2940 >
3041 <Button
3142 icon="more-horizontal"
3647 </div >
3748 </div >
3849 </div >
39- <div
40- class =" prose-f grow cursor-pointer rounded bg-gray-50 px-4 py-3 text-base leading-6 transition-all duration-300 ease-in-out"
41- v-html =" content"
42- />
50+ <div class =" rounded bg-gray-50 px-4 py-3" >
51+ <TextEditor
52+ ref="editorRef"
53+ :editor-class =" ' prose-f shrink text-p-sm transition-all duration-300 ease-in-out block w-full content' "
54+ :content =" _content "
55+ :editable =" editable "
56+ :bubble-menu =" textEditorMenuButtons "
57+ @change =" (event : string ) => {_content = event } "
58+ >
59+ <template #bottom v-if =" editable " >
60+ <div class =" flex flex-row-reverse gap-2" >
61+ <Button label="Save" @click =" handleSaveComment " variant="solid" />
62+ <Button label="Discard" @click =" handleDiscard " />
63+ </div >
64+ </template >
65+ </TextEditor >
66+ <div class =" flex flex-wrap gap-2" v-if =" !editable" >
67+ <AttachmentItem
68+ v-for =" a in attachments "
69+ :key =" a .file_url "
70+ :label =" a .file_name "
71+ :url =" a .file_url "
72+ />
73+ </div >
74+ </div >
4375 </div >
4476 <Dialog
4577 v-model =" showDialog "
5991</template >
6092
6193<script setup lang="ts">
62- import { ref } from " vue" ;
63- import { Dropdown , createResource , Dialog , Avatar } from " frappe-ui" ;
64- import { dateFormat , timeAgo , dateTooltipFormat , createToast } from " @/utils" ;
94+ import { ref , PropType , onMounted } from " vue" ;
95+ import {
96+ Dropdown ,
97+ createResource ,
98+ Dialog ,
99+ Avatar ,
100+ TextEditor ,
101+ } from " frappe-ui" ;
102+ import {
103+ dateFormat ,
104+ timeAgo ,
105+ dateTooltipFormat ,
106+ createToast ,
107+ textEditorMenuButtons ,
108+ isContentEmpty ,
109+ } from " @/utils" ;
110+ import { AttachmentItem } from " @/components" ;
65111import { useAuthStore } from " @/stores/auth" ;
66112import { useUserStore } from " @/stores/user" ;
67-
113+ import { CommentActivity } from " @/types" ;
114+ import { updateRes as updateComment } from " @/stores/knowledgeBase" ;
68115const authStore = useAuthStore ();
69116const props = defineProps ({
70117 activity: {
71- type: Object ,
118+ type: Object as PropType < CommentActivity > ,
72119 required: true ,
73120 },
74121});
75122const { getUser } = useUserStore ();
76123
77- const { name, creation, content, commenter, commentedBy } = props .activity ;
124+ const { name, creation, content, commenter, commentedBy, attachments } =
125+ props .activity ;
78126
79127const emit = defineEmits ([" update" ]);
80128const showDialog = ref (false );
129+ const editable = ref (false );
130+ const _content = ref (content );
131+
132+ // HTML refs
133+ const commentBoxRef = ref (null );
134+ const editorRef = ref (null );
135+
136+ function handleEditMode() {
137+ editable .value = true ;
138+ editorRef .value .editor .chain ().focus (" start" );
139+ }
140+
141+ function handleDiscard() {
142+ _content .value = content ;
143+ editable .value = false ;
144+ }
81145
82146const deleteComment = createResource ({
83147 url: " frappe.client.delete" ,
@@ -94,4 +158,43 @@ const deleteComment = createResource({
94158 });
95159 },
96160});
161+
162+ function handleSaveComment() {
163+ if (content === _content .value ) {
164+ editable .value = false ;
165+ return ;
166+ }
167+ if (isContentEmpty (_content .value )) {
168+ createToast ({
169+ title: " Comment cannot be empty" ,
170+ icon: " x" ,
171+ iconClasses: " text-red-600" ,
172+ });
173+ return ;
174+ }
175+
176+ updateComment .submit (
177+ {
178+ doctype: " HD Ticket Comment" ,
179+ name: name ,
180+ fieldname: " content" ,
181+ value: _content .value ,
182+ },
183+ {
184+ onSuccess : () => {
185+ editable .value = false ;
186+ emit (" update" );
187+ createToast ({
188+ title: " Comment updated" ,
189+ icon: " check" ,
190+ iconClasses: " text-green-500" ,
191+ });
192+ },
193+ }
194+ );
195+ }
196+
197+ onMounted (() => {
198+ commentBoxRef .value .style .width = " 0px" ;
199+ });
97200 </script >
0 commit comments