@memori.ai/memori-react 5.1.0 → 6.0.0-rc.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 (93) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +1 -0
  3. package/dist/components/AttachmentMediaModal/AttachmentMediaModal.d.ts +14 -0
  4. package/dist/components/AttachmentMediaModal/AttachmentMediaModal.js +66 -0
  5. package/dist/components/AttachmentMediaModal/AttachmentMediaModal.js.map +1 -0
  6. package/dist/components/Header/Header.css +2 -1
  7. package/dist/components/Header/Header.d.ts +2 -0
  8. package/dist/components/Header/Header.js +3 -2
  9. package/dist/components/Header/Header.js.map +1 -1
  10. package/dist/components/ImageUpload/ImageUpload.css +168 -0
  11. package/dist/components/ImageUpload/ImageUpload.d.ts +28 -0
  12. package/dist/components/ImageUpload/ImageUpload.js +163 -0
  13. package/dist/components/ImageUpload/ImageUpload.js.map +1 -0
  14. package/dist/components/LoginDrawer/LoginDrawer.css +84 -0
  15. package/dist/components/LoginDrawer/LoginDrawer.d.ts +15 -0
  16. package/dist/components/LoginDrawer/LoginDrawer.js +129 -0
  17. package/dist/components/LoginDrawer/LoginDrawer.js.map +1 -0
  18. package/dist/components/MemoriWidget/MemoriWidget.d.ts +2 -1
  19. package/dist/components/MemoriWidget/MemoriWidget.js +33 -5
  20. package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
  21. package/dist/components/StartPanel/StartPanel.d.ts +2 -0
  22. package/dist/components/StartPanel/StartPanel.js +2 -2
  23. package/dist/components/StartPanel/StartPanel.js.map +1 -1
  24. package/dist/components/layouts/Default.d.ts +17 -0
  25. package/dist/components/layouts/Default.js +8 -0
  26. package/dist/components/layouts/Default.js.map +1 -0
  27. package/dist/components/ui/Message.d.ts +17 -0
  28. package/dist/components/ui/Message.js +13 -0
  29. package/dist/components/ui/Message.js.map +1 -0
  30. package/dist/index.d.ts +1 -0
  31. package/dist/index.js +3 -2
  32. package/dist/index.js.map +1 -1
  33. package/dist/locales/en.json +25 -0
  34. package/dist/locales/it.json +25 -0
  35. package/dist/styles.css +1 -0
  36. package/esm/components/AttachmentMediaModal/AttachmentMediaModal.d.ts +14 -0
  37. package/esm/components/AttachmentMediaModal/AttachmentMediaModal.js +63 -0
  38. package/esm/components/AttachmentMediaModal/AttachmentMediaModal.js.map +1 -0
  39. package/esm/components/Header/Header.css +2 -1
  40. package/esm/components/Header/Header.d.ts +2 -0
  41. package/esm/components/Header/Header.js +3 -2
  42. package/esm/components/Header/Header.js.map +1 -1
  43. package/esm/components/ImageUpload/ImageUpload.css +168 -0
  44. package/esm/components/ImageUpload/ImageUpload.d.ts +28 -0
  45. package/esm/components/ImageUpload/ImageUpload.js +160 -0
  46. package/esm/components/ImageUpload/ImageUpload.js.map +1 -0
  47. package/esm/components/LoginDrawer/LoginDrawer.css +84 -0
  48. package/esm/components/LoginDrawer/LoginDrawer.d.ts +15 -0
  49. package/esm/components/LoginDrawer/LoginDrawer.js +125 -0
  50. package/esm/components/LoginDrawer/LoginDrawer.js.map +1 -0
  51. package/esm/components/MemoriWidget/MemoriWidget.d.ts +2 -1
  52. package/esm/components/MemoriWidget/MemoriWidget.js +33 -5
  53. package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
  54. package/esm/components/StartPanel/StartPanel.d.ts +2 -0
  55. package/esm/components/StartPanel/StartPanel.js +2 -2
  56. package/esm/components/StartPanel/StartPanel.js.map +1 -1
  57. package/esm/components/layouts/Default.d.ts +17 -0
  58. package/esm/components/layouts/Default.js +5 -0
  59. package/esm/components/layouts/Default.js.map +1 -0
  60. package/esm/components/ui/Message.d.ts +17 -0
  61. package/esm/components/ui/Message.js +10 -0
  62. package/esm/components/ui/Message.js.map +1 -0
  63. package/esm/index.d.ts +1 -0
  64. package/esm/index.js +3 -2
  65. package/esm/index.js.map +1 -1
  66. package/esm/locales/en.json +25 -0
  67. package/esm/locales/it.json +25 -0
  68. package/esm/styles.css +1 -0
  69. package/package.json +1 -1
  70. package/src/components/Header/Header.css +2 -1
  71. package/src/components/Header/Header.stories.tsx +27 -1
  72. package/src/components/Header/Header.test.tsx +14 -1
  73. package/src/components/Header/Header.tsx +17 -0
  74. package/src/components/Header/__snapshots__/Header.test.tsx.snap +260 -0
  75. package/src/components/LoginDrawer/LoginDrawer.css +84 -0
  76. package/src/components/LoginDrawer/LoginDrawer.stories.tsx +58 -0
  77. package/src/components/LoginDrawer/LoginDrawer.test.tsx +86 -0
  78. package/src/components/LoginDrawer/LoginDrawer.tsx +330 -0
  79. package/src/components/LoginDrawer/__snapshots__/LoginDrawer.test.tsx.snap +35 -0
  80. package/src/components/MemoriWidget/MemoriWidget.tsx +48 -4
  81. package/src/components/StartPanel/StartPanel.stories.tsx +3 -1
  82. package/src/components/StartPanel/StartPanel.test.tsx +12 -0
  83. package/src/components/StartPanel/StartPanel.tsx +11 -0
  84. package/src/components/StartPanel/__snapshots__/StartPanel.test.tsx.snap +7 -0
  85. package/src/components/layouts/__snapshots__/Chat.test.tsx.snap +20 -0
  86. package/src/components/layouts/__snapshots__/FullPage.test.tsx.snap +20 -0
  87. package/src/components/layouts/__snapshots__/Totem.test.tsx.snap +20 -0
  88. package/src/index.stories.tsx +0 -1
  89. package/src/index.tsx +4 -0
  90. package/src/locales/en.json +25 -0
  91. package/src/locales/it.json +25 -0
  92. package/src/mocks/data.ts +2 -2
  93. package/src/styles.css +1 -0
