app-tutor-ai-consumer 1.4.0 → 1.6.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 (102) hide show
  1. package/.github/workflows/staging-staging.yml +148 -0
  2. package/.github/workflows/staging.yml +1 -2
  3. package/CHANGELOG.md +19 -0
  4. package/config/rspack/rspack.config.js +5 -1
  5. package/config/vitest/__mocks__/icons.tsx +3 -0
  6. package/config/vitest/__mocks__/intersection-observer.ts +10 -0
  7. package/config/vitest/__mocks__/sparkie.tsx +9 -9
  8. package/config/vitest/__mocks__/use-init-sparkie.tsx +14 -0
  9. package/config/vitest/vitest.config.mts +13 -8
  10. package/environments/.env.test +2 -0
  11. package/eslint.config.mjs +27 -0
  12. package/package.json +8 -4
  13. package/public/index.html +3 -4
  14. package/src/@types/index.d.ts +5 -2
  15. package/src/config/styles/global.css +2 -2
  16. package/src/config/tanstack/query-client.ts +3 -2
  17. package/src/config/tests/utils.tsx +3 -2
  18. package/src/config/tests/wrappers.tsx +4 -1
  19. package/src/development-bootstrap.tsx +15 -15
  20. package/src/index.tsx +37 -5
  21. package/src/lib/components/icons/ai-color.svg +17 -0
  22. package/src/lib/components/icons/arrow-down.svg +5 -0
  23. package/src/lib/components/icons/chevron-down.svg +4 -0
  24. package/src/lib/components/icons/icon-names.d.ts +1 -1
  25. package/src/lib/components/markdownrenderer/markdownrenderer.tsx +7 -9
  26. package/src/lib/hooks/index.ts +3 -0
  27. package/src/lib/hooks/use-intersection-observer-reverse-scroll/index.ts +2 -0
  28. package/src/lib/hooks/use-intersection-observer-reverse-scroll/use-intersection-observer-reverse-scroll.tsx +147 -0
  29. package/src/lib/hooks/use-ref-client-height/index.ts +2 -0
  30. package/src/lib/hooks/use-ref-client-height/use-ref-client-height.tsx +38 -0
  31. package/src/lib/hooks/use-scroll-to-ref/index.ts +2 -0
  32. package/src/lib/hooks/use-scroll-to-ref/use-scroll-to-ref.tsx +14 -0
  33. package/src/lib/hooks/use-throttle/index.ts +3 -0
  34. package/src/lib/hooks/use-throttle/types.ts +13 -0
  35. package/src/lib/hooks/use-throttle/use-throttle.spec.tsx +296 -0
  36. package/src/lib/hooks/use-throttle/use-throttle.tsx +91 -0
  37. package/src/lib/utils/is-text-empty.ts +3 -0
  38. package/src/main/main.spec.tsx +7 -6
  39. package/src/modules/cursor/__tests__/icursor-update.builder.ts +42 -0
  40. package/src/modules/cursor/hooks/index.ts +1 -0
  41. package/src/modules/cursor/hooks/use-update-cursor/index.ts +2 -0
  42. package/src/modules/cursor/hooks/use-update-cursor/use-update-cursor.spec.tsx +23 -0
  43. package/src/modules/cursor/hooks/use-update-cursor/use-update-cursor.ts +11 -0
  44. package/src/modules/cursor/index.ts +2 -0
  45. package/src/modules/cursor/service.ts +15 -0
  46. package/src/modules/cursor/types.ts +9 -0
  47. package/src/modules/global-providers/index.ts +1 -0
  48. package/src/modules/messages/__tests__/parsed-message.builder.ts +164 -0
  49. package/src/modules/messages/components/chat-input/chat-input.spec.tsx +72 -0
  50. package/src/modules/messages/components/chat-input/chat-input.tsx +52 -6
  51. package/src/modules/messages/components/index.ts +1 -0
  52. package/src/modules/messages/components/message-item/message-item.spec.tsx +2 -2
  53. package/src/modules/messages/components/message-item/message-item.tsx +14 -1
  54. package/src/modules/messages/components/message-item-end-of-scroll/index.ts +2 -0
  55. package/src/modules/messages/components/message-item-end-of-scroll/message-item-end-of-scroll.tsx +14 -0
  56. package/src/modules/messages/components/message-item-error/index.ts +2 -0
  57. package/src/modules/messages/components/message-item-error/message-item-error.tsx +25 -0
  58. package/src/modules/messages/components/message-item-loading/index.ts +2 -0
  59. package/src/modules/messages/components/message-item-loading/message-item-loading.tsx +16 -0
  60. package/src/modules/messages/components/message-skeleton/index.ts +1 -0
  61. package/src/modules/messages/components/message-skeleton/message-skeleton.tsx +23 -0
  62. package/src/modules/messages/components/messages-list/index.ts +1 -1
  63. package/src/modules/messages/components/messages-list/messages-list.tsx +82 -39
  64. package/src/modules/messages/constants.ts +1 -0
  65. package/src/modules/messages/hooks/index.ts +5 -0
  66. package/src/modules/messages/hooks/use-all-messages/index.ts +2 -0
  67. package/src/modules/messages/hooks/use-all-messages/use-all-messages.tsx +30 -0
  68. package/src/modules/messages/hooks/use-infinite-get-messages/index.ts +2 -0
  69. package/src/modules/messages/hooks/use-infinite-get-messages/use-infinite-get-messages.spec.tsx +65 -0
  70. package/src/modules/messages/hooks/use-infinite-get-messages/use-infinite-get-messages.tsx +81 -0
  71. package/src/modules/messages/hooks/use-manage-scroll/index.ts +2 -0
  72. package/src/modules/messages/hooks/use-manage-scroll/use-manage-scroll.tsx +70 -0
  73. package/src/modules/messages/hooks/use-send-text-message/index.ts +2 -0
  74. package/src/modules/messages/hooks/use-send-text-message/use-send-text-message.spec.tsx +86 -0
  75. package/src/modules/messages/hooks/use-send-text-message/use-send-text-message.tsx +60 -0
  76. package/src/modules/messages/hooks/use-skeleton-ref/index.ts +2 -0
  77. package/src/modules/messages/hooks/use-skeleton-ref/use-skeleton-ref.tsx +34 -0
  78. package/src/modules/messages/hooks/use-subscribe-message-received-event/index.ts +2 -0
  79. package/src/modules/messages/hooks/use-subscribe-message-received-event/use-subscribe-message-received-event.tsx +80 -0
  80. package/src/modules/messages/service.ts +8 -7
  81. package/src/modules/messages/utils/has-to-update-cursor/has-to-update-cursor.spec.tsx +58 -0
  82. package/src/modules/messages/utils/has-to-update-cursor/has-to-update-cursor.ts +30 -0
  83. package/src/modules/messages/utils/has-to-update-cursor/index.ts +2 -0
  84. package/src/modules/sparkie/__tests__/sparkie.mock.ts +33 -0
  85. package/src/modules/sparkie/service.ts +182 -35
  86. package/src/modules/sparkie/types.ts +10 -2
  87. package/src/modules/widget/__tests__/widget-settings-props.builder.ts +29 -1
  88. package/src/modules/widget/components/ai-avatar/ai-avatar.tsx +11 -53
  89. package/src/modules/widget/components/chat-page/chat-page.spec.tsx +28 -0
  90. package/src/modules/widget/components/chat-page/chat-page.tsx +23 -4
  91. package/src/modules/widget/components/container/container.tsx +5 -19
  92. package/src/modules/widget/components/index.ts +1 -0
  93. package/src/modules/widget/components/scroll-to-bottom-button/index.ts +2 -0
  94. package/src/modules/widget/components/scroll-to-bottom-button/scroll-to-bottom-button.tsx +32 -0
  95. package/src/modules/widget/events.ts +4 -0
  96. package/src/modules/widget/hooks/use-init-sparkie/use-init-sparkie.tsx +8 -6
  97. package/src/modules/widget/store/index.ts +3 -0
  98. package/src/modules/widget/store/widget-container-intrinsic-height.atom.ts +13 -0
  99. package/src/modules/widget/store/widget-loading.atom.ts +11 -0
  100. package/src/modules/widget/store/widget-scrolling.atom.ts +11 -0
  101. package/src/modules/widget/store/widget-tabs.atom.ts +2 -1
  102. package/src/types.ts +4 -1
