app-tutor-ai-consumer 1.33.1 → 1.35.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 (129) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/config/vitest/__mocks__/sparkie.tsx +2 -2
  3. package/package.json +2 -2
  4. package/src/@types/index.d.ts +3 -2
  5. package/src/bootstrap.ts +40 -0
  6. package/src/config/tanstack/query-provider.tsx +15 -4
  7. package/src/config/tests/handlers.ts +5 -4
  8. package/src/config/theme/init-theme.ts +11 -5
  9. package/src/index.backup.tsx +61 -0
  10. package/src/index.tsx +80 -17
  11. package/src/lib/components/dropdown-actions/dropdown-actions.tsx +87 -0
  12. package/src/lib/components/dropdown-actions/dropdownActions.builder.ts +58 -0
  13. package/src/lib/components/dropdown-actions/dropdownActions.spec.tsx +76 -0
  14. package/src/lib/components/dropdown-actions/index.ts +1 -0
  15. package/src/lib/components/dropdown-actions/types.ts +16 -0
  16. package/src/lib/components/errors/generic/generic-error.tsx +19 -10
  17. package/src/lib/components/icons/document.svg +3 -0
  18. package/src/lib/components/icons/file.svg +3 -0
  19. package/src/lib/components/icons/icon-names.d.ts +7 -0
  20. package/src/lib/components/icons/image.svg +3 -0
  21. package/src/lib/components/icons/pdf.svg +3 -0
  22. package/src/lib/components/icons/plus.svg +3 -0
  23. package/src/lib/components/icons/retry.svg +3 -0
  24. package/src/lib/components/icons/spreadsheet.svg +3 -0
  25. package/src/lib/components/index.ts +1 -0
  26. package/src/lib/components/markdownrenderer/markdownrenderer.tsx +1 -3
  27. package/src/lib/hooks/index.ts +1 -0
  28. package/src/lib/hooks/use-chat-file-upload/constants.ts +11 -0
  29. package/src/lib/hooks/use-chat-file-upload/index.ts +1 -0
  30. package/src/lib/hooks/use-chat-file-upload/types.ts +14 -0
  31. package/src/lib/hooks/use-chat-file-upload/use-chat-file-upload.spec.ts +59 -0
  32. package/src/lib/hooks/use-chat-file-upload/use-chat-file-upload.ts +28 -0
  33. package/src/lib/hooks/use-click-outside/index.ts +1 -0
  34. package/src/lib/hooks/use-click-outside/use-click-outside.tsx +23 -0
  35. package/src/lib/hooks/use-click-outside/useClickOutside.spec.ts +102 -0
  36. package/src/lib/utils/index.ts +1 -0
  37. package/src/lib/utils/is-theme-dark.ts +21 -0
  38. package/src/main/hooks/use-initial-store/index.ts +1 -0
  39. package/src/main/hooks/use-initial-store/use-initial-store.tsx +64 -0
  40. package/src/main/hooks/use-initial-tab/index.ts +1 -0
  41. package/src/main/hooks/use-initial-tab/use-initial-tab.tsx +48 -0
  42. package/src/main/index.ts +1 -0
  43. package/src/main/main-content.tsx +14 -0
  44. package/src/main/main-wrapper.tsx +16 -0
  45. package/src/main/main.spec.tsx +5 -3
  46. package/src/main/main.tsx +7 -16
  47. package/src/main/types.ts +5 -0
  48. package/src/modules/global-providers/global-providers.tsx +2 -21
  49. package/src/modules/messages/__tests__/imessage-with-sender-data.builder.ts +1 -1
  50. package/src/modules/messages/__tests__/signed-urls.builder.ts +42 -0
  51. package/src/modules/messages/components/chat-file-preview/chat-file-preview.builder.ts +121 -0
  52. package/src/modules/messages/components/chat-file-preview/chat-file-preview.spec.tsx +107 -0
  53. package/src/modules/messages/components/chat-file-preview/chat-file-preview.tsx +45 -0
  54. package/src/modules/messages/components/chat-file-preview/components/close-button/close-button.tsx +31 -0
  55. package/src/modules/messages/components/chat-file-preview/components/close-button/index.ts +1 -0
  56. package/src/modules/messages/components/chat-file-preview/components/close-button/types.ts +4 -0
  57. package/src/modules/messages/components/chat-file-preview/components/document-preview/document-preview.tsx +63 -0
  58. package/src/modules/messages/components/chat-file-preview/components/document-preview/index.ts +1 -0
  59. package/src/modules/messages/components/chat-file-preview/components/document-preview/types.ts +10 -0
  60. package/src/modules/messages/components/chat-file-preview/components/error-preview/error-preview.tsx +37 -0
  61. package/src/modules/messages/components/chat-file-preview/components/error-preview/index.ts +1 -0
  62. package/src/modules/messages/components/chat-file-preview/components/error-preview/types.ts +4 -0
  63. package/src/modules/messages/components/chat-file-preview/components/image-preview/image-preview.tsx +32 -0
  64. package/src/modules/messages/components/chat-file-preview/components/image-preview/index.ts +1 -0
  65. package/src/modules/messages/components/chat-file-preview/components/image-preview/types.ts +6 -0
  66. package/src/modules/messages/components/chat-file-preview/constants.ts +22 -0
  67. package/src/modules/messages/components/chat-file-preview/index.ts +1 -0
  68. package/src/modules/messages/components/chat-file-preview/types.ts +13 -0
  69. package/src/modules/messages/components/chat-file-preview/utils.spec.ts +38 -0
  70. package/src/modules/messages/components/chat-file-preview/utils.ts +13 -0
  71. package/src/modules/messages/components/chat-file-uploader/chat-file-uploader.builder.ts +19 -0
  72. package/src/modules/messages/components/chat-file-uploader/chat-file-uploader.spec.tsx +58 -0
  73. package/src/modules/messages/components/chat-file-uploader/chat-file-uploader.tsx +80 -0
  74. package/src/modules/messages/components/chat-file-uploader/index.ts +1 -0
  75. package/src/modules/messages/components/chat-file-uploader/types.ts +4 -0
  76. package/src/modules/messages/components/index.ts +1 -0
  77. package/src/modules/messages/constants.ts +2 -1
  78. package/src/modules/messages/hooks/use-get-signed-urls/index.ts +1 -0
  79. package/src/modules/messages/hooks/use-get-signed-urls/use-get-signed-urls.spec.tsx +27 -0
  80. package/src/modules/messages/hooks/use-get-signed-urls/use-get-signed-urls.tsx +38 -0
  81. package/src/modules/messages/hooks/use-infinite-get-messages/use-infinite-get-messages.spec.tsx +1 -2
  82. package/src/modules/messages/hooks/use-infinite-get-messages/use-infinite-get-messages.tsx +6 -8
  83. package/src/modules/messages/hooks/use-prefetch-messages/index.ts +1 -0
  84. package/src/modules/messages/hooks/use-prefetch-messages/use-prefetch-messages.tsx +28 -0
  85. package/src/modules/messages/hooks/use-suspense-messages/index.ts +2 -0
  86. package/src/modules/messages/hooks/use-suspense-messages/types.ts +4 -0
  87. package/src/modules/messages/hooks/use-suspense-messages/use-suspense-messages.tsx +21 -0
  88. package/src/modules/messages/service.direct.ts +19 -1
  89. package/src/modules/messages/service.ts +1 -1
  90. package/src/modules/messages/store/messages-max-count.atom.ts +2 -2
  91. package/src/modules/messages/types.ts +15 -1
  92. package/src/modules/messages/utils/set-messages-cache/utils.ts +1 -1
  93. package/src/modules/profile/hooks/use-get-profile/use-get-profile.tsx +7 -6
  94. package/src/modules/sparkie/__tests__/sparkie.mock.ts +1 -4
  95. package/src/modules/sparkie/hooks/use-init-sparkie/use-init-sparkie.tsx +34 -28
  96. package/src/modules/sparkie/service.ts +1 -1
  97. package/src/modules/sparkie/store/index.ts +1 -0
  98. package/src/modules/sparkie/store/sparkie-state.atom.ts +13 -0
  99. package/src/modules/widget/__tests__/widget-settings-props.builder.ts +20 -1
  100. package/src/modules/widget/components/chat-page/chat-page.tsx +58 -0
  101. package/src/modules/widget/components/constants.tsx +3 -1
  102. package/src/modules/widget/components/error-page/error-page.spec.tsx +17 -0
  103. package/src/modules/widget/components/error-page/error-page.tsx +12 -0
  104. package/src/modules/widget/components/error-page/index.ts +1 -0
  105. package/src/modules/widget/components/loading-page/loading-page.tsx +9 -15
  106. package/src/modules/widget/components/starter-page/starter-page-actions/index.ts +1 -0
  107. package/src/modules/widget/components/starter-page/starter-page-actions/starter-page-actions.spec.tsx +68 -0
  108. package/src/modules/widget/components/starter-page/starter-page-actions/starter-page-actions.tsx +34 -0
  109. package/src/modules/widget/components/starter-page/starter-page-content/index.ts +1 -0
  110. package/src/modules/widget/components/starter-page/starter-page-content/starter-page-content.spec.tsx +16 -0
  111. package/src/modules/widget/components/starter-page/starter-page-content/starter-page-content.tsx +28 -0
  112. package/src/modules/widget/components/starter-page/starter-page-header/index.ts +1 -0
  113. package/src/modules/widget/components/starter-page/starter-page-header/starter-page-header.spec.tsx +45 -0
  114. package/src/modules/widget/components/starter-page/starter-page-header/starter-page-header.tsx +36 -0
  115. package/src/modules/widget/components/starter-page/starter-page.spec.tsx +13 -3
  116. package/src/modules/widget/components/starter-page/starter-page.tsx +15 -109
  117. package/src/modules/widget/hooks/index.ts +1 -1
  118. package/src/modules/widget/hooks/use-listen-to-theme-change-event/use-listen-to-theme-change-event.tsx +8 -6
  119. package/src/modules/widget/hooks/use-retry-last-message/index.ts +1 -0
  120. package/src/modules/widget/hooks/use-retry-last-message/use-retry-last-message.tsx +37 -0
  121. package/src/modules/widget/store/create-store.ts +7 -0
  122. package/src/modules/widget/store/index.ts +2 -0
  123. package/src/modules/widget/store/widget-last-user-message.atom.ts +12 -0
  124. package/src/modules/widget/store/widget-settings-config.atom.ts +1 -6
  125. package/src/modules/widget/store/widget-tabs.atom.ts +17 -6
  126. package/src/types.ts +10 -0
  127. package/src/wrapper.tsx +52 -0
  128. package/src/modules/widget/hooks/use-init-widget/index.ts +0 -1
  129. package/src/modules/widget/hooks/use-init-widget/use-init-widget.tsx +0 -56