@@ -0,0 +1,84 @@
1
+ .memori--login-drawer {
2
+ z-index: 1000;
3
+ }
4
+
5
+ .memori--login-drawer .memori-drawer--panel {
6
+ --memori-drawer--width: 100%;
7
+ --memori-drawer--width--md: 60%;
8
+ --memori-drawer--width--lg: 40%;
9
+ }
10
+
11
+ .memori--login-drawer.memori--login-drawer--logged .memori-drawer--panel {
12
+ --memori-drawer--width: 100%;
13
+ --memori-drawer--width--md: 40%;
14
+ --memori-drawer--width--lg: 30%;
15
+ }
16
+
17
+ h2.memori--login-drawer--title {
18
+ margin: 0;
19
+ font-size: inherit;
20
+ font-weight: 400;
21
+ line-height: inherit;
22
+ }
23
+
24
+ form.memori--login-drawer--form {
25
+ display: flex;
26
+ flex-direction: column;
27
+ padding: 1rem 0.5rem;
28
+ }
29
+
30
+ p.memori--login-drawer--error {
31
+ margin: 0.5rem 0;
32
+ color: var(--memori-error-color);
33
+ text-align: center;
34
+ }
35
+
36
+ .memori--login-drawer--form label:not(.memori-checkbox) {
37
+ display: block;
38
+ width: 100%;
39
+ margin: 0.5rem 0;
40
+ }
41
+
42
+ .memori--login-drawer--form label.memori-checkbox {
43
+ width: 100%;
44
+ margin: 0.5rem 0;
45
+ }
46
+
47
+ .memori--login-drawer--form input {
48
+ width: 100%;
49
+ padding: 10px;
50
+ border: 1px solid var(--memori-button-border-color, #ccc);
51
+ border-radius: 5px;
52
+ margin: 0.5rem 0;
53
+ font-size: 16px;
54
+ line-height: 1.5;
55
+ transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
56
+ }
57
+
58
+ .memori--login-drawer--form button:focus,
59
+ .memori--login-drawer--form input:focus,
60
+ .memori--login-drawer--form input[type="checkbox"]:focus+.memori-checkbox--inner {
61
+ border-color: var(--memori-primary);
62
+ box-shadow: 0 2px 0 rgba(0, 0, 0, 0.2);
63
+ outline: none;
64
+ }
65
+
66
+ .memori--login-drawer--form a {
67
+ color: var(--memori-primary);
68
+ text-decoration: underline;
69
+ }
70
+
71
+ .memori--login-drawer--form a:hover,
72
+ .memori--login-drawer--form a:focus,
73
+ .memori--login-drawer--form a:active {
74
+ opacity: 0.8;
75
+ outline-color: var(--memori-primary);
76
+ }
77
+
78
+ .memori--login-drawer--form button[type="submit"] {
79
+ margin-top: 1rem;
80
+ }
81
+
82
+ p.memori--login-drawer--signup {
83
+ margin-top: 1.25rem;
84
+ }
@@ -0,0 +1,58 @@
1
+ import React from 'react';
2
+ import { Meta, Story } from '@storybook/react';
3
+ import I18nWrapper from '../../I18nWrapper';
4
+ import LoginDrawer, { Props } from './LoginDrawer';
5
+ import { tenant, user } from '../../mocks/data';
6
+ import './LoginDrawer.css';
7
+
8
+ const meta: Meta = {
9
+ title: 'Widget/LoginDrawer',
10
+ component: LoginDrawer,
11
+ argTypes: {
12
+ open: {
13
+ control: {
14
+ type: 'boolean',
15
+ },
16
+ },
17
+ },
18
+ parameters: {
19
+ controls: { expanded: true },
20
+ },
21
+ };
22
+
23
+ export default meta;
24
+
25
+ const Template: Story<Props> = args => {
26
+ return (
27
+ <I18nWrapper>
28
+ <LoginDrawer
29
+ {...args}
30
+ onClose={() => {}}
31
+ onLogin={console.log}
32
+ onLogout={() => {}}
33
+ tenant={tenant}
34
+ apiUrl="https://backend.memori.ai"
35
+ />
36
+ </I18nWrapper>
37
+ );
38
+ };
39
+
40
+ // By passing using the Args format for exported stories, you can control the props for a component for reuse in a test
41
+ // https://storybook.js.org/docs/react/workflows/unit-testing
42
+ export const Default = Template.bind({});
43
+ Default.args = {
44
+ open: true,
45
+ };
46
+
47
+ export const NeedsMissingData = Template.bind({});
48
+ NeedsMissingData.args = {
49
+ open: true,
50
+ __TEST__needMissingData: true,
51
+ };
52
+
53
+ export const LoggedIn = Template.bind({});
54
+ LoggedIn.args = {
55
+ open: true,
56
+ user,
57
+ loginToken: 'token',
58
+ };
@@ -0,0 +1,86 @@
1
+ import { render } from '@testing-library/react';
2
+ import LoginDrawer from './LoginDrawer';
3
+ import React from 'react';
4
+ import { tenant, expertReference, user } from '../../mocks/data';
5
+
6
+ beforeEach(() => {
7
+ // @ts-ignore
8
+ window.IntersectionObserver = jest.fn(() => ({
9
+ observe: jest.fn(),
10
+ unobserve: jest.fn(),
11
+ disconnect: jest.fn(),
12
+ takeRecords: jest.fn(),
13
+ }));
14
+ });
15
+
16
+ it('renders LoginDrawer closed unchanged', () => {
17
+ const { container } = render(
18
+ <LoginDrawer
19
+ onClose={jest.fn()}
20
+ apiUrl="https://backend.memori.ai"
21
+ tenant={tenant}
22
+ onLogin={jest.fn()}
23
+ onLogout={jest.fn()}
24
+ />
25
+ );
26
+ expect(container).toMatchSnapshot();
27
+ });
28
+
29
+ it('renders LoginDrawer open unchanged', () => {
30
+ const { container } = render(
31
+ <LoginDrawer
32
+ onClose={jest.fn()}
33
+ apiUrl="https://backend.memori.ai"
34
+ tenant={tenant}
35
+ onLogin={jest.fn()}
36
+ onLogout={jest.fn()}
37
+ open
38
+ />
39
+ );
40
+ expect(container).toMatchSnapshot();
41
+ });
42
+
43
+ it('renders LoginDrawer unlogged unchanged', () => {
44
+ const { container } = render(
45
+ <LoginDrawer
46
+ onClose={jest.fn()}
47
+ apiUrl="https://backend.memori.ai"
48
+ tenant={tenant}
49
+ onLogin={jest.fn()}
50
+ onLogout={jest.fn()}
51
+ open
52
+ />
53
+ );
54
+ expect(container).toMatchSnapshot();
55
+ });
56
+
57
+ it('renders LoginDrawer logged with missing data unchanged', () => {
58
+ const { container } = render(
59
+ <LoginDrawer
60
+ onClose={jest.fn()}
61
+ apiUrl="https://backend.memori.ai"
62
+ tenant={tenant}
63
+ onLogin={jest.fn()}
64
+ onLogout={jest.fn()}
65
+ open
66
+ __TEST__needMissingData
67
+ />
68
+ );
69
+ expect(container).toMatchSnapshot();
70
+ });
71
+
72
+ it('renders LoginDrawer logged in unchanged', () => {
73
+ const { container } = render(
74
+ <LoginDrawer
75
+ onClose={jest.fn()}
76
+ apiUrl="https://backend.memori.ai"
77
+ tenant={tenant}
78
+ onLogin={jest.fn()}
79
+ onLogout={jest.fn()}
80
+ open
81
+ user={user}
82
+ loginToken="token"
83
+ />
84
+ );
85
+ expect(container).toMatchSnapshot();
86
+ });
@@ -0,0 +1,330 @@
1
+ import { User, Tenant } from '@memori.ai/memori-api-client/dist/types';
2
+ import React, { useEffect, useState } from 'react';
3
+ import Button from '../ui/Button';
4
+ import Drawer from '../ui/Drawer';
5
+ import toast from 'react-hot-toast';
6
+ import { useTranslation } from 'react-i18next';
7
+ import cx from 'classnames';
8
+ import memoriApiClient from '@memori.ai/memori-api-client';
9
+ import { getErrori18nKey } from '../../helpers/error';
10
+
11
+ export const mailRegEx = /^\w+([.-]?[+]?\w+)*@\w+([.-]?\w+)*(\.\w{2,})+$/;
12
+
13
+ export interface Props {
14
+ open?: boolean;
15
+ onClose: () => void;
16
+ user?: User;
17
+ loginToken?: string;
18
+ onLogin: (user: User, token: string) => void;
19
+ onLogout: () => void;
20
+ tenant: Tenant;
21
+ apiUrl: string;
22
+ __TEST__needMissingData?: boolean;
23
+ }
24
+
25
+ const LoginDrawer = ({
26
+ open = false,
27
+ onClose,
28
+ onLogin,
29
+ onLogout,
30
+ user,
31
+ loginToken,
32
+ tenant,
33
+ apiUrl,
34
+ __TEST__needMissingData = false,
35
+ }: Props) => {
36
+ const { t, i18n } = useTranslation();
37
+ const lang = i18n.language === 'it' ? 'it' : 'en';
38
+
39
+ const client = memoriApiClient(apiUrl);
40
+ const { userLogin, updateUser } = client.backend;
41
+
42
+ const [loading, setLoading] = useState(false);
43
+ const [needsMissingData, setNeedsMissingData] = useState<{
44
+ token: string;
45
+ birthDate?: boolean;
46
+ tnCAndPPAccepted?: boolean;
47
+ }>(
48
+ __TEST__needMissingData
49
+ ? {
50
+ token: 'token',
51
+ birthDate: true,
52
+ tnCAndPPAccepted: true,
53
+ }
54
+ : ({} as any)
55
+ );
56
+ const [error, setError] = useState<string | null>(null);
57
+
58
+ const [redirectTo, setRedirectTo] = useState<string | null>(null);
59
+ useEffect(() => {
60
+ setRedirectTo(window.location.href);
61
+ }, []);
62
+
63
+ const isUserLoggedIn = user && loginToken;
64
+
65
+ const login = (e: React.FormEvent<HTMLFormElement>) => {
66
+ e.preventDefault();
67
+ const form = e.currentTarget as HTMLFormElement;
68
+ const userNameOrEmail = form.userNameOrEmail.value;
69
+ const password = form.password.value;
70
+
71
+ const isEmail = mailRegEx.test(userNameOrEmail);
72
+ const user: User = isEmail
73
+ ? {
74
+ tenant: tenant?.id,
75
+ eMail: userNameOrEmail,
76
+ password: password,
77
+ }
78
+ : {
79
+ tenant: tenant?.id,
80
+ userName: userNameOrEmail,
81
+ password: password,
82
+ };
83
+ setLoading(true);
84
+ setError(null);
85
+ userLogin(user)
86
+ .then(data => {
87
+ if (data.resultCode !== 0) {
88
+ console.error(data);
89
+ toast.error(t(getErrori18nKey(data.resultCode), { ns: 'common' }));
90
+ setError(data.resultMessage);
91
+ } else if (data.user && data.token) {
92
+ onLogin(data.user as User, data.token);
93
+
94
+ if (!data.user?.tnCAndPPAccepted || !data.user?.birthDate) {
95
+ setNeedsMissingData({
96
+ token: data.token,
97
+ birthDate: !data.user?.birthDate,
98
+ tnCAndPPAccepted: !data.user?.tnCAndPPAccepted,
99
+ });
100
+ }
101
+ }
102
+ })
103
+ .catch(err => {
104
+ console.error('[LOGIN]', err);
105
+ toast.error(err);
106
+
107
+ if (err.message) setError(err.message);
108
+ })
109
+ .finally(() => {
110
+ setLoading(false);
111
+ });
112
+ };
113
+
114
+ const updateMissingData = async (e: React.FormEvent<HTMLFormElement>) => {
115
+ e.preventDefault();
116
+ const form = e.currentTarget as HTMLFormElement;
117
+
118
+ const birthDate = form.birthDate?.value;
119
+ const tnCAndPPAccepted = form.tnCAndPPAccepted?.checked;
120
+ const pAndCUAccepted = form.pAndCUAccepted?.checked;
121
+
122
+ if (!user?.userID || !needsMissingData?.token) {
123
+ return;
124
+ }
125
+
126
+ if (!birthDate || !tnCAndPPAccepted) {
127
+ setError(t('missingData'));
128
+ return;
129
+ }
130
+
131
+ let newUser: Partial<User> = {
132
+ userID: user.userID,
133
+ birthDate:
134
+ user?.birthDate || !needsMissingData.birthDate ? undefined : birthDate,
135
+ tnCAndPPAccepted: tnCAndPPAccepted || user?.tnCAndPPAccepted,
136
+ tnCAndPPAcceptanceDate: tnCAndPPAccepted
137
+ ? new Date().toISOString()
138
+ : undefined,
139
+ pAndCUAccepted: pAndCUAccepted || user?.pAndCUAccepted,
140
+ pAndCUAcceptanceDate: pAndCUAccepted
141
+ ? new Date().toISOString()
142
+ : undefined,
143
+ };
144
+
145
+ const { user: patchedUser, ...resp } = await updateUser(
146
+ needsMissingData.token,
147
+ user.userID,
148
+ newUser
149
+ );
150
+ if (resp.resultCode !== 0) {
151
+ console.error(resp);
152
+ toast.error(t(getErrori18nKey(resp.resultCode), { ns: 'common' }));
153
+ setError(resp.resultMessage);
154
+ } else {
155
+ toast.success(t('success'));
156
+ onLogin(patchedUser || newUser, needsMissingData.token);
157
+ }
158
+ };
159
+
160
+ return (
161
+ <Drawer
162
+ open={open}
163
+ onClose={onClose}
164
+ className={cx('memori--login-drawer', {
165
+ 'memori--login-drawer--logged': isUserLoggedIn,
166
+ })}
167
+ title={
168
+ <h2 className="memori--login-drawer--title">
169
+ {isUserLoggedIn
170
+ ? t('login.loggedDrawerTitle', { name: user.userName })
171
+ : t('login.loginDrawerTitle')}
172
+ </h2>
173
+ }
174
+ >
175
+ {isUserLoggedIn ? (
176
+ <div className="memori--login-drawer--logged">
177
+ <Button
178
+ primary
179
+ onClick={() => {
180
+ onLogout();
181
+ }}
182
+ >
183
+ {t('login.logout')}
184
+ </Button>
185
+ </div>
186
+ ) : needsMissingData?.token?.length ? (
187
+ <>
188
+ <h3>{t('login.missingData')}</h3>
189
+ <p>{t('login.missingDataHelper')}</p>
190
+
191
+ <form
192
+ className="memori--login-drawer--form"
193
+ onSubmit={updateMissingData}
194
+ >
195
+ {needsMissingData.birthDate && (
196
+ <>
197
+ <label htmlFor="#birthDate">
198
+ {t('login.birthDate')}
199
+ <input
200
+ id="birthDate"
201
+ name="birthDate"
202
+ type="date"
203
+ required
204
+ autoComplete="bday"
205
+ />
206
+ </label>
207
+ <p>
208
+ <small>{t('login.birthDateHelper')}</small>
209
+ </p>
210
+ </>
211
+ )}
212
+
213
+ {needsMissingData?.tnCAndPPAccepted && (
214
+ <>
215
+ <label className="memori-checkbox">
216
+ <span className="memori-checkbox--input-wrapper">
217
+ <input
218
+ type="checkbox"
219
+ name="tnCAndPPAccepted"
220
+ className="memori-checkbox--input"
221
+ />
222
+ <span className="memori-checkbox--inner" />
223
+ </span>
224
+ <span className="memori-checkbox--text">
225
+ {t('login.privacyLabel')}{' '}
226
+ <a
227
+ href={`https://memori.ai/${lang}/privacy_and_cookie`}
228
+ target="_blank"
229
+ rel="noopener noreferrer"
230
+ >
231
+ {t('login.privacyAndCookiePolicy')}
232
+ </a>{' '}
233
+ {t('login.and')}{' '}
234
+ <a
235
+ href={`https://memori.ai/${lang}/tos`}
236
+ target="_blank"
237
+ rel="noopener noreferrer"
238
+ >
239
+ {t('login.termsOfService')}
240
+ </a>
241
+ </span>
242
+ </label>
243
+
244
+ <label className="memori-checkbox">
245
+ <span className="memori-checkbox--input-wrapper">
246
+ <input
247
+ type="checkbox"
248
+ name="pAndCUAccepted"
249
+ defaultChecked={user?.pAndCUAccepted}
250
+ className="memori-checkbox--input"
251
+ />
252
+ <span className="memori-checkbox--inner" />
253
+ </span>
254
+ <span className="memori-checkbox--text">
255
+ {t('login.pAndCUAccepted')}{' '}
256
+ <small>
257
+ <em>({t('login.optional')})</em>
258
+ </small>
259
+ </span>
260
+ </label>
261
+ <p>
262
+ <small>{t('login.goToAccountToChangeYourPreferences')}</small>
263
+ </p>
264
+ <p>
265
+ <small>{t('login.deepThoughtExplaination')}</small>
266
+ </p>
267
+ </>
268
+ )}
269
+
270
+ <Button htmlType="submit" primary loading={loading}>
271
+ {t('login.save')}
272
+ </Button>
273
+ </form>
274
+ </>
275
+ ) : (
276
+ <>
277
+ <form className="memori--login-drawer--form" onSubmit={login}>
278
+ <label htmlFor="#userNameOrEmail">
279
+ {t('login.userNameOrEmail')}
280
+ <input
281
+ id="userNameOrEmail"
282
+ name="userNameOrEmail"
283
+ required
284
+ autoComplete="email"
285
+ placeholder="Username/email"
286
+ />
287
+ </label>
288
+
289
+ <label htmlFor="#password">
290
+ Password
291
+ <input
292
+ id="password"
293
+ name="password"
294
+ type="password"
295
+ required
296
+ autoComplete="password"
297
+ placeholder="Password"
298
+ />
299
+ </label>
300
+
301
+ <Button htmlType="submit" primary loading={loading}>
302
+ {t('login.login')}
303
+ </Button>
304
+
305
+ {!tenant?.disableRegistration && (
306
+ <p className="memori--login-drawer--signup">
307
+ {t('login.newUserSignUp')}{' '}
308
+ <a
309
+ href={`https://${
310
+ tenant.name || 'www.aisuru.com'
311
+ }/${lang}/auth?signup=1&redirectTo=${redirectTo}`}
312
+ >
313
+ {t('login.signUp')}
314
+ </a>
315
+ </p>
316
+ )}
317
+ </form>
318
+
319
+ {error && (
320
+ <p role="alert" className="memori--login-drawer--error">
321
+ {error}
322
+ </p>
323
+ )}
324
+ </>
325
+ )}
326
+ </Drawer>
327
+ );
328
+ };
329
+
330
+ export default LoginDrawer;
@@ -0,0 +1,35 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`renders LoginDrawer closed unchanged 1`] = `<div />`;
4
+
5
+ exports[`renders LoginDrawer logged in unchanged 1`] = `
6
+ <div>
7
+ <div
8
+ style="position: fixed; top: 1px; left: 1px; width: 1px; height: 0px; padding: 0px; margin: -1px; overflow: hidden; clip: rect(0px, 0px, 0px, 0px); white-space: nowrap; border-width: 0px; display: none;"
9
+ />
10
+ </div>
11
+ `;
12
+
13
+ exports[`renders LoginDrawer logged with missing data unchanged 1`] = `
14
+ <div>
15
+ <div
16
+ style="position: fixed; top: 1px; left: 1px; width: 1px; height: 0px; padding: 0px; margin: -1px; overflow: hidden; clip: rect(0px, 0px, 0px, 0px); white-space: nowrap; border-width: 0px; display: none;"
17
+ />
18
+ </div>
19
+ `;
20
+
21
+ exports[`renders LoginDrawer open unchanged 1`] = `
22
+ <div>
23
+ <div
24
+ style="position: fixed; top: 1px; left: 1px; width: 1px; height: 0px; padding: 0px; margin: -1px; overflow: hidden; clip: rect(0px, 0px, 0px, 0px); white-space: nowrap; border-width: 0px; display: none;"
25
+ />
26
+ </div>
27
+ `;
28
+
29
+ exports[`renders LoginDrawer unlogged unchanged 1`] = `
30
+ <div>
31
+ <div
32
+ style="position: fixed; top: 1px; left: 1px; width: 1px; height: 0px; padding: 0px; margin: -1px; overflow: hidden; clip: rect(0px, 0px, 0px, 0px); white-space: nowrap; border-width: 0px; display: none;"
33
+ />
34
+ </div>
35
+ `;
@@ -53,6 +53,7 @@ import AgeVerificationModal from '../AgeVerificationModal/AgeVerificationModal';
53
53
  import SettingsDrawer from '../SettingsDrawer/SettingsDrawer';
