@openedx/frontend-app-authn 1.0.0-alpha.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 (177) hide show
  1. package/LICENSE +661 -0
  2. package/README.rst +204 -0
  3. package/package.json +79 -0
  4. package/src/Main.tsx +23 -0
  5. package/src/__mocks__/file.js +1 -0
  6. package/src/__mocks__/svg.js +1 -0
  7. package/src/app.scss +6 -0
  8. package/src/app.ts +43 -0
  9. package/src/base-container/components/default-layout/DefaultLayout.test.jsx +48 -0
  10. package/src/base-container/components/default-layout/LargeLayout.jsx +42 -0
  11. package/src/base-container/components/default-layout/MediumLayout.jsx +49 -0
  12. package/src/base-container/components/default-layout/SmallLayout.jsx +37 -0
  13. package/src/base-container/components/default-layout/index.jsx +3 -0
  14. package/src/base-container/components/default-layout/messages.js +16 -0
  15. package/src/base-container/components/image-layout/ExtraSmallLayout.jsx +31 -0
  16. package/src/base-container/components/image-layout/LargeLayout.jsx +32 -0
  17. package/src/base-container/components/image-layout/MediumLayout.jsx +32 -0
  18. package/src/base-container/components/image-layout/SmallLayout.jsx +31 -0
  19. package/src/base-container/components/image-layout/index.jsx +4 -0
  20. package/src/base-container/components/image-layout/index.scss +37 -0
  21. package/src/base-container/components/image-layout/messages.js +16 -0
  22. package/src/base-container/components/welcome-page-layout/LargeLayout.jsx +46 -0
  23. package/src/base-container/components/welcome-page-layout/MediumLayout.jsx +49 -0
  24. package/src/base-container/components/welcome-page-layout/SmallLayout.jsx +38 -0
  25. package/src/base-container/components/welcome-page-layout/index.jsx +3 -0
  26. package/src/base-container/components/welcome-page-layout/messages.js +21 -0
  27. package/src/base-container/index.jsx +70 -0
  28. package/src/base-container/tests/BaseContainer.test.jsx +44 -0
  29. package/src/common-components/EmbeddedRegistrationRoute.jsx +26 -0
  30. package/src/common-components/EnterpriseSSO.jsx +110 -0
  31. package/src/common-components/FormGroup.jsx +106 -0
  32. package/src/common-components/InstitutionLogistration.jsx +102 -0
  33. package/src/common-components/NotFoundPage.jsx +15 -0
  34. package/src/common-components/PasswordField.jsx +175 -0
  35. package/src/common-components/RedirectLogistration.jsx +94 -0
  36. package/src/common-components/RegisterFaIcons.jsx +8 -0
  37. package/src/common-components/SocialAuthProviders.jsx +75 -0
  38. package/src/common-components/ThirdPartyAuth.jsx +123 -0
  39. package/src/common-components/ThirdPartyAuthAlert.jsx +49 -0
  40. package/src/common-components/UnAuthOnlyRoute.jsx +41 -0
  41. package/src/common-components/data/actions.js +27 -0
  42. package/src/common-components/data/reducers.js +63 -0
  43. package/src/common-components/data/sagas.js +32 -0
  44. package/src/common-components/data/selectors.js +28 -0
  45. package/src/common-components/data/service.js +23 -0
  46. package/src/common-components/data/tests/reducer.test.js +82 -0
  47. package/src/common-components/data/tests/sagas.test.js +71 -0
  48. package/src/common-components/index.jsx +14 -0
  49. package/src/common-components/messages.jsx +127 -0
  50. package/src/common-components/tests/EmbeddedRegistrationRoute.test.jsx +76 -0
  51. package/src/common-components/tests/FormField.test.jsx +289 -0
  52. package/src/common-components/tests/SocialAuthProviders.test.jsx +75 -0
  53. package/src/common-components/tests/ThirdPartyAuthAlert.test.jsx +39 -0
  54. package/src/common-components/tests/UnAuthOnlyRoute.test.jsx +73 -0
  55. package/src/common-components/tests/__snapshots__/SocialAuthProviders.test.jsx.snap +156 -0
  56. package/src/common-components/tests/__snapshots__/ThirdPartyAuthAlert.test.jsx.snap +53 -0
  57. package/src/constants.ts +1 -0
  58. package/src/data/configureStore.js +33 -0
  59. package/src/data/constants.js +38 -0
  60. package/src/data/countries.ts +59 -0
  61. package/src/data/oneTrust.js +3 -0
  62. package/src/data/reducers.js +36 -0
  63. package/src/data/sagas.js +19 -0
  64. package/src/data/tests/cookies.test.js +42 -0
  65. package/src/data/tests/dataUtils.test.js +19 -0
  66. package/src/data/tests/reduxUtils.test.js +14 -0
  67. package/src/data/utils/cookies.js +12 -0
  68. package/src/data/utils/dataUtils.js +81 -0
  69. package/src/data/utils/index.js +11 -0
  70. package/src/data/utils/reduxUtils.js +34 -0
  71. package/src/data/utils/useMobileResponsive.js +24 -0
  72. package/src/field-renderer/FieldRenderer.jsx +156 -0
  73. package/src/field-renderer/index.jsx +2 -0
  74. package/src/field-renderer/tests/FieldRenderer.test.jsx +201 -0
  75. package/src/forgot-password/ForgotPasswordAlert.jsx +97 -0
  76. package/src/forgot-password/ForgotPasswordPage.jsx +193 -0
  77. package/src/forgot-password/data/actions.js +32 -0
  78. package/src/forgot-password/data/reducers.js +58 -0
  79. package/src/forgot-password/data/sagas.js +35 -0
  80. package/src/forgot-password/data/selectors.js +10 -0
  81. package/src/forgot-password/data/service.js +21 -0
  82. package/src/forgot-password/data/tests/reducers.test.js +34 -0
  83. package/src/forgot-password/data/tests/sagas.test.js +67 -0
  84. package/src/forgot-password/index.js +5 -0
  85. package/src/forgot-password/messages.js +128 -0
  86. package/src/forgot-password/tests/ForgotPasswordPage.test.jsx +269 -0
  87. package/src/i18n/index.js +25 -0
  88. package/src/index.ts +3 -0
  89. package/src/login/AccountActivationMessage.jsx +78 -0
  90. package/src/login/ChangePasswordPrompt.jsx +91 -0
  91. package/src/login/LoginFailure.jsx +231 -0
  92. package/src/login/LoginPage.jsx +368 -0
  93. package/src/login/data/actions.js +39 -0
  94. package/src/login/data/constants.js +20 -0
  95. package/src/login/data/reducers.js +76 -0
  96. package/src/login/data/sagas.js +45 -0
  97. package/src/login/data/service.js +24 -0
  98. package/src/login/data/tests/reducers.test.js +155 -0
  99. package/src/login/data/tests/sagas.test.js +110 -0
  100. package/src/login/index.js +5 -0
  101. package/src/login/messages.jsx +211 -0
  102. package/src/login/tests/AccountActivationMessage.test.jsx +122 -0
  103. package/src/login/tests/ChangePasswordPrompt.test.jsx +75 -0
  104. package/src/login/tests/LoginFailure.test.jsx +323 -0
  105. package/src/login/tests/LoginPage.test.jsx +839 -0
  106. package/src/logistration/Logistration.jsx +189 -0
  107. package/src/logistration/Logistration.test.jsx +275 -0
  108. package/src/progressive-profiling/ProgressiveProfiling.jsx +313 -0
  109. package/src/progressive-profiling/ProgressiveProfilingPageModal.jsx +56 -0
  110. package/src/progressive-profiling/data/actions.js +22 -0
  111. package/src/progressive-profiling/data/reducers.js +38 -0
  112. package/src/progressive-profiling/data/sagas.js +24 -0
  113. package/src/progressive-profiling/data/selectors.js +14 -0
  114. package/src/progressive-profiling/data/service.js +17 -0
  115. package/src/progressive-profiling/index.js +5 -0
  116. package/src/progressive-profiling/messages.jsx +57 -0
  117. package/src/progressive-profiling/tests/ProgressiveProfiling.test.jsx +322 -0
  118. package/src/register/RegistrationFields/CountryField/CountryField.jsx +164 -0
  119. package/src/register/RegistrationFields/CountryField/CountryField.test.jsx +204 -0
  120. package/src/register/RegistrationFields/CountryField/validator.js +32 -0
  121. package/src/register/RegistrationFields/EmailField/EmailField.jsx +140 -0
  122. package/src/register/RegistrationFields/EmailField/EmailField.test.jsx +236 -0
  123. package/src/register/RegistrationFields/EmailField/constants.js +131 -0
  124. package/src/register/RegistrationFields/EmailField/validator.js +126 -0
  125. package/src/register/RegistrationFields/EmailField/validator.test.js +39 -0
  126. package/src/register/RegistrationFields/HonorCodeField/HonorCode.jsx +104 -0
  127. package/src/register/RegistrationFields/HonorCodeField/HonorCode.test.jsx +70 -0
  128. package/src/register/RegistrationFields/NameField/NameField.jsx +68 -0
  129. package/src/register/RegistrationFields/NameField/NameField.test.jsx +146 -0
  130. package/src/register/RegistrationFields/NameField/validator.js +22 -0
  131. package/src/register/RegistrationFields/TermsOfServiceField/TermsOfService.jsx +58 -0
  132. package/src/register/RegistrationFields/TermsOfServiceField/TermsOfService.test.jsx +55 -0
  133. package/src/register/RegistrationFields/UsernameField/UsernameField.jsx +162 -0
  134. package/src/register/RegistrationFields/UsernameField/UsernameField.test.jsx +319 -0
  135. package/src/register/RegistrationFields/UsernameField/validator.js +16 -0
  136. package/src/register/RegistrationFields/index.js +6 -0
  137. package/src/register/RegistrationPage.jsx +428 -0
  138. package/src/register/RegistrationPage.test.jsx +854 -0
  139. package/src/register/components/ConfigurableRegistrationForm.jsx +244 -0
  140. package/src/register/components/RegistrationFailure.jsx +80 -0
  141. package/src/register/components/tests/ConfigurableRegistrationForm.test.jsx +401 -0
  142. package/src/register/components/tests/RegistrationFailure.test.jsx +212 -0
  143. package/src/register/components/tests/ThirdPartyAuth.test.jsx +439 -0
  144. package/src/register/data/actions.js +85 -0
  145. package/src/register/data/constants.js +13 -0
  146. package/src/register/data/reducers.js +140 -0
  147. package/src/register/data/sagas.js +58 -0
  148. package/src/register/data/selectors.js +34 -0
  149. package/src/register/data/service.js +45 -0
  150. package/src/register/data/tests/reducers.test.js +277 -0
  151. package/src/register/data/tests/sagas.test.js +239 -0
  152. package/src/register/data/tests/utils.test.js +98 -0
  153. package/src/register/data/utils.js +153 -0
  154. package/src/register/index.js +4 -0
  155. package/src/register/messages.jsx +206 -0
  156. package/src/reset-password/ResetPasswordFailure.jsx +55 -0
  157. package/src/reset-password/ResetPasswordPage.jsx +224 -0
  158. package/src/reset-password/ResetPasswordSuccess.jsx +19 -0
  159. package/src/reset-password/data/actions.js +50 -0
  160. package/src/reset-password/data/constants.js +16 -0
  161. package/src/reset-password/data/reducers.js +44 -0
  162. package/src/reset-password/data/sagas.js +67 -0
  163. package/src/reset-password/data/selectors.js +10 -0
  164. package/src/reset-password/data/service.js +61 -0
  165. package/src/reset-password/data/tests/sagas.test.js +185 -0
  166. package/src/reset-password/index.js +5 -0
  167. package/src/reset-password/messages.js +88 -0
  168. package/src/reset-password/tests/ResetPasswordPage.test.jsx +241 -0
  169. package/src/routes.jsx +79 -0
  170. package/src/sass/_base_component.scss +202 -0
  171. package/src/sass/_forgot_password.scss +3 -0
  172. package/src/sass/_login_page.scss +3 -0
  173. package/src/sass/_progressive_profiling_page.scss +30 -0
  174. package/src/sass/_registration.scss +123 -0
  175. package/src/sass/_reset_password.scss +3 -0
  176. package/src/sass/_style.scss +465 -0
  177. package/src/setupTest.js +68 -0
