@quidgest/chatbot 0.4.0 → 0.5.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.
Files changed (73) hide show
  1. package/dist/components/ChatBot/ChatBot.vue.d.ts +53 -0
  2. package/dist/components/ChatBot/types.d.ts +48 -0
  3. package/dist/components/ChatBotInput/ChatBotInput.vue.d.ts +17 -0
  4. package/dist/components/ChatBotInput/index.d.ts +5 -0
  5. package/dist/components/ChatBotInput/types.d.ts +28 -0
  6. package/dist/components/ChatBotMessage/ChatBotMessage.vue.d.ts +41 -0
  7. package/dist/components/ChatBotMessage/ChatBotMessageButtons.vue.d.ts +21 -0
  8. package/dist/components/ChatBotMessage/index.d.ts +6 -0
  9. package/dist/components/ChatBotMessage/types.d.ts +46 -0
  10. package/dist/components/ChatToolBar/ChatToolBar.vue.d.ts +39 -0
  11. package/dist/components/ChatToolBar/index.d.ts +5 -0
  12. package/dist/components/ChatToolBar/types.d.ts +16 -0
  13. package/dist/components/FieldPreview/FieldPreview.vue.d.ts +17 -0
  14. package/dist/components/FieldPreview/index.d.ts +5 -0
  15. package/dist/components/FieldPreview/types.d.ts +7 -0
  16. package/dist/components/MarkdownRender/MarkdownRender.vue.d.ts +13 -0
  17. package/dist/components/MarkdownRender/index.d.ts +5 -0
  18. package/dist/components/MarkdownRender/types.d.ts +7 -0
  19. package/dist/composables/useChatApi.d.ts +23 -0
  20. package/dist/composables/useChatMessages.d.ts +11 -0
  21. package/dist/composables/useSSE.d.ts +10 -0
  22. package/dist/composables/useTexts.d.ts +26 -0
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.js +27 -47
  25. package/dist/index.mjs +2651 -8663
  26. package/dist/style.css +1 -1
  27. package/dist/utils/helper.d.ts +1 -0
  28. package/package.json +5 -5
  29. package/src/assets/chatbot_profile.svg +1 -0
  30. package/src/assets/styles/styles.scss +10 -42
  31. package/src/components/ChatBot/ChatBot.vue +375 -0
  32. package/src/components/ChatBot/types.ts +55 -0
  33. package/src/components/ChatBotInput/ChatBotInput.vue +195 -0
  34. package/src/components/ChatBotInput/index.ts +5 -0
  35. package/src/components/ChatBotInput/types.ts +33 -0
  36. package/src/components/ChatBotMessage/ChatBotMessage.vue +139 -0
  37. package/src/components/ChatBotMessage/ChatBotMessageButtons.vue +169 -0
  38. package/src/components/ChatBotMessage/index.ts +8 -0
  39. package/src/components/ChatBotMessage/types.ts +70 -0
  40. package/src/components/ChatToolBar/ChatToolBar.vue +82 -0
  41. package/src/components/ChatToolBar/index.ts +5 -0
  42. package/src/components/ChatToolBar/types.ts +18 -0
  43. package/src/components/FieldPreview/FieldPreview.vue +78 -0
  44. package/src/components/FieldPreview/field-preview.scss +34 -0
  45. package/src/components/FieldPreview/index.ts +5 -0
  46. package/src/components/FieldPreview/types.ts +7 -0
  47. package/src/components/MarkdownRender/MarkdownRender.vue +25 -0
  48. package/src/components/MarkdownRender/index.ts +5 -0
  49. package/src/components/MarkdownRender/markdown-render.scss +24 -0
  50. package/src/components/MarkdownRender/types.ts +7 -0
  51. package/src/components/PulseDots/PulseDots.vue +24 -0
  52. package/src/components/PulseDots/pulse-dots.scss +37 -0
  53. package/src/composables/useChatApi.ts +156 -0
  54. package/src/composables/useChatMessages.ts +58 -0
  55. package/src/composables/useSSE.ts +90 -0
  56. package/src/composables/useTexts.ts +32 -0
  57. package/src/index.ts +1 -1
  58. package/src/utils/helper.ts +12 -0
  59. package/dist/components/CBMessage.vue.d.ts +0 -95
  60. package/dist/components/ChatBot.vue.d.ts +0 -65
  61. package/dist/components/index.d.ts +0 -4
  62. package/dist/types/chatbot.type.d.ts +0 -14
  63. package/dist/types/message.type.d.ts +0 -34
  64. package/dist/types/texts.type.d.ts +0 -3
  65. package/src/assets/chatbot.png +0 -0
  66. package/src/components/CBMessage.vue +0 -276
  67. package/src/components/ChatBot.vue +0 -496
  68. package/src/components/PulseDots.vue +0 -15
  69. package/src/components/index.ts +0 -4
  70. package/src/types/chatbot.type.ts +0 -15
  71. package/src/types/message.type.ts +0 -55
  72. package/src/types/texts.type.ts +0 -3
  73. /package/dist/components/{PulseDots.vue.d.ts → PulseDots/PulseDots.vue.d.ts} +0 -0