54
54
  import KnownFacts from '../KnownFacts/KnownFacts';
55
55
  import ExpertsDrawer from '../ExpertsDrawer/ExpertsDrawer';
56
+ import LoginDrawer from '../LoginDrawer/LoginDrawer';
56
57
 
57
58
  // Layout
58
59
  import FullPageLayout from '../layouts/FullPage';
@@ -331,6 +332,7 @@ export interface Props {
331
332
  showClear?: boolean;
332
333
  showOnlyLastMessages?: boolean;
333
334
  showTypingText?: boolean;
335
+ showLogin?: boolean;
334
336
  preview?: boolean;
335
337
  embed?: boolean;
336
338
  height?: number | string;
@@ -375,6 +377,7 @@ const MemoriWidget = ({
375
377
  showSettings = true,
376
378
  showTypingText = false,
377
379
  showClear = false,
380
+ showLogin = true,
378
381
  showOnlyLastMessages,
379
382
  height = '100vh',
380
383
  secret,
@@ -420,16 +423,22 @@ const MemoriWidget = ({
420
423
  const [loginToken, setLoginToken] = useState<string | undefined>(
421
424
  additionalInfo?.loginToken ?? authToken
422
425
  );
423
- const [user, setUser] = useState<User>({
426
+ const [user, setUser] = useState<User | undefined>({
424
427
  avatarURL: typeof userAvatar === 'string' ? userAvatar : undefined,
425
428
  } as User);
426
429
  useEffect(() => {
427
- if (loginToken) {
430
+ if (loginToken && !user?.userID) {
428
431
  client.backend.getCurrentUser(loginToken).then(({ user, resultCode }) => {
429
- if (user && resultCode === 0) setUser(user);
432
+ if (user && resultCode === 0) {
433
+ setUser(user);
434
+ setLocalConfig('loginToken', loginToken);
435
+ } else {
436
+ setLocalConfig('loginToken', undefined);
437
+ }
430
438
  });
431
439
  }
432
- }, [loginToken]);
440
+ }, [loginToken, user?.userID]);
441
+ const [showLoginDrawer, setShowLoginDrawer] = useState(false);
433
442
 
434
443
  const [clickedStart, setClickedStart] = useState(false);
435
444
  const [gotErrorInOpening, setGotErrorInOpening] = useState(false);
@@ -514,6 +523,10 @@ const MemoriWidget = ({
514
523
  getLocalConfig('controlsPosition', defaultControlsPosition)
515
524
  );
516
525
  setHideEmissions(getLocalConfig('hideEmissions', false));
526
+
527
+ if (!additionalInfo?.loginToken && !authToken) {
528
+ setLoginToken(getLocalConfig<typeof loginToken>('loginToken', undefined));
529
+ }
517
530
  }, []);
518
531
 
519
532
  /**
@@ -2622,6 +2635,8 @@ const MemoriWidget = ({
2622
2635
  showReload: selectedLayout === 'TOTEM',
2623
2636
  showClear,
2624
2637
  clearHistory: () => setHistory(h => h.slice(-1)),
2638
+ showLogin,
2639
+ setShowLoginDrawer,
2625
2640
  loginToken,
2626
2641
  user,
2627
2642
  sessionID: sessionId,
@@ -2660,6 +2675,8 @@ const MemoriWidget = ({
2660
2675
  onClickStart: onClickStart,
2661
2676
  initializeTTS: initializeTTS,
2662
2677
  isUserLoggedIn: !!loginToken,
2678
+ showLogin,
2679
+ setShowLoginDrawer,
2663
2680
  user,
2664
2681
  };
2665
2682
 
@@ -2946,6 +2963,33 @@ const MemoriWidget = ({
2946
2963
  onClose={() => setShowExpertsDrawer(false)}
2947
2964
  />
2948
2965
  )}
2966
+
2967
+ {showLoginDrawer && tenant?.id && (
2968
+ <LoginDrawer
2969
+ tenant={tenant}
2970
+ apiUrl={apiUrl}
2971
+ open={!!showLoginDrawer}
2972
+ user={user}
2973
+ loginToken={loginToken}
2974
+ onClose={() => setShowLoginDrawer(false)}
2975
+ onLogin={(user, token) => {
2976
+ setUser(user);
2977
+ setLoginToken(token);
2978
+ setShowLoginDrawer(false);
2979
+ setLocalConfig('loginToken', token);
2980
+ }}
2981
+ onLogout={() => {
2982
+ if (!loginToken) return;
2983
+
2984
+ client.backend.userLogout(loginToken).then(() => {
2985
+ setShowLoginDrawer(false);
2986
+ setUser(undefined);
2987
+ setLoginToken(undefined);
2988
+ setLocalConfig('loginToken', undefined);
2989
+ });
2990
+ }}
2991
+ />
2992
+ )}
2949
2993
  </div>
2950
2994
  );
2951
2995
  };