@quidgest/chatbot 0.5.3-dev.0 → 0.5.4

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 (32) hide show
  1. package/dist/components/ChatBot/__tests__/ChatBot.spec.d.ts +1 -0
  2. package/dist/components/ChatBot/types.d.ts +3 -1
  3. package/dist/components/ChatBotInput/ChatBotInput.vue.d.ts +3 -3
  4. package/dist/components/ChatBotInput/index.d.ts +2 -2
  5. package/dist/components/ChatBotInput/types.d.ts +4 -4
  6. package/dist/components/ChatBotMessage/__tests__/ChatBotMessage.spec.d.ts +1 -0
  7. package/dist/components/ChatBotMessage/types.d.ts +3 -2
  8. package/dist/composables/useChatMessages.d.ts +2 -1
  9. package/dist/composables/useTexts.d.ts +1 -0
  10. package/dist/index.js +18 -18
  11. package/dist/index.mjs +1200 -1165
  12. package/dist/style.css +1 -1
  13. package/package.json +2 -1
  14. package/src/assets/styles/preview-file.scss +70 -0
  15. package/src/assets/styles/styles.scss +9 -33
  16. package/src/components/ChatBot/ChatBot.vue +10 -14
  17. package/src/components/ChatBot/__tests__/ChatBot.spec.ts +52 -0
  18. package/src/components/ChatBot/__tests__/__snapshots__/ChatBot.spec.ts.snap +39 -0
  19. package/src/components/ChatBot/types.ts +3 -1
  20. package/src/components/ChatBotInput/ChatBotInput.vue +81 -74
  21. package/src/components/ChatBotInput/__tests__/ChatBotInput.spec.ts +29 -42
  22. package/src/components/ChatBotInput/__tests__/__snapshots__/ChatBotInput.spec.ts.snap +5 -5
  23. package/src/components/ChatBotInput/index.ts +2 -2
  24. package/src/components/ChatBotInput/types.ts +4 -4
  25. package/src/components/ChatBotMessage/ChatBotMessage.vue +31 -3
  26. package/src/components/ChatBotMessage/ChatBotMessageButtons.vue +1 -0
  27. package/src/components/ChatBotMessage/__tests__/ChatBotMessage.spec.ts +260 -0
  28. package/src/components/ChatBotMessage/__tests__/__snapshots__/ChatBotMessage.spec.ts.snap +35 -0
  29. package/src/components/ChatBotMessage/types.ts +4 -3
  30. package/src/components/FieldPreview/__tests__/__snapshots__/FieldPreview.spec.ts.snap +1 -1
  31. package/src/composables/useChatMessages.ts +3 -2
  32. package/src/composables/useTexts.ts +2 -1
@@ -7,13 +7,6 @@ import { describe, expect, it, vi } from 'vitest'
7
7
 
8
8
  // Types
9
9
  import type { ChatBotInputProps } from '..'
10
- import type { ComponentPublicInstance } from 'vue'
11
-
12
- type ChatBotInputVm = ComponentPublicInstance<{
13
- imageInput: HTMLInputElement | null
14
- imagePreviewUrl: string
15
- hasSelectedImage: boolean
16
- }>
17
10
 
