app-tutor-ai-consumer 1.0.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/.adr-dir +1 -0
- package/.github/CODEOWNERS +1 -0
- package/.github/pull_request_template.md +46 -0
- package/.github/workflows/jira.yaml +13 -0
- package/.github/workflows/merge-checker.yaml +14 -0
- package/.github/workflows/pr-agent.yaml +17 -0
- package/.github/workflows/production.yml +155 -0
- package/.github/workflows/quality.yml +63 -0
- package/.github/workflows/rollback.yml +133 -0
- package/.github/workflows/staging.yml +153 -0
- package/.husky/commit-msg +1 -0
- package/.husky/post-merge +1 -0
- package/.husky/pre-commit +1 -0
- package/.nvmrc +1 -0
- package/.prettierignore +8 -0
- package/.prettierrc +20 -0
- package/.releaserc.json +22 -0
- package/CHANGELOG.md +7 -0
- package/babel.config.js +3 -0
- package/catalog-info.yaml +21 -0
- package/commitlint.config.js +8 -0
- package/config/certs/.keep +0 -0
- package/config/certs/ssl-generate.sh +12 -0
- package/config/rspack/rspack.config.js +176 -0
- package/config/rspack/utils/alias.js +12 -0
- package/config/rspack/utils/devserver.config.js +34 -0
- package/config/rspack/utils/envs.js +39 -0
- package/config/rspack/utils/paths.js +29 -0
- package/config/rspack/utils/plugins.js +41 -0
- package/config/vitest/__mocks__/i18n.tsx +15 -0
- package/config/vitest/index.ts +1 -0
- package/config/vitest/polyfills/global.js +19 -0
- package/config/vitest/setupTests.ts +26 -0
- package/config/vitest/vitest.config.mts +47 -0
- package/docs/README.md +15 -0
- package/docs/architecture/decisions/index.md +0 -0
- package/docs/architecture/decisions/templates/template.md +82 -0
- package/docs/architecture/design/index.md +0 -0
- package/environments/.env.development +42 -0
- package/environments/.env.production +41 -0
- package/environments/.env.staging +40 -0
- package/environments/.env.test +41 -0
- package/eslint.config.mjs +87 -0
- package/mkdocs.yaml +11 -0
- package/package.json +130 -0
- package/postcss.config.js +3 -0
- package/public/favicon.ico +0 -0
- package/public/index.html +18 -0
- package/scripts/generate-icon-types.js +31 -0
- package/src/@types/declarations.d.ts +25 -0
- package/src/@types/env.d.ts +1 -0
- package/src/@types/global.types.ts +64 -0
- package/src/@types/index.d.ts +16 -0
- package/src/config/i18n/constants.ts +9 -0
- package/src/config/i18n/hooks/index.ts +1 -0
- package/src/config/i18n/hooks/use-app-lang/index.ts +1 -0
- package/src/config/i18n/hooks/use-app-lang/use-app-lang.tsx +22 -0
- package/src/config/i18n/index.ts +5 -0
- package/src/config/i18n/init.ts +44 -0
- package/src/config/i18n/types.ts +5 -0
- package/src/config/i18n/utils/get-lang.ts +10 -0
- package/src/config/styles/global.css +171 -0
- package/src/config/styles/index.css +5 -0
- package/src/config/styles/shared-styles.module.css +16 -0
- package/src/config/tests/abstract-mock-generator.ts +9 -0
- package/src/config/tests/customRenderHook.tsx +32 -0
- package/src/config/tests/handlers.ts +7 -0
- package/src/config/tests/index.ts +4 -0
- package/src/config/tests/mockRequest.tsx +36 -0
- package/src/config/tests/types.ts +10 -0
- package/src/config/tests/utils.tsx +38 -0
- package/src/config/tests/worker.ts +5 -0
- package/src/config/tests/wrappers.tsx +50 -0
- package/src/development-bootstrap.tsx +26 -0
- package/src/index.tsx +26 -0
- package/src/lib/components/icons/icon-names.d.ts +2 -0
- package/src/lib/components/icons/icon.tsx +41 -0
- package/src/lib/components/icons/index.ts +1 -0
- package/src/lib/components/icons/send.svg +3 -0
- package/src/lib/components/index.ts +1 -0
- package/src/lib/components/spinner/index.ts +2 -0
- package/src/lib/components/spinner/spinner.tsx +25 -0
- package/src/lib/components/spinner/styles.module.css +31 -0
- package/src/lib/components/spinner/types.ts +1 -0
- package/src/lib/hooks/index.ts +1 -0
- package/src/lib/hooks/use-default-id/index.ts +1 -0
- package/src/lib/hooks/use-default-id/use-default-id.tsx +13 -0
- package/src/lib/utils/constants.ts +2 -0
- package/src/lib/utils/http-codes.ts +13 -0
- package/src/lib/utils/index.ts +3 -0
- package/src/lib/utils/languages.ts +7 -0
- package/src/main/index.ts +1 -0
- package/src/main/main.spec.tsx +15 -0
- package/src/main/main.tsx +27 -0
- package/src/main/styles.module.css +15 -0
- package/src/modules/create-message/components/chat-input/chat-input.tsx +29 -0
- package/src/modules/create-message/components/chat-input/index.ts +2 -0
- package/src/modules/create-message/components/chat-input/types.ts +3 -0
- package/src/modules/create-message/components/index.ts +1 -0
- package/src/modules/widget/components/ai-avatar/ai-avatar.tsx +59 -0
- package/src/modules/widget/components/ai-avatar/index.ts +1 -0
- package/src/modules/widget/components/greetings-card/greetings-card.tsx +40 -0
- package/src/modules/widget/components/greetings-card/index.ts +1 -0
- package/src/modules/widget/components/greetings-card/styles.module.css +9 -0
- package/src/modules/widget/components/index.ts +1 -0
- package/src/modules/widget/events.ts +21 -0
- package/src/modules/widget/index.ts +2 -0
- package/src/modules/widget/types.ts +7 -0
- package/src/types.ts +23 -0
- package/tailwind.config.js +8 -0
- package/tsconfig.json +128 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--hc-color-primary-100: #ebf0ff;
|
|
3
|
+
/* Hotmart Product */
|
|
4
|
+
--hc-color-primary-200: #bed1ff;
|
|
5
|
+
/* Hotmart Product */
|
|
6
|
+
--hc-color-primary-300: #89a8f8;
|
|
7
|
+
/* Hotmart Product */
|
|
8
|
+
--hc-color-primary-400: #5981e3;
|
|
9
|
+
/* Hotmart Product */
|
|
10
|
+
--hc-color-primary-500: #355cc0;
|
|
11
|
+
/* Hotmart Product */
|
|
12
|
+
--hc-color-primary-600: #253f82;
|
|
13
|
+
/* Hotmart Product */
|
|
14
|
+
--hc-color-primary-700: #162c64;
|
|
15
|
+
/* Hotmart Product */
|
|
16
|
+
--hc-color-secondary-100: #edffff;
|
|
17
|
+
--hc-color-secondary-200: #a2eaea;
|
|
18
|
+
--hc-color-secondary-300: #7cdbdb;
|
|
19
|
+
--hc-color-secondary-400: #51c1c3;
|
|
20
|
+
--hc-color-secondary-500: #0a9090;
|
|
21
|
+
--hc-color-secondary-600: #066262;
|
|
22
|
+
--hc-color-secondary-700: #024242;
|
|
23
|
+
--hc-color-success-100: #edfff5;
|
|
24
|
+
--hc-color-success-200: #99e9bb;
|
|
25
|
+
--hc-color-success-300: #4acc82;
|
|
26
|
+
--hc-color-success-400: #009d43;
|
|
27
|
+
--hc-color-success-500: #006e2f;
|
|
28
|
+
--hc-color-success-600: #005122;
|
|
29
|
+
--hc-color-success-700: #003416;
|
|
30
|
+
--hc-color-warning-100: #fffaeb;
|
|
31
|
+
--hc-color-warning-200: #f9e298;
|
|
32
|
+
--hc-color-warning-300: #fbd458;
|
|
33
|
+
--hc-color-warning-400: #efba0f;
|
|
34
|
+
--hc-color-warning-500: #a47c00;
|
|
35
|
+
--hc-color-warning-600: #6c5200;
|
|
36
|
+
--hc-color-warning-700: #453400;
|
|
37
|
+
--hc-color-danger-100: #fff0f0;
|
|
38
|
+
--hc-color-danger-200: #f9cac8;
|
|
39
|
+
--hc-color-danger-300: #e37570;
|
|
40
|
+
--hc-color-danger-400: #d6342c;
|
|
41
|
+
--hc-color-danger-500: #a81a0a;
|
|
42
|
+
--hc-color-danger-600: #830d00;
|
|
43
|
+
--hc-color-danger-700: #590900;
|
|
44
|
+
--hc-color-info-100: #f0f4ff;
|
|
45
|
+
--hc-color-info-200: #bed1ff;
|
|
46
|
+
--hc-color-info-300: #89a8f8;
|
|
47
|
+
--hc-color-info-400: #5981e3;
|
|
48
|
+
--hc-color-info-500: #355cc0;
|
|
49
|
+
--hc-color-info-600: #253f82;
|
|
50
|
+
--hc-color-info-700: #162c64;
|
|
51
|
+
--hc-color-andromeda-100: #f6f2ff;
|
|
52
|
+
--hc-color-andromeda-200: #d3c6fd;
|
|
53
|
+
--hc-color-andromeda-300: #ac93fc;
|
|
54
|
+
--hc-color-andromeda-400: #7c5ee2;
|
|
55
|
+
--hc-color-andromeda-500: #5a38c6;
|
|
56
|
+
--hc-color-andromeda-600: #4727b0;
|
|
57
|
+
--hc-color-andromeda-700: #260a81;
|
|
58
|
+
--hc-color-sirius-100: #fff6fe;
|
|
59
|
+
--hc-color-sirius-200: #ffb8f8;
|
|
60
|
+
--hc-color-sirius-300: #f899ee;
|
|
61
|
+
--hc-color-sirius-400: #e472d9;
|
|
62
|
+
--hc-color-sirius-500: #b832ab;
|
|
63
|
+
--hc-color-sirius-600: #7e2274;
|
|
64
|
+
--hc-color-sirius-700: #55154f;
|
|
65
|
+
--hc-color-neutral-0: #ffffff;
|
|
66
|
+
--hc-color-neutral-100: #f7f9fa;
|
|
67
|
+
--hc-color-neutral-200: #e6e9ed;
|
|
68
|
+
--hc-color-neutral-300: #c9ced4;
|
|
69
|
+
--hc-color-neutral-400: #9ea4ac;
|
|
70
|
+
--hc-color-neutral-500: #707780;
|
|
71
|
+
--hc-color-neutral-600: #464b52;
|
|
72
|
+
--hc-color-neutral-700: #32363b;
|
|
73
|
+
--hc-color-neutral-800: #282c2f;
|
|
74
|
+
--hc-color-neutral-900: #191c1f;
|
|
75
|
+
--hc-color-neutral-1000: #000000;
|
|
76
|
+
--ai-color-primary: #a095ec;
|
|
77
|
+
--ai-color-secondary: #6ba1f0;
|
|
78
|
+
--ai-color-dark: #111925;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
#hotmart-app-tutor-ai-consumer-root {
|
|
82
|
+
composes: scrollbar from './shared-styles.module.css';
|
|
83
|
+
font-family:
|
|
84
|
+
'Nunito Sans',
|
|
85
|
+
-apple-system,
|
|
86
|
+
BlinkMacSystemFont,
|
|
87
|
+
'Segoe UI',
|
|
88
|
+
Helvetica,
|
|
89
|
+
Arial,
|
|
90
|
+
sans-serif,
|
|
91
|
+
'Apple Color Emoji',
|
|
92
|
+
'Segoe UI Emoji',
|
|
93
|
+
'Segoe UI Symbol';
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
#hotmart-app-tutor-ai-consumer-root.dark {
|
|
97
|
+
--hc-color-primary-100: #162c64;
|
|
98
|
+
/* Hotmart Product */
|
|
99
|
+
--hc-color-primary-200: #253f82;
|
|
100
|
+
/* Hotmart Product */
|
|
101
|
+
--hc-color-primary-300: #355cc0;
|
|
102
|
+
/* Hotmart Product */
|
|
103
|
+
--hc-color-primary-400: #5981e3;
|
|
104
|
+
/* Hotmart Product */
|
|
105
|
+
--hc-color-primary-500: #89a8f8;
|
|
106
|
+
/* Hotmart Product */
|
|
107
|
+
--hc-color-primary-600: #bed1ff;
|
|
108
|
+
/* Hotmart Product */
|
|
109
|
+
--hc-color-primary-700: #ebf0ff;
|
|
110
|
+
/* Hotmart Product */
|
|
111
|
+
--hc-color-secondary-100: #024242;
|
|
112
|
+
--hc-color-secondary-200: #066262;
|
|
113
|
+
--hc-color-secondary-300: #0a9090;
|
|
114
|
+
--hc-color-secondary-400: #51c1c3;
|
|
115
|
+
--hc-color-secondary-500: #7cdbdb;
|
|
116
|
+
--hc-color-secondary-600: #a2eaea;
|
|
117
|
+
--hc-color-secondary-700: #edffff;
|
|
118
|
+
--hc-color-success-100: #003416;
|
|
119
|
+
--hc-color-success-200: #005122;
|
|
120
|
+
--hc-color-success-300: #006e2f;
|
|
121
|
+
--hc-color-success-400: #009d43;
|
|
122
|
+
--hc-color-success-500: #4acc82;
|
|
123
|
+
--hc-color-success-600: #99e9bb;
|
|
124
|
+
--hc-color-success-700: #edfff5;
|
|
125
|
+
--hc-color-warning-100: #453400;
|
|
126
|
+
--hc-color-warning-200: #6c5200;
|
|
127
|
+
--hc-color-warning-300: #a47c00;
|
|
128
|
+
--hc-color-warning-400: #efba0f;
|
|
129
|
+
--hc-color-warning-500: #fbd458;
|
|
130
|
+
--hc-color-warning-600: #f9e298;
|
|
131
|
+
--hc-color-warning-700: #fffaeb;
|
|
132
|
+
--hc-color-danger-100: #590900;
|
|
133
|
+
--hc-color-danger-200: #830d00;
|
|
134
|
+
--hc-color-danger-300: #a81a0a;
|
|
135
|
+
--hc-color-danger-400: #d6342c;
|
|
136
|
+
--hc-color-danger-500: #e37570;
|
|
137
|
+
--hc-color-danger-600: #f9cac8;
|
|
138
|
+
--hc-color-danger-700: #fff0f0;
|
|
139
|
+
--hc-color-info-100: #162c64;
|
|
140
|
+
--hc-color-info-200: #253f82;
|
|
141
|
+
--hc-color-info-300: #355cc0;
|
|
142
|
+
--hc-color-info-400: #5981e3;
|
|
143
|
+
--hc-color-info-500: #89a8f8;
|
|
144
|
+
--hc-color-info-600: #bed1ff;
|
|
145
|
+
--hc-color-info-700: #f0f4ff;
|
|
146
|
+
--hc-color-andromeda-100: #260a81;
|
|
147
|
+
--hc-color-andromeda-200: #4727b0;
|
|
148
|
+
--hc-color-andromeda-300: #5a38c6;
|
|
149
|
+
--hc-color-andromeda-400: #7c5ee2;
|
|
150
|
+
--hc-color-andromeda-500: #ac93fc;
|
|
151
|
+
--hc-color-andromeda-600: #d3c6fd;
|
|
152
|
+
--hc-color-andromeda-700: #f6f2ff;
|
|
153
|
+
--hc-color-sirius-100: #55154f;
|
|
154
|
+
--hc-color-sirius-200: #7e2274;
|
|
155
|
+
--hc-color-sirius-300: #b832ab;
|
|
156
|
+
--hc-color-sirius-400: #e472d9;
|
|
157
|
+
--hc-color-sirius-500: #f899ee;
|
|
158
|
+
--hc-color-sirius-600: #ffb8f8;
|
|
159
|
+
--hc-color-sirius-700: #fff6fe;
|
|
160
|
+
--hc-color-neutral-0: #000000;
|
|
161
|
+
--hc-color-neutral-100: #191c1f;
|
|
162
|
+
--hc-color-neutral-200: #282c2f;
|
|
163
|
+
--hc-color-neutral-300: #32363b;
|
|
164
|
+
--hc-color-neutral-400: #464b52;
|
|
165
|
+
--hc-color-neutral-500: #707780;
|
|
166
|
+
--hc-color-neutral-600: #9ea4ac;
|
|
167
|
+
--hc-color-neutral-700: #c9ced4;
|
|
168
|
+
--hc-color-neutral-800: #e6e9ed;
|
|
169
|
+
--hc-color-neutral-900: #f7f9fa;
|
|
170
|
+
--hc-color-neutral-1000: #ffffff;
|
|
171
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
.scrollbar {
|
|
2
|
+
&::-webkit-scrollbar {
|
|
3
|
+
width: var(--hc-size-spacing-2);
|
|
4
|
+
height: var(--hc-size-spacing-2);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
&::-webkit-scrollbar-track {
|
|
8
|
+
background: transparent;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
&::-webkit-scrollbar-thumb {
|
|
12
|
+
background: var(--hc-color-neutral-400);
|
|
13
|
+
border-radius: var(--hc-size-border-medium);
|
|
14
|
+
border: calc(var(--hc-size-border-medium) / 2) solid transparent;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react'
|
|
2
|
+
import userEvent from '@testing-library/user-event'
|
|
3
|
+
|
|
4
|
+
import type { CustomRenderHooksOptions } from '@/src/config/tests/types'
|
|
5
|
+
import { setupComponents } from '@/src/config/tests/wrappers'
|
|
6
|
+
|
|
7
|
+
const customRenderHook = <TProps, TResult>(
|
|
8
|
+
callback: (props: TProps) => TResult,
|
|
9
|
+
{
|
|
10
|
+
shallow,
|
|
11
|
+
withQueryProvider = true,
|
|
12
|
+
withProvider,
|
|
13
|
+
...options
|
|
14
|
+
}: CustomRenderHooksOptions<TProps> = {}
|
|
15
|
+
) => {
|
|
16
|
+
const result = renderHook(callback, {
|
|
17
|
+
wrapper: (props) =>
|
|
18
|
+
setupComponents(<div {...props} />, {
|
|
19
|
+
shallow,
|
|
20
|
+
withQueryProvider,
|
|
21
|
+
withProvider
|
|
22
|
+
}),
|
|
23
|
+
...options
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
...result,
|
|
28
|
+
user: userEvent.setup()
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default customRenderHook
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { delay, http, HttpResponse } from 'msw'
|
|
2
|
+
|
|
3
|
+
import { serviceWorker } from '@/src/config/tests/worker'
|
|
4
|
+
import { HttpCodes } from '@/src/lib/utils'
|
|
5
|
+
|
|
6
|
+
type Method = 'get' | 'post' | 'put' | 'delete'
|
|
7
|
+
|
|
8
|
+
type Options = {
|
|
9
|
+
status?: number
|
|
10
|
+
delay?: number
|
|
11
|
+
httpMethod?: Method
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
class MockRequest {
|
|
15
|
+
rest<ResponseType extends BodyInit | null = never>(
|
|
16
|
+
url: string,
|
|
17
|
+
response: ResponseType,
|
|
18
|
+
options?: Options
|
|
19
|
+
) {
|
|
20
|
+
const restAPI = http[options?.httpMethod ?? 'get']
|
|
21
|
+
|
|
22
|
+
return restAPI(url, async () => {
|
|
23
|
+
await delay(options?.delay)
|
|
24
|
+
|
|
25
|
+
return new HttpResponse(response, {
|
|
26
|
+
status: options?.status ?? HttpCodes.OK
|
|
27
|
+
})
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
mock<ResponseType = object>(url: string, response: ResponseType, options?: Options) {
|
|
32
|
+
serviceWorker.use(this.rest(url, JSON.stringify(response), options))
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default new MockRequest()
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { RenderHookOptions, RenderOptions } from '@testing-library/react'
|
|
2
|
+
import type { ComponentType, ReactNode } from 'react'
|
|
3
|
+
|
|
4
|
+
export type IExtendedRenderOptions = RenderOptions & {
|
|
5
|
+
shallow?: boolean
|
|
6
|
+
withQueryProvider?: boolean
|
|
7
|
+
withProvider?: ComponentType<{ children: ReactNode }>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type CustomRenderHooksOptions<TProps> = RenderHookOptions<TProps> & IExtendedRenderOptions
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { render } from '@testing-library/react'
|
|
2
|
+
import userEvent from '@testing-library/user-event'
|
|
3
|
+
import Chance from 'chance'
|
|
4
|
+
|
|
5
|
+
import customRenderHook from '@/src/config/tests/customRenderHook'
|
|
6
|
+
import type { IExtendedRenderOptions } from '@/src/config/tests/types'
|
|
7
|
+
import { setupComponents } from '@/src/config/tests/wrappers'
|
|
8
|
+
|
|
9
|
+
import MockGenerator from './abstract-mock-generator'
|
|
10
|
+
import MockRequest from './mockRequest'
|
|
11
|
+
|
|
12
|
+
const customRender = (
|
|
13
|
+
ui: React.ReactElement,
|
|
14
|
+
{ shallow, withQueryProvider = true, ...options }: IExtendedRenderOptions = {}
|
|
15
|
+
) => ({
|
|
16
|
+
user: userEvent.setup(),
|
|
17
|
+
...render(
|
|
18
|
+
setupComponents(ui, {
|
|
19
|
+
shallow,
|
|
20
|
+
withQueryProvider
|
|
21
|
+
}),
|
|
22
|
+
options
|
|
23
|
+
)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
const chance = new Chance()
|
|
27
|
+
|
|
28
|
+
export * from '@testing-library/react'
|
|
29
|
+
export { default as userEvent } from '@testing-library/user-event'
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
chance,
|
|
33
|
+
MockGenerator,
|
|
34
|
+
MockRequest,
|
|
35
|
+
// override render export
|
|
36
|
+
customRender as render,
|
|
37
|
+
customRenderHook as renderHook
|
|
38
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
2
|
+
import type { ComponentType, JSX, ReactNode } from 'react'
|
|
3
|
+
|
|
4
|
+
import type { IExtendedRenderOptions } from '@/src/config/tests/types'
|
|
5
|
+
|
|
6
|
+
export const testQueryClient = new QueryClient({
|
|
7
|
+
defaultOptions: {
|
|
8
|
+
queries: {
|
|
9
|
+
retry: false,
|
|
10
|
+
refetchOnWindowFocus: false
|
|
11
|
+
},
|
|
12
|
+
mutations: {
|
|
13
|
+
retry: false
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const wrapInQueryProvider = (componentTree: JSX.Element) => (
|
|
19
|
+
<QueryClientProvider client={testQueryClient}>{componentTree}</QueryClientProvider>
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
const wrapInProvider = ({
|
|
23
|
+
componentTree,
|
|
24
|
+
provider: Provider
|
|
25
|
+
}: {
|
|
26
|
+
provider: ComponentType<{ children: ReactNode }>
|
|
27
|
+
componentTree: JSX.Element
|
|
28
|
+
}) => <Provider>{componentTree}</Provider>
|
|
29
|
+
|
|
30
|
+
export const setupComponents = (
|
|
31
|
+
ui: JSX.Element,
|
|
32
|
+
renderOptions?: IExtendedRenderOptions
|
|
33
|
+
): JSX.Element => {
|
|
34
|
+
let componentTree: JSX.Element = <>{ui}</>
|
|
35
|
+
|
|
36
|
+
if (!renderOptions || renderOptions?.shallow) return componentTree
|
|
37
|
+
|
|
38
|
+
if (renderOptions?.withQueryProvider) {
|
|
39
|
+
componentTree = wrapInQueryProvider(componentTree)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (renderOptions.withProvider) {
|
|
43
|
+
componentTree = wrapInProvider({
|
|
44
|
+
provider: renderOptions.withProvider,
|
|
45
|
+
componentTree
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return componentTree
|
|
50
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import './index'
|
|
2
|
+
|
|
3
|
+
import { LANGUAGES } from './config/i18n'
|
|
4
|
+
import { devMode } from './lib/utils'
|
|
5
|
+
|
|
6
|
+
if (devMode) {
|
|
7
|
+
window.TOKEN = process.env.TOKEN ?? ''
|
|
8
|
+
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
|
+
}
|
|
24
|
+
})
|
|
25
|
+
})()
|
|
26
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { StrictMode } from 'react'
|
|
2
|
+
import { createRoot } from 'react-dom/client'
|
|
3
|
+
|
|
4
|
+
import { initLanguage } from './config/i18n'
|
|
5
|
+
import { Main } from './main'
|
|
6
|
+
import { TutorWidgetEvents, TutorWidgetEventTypes } from './modules/widget'
|
|
7
|
+
import type { StartTutorWidgetProps } from './types'
|
|
8
|
+
|
|
9
|
+
window.startTutorWidget = async ({
|
|
10
|
+
elementId = 'tutor-chat-app-widget',
|
|
11
|
+
settings
|
|
12
|
+
}: StartTutorWidgetProps) => {
|
|
13
|
+
const rootElement = document.getElementById(elementId) as HTMLElement
|
|
14
|
+
const root = createRoot(rootElement)
|
|
15
|
+
|
|
16
|
+
await initLanguage(settings.locale)
|
|
17
|
+
|
|
18
|
+
if (root)
|
|
19
|
+
root.render(
|
|
20
|
+
<StrictMode>
|
|
21
|
+
<Main settings={settings} />
|
|
22
|
+
</StrictMode>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
window.closeTutorWidget = () => TutorWidgetEvents.get(TutorWidgetEventTypes.CLOSE)?.dispatch()
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
import type { FunctionComponent, SVGProps } from 'react'
|
|
3
|
+
|
|
4
|
+
import type { ValidIconNames } from '@/src/lib/components/icons/icon-names'
|
|
5
|
+
import { Spinner } from '../spinner'
|
|
6
|
+
|
|
7
|
+
export type IconProps = SVGProps<SVGSVGElement> & {
|
|
8
|
+
name: ValidIconNames
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function Icon({ name, ...rest }: IconProps) {
|
|
12
|
+
const [IconComponent, setIconComponent] = useState<FunctionComponent<
|
|
13
|
+
SVGProps<SVGSVGElement>
|
|
14
|
+
> | null>(null)
|
|
15
|
+
const [loading, setLoading] = useState(false)
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
const loadIcon = async () => {
|
|
19
|
+
setLoading(true)
|
|
20
|
+
try {
|
|
21
|
+
// Dynamic import with explicit path
|
|
22
|
+
const imported = (await import(`@/src/lib/components/icons/${name}.svg`)) as {
|
|
23
|
+
default: FunctionComponent<SVGProps<SVGSVGElement>>
|
|
24
|
+
}
|
|
25
|
+
setIconComponent(() => imported.default)
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error(`Error loading icon ${name}:`, error)
|
|
28
|
+
} finally {
|
|
29
|
+
setLoading(false)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
void loadIcon()
|
|
34
|
+
}, [name])
|
|
35
|
+
|
|
36
|
+
if (loading) return <Spinner className={rest.className} />
|
|
37
|
+
|
|
38
|
+
return IconComponent ? <IconComponent {...rest} /> : null
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default Icon
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Icon } from './icon'
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M0.459613 5.98654C0.0677077 5.85591 0.0646103 5.64514 0.467675 5.51078L14.7823 0.739229C15.1787 0.607109 15.406 0.828966 15.2949 1.21768L11.205 15.5324C11.0918 15.9287 10.8633 15.9425 10.6959 15.5657L7.99999 9.49997L12.5 3.50001L6.5 7.99997L0.459613 5.98654Z" fill="currentColor"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './icons'
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { SpinnerProps } from './types'
|
|
2
|
+
|
|
3
|
+
import styles from './styles.module.css'
|
|
4
|
+
|
|
5
|
+
function Spinner({ className }: SpinnerProps) {
|
|
6
|
+
return (
|
|
7
|
+
<svg
|
|
8
|
+
className={className}
|
|
9
|
+
stroke='currentColor'
|
|
10
|
+
viewBox='0 0 24 24'
|
|
11
|
+
xmlns='http://www.w3.org/2000/svg'>
|
|
12
|
+
<g className={styles.spinner}>
|
|
13
|
+
<circle
|
|
14
|
+
className={styles.circle}
|
|
15
|
+
cx='12'
|
|
16
|
+
cy='12'
|
|
17
|
+
r='9.5'
|
|
18
|
+
fill='none'
|
|
19
|
+
strokeWidth='3'></circle>
|
|
20
|
+
</g>
|
|
21
|
+
</svg>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default Spinner
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
.spinner {
|
|
2
|
+
transform-origin: center;
|
|
3
|
+
animation: spinner_zKoa 2s linear infinite;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.circle {
|
|
7
|
+
stroke-linecap: round;
|
|
8
|
+
animation: spinner_YpZS 1.5s ease-in-out infinite;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
@keyframes spinner_zKoa {
|
|
12
|
+
100% {
|
|
13
|
+
transform: rotate(360deg);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@keyframes spinner_YpZS {
|
|
18
|
+
0% {
|
|
19
|
+
stroke-dasharray: 0 150;
|
|
20
|
+
stroke-dashoffset: 0;
|
|
21
|
+
}
|
|
22
|
+
47.5% {
|
|
23
|
+
stroke-dasharray: 42 150;
|
|
24
|
+
stroke-dashoffset: -16;
|
|
25
|
+
}
|
|
26
|
+
95%,
|
|
27
|
+
100% {
|
|
28
|
+
stroke-dasharray: 42 150;
|
|
29
|
+
stroke-dashoffset: -59;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type SpinnerProps = { className?: string }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './use-default-id'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as useDefaultId } from './use-default-id'
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useLayoutEffect } from 'react'
|
|
2
|
+
|
|
3
|
+
const useDefaultId = () => {
|
|
4
|
+
useLayoutEffect(() => {
|
|
5
|
+
document.body.setAttribute('id', 'hotmart-app-tutor-ai-consumer-root')
|
|
6
|
+
|
|
7
|
+
return () => {
|
|
8
|
+
document.body.removeAttribute('id')
|
|
9
|
+
}
|
|
10
|
+
})
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default useDefaultId
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Main } from './main'
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { render, screen, waitFor } from '@/config/tests'
|
|
2
|
+
import type { WidgetSettingProps } from '../types'
|
|
3
|
+
import { Main } from '.'
|
|
4
|
+
|
|
5
|
+
describe('Main', () => {
|
|
6
|
+
const renderComponent = () => render(<Main settings={{} as WidgetSettingProps} />)
|
|
7
|
+
|
|
8
|
+
it('should render without errors', async () => {
|
|
9
|
+
renderComponent()
|
|
10
|
+
|
|
11
|
+
await waitFor(() =>
|
|
12
|
+
expect(screen.getByText(/general.greetings.description/i)).toBeInTheDocument()
|
|
13
|
+
)
|
|
14
|
+
})
|
|
15
|
+
})
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import '@/config/styles/index.css'
|
|
2
|
+
|
|
3
|
+
import clsx from 'clsx'
|
|
4
|
+
|
|
5
|
+
import { useDefaultId } from '@/src/lib/hooks'
|
|
6
|
+
import { useAppLang } from '../config/i18n'
|
|
7
|
+
import { ChatInput } from '../modules/create-message/components'
|
|
8
|
+
import { GreetingsCard } from '../modules/widget/components'
|
|
9
|
+
import type { WidgetSettingProps } from '../types'
|
|
10
|
+
|
|
11
|
+
import styles from './styles.module.css'
|
|
12
|
+
|
|
13
|
+
function Main({ settings }: { settings: WidgetSettingProps }) {
|
|
14
|
+
useDefaultId()
|
|
15
|
+
useAppLang(settings.locale)
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<div className={clsx('flex min-h-svh flex-col items-center justify-center p-5', styles.main)}>
|
|
19
|
+
<div className='flex flex-1 flex-col justify-center gap-6 lg:max-w-sm'>
|
|
20
|
+
<GreetingsCard author={settings.author ?? ''} tutorName={settings.tutorName ?? ''} />
|
|
21
|
+
<ChatInput name='new-chat-msg-input' />
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default Main
|