@@ -1,20 +1,22 @@
1
1
  import { useLayoutEffect } from 'react'
2
2
  import { produce } from 'immer'
3
+ import type { useStore } from 'jotai'
3
4
 
4
5
  import { initTheme } from '@/src/config/theme'
5
6
  import { TutorWidgetEvents } from '../../events'
6
- import { useWidgetSettingsAtom } from '../../store'
7
-
8
- function useListenToThemeChangeEvent() {
9
- const [widgetSettings, setWidgetSettings] = useWidgetSettingsAtom()
7
+ import { widgetSettingsAtom } from '../../store'
10
8
 
9
+ function useListenToThemeChangeEvent(store?: ReturnType<typeof useStore>) {
11
10
  useLayoutEffect(() => {
12
11
  const clear = TutorWidgetEvents['c3po-app-widget-theme-change'].handler(({ theme }) => {
12
+ const widgetSettings = store?.get(widgetSettingsAtom)
13
+
13
14
  initTheme(theme)
14
15
 
15
16
  if (!widgetSettings || theme === widgetSettings?.config?.theme) return
16
17
 
17
- setWidgetSettings(
18
+ store?.set(
19
+ widgetSettingsAtom,
18
20
  produce(widgetSettings, (draft) => {
19
21
  draft.config = { ...draft.config, theme }
20
22
 
@@ -24,7 +26,7 @@ function useListenToThemeChangeEvent() {
24
26
  })
25
27
 
26
28
  return () => clear?.()
27
- }, [setWidgetSettings, widgetSettings])
29
+ }, [store])
28
30
  }
29
31
 
30
32
  export default useListenToThemeChangeEvent
@@ -0,0 +1 @@
1
+ export { default as useRetryLastMessage } from './use-retry-last-message'
@@ -0,0 +1,37 @@
1
+ import { useMemo } from 'react'
2
+
3
+ import { useSendTextMessage } from '@/src/modules/messages/hooks'
4
+ import {
5
+ useWidgetLastUserMessageAtom,
6
+ useWidgetLastUserMessageAtomValue,
7
+ useWidgetSettingsAtomValue,
8
+ useWidgetTabsAtom
9
+ } from '../../store'
10
+
11
+ function useRetryLastMessage() {
12
+ const settings = useWidgetSettingsAtomValue()
13
+ const lastUserMessage = useWidgetLastUserMessageAtomValue()
14
+ const [, setLastUserMessage] = useWidgetLastUserMessageAtom()
15
+ const [, setTab] = useWidgetTabsAtom()
16
+ const sendTextMessageMutation = useSendTextMessage()
17
+
18
+ const isAgentMode = useMemo(
19
+ () => settings?.config?.metadata?.parent === 'AGENT',
20
+ [settings?.config?.metadata?.parent]
21
+ )
22
+
23
+ const retryLastMessage = () => {
24
+ if (isAgentMode && lastUserMessage) {
25
+ sendTextMessageMutation.mutate(lastUserMessage, {
26
+ onSuccess() {
27
+ setLastUserMessage('')
28
+ setTab('chat')
29
+ }
30
+ })
31
+ }
32
+ }
33
+
34
+ return retryLastMessage
35
+ }
36
+
37
+ export default useRetryLastMessage
@@ -0,0 +1,7 @@
1
+ import { createStore as jotaiCreateStore } from 'jotai'
2
+
3
+ export const createStore = () => {
4
+ const store = jotaiCreateStore()
5
+
6
+ return store
7
+ }
@@ -1,4 +1,6 @@
1
+ export * from './create-store'
1
2
  export * from './widget-container-intrinsic-height.atom'
3
+ export * from './widget-last-user-message.atom'
2
4
  export * from './widget-loading.atom'
3
5
  export * from './widget-scrolling.atom'
4
6
  export * from './widget-settings.atom'
@@ -0,0 +1,12 @@
1
+ import { atom, useAtom, useAtomValue } from 'jotai'
2
+
3
+ export const widgetLastUserMessageAtom = atom<string | null>(null)
4
+
5
+ export const setWidgetLastUserMessageAtom = atom(
6
+ (get) => get(widgetLastUserMessageAtom),
7
+ (_, set, lastMessage: string) => set(widgetLastUserMessageAtom, lastMessage)
8
+ )
9
+
10
+ export const useWidgetLastUserMessageAtom = () => useAtom(setWidgetLastUserMessageAtom)
11
+
12
+ export const useWidgetLastUserMessageAtomValue = () => useAtomValue(setWidgetLastUserMessageAtom)
@@ -1,10 +1,5 @@
1
1
  import { atom, useAtomValue } from 'jotai'
2
2
 
3
- import { widgetSettingsAtom } from './widget-settings.atom'
4
-
5
- export const widgetSettingsConfigAgentParentAtom = atom((get) => {
6
- const settings = get(widgetSettingsAtom)
7
- return settings?.config?.metadata?.parent === 'AGENT'
8
- })
3
+ export const widgetSettingsConfigAgentParentAtom = atom(false)
9
4
 
10
5
  export const useIsAgentParentAtomValue = () => useAtomValue(widgetSettingsConfigAgentParentAtom)
@@ -7,9 +7,12 @@ export type WidgetTabsProps = {
7
7
  history: Set<CurrentTabKey>
8
8
  }
9
9
 
10
+ // Prevent memory issues
11
+ const MAX_HISTORY_SIZE = 10
12
+
10
13
  const INITIAL_PROPS: WidgetTabsProps = {
11
- currentTab: 'starter',
12
- history: new Set(['starter'])
14
+ currentTab: 'loading',
15
+ history: new Set(['loading'])
13
16
  }
14
17
 
15
18
  export const widgetTabsAtom = atom<WidgetTabsProps>(INITIAL_PROPS)
@@ -21,11 +24,15 @@ export const setWidgetTabsAtom = atom(
21
24
 
22
25
  if (currentValue.currentTab === currentTab) return
23
26
 
24
- const history = new Set([...currentValue.history, currentTab])
27
+ const historyArray = [...currentValue.history, currentTab]
28
+
29
+ if (historyArray.length > MAX_HISTORY_SIZE) {
30
+ historyArray.shift()
31
+ }
25
32
 
26
33
  const config: WidgetTabsProps = {
27
34
  currentTab,
28
- history
35
+ history: new Set(historyArray)
29
36
  }
30
37
 
31
38
  set(widgetTabsAtom, config)
@@ -40,8 +47,12 @@ export const goBackTabAtom = atom(null, (get, set) => {
40
47
 
41
48
  historyArray.pop()
42
49
 
50
+ const previousTab = historyArray[historyArray.length - 1]
51
+
52
+ if (!previousTab) return
53
+
43
54
  const config: WidgetTabsProps = {
44
- currentTab: historyArray[historyArray.length - 1],
55
+ currentTab: previousTab,
45
56
  history: new Set(historyArray)
46
57
  }
47
58
 
@@ -50,5 +61,5 @@ export const goBackTabAtom = atom(null, (get, set) => {
50
61
 
51
62
  export const useGetWidgetTabsAtom = () => useAtom(widgetTabsAtom)
52
63
  export const useWidgetTabsAtom = () => useAtom(setWidgetTabsAtom)
53
- export const useWidgetTabsValueAtom = () => useAtomValue(setWidgetTabsAtom)
64
+ export const useWidgetTabsValueAtom = () => useAtomValue(widgetTabsAtom)
54
65
  export const useWidgetGoBackTabAtom = () => useAtom(goBackTabAtom)
package/src/types.ts CHANGED
@@ -1,3 +1,6 @@
1
+ import type { QueryClient } from '@tanstack/react-query'
2
+ import type { Root } from 'react-dom/client'
3
+
1
4
  import type { ILanguages } from './config/i18n'
2
5
 
3
6
  export type StartTutorWidgetProps = {
@@ -59,6 +62,7 @@ export type WidgetSettingProps = {
59
62
  courseName?: string
60
63
  source?: string
61
64
  promptId?: string
65
+ showFileUpload?: boolean
62
66
  }
63
67
  }
64
68
  }
@@ -68,3 +72,9 @@ export interface ICustomEvent<T = object> {
68
72
  handler: (listener: EventListenerOrEventListenerObject) => () => void | Promise<void>
69
73
  dispatch: <D = unknown>(detail?: D) => void
70
74
  }
75
+
76
+ export type WidgetInstance = {
77
+ root: Root
78
+ container: HTMLElement
79
+ queryClient: QueryClient
80
+ }
@@ -0,0 +1,52 @@
1
+ import { StrictMode } from 'react'
2
+ import type { QueryClient } from '@tanstack/react-query'
3
+ import { Provider as JotaiProvider } from 'jotai'
4
+
5
+ import { QueryProvider } from './config/tanstack'
6
+ import { Main } from './main'
7
+ import { usePrefetchMessages } from './modules/messages/hooks/use-prefetch-messages'
8
+ import { WidgetLoadingPage } from './modules/widget'
9
+ import { WidgetErrorPage } from './modules/widget/components/error-page'
10
+ import { useListenToThemeChangeEvent } from './modules/widget/hooks'
11
+ import type { createStore } from './modules/widget/store'
12
+ import type { WidgetSettingProps } from './types'
13
+
14
+ export type WrapperProps = {
15
+ settings: WidgetSettingProps
16
+ queryClient: QueryClient
17
+ state: 'ERROR' | 'LOADING' | 'READY'
18
+ store?: ReturnType<typeof createStore>
19
+ }
20
+
21
+ function ContentWrapper({ settings }: Pick<WrapperProps, 'settings'>) {
22
+ usePrefetchMessages(settings)
23
+
24
+ return <Main settings={settings} />
25
+ }
26
+
27
+ function WrapperItem({ state, settings }: Omit<WrapperProps, 'queryClient' | 'store'>) {
28
+ switch (state) {
29
+ case 'READY':
30
+ return <ContentWrapper settings={settings} />
31
+ case 'LOADING':
32
+ return <WidgetLoadingPage />
33
+ default:
34
+ return <WidgetErrorPage />
35
+ }
36
+ }
37
+
38
+ function Wrapper({ queryClient, settings, state, store }: WrapperProps) {
39
+ useListenToThemeChangeEvent(store)
40
+
41
+ return (
42
+ <StrictMode>
43
+ <JotaiProvider store={store}>
44
+ <QueryProvider queryClient={queryClient} showDevTools={false}>
45
+ <WrapperItem settings={settings} state={state} />
46
+ </QueryProvider>
47
+ </JotaiProvider>
48
+ </StrictMode>
49
+ )
50
+ }
51
+
52
+ export default Wrapper
@@ -1 +0,0 @@
1
- export { default as useInitWidget } from './use-init-widget'
@@ -1,56 +0,0 @@
1
- import { useEffect, useState } from 'react'
2
-
3
- import { DataHubStore } from '@/src/config/datahub'
4
- import { initDayjs } from '@/src/config/dayjs'
5
- import { initAxios } from '@/src/config/request/api'
6
- import type { WidgetSettingProps } from '@/src/types'
7
- import { TutorWidgetEvents } from '../../events'
8
-
9
- const init = async (settings: WidgetSettingProps) => {
10
- try {
11
- initAxios(settings.hotmartToken)
12
- await initDayjs(settings.locale)
13
- DataHubStore.initData({
14
- ucode: settings.user?.ucode ?? '',
15
- membershipId: settings.membershipId ?? '',
16
- membershipSlug: settings.membershipSlug ?? '',
17
- sessionId: settings.sessionId,
18
- userId: Number(settings.userId),
19
- product: {
20
- id: Number(settings.productId) || 0,
21
- category: settings.productType ?? ''
22
- }
23
- })
24
- TutorWidgetEvents['tutor-app-widget-loaded'].dispatch()
25
- } catch (error) {
26
- console.error(error)
27
- TutorWidgetEvents['tutor-app-widget-loaded'].dispatch({ detail: { isSuccess: false } })
28
- }
29
- }
30
-
31
- function useInitWidget(settings: WidgetSettingProps) {
32
- const [completeSetup, setCompleteSetup] = useState(false)
33
- const [error, setError] = useState<unknown>(null)
34
-
35
- useEffect(() => {
36
- let isMounted = true
37
-
38
- if (completeSetup) return
39
-
40
- init(settings)
41
- .then(() => {
42
- if (isMounted) setCompleteSetup(true)
43
- })
44
- .catch((e) => {
45
- if (isMounted) setError(e)
46
- })
47
-
48
- return () => {
49
- isMounted = false
50
- }
51
- }, [completeSetup, settings])
52
-
53
- return { completeSetup, error }
54
- }
55
-
56
- export default useInitWidget