@@ -0,0 +1,428 @@
1
+ import {
2
+ useEffect, useMemo, useState,
3
+ } from 'react';
4
+ import { useDispatch, useSelector } from 'react-redux';
5
+
6
+ import {
7
+ useAppConfig,
8
+ getSiteConfig,
9
+ sendPageEvent, sendTrackEvent,
10
+ useIntl
11
+ } from '@openedx/frontend-base';
12
+ import { Form, Spinner, StatefulButton } from '@openedx/paragon';
13
+ import classNames from 'classnames';
14
+ import PropTypes from 'prop-types';
15
+ import { Helmet } from 'react-helmet';
16
+ import Skeleton from 'react-loading-skeleton';
17
+
18
+ import {
19
+ InstitutionLogistration,
20
+ PasswordField,
21
+ RedirectLogistration,
22
+ ThirdPartyAuthAlert,
23
+ } from '../common-components';
24
+ import { getThirdPartyAuthContext as getRegistrationDataFromBackend } from '../common-components/data/actions';
25
+ import EnterpriseSSO from '../common-components/EnterpriseSSO';
26
+ import ThirdPartyAuth from '../common-components/ThirdPartyAuth';
27
+ import {
28
+ COMPLETE_STATE, PENDING_STATE, REGISTER_PAGE,
29
+ } from '../data/constants';
30
+ import {
31
+ getAllPossibleQueryParams, getTpaHint, getTpaProvider, isHostAvailableInQueryParams, setCookie,
32
+ } from '../data/utils';
33
+ import ConfigurableRegistrationForm from './components/ConfigurableRegistrationForm';
34
+ import RegistrationFailure from './components/RegistrationFailure';
35
+ import {
36
+ backupRegistrationFormBegin,
37
+ clearRegistrationBackendError,
38
+ registerNewUser,
39
+ setEmailSuggestionInStore,
40
+ setUserPipelineDataLoaded,
41
+ } from './data/actions';
42
+ import {
43
+ FORM_SUBMISSION_ERROR,
44
+ TPA_AUTHENTICATION_FAILURE,
45
+ } from './data/constants';
46
+ import getBackendValidations from './data/selectors';
47
+ import {
48
+ isFormValid, prepareRegistrationPayload,
49
+ } from './data/utils';
50
+ import messages from './messages';
51
+ import { EmailField, NameField, UsernameField } from './RegistrationFields';
52
+
53
+ /**
54
+ * Main Registration Page component
55
+ */
56
+ const RegistrationPage = (props) => {
57
+ const { formatMessage } = useIntl();
58
+ const dispatch = useDispatch();
59
+ const registrationEmbedded = isHostAvailableInQueryParams();
60
+ const platformName = getSiteConfig().siteName;
61
+ const {
62
+ handleInstitutionLogin,
63
+ institutionLogin,
64
+ } = props;
65
+ const {
66
+ ENABLE_AUTO_GENERATED_USERNAME,
67
+ ENABLE_PROGRESSIVE_PROFILING_ON_AUTHN,
68
+ MARKETING_EMAILS_OPT_IN,
69
+ USER_RETENTION_COOKIE_NAME,
70
+ SESSION_COOKIE_DOMAIN,
71
+ } = useAppConfig();
72
+ const flags = {
73
+ showMarketingEmailOptInCheckbox: MARKETING_EMAILS_OPT_IN,
74
+ autoGeneratedUsernameEnabled: ENABLE_AUTO_GENERATED_USERNAME,
75
+ };
76
+
77
+ const backedUpFormData = useSelector(state => state.register.registrationFormData);
78
+ const registrationError = useSelector(state => state.register.registrationError);
79
+ const registrationErrorCode = registrationError?.errorCode;
80
+ const registrationResult = useSelector(state => state.register.registrationResult);
81
+ const shouldBackupState = useSelector(state => state.register.shouldBackupState);
82
+ const userPipelineDataLoaded = useSelector(state => state.register.userPipelineDataLoaded);
83
+ const submitState = useSelector(state => state.register.submitState);
84
+
85
+ const fieldDescriptions = useSelector(state => state.commonComponents.fieldDescriptions);
86
+ const optionalFields = useSelector(state => state.commonComponents.optionalFields);
87
+ const thirdPartyAuthApiStatus = useSelector(state => state.commonComponents.thirdPartyAuthApiStatus);
88
+ const autoSubmitRegForm = useSelector(state => state.commonComponents.thirdPartyAuthContext.autoSubmitRegForm);
89
+ const thirdPartyAuthErrorMessage = useSelector(state => state.commonComponents.thirdPartyAuthContext.errorMessage);
90
+ const finishAuthUrl = useSelector(state => state.commonComponents.thirdPartyAuthContext.finishAuthUrl);
91
+ const currentProvider = useSelector(state => state.commonComponents.thirdPartyAuthContext.currentProvider);
92
+ const providers = useSelector(state => state.commonComponents.thirdPartyAuthContext.providers);
93
+ const secondaryProviders = useSelector(state => state.commonComponents.thirdPartyAuthContext.secondaryProviders);
94
+ const pipelineUserDetails = useSelector(state => state.commonComponents.thirdPartyAuthContext.pipelineUserDetails);
95
+
96
+ const backendValidations = useSelector(getBackendValidations);
97
+ const queryParams = useMemo(() => getAllPossibleQueryParams(), []);
98
+ const tpaHint = useMemo(() => getTpaHint(), []);
99
+
100
+ const [formFields, setFormFields] = useState({ ...backedUpFormData.formFields });
101
+ const [configurableFormFields, setConfigurableFormFields] = useState({ ...backedUpFormData.configurableFormFields });
102
+ const [errors, setErrors] = useState({ ...backedUpFormData.errors });
103
+ const [errorCode, setErrorCode] = useState({ type: '', count: 0 });
104
+ const [formStartTime, setFormStartTime] = useState(null);
105
+ // temporary error state for embedded experience because we don't want to show errors on blur
106
+ const [temporaryErrors, setTemporaryErrors] = useState({ ...backedUpFormData.errors });
107
+
108
+ const { cta, host } = queryParams;
109
+ const buttonLabel = cta
110
+ ? formatMessage(messages['create.account.cta.button'], { label: cta })
111
+ : formatMessage(messages['create.account.for.free.button']);
112
+
113
+ /**
114
+ * Set the userPipelineDetails data in formFields for only first time
115
+ */
116
+ useEffect(() => {
117
+ if (!userPipelineDataLoaded && thirdPartyAuthApiStatus === COMPLETE_STATE) {
118
+ if (thirdPartyAuthErrorMessage) {
119
+ setErrorCode(prevState => ({ type: TPA_AUTHENTICATION_FAILURE, count: prevState.count + 1 }));
120
+ }
121
+ if (pipelineUserDetails && Object.keys(pipelineUserDetails).length !== 0) {
122
+ const { name = '', username = '', email = '' } = pipelineUserDetails;
123
+ setFormFields(prevState => ({
124
+ ...prevState, name, username, email,
125
+ }));
126
+ dispatch(setUserPipelineDataLoaded(true));
127
+ }
128
+ }
129
+ }, [ // eslint-disable-line react-hooks/exhaustive-deps
130
+ thirdPartyAuthApiStatus,
131
+ thirdPartyAuthErrorMessage,
132
+ pipelineUserDetails,
133
+ userPipelineDataLoaded,
134
+ ]);
135
+
136
+ useEffect(() => {
137
+ if (!formStartTime) {
138
+ sendPageEvent('login_and_registration', 'register');
139
+ const payload = { ...queryParams, is_register_page: true };
140
+ if (tpaHint) {
141
+ payload.tpa_hint = tpaHint;
142
+ }
143
+ dispatch(getRegistrationDataFromBackend(payload));
144
+ setFormStartTime(Date.now());
145
+ }
146
+ }, [dispatch, formStartTime, queryParams, tpaHint]);
147
+
148
+ /**
149
+ * Backup the registration form in redux when register page is toggled.
150
+ */
151
+ useEffect(() => {
152
+ if (shouldBackupState) {
153
+ dispatch(backupRegistrationFormBegin({
154
+ ...backedUpFormData,
155
+ configurableFormFields: { ...configurableFormFields },
156
+ formFields: { ...formFields },
157
+ errors: { ...errors },
158
+ }));
159
+ }
160
+ }, [shouldBackupState, configurableFormFields, formFields, errors, dispatch, backedUpFormData]);
161
+
162
+ useEffect(() => {
163
+ if (backendValidations) {
164
+ if (registrationEmbedded) {
165
+ setTemporaryErrors(prevErrors => ({ ...prevErrors, ...backendValidations }));
166
+ } else {
167
+ setErrors(prevErrors => ({ ...prevErrors, ...backendValidations }));
168
+ }
169
+ }
170
+ }, [backendValidations, registrationEmbedded]);
171
+
172
+ useEffect(() => {
173
+ if (registrationErrorCode) {
174
+ setErrorCode(prevState => ({ type: registrationErrorCode, count: prevState.count + 1 }));
175
+ }
176
+ }, [registrationErrorCode]);
177
+
178
+ useEffect(() => {
179
+ if (registrationResult.success) {
180
+ // This event is used by GTM
181
+ sendTrackEvent('edx.bi.user.account.registered.client', {});
182
+
183
+ // This is used by the "User Retention Rate Event" on GTM
184
+ setCookie(USER_RETENTION_COOKIE_NAME, true, SESSION_COOKIE_DOMAIN);
185
+ }
186
+ }, [registrationResult]);
187
+
188
+ const handleOnChange = (event) => {
189
+ const { name } = event.target;
190
+ const value = event.target.type === 'checkbox' ? event.target.checked : event.target.value;
191
+ if (registrationError[name]) {
192
+ dispatch(clearRegistrationBackendError(name));
193
+ }
194
+ setErrors(prevErrors => ({ ...prevErrors, [name]: '' }));
195
+ setFormFields(prevState => ({ ...prevState, [name]: value }));
196
+ };
197
+
198
+ const handleErrorChange = (fieldName, error) => {
199
+ if (registrationEmbedded) {
200
+ setTemporaryErrors(prevErrors => ({
201
+ ...prevErrors,
202
+ [fieldName]: error,
203
+ }));
204
+ if (error === '' && errors[fieldName] !== '') {
205
+ setErrors(prevErrors => ({
206
+ ...prevErrors,
207
+ [fieldName]: error,
208
+ }));
209
+ }
210
+ } else {
211
+ setErrors(prevErrors => ({
212
+ ...prevErrors,
213
+ [fieldName]: error,
214
+ }));
215
+ }
216
+ };
217
+
218
+ const registerUser = () => {
219
+ const totalRegistrationTime = (Date.now() - formStartTime) / 1000;
220
+ let payload = { ...formFields };
221
+
222
+ if (currentProvider) {
223
+ delete payload.password;
224
+ payload.social_auth_provider = currentProvider;
225
+ }
226
+ if (flags.autoGeneratedUsernameEnabled) {
227
+ delete payload.username;
228
+ }
229
+
230
+ // Validating form data before submitting
231
+ const { isValid, fieldErrors, emailSuggestion } = isFormValid(
232
+ payload,
233
+ registrationEmbedded ? temporaryErrors : errors,
234
+ configurableFormFields,
235
+ fieldDescriptions,
236
+ formatMessage,
237
+ );
238
+ setErrors({ ...fieldErrors });
239
+ dispatch(setEmailSuggestionInStore(emailSuggestion));
240
+
241
+ // returning if not valid
242
+ if (!isValid) {
243
+ setErrorCode(prevState => ({ type: FORM_SUBMISSION_ERROR, count: prevState.count + 1 }));
244
+ return;
245
+ }
246
+
247
+ // Preparing payload for submission
248
+ payload = prepareRegistrationPayload(
249
+ payload,
250
+ configurableFormFields,
251
+ flags.showMarketingEmailOptInCheckbox,
252
+ totalRegistrationTime,
253
+ queryParams
254
+ );
255
+
256
+ // making register call
257
+ dispatch(registerNewUser(payload));
258
+ };
259
+
260
+ const handleSubmit = (e) => {
261
+ e.preventDefault();
262
+ registerUser();
263
+ };
264
+
265
+ useEffect(() => {
266
+ if (autoSubmitRegForm && userPipelineDataLoaded) {
267
+ registerUser();
268
+ }
269
+ }, [autoSubmitRegForm, userPipelineDataLoaded]); // eslint-disable-line react-hooks/exhaustive-deps
270
+
271
+ const renderForm = () => {
272
+ if (institutionLogin) {
273
+ return (
274
+ <InstitutionLogistration
275
+ secondaryProviders={secondaryProviders}
276
+ headingTitle={formatMessage(messages['register.institution.login.page.title'])}
277
+ />
278
+ );
279
+ }
280
+ return (
281
+ <>
282
+ <Helmet>
283
+ <title>{formatMessage(messages['register.page.title'], { siteName: getSiteConfig().siteName })}</title>
284
+ </Helmet>
285
+ <RedirectLogistration
286
+ host={host}
287
+ authenticatedUser={registrationResult.authenticatedUser}
288
+ success={registrationResult.success}
289
+ redirectUrl={registrationResult.redirectUrl}
290
+ finishAuthUrl={finishAuthUrl}
291
+ optionalFields={optionalFields}
292
+ registrationEmbedded={registrationEmbedded}
293
+ redirectToProgressiveProfilingPage={
294
+ ENABLE_PROGRESSIVE_PROFILING_ON_AUTHN && !!Object.keys(optionalFields.fields).length
295
+ }
296
+ />
297
+ {autoSubmitRegForm && !errorCode.type ? (
298
+ <div className="mw-xs mt-5 text-center">
299
+ <Spinner animation="border" variant="primary" id="tpa-spinner" />
300
+ </div>
301
+ ) : (
302
+ <div
303
+ className={classNames(
304
+ 'mw-xs mt-3',
305
+ { 'w-100 m-auto pt-4 main-content': registrationEmbedded },
306
+ )}
307
+ >
308
+ <ThirdPartyAuthAlert
309
+ currentProvider={currentProvider}
310
+ platformName={platformName}
311
+ referrer={REGISTER_PAGE}
312
+ />
313
+ <RegistrationFailure
314
+ errorCode={errorCode.type}
315
+ failureCount={errorCode.count}
316
+ context={{ provider: currentProvider, errorMessage: thirdPartyAuthErrorMessage }}
317
+ />
318
+ <Form id="registration-form" name="registration-form">
319
+ <NameField
320
+ name="name"
321
+ value={formFields.name}
322
+ shouldFetchUsernameSuggestions={!formFields.username.trim()}
323
+ handleChange={handleOnChange}
324
+ handleErrorChange={handleErrorChange}
325
+ errorMessage={errors.name}
326
+ helpText={[formatMessage(messages['help.text.name'])]}
327
+ floatingLabel={formatMessage(messages['registration.fullname.label'])}
328
+ />
329
+ <EmailField
330
+ name="email"
331
+ value={formFields.email}
332
+ confirmEmailValue={configurableFormFields?.confirm_email}
333
+ handleErrorChange={handleErrorChange}
334
+ handleChange={handleOnChange}
335
+ errorMessage={errors.email}
336
+ helpText={[formatMessage(messages['help.text.email'])]}
337
+ floatingLabel={formatMessage(messages['registration.email.label'])}
338
+ />
339
+ {!flags.autoGeneratedUsernameEnabled && (
340
+ <UsernameField
341
+ name="username"
342
+ spellCheck="false"
343
+ value={formFields.username}
344
+ handleChange={handleOnChange}
345
+ handleErrorChange={handleErrorChange}
346
+ errorMessage={errors.username}
347
+ helpText={[formatMessage(messages['help.text.username.1']), formatMessage(messages['help.text.username.2'])]}
348
+ floatingLabel={formatMessage(messages['registration.username.label'])}
349
+ />
350
+ )}
351
+ {!currentProvider && (
352
+ <PasswordField
353
+ name="password"
354
+ value={formFields.password}
355
+ handleChange={handleOnChange}
356
+ handleErrorChange={handleErrorChange}
357
+ errorMessage={errors.password}
358
+ floatingLabel={formatMessage(messages['registration.password.label'])}
359
+ />
360
+ )}
361
+ <ConfigurableRegistrationForm
362
+ email={formFields.email}
363
+ fieldErrors={errors}
364
+ formFields={configurableFormFields}
365
+ setFieldErrors={registrationEmbedded ? setTemporaryErrors : setErrors}
366
+ setFormFields={setConfigurableFormFields}
367
+ autoSubmitRegisterForm={autoSubmitRegForm}
368
+ fieldDescriptions={fieldDescriptions}
369
+ />
370
+ <StatefulButton
371
+ id="register-user"
372
+ name="register-user"
373
+ type="submit"
374
+ variant="brand"
375
+ className="register-button mt-4 mb-4"
376
+ state={submitState}
377
+ labels={{
378
+ default: buttonLabel,
379
+ pending: '',
380
+ }}
381
+ onClick={handleSubmit}
382
+ onMouseDown={(e) => e.preventDefault()}
383
+ />
384
+ {!registrationEmbedded && (
385
+ <ThirdPartyAuth
386
+ currentProvider={currentProvider}
387
+ providers={providers}
388
+ secondaryProviders={secondaryProviders}
389
+ handleInstitutionLogin={handleInstitutionLogin}
390
+ thirdPartyAuthApiStatus={thirdPartyAuthApiStatus}
391
+ />
392
+ )}
393
+ </Form>
394
+ </div>
395
+ )}
396
+
397
+ </>
398
+ );
399
+ };
400
+
401
+ if (tpaHint) {
402
+ if (thirdPartyAuthApiStatus === PENDING_STATE) {
403
+ return <Skeleton height={36} />;
404
+ }
405
+ const { provider, skipHintedLogin } = getTpaProvider(tpaHint, providers, secondaryProviders);
406
+ if (skipHintedLogin) {
407
+ window.location.href = getSiteConfig().lmsBaseUrl + provider.registerUrl;
408
+ return null;
409
+ }
410
+ return provider ? <EnterpriseSSO provider={provider} /> : renderForm();
411
+ }
412
+ return (
413
+ renderForm()
414
+ );
415
+ };
416
+
417
+ RegistrationPage.propTypes = {
418
+ institutionLogin: PropTypes.bool,
419
+ // Actions
420
+ handleInstitutionLogin: PropTypes.func,
421
+ };
422
+
423
+ RegistrationPage.defaultProps = {
424
+ handleInstitutionLogin: null,
425
+ institutionLogin: false,
426
+ };
427
+
428
+ export default RegistrationPage;