@@ -0,0 +1,148 @@
1
+ name: Deploy Staging (staging branch)
2
+ run-name: "Staging deployment: ${{ github.ref_type }} ${{ github.ref_name }} by @${{ github.actor }}"
3
+
4
+ concurrency: staging-${{ github.ref }}
5
+
6
+ on:
7
+ push:
8
+ branches:
9
+ - staging/**
10
+
11
+ env:
12
+ APP_NAME: app-tutor-ai-consumer
13
+ BUCKET_NAME: app-club-microfrontends
14
+ AWS_DEFAULT_REGION: us-east-1
15
+ CLOUDFRONT_ACCOUNT_ID: "449466460580"
16
+ CLOUDFRONT_DISTRIBUTION: "E19SXB8YMSF4QX"
17
+ CLOUDFRONT_URL: https://app-club-microfrontends.buildstaging.com
18
+ GH_TOKEN: ${{ secrets.CI_GH_TOKEN }}
19
+ SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
20
+ LANG_ENV: staging
21
+
22
+ jobs:
23
+ deploy-staging:
24
+ name: Deploy Staging
25
+ runs-on: [buildstaging-high-iac]
26
+ steps:
27
+ - name: Checkout
28
+ uses: actions/checkout@v4
29
+ with:
30
+ token: ${{ secrets.CI_GH_TOKEN }}
31
+ fetch-depth: 0
32
+ ref: ${{ env.BRANCH_REF }}
33
+ fetch-tags: true
34
+
35
+ - name: Restore Cache
36
+ uses: Hotmart-Org/actions/cache@master
37
+ with:
38
+ bucket: buildstaging-pipeline-cache
39
+ cache-key: "${{ hashFiles('package.json') }}"
40
+ restore: true
41
+ mount: 'node_modules/'
42
+
43
+ - name: Setup Node.js
44
+ uses: actions/setup-node@v4
45
+ with:
46
+ node-version-file: '.nvmrc'
47
+
48
+ - name: Retrieve .npmrc
49
+ uses: Hotmart-Org/actions/codeartifact@master
50
+ with:
51
+ npmrc: ${{ secrets.NPM_RC }}
52
+ fontawesome-registry-token: ${{ secrets.FONTAWESOME_REGISTRY_TOKEN }}
53
+
54
+ - name: install-dependencies
55
+ run: npm ci --include=optional
56
+
57
+ - name: Rebuild Cache
58
+ uses: Hotmart-Org/actions/cache@master
59
+ with:
60
+ bucket: buildstaging-pipeline-cache
61
+ cache-key: "${{ hashFiles('package.json') }}"
62
+ rebuild: true
63
+ mount: 'node_modules/'
64
+
65
+ - name: pre-build-staging
66
+ run: |
67
+ echo "${{ env.LANG_ENV }}"
68
+ rm -rf public/locales
69
+ mkdir public/locales
70
+ npm run fetch-langs
71
+
72
+ - name: build-staging
73
+ run: npm run build:staging
74
+
75
+ - name: Deploy Microfrontend s3 - ${{ env.APP_NAME }}
76
+ uses: Hotmart-Org/actions/s3/microfrontend/deploy@master
77
+ with:
78
+ bucket-name: ${{ env.BUCKET_NAME }}
79
+ app-name: ${{ env.APP_NAME }}
80
+ package-json-path: ./package.json
81
+ bundle-source: ./dist
82
+ account-id: ${{ env.CLOUDFRONT_ACCOUNT_ID }}
83
+ cdn-distribution: ${{ env.CLOUDFRONT_DISTRIBUTION }}
84
+ cdn-url: ${{ env.CLOUDFRONT_URL }}
85
+
86
+ staging-notification:
87
+ name: 'Staging Notification'
88
+ runs-on: [buildstaging-iac]
89
+ needs: ['deploy-staging']
90
+ steps:
91
+ - name: Checkout
92
+ uses: actions/checkout@v4
93
+
94
+ - name: Extract version from package.json
95
+ run: |
96
+ VERSION=$(cat package.json | jq -r .version)
97
+ echo "Extracted version: $VERSION"
98
+ echo "VERSION=$VERSION" >> $GITHUB_ENV
99
+
100
+ - name: Notify Google Chat
101
+ uses: Hotmart-Org/actions/notification@master
102
+ env:
103
+ VERSION: ${{ env.VERSION }}
104
+ with:
105
+ type: 'Original'
106
+ author: true
107
+ webhook-chat: 'https://chat.googleapis.com/v1/spaces/AAAA1nvOyjo/messages?key=AIzaSyDdI0hCZtE6vySjMm-WEfRq3CPzqKqqsHI&token=uuL7DA8zTxUkJjQa39HIM0TYVZV0DvneZ0mklNhEr5M'
108
+ body: |
109
+ {
110
+ "text": "🚧 *[STAGING] app-tutor-ai-consumer v${{ env.VERSION }}*: staging deploy: ${{ github.ref_name }}",
111
+ "cards": [
112
+ {
113
+ "header": {
114
+ "title": "Started by ${{ github.actor }}"
115
+ },
116
+ "sections": [
117
+ {
118
+ "widgets": [
119
+ {
120
+ "buttons": [
121
+ {
122
+ "textButton": {
123
+ "onClick": {
124
+ "openLink": {
125
+ "url": "${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}"
126
+ }
127
+ },
128
+ "text": "View commit"
129
+ }
130
+ },
131
+ {
132
+ "textButton": {
133
+ "onClick": {
134
+ "openLink": {
135
+ "url": "https://github.com/Hotmart-Org/app-tutor-ai-consumer/actions/runs/${{ github.run_id }}"
136
+ }
137
+ },
138
+ "text": "Follow this deploy"
139
+ }
140
+ }
141
+ ]
142
+ }
143
+ ]
144
+ }
145
+ ]
146
+ }
147
+ ]
148
+ }
@@ -5,7 +5,6 @@ on:
5
5
  push:
6
6
  branches:
7
7
  - main
8
- - staging/*
9
8
 
10
9
  concurrency: staging
11
10
 
@@ -141,7 +140,7 @@ jobs:
141
140
  uses: newrelic/deployment-marker-action@v2.5.1
142
141
  env:
143
142
  VERSION: ${{ env.VERSION }}
144
- NEW_RELIC_API_KEY: NRAK-14TLYFWRA5RCKUGK4A3TJSAPU78
143
+ NEW_RELIC_API_KEY: ${{ secrets.NEW_RELIC_API_KEY }}
145
144
  NEW_RELIC_DEPLOYMENT_ENTITY_GUID: Mjc1MDN8QVBNfEFQUExJQ0FUSU9OfDEwOTE1MDMzMTE
146
145
  with:
147
146
  apiKey: ${{ env.NEW_RELIC_API_KEY }}
package/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
1
+ # [1.6.0](https://github.com/Hotmart-Org/app-tutor-ai-consumer/compare/v1.5.0...v1.6.0) (2025-07-11)
2
+
3
+ ### Features
4
+
5
+ - add send text message support ([6526d55](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/6526d55fe2781cd9842be805f3406bf2176c825e))
6
+
7
+ # [1.5.0](https://github.com/Hotmart-Org/app-tutor-ai-consumer/compare/v1.4.0...v1.5.0) (2025-07-10)
8
+
9
+ ### Bug Fixes
10
+
11
+ - scrolling PR issues ([d2a8faa](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/d2a8faac05c79d1b18f793981d69036687fefa0e))
12
+
13
+ ### Features
14
+
15
+ - add infinite scroll UI ([8c86dd8](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/8c86dd8043d0f10e8deae787e6f8870555f5c5f2))
16
+ - adding specific pipiline to staging branch ([bee74d3](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/bee74d37721e8900163409b5cd493a213c92fa8d))
17
+ - adding styles ([2a33e03](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/2a33e035f1e4b146cc7b03ba9f201a2f494bc31b))
18
+ - adding styles file ([ef4d4ec](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/ef4d4ec50c010f39dfba154b084cbe0dc9ece05b))
19
+
1
20
  # [1.4.0](https://github.com/Hotmart-Org/app-tutor-ai-consumer/compare/v1.3.0...v1.4.0) (2025-06-26)
2
21
 
3
22
  ### Bug Fixes
@@ -46,7 +46,7 @@ module.exports = async function (env) {
46
46
  path: paths.DIST,
47
47
  publicPath: bundlePath,
48
48
  pathinfo: !productionMode,
49
- filename: `[name].[fullhash].${fileVersion}.js`,
49
+ filename: `remoteEntry.js`,
50
50
  chunkFilename: `[name].[fullhash].chunk.${fileVersion}.js`,
51
51
  sourceMapFilename: `[name].[fullhash].${fileVersion}.js.map`,
52
52
  clean: true
@@ -166,6 +166,10 @@ module.exports = async function (env) {
166
166
  new rspack.HtmlRspackPlugin({
167
167
  template: path.resolve(paths.PUBLIC, 'index.html'),
168
168
  favicon: path.resolve(paths.PUBLIC, 'favicon.ico')
169
+ }),
170
+ new rspack.CssExtractRspackPlugin({
171
+ filename: productionMode ? `app-tutor-ai-consumer.css` : '[name].[contenthash].css',
172
+ chunkFilename: productionMode ? '[id].[contenthash].css' : '[id].[contenthash].css'
169
173
  })
170
174
  ].concat(await getEnvironmentPlugins(!productionMode)),
171
175
  watchOptions: {
@@ -0,0 +1,3 @@
1
+ vi.mock('@/src/lib/components/icons', () => ({
2
+ Icon: vi.fn(({ name }) => <div data-test='lazy-icon'>{name}</div>)
3
+ }))
@@ -0,0 +1,10 @@
1
+ beforeEach(() => {
2
+ const mockIntersectionObserver = vi.fn()
3
+ mockIntersectionObserver.mockReturnValue({
4
+ observe: () => null,
5
+ unobserve: () => null,
6
+ disconnect: () => null
7
+ })
8
+
9
+ window.IntersectionObserver = mockIntersectionObserver
10
+ })
@@ -1,12 +1,12 @@
1
- vi.mock('@hotmart/sparkie', () => {
2
- const Sparkie = vi.fn()
1
+ import SparkieMock from '@/src/modules/sparkie/__tests__/sparkie.mock'
2
+ import { SparkieService } from '@/src/modules/sparkie'
3
+ import { SparkieMessageServiceMock } from '@/src/modules/sparkie/__tests__/sparkie.mock'
4
+ import MessageService from '@hotmart/sparkie/dist/MessageService'
3
5
 
4
- Sparkie.prototype.destroy = vi.fn()
5
- Sparkie.prototype.init = vi.fn()
6
- Sparkie.prototype.off = vi.fn()
7
- Sparkie.prototype.on = vi.fn()
8
- Sparkie.prototype.listener = { trackTyping: vi.fn() }
9
- Sparkie.prototype.setAPIToken = vi.fn()
6
+ vi.mock('@hotmart/sparkie', () => ({ default: SparkieMock }))
10
7
 
11
- return { default: Sparkie }
8
+ beforeEach(() => {
9
+ vi.spyOn(SparkieService, 'getMessageService').mockResolvedValue(
10
+ SparkieMessageServiceMock as unknown as MessageService
11
+ )
12
12
  })
@@ -0,0 +1,14 @@
1
+ import { useInitSparkie } from '@/src/modules/widget/hooks/use-init-sparkie'
2
+
3
+ vi.mock('@/src/modules/widget/hooks/use-init-sparkie/use-init-sparkie', () => ({
4
+ useInitSparkie: vi.fn()
5
+ }))
6
+
7
+ beforeEach(() => {
8
+ vi.mocked(useInitSparkie).mockReturnValue({
9
+ data: true,
10
+ isError: false,
11
+ isLoading: false,
12
+ refetch: vi.fn()
13
+ } as unknown as ReturnType<typeof useInitSparkie>)
14
+ })
@@ -12,7 +12,10 @@ export default defineConfig({
12
12
  './config/vitest/setupTests.ts',
13
13
  './config/vitest/__mocks__/i18n.tsx',
14
14
  './config/vitest/__mocks__/sparkie.tsx',
15
- './config/vitest/polyfills/global.js',
15
+ './config/vitest/__mocks__/icons.tsx',
16
+ './config/vitest/__mocks__/intersection-observer.ts',
17
+ './config/vitest/__mocks__/use-init-sparkie.tsx',
18
+ './config/vitest/polyfills/global.js'
16
19
  ],
17
20
  coverage: {
18
21
  include: ['src/**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
@@ -28,7 +31,7 @@ export default defineConfig({
28
31
  'src/**/*.builder.ts*',
