app-tutor-ai-consumer 1.43.1 → 1.44.1
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.
- package/CHANGELOG.md +23 -0
- package/package.json +1 -1
- package/src/index.tsx +3 -1
- package/src/lib/components/ai-icon/ai-icon.builder.ts +59 -0
- package/src/lib/components/ai-icon/ai-icon.spec.tsx +12 -0
- package/src/lib/components/ai-icon/ai-icon.tsx +43 -0
- package/src/lib/components/ai-icon/index.ts +2 -0
- package/src/lib/components/ai-icon/styles.module.css +47 -0
- package/src/lib/components/ai-icon/types.ts +7 -0
- package/src/lib/components/ai-icon-circle/ai-icon-circle.builder.ts +50 -0
- package/src/lib/components/ai-icon-circle/ai-icon-circle.spec.tsx +15 -0
- package/src/lib/components/ai-icon-circle/ai-icon-circle.tsx +101 -0
- package/src/lib/components/ai-icon-circle/index.ts +2 -0
- package/src/lib/components/ai-icon-circle/types.ts +6 -0
- package/src/lib/components/index.ts +2 -0
- package/src/lib/utils/get-membership-color.ts +12 -0
- package/src/lib/utils/index.ts +1 -0
- package/src/modules/widget/components/ai-disclaimer/ai-disclaimer.tsx +2 -2
- package/src/modules/widget/components/avatar-animation/avatar-animation.tsx +5 -2
- package/src/modules/widget/components/greetings-card/greetings-card.tsx +4 -2
- package/src/modules/widget/components/header/widget-header.spec.tsx +1 -1
- package/src/modules/widget/components/header/widget-header.tsx +4 -3
- package/src/modules/widget/components/information-page/information-page.tsx +4 -2
- package/src/modules/widget/components/loading-page/loading-page.tsx +5 -1
- package/src/modules/widget/components/starter-page/starter-page-content/starter-page-content.spec.tsx +1 -1
- package/src/modules/widget/hooks/index.ts +1 -0
- package/src/modules/widget/hooks/use-membership-color/index.ts +1 -0
- package/src/modules/widget/hooks/use-membership-color/use-membership-color.tsx +12 -0
- package/src/types.ts +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
## [1.44.1](https://github.com/Hotmart-Org/app-tutor-ai-consumer/compare/v1.44.0...v1.44.1) (2025-12-26)
|
|
2
|
+
|
|
3
|
+
# [1.44.0](https://github.com/Hotmart-Org/app-tutor-ai-consumer/compare/v1.43.1...v1.44.0) (2025-12-22)
|
|
4
|
+
|
|
5
|
+
### Bug Fixes
|
|
6
|
+
|
|
7
|
+
- animation icon ([b6c4f7c](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/b6c4f7c7815be5158cc30ed79cc1b4dd343c1a20))
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
- change ia icon disclaimer ([a45724c](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/a45724c262a6204bb5c556423a381fbb966d461f))
|
|
12
|
+
- create circle ([6f7a660](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/6f7a660717426f1f6b918f155a44710c7c5cf4a1))
|
|
13
|
+
- create ia icon cosmos ds ([1cc1f27](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/1cc1f2728dffc9c9591135d1862fe5341d95b2fc))
|
|
14
|
+
- update ([54caf52](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/54caf527db88b299ffb003123ab454bf2f510330))
|
|
15
|
+
- update ia-consumer ([1b97f6b](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/1b97f6b13ad38075919ea0a05d2afd1434918ee3))
|
|
16
|
+
- update tutor ([809a465](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/809a465464fe5aba13a994bb7dcb9a7e73673fb7))
|
|
17
|
+
- update unit test ([2ddb0e4](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/2ddb0e41e89f792355e34be4159889947e89a37d))
|
|
18
|
+
- use new ai icon ([87dc20e](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/87dc20ec62374f729215b8f3bdfdcef85ea1ab2e))
|
|
19
|
+
|
|
20
|
+
### Reverts
|
|
21
|
+
|
|
22
|
+
- animation icon change ([b3f98a5](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/b3f98a55cc2557f061ee1e9b05d92ae0f958c562))
|
|
23
|
+
|
|
1
24
|
## [1.43.1](https://github.com/Hotmart-Org/app-tutor-ai-consumer/compare/v1.43.0...v1.43.1) (2025-12-16)
|
|
2
25
|
|
|
3
26
|
# [1.43.0](https://github.com/Hotmart-Org/app-tutor-ai-consumer/compare/v1.42.0...v1.43.0) (2025-12-02)
|
package/package.json
CHANGED
package/src/index.tsx
CHANGED
|
@@ -10,7 +10,7 @@ import { bootstrap } from './bootstrap'
|
|
|
10
10
|
import { initTheme } from './config/theme'
|
|
11
11
|
import { devMode, productionMode } from './lib/utils'
|
|
12
12
|
import { SparkieService } from './modules/sparkie'
|
|
13
|
-
import { createStore } from './modules/widget/store'
|
|
13
|
+
import { createStore, widgetSettingsAtom } from './modules/widget/store'
|
|
14
14
|
import type { Theme, WidgetSettingProps } from './types'
|
|
15
15
|
import Wrapper from './wrapper'
|
|
16
16
|
|
|
@@ -54,6 +54,8 @@ window.startChatWidget = async (
|
|
|
54
54
|
const widgetSettings = { ...settings, config: { ...settings.config, theme } }
|
|
55
55
|
const store = createStore()
|
|
56
56
|
|
|
57
|
+
store.set(widgetSettingsAtom, widgetSettings)
|
|
58
|
+
|
|
57
59
|
root.render(
|
|
58
60
|
<Wrapper settings={widgetSettings} store={store} queryClient={queryClient} state={'LOADING'} />
|
|
59
61
|
)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { chance } from '@/src/config/tests'
|
|
2
|
+
|
|
3
|
+
import type { AiIconProps } from './types'
|
|
4
|
+
|
|
5
|
+
export class AiIconPropsBuilder implements AiIconProps {
|
|
6
|
+
animate?: boolean
|
|
7
|
+
color?: string
|
|
8
|
+
className?: string
|
|
9
|
+
size?: number
|
|
10
|
+
renderCustomAvatar?: boolean
|
|
11
|
+
customAvatarSize?: 'sm' | 'lg'
|
|
12
|
+
|
|
13
|
+
constructor() {
|
|
14
|
+
this.animate = false
|
|
15
|
+
this.size = 30
|
|
16
|
+
this.color = '#ffffff'
|
|
17
|
+
this.renderCustomAvatar = false
|
|
18
|
+
this.customAvatarSize = 'sm'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
withAnimate(animate: boolean) {
|
|
22
|
+
this.animate = animate
|
|
23
|
+
return this
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
withSize(size: number) {
|
|
27
|
+
this.size = size
|
|
28
|
+
return this
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
withColor(color: string) {
|
|
32
|
+
this.color = color
|
|
33
|
+
return this
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
withClassName(className: string) {
|
|
37
|
+
this.className = className
|
|
38
|
+
return this
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
withRenderCustomAvatar(renderCustomAvatar: boolean) {
|
|
42
|
+
this.renderCustomAvatar = renderCustomAvatar
|
|
43
|
+
return this
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
withCustomAvatarSize(customAvatarSize: 'sm' | 'lg') {
|
|
47
|
+
this.customAvatarSize = customAvatarSize
|
|
48
|
+
return this
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
withRandomColor() {
|
|
52
|
+
this.color = chance.color({ format: 'hex' })
|
|
53
|
+
return this
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
build() {
|
|
57
|
+
return this
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import { AiIcon } from './ai-icon'
|
|
5
|
+
|
|
6
|
+
describe('AiIcon', () => {
|
|
7
|
+
it('should render the icon', () => {
|
|
8
|
+
render(<AiIcon />)
|
|
9
|
+
|
|
10
|
+
expect(screen.getByTestId('ai-icon')).toBeInTheDocument()
|
|
11
|
+
})
|
|
12
|
+
})
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import clsx from 'clsx'
|
|
2
|
+
|
|
3
|
+
import type { AiIconProps } from './types'
|
|
4
|
+
|
|
5
|
+
import styles from './styles.module.css'
|
|
6
|
+
|
|
7
|
+
export function AiIcon({ animate = false, size, color, className, style, ...props }: AiIconProps) {
|
|
8
|
+
return (
|
|
9
|
+
<svg
|
|
10
|
+
data-test='ai-icon'
|
|
11
|
+
width={size}
|
|
12
|
+
height={size}
|
|
13
|
+
viewBox='0 0 96 96'
|
|
14
|
+
fill='none'
|
|
15
|
+
xmlns='http://www.w3.org/2000/svg'
|
|
16
|
+
className={className}
|
|
17
|
+
style={{ color, ...style }}
|
|
18
|
+
{...props}>
|
|
19
|
+
<defs>
|
|
20
|
+
<filter id='ai-icon-glow-light' x='-50%' y='-50%' width='200%' height='200%'>
|
|
21
|
+
<feGaussianBlur stdDeviation='8' result='coloredBlur' />
|
|
22
|
+
<feMerge>
|
|
23
|
+
<feMergeNode in='coloredBlur' />
|
|
24
|
+
<feMergeNode in='SourceGraphic' />
|
|
25
|
+
</feMerge>
|
|
26
|
+
</filter>
|
|
27
|
+
</defs>
|
|
28
|
+
|
|
29
|
+
<path
|
|
30
|
+
d='M.919 47.865c-.119-.014-.119-.197 0-.21C45.02 42.415 46.093 16.083 47.904.563c.014-.118.178-.118.192 0 1.81 15.52 2.883 41.852 46.986 47.09.118.014.118.197 0 .21-44.103 5.239-45.175 31.571-46.986 47.092-.014.117-.178.117-.192 0-1.81-15.52-2.883-41.853-46.985-47.091Z'
|
|
31
|
+
fill='currentColor'
|
|
32
|
+
fillOpacity='0.5'
|
|
33
|
+
className={clsx(styles.backLayer, animate && styles.backRotate)}
|
|
34
|
+
style={{ filter: animate ? `url(#ai-icon-glow-light)` : undefined }}
|
|
35
|
+
/>
|
|
36
|
+
|
|
37
|
+
<path
|
|
38
|
+
d='M.919 47.865c-.119-.014-.119-.197 0-.21C45.02 42.415 46.093 16.083 47.904.563c.014-.118.178-.118.192 0 1.81 15.52 2.883 41.852 46.986 47.09.118.014.118.197 0 .21-44.103 5.239-45.175 31.571-46.986 47.092-.014.117-.178.117-.192 0-1.81-15.52-2.883-41.853-46.985-47.091Z'
|
|
39
|
+
fill='currentColor'
|
|
40
|
+
/>
|
|
41
|
+
</svg>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
@keyframes ai-icon-back-rotate {
|
|
2
|
+
0% {
|
|
3
|
+
opacity: 0.4;
|
|
4
|
+
transform: rotate(45deg) scale(0.9);
|
|
5
|
+
}
|
|
6
|
+
10% {
|
|
7
|
+
opacity: 0.7;
|
|
8
|
+
transform: rotate(135deg) scale(1.1);
|
|
9
|
+
}
|
|
10
|
+
25% {
|
|
11
|
+
opacity: 0.7;
|
|
12
|
+
transform: rotate(135deg) scale(1.1);
|
|
13
|
+
}
|
|
14
|
+
35% {
|
|
15
|
+
opacity: 0.7;
|
|
16
|
+
transform: rotate(225deg) scale(1.1);
|
|
17
|
+
}
|
|
18
|
+
50% {
|
|
19
|
+
opacity: 0.7;
|
|
20
|
+
transform: rotate(225deg) scale(1.1);
|
|
21
|
+
}
|
|
22
|
+
60% {
|
|
23
|
+
opacity: 0.7;
|
|
24
|
+
transform: rotate(315deg) scale(1.1);
|
|
25
|
+
}
|
|
26
|
+
75% {
|
|
27
|
+
opacity: 0.7;
|
|
28
|
+
transform: rotate(315deg) scale(1.1);
|
|
29
|
+
}
|
|
30
|
+
85% {
|
|
31
|
+
opacity: 0.4;
|
|
32
|
+
transform: rotate(405deg) scale(0.9);
|
|
33
|
+
}
|
|
34
|
+
100% {
|
|
35
|
+
opacity: 0.4;
|
|
36
|
+
transform: rotate(405deg) scale(0.9);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.backLayer {
|
|
41
|
+
transform-origin: center;
|
|
42
|
+
transform: rotate(45deg);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.backRotate {
|
|
46
|
+
animation: ai-icon-back-rotate 3s cubic-bezier(0.5, -0.5, 0, 1.5) infinite;
|
|
47
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { chance } from '@/src/config/tests'
|
|
2
|
+
|
|
3
|
+
import type { AiIconCircleProps } from './types'
|
|
4
|
+
|
|
5
|
+
export class AiIconCirclePropsBuilder implements AiIconCircleProps {
|
|
6
|
+
size: number
|
|
7
|
+
animate?: boolean
|
|
8
|
+
className?: string
|
|
9
|
+
color?: string
|
|
10
|
+
|
|
11
|
+
constructor() {
|
|
12
|
+
this.size = 36
|
|
13
|
+
this.animate = false
|
|
14
|
+
this.color = '#ffffff'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
withSize(size: number) {
|
|
18
|
+
this.size = size
|
|
19
|
+
return this
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
withAnimate(animate: boolean) {
|
|
23
|
+
this.animate = animate
|
|
24
|
+
return this
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
withClassName(className: string) {
|
|
28
|
+
this.className = className
|
|
29
|
+
return this
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
withColor(color: string) {
|
|
33
|
+
this.color = color
|
|
34
|
+
return this
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
withRandomSize() {
|
|
38
|
+
this.size = chance.integer({ min: 40, max: 200 })
|
|
39
|
+
return this
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
withRandomColor() {
|
|
43
|
+
this.color = chance.color({ format: 'hex' })
|
|
44
|
+
return this
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
build() {
|
|
48
|
+
return this
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import { AiIconCircle } from './ai-icon-circle'
|
|
5
|
+
import { AiIconCirclePropsBuilder } from './ai-icon-circle.builder'
|
|
6
|
+
|
|
7
|
+
describe('AiIconCircle', () => {
|
|
8
|
+
it('should render the component', () => {
|
|
9
|
+
const props = new AiIconCirclePropsBuilder().build()
|
|
10
|
+
|
|
11
|
+
render(<AiIconCircle {...props} />)
|
|
12
|
+
|
|
13
|
+
expect(screen.getByTestId('ai-icon-circle')).toBeInTheDocument()
|
|
14
|
+
})
|
|
15
|
+
})
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import clsx from 'clsx'
|
|
2
|
+
|
|
3
|
+
import { useWidgetSettingsAtomValue } from '@/src/modules/widget/store'
|
|
4
|
+
import { AiIcon } from '../ai-icon'
|
|
5
|
+
|
|
6
|
+
import type { AiIconCircleProps } from './types'
|
|
7
|
+
|
|
8
|
+
export function AiIconCircle({ size = 56, animate = false, className, color }: AiIconCircleProps) {
|
|
9
|
+
const settings = useWidgetSettingsAtomValue()
|
|
10
|
+
const theme = settings?.config?.theme || 'dark'
|
|
11
|
+
const customAvatarImg = settings?.avatar
|
|
12
|
+
const iconSize = size * 0.5
|
|
13
|
+
const isLightMode = theme === 'light'
|
|
14
|
+
|
|
15
|
+
if (customAvatarImg) {
|
|
16
|
+
return (
|
|
17
|
+
<div
|
|
18
|
+
data-test='ai-icon-circle'
|
|
19
|
+
className={clsx('overflow-hidden rounded-full', className)}
|
|
20
|
+
style={{
|
|
21
|
+
width: `${size}px`,
|
|
22
|
+
height: `${size}px`,
|
|
23
|
+
border: `1.5px solid ${color}`
|
|
24
|
+
}}>
|
|
25
|
+
<picture>
|
|
26
|
+
<source srcSet={customAvatarImg.webp} type='image/webp' />
|
|
27
|
+
<img src={customAvatarImg.original} alt='Avatar' className='h-full w-full object-cover' />
|
|
28
|
+
</picture>
|
|
29
|
+
</div>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div
|
|
35
|
+
data-test='ai-icon-circle'
|
|
36
|
+
className={clsx('relative', className)}
|
|
37
|
+
style={{ width: size, height: size }}>
|
|
38
|
+
<svg
|
|
39
|
+
width={size}
|
|
40
|
+
height={size}
|
|
41
|
+
viewBox='0 0 59 59'
|
|
42
|
+
fill='none'
|
|
43
|
+
xmlns='http://www.w3.org/2000/svg'
|
|
44
|
+
style={{ position: 'absolute', top: 0, left: 0 }}>
|
|
45
|
+
<rect x='0.75' y='0.75' width='57.5' height='57.5' rx='28.75' fill={color} />
|
|
46
|
+
<rect x='0.75' y='0.75' width='57.5' height='57.5' rx='28.75' fill='url(#paint0_linear)' />
|
|
47
|
+
<rect
|
|
48
|
+
x='0.75'
|
|
49
|
+
y='0.75'
|
|
50
|
+
width='57.5'
|
|
51
|
+
height='57.5'
|
|
52
|
+
rx='28.75'
|
|
53
|
+
stroke={color}
|
|
54
|
+
strokeWidth='1.5'
|
|
55
|
+
/>
|
|
56
|
+
<rect
|
|
57
|
+
x='0.75'
|
|
58
|
+
y='0.75'
|
|
59
|
+
width='57.5'
|
|
60
|
+
height='57.5'
|
|
61
|
+
rx='28.75'
|
|
62
|
+
stroke='url(#paint1_linear)'
|
|
63
|
+
strokeOpacity='0.5'
|
|
64
|
+
strokeWidth='1.5'
|
|
65
|
+
/>
|
|
66
|
+
<circle cx='29.5' cy='29.5' r='24' fill={isLightMode ? 'white' : 'black'} />
|
|
67
|
+
<defs>
|
|
68
|
+
<linearGradient
|
|
69
|
+
id='paint0_linear'
|
|
70
|
+
x1='29.5'
|
|
71
|
+
y1={isLightMode ? '57.5' : '1.5'}
|
|
72
|
+
x2='29.5'
|
|
73
|
+
y2={isLightMode ? '1.5' : '57.5'}
|
|
74
|
+
gradientUnits='userSpaceOnUse'>
|
|
75
|
+
<stop stopColor={isLightMode ? 'white' : '#090909'} stopOpacity='0.9' />
|
|
76
|
+
<stop offset='1' stopColor={isLightMode ? 'white' : '#090909'} stopOpacity='0.95' />
|
|
77
|
+
</linearGradient>
|
|
78
|
+
<linearGradient
|
|
79
|
+
id='paint1_linear'
|
|
80
|
+
x1='29.5'
|
|
81
|
+
y1={isLightMode ? '57.5' : '1.5'}
|
|
82
|
+
x2='29.5'
|
|
83
|
+
y2={isLightMode ? '1.5' : '57.5'}
|
|
84
|
+
gradientUnits='userSpaceOnUse'>
|
|
85
|
+
<stop stopColor={isLightMode ? 'white' : undefined} stopOpacity='0' />
|
|
86
|
+
<stop offset='1' stopColor={isLightMode ? 'white' : undefined} />
|
|
87
|
+
</linearGradient>
|
|
88
|
+
</defs>
|
|
89
|
+
</svg>
|
|
90
|
+
|
|
91
|
+
<div
|
|
92
|
+
className='absolute left-1/2 top-1/2 flex -translate-x-1/2 -translate-y-1/2 items-center justify-center'
|
|
93
|
+
style={{
|
|
94
|
+
width: iconSize,
|
|
95
|
+
height: iconSize
|
|
96
|
+
}}>
|
|
97
|
+
<AiIcon animate={animate} size={iconSize} color={color} />
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
)
|
|
101
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Theme } from '@/src/types'
|
|
2
|
+
|
|
3
|
+
const DEFAULT_COLOR = '#a5a09f'
|
|
4
|
+
|
|
5
|
+
export const getMembershipColor = (
|
|
6
|
+
darkTransformed?: string,
|
|
7
|
+
lightTransformed?: string,
|
|
8
|
+
theme?: Theme
|
|
9
|
+
): string => {
|
|
10
|
+
const color = theme === 'dark' ? darkTransformed : lightTransformed
|
|
11
|
+
return color || DEFAULT_COLOR
|
|
12
|
+
}
|
package/src/lib/utils/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from './constants'
|
|
2
2
|
export * from './copy-text-to-clipboard'
|
|
3
3
|
export * from './extract-text-from-react-nodes'
|
|
4
|
+
export * from './get-membership-color'
|
|
4
5
|
export { default as HttpCodes } from './http-codes'
|
|
5
6
|
export * from './is-theme-dark'
|
|
6
7
|
export * from './languages'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useTranslation } from 'react-i18next'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { AiIcon } from '@/src/lib/components'
|
|
4
4
|
|
|
5
5
|
const AIDisclaimer = () => {
|
|
6
6
|
const { t } = useTranslation()
|
|
@@ -9,7 +9,7 @@ const AIDisclaimer = () => {
|
|
|
9
9
|
<div className='mt-4 flex w-full items-center gap-1 text-xs text-neutral-500'>
|
|
10
10
|
<p className='mb-0'>{t('ai_disclaimer.technology')}</p>
|
|
11
11
|
|
|
12
|
-
<
|
|
12
|
+
<AiIcon className='inline-flex h-3 w-3 align-middle' />
|
|
13
13
|
|
|
14
14
|
<p className='mb-0'>{t('ai_disclaimer.hotmart_ai')}</p>
|
|
15
15
|
</div>
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
import { AiIcon } from '@/src/lib/components'
|
|
2
|
+
import { useMembershipColor } from '../../hooks'
|
|
2
3
|
|
|
3
4
|
const AvatarAnimation = () => {
|
|
5
|
+
const membershipColor = useMembershipColor()
|
|
6
|
+
|
|
4
7
|
return (
|
|
5
8
|
<div className='flex h-11 w-11 items-center justify-center rounded-lg bg-neutral-300'>
|
|
6
|
-
<
|
|
9
|
+
<AiIcon size={30} animate aria-hidden color={membershipColor} />
|
|
7
10
|
</div>
|
|
8
11
|
)
|
|
9
12
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import clsx from 'clsx'
|
|
2
2
|
import { useTranslation } from 'react-i18next'
|
|
3
3
|
|
|
4
|
+
import { AiIconCircle } from '@/src/lib/components'
|
|
5
|
+
import { useMembershipColor } from '../../hooks'
|
|
4
6
|
import { useIsAgentParentAtomValue } from '../../store'
|
|
5
|
-
import { AIAvatar } from '../ai-avatar'
|
|
6
7
|
|
|
7
8
|
export type GreetingsCardProps = {
|
|
8
9
|
tutorName: string
|
|
@@ -13,11 +14,12 @@ export type GreetingsCardProps = {
|
|
|
13
14
|
function GreetingsCard({ author, tutorName, isDarkTheme = false }: GreetingsCardProps) {
|
|
14
15
|
const { t } = useTranslation()
|
|
15
16
|
const isAgentMode = useIsAgentParentAtomValue()
|
|
17
|
+
const membershipColor = useMembershipColor()
|
|
16
18
|
|
|
17
19
|
return (
|
|
18
20
|
<div className='flex flex-col items-center justify-center'>
|
|
19
21
|
<div className='max-md:hidden md:mb-4 md:block'>
|
|
20
|
-
<
|
|
22
|
+
<AiIconCircle size={56} color={membershipColor} />
|
|
21
23
|
</div>
|
|
22
24
|
<div className='flex flex-col items-center justify-center gap-4 text-center'>
|
|
23
25
|
<div className='flex flex-col gap-2'>
|
|
@@ -24,7 +24,7 @@ describe('<WidgetHeader />', () => {
|
|
|
24
24
|
it('should render WidgetHeaderContent when prop showContent is true', () => {
|
|
25
25
|
renderComponent()
|
|
26
26
|
|
|
27
|
-
expect(screen.
|
|
27
|
+
expect(screen.getByTestId(/ai-icon-circle/i)).toBeInTheDocument()
|
|
28
28
|
|
|
29
29
|
expect(screen.queryByRole('button', { name: /Arrow Left Icon/i })).not.toBeInTheDocument()
|
|
30
30
|
})
|
|
@@ -9,11 +9,11 @@ import {
|
|
|
9
9
|
ClickTutorInfoSchema,
|
|
10
10
|
ClickTutorMinimizeSchema
|
|
11
11
|
} from '@/src/config/datahub/schemas/tutor'
|
|
12
|
-
import { Button, Icon, Tooltip } from '@/src/lib/components'
|
|
12
|
+
import { AiIconCircle, Button, Icon, Tooltip } from '@/src/lib/components'
|
|
13
13
|
import { useMediaQuery } from '@/src/lib/hooks'
|
|
14
14
|
import { TutorWidgetEvents } from '../../events'
|
|
15
|
+
import { useMembershipColor } from '../../hooks'
|
|
15
16
|
import { useWidgetGoBackTabAtom, useWidgetTabsAtom } from '../../store'
|
|
16
|
-
import { AIAvatar } from '../ai-avatar'
|
|
17
17
|
|
|
18
18
|
import type { WidgetHeaderProps } from './types'
|
|
19
19
|
|
|
@@ -91,6 +91,7 @@ function WidgetHeader({
|
|
|
91
91
|
}: WidgetHeaderProps) {
|
|
92
92
|
const { t } = useTranslation()
|
|
93
93
|
const [, goBack] = useWidgetGoBackTabAtom()
|
|
94
|
+
const membershipColor = useMembershipColor()
|
|
94
95
|
const name = tutorName ?? t('general.name')
|
|
95
96
|
|
|
96
97
|
const handleHideWidget = () => {
|
|
@@ -144,7 +145,7 @@ function WidgetHeader({
|
|
|
144
145
|
})}>
|
|
145
146
|
{showContent && (
|
|
146
147
|
<>
|
|
147
|
-
{!showContentWithoutMeta && <
|
|
148
|
+
{!showContentWithoutMeta && <AiIconCircle size={40} color={membershipColor} />}
|
|
148
149
|
{name && <h4 className='text-sm/loose font-bold'>{name}</h4>}
|
|
149
150
|
</>
|
|
150
151
|
)}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import clsx from 'clsx'
|
|
2
2
|
import { useTranslation } from 'react-i18next'
|
|
3
3
|
|
|
4
|
+
import { AiIconCircle } from '@/src/lib/components'
|
|
5
|
+
import { useMembershipColor } from '../../hooks'
|
|
4
6
|
import { useWidgetSettingsAtom } from '../../store'
|
|
5
|
-
import { AIAvatar } from '../ai-avatar'
|
|
6
7
|
import { WidgetHeader } from '../header'
|
|
7
8
|
import { PageLayout } from '../page-layout'
|
|
8
9
|
|
|
@@ -12,6 +13,7 @@ import { InformationCard } from './information-card'
|
|
|
12
13
|
function WidgetInformationPage() {
|
|
13
14
|
const { t } = useTranslation()
|
|
14
15
|
const [settings] = useWidgetSettingsAtom()
|
|
16
|
+
const membershipColor = useMembershipColor()
|
|
15
17
|
const isDarkMode = settings?.config?.theme === 'dark'
|
|
16
18
|
const tutorName = settings?.tutorName ?? t('general.name')
|
|
17
19
|
|
|
@@ -26,7 +28,7 @@ function WidgetInformationPage() {
|
|
|
26
28
|
|
|
27
29
|
<div className='my-8 flex justify-center'>
|
|
28
30
|
<div className='flex flex-col items-center gap-2'>
|
|
29
|
-
<
|
|
31
|
+
<AiIconCircle size={56} color={membershipColor} />
|
|
30
32
|
|
|
31
33
|
<h3
|
|
32
34
|
className={clsx('font-bold', {
|
|
@@ -5,7 +5,11 @@ import { ChatInput, MessageSkeleton } from '@/src/modules/messages/components'
|
|
|
5
5
|
import { WidgetHeader } from '../header'
|
|
6
6
|
import { PageLayout } from '../page-layout'
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
type WidgetLoadingPageProps = {
|
|
9
|
+
showHeader?: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function WidgetLoadingPage({ showHeader = true }: WidgetLoadingPageProps) {
|
|
9
13
|
const chatInputRef = useRef<HTMLTextAreaElement>(null)
|
|
10
14
|
|
|
11
15
|
const handler = useCallback((e: Event) => {
|
|
@@ -8,7 +8,7 @@ describe('<WidgetStarterPageContent />', () => {
|
|
|
8
8
|
it('should render greetings card when not in agent mode', () => {
|
|
9
9
|
renderComponent()
|
|
10
10
|
|
|
11
|
-
expect(screen.
|
|
11
|
+
expect(screen.getByTestId('ai-icon-circle')).toBeInTheDocument()
|
|
12
12
|
expect(screen.getByText(/general.greetings.hello/i)).toBeInTheDocument()
|
|
13
13
|
expect(screen.getByText(/general.greetings.firstMessage/i)).toBeInTheDocument()
|
|
14
14
|
expect(screen.getByText(/general.greetings.description/i)).toBeInTheDocument()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './use-membership-color'
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { getMembershipColor } from '@/src/lib/utils'
|
|
2
|
+
import { useWidgetSettingsAtomValue } from '@/src/modules/widget/store'
|
|
3
|
+
|
|
4
|
+
export const useMembershipColor = () => {
|
|
5
|
+
const settings = useWidgetSettingsAtomValue()
|
|
6
|
+
|
|
7
|
+
return getMembershipColor(
|
|
8
|
+
settings?.membershipPrimaryColor?.darkTransformed,
|
|
9
|
+
settings?.membershipPrimaryColor?.lightTransformed,
|
|
10
|
+
settings?.config?.theme
|
|
11
|
+
)
|
|
12
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -43,6 +43,11 @@ export type WidgetSettingProps = {
|
|
|
43
43
|
productName: string
|
|
44
44
|
sessionId: string
|
|
45
45
|
membershipId?: string
|
|
46
|
+
membershipPrimaryColor?: {
|
|
47
|
+
darkTransformed: string
|
|
48
|
+
lightTransformed: string
|
|
49
|
+
original: string
|
|
50
|
+
}
|
|
46
51
|
membershipSlug?: string
|
|
47
52
|
userId?: string
|
|
48
53
|
tutorName?: string
|