app-tutor-ai-consumer 1.37.0 → 1.39.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 (53) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/package.json +1 -1
  3. package/src/config/datahub/actions.ts +3 -2
  4. package/src/config/datahub/constants.ts +1 -0
  5. package/src/config/datahub/entities.ts +2 -1
  6. package/src/config/datahub/schemas/constants.ts +2 -1
  7. package/src/config/datahub/schemas/tutor/__tests__/{try-send-tutor-message.spec.ts → try-send-message.spec.ts} +11 -6
  8. package/src/config/datahub/schemas/tutor/__tests__/{view-tutor-answer-message.spec.ts → view-answer-message.spec.ts} +9 -4
  9. package/src/config/datahub/schemas/tutor/__tests__/view-chat.spec.ts +38 -0
  10. package/src/config/datahub/schemas/tutor/index.ts +2 -2
  11. package/src/config/datahub/schemas/tutor/{try-send-tutor-message.ts → try-send-message.ts} +22 -12
  12. package/src/config/datahub/schemas/tutor/{view-tutor-answer-message.ts → view-answer-message.ts} +20 -7
  13. package/src/config/datahub/schemas/tutor/view-chat.ts +56 -0
  14. package/src/config/datahub/types.ts +5 -0
  15. package/src/config/styles/global.css +3 -0
  16. package/src/config/tests/handlers.ts +3 -3
  17. package/src/modules/messages/__tests__/{signed-urls.builder.ts → files-signed-urls.builder.ts} +3 -3
  18. package/src/modules/messages/components/chat-file-preview/chat-file-preview.tsx +0 -1
  19. package/src/modules/messages/components/chat-file-preview/components/document-preview/document-preview.tsx +4 -3
  20. package/src/modules/messages/components/chat-file-preview/components/document-preview/types.ts +0 -3
  21. package/src/modules/messages/components/chat-file-preview/utils.ts +17 -0
  22. package/src/modules/messages/components/chat-file-uploader/chat-file-uploader.builder.ts +4 -7
  23. package/src/modules/messages/components/chat-file-uploader/chat-file-uploader.spec.tsx +0 -20
  24. package/src/modules/messages/components/chat-file-uploader/chat-file-uploader.tsx +16 -14
  25. package/src/modules/messages/components/chat-file-uploader-wrapper/chat-file-uploader-wrapper.tsx +34 -0
  26. package/src/modules/messages/components/chat-file-uploader-wrapper/index.ts +1 -0
  27. package/src/modules/messages/components/message-item/message-item.tsx +10 -0
  28. package/src/modules/messages/constants.ts +1 -1
  29. package/src/{lib → modules/messages}/hooks/use-chat-file-upload/constants.ts +2 -0
  30. package/src/modules/messages/hooks/use-chat-file-upload/use-chat-file-upload.spec.ts +19 -0
  31. package/src/modules/messages/hooks/use-chat-file-upload/use-chat-file-upload.ts +102 -0
  32. package/src/modules/messages/hooks/use-get-files-signed-urls/index.ts +1 -0
  33. package/src/modules/messages/hooks/{use-get-signed-urls/use-get-signed-urls.spec.tsx → use-get-files-signed-urls/use-get-files-signed-urls.spec.ts} +7 -7
  34. package/src/modules/messages/hooks/use-get-files-signed-urls/use-get-files-signed-urls.ts +25 -0
  35. package/src/modules/messages/hooks/use-send-text-message/use-send-text-message.spec.tsx +1 -1
  36. package/src/modules/messages/hooks/use-send-text-message/use-send-text-message.tsx +31 -19
  37. package/src/modules/messages/hooks/use-subscribe-message-received-event/use-subscribe-message-received-event.tsx +9 -5
  38. package/src/modules/messages/hooks/use-upload-file/index.ts +1 -0
  39. package/src/modules/messages/hooks/use-upload-file/use-upload-file.ts +15 -0
  40. package/src/modules/messages/service.direct.ts +27 -10
  41. package/src/modules/messages/store/attached-file.atom.ts +7 -0
  42. package/src/modules/messages/types.ts +30 -2
  43. package/src/modules/widget/components/chat-page/chat-page.tsx +26 -11
  44. package/src/modules/widget/hooks/index.ts +1 -0
  45. package/src/modules/widget/hooks/use-send-view-chat-event/index.ts +1 -0
  46. package/src/modules/widget/hooks/use-send-view-chat-event/use-send-view-chat-event.tsx +28 -0
  47. package/src/lib/hooks/use-chat-file-upload/types.ts +0 -14
  48. package/src/lib/hooks/use-chat-file-upload/use-chat-file-upload.spec.ts +0 -59
  49. package/src/lib/hooks/use-chat-file-upload/use-chat-file-upload.ts +0 -28
  50. package/src/modules/messages/components/chat-file-uploader/types.ts +0 -4
  51. package/src/modules/messages/hooks/use-get-signed-urls/index.ts +0 -1
  52. package/src/modules/messages/hooks/use-get-signed-urls/use-get-signed-urls.tsx +0 -38
  53. /package/src/{lib → modules/messages}/hooks/use-chat-file-upload/index.ts +0 -0