29
32
  'src/app-injector.tsx',
30
33
  'src/bootstrap.tsx',
31
- 'src/index.ts',
34
+ 'src/index.ts'
32
35
  ],
33
36
  reporter: ['text', 'html'],
34
37
  provider: 'istanbul',
@@ -36,13 +39,15 @@ export default defineConfig({
36
39
  branches: 3,
37
40
  lines: 3,
38
41
  functions: 3,
39
- statements: 3,
40
- },
42
+ statements: 3
43
+ }
41
44
  },
45
+ testTimeout: 10000,
46
+ hookTimeout: 10000,
42
47
  env: {
43
48
  ...config({
44
- path: './environments/.env.test',
45
- }).parsed,
46
- },
47
- },
49
+ path: './environments/.env.test'
50
+ }).parsed
51
+ }
52
+ }
48
53
  })
@@ -39,3 +39,5 @@ AUTH_CLIENT_SECRET=
39
39
 
40
40
  # BUNDLE
41
41
  BUNDLE_PATH=
42
+
43
+ VITE_REACT_QUERY_EXPERIMENTAL_PREFETCH_IN_RENDER=true
package/eslint.config.mjs CHANGED
@@ -8,6 +8,7 @@ import globals from 'globals'
8
8
  import simpleImportSort from 'eslint-plugin-simple-import-sort'
