1
1
<template >
2
- <div class =" flex-col text-base" >
2
+ <div class =" flex-col text-base flex-1 " ref = " commentBoxRef " >
3
3
<div class =" mb-1 ml-0.5 flex items-center justify-between" >
4
4
<div class =" text-gray-600 flex items-center gap-2" >
5
5
<Avatar
17
17
</span >
18
18
</p >
19
19
</div >
20
- <div class =" flex items-center" >
20
+ <div class =" flex items-center gap-1 " >
21
21
<Tooltip :text =" dateFormat(creation, dateTooltipFormat)" >
22
22
<span class =" pl-0.5 text-sm text-gray-600" >
23
23
{{ timeAgo(creation) }}
24
24
</span >
25
25
</Tooltip >
26
- <div v-if =" authStore.userId === commentedBy" >
26
+ <div v-if =" authStore.userId === commentedBy && !editable " >
27
27
<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
+ ]"
29
40
>
30
41
<Button
31
42
icon =" more-horizontal"
36
47
</div >
37
48
</div >
38
49
</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 >
43
75
</div >
44
76
<Dialog
45
77
v-model =" showDialog"
59
91
</template >
60
92
61
93
<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" ;
65
111
import { useAuthStore } from " @/stores/auth" ;
66
112
import { useUserStore } from " @/stores/user" ;
67
-
113
+ import { CommentActivity } from " @/types" ;
114
+ import { updateRes as updateComment } from " @/stores/knowledgeBase" ;
68
115
const authStore = useAuthStore ();
69
116
const props = defineProps ({
70
117
activity: {
71
- type: Object ,
118
+ type: Object as PropType < CommentActivity > ,
72
119
required: true ,
73
120
},
74
121
});
75
122
const { getUser } = useUserStore ();
76
123
77
- const { name, creation, content, commenter, commentedBy } = props .activity ;
124
+ const { name, creation, content, commenter, commentedBy, attachments } =
125
+ props .activity ;
78
126
79
127
const emit = defineEmits ([" update" ]);
80
128
const 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
+ }
81
145
82
146
const deleteComment = createResource ({
83
147
url: " frappe.client.delete" ,
@@ -94,4 +158,43 @@ const deleteComment = createResource({
94
158
});
95
159
},
96
160
});
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
+ });
97
200
</script >
0 commit comments