@@ -1,14 +1,17 @@
1
- import { useCallback, useEffect, useMemo, useRef } from 'react'
1
+ import { Fragment, useCallback, useEffect, useMemo, useRef } from 'react'
2
2
  import { useInfiniteQuery } from '@tanstack/react-query'
3
3
 
4
4
  import { useMediaQuery } from '@/src/lib/hooks'
5
5
  import { isTextEmpty } from '@/src/lib/utils/is-text-empty'
6
6
  import { ChatInput, MessagesList, useChatInputValueAtom } from '@/src/modules/messages/components'
7
+ import { ChatFileUploaderWrapper } from '@/src/modules/messages/components/chat-file-uploader-wrapper'
7
8
  import { MessagesContainer } from '@/src/modules/messages/components/messages-container'
8
9
  import { getAllMessagesQuery, useSendTextMessage } from '@/src/modules/messages/hooks'
9
10
  import { useMessagesMaxCount } from '@/src/modules/messages/store'
11
+ import { useAttachedFileAtom } from '@/src/modules/messages/store/attached-file.atom'
10
12
  import { useGetProfile } from '@/src/modules/profile'
11
13
  import { TutorWidgetEvents } from '../../events'
14
+ import { useSendViewChatEvent } from '../../hooks'
12
15
  import { useSendViewTutorEvent } from '../../hooks/use-send-view-tutor-event'
13
16
  import {
14
17
  useWidgetLoadingAtom,
@@ -24,6 +27,7 @@ function ChatPage() {
24
27
  const chatInputRef = useRef<HTMLTextAreaElement>(null)
25
28
  const scrollerRef = useRef<HTMLDivElement>(null)
26
29
  const settings = useWidgetSettingsAtomValue()
30
+ const [attachedFileAtom] = useAttachedFileAtom()
27
31
  const profileQuery = useGetProfile()
28
32
  const widgetTabs = useWidgetTabsValueAtom()
29
33
  const sendTextMessageMutation = useSendTextMessage()
@@ -45,11 +49,11 @@ function ChatPage() {
45
49
  }),
46
50
  [conversationId, limit, profileId]
47
51
  )
52
+ useSendViewTutorEvent()
53
+ useSendViewChatEvent()
48
54
 
49
55
  const messagesQuery = useInfiniteQuery(messagesQueryConfig)
50
56
 
51
- useSendViewTutorEvent()
52
-
53
57
  const handleSendMessage = () => {
54
58
  const text = chatInputRef.current?.value ?? ''
55
59
 
@@ -100,18 +104,29 @@ function ChatPage() {
100
104
  }
101
105
  }, [messagesQuery.isError, setWidgetLoading])
102
106
 