9
9
  import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'
10
10
  import pluginQuery from '@tanstack/eslint-plugin-query'
11
+ import vitest from '@vitest/eslint-plugin'
11
12
 
12
13
  export default tseslint.config(
13
14
  {
@@ -89,4 +90,30 @@ export default tseslint.config(
89
90
  '@typescript-eslint/no-explicit-any': 'off',
90
91
  },
91
92
  },
93
+ {
94
+ files: [
95
+ '**/*.test.ts',
96
+ '**/*.test.tsx',
97
+ '**/*.spec.ts',
98
+ '**/*.spec.tsx'
99
+ ],
100
+ plugins: {
101
+ vitest,
102
+ },
103
+ languageOptions: {
104
+ globals: {
105
+ ...vitest.environments.env.globals,
106
+ },
107
+ },
108
+ rules: {
109
+ ...vitest.configs.recommended.rules,
110
+ '@typescript-eslint/unbound-method': 'off',
111
+ '@typescript-eslint/no-unsafe-assignment': 'off',
112
+ '@typescript-eslint/no-unsafe-member-access': 'off',
113
+ '@typescript-eslint/no-unsafe-call': 'off',
114
+ '@typescript-eslint/no-unsafe-return': 'off',
115
+ '@typescript-eslint/no-explicit-any': 'off',
116
+ '@typescript-eslint/no-non-null-assertion': 'off',
117
+ },
118
+ }
92
119
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "app-tutor-ai-consumer",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "dev": "rspack serve --env=development --config config/rspack/rspack.config.js",
@@ -8,9 +8,9 @@
8
8
  "generate-icon-types": "ts-node scripts/generate-icon-types.js",
9
9
  "postgenerate-icon-types": "npm run format",
10
10
  "prebuild": "npm run generate-icon-types",
11
- "build": "rspack build --mode=production --env=production --config config/rspack/rspack.config.js",
11
+ "build": "NODE_ENV=production rspack build --mode=production --env=production --config config/rspack/rspack.config.js",
12
12
  "prebuild:staging": "npm run generate-icon-types",
13
- "build:staging": "rspack build --mode=production --env=staging --config config/rspack/rspack.config.js",
13
+ "build:staging": "NODE_ENV=staging rspack build --mode=production --env=staging --config config/rspack/rspack.config.js",
14
14
  "prebuild:start": "npm run build",
15
15
  "build:start": "NODE_ENV=production rspack preview --config config/rspack/rspack.config.js --port 4200",
16
16
  "test": "vitest --config config/vitest/vitest.config.mts",
@@ -60,9 +60,11 @@
60
60
  "@types/react-dom": "~19.1.6",
61
61
  "@types/react-router-dom": "~5.3.3",
62
62
  "@types/ua-parser-js": "~0.7.39",
63
+ "@types/uuid": "~10.0.0",
63
64
  "@vitejs/plugin-react": "~4.5.2",
64
65
  "@vitest/coverage-istanbul": "~3.2.3",
65
66
  "@vitest/coverage-v8": "~3.2.3",
67
+ "@vitest/eslint-plugin": "~1.3.4",
66
68
  "autoprefixer": "~10.4.21",
67
69
  "chance": "~1.1.13",
68
70
  "compression-webpack-plugin": "~11.1.0",
@@ -111,6 +113,7 @@
111
113
  "dayjs": "~1.11.13",
112
114
  "i18next": "~25.2.1",
113
115
  "i18next-resources-to-backend": "~1.2.1",
116
+ "immer": "~10.1.1",
114
117
  "jotai": "~2.12.5",
115
118
  "linkify-it": "~5.0.0",
116
119
  "prism-react-renderer": "~2.4.1",
@@ -121,7 +124,8 @@
121
124
  "rehype-raw": "~7.0.0",
122
125
  "rehype-sanitize": "~6.0.0",
123
126
  "remark-breaks": "~4.0.0",
124
- "remark-gfm": "~4.0.1"
127
+ "remark-gfm": "~4.0.1",
128
+ "uuid": "~11.1.0"
125
129
  },
