@quidgest/chatbot 0.0.5 → 0.0.7

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.
@@ -1,5 +1,12 @@
1
1
  <script setup lang="ts">
2
- import { onMounted, nextTick, computed, ref, watch, defineOptions } from 'vue'
2
+ import {
3
+ onMounted,
4
+ nextTick,
5
+ computed,
6
+ ref,
7
+ watch,
8
+ defineOptions
9
+ } from 'vue'
3
10
  import type { Ref } from 'vue'
4
11
  import Axios from 'axios'
5
12
  import type { AxiosResponse } from 'axios'
@@ -7,31 +14,33 @@
7
14
 
8
15
  import {
9
16
  QButton,
10
- QTextField,
17
+ QTextField,
11
18
  QInputGroup,
12
19
  QIcon
13
- } from '@quidgest/ui'
20
+ } from '@quidgest/ui/components'
14
21
 
15
22
  import type { ResourceStrings } from '@/types/texts.type'
16
- import type { ChatBotMessage, ChatBotMessageContent } from '@/types/message.type'
17
-
23
+ import type {
24
+ ChatBotMessage,
25
+ ChatBotMessageContent
26
+ } from '@/types/message.type'
18
27
 
19
28
  let messages: Ref<ChatBotMessage[]> = ref([])
20
29
  let msgHistoryStack: string[] = []
21
30
  let nextMessageId: number = 1
22
31
  let userPrompt: Ref<string> = ref('')
32
+ let isLoading: Ref<boolean> = ref(false)
23
33
  let isChatDisabled: boolean = false
24
- let isLoading: boolean = false
25
-
34
+
26
35
  // refs
27
- const messagesContainer = ref<HTMLElement | null>(null)
36
+ const scrollElement = ref<HTMLElement | null>(null)
28
37
  const promptInput = ref<HTMLInputElement | null>(null)
29
38
 
30
39
  export type ChatBotProps = {
31
40
  /**
32
41
  * API Enpoint URL
33
42
  */
34
- apiEndpoint?: string,
43
+ apiEndpoint?: string
35
44
 
36
45
  /**
37
46
  * Static resource texts used by ChatBot
@@ -47,6 +56,11 @@
47
56
  * Project aplication path
48
57
  */
49
58
  projectPath: string
59
+
60
+ /**
61
+ * Project locale
62
+ */
63
+ dateFormat?: string
50
64
  }
51
65
 