@@ -0,0 +1,195 @@
1
+ <template>
2
+ <q-label :label="texts.inputLabel" />
3
+ <div
4
+ class="q-chatbot__footer"
5
+ @dragover.prevent="onDragOver"
6
+ @dragleave.prevent="onDragLeave"
7
+ @drop.prevent="onDrop"
8
+ :class="chatBotFooterClasses">
9
+ <div class="q-chatbot__input-wrapper">
10
+ <div
11
+ v-if="imagePreviewUrl"
12
+ class="q-chatbot__image-preview">
13
+ <img
14
+ :src="imagePreviewUrl"
15
+ tabindex="0"
16
+ alt="Image preview" />
17
+ <q-button
18
+ class="q-chatbot__remove-image"
19
+ tabindex="0"
20
+ flat
21
+ round
22
+ @click="removeImage">
23
+ <q-icon icon="bin" />
24
+ </q-button>
25
+ </div>
26
+ <div class="q-chatbot__input">
27
+ <q-text-area
28
+ v-model="userPrompt"
29
+ size="block"
30
+ autosize
31
+ resize="none"
32
+ :rows="2"
33
+ :disabled="props.disabled"
34
+ @keyup.enter="sendMessage" />
35
+ </div>
36
+ </div>
37
+ <div class="q-chatbot__send-container">
38
+ <q-button
39
+ :title="texts.imageUpload"
40
+ class="q-chatbot__upload"
41
+ :disabled="props.disabled || props.loading || hasSelectedImage"
42
+ @click="triggerImageUpload">
43
+ <q-icon icon="upload" />
44
+ </q-button>
45
+
46
+ <!-- Hidden file input -->
47
+ <input
48
+ id="image-upload"
49
+ type="file"
50
+ ref="imageInput"
51
+ @change="handleImageUpload"
52
+ :accept="acceptedImageTypes"
53
+ class="hidden-input" />
54
+
55
+ <q-button
56
+ :title="texts.sendMessage"
57
+ variant="bold"
58
+ class="q-chatbot__send"
59
+ :disabled="isSendButtonDisabled"
60
+ :readonly="isSendButtonDisabled"
61
+ @click="sendMessage">
62
+ <q-icon icon="send" />
63
+ </q-button>
64
+ </div>
65
+ </div>
66
+ </template>
67
+
68
+ <script setup lang="ts">
69
+ // Components
70
+ import { QLabel } from '@quidgest/ui/components'
71
+
72
+ // Composables
73
+ import { useTexts } from '@/composables/useTexts'
74
+
75
+ // Utils
76
+ import { ref, computed } from 'vue'
77
+
78
+ // Types
79
+ import { ChatBotInputProps, ChatBotImage } from './types'
80
+
81
+ const texts = useTexts()
82
+
83
+ const props = defineProps<ChatBotInputProps>()
84
+
85
+ const imageInput = ref<HTMLInputElement | null>(null)
86
+ const imagePreviewUrl = ref<string>('')
87
+ const acceptedImageTypes = computed(() => '.png, .jpeg, .jpg, .svg, .webp')
88
+ const hasSelectedImage = ref<boolean>(false)
89
+
90
+ const isDragging = ref(false)
91
+ const userPrompt = ref(props.userPrompt ?? '')
92
+
93
+ const emits = defineEmits<{
94
+ (e: 'sendMessage', prompt: string, image?: ChatBotImage): void
95
+ }>()
96
+
97
+ const isSendButtonDisabled = computed(() => {
98
+ return (
99
+ props.disabled ||
100
+ props.loading ||
101
+ userPrompt.value.trim().length === 0
102
+ )
103
+ })
104
+
105
+ function onDragOver(event: DragEvent) {
106
+ event.preventDefault()
107
+ if (!props.disabled) {
108
+ isDragging.value = true
109
+ }
110
+ }
111
+
112
+ const chatBotFooterClasses = computed(() => {
113
+ return {
114
+ 'q-chatbot__footer-disabled': props.disabled,
115
+ 'drag-over': isDragging.value && !hasSelectedImage.value
116
+ }
117
+ })
118
+
119
+ function onDragLeave(event: DragEvent) {
120
+ event.preventDefault()
121
+ isDragging.value = false
122
+ }
123
+
124
+ function onDrop(event: DragEvent) {
125
+ event.preventDefault()
126
+ isDragging.value = false
127
+
128
+ if (props.disabled || hasSelectedImage.value) return
129
+
130
+ const files = event.dataTransfer?.files
131
+ if (!files) return
132
+
133
+ const file = files[0]
134
+ if (file.type.startsWith('image/')) {
135
+ if (!imageInput.value) return
136
+
137
+ // Create a new FileList-like object
138
+ const dataTransfer = new DataTransfer()
139
+ dataTransfer.items.add(file)
140
+ imageInput.value.files = dataTransfer.files
141
+
142
+ imagePreviewUrl.value = URL.createObjectURL(file)
143
+ hasSelectedImage.value = true
144
+ }
145
+ }
146
+
147
+ function sendMessage() {
148
+ if (userPrompt.value.trim() === '' || props.loading || props.disabled)
149
+ return
150
+
151
+ // Check if an image is selected
152
+ if (
153
+ imageInput.value &&
154
+ imageInput.value.files &&
155
+ imageInput.value.files.length > 0
156
+ ) {
157
+ const file = imageInput.value.files[0]
158
+
159
+ const fileData: ChatBotImage = {
160
+ file: file,
161
+ previewUrl: imagePreviewUrl.value
162
+ }
163
+
164
+ emits('sendMessage', userPrompt.value, fileData)
165
+ removeImage()
166
+ } else emits('sendMessage', userPrompt.value)
167
+
168
+ userPrompt.value = ''
169
+ }
170
+
171
+ function removeImage() {
172
+ imagePreviewUrl.value = ''
173
+
174
+ if (imageInput.value) {
175
+ imageInput.value.value = ''
176
+ imageInput.value.files = null
177
+ }
178
+ hasSelectedImage.value = false
179
+ }
180
+
181
+ function triggerImageUpload() {
182
+ imageInput.value?.click()
183
+ }
184
+
185
+ function handleImageUpload(event: Event) {
186
+ // Store the selected image in imageInput
187
+ const target = event.target as HTMLInputElement
188
+ imageInput.value = target
189
+
190
+ if (target.files && target.files[0]) {
191
+ imagePreviewUrl.value = URL.createObjectURL(target.files[0])
192
+ hasSelectedImage.value = true
193
+ }
194
+ }
195
+ </script>
@@ -0,0 +1,5 @@
1
+ import ChatBotInput from './ChatBotInput.vue'
2
+ import type { ChatBotInputProps, ChatBotImage } from './types'
3
+
4
+ export { ChatBotInput }
5
+ export type { ChatBotInputProps, ChatBotImage }
@@ -0,0 +1,33 @@
1
+ export type ChatBotInputProps = {
2
+ /**
3
+ * If the footer is disabled or not
4
+ */
5
+ disabled?: boolean
6
+
7
+ /**
8
+ * If the footer is loading or not
9
+ */
10
+ loading?: boolean
11
+
12
+ /**
13
+ * The message to be sent
14
+ */
15
+ userPrompt?: string
16
+
17
+ /**
18
+ * The agent to be used
19
+ */
20
+ agentId?: string
21
+ }
22
+
23
+ export type ChatBotImage = {
24
+ /**
25
+ * The image URL
26
+ */
27
+ previewUrl: string
28
+
29
+ /**
30
+ * The file object
31
+ */
32
+ file: File
33
+ }
@@ -0,0 +1,139 @@
1
+ <template>
2
+ <div class="q-chatbot__message-container">
3
+ <q-icon
4
+ type="img"
5
+ :icon="messageImage"
6
+ :alt="texts.senderImage"
7
+ class="q-chatbot__profile" />
8
+
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="texts.imagePreview" />
16
+ </div>
17
+ <div class="q-chatbot__message">
18
+ <pulse-dots v-if="loading" />
19
+ <template
20
+ v-else-if="
21
+ props.sender === 'bot' && props.fields.length > 0
22
+ ">
23
+ <field-preview
24
+ v-for="(field, index) in props.fields"
25
+ class="q-chatbot__text"
26
+ :key="index"
27
+ :text="field.text || ''"
28
+ :name="field.name"
29
+ :type="field.type"
30
+ :disabled="loading"
31
+ @apply="(text) => applyField(text, field)" />
32
+ </template>
33
+ <template v-else>
34
+ <markdown-render
35
+ v-if="props.sender === 'bot'"
36
+ class="q-chatbot__text"
37
+ :source="props.message || ''" />
38
+ <div
39
+ v-else
40
+ class="q-chatbot__text">
41
+ {{ props.message }}
42
+ </div>
43
+ </template>
44
+ </div>
45
+ </div>
46
+
47
+ <chat-bot-message-buttons
48
+ :showButtons="isBotMessageAndNotDefault"
49
+ :loading="props.loading"
50
+ :date-format="props.dateFormat"
51
+ @copy-response="copyResponse"
52
+ @submit-feedback="onSubmitFeedback"
53
+ @apply-all="applyAllFields" />
54
+ </div>
55
+ </template>
56
+
57
+ <script setup lang="ts">
58
+ // Utils
59
+ import { computed } from 'vue'
60
+ import { QIcon } from '@quidgest/ui/components'
61
+
62
+ // Types
63
+ import type { ChatBotMessageProps } from './'
64
+ import { AppliedFieldData, FieldData } from '../ChatBot/types'
65
+
66
+ // Composables
67
+ import { useTexts } from '@/composables/useTexts'
68
+ import { useChatApi } from '@/composables/useChatApi'
69
+
70
+ // Components
71
+ import { MarkdownRender } from '../MarkdownRender'
72
+ import { FieldPreview } from '../FieldPreview'
73
+ import { ChatBotMessageButtons } from './'
74
+ import PulseDots from '@/components/PulseDots/PulseDots.vue'
75
+ import { parseFieldValue } from '@/utils/helper'
76
+
77
+ const emits = defineEmits<{
78
+ (e: 'apply-fields', fields: AppliedFieldData[]): void
79
+ }>()
80
+
81
+ const texts = useTexts()
82
+
83
+ const props = withDefaults(defineProps<ChatBotMessageProps>(), {
84
+ sender: 'user',
85
+ userImage: undefined,
86
+ date: () => new Date(),
87
+ fields: () => []
88
+ })
89
+
90
+ const { handleFeedback } = useChatApi(props.apiEndpoint)
91
+
92
+ const isBotMessageAndNotDefault = computed(() => {
93
+ return (
94
+ props.sender === 'bot' &&
95
+ !Object.values(texts || {}).includes(props.message || '') &&
96
+ !props.isWelcomeMessage
97
+ )
98
+ })
99
+
100
+ const messageImage = computed(() =>
101
+ props.sender === 'bot' ? props.chatbotImage : props.userImage
102
+ )
103
+
104
+ function copyResponse() {
105
+ if (!props.message) return
106
+
107
+ navigator.clipboard
108
+ .writeText(props.message)
109
+ .then(() => {
110
+ console.log('Response copied to clipboard')
111
+ })
112
+ .catch((err) => {
113
+ console.error('Failed to copy response: ', err)
114
+ })
115
+ }
116
+
117
+ function applyField(text: unknown, field: FieldData) {
118
+ emits('apply-fields', [{ name: field.name, text }])
119
+ }
120
+
121
+ function applyAllFields() {
122
+ if (!props.fields || props.fields.length === 0) return
123
+
124
+ const fieldsToApply = props.fields.map((field) => {
125
+ return {
126
+ name: field.name,
127
+ text: parseFieldValue(field.type, field.text)
128
+ }
129
+ })
130
+
131
+ emits('apply-fields', fieldsToApply)
132
+ }
133
+
134
+ function onSubmitFeedback(feedback: number, comment: string) {
135
+ if (feedback === null || comment === null || !props.sessionID) return
136
+
137
+ handleFeedback(feedback, comment, props.sessionID)
138
+ }
139
+ </script>
@@ -0,0 +1,169 @@
1
+ <template>
2
+ <q-dialog
3
+ v-model="showDialog"
4
+ :buttons="commentButtons">
5
+ <template #body>
6
+ <div class="q-chatbot__dialog-title">
7
+ {{ texts.commentDialogTitle }}
8
+ </div>
9
+ <q-text-field
10
+ v-model="feedbackComment"
11
+ :maxLength="150"
12
+ size="large"
13
+ :placeholder="texts.commentPlaceholder" />
14
+ </template>
15
+ </q-dialog>
16
+
17
+ <div
18
+ v-if="showButtons"
19
+ class="q-chatbot__feedback-buttons">
20
+ <q-button-group>
21
+ <q-button
22
+ :title="texts.goodResponse"
23
+ borderless
24
+ :disabled="props.loading"
25
+ @click="openFeedbackDialog(1)">
26
+ <q-icon icon="thumb-up" />
27
+ </q-button>
28
+ <q-button
29
+ :title="texts.badResponse"
30
+ borderless
31
+ :disabled="props.loading"
32
+ @click="openFeedbackDialog(0)">
33
+ <q-icon icon="thumb-down" />
34
+ </q-button>
35
+ <q-button
36
+ :title="texts.copyResponse"
37
+ borderless
38
+ :disabled="props.loading"
39
+ @click="copyResponse">
40
+ <q-icon icon="copy-content" />
41
+ </q-button>
42
+ <q-button
43
+ v-if="showApplyAll"
44
+ :title="texts.applyAll"
45
+ borderless
46
+ :disabled="blockApplyAllButton"
47
+ :readonly="blockApplyAllButton"
48
+ @click="onApplyAll">
49
+ <q-icon icon="apply-all" />
50
+ </q-button>
51
+ </q-button-group>
52
+ </div>
53
+ <div class="q-chatbot__sender">
54
+ {{ messageDate }}
55
+ </div>
56
+ </template>
57
+
58
+ <script setup lang="ts">
59
+ import { computed, ref } from 'vue'
60
+ import { QDialog } from '@quidgest/ui/components'
61
+ import type { ChatBotMessageButtonsProps } from './'
62
+
63
+ // Composables
64
+ import { useTexts } from '@/composables/useTexts'
65
+ import { useChatMessages } from '@/composables/useChatMessages'
66
+
67
+ const texts = useTexts()
68
+ const { getLastMessage } = useChatMessages()
69
+
70
+ const props = defineProps<ChatBotMessageButtonsProps>()
71
+ const showDialog = ref(false)
72
+ const feedbackComment = ref('')
73
+ const currentFeedback = ref<number | null>(null)
74
+ const blockApplyAll = ref(false)
75
+ const blockApplyAllButton = computed(() => {
76
+ return props.loading || blockApplyAll.value
77
+ })
78
+
79
+ const date = props.date || new Date()
80
+
81
+ const emits = defineEmits<{
82
+ (e: 'submit-feedback', feedback: number, comment: string): void
83
+ (e: 'copy-response'): void
84
+ (e: 'apply-all'): void
85
+ }>()
86
+
87
+ const lastMessage = getLastMessage()
88
+
89
+ const showApplyAll = computed(() => {
90
+ if (!lastMessage) return false
91
+
92
+ return lastMessage.fields && lastMessage.fields.length > 1
93
+ })
94
+
95
+ const commentButtons = [
96
+ {
97
+ id: 'confirm-btn',
98
+ action: submitFeedback,
99
+ props: {
100
+ label: texts.submitButton
101
+ },
102
+ icon: {
103
+ icon: 'submit'
104
+ }
105
+ },
106
+ {
107
+ id: 'cancel-btn',
108
+ props: {
109
+ label: texts.cancelButton
110
+ },
111
+ icon: {
112
+ icon: 'cancel'
113
+ }
114
+ }
115
+ ]
116
+
117
+ const getLocaleDate = computed(() => {
118
+ if (!props.dateFormat) return date.toLocaleString()
119
+
120
+ return formatDate(date, 'HH:mm')
121
+ })
122
+
123
+ const messageDate = computed(() => {
124
+ return `${getLocaleDate.value}`
125
+ })
126
+
127
+ function formatDate(date: Date, format: string) {
128
+ const day = date.getDate().toString().padStart(2, '0')
129
+ const month = (date.getMonth() + 1).toString().padStart(2, '0')
130
+ const year = date.getFullYear().toString().padStart(2, '0')
131
+ const hours = date.getHours().toString().padStart(2, '0')
132
+ const minutes = date.getMinutes().toString().padStart(2, '0')
133
+ const seconds = date.getSeconds().toString().padStart(2, '0')
134
+
135
+ return format
136
+ .replace('dd', day)
137
+ .replace('MM', month)
138
+ .replace('yyyy', year)
139
+ .replace('HH', hours)
140
+ .replace('mm', minutes)
141
+ .replace('ss', seconds)
142
+ }
143
+
144
+ function openFeedbackDialog(feedback: number) {
145
+ showDialog.value = true
146
+ feedbackComment.value = ''
147
+ currentFeedback.value = feedback
148
+ }
149
+
150
+ function onApplyAll() {
151
+ if (blockApplyAll.value) return
152
+ blockApplyAll.value = true
153
+
154
+ emits('apply-all')
155
+ }
156
+
157
+ function submitFeedback() {
158
+ if (currentFeedback.value === null) return
159
+
160
+ emits('submit-feedback', currentFeedback.value, feedbackComment.value)
161
+ showDialog.value = false
162
+ feedbackComment.value = ''
163
+ currentFeedback.value = null
164
+ }
165
+
166
+ function copyResponse() {
167
+ emits('copy-response')
168
+ }
169
+ </script>
@@ -0,0 +1,8 @@
1
+ import ChatBotMessage from './ChatBotMessage.vue'
2
+ import type { ChatBotMessageProps } from './types'
3
+
4
+ import ChatBotMessageButtons from './ChatBotMessageButtons.vue'
5
+ import type { ChatBotMessageButtonsProps } from './types'
6
+
7
+ export { ChatBotMessage, ChatBotMessageButtons }
8
+ export type { ChatBotMessageProps, ChatBotMessageButtonsProps }
@@ -0,0 +1,70 @@
1
+ import { ChatBotMessageSender, FieldData } from '@/components/ChatBot/types'
2
+
3
+ export type ChatBotMessageProps = {
4
+ /*
5
+ * Sender of the message
6
+ */
7
+ sender?: ChatBotMessageSender
8
+
9
+ /*
10
+ * Message to be displayed
11
+ */
12
+ message?: string
13
+
14
+ /*
15
+ * Date of when the message was sent
16
+ */
17
+ date?: Date
18
+
19
+ /*
20
+ * If the message is loading
21
+ */
22
+ loading?: boolean
23
+
24
+ /**
25
+ * Project locale
26
+ */
27
+ dateFormat: string
28
+
29
+ /**
30
+ * Image preview URL
31
+ */
32
+ imagePreviewUrl?: string
33
+
34
+ /**
35
+ * Default api endpoint
36
+ */
37
+ apiEndpoint: string
38
+
39
+ /**
40
+ * Session ID
41
+ */
42
+ sessionID?: string
43
+
44
+ /**
45
+ * User image
46
+ */
47
+ userImage: string
48
+
49
+ /**
50
+ * Chatbot image
51
+ */
52
+ chatbotImage: string
53
+
54
+ /**
55
+ * Flag to mark welcome messages
56
+ */
57
+ isWelcomeMessage?: boolean
58
+
59
+ /**
60
+ * Additional fields for the message
61
+ */
62
+ fields?: FieldData[]
63
+ }
64
+
65
+ export type ChatBotMessageButtonsProps = {
66
+ loading: boolean
67
+ showButtons: boolean
68
+ dateFormat: string
69
+ date?: Date
70
+ }
@@ -0,0 +1,82 @@
1
+ <template>
2
+ <div class="q-chatbot__tools">
3
+ <div class="q-chatbot__tools__select">
4
+ <q-select
5
+ v-if="hasAgents"
6
+ v-model="selectedChat"
7
+ size="medium"
8
+ :items="availableChats"
9
+ @update:model-value="changeChat" />
10
+ </div>
11
+
12
+ <q-button
13
+ :title="clearChat"
14
+ :disabled="props.disabled"
15
+ borderless
16
+ @click="clear">
17
+ <q-icon icon="bin" />
18
+ </q-button>
19
+ </div>
20
+ </template>
21
+
22
+ <script setup lang="ts">
23
+ import { useTexts } from '@/composables/useTexts'
24
+ import { ChatToolBarProps } from './types'
25
+ import { computed, ref, watch } from 'vue'
26
+
27
+ const { clearChat } = useTexts()
28
+
29
+ const defaultChat = {
30
+ key: '',
31
+ value: 'Default Chat',
32
+ formId: ''
33
+ }
34
+
35
+ const props = withDefaults(defineProps<ChatToolBarProps>(), {
36
+ availableAgents: () => []
37
+ })
38
+
39
+ const emit = defineEmits<{
40
+ (e: 'clear'): void
41
+ (e: 'change-chat', value: { key: string; formId: string }): void
42
+ }>()
43
+
44
+ const hasAgents = computed(() => {
45
+ return props.availableAgents && props.availableAgents.length > 0
46
+ })
47
+
48
+ const selectedChat = ref(props.selectedAgentKey || defaultChat.key)
49
+ watch(
50
+ () => props.selectedAgentKey,
51
+ (newValue) => {
52
+ selectedChat.value = newValue || defaultChat.key
53
+ }
54
+ )
55
+
56
+ const availableChats = computed(() => {
57
+ const chats = props.availableAgents?.map((agent) => ({
58
+ key: agent.key,
59
+ value: agent.value,
60
+ formId: agent.formId
61
+ }))
62
+
63
+ chats?.push(defaultChat)
64
+ return chats || [defaultChat]
65
+ })
66
+
67
+ function clear() {
68
+ emit('clear')
69
+ }
70
+
71
+ function changeChat(value: string) {
72
+ const item = availableChats.value.find((chat) => chat.key === value)
73
+ if (!item) return
74
+
75
+ if (item.key === defaultChat.key) {
76
+ emit('change-chat', { key: '', formId: '' })
77
+ return
78
+ }
79
+
80
+ emit('change-chat', item)
81
+ }
82
+ </script>