main
vue 217 lines 5.4 KB
Raw
1 <template>
2 <div class="alert-comment-item flex gap-3" :class="{ embedded }">
3 <div v-if="userPic" class="user-pic">
4 <n-avatar round :size="32" :src="userPic" />
5 </div>
6 <div class="comment flex grow flex-col gap-1 overflow-hidden">
7 <div class="user flex items-center gap-3">
8 <div class="user-name">
9 {{ comment.user_name }}
10 </div>
11 <div class="comment-time">
12 {{ formatDate(comment.created_at, dFormats.datetime) }}
13 </div>
14 </div>
15 <div v-if="mode === 'view'" class="comment-message">
16 <Markdown :source="comment.comment" />
17 </div>
18
19 <n-input
20 v-if="mode === 'edit'"
21 v-model:value="commentModel"
22 type="textarea"
23 :disabled="saving"
24 placeholder="Insert the updated comment"
25 size="large"
26 :autosize="{
27 minRows: 3,
28 maxRows: 18
29 }"
30 />
31
32 <div class="comment-actions flex justify-end gap-1">
33 <template v-if="mode === 'view'">
34 <n-button size="tiny" secondary :disabled="canceling" @click="editComment()">
35 <template #icon>
36 <Icon :name="EditIcon" :size="12" />
37 </template>
38 <span>Edit</span>
39 </n-button>
40 <n-popconfirm to="body" @positive-click="deleteAlertComment()">
41 <template #trigger>
42 <n-button size="tiny" secondary type="error" :loading="canceling">
43 <template #icon>
44 <Icon :name="DeleteIcon" :size="12" />
45 </template>
46 <span>Delete</span>
47 </n-button>
48 </template>
49 Are you sure you want to delete the comment?
50 </n-popconfirm>
51 </template>
52 <template v-if="mode === 'edit'">
53 <n-button size="tiny" secondary :disabled="saving" @click="setMode('view')">
54 <template #icon>
55 <Icon :name="ArrowLeftIcon" :size="12" />
56 </template>
57 <span>Cancel</span>
58 </n-button>
59
60 <n-button
61 size="tiny"
62 secondary
63 type="success"
64 :loading="saving"
65 :disabled="!commentModel"
66 @click="updateAlertComment()"
67 >
68 <template #icon>
69 <Icon :name="SaveIcon" :size="13" />
70 </template>
71 <span>Save</span>
72 </n-button>
73 </template>
74 </div>
75 </div>
76 </div>
77 </template>
78
79 <script setup lang="ts">
80 import type { AlertComment } from "@/types/incidentManagement/alerts.d"
81 import { NAvatar, NButton, NInput, NPopconfirm, useMessage } from "naive-ui"
82 import { defineAsyncComponent, onBeforeMount, ref, toRefs } from "vue"
83 import Api from "@/api"
84 import Icon from "@/components/common/Icon.vue"
85 import { useSettingsStore } from "@/stores/settings"
86 import { getAvatar, getNameInitials } from "@/utils"
87 import { formatDate } from "@/utils/format"
88
89 type Mode = "view" | "edit"
90
91 const props = defineProps<{ comment: AlertComment; embedded?: boolean }>()
92
93 const emit = defineEmits<{
94 (e: "deleted"): void
95 (e: "updated", value: AlertComment): void
96 }>()
97
98 const Markdown = defineAsyncComponent(() => import("@/components/common/Markdown.vue"))
99
100 const { comment, embedded } = toRefs(props)
101
102 const ArrowLeftIcon = "carbon:arrow-left"
103 const SaveIcon = "carbon:save"
104 const EditIcon = "uil:edit-alt"
105 const DeleteIcon = "ph:trash"
106 const mode = ref<Mode>("view")
107 const canceling = ref(false)
108 const saving = ref(false)
109 const dFormats = useSettingsStore().dateFormat
110 const userPic = ref("")
111 const commentModel = ref(comment.value.comment)
112 const message = useMessage()
113
114 function setMode(newMode: Mode) {
115 mode.value = newMode
116 }
117
118 function editComment() {
119 setMode("edit")
120 commentModel.value = comment.value.comment
121 }
122
123 function updateAlertComment() {
124 saving.value = true
125
126 Api.incidentManagement.alerts
127 .updateAlertComment({
128 alert_id: comment.value.alert_id,
129 comment_id: comment.value.id,
130 comment: commentModel.value,
131 created_at: new Date(),
132 user_name: comment.value.user_name
133 })
134 .then(res => {
135 if (res.data.success) {
136 message.success(res.data?.message || "Comment updated successfully")
137 setMode("view")
138 emit("updated", res.data.comment)
139 } else {
140 message.warning(res.data?.message || "An error occurred. Please try again later.")
141 }
142 })
143 .catch(err => {
144 message.error(err.response?.data?.message || "An error occurred. Please try again later.")
145 })
146 .finally(() => {
147 saving.value = false
148 })
149 }
150
151 function deleteAlertComment() {
152 canceling.value = true
153
154 Api.incidentManagement.alerts
155 .deleteAlertComment(comment.value.id)
156 .then(res => {
157 if (res.data.success) {
158 message.success(res.data?.message || "Comment deleted successfully")
159 emit("deleted")
160 } else {
161 message.warning(res.data?.message || "An error occurred. Please try again later.")
162 }
163 })
164 .catch(err => {
165 message.error(err.response?.data?.message || "An error occurred. Please try again later.")
166 })
167 .finally(() => {
168 canceling.value = false
169 })
170 }
171
172 onBeforeMount(() => {
173 const initials = getNameInitials(comment.value.user_name)
174 userPic.value = getAvatar({ seed: initials, text: initials, size: 64 })
175 })
176 </script>
177
178 <style lang="scss" scoped>
179 .alert-comment-item {
180 width: 100%;
181
182 .user-pic {
183 padding-top: 2px;
184 }
185
186 .comment {
187 .user {
188 margin-left: 2px;
189
190 .user-name {
191 font-weight: 600;
192 }
193
194 .comment-time {
195 font-size: 11px;
196 color: var(--fg-secondary-color);
197 font-family: var(--font-family-mono);
198 }
199 }
200
201 .comment-message {
202 border-radius: var(--border-radius);
203 background-color: var(--bg-default-color);
204 border: 1px solid var(--border-color);
205 padding: 6px 10px;
206 }
207 }
208
209 &.embedded {
210 .comment {
211 .comment-message {
212 background-color: var(--bg-secondary-color);
213 }
214 }
215 }
216 }
217 </style>