18
11
  describe('ChatBotInput', () => {
19
12
  const props: ChatBotInputProps = {
@@ -81,7 +74,7 @@ describe('ChatBotInput', () => {
81
74
  await footer.trigger('drop')
82
75
 
83
76
  expect(wrapper.find('img').exists()).toBe(false)
84
- expect(wrapper.find('.q-chatbot__image-preview').exists()).toBe(false)
77
+ expect(wrapper.find('.q-chatbot__file-preview').exists()).toBe(false)
85
78
  })
86
79
 
87
80
  it('prevents drop if theres is no files', async () => {
@@ -95,7 +88,7 @@ describe('ChatBotInput', () => {
95
88
  })
96
89
 
97
90
  expect(wrapper.find('img').exists()).toBe(false)
98
- expect(wrapper.find('.q-chatbot__image-preview').exists()).toBe(false)
91
+ expect(wrapper.find('.q-chatbot__file-preview').exists()).toBe(false)
99
92
  })
100
93
 
101
94
  it('accepts image files on drop', async () => {
@@ -113,32 +106,7 @@ describe('ChatBotInput', () => {
113
106
  })
114
107
 
115
108
  expect(wrapper.find('img').exists()).toBe(true)
116
- expect(wrapper.find('.q-chatbot__image-preview').exists()).toBe(true)
117
- })
118
-
119
- it('does not drop image if hidden input does not exist', async () => {
120
- const wrapper = mount(ChatBotInput, {
121
- props: { ...props }
122
- })
123
-
124
- const footer = wrapper.find('.q-chatbot__footer')
125
- const file = new File(['dummy content'], 'example.png', { type: 'image/png' })
126
-
127
- // Manually set imageInput to null to simulate the missing input
128
- // In reality, this should not happen as the input is always rendered but hidden
129
- const vm = wrapper.vm as ChatBotInputVm
130
- vm.imageInput = null
131
-
132
- // Trigger the drop event
133
- await footer.trigger('drop', {
134
- dataTransfer: {
135
- files: [file]
136
- }
137
- })
138
-
139
- // Assert that no image preview is created
140
- expect(wrapper.find('img').exists()).toBe(false)
141
- expect(wrapper.find('.q-chatbot__image-preview').exists()).toBe(false)
109
+ expect(wrapper.find('.q-chatbot__file-preview').exists()).toBe(true)
142
110
  })
143
111
 
144
112
  it('does not send empty prompts', async () => {
@@ -222,7 +190,7 @@ describe('ChatBotInput', () => {
222
190
  expect(wrapper.emitted()['send-message'][0]).toEqual([
223
191
  'Hello',
224
192
  {
225
- file: file,
193
+ fileData: file,
226
194
  previewUrl: expect.stringContaining('blob:')
227
195
  }
228
196
  ])
@@ -244,14 +212,14 @@ describe('ChatBotInput', () => {
244
212
  })
245
213
 
246
214
  expect(wrapper.find('img').exists()).toBe(true)
247
- expect(wrapper.find('.q-chatbot__image-preview').exists()).toBe(true)
215
+ expect(wrapper.find('.q-chatbot__file-preview').exists()).toBe(true)
248
216
 
249
217
  // Simulate clicking the remove image button
250
- const removeButton = wrapper.find('.q-chatbot__remove-image')
218
+ const removeButton = wrapper.find('.q-chatbot__remove-file')
251
219
  await removeButton.trigger('click')
252
220
 
253
221
  expect(wrapper.find('img').exists()).toBe(false)
254
- expect(wrapper.find('.q-chatbot__image-preview').exists()).toBe(false)
222
+ expect(wrapper.find('.q-chatbot__file-preview').exists()).toBe(false)
255
223
  })
256
224
 
257
225
  it('triggers click on the hidden input when upload button is clicked', async () => {
@@ -260,7 +228,7 @@ describe('ChatBotInput', () => {
260
228
  })
261
229
 
262
230
  const uploadButton = wrapper.find('.q-chatbot__upload')
263
- const fileInput = wrapper.find<HTMLInputElement>('#image-upload')
231
+ const fileInput = wrapper.find<HTMLInputElement>('#file-upload')
264
232
 
265
233
  // Mock the click method on the file input
266
234
  const clickMock = vi.fn()
@@ -276,7 +244,7 @@ describe('ChatBotInput', () => {
276
244
  const wrapper = mount(ChatBotInput, {
277
245
  props: { ...props }
278
246
  })
279
- const fileInput = wrapper.find<HTMLInputElement>('#image-upload')
247
+ const fileInput = wrapper.find<HTMLInputElement>('#file-upload')
280
248
  const file = new File(['dummy content'], 'example.png', { type: 'image/png' })
281
249
 
282
250
  // Simulate selecting a file
@@ -287,6 +255,25 @@ describe('ChatBotInput', () => {
287
255
  await fileInput.trigger('change')
288
256
 
289
257
  expect(wrapper.find('img').exists()).toBe(true)
290
- expect(wrapper.find('.q-chatbot__image-preview').exists()).toBe(true)
258
+ expect(wrapper.find('.q-chatbot__file-preview').exists()).toBe(true)
259
+ })
260
+
261
+ it('renders a document preview for non-image files', async () => {
262
+ const wrapper = mount(ChatBotInput, {
263
+ props: { ...props }
264
+ })
265
+ const fileInput = wrapper.find<HTMLInputElement>('#file-upload')
266
+ const file = new File(['dummy content'], 'example.pdf', { type: 'application/pdf' })
267
+
268
+ // Simulate selecting a file
269
+ const dataTransfer = new DataTransfer()
270
+ dataTransfer.items.add(file)
271
+ fileInput.element.files = dataTransfer.files
272
+
273
+ await fileInput.trigger('change')
274
+
275
+ expect(wrapper.find('img').exists()).toBe(false)
276
+ expect(wrapper.find('.q-chatbot__file-name').exists()).toBe(true)
277
+ expect(wrapper.find('.q-chatbot__file-name').text()).toBe(file.name)
291
278
  })
292
279
  })
@@ -15,11 +15,11 @@ exports[`ChatBotInput > renders correctly with default props 1`] = `
15
15
  <!--v-if-->
16
16
  </div>
17
17
  </div>
18
+ <div class="q-chatbot__send-container"><button type="button" class="q-button q-button--outlined q-button--primary q-chatbot__upload" title="Upload Image">
19
+ <!--v-if--><span class="q-button__content"><span data-test="upload"></span> </span>
20
+ </button><!-- Hidden file input --><input id="file-upload" type="file" accept=".png,.jpeg,.jpg,.svg,.webp,.pdf,.doc,.docx" class="hidden-input"><button type="button" class="q-button q-button--bold q-button--primary q-chatbot__send" disabled="" title="Send message" readonly="">
21
+ <!--v-if--><span class="q-button__content"><span data-test="send"></span> </span>
22
+ </button></div>
18
23
  </div>
19
- <div class="q-chatbot__send-container"><button type="button" class="q-button q-button--outlined q-button--primary q-chatbot__upload" title="Upload Image">
20
- <!--v-if--><span class="q-button__content"><span data-test="upload"></span> </span>
21
- </button><!-- Hidden file input --><input id="image-upload" type="file" accept=".png, .jpeg, .jpg, .svg, .webp" class="hidden-input"><button type="button" class="q-button q-button--bold q-button--primary q-chatbot__send" disabled="" title="Send message" readonly="">
22
- <!--v-if--><span class="q-button__content"><span data-test="send"></span> </span>
23
- </button></div>
24
24
  </div>"
25
25
  `;
@@ -1,5 +1,5 @@
1
1
  import ChatBotInput from './ChatBotInput.vue'
2
- import type { ChatBotInputProps, ChatBotImage } from './types'
2
+ import type { ChatBotInputProps, ChatBotFile } from './types'
3
3
 
4
4
  export { ChatBotInput }
5
- export type { ChatBotInputProps, ChatBotImage }
5
+ export type { ChatBotInputProps, ChatBotFile }
@@ -20,14 +20,14 @@ export type ChatBotInputProps = {
20
20
  agentId?: string
21
21
  }
22
22
 
23
- export type ChatBotImage = {
23
+ export type ChatBotFile = {
24
24
  /**
25
- * The image URL
25
+ * The preview URL for the file (if it's an image)
26
26
  */
27
- previewUrl: string
27
+ previewUrl?: string
28
28
 
29
29
  /**
30
30
  * The file object
31
31
  */
32
- file: File
32
+ fileData: File
33
33
  }
@@ -8,12 +8,29 @@
8
8
 
9
9
  <div class="q-chatbot__message-wrapper">
10
10
  <div
11
- v-if="props.imagePreviewUrl && props.imagePreviewUrl.length > 0"
11
+ v-if="isImageFile"
12
12
  class="q-chatbot__image-preview">
13
13
  <img
14
- :src="props.imagePreviewUrl"
14
+ :src="props.file?.previewUrl"
15
15
  :alt="texts.imagePreview" />
16
16
  </div>
17
+ <div
18
+ v-else-if="props.file?.fileData && !isImageFile"
19
+ class="q-chatbot__file-preview-container">
20
+ <div class="q-chatbot__file-icon-container">
21
+ <q-icon
22
+ icon="file"
23
+ class="q-chatbot__file-icon" />
24
+ </div>
25
+ <div class="q-chatbot__file-info">
26
+ <span class="q-chatbot__file-name">
27
+ {{ props.file?.fileData.name }}
28
+ </span>
29
+ <span class="q-chatbot__file-extension">
30
+ {{ fileExtension }}
31
+ </span>
32
+ </div>
33
+ </div>
17
34
  <div class="q-chatbot__message">
18
35
  <pulse-dots v-if="loading" />
19
36
  <template v-else-if="props.sender === 'bot' && props.fields.length > 0">
@@ -35,7 +52,7 @@
35
52
  :source="props.message || ''" />
36
53
  <div
37
54
  v-else
38
- class="q-chatbot__text">
55
+ class="q-chatbot__text q-chatbot__user-text">
39
56
  {{ props.message }}
40
57
  </div>
41
58
  </template>
@@ -96,10 +113,21 @@
96
113
  )
97
114
  })
98
115
 
116
+ const isImageFile = computed(() => {
117
+ return props.file?.fileData.type.startsWith('image/') ?? false
118
+ })
119
+
99
120
  const messageImage = computed(() =>
100
121
  props.sender === 'bot' ? props.chatbotImage : props.userImage
101
122
  )
102
123
 
124
+ const fileExtension = computed(() => {
125
+ if (!props.file?.fileData) return ''
126
+ const ext = props.file.fileData.name.split('.').pop()?.toUpperCase()
127
+
128
+ return ext ?? ''
129
+ })
130
+
103
131
  function copyResponse() {
104
132
  if (!props.message) return
105
133
 
@@ -51,6 +51,7 @@
51
51
  borderless
52
52
  :disabled="blockApplyAllButton"
53
53
  :readonly="blockApplyAllButton"
54
+ :label="texts.applyAll"
54
55
  @click="onApplyAll">
55
56
  <q-icon icon="apply-all" />
56
57
  </q-button>
@@ -0,0 +1,260 @@
1
+ // Components
2
+ import { ChatBotMessage, ChatBotMessageButtons } from '..'
3
+ import PulseDots from '@/components/PulseDots/PulseDots.vue'
4
+ import { FieldPreview } from '@/components/FieldPreview/'
5
+ import { MarkdownRender } from '@/components/MarkdownRender/'
6
+
7
+ // Utils
8
+ import { mount } from '@vue/test-utils'
9
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
10
+ import { parseFieldValue } from '@/utils/parseFieldValue'
11
+
12
+ // Types
13
+ import type { ChatBotMessageProps } from '..'
14
+
15
+ // Composable
16
+ import { useChatMessages } from '@/composables/useChatMessages'
17
+ import { ChatBotFile } from '@/components/ChatBotInput'
18
+
19
+ describe('ChatBotMessage', () => {
20
+ const fixedDate = new Date('2025-01-01T11:47:00')
21
+ const { clearMessages, addChatMessage } = useChatMessages()
22
+
23
+ beforeEach(() => {
24
+ vi.useFakeTimers()
25
+ vi.setSystemTime(fixedDate)
26
+
27
+ navigator.clipboard.writeText('')
28
+
29
+ clearMessages()
30
+ })
31
+
32
+ afterEach(() => {
33
+ vi.useRealTimers()
34
+ })
35
+
36
+ const props: ChatBotMessageProps = {
37
+ sender: 'bot',
38
+ message: 'Hello, this is a test message.',
39
+ date: fixedDate,
40
+ loading: false,
41
+ dateFormat: 'HH:mm',
42
+ apiEndpoint: 'https://api.example.com/chat',
43
+ fields: [],
44
+ userImage: '',
45
+ chatbotImage: '',
46
+ file: undefined
47
+ }
48
+
49
+ it('renders the component with default props', () => {
50
+ const wrapper = mount(ChatBotMessage, { props })
51
+ expect(wrapper.html()).toMatchSnapshot()
52
+ })
53
+
54
+ it('renders the image preview when imagePreviewUrl is provided', () => {
55
+ const file: ChatBotFile = {
56
+ fileData: new File(['dummy content'], 'example.png', { type: 'image/png' }),
57
+ previewUrl: 'tottaly-legit-image.png'
58
+ }
59
+ const wrapper = mount(ChatBotMessage, {
60
+ props: { ...props, file }
61
+ })
62
+
63
+ const img = wrapper.find('.q-chatbot__image-preview img')
64
+ expect(img.exists()).toBe(true)
65
+ expect(img.attributes('src')).toBe('tottaly-legit-image.png')
66
+ })
67
+
68
+ it('shows loading state when loading is true', () => {
69
+ const wrapper = mount(ChatBotMessage, {
70
+ props: { ...props, loading: true }
71
+ })
72
+
73
+ const pulseDots = wrapper.findComponent(PulseDots)
74
+ expect(pulseDots.exists()).toBe(true)
75
+ })
76
+
77
+ it('renders the field preview component when fields are provided and the sender is bot', () => {
78
+ const wrapper = mount(ChatBotMessage, {
79
+ props: {
80
+ ...props,
81
+ sender: 'bot',
82
+ fields: [
83
+ {
84
+ id: 'field-1',
85
+ type: 'text',
86
+ text: 'Sample Field',
87
+ name: 'This is a sample field value'
88
+ }
89
+ ]
90
+ }
91
+ })
92
+
93
+ const fieldPreview = wrapper.findComponent(FieldPreview)
94
+ expect(fieldPreview.exists()).toBe(true)
95
+ })
96
+
97
+ it('renders a normal when no fields are provided and the sender is user', () => {
98
+ const wrapper = mount(ChatBotMessage, {
99
+ props: { ...props, sender: 'user' }
100
+ })
101
+
102
+ const fieldPreview = wrapper.findComponent(FieldPreview)
103
+ expect(fieldPreview.exists()).toBe(false)
104
+
105
+ const markdownRender = wrapper.findComponent(MarkdownRender)
106
+ expect(markdownRender.exists()).toBe(false)
107
+
108
+ const messageDiv = wrapper.find('.q-chatbot__user-text')
109
+ expect(messageDiv.exists()).toBe(true)
110
+ expect(messageDiv.text()).toBe(props.message)
111
+ })
112
+
113
+ it('renders markdown-render when no fields are provided', () => {
114
+ const wrapper = mount(ChatBotMessage, { props })
115
+
116
+ const markdownRender = wrapper.findComponent(MarkdownRender)
117
+ expect(markdownRender.exists()).toBe(true)
118
+ })
119
+
120
+ it('adds the text to the clipboard when copy event is emmited', async () => {
121
+ const wrapper = mount(ChatBotMessage, { props })
122
+
123
+ const messageButtons = wrapper.findComponent(ChatBotMessageButtons)
124
+ const copyButton = messageButtons.find('.q-chatbot__copy-button')
125
+
126
+ await copyButton.trigger('click')
127
+ const clipboardText = await navigator.clipboard.readText()
128
+
129
+ expect(navigator.clipboard).toBeDefined()
130
+ expect(clipboardText).toBe(props.message)
131
+ })
132
+
133
+ it('does not add the text to the clipboard if message is empty', async () => {
134
+ const wrapper = mount(ChatBotMessage, { props: { ...props, message: '' } })
135
+ const messageButtons = wrapper.findComponent(ChatBotMessageButtons)
136
+ const copyButton = messageButtons.find('.q-chatbot__copy-button')
137
+
138
+ await copyButton.trigger('click')
139
+ const clipboardText = await navigator.clipboard.readText()
140
+
141
+ expect(navigator.clipboard).toBeDefined()
142
+ expect(clipboardText).toBe('')
143
+ })
144
+
145
+ it('catchs errors when trying to copy to clipboard', async () => {
146
+ const wrapper = mount(ChatBotMessage, { props })
147
+ const messageButtons = wrapper.findComponent(ChatBotMessageButtons)
148
+ const copyButton = messageButtons.find('.q-chatbot__copy-button')
149
+
150
+ vi.spyOn(navigator.clipboard, 'writeText').mockRejectedValueOnce(
151
+ new Error('Clipboard error')
152
+ )
153
+ // Suppress console.error for this test
154
+ const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
155
+
156
+ await copyButton.trigger('click')
157
+ const clipboardText = await navigator.clipboard.readText()
158
+
159
+ expect(navigator.clipboard).toBeDefined()
160
+ expect(clipboardText).toBe('')
161
+ consoleErrorSpy.mockRestore()
162
+ })
163
+
164
+ it('emits apply-fields event with fields when apply all button is clicked', async () => {
165
+ const testFields = [
166
+ {
167
+ id: 'field-1',
168
+ type: 'text',
169
+ text: 'Sample Field',
170
+ name: 'This is a sample field value'
171
+ },
172
+ {
173
+ id: 'field-2',
174
+ type: 'number',
175
+ text: 'Number Field',
176
+ name: '42'
177
+ }
178
+ ]
179
+ addChatMessage('Test message 1', 'bot').fields = testFields
180
+
181
+ const wrapper = mount(ChatBotMessage, {
182
+ props: { ...props, sender: 'bot', fields: testFields }
183
+ })
184
+
185
+ const messageButtons = wrapper.findComponent(ChatBotMessageButtons)
186
+ const applyAllButton = messageButtons.find('.q-chatbot__apply-all-button')
187
+
188
+ expect(applyAllButton.exists()).toBe(true)
189
+ await applyAllButton.trigger('click')
190
+
191
+ const fieldsToApply = testFields.map((field) => {
192
+ return {
193
+ id: field.id,
194
+ text: parseFieldValue(field.type, field.text)
195
+ }
196
+ })
197
+
198
+ expect(wrapper.emitted('apply-fields')).toBeTruthy()
199
+ expect(wrapper.emitted('apply-fields')?.[0]).toEqual([fieldsToApply])
200
+ })
201
+
202
+ it('emits apply-field when a single field is applied', async () => {
203
+ const testFields = [
204
+ {
205
+ id: 'field-1',
206
+ type: 'text',
207
+ text: 'Sample Field',
208
+ name: 'This is a sample field value'
209
+ }
210
+ ]
211
+
212
+ addChatMessage('Test message 1', 'bot').fields = testFields
213
+
214
+ const wrapper = mount(ChatBotMessage, {
215
+ props: { ...props, sender: 'bot', fields: testFields }
216
+ })
217
+ const fieldPreview = wrapper.findComponent(FieldPreview)
218
+ expect(fieldPreview.exists()).toBe(true)
219
+
220
+ const applyButton = fieldPreview.find("[data-testid='apply-button']")
221
+ expect(applyButton.exists()).toBe(true)
222
+
223
+ await applyButton.trigger('click')
224
+
225
+ const fieldToApply = [
226
+ {
227
+ id: testFields[0].id,
228
+ text: parseFieldValue(testFields[0].type, testFields[0].text)
229
+ }
230
+ ]
231
+
232
+ expect(wrapper.emitted('apply-fields')).toBeTruthy()
233
+ expect(wrapper.emitted('apply-fields')?.[0]).toEqual([fieldToApply])
234
+ })
235
+
236
+ it('renders a different preview when the file is not an image', async () => {
237
+ const file: ChatBotFile = {
238
+ fileData: new File(['dummy content'], 'example.pdf', { type: 'application/pdf' }),
239
+ previewUrl: ''
240
+ }
241
+
242
+ const wrapper = mount(ChatBotMessage, {
243
+ props: { ...props, file }
244
+ })
245
+
246
+ const img = wrapper.find('.q-chatbot__image-preview img')
247
+ expect(img.exists()).toBe(false)
248
+
249
+ const filePreview = wrapper.find('.q-chatbot__file-preview-container')
250
+ expect(filePreview.exists()).toBe(true)
251
+
252
+ const fileName = wrapper.find('.q-chatbot__file-name')
253
+ expect(fileName.exists()).toBe(true)
254
+ expect(fileName.text()).toBe(file.fileData.name)
255
+
256
+ const fileExtension = wrapper.find('.q-chatbot__file-extension')
257
+ expect(fileExtension.exists()).toBe(true)
258
+ expect(fileExtension.text()).toBe('PDF')
259
+ })
260
+ })
@@ -0,0 +1,35 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`ChatBotMessage > renders the component with default props 1`] = `
4
+ "<div class="q-chatbot__message-container"><span data-test="" type="img" alt="Sender Image" class="q-chatbot__profile"></span>
5
+ <div class="q-chatbot__message-wrapper">
6
+ <!--v-if-->
7
+ <div class="q-chatbot__message">
8
+ <div class="markdown-renderer q-chatbot__text">
9
+ <p>Hello, this is a test message.</p>
10
+ </div>
11
+ </div>
12
+ </div>
13
+ <!--teleport start-->
14
+ <transition-stub name="fade" appear="true" persisted="false" css="true">
15
+ <!--v-if-->
16
+ </transition-stub>
17
+ <transition-stub name="fade" appear="true" persisted="false" css="true">
18
+ <!--v-if-->
19
+ </transition-stub>
20
+ <!--teleport end-->
21
+ <!--v-if-->
22
+ <div class="q-chatbot__feedback-buttons">
23
+ <div class="q-button-group" role="group"><button type="button" class="q-button q-button--outlined q-button--primary q-button--borderless q-chatbot__good-response-button" title="Good response">
24
+ <!--v-if--><span class="q-button__content"><span data-test="thumb-up"></span> </span>
25
+ </button><button type="button" class="q-button q-button--outlined q-button--primary q-button--borderless q-chatbot__bad-response-button" title="Bad response">
26
+ <!--v-if--><span class="q-button__content"><span data-test="thumb-down"></span> </span>
27
+ </button><button type="button" class="q-button q-button--outlined q-button--primary q-button--borderless q-chatbot__copy-button" title="Copy response">
28
+ <!--v-if--><span class="q-button__content"><span data-test="copy-content"></span> </span>
29
+ </button>
30
+ <!--v-if-->
31
+ </div>
32
+ </div>
33
+ <div class="q-chatbot__sender">11:47</div>
34
+ </div>"
35
+ `;
@@ -1,4 +1,5 @@
1
- import { ChatBotMessageSender, FieldData } from '@/components/ChatBot/types'
1
+ import type { ChatBotMessageSender, FieldData } from '@/components/ChatBot/types'
2
+ import type { ChatBotFile } from '../ChatBotInput'
2
3
 
3
4
  export type ChatBotMessageProps = {
4
5
  /*
@@ -27,9 +28,9 @@ export type ChatBotMessageProps = {
27
28
  dateFormat: string
28
29
 
29
30
  /**
30
- * Image preview URL
31
+ * File Preview
31
32
  */
32
- imagePreviewUrl?: string
33
+ file?: ChatBotFile
33
34
 
34
35
  /**
35
36
  * Default api endpoint
@@ -2,7 +2,7 @@
2
2
 
3
3
  exports[`FieldPreview > renders correctly with default props 1`] = `
4
4
  "<div class="q-field-preview">
5
- <div class="q-field-preview__toolbar"><span>Suggestions for field <b>Test Field</b></span></div>
5
+ <div class="q-field-preview__toolbar"><span>Suggestions for field: <b>Test Field</b></span></div>
6
6
  <div class="q-field-preview__content">
7
7
  <div class="markdown-renderer">
8
8
  <p>Hello, World!</p>
@@ -3,6 +3,7 @@ import { v4 as uuidv4 } from 'uuid'
3
3
 
4
4
  import type { Ref } from 'vue'
5
5
  import type { ChatMessage, ChatBotMessageSender } from '@/components/ChatBot/types'
6
+ import type { ChatBotFile } from '@/components/ChatBotInput'
6
7
 
7
8
  const messages: Ref<ChatMessage[]> = ref([])
8
9
  const nextMessageId = ref(1)
@@ -11,7 +12,7 @@ export function useChatMessages() {
11
12
  function addChatMessage(
12
13
  message: string,
13
14
  sender?: ChatBotMessageSender,
14
- imagePreviewUrl?: string | null,
15
+ file?: ChatBotFile,
15
16
  sessionID?: string,
16
17
  isWelcomeMessage?: boolean
17
18
  ) {
@@ -21,7 +22,7 @@ export function useChatMessages() {
21
22
  date: new Date(),
22
23
  sender: sender || 'bot',
23
24
  sessionID: sessionID || uuidv4(),
24
- imagePreviewUrl: imagePreviewUrl || undefined,
25
+ file: file,
25
26
  isWelcomeMessage,
26
27
  fields: []
27
28
  }
@@ -30,6 +30,7 @@ export function useTexts() {
30
30
  regenerateResponsePrompt: 'Regenerate a new response for field {0}',
31
31
  regenerateResponse: 'Regenerate response',
32
32
  generatingResponse: 'Generating',
33
- suggestionsForField: 'Suggestions for field'
33
+ suggestionsForField: 'Suggestions for field:',
34
+ fileUpload: 'Upload File'
34
35
  }
35
36
  }