@quidgest/chatbot 0.0.9 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/style.css CHANGED
@@ -1 +1 @@
1
- .q-chatbot{width:100%;padding:1rem;display:flex;flex-direction:column;height:100%}.q-chatbot input{line-height:1.5rem}.q-chatbot .q-input-group .i-text__field{border-radius:0;flex:1}.q-chatbot__content{background-color:#fff;border:1px solid #eaebec;height:100%;width:100%;display:flex;gap:.75rem;flex-direction:column;overflow:hidden}.q-chatbot__footer-container{padding:.8rem}.q-chatbot__send-container{padding-bottom:.25rem;display:flex;justify-content:flex-end}.q-chatbot__send-container .q-chatbot__send{border-radius:1rem}.q-chatbot__footer{position:sticky;padding:0 .5rem;border:1px solid #eaebec;border-radius:.25rem;bottom:0;width:100%;display:flex;flex-direction:column;gap:.25rem}.q-chatbot__footer-disabled{background-color:rgb(var(--q-theme-neutral-light-rgb)/.25);cursor:not-allowed}.q-chatbot__footer .q-chatbot__input{min-height:50px;max-height:100px;border-bottom:1px solid #eaebec;overflow-y:auto}.q-chatbot__footer .q-text-area{max-height:100%;overflow-y:auto}.q-chatbot__footer .q-text-area .q-field__control{border:none}.q-chatbot__messages-container{display:flex;flex-direction:column;flex-grow:1;padding:0 1rem 2rem;gap:1.5rem;overflow-y:auto}.q-chatbot__messages-wrapper{display:flex;max-width:100%;gap:.2rem}.q-chatbot__tools{display:flex;flex-direction:row;justify-content:end;max-width:100%}.q-chatbot__message-wrapper{display:flex;flex-direction:column;gap:.2rem}.q-chatbot__message-container{display:flex;flex-direction:column;gap:.25rem}.q-chatbot__messages-wrapper_right{justify-content:flex-end}.q-chatbot__messages-wrapper_right .q-chatbot__message-container{align-items:flex-end}.q-chatbot__messages-wrapper_right .q-chatbot__message-wrapper{display:flex;align-items:flex-end}.q-chatbot__profile.q-icon__img{border-radius:50%;height:2rem;width:2rem}.q-chatbot__message{display:flex;align-items:center;padding:.3rem .5rem;background-color:#eaebec;width:fit-content;white-space:normal;min-height:2rem;word-wrap:break-word;word-break:break-word;border-radius:0 .5rem .5rem}.q-chatbot__messages-wrapper_right .q-chatbot__message{background-color:#018bd233;border-radius:.5rem 0 .5rem .5rem}.q-chatbot__sender{white-space:nowrap;color:#7c858d;font-size:.7rem}.pulsing-dots{display:flex;align-items:center;justify-content:center;gap:.1rem}.dot{font-size:20px;line-height:1;animation:pulse 1s infinite;color:var(--q-theme-primary)}@keyframes pulse{0%,to{transform:scale(.8);opacity:.6}50%{transform:scale(1);opacity:1}}
1
+ .q-chatbot{width:100%;padding:1rem;display:flex;flex-direction:column;height:100%}.q-chatbot input{line-height:1.5rem}.q-chatbot .q-input-group .i-text__field{border-radius:0;flex:1}.q-chatbot__text p{margin:0}.q-chatbot__content{background-color:#fff;border:1px solid #eaebec;height:100%;width:100%;display:flex;gap:.75rem;flex-direction:column;overflow:hidden}.q-chatbot__footer-container{padding:.8rem}.q-chatbot__input-wrapper{display:flex;flex-direction:column;position:relative}.q-chatbot__image-preview{display:inline-flex;align-items:center;position:relative;margin-top:.5rem;width:fit-content}.q-chatbot__image-preview img{width:60px;height:60px;object-fit:cover;border-radius:4px;margin-right:.25rem;border:1px solid #eaebec;overflow:hidden}.q-chatbot__image-preview img:hover+.q-chatbot__remove-image,.q-chatbot__image-preview img:focus+.q-chatbot__remove-image{opacity:1;pointer-events:auto}.q-chatbot__image-preview img:focus{outline:solid rgb(var(--q-theme-info-rgb)/50%)}.q-chatbot .q-btn.q-chatbot__remove-image{position:absolute;top:-5px;right:-5px;background-color:#00000080;color:#fff;border-radius:50%;padding:5px;font-size:12px;border:none;opacity:0;pointer-events:none;transition:opacity .2s ease-in-out}.q-chatbot .q-btn.q-chatbot__remove-image:hover,.q-chatbot .q-btn.q-chatbot__remove-image:focus{opacity:1;pointer-events:auto}.q-chatbot__send-container{padding-bottom:.25rem;display:flex;justify-content:space-between;width:100%}.q-chatbot__send-container .q-chatbot__send,.q-chatbot__send-container .q-chatbot__upload{border-radius:1rem}.q-chatbot__send-container .spacer{flex-grow:1}.q-chatbot__footer{position:sticky;padding:0 .5rem;border:1px solid #eaebec;border-radius:.25rem;bottom:0;width:100%;display:flex;flex-direction:column;gap:.25rem}.q-chatbot__footer-disabled{background-color:rgb(var(--q-theme-neutral-light-rgb)/.25);cursor:not-allowed}.q-chatbot__footer.drag-over{border:2px dashed rgb(var(--q-theme-primary-rgb)/.25);background-color:#018bd20d}.q-chatbot__footer .q-chatbot__input{min-height:50px;max-height:100px;border-bottom:1px solid #eaebec;overflow-y:auto}.q-chatbot__footer .q-text-area{max-height:100%;overflow-y:auto}.q-chatbot__footer .q-text-area .q-field__control{border:none}.q-chatbot__upload-container{display:flex;justify-content:flex-start;padding:.25rem 0}.q-chatbot__upload-container .q-chatbot__upload{border-radius:1rem}.q-chatbot__messages-container{display:flex;flex-direction:column;flex-grow:1;padding:0 1rem 2rem;gap:1.5rem;overflow-y:auto}.q-chatbot__messages-wrapper{display:flex;max-width:100%;gap:.2rem}.q-chatbot__tools{display:flex;flex-direction:row;justify-content:end;max-width:100%}.q-chatbot__message-wrapper{display:flex;flex-direction:column;gap:.2rem}.q-chatbot__message-container{display:flex;flex-direction:column;gap:.25rem}.q-chatbot__messages-wrapper_right{justify-content:flex-end}.q-chatbot__messages-wrapper_right .q-chatbot__message-container{align-items:flex-end}.q-chatbot__messages-wrapper_right .q-chatbot__message-wrapper{display:flex;align-items:flex-end}.q-chatbot__profile.q-icon__img{border-radius:50%;height:2rem;width:2rem}.q-chatbot__message{display:flex;align-items:center;padding:.3rem .5rem;background-color:#eaebec;width:fit-content;white-space:normal;min-height:2rem;word-wrap:break-word;word-break:break-word;border-radius:0 .5rem .5rem}.q-chatbot__messages-wrapper_right .q-chatbot__message{background-color:#018bd233;border-radius:.5rem 0 .5rem .5rem}.q-chatbot__sender{white-space:nowrap;color:#7c858d;font-size:.7rem}.q-chatbot__retry-button{align-items:center;display:flex}.q-chatbot__dialog-title{margin:.5rem 0}#comment-dialog .q-dialog__header,.hidden-input{display:none}.pulsing-dots{display:flex;align-items:center;justify-content:center;gap:.1rem}.dot{font-size:20px;line-height:1;animation:pulse 1s infinite;color:var(--q-theme-primary)}@keyframes pulse{0%,to{transform:scale(.8);opacity:.6}50%{transform:scale(1);opacity:1}}
@@ -2,6 +2,7 @@ import { ResourceStrings } from './texts.type';
2
2
 
3
3
  export type ChatBotProps = {
4
4
  apiEndpoint?: string;
5
+ controllerEndpoint?: string;
5
6
  texts?: ResourceStrings;
6
7
  username: string;
7
8
  projectPath: string;
@@ -3,9 +3,32 @@ export type ChatBotMessage = {
3
3
  message: string;
4
4
  date: Date;
5
5
  sender: ChatBotMessageSender;
6
+ sessionID: string;
7
+ imagePreviewUrl?: string;
8
+ isWelcomeMessage?: boolean;
6
9
  };
7
10
  export type ChatBotMessageContent = {
8
11
  content: string;
9
12
  type: string;
13
+ sessionID: string;
14
+ imageUrl?: string;
10
15
  };
11
16
  export type ChatBotMessageSender = 'bot' | 'user';
17
+ export interface CBMessageProps {
18
+ sender?: ChatBotMessageSender;
19
+ message?: string;
20
+ date?: Date;
21
+ loading?: boolean;
22
+ /**
23
+ * Project locale
24
+ */
25
+ dateFormat?: string;
26
+ /**
27
+ * User image
28
+ */
29
+ userImage: string;
30
+ /**
31
+ * Chatbot image
32
+ */
33
+ chatbotImage: string;
34
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@quidgest/chatbot",
3
3
  "private": false,
4
- "version": "0.0.9",
4
+ "version": "0.2.0",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
7
7
  "main": "dist/index.cjs",
@@ -32,20 +32,23 @@
32
32
  "lint:fix": "eslint . --fix && prettier --write --list-different src"
33
33
  },
34
34
  "dependencies": {
35
- "axios": "^1.6.7"
35
+ "axios": "^1.7.0",
36
+ "uuid": "^11.1.0",
37
+ "vue-markdown-render": "^2.2.1"
36
38
  },
37
39
  "devDependencies": {
40
+ "@types/markdown-it": "^14.1.2",
38
41
  "@types/node": "^20.11.13",
39
42
  "@vitejs/plugin-vue": "^4.5.2",
40
- "eslint": "^9.8.0",
43
+ "eslint": "^9.9.0",
41
44
  "eslint-plugin-prettier": "^5.2.1",
42
45
  "prettier": "^3.3.3",
43
- "rimraf": "^5.0.5",
44
- "sass": "^1.70.0",
45
- "typescript": "^5.2.2",
46
- "vite": "^5.0.8",
47
- "vite-plugin-dts": "^3.7.3",
48
- "vue-tsc": "^1.8.25"
46
+ "rimraf": "^5.0.10",
47
+ "sass": "^1.77.8",
48
+ "typescript": "^5.5.4",
49
+ "vite": "^5.4.2",
50
+ "vite-plugin-dts": "^3.9.1",
51
+ "vue-tsc": "^1.8.27"
49
52
  },
50
53
  "peerDependencies": {
51
54
  "@quidgest/ui": "^0.14.19",
@@ -15,6 +15,12 @@
15
15
  flex: 1;
16
16
  }
17
17
 
18
+ &__text {
19
+ p {
20
+ margin: 0;
21
+ }
22
+ }
23
+
18
24
  &__content {
19
25
  background-color: white;
20
26
  border: 1px solid #eaebec;
@@ -27,53 +33,135 @@
27
33
  overflow: hidden;
28
34
  }
29
35
 
30
-
31
36
  &__footer-container {
32
37
  padding: 0.8rem;
33
38
  }
34
39
 
40
+ &__input-wrapper {
41
+ display: flex;
42
+ flex-direction: column;
43
+ position: relative;
44
+ }
45
+
46
+ &__image-preview {
47
+ display: inline-flex;
48
+ align-items: center;
49
+ position: relative;
50
+ margin-top: 0.5rem;
51
+ width: fit-content;
52
+
53
+ img {
54
+ width: 60px;
55
+ height: 60px;
56
+ object-fit: cover;
57
+ border-radius: 4px;
58
+ margin-right: 0.25rem;
59
+ border: 1px solid #eaebec;
60
+ overflow: hidden;
61
+
62
+ &:hover,
63
+ &:focus {
64
+
65
+ & + .q-chatbot__remove-image {
66
+ opacity: 1;
67
+ pointer-events: auto;
68
+ }
69
+ }
70
+
71
+ &:focus {
72
+ outline: solid rgb(var(--q-theme-info-rgb) / 50%);
73
+ }
74
+ }
75
+ }
76
+
77
+ .q-btn.q-chatbot__remove-image {
78
+ position: absolute;
79
+ top: -5px;
80
+ right: -5px;
81
+ background-color: rgba(0, 0, 0, 0.5);
82
+ color: white;
83
+ border-radius: 50%;
84
+ padding: 5px;
85
+ font-size: 12px;
86
+ border: none;
87
+ opacity: 0;
88
+ pointer-events: none;
89
+ transition: opacity 0.2s ease-in-out;
90
+
91
+ &:hover,
92
+ &:focus {
93
+ opacity: 1;
94
+ pointer-events: auto;
95
+ }
96
+ }
97
+
35
98
  &__send-container {
36
- padding-bottom: .25rem;
99
+ padding-bottom: 0.25rem;
37
100
  display: flex;
38
- justify-content: flex-end;
101
+ justify-content: space-between;
102
+ width: 100%;
39
103
 
40
104
  & .q-chatbot__send {
41
105
  border-radius: 1rem;
42
106
  }
107
+
108
+ & .q-chatbot__upload {
109
+ border-radius: 1rem;
110
+ }
111
+
112
+ & .spacer {
113
+ flex-grow: 1;
114
+ }
43
115
  }
44
-
116
+
45
117
  &__footer {
46
118
  position: sticky;
47
- padding: 0 .5rem;
119
+ padding: 0 0.5rem;
48
120
  border: 1px solid #eaebec;
49
- border-radius: .25rem;
121
+ border-radius: 0.25rem;
50
122
  bottom: 0;
51
123
  width: 100%;
52
124
  display: flex;
53
125
  flex-direction: column;
54
- gap: .25rem;
126
+ gap: 0.25rem;
55
127
 
56
128
  &-disabled {
57
129
  background-color: rgb(var(--q-theme-neutral-light-rgb) / 0.25);
58
130
  cursor: not-allowed;
59
131
  }
60
-
132
+
133
+ &.drag-over {
134
+ border: 2px dashed rgb(var(--q-theme-primary-rgb) / 0.25);
135
+ background-color: rgba(1, 139, 210, 0.05);
136
+ }
137
+
61
138
  & .q-chatbot__input {
62
- min-height: 50px;
63
- max-height: 100px;
64
- border-bottom: 1px solid #eaebec;
65
- overflow-y: auto;
139
+ min-height: 50px;
140
+ max-height: 100px;
141
+ border-bottom: 1px solid #eaebec;
142
+ overflow-y: auto;
66
143
  }
67
-
144
+
68
145
  & .q-text-area {
69
146
  max-height: 100%;
70
147
  overflow-y: auto;
71
-
148
+
72
149
  & .q-field__control {
73
150
  border: none;
74
151
  }
75
152
  }
76
- }
153
+ }
154
+
155
+ &__upload-container {
156
+ display: flex;
157
+ justify-content: flex-start;
158
+ padding: 0.25rem 0;
159
+
160
+ & .q-chatbot__upload {
161
+ border-radius: 1rem;
162
+ }
163
+ }
164
+
77
165
  &__messages-container {
78
166
  display: flex;
79
167
  flex-direction: column;
@@ -141,12 +229,12 @@
141
229
  min-height: 2rem;
142
230
  word-wrap: break-word;
143
231
  word-break: break-word;
144
- border-radius: 0 0.5rem 0.5rem .5rem;
232
+ border-radius: 0 0.5rem 0.5rem 0.5rem;
145
233
  }
146
234
 
147
235
  &__messages-wrapper_right .q-chatbot__message {
148
236
  background-color: rgba(#018bd2, 20%);
149
- border-radius: 0.5rem 0 0.5rem .5rem;
237
+ border-radius: 0.5rem 0 0.5rem 0.5rem;
150
238
  }
151
239
 
152
240
  &__sender {
@@ -154,29 +242,49 @@
154
242
  color: #7c858d;
155
243
  font-size: 0.7rem;
156
244
  }
245
+
246
+ &__retry-button {
247
+ align-items: center;
248
+ display: flex;
249
+ }
250
+
251
+ &__dialog-title{
252
+ margin: 0.5rem 0;
253
+ }
254
+ }
255
+
256
+ #comment-dialog {
257
+ .q-dialog__header {
258
+ display: none;
259
+ }
260
+ }
261
+
262
+ .hidden-input {
263
+ display: none;
157
264
  }
158
265
 
159
266
  .pulsing-dots {
160
- display: flex;
161
- align-items: center;
162
- justify-content: center;
163
- gap: 0.1rem;
267
+ display: flex;
268
+ align-items: center;
269
+ justify-content: center;
270
+ gap: 0.1rem;
164
271
  }
165
272
 
166
273
  .dot {
167
- font-size: 20px;
168
- line-height: 1;
169
- animation: pulse 1s infinite;
170
- color: var(--q-theme-primary);
274
+ font-size: 20px;
275
+ line-height: 1;
276
+ animation: pulse 1s infinite;
277
+ color: var(--q-theme-primary);
171
278
  }
172
279
 
173
280
  @keyframes pulse {
174
- 0%, 100% {
175
- transform: scale(0.8);
176
- opacity: 0.6;
281
+ 0%,
282
+ 100% {
283
+ transform: scale(0.8);
284
+ opacity: 0.6;
177
285
  }
178
286
  50% {
179
- transform: scale(1);
180
- opacity: 1;
287
+ transform: scale(1);
288
+ opacity: 1;
181
289
  }
182
- }
290
+ }
@@ -1,29 +1,84 @@
1
1
  <template>
2
2
  <div class="q-chatbot__message-container">
3
- <!-- Chatbot Image -->
4
3
  <q-icon
5
4
  type="img"
6
5
  :icon="messageImage"
7
- alt="Sender Image"
6
+ :alt="props.texts.senderImage"
8
7
  class="q-chatbot__profile" />
9
8
 
10
- <div class="q-chatbot__message-wrapper">
9
+ <div class="q-chatbot__message-wrapper">
10
+ <div
11
+ v-if="props.imagePreviewUrl && props.imagePreviewUrl.length > 0"
12
+ class="q-chatbot__image-preview">
13
+ <img
14
+ :src="props.imagePreviewUrl"
15
+ :alt="props.texts.imagePreview" />
16
+ </div>
11
17
  <!-- Message body -->
12
18
  <div class="q-chatbot__message">
13
19
  <!-- When loading is true, show bouncing dots animation -->
14
- <pulse-dots v-if="loading"/>
20
+ <pulse-dots v-if="loading" />
15
21
  <template v-else>
22
+ <vue-markdown-render
23
+ v-if="props.sender === 'bot'"
24
+ class="q-chatbot__text"
25
+ :source="props.message || ''" />
26
+
16
27
  <div
17
- class="q-chatbot__text"
18
- v-if="props.sender === 'bot'"
19
- v-html="props.message"></div>
20
- <div class="q-chatbot__text" v-else>
28
+ v-else
29
+ class="q-chatbot__text">
21
30
  {{ props.message }}
22
31
  </div>
23
32
  </template>
24
33
  </div>
25
- <div
26
- class="q-chatbot__sender">
34
+ <q-dialog
35
+ id="comment-dialog"
36
+ v-model="showDialog"
37
+ :buttons="commentButtons">
38
+ <template #body.content>
39
+ <div class="q-chatbot__dialog-title">
40
+ {{ props.texts.commentDialogTitle }}
41
+ </div>
42
+ <q-text-field
43
+ v-model="feedbackComment"
44
+ :maxLength="150"
45
+ size="large"
46
+ :placeholder="props.texts.commentPlaceholder" />
47
+ </template>
48
+ </q-dialog>
49
+
50
+ <!-- Thumbs up and down buttons, only for bot messages -->
51
+ <div
52
+ v-if="isBotMessageAndNotDefault"
53
+ class="q-chatbot__feedback-buttons">
54
+ <q-button-group>
55
+ <q-button
56
+ :title="props.texts.goodResponse"
57
+ borderless
58
+ :disabled="loading"
59
+ b-style="secondary"
60
+ @click="openFeedbackDialog(1)">
61
+ <q-icon icon="thumb-up" />
62
+ </q-button>
63
+ <q-button
64
+ :title="props.texts.badResponse"
65
+ borderless
66
+ :disabled="loading"
67
+ b-style="secondary"
68
+ @click="openFeedbackDialog(0)">
69
+ <q-icon icon="thumb-down" />
70
+ </q-button>
71
+ <q-button
72
+ :title="props.texts.copyResponse"
73
+ borderless
74
+ :disabled="loading"
75
+ b-style="secondary"
76
+ @click="copyResponse">
77
+ <q-icon icon="copy-content" />
78
+ </q-button>
79
+ </q-button-group>
80
+ </div>
81
+ <div class="q-chatbot__sender">
27
82
  {{ messageDate }}
28
83
  </div>
29
84
  </div>
@@ -31,10 +86,19 @@
31
86
  </template>
32
87
 
33
88
  <script setup lang="ts">
34
- import { computed } from 'vue'
35
- import { QIcon } from '@quidgest/ui/components'
89
+ import { computed, ref } from 'vue'
90
+ import {
91
+ QButton,
92
+ QIcon,
93
+ QDialog,
94
+ QTextField
95
+ } from '@quidgest/ui/components'
36
96
  import PulseDots from '@/components/PulseDots.vue'
37
97
  import type { ChatBotMessageSender } from '@/types/message.type'
98
+ import { ResourceStrings } from '@/types/texts.type'
99
+ import VueMarkdownRender from 'vue-markdown-render'
100
+ import Axios from 'axios'
101
+
38
102
  export interface CBMessageProps {
39
103
  /*
40
104
  * Sender of the message
@@ -59,7 +123,27 @@
59
123
  /**
60
124
  * Project locale
61
125
  */
62
- dateFormat?: string,
126
+ dateFormat?: string
127
+
128
+ /**
129
+ * Image preview URL
130
+ */
131
+ imagePreviewUrl?: string
132
+
133
+ /**
134
+ * Default chatBot texts
135
+ */
136
+ texts?: ResourceStrings
137
+
138
+ /**
139
+ * Default api endpoint
140
+ */
141
+ apiEndpoint?: String
142
+
143
+ /**
144
+ * Session ID
145
+ */
146
+ sessionID?: String
63
147
 
64
148
  /**
65
149
  * User image
@@ -70,29 +154,78 @@
70
154
  * Chatbot image
71
155
  */
72
156
  chatbotImage: string
157
+
158
+ /**
159
+ * Flag to mark welcome messages
160
+ */
161
+ isWelcomeMessage?: boolean
73
162
  }
74
163
 
75
164
  const props = withDefaults(defineProps<CBMessageProps>(), {
76
165
  sender: 'user',
77
166
  userImage: undefined,
78
- date: () => new Date()
167
+ date: () => new Date(),
168
+ texts: () => ({
169
+ commentDialogTitle: 'Would you like to add a comment?',
170
+ commentPlaceholder: 'Type your comment here (optional)...',
171
+ goodResponse: 'Good response',
172
+ badResponse: 'Bad response',
173
+ copyResponse: 'Copy response',
174
+ submitButton: 'Submit',
175
+ cancelButton: 'Cancel',
176
+ senderImage: 'Sender Image',
177
+ imagePreview: 'Image preview'
178
+ })
79
179
  })
80
180
 
81
- const senderName = computed(() => {
82
- return props.sender === 'bot' ? 'GenioBot' : 'You'
181
+ const showDialog = ref(false)
182
+ const feedbackComment = ref('')
183
+ const currentFeedback = ref<number | null>(null)
184
+ const commentButtons = [
185
+ {
186
+ id: 'confirm-btn',
187
+ action: submitFeedback,
188
+ props: {
189
+ label: props.texts.submitButton,
190
+ 'b-style': 'primary',
191
+ },
192
+ icon: {
193
+ icon: 'submit'
194
+ }
195
+ },
196
+ {
197
+ id: 'cancel-btn',
198
+ props: {
199
+ label: props.texts.cancelButton,
200
+ 'b-style': 'secondary',
201
+ },
202
+ icon: {
203
+ icon: 'cancel'
204
+ }
205
+ }
206
+ ]
207
+
208
+ const isBotMessageAndNotDefault = computed(() => {
209
+ return (
210
+ props.sender === 'bot' &&
211
+ !Object.values(props.texts || {}).includes(props.message || '') &&
212
+ !props.isWelcomeMessage
213
+ )
83
214
  })
84
215
 
85
216
  const getLocaleDate = computed(() => {
86
217
  if (!props.dateFormat) return props.date.toLocaleString()
87
218
 
88
- return formatDate(props.date, props.dateFormat)
219
+ return formatDate(props.date, 'HH:mm')
89
220
  })
90
221
 
91
222
  const messageDate = computed(() => {
92
- return `${senderName.value} ${getLocaleDate.value}`
223
+ return `${getLocaleDate.value}`
93
224
  })
94
225
 
95
- const messageImage = computed(() => props.sender === 'bot' ? props.chatbotImage : props.userImage);
226
+ const messageImage = computed(() =>
227
+ props.sender === 'bot' ? props.chatbotImage : props.userImage
228
+ )
96
229
 
97
230
  function formatDate(date: Date, format: string) {
98
231
  const day = date.getDate().toString().padStart(2, '0')
@@ -110,4 +243,38 @@
110
243
  .replace('mm', minutes)
111
244
  .replace('ss', seconds)
112
245
  }
246
+
247
+ function openFeedbackDialog(feedback: number) {
248
+ showDialog.value = true
249
+ feedbackComment.value = ''
250
+ currentFeedback.value = feedback
251
+ }
252
+
253
+ function submitFeedback() {
254
+ if (currentFeedback.value != null) {
255
+ handleFeedback(currentFeedback.value, feedbackComment.value)
256
+ }
257
+ }
258
+ function handleFeedback(feedback: number, comment: string) {
259
+ Axios.post(props.apiEndpoint + '/prompt/feedback', {
260
+ messageSessionID: props.sessionID,
261
+ feedbackValue: feedback,
262
+ feedbackComment: comment
263
+ }).catch((error) => {
264
+ console.error('Error sending message feedback: ', error)
265
+ })
266
+ }
267
+
268
+ function copyResponse() {
269
+ if (!props.message) return
270
+
271
+ navigator.clipboard
272
+ .writeText(props.message)
273
+ .then(() => {
274
+ console.log('Message copied to clipboard')
275
+ })
276
+ .catch((error) => {
277
+ console.error('Failed to copy message to clipboard: ', error)
278
+ })
279
+ }
113
280
  </script>