126
130
  "optionalDependencies": {
127
131
  "@rollup/rollup-linux-x64-gnu": "4.6.1",
package/public/index.html CHANGED
@@ -6,13 +6,12 @@
6
6
  <link rel="preconnect" href="https://fonts.googleapis.com" />
7
7
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
8
8
  <link
9
- href="https://fonts.googleapis.com/css?family=Nunito:400,700"
10
- rel="stylesheet"
11
- />
9
+ href="https://fonts.googleapis.com/css?family=Nunito:400,700&display=swap"
10
+ rel="stylesheet" />
12
11
  <title>App Tutor AI Consumer</title>
13
12
  </head>
14
13
 
15
- <body>
14
+ <body class="bg-ai-dark">
16
15
  <div id="root"></div>
17
16
  </body>
18
17
  </html>
@@ -9,8 +9,11 @@ declare global {
9
9
 
10
10
  interface Window {
11
11
  __INJECT_CSS_MODULE__?: (cssText: string, id: string) => void
12
- startTutorWidget: (props: StartTutorWidgetProps) => Promise<void>
13
- closeTutorWidget: () => void
12
+ startChatWidget: (
13
+ elementId: StartTutorWidgetProps['elementId'],
14
+ settings: StartTutorWidgetProps['settings']
15
+ ) => Promise<void>
16
+ closeChatWidget: () => void
14
17
  TOKEN: string
15
18
  }
16
19
  }
@@ -75,8 +75,8 @@
75
75
  --hc-color-neutral-1000: #000000;
76
76
  --ai-color-primary: #a095ec;
77
77
  --ai-color-secondary: #6ba1f0;
78
- --ai-color-dark: #111925;
79
- --ai-color-chat-response: #26202f;
78
+ --ai-color-dark: #1a1c1f;
79
+ --ai-color-chat-response: #1e1926;
80
80
 
81
81
  /* Size */
82
82
  --hc-size-spacing-2: 0.5rem;
@@ -14,14 +14,15 @@ export const queryClient = new QueryClient({
14
14
  retry(failureCount, error) {
15
15
  if (!api?.defaults?.headers?.common?.['Authorization']) return false
16
16
 
17
- const maxRetries = 2
17
+ const maxRetries = 3
18
18
  const statusCode = (error as ApiError).statusCode
19
19
 
20
20
  return (
21
21
  (statusCode < HttpCodes.BAD_REQUEST || statusCode >= HttpCodes.INTERNAL_SERVER_ERROR) &&
22
22
  failureCount <= maxRetries
23
23
  )
24
- }
24
+ },
25
+ retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000)
25
26
  }