107
+ const ChatInputWrapper = settings?.config?.metadata?.showFileUpload
108
+ ? ChatFileUploaderWrapper
109
+ : Fragment
110
+
103
111
  return (
104
112
  <PageLayout
105
113
  asideChild={
106
114
  <>
107
- <ChatInput
108
- name='new-chat-msg-input'
109
- ref={chatInputRef}
110
- onSend={widgetTabs.currentTab === 'chat' ? handleSendMessage : undefined}
111
- loading={sendTextMessageMutation.isPending}
112
- inputDisabled={messagesQuery?.isLoading}
113
- buttonDisabled={widgetLoading || messagesQuery?.isLoading || !value.trim()}
114
- />
115
+ <ChatInputWrapper>
116
+ <ChatInput
117
+ name='new-chat-msg-input'
118
+ ref={chatInputRef}
119
+ onSend={widgetTabs.currentTab === 'chat' ? handleSendMessage : undefined}
120
+ loading={sendTextMessageMutation.isPending}
121
+ inputDisabled={messagesQuery?.isLoading}
122
+ buttonDisabled={
123
+ widgetLoading ||
124
+ messagesQuery?.isLoading ||
125
+ !value.trim() ||
126
+ (Boolean(attachedFileAtom) && !attachedFileAtom?.downloadUrl)
127
+ }
128
+ />
129
+ </ChatInputWrapper>
115
130
 
116
131
  <div className='mx-auto w-fit'>
117
132
  <AIDisclaimer />
@@ -1,3 +1,4 @@
1
1
  export * from './use-listen-to-theme-change-event'
2
2
  export * from './use-listen-to-visibility-events'
3
+ export * from './use-send-view-chat-event'
3
4
  export * from './use-send-view-tutor-event'
@@ -0,0 +1 @@
1
+ export { default as useSendViewChatEvent } from './use-send-view-chat-event'
@@ -0,0 +1,28 @@
1
+ import { useEffect, useRef } from 'react'
2
+
3
+ import { AgentType, ComponentSource, DataHubEntities, DataHubService } from '@/src/config/datahub'
4
+ import ViewChatSchema from '@/src/config/datahub/schemas/tutor/view-chat'
5
+ import { useIsAgentParentAtomValue, useWidgetSettingsAtomValue } from '../../store'
6
+
7
+ function useSendViewChatEvent() {
8
+ const isAgentMode = useIsAgentParentAtomValue()
9
+ const settings = useWidgetSettingsAtomValue()
10
+
11
+ const sent = useRef(false)
12
+
13
+ useEffect(() => {
14
+ if (sent.current) return
15
+
16
+ const schema = new ViewChatSchema({
17
+ componentSource: isAgentMode ? ComponentSource.PRODUCT_AGENT : ComponentSource.HOTMART_TUTOR,
18
+ agent: isAgentMode ? AgentType.PRODUCT_AGENT : AgentType.HOTMART_TUTOR,
19
+ conversationId: settings?.conversationId,
20
+ entity: DataHubEntities.CONVERSATIONAL_AGENT
21
+ })
22
+
23
+ DataHubService.sendEvent({ schema })
24
+ sent.current = true
25
+ }, [isAgentMode, settings?.conversationId])
26
+ }
27
+
28
+ export default useSendViewChatEvent
@@ -1,14 +0,0 @@
1
- export type FileType = 'document' | 'image'
2
-
3
- export type SelectedFile = {
4
- file: File
5
- type: FileType
6
- name: string
7
- size: number
8
- previewUrl?: string
9
- }
10
-
11
- export type UseFileUploadReturn = {
12
- selectedFile: SelectedFile | null
13
- handleSelectFile: (file: File, type: FileType) => void
14
- }
@@ -1,59 +0,0 @@
1
- import { act, renderHook } from '@/src/config/tests'
2
-
3
- import { useChatFileUpload } from './use-chat-file-upload'
4
-
5
- describe('useChatFileUpload', () => {
6
- beforeEach(() => {
7
- global.URL.createObjectURL = vi.fn(() => 'blob:mock-url')
8
- })
9
-
10
- afterEach(() => {
11
- vi.clearAllMocks()
12
- })
13
-
14
- it('should initialize with default values', () => {
15
- const { result } = renderHook(() => useChatFileUpload())
16
-
17
- expect(result.current.selectedFile).toBeNull()
18
- })
19
-
20
- describe('when selecting a document file', () => {
21
- it('should store the file with correct properties', () => {
22
- const { result } = renderHook(() => useChatFileUpload())
23
-
24
- const mockFile = new File(['content'], 'document.pdf', { type: 'application/pdf' })
25
-
26
- act(() => {
27
- result.current.handleSelectFile(mockFile, 'document')
28
- })
29
-
30
- expect(result.current.selectedFile).toEqual({
31
- file: mockFile,
32
- type: 'document',
33
- name: 'document.pdf',
34
- size: mockFile.size
35
- })
36
- })
37
- })
38
-
39
- describe('when selecting an image file', () => {
40
- it('should create a preview URL', () => {
41
- const { result } = renderHook(() => useChatFileUpload())
42
-
43
- const mockImage = new File(['image'], 'photo.jpg', { type: 'image/jpeg' })
44
-
45
- act(() => {
46
- result.current.handleSelectFile(mockImage, 'image')
47
- })
48
-
49
- expect(result.current.selectedFile).toEqual({
50
- file: mockImage,
51
- type: 'image',
52
- name: 'photo.jpg',
53
- size: mockImage.size,
54
- previewUrl: 'blob:mock-url'
55
- })
56
- expect(global.URL.createObjectURL).toHaveBeenCalledWith(mockImage)
57
- })
58
- })
59
- })
@@ -1,28 +0,0 @@
1
- import { useState } from 'react'
2
-
3
- import { FILE_TYPES_KEYS } from './constants'
4
- import type { FileType, SelectedFile, UseFileUploadReturn } from './types'
5
-
6
- export function useChatFileUpload(): UseFileUploadReturn {
7
- const [selectedFile, setSelectedFile] = useState<SelectedFile | null>(null)
8
-
9
- const handleSelectFile = (file: File, type: FileType) => {
10
- const newFile: SelectedFile = {
11
- file,
12
- type,
13
- name: file.name,
14
- size: file.size
15
- }
16
-
17
- if (type === FILE_TYPES_KEYS.IMAGE) {
18
- newFile.previewUrl = URL.createObjectURL(file)
19
- }
20
-
21
- setSelectedFile(newFile)
22
- }
23
-
24
- return {
25
- selectedFile,
26
- handleSelectFile
27
- }
28
- }
@@ -1,4 +0,0 @@
1
- export type ChatFileUploaderProps = {
2
- disabled?: boolean
3
- loading?: boolean
4
- }
@@ -1 +0,0 @@
1
- export * from './use-get-signed-urls'
@@ -1,38 +0,0 @@
1
- import { useQuery } from '@tanstack/react-query'
2
-
3
- import { MessagesEndpoints } from '../../constants'
4
- import DirectMessagesService from '../../service.direct'
5
-
6
- export type UseGetSignedUrlsProps = {
7
- chatId: string
8
- fileExtension: string
9
- fileNameWithExtension: string
10
- productId: number
11
- enabled?: boolean
12
- }
13
-
14
- export function useGetSignedUrls({
15
- chatId,
16
- fileExtension,
17
- fileNameWithExtension,
18
- productId,
19
- enabled
20
- }: UseGetSignedUrlsProps) {
21
- return useQuery({
22
- queryKey: [
23
- MessagesEndpoints.getSignedUrls(),
24
- chatId,
25
- fileExtension,
26
- fileNameWithExtension,
27
- productId
28
- ],
29
- queryFn: () =>
30
- DirectMessagesService.getSignedUrls({
31
- chatId,
32
- fileExtension,
33
- fileNameWithExtension,
34
- productId
35
- }),
36
- enabled
37
- })
38
- }