52
66
  const props = withDefaults(defineProps<ChatBotProps>(), {
@@ -66,7 +80,6 @@
66
80
 
67
81
  onMounted(() => {
68
82
  initChat()
69
- nextTick(scrollChatToBottom)
70
83
  })
71
84
 
72
85
  const userMessages = computed(() => {
@@ -76,31 +89,31 @@
76
89
  function setDisabledState(state: boolean) {
77
90
  isChatDisabled = state
78
91
  }
79
-
92
+
80
93
  function initChat() {
81
94
  Axios.post(props.apiEndpoint + '/auth/login', {
82
95
  username: props.username,
83
96
  password: 'test'
84
97
  })
85
- .then((response: AxiosResponse) => {
86
- if (response.status != 200 || !response.data.success) {
98
+ .then((response: AxiosResponse) => {
99
+ if (response.status != 200 || !response.data.success) {
100
+ setDisabledState(true)
101
+ addChatMessage(props.texts.loginError)
102
+ return console.log(
103
+ `Unsuccessful login, endpoint gave status ${response.status}`
104
+ )
105
+ }
106
+
107
+ loadChatData()
108
+ })
109
+ .catch((error: Error) => {
87
110
  setDisabledState(true)
88
111
  addChatMessage(props.texts.loginError)
89
- return console.log(
90
- `Unsuccessful login, endpoint gave status ${response.status}`
112
+ console.log(
113
+ 'The following error ocurred while trying to login: \n' +
114
+ error
91
115
  )
92
- }
93
-
94
- loadChatData()
95
- })
96
- .catch((error: Error) => {
97
- setDisabledState(true)
98
- addChatMessage(props.texts.loginError)
99
- console.log(
100
- 'The following error ocurred while trying to login: \n' +
101
- error
102
- )
103
- })
116
+ })
104
117
  }
105
118
 
106
119
  function loadChatData() {
@@ -108,28 +121,33 @@
108
121
  username: props.username,
109
122
  project: props.projectPath
110
123
  })
111
- .then((response: AxiosResponse) => {
112
- if (response.status != 200 || !response.data.success) {
124
+ .then((response: AxiosResponse) => {
125
+ if (response.status != 200 || !response.data.success) {
126
+ setDisabledState(true)
127
+ addChatMessage(props.texts.loginError)
128
+ return console.log(
129
+ `Unsuccessful load, endpoint gave status ${response.status}`
130
+ )
131
+ }
132
+
133
+ sendInitialMessage()
134
+ response.data.history.forEach(
135
+ (message: ChatBotMessageContent) => {
136
+ addChatMessage(
137
+ message.content,
138
+ message.type === 'ai' ? 'bot' : 'user'
139
+ )
140
+ }
141
+ )
142
+ })
143
+ .catch((error: Error) => {
113
144
  setDisabledState(true)
114
145
  addChatMessage(props.texts.loginError)
115
- return console.log(
116
- `Unsuccessful load, endpoint gave status ${response.status}`
146
+ console.log(
147
+ 'The following error ocurred while trying to login: \n' +
148
+ error
117
149
  )
118
- }
119
-
120
- sendInitialMessage()
121
- response.data.history.forEach((message: ChatBotMessageContent) => {
122
- addChatMessage(message.content, message.type === "ai" ? "bot" : "user")
123
150
  })
124
- })
125
- .catch((error: Error) => {
126
- setDisabledState(true)
127
- addChatMessage(props.texts.loginError)
128
- console.log(
129
- 'The following error ocurred while trying to login: \n' +
130
- error
131
- )
132
- })
133
151
  }
134
152
 
135
153
  function addChatMessage(message: string, sender: 'bot' | 'user' = 'bot') {
@@ -139,6 +157,8 @@
139
157
  date: new Date(),
140
158
  sender: sender
141
159
  })
160
+
161
+ nextTick(scrollToBottom)
142
162
  }
143
163
 
144
164
  function getLastMessage() {
@@ -155,23 +175,16 @@
155
175
  messages.value = []
156
176
  msgHistoryStack = []
157
177
  userPrompt.value = ''
158
- isLoading = false
178
+ isLoading.value = false
159
179
  setDisabledState(false)
160
180
  }
161
181
 
162
- function scrollChatToBottom() {
163
- if(messagesContainer.value == null) return;
164
-
165
- const element = messagesContainer.value
166
- setTimeout(
167
- () =>
168
- (element.scrollIntoView(false)),
169
- 100
170
- )
182
+ function scrollToBottom() {
183
+ scrollElement.value?.scrollIntoView({ behavior: 'smooth' })
171
184
  }
172
185
 
173
186
  function handleKey(event: KeyboardEvent) {
174
- if(promptInput.value == null) return;
187
+ if (promptInput.value == null) return
175
188
 
176
189
  if (event.key == 'ArrowUp') {
177
190
  //No user messages, no need to continue
@@ -180,9 +193,7 @@
180
193
  //Get next message to read
181
194
  let lastMsgObj =
182
195
  userMessages.value[
183
- userMessages.value.length -
184
- 1 -
185
- msgHistoryStack.length
196
+ userMessages.value.length - 1 - msgHistoryStack.length
186
197
  ]
187
198
 
188
199
  //No more messages to go through
@@ -211,18 +222,17 @@
211
222
  userPrompt.value = previousHistoryText
212
223
  }
213
224
  }
214
-
225
+
215
226
  function sendMessage() {
216
227
  if (
217
228
  userPrompt.value.trim().length == 0 ||
218
- isLoading ||
229
+ isLoading.value ||
219
230
  isChatDisabled
220
231
  )
221
232
  return
222
233
 
223
234
  addChatMessage(userPrompt.value, 'user')
224
235
 
225
- scrollChatToBottom()
226
236
  setChatPrompt(userPrompt.value)
227
237
 
228
238
  userPrompt.value = '' //Clear user input
@@ -239,33 +249,33 @@
239
249
  user: props.username
240
250
  }
241
251
 
242
- isLoading = true
252
+ isLoading.value = true
243
253
  Axios({
244
254
  url: props.apiEndpoint + '/prompt/message',
245
255
  method: 'POST',
246
256
  data: params,
247
257
  onDownloadProgress: (progressEvent) => {
248
- const chunk =
249
- progressEvent.event?.currentTarget.response
258
+ const chunk = progressEvent.event?.currentTarget.response
250
259
  const status = progressEvent.event?.currentTarget.status
251
260
 
252
261
  if (status != 200) return
253
262
 
254
263
  if (msg) msg.message = chunk
264
+ scrollToBottom()
255
265
  }
256
266
  })
257
- .then(({ data }) => {
258
- if (msg) msg.message = data
259
- })
260
- .catch((error) => {
261
- addChatMessage(props.texts.botIsSick)
267
+ .then(({ data }) => {
268
+ if (msg) msg.message = data
269
+ })
270
+ .catch((error) => {
271
+ addChatMessage(props.texts.botIsSick)
262
272
 
263
- setDisabledState(true)
264
- console.log(error)
265
- })
266
- .finally(() => {
267
- isLoading = false
268
- })
273
+ setDisabledState(true)
274
+ console.log(error)
275
+ })
276
+ .finally(() => {
277
+ isLoading.value = false
278
+ })
269
279
  }
270
280
 
271
281
  function setCursorPosition(elem: HTMLInputElement, pos: number) {
@@ -273,67 +283,65 @@
273
283
  elem.setSelectionRange(pos, pos)
274
284
  }
275
285
 
276
- function clearChat() {
286
+ function clearChat() {
277
287
  Axios.post(props.apiEndpoint + '/prompt/clear', {
278
288
  username: props.username,
279
289
  project: props.projectPath
280
290
  })
281
- .then((response: AxiosResponse) => {
282
- if (response.status != 200 || !response.data.success) {
291
+ .then((response: AxiosResponse) => {
292
+ if (response.status != 200 || !response.data.success) {
293
+ setDisabledState(true)
294
+ addChatMessage(props.texts.loginError)
295
+ return console.log(
296
+ `Unsuccessful login, endpoint gave status ${response.status}`
297
+ )
298
+ }
299
+
300
+ resetChat()
301
+ sendInitialMessage()
302
+ })
303
+ .catch((error: Error) => {
283
304
  setDisabledState(true)
284
305
  addChatMessage(props.texts.loginError)
285
- return console.log(
286
- `Unsuccessful login, endpoint gave status ${response.status}`
306
+ console.log(
307
+ 'The following error ocurred while trying to communicate with the endpoint: \n' +
308
+ error
287
309
  )
288
- }
289
-
290
- resetChat()
291
- sendInitialMessage()
292
- })
293
- .catch((error: Error) => {
294
- setDisabledState(true)
295
- addChatMessage(props.texts.loginError)
296
- console.log(
297
- 'The following error ocurred while trying to communicate with the endpoint: \n' +
298
- error
299
- )
300
- })
310
+ })
301
311
  }
302
312
 
303
313
  function getMessageClasses(sender: 'user' | 'bot') {
304
314
  const classes: string[] = ['q-chatbot__messages-wrapper']
305
315
 
306
- if(sender == 'user')
307
- classes.push('q-chatbot__messages-wrapper_right')
316
+ if (sender == 'user') classes.push('q-chatbot__messages-wrapper_right')
308
317
 
309
318
  return classes
310
319
  }
311
320
 
312
- watch(() => props.apiEndpoint, () => {
313
- resetChat()
314
- initChat()
315
- })
321
+ watch(
322
+ () => props.apiEndpoint,
323
+ () => {
324
+ resetChat()
325
+ initChat()
326
+ }
327
+ )
316
328
 
317
329
  defineOptions({ name: 'ChatBot' })
318
330
  </script>
319
331
 
320
332
  <template>
321
333
  <div class="q-chatbot">
322
- <div
323
- ref="messagesContainer"
324
- class="q-chatbot__content">
325
-
334
+ <div class="q-chatbot__content">
326
335
  <!-- Chat tools -->
327
336
  <div class="q-chatbot__tools">
328
- <q-button
337
+ <QButton
329
338
  :title="props.texts.qButtonTitle"
330
- b-style="plain"
331
- class="clear-btn"
339
+ b-style="secondary"
332
340
  :disabled="isChatDisabled"
333
341
  borderless
334
342
  @click="clearChat">
335
- <q-icon icon="bin" />
336
- </q-button>
343
+ <QIcon icon="bin" />
344
+ </QButton>
337
345
  </div>
338
346
 
339
347
  <div class="q-chatbot__messages-container">
@@ -341,15 +349,20 @@
341
349
  v-for="message in messages"
342
350
  :key="message.id"
343
351
  :class="getMessageClasses(message.sender)">
344
- <CBMessage v-bind="message" :loading="isLoading && !message.message" />
352
+ <CBMessage
353
+ v-bind="message"
354
+ :date-format="props.dateFormat"
355
+ :loading="isLoading && !message.message" />
345
356
  </div>
346
357
  </div>
358
+
359
+ <div ref="scrollElement"></div>
347
360
  </div>
348
361
 
349
- <q-input-group
362
+ <QInputGroup
350
363
  size="block"
351
364
  :disabled="isChatDisabled">
352
- <q-text-field
365
+ <QTextField
353
366
  ref="promptInput"
354
367
  v-model="userPrompt"
355
368
  class="q-chatbot__input"
@@ -359,15 +372,16 @@
359
372
  @keydown="handleKey" />
360
373
 
361
374
  <template #append>
362
- <q-button
375
+ <QButton
363
376
  :title="props.texts.qButtonTitle"
364
377
  b-style="primary"
365
378
  class="q-chatbot__send"
366
- :disabled="isChatDisabled"
379
+ :disabled="isChatDisabled || isLoading"
380
+ :loading="isLoading"
367
381
  @click="sendMessage">
368
- <q-icon icon="send" />
369
- </q-button>
382
+ <QIcon icon="send" />
383
+ </QButton>
370
384
  </template>
371
- </q-input-group>
385
+ </QInputGroup>
372
386
  </div>
373
387
  </template>
@@ -1,7 +1,4 @@
1
- import CBMessage from "./CBMessage.vue"
2
- import type { CBMessageProps } from "./CBMessage.vue"
1
+ import CBMessage from './CBMessage.vue'
2
+ import type { CBMessageProps } from './CBMessage.vue'
3
3
 
4
- export {
5
- CBMessage,
6
- CBMessageProps
7
- }
4
+ export { CBMessage, CBMessageProps }
@@ -1,11 +1,11 @@
1
1
  export type ChatBotMessage = {
2
- id: number,
3
- message: string,
4
- date: Date,
2
+ id: number
3
+ message: string
4
+ date: Date
5
5
  sender: 'bot' | 'user'
6
6
  }
7
7
 
8
8
  export type ChatBotMessageContent = {
9
- content: string,
9
+ content: string
10
10
  type: string
11
- }
11
+ }
@@ -1,3 +1,3 @@
1
1
  export type ResourceStrings = {
2
2
  [key: string]: string
3
- }
3
+ }