26
27
  }
27
28
  })
@@ -11,13 +11,14 @@ import MockRequest from './mockRequest'
11
11
 
12
12
  const customRender = (
13
13
  ui: React.ReactElement,
14
- { shallow, withQueryProvider = true, ...options }: IExtendedRenderOptions = {}
14
+ { shallow, withQueryProvider = true, withProvider, ...options }: IExtendedRenderOptions = {}
15
15
  ) => ({
16
16
  user: userEvent.setup(),
17
17
  ...render(
18
18
  setupComponents(ui, {
19
19
  shallow,
20
- withQueryProvider
20
+ withQueryProvider,
21
+ withProvider
21
22
  }),
22
23
  options
23
24
  )
@@ -7,7 +7,10 @@ export const testQueryClient = new QueryClient({
7
7
  defaultOptions: {
8
8
  queries: {
9
9
  retry: false,
10
- refetchOnWindowFocus: false
10
+ gcTime: 0,
11
+ staleTime: 0,
12
+ refetchOnWindowFocus: false,
13
+ experimental_prefetchInRender: true
11
14
  },
12
15
  mutations: {
13
16
  retry: false
@@ -1,26 +1,26 @@
1
1
  import './index'
2
2
 
3
+ import { v4 } from 'uuid'
4
+
3
5
  import { LANGUAGES } from './config/i18n'
4
6
  import { devMode } from './lib/utils'
5
7
 
6
8
  if (devMode) {
7
9
  window.TOKEN = process.env.TOKEN ?? ''
8
10
  void (async () => {
9
- await window.startTutorWidget({
10
- elementId: 'root',
11
- settings: {
12
- hotmartToken: window.TOKEN,
13
- locale: LANGUAGES.PT_BR,
14
- conversationId: '21506473-a93c-4b38-9c32-68a5ca37ce73', // OWNER
15
- tutorName: 'Prof Jou Robots',
16
- contactId: '38138170-6009-40cd-be50-001249e80a0d',
17
- membershipId: '6297a4efa488cc775ac5e1dd',
18
- namespace: 'tutor_v1-2',
19
- author: 'Jonathan',
20
- clubName: 'comofazerumvideodeteste',
21
- productName: 'Curso de Assinatura',
22
- productId: 4266504
23
- }
11
+ await window.startChatWidget('root', {
12
+ hotmartToken: window.TOKEN,
13
+ locale: LANGUAGES.PT_BR,
14
+ conversationId: '21506473-a93c-4b38-9c32-68a5ca37ce73', // OWNER
15
+ tutorName: 'Prof Jou Robots',
16
+ contactId: '38138170-6009-40cd-be50-001249e80a0d',
17
+ membershipId: '6297a4efa488cc775ac5e1dd',
18
+ namespace: 'tutor_v1-2',
19
+ author: 'user',
20
+ clubName: 'comofazerumvideodeteste',
21
+ productName: 'Curso de Assinatura',
22
+ productId: 4266504,
23
+ sessionId: v4()
24
24
  })
25
25
  })()
26
26
  }
package/src/index.tsx CHANGED
@@ -1,20 +1,52 @@
1
+ import './config/styles/global.css'
2
+ import './config/styles/index.css'
3
+
1
4
  import { StrictMode } from 'react'
2
5
  import { createRoot } from 'react-dom/client'
3
6
 
4
7
  import { initDayjs } from './config/dayjs'
5
8
  import { initLanguage } from './config/i18n'
6
9
  import { initAxios } from './config/request/api'
10
+ import { productionMode } from './lib/utils'
7
11
  import { Main } from './main'
12
+ import { SparkieService } from './modules/sparkie'
8
13
  import { TutorWidgetEvents, TutorWidgetEventTypes } from './modules/widget'
9
- import type { StartTutorWidgetProps } from './types'
14
+ import type { WidgetSettingProps } from './types'
15
+
16
+ const loadMainStyles = () => {
17
+ const isProduction = productionMode
18
+ const bundlePath = !isProduction
19
+ ? `${process.env.BUNDLE_PATH}/`
20
+ : `${process.env.BUNDLE_PATH}/${process.env.APP_NAME}/_current/`
21
+
22
+ const cssPath = `${bundlePath}app-tutor-ai-consumer.css`
10
23
 
11
- window.startTutorWidget = async ({
24
+ if (!document.querySelector(`link[href="${cssPath}"]`)) {
25
+ const linkElement = document.createElement('link')
26
+ linkElement.rel = 'stylesheet'
27
+ linkElement.href = cssPath
28
+ document.head.appendChild(linkElement)
29
+ }
30
+ }
31
+
32
+ window.startChatWidget = async (
12
33
  elementId = 'tutor-chat-app-widget',
13
- settings
14
- }: StartTutorWidgetProps) => {
34
+ settings: WidgetSettingProps
35
+ ) => {
36
+ loadMainStyles()
37
+
15
38
  const rootElement = document.getElementById(elementId) as HTMLElement
16
39
  const root = createRoot(rootElement)
17
40
 
41
+ await SparkieService.initSparkie({
42
+ token: settings.hotmartToken,
43
+ skipPresenceSetup: true,
44
+ retryOptions: {
45
+ maxRetries: 5,
46
+ retryDelay: 2000,
47
+ backoffMultiplier: 1.5
48
+ }
49
+ })
18
50
  initAxios(settings.hotmartToken)
19
51
  await initLanguage(settings.locale)
20
52
  await initDayjs(settings.locale)
@@ -27,4 +59,4 @@ window.startTutorWidget = async ({
27
59
  )
28
60
  }
29
61
 
30
- window.closeTutorWidget = () => TutorWidgetEvents.get(TutorWidgetEventTypes.CLOSE)?.dispatch()
62
+ window.closeChatWidget = () => TutorWidgetEvents.get(TutorWidgetEventTypes.CLOSE)?.dispatch()
@@ -0,0 +1,17 @@
1
+ <svg viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path
3
+ d="M21.5813 16.7843L24.9086 18.5499L21.5813 20.3154L19.8068 23.6263L18.0313 20.3154L14.7041 18.5499L18.0313 16.7843L19.8068 13.4734L21.5813 16.7843ZM11.9119 9.10141L17.2354 11.9261L11.9119 14.7508L9.07199 20.0485L6.23301 14.7508L0.908569 11.9261L6.23301 9.10141L9.07199 3.80376L11.9119 9.10141ZM20.1457 3.24642L22.7786 4.64367L20.1457 6.03994L18.7413 8.66002L17.337 6.03994L14.7041 4.64367L17.337 3.24642L18.7413 0.626343L20.1457 3.24642Z"
4
+ fill="url(#paint0_linear_18592_52336)" />
5
+ <defs>
6
+ <linearGradient
7
+ id="paint0_linear_18592_52336"
8
+ x1="0.908569"
9
+ y1="12.1263"
10
+ x2="24.9086"
11
+ y2="12.1263"
12
+ gradientUnits="userSpaceOnUse">
13
+ <stop stop-color="#44D0FF" />
14
+ <stop offset="1" stop-color="#B48EFF" />
15
+ </linearGradient>
16
+ </defs>
17
+ </svg>
@@ -0,0 +1,5 @@
1
+ <svg viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path
3
+ d="M13.9748 9.83038L8.47139 15.0179C8.37759 15.1116 8.25251 15.1429 8.1587 15.1429C8.03362 15.1429 7.90855 15.1116 7.81474 15.0179L2.31136 9.83038C2.09248 9.64288 2.09248 9.33038 2.28009 9.11163C2.46771 8.89288 2.7804 8.89288 2.96802 9.08038L7.65839 13.4554L7.65839 1.67413C7.65839 1.39288 7.87728 1.14288 8.12743 1.14288C8.37759 1.14288 8.65901 1.39288 8.65901 1.67413L8.65901 13.4554L13.3181 9.08038C13.5057 8.89288 13.8184 8.89288 14.006 9.11163C14.1937 9.33038 14.1937 9.64288 13.9748 9.83038Z"
4
+ fill="currentColor" />
5
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
2
+ stroke="currentColor">
3
+ <path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
4
+ </svg>
@@ -1,2 +1,2 @@
1
1
  // Auto-generated file - DO NOT EDIT
2
- export type ValidIconNames = 'send'
2
+ export type ValidIconNames = 'ai-color' | 'arrow-down' | 'chevron-down' | 'send'