From f53b03c27d7deac8a061d4ae68f8837c259ed5cf Mon Sep 17 00:00:00 2001 From: "waleed.mujahid" Date: Wed, 10 Dec 2025 17:41:06 +0500 Subject: [PATCH 1/5] feat: implement edly login/registration flow - Added Edly reducer and saga to the root reducer and saga respectively. - Updated LoginPage to utilize Edly's prefilled email feature and display EdlyAlert. - Enhanced Logistration component to conditionally render EmailCheckPage based on props. - Incorporated EdlyAlert in RegistrationPage for consistent user feedback. --- src/data/reducers.js | 2 + src/data/sagas.js | 2 + src/edly/EdlyAlert.jsx | 59 ++++++++++++++ src/edly/EmailCheckPage.jsx | 125 ++++++++++++++++++++++++++++++ src/edly/data/actions.js | 27 +++++++ src/edly/data/constants.js | 13 ++++ src/edly/data/index.js | 4 + src/edly/data/reducers.js | 61 +++++++++++++++ src/edly/data/sagas.js | 25 ++++++ src/edly/data/service.js | 25 ++++++ src/edly/index.js | 3 + src/edly/messages.js | 37 +++++++++ src/login/LoginPage.jsx | 11 ++- src/logistration/Logistration.jsx | 19 ++++- src/register/RegistrationPage.jsx | 2 + 15 files changed, 412 insertions(+), 3 deletions(-) create mode 100644 src/edly/EdlyAlert.jsx create mode 100644 src/edly/EmailCheckPage.jsx create mode 100644 src/edly/data/actions.js create mode 100644 src/edly/data/constants.js create mode 100644 src/edly/data/index.js create mode 100644 src/edly/data/reducers.js create mode 100644 src/edly/data/sagas.js create mode 100644 src/edly/data/service.js create mode 100644 src/edly/index.js create mode 100644 src/edly/messages.js diff --git a/src/data/reducers.js b/src/data/reducers.js index 11c126198e..4fcc08552a 100755 --- a/src/data/reducers.js +++ b/src/data/reducers.js @@ -4,6 +4,7 @@ import { reducer as commonComponentsReducer, storeName as commonComponentsStoreName, } from '../common-components'; +import { reducer as edlyReducer, storeName as edlyStoreName } from '../edly'; import { reducer as forgotPasswordReducer, storeName as forgotPasswordStoreName, @@ -29,6 +30,7 @@ const createRootReducer = () => combineReducers({ [loginStoreName]: loginReducer, [registerStoreName]: registerReducer, [commonComponentsStoreName]: commonComponentsReducer, + [edlyStoreName]: edlyReducer, [forgotPasswordStoreName]: forgotPasswordReducer, [resetPasswordStoreName]: resetPasswordReducer, [authnProgressiveProfilingStoreName]: authnProgressiveProfilingReducers, diff --git a/src/data/sagas.js b/src/data/sagas.js index 07c9259c5d..f1ea80ebf9 100644 --- a/src/data/sagas.js +++ b/src/data/sagas.js @@ -1,6 +1,7 @@ import { all } from 'redux-saga/effects'; import { saga as commonComponentsSaga } from '../common-components'; +import { saga as edlySaga } from '../edly'; import { saga as forgotPasswordSaga } from '../forgot-password'; import { saga as loginSaga } from '../login'; import { saga as authnProgressiveProfilingSaga } from '../progressive-profiling'; @@ -12,6 +13,7 @@ export default function* rootSaga() { loginSaga(), registrationSaga(), commonComponentsSaga(), + edlySaga(), forgotPasswordSaga(), resetPasswordSaga(), authnProgressiveProfilingSaga(), diff --git a/src/edly/EdlyAlert.jsx b/src/edly/EdlyAlert.jsx new file mode 100644 index 0000000000..718f6e0efc --- /dev/null +++ b/src/edly/EdlyAlert.jsx @@ -0,0 +1,59 @@ +import React from 'react'; +import { useSelector } from 'react-redux'; + +import { useIntl } from '@edx/frontend-platform/i18n'; +import { Alert } from '@openedx/paragon'; +import { Info } from '@openedx/paragon/icons'; + +import { + EMAIL_REGISTERED_DIFFERENT_ORG, + CROSS_TENANT_LOGIN, + REGISTRATION_REQUIRED_FOR_CURRENT_TENANT, +} from './data/constants'; +import messages from './messages'; + +const EdlyAlert = () => { + const { formatMessage } = useIntl(); + const errorCode = useSelector(state => state.edly.errorCode); + const context = useSelector(state => state.edly.context); + + if (!errorCode) { + return null; + } + + let alertMessage; + switch (errorCode) { + case EMAIL_REGISTERED_DIFFERENT_ORG: + alertMessage =

{formatMessage(messages['edly.email.registered.different.org'])}

; + break; + case CROSS_TENANT_LOGIN: + alertMessage = ( +

+ {formatMessage(messages['edly.cross.tenant.login.enabled'], { + registeredSite: context.registered_site || 'a partner site', + })} +

+ ); + break; + case REGISTRATION_REQUIRED_FOR_CURRENT_TENANT: + alertMessage = ( +

+ {formatMessage(messages['edly.registration.required.for.tenant'], { + registeredSite: context.registered_site || 'a partner site', + })} +

+ ); + break; + default: + return null; + } + + return ( + + {alertMessage} + + ); +}; + +export default EdlyAlert; + diff --git a/src/edly/EmailCheckPage.jsx b/src/edly/EmailCheckPage.jsx new file mode 100644 index 0000000000..83dc17fc0f --- /dev/null +++ b/src/edly/EmailCheckPage.jsx @@ -0,0 +1,125 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; + +import { useIntl } from '@edx/frontend-platform/i18n'; +import { Alert, Form, StatefulButton } from '@openedx/paragon'; +import { Info } from '@openedx/paragon/icons'; +import PropTypes from 'prop-types'; +import BaseContainer from '../base-container'; + +import { FormGroup } from '../common-components'; +import { VALID_EMAIL_REGEX } from '../data/constants'; +import { checkEmailRequest } from './data/actions'; +import messages from './messages'; + +const EmailCheckPage = ({ onEmailCheckComplete }) => { + const { formatMessage } = useIntl(); + const dispatch = useDispatch(); + + const [email, setEmail] = useState(''); + const [emailError, setEmailError] = useState(''); + const hasCompletedRef = useRef(false); + + const submitState = useSelector(state => state.edly.submitState); + const redirectTo = useSelector(state => state.edly.redirectTo); + const checkedEmail = useSelector(state => state.edly.email); + const errorCode = useSelector(state => state.edly.errorCode); + const errorMessage = useSelector(state => state.edly.errorMessage); + + useEffect(() => { + if (redirectTo && checkedEmail && !hasCompletedRef.current) { + hasCompletedRef.current = true; + onEmailCheckComplete(redirectTo, checkedEmail, errorCode); + } + }, [redirectTo, checkedEmail, errorCode, onEmailCheckComplete]); + + const validateEmail = (emailValue) => { + if (!emailValue) { + return formatMessage(messages['email.validation.message']); + } + + const emailRegex = new RegExp(VALID_EMAIL_REGEX, 'i'); + if (!emailRegex.test(emailValue)) { + return formatMessage(messages['email.format.validation.message']); + } + + return ''; + }; + + const handleSubmit = (e) => { + e.preventDefault(); + + const error = validateEmail(email); + if (error) { + setEmailError(error); + return; + } + + dispatch(checkEmailRequest(email)); + }; + + const handleOnChange = (e) => { + const { value } = e.target; + setEmail(value); + if (emailError) { + setEmailError(''); + } + }; + + const handleOnFocus = () => { + if (emailError) { + setEmailError(''); + } + }; + + return ( + +
+
+

{formatMessage(messages['email.check.heading'])}

+

{formatMessage(messages['email.check.subheading'])}

+ + {errorMessage && ( + + {errorMessage} + + )} + +
+ + e.preventDefault()} + /> + +
+
+
+ ); +}; + +EmailCheckPage.propTypes = { + onEmailCheckComplete: PropTypes.func.isRequired, +}; + +export default EmailCheckPage; + diff --git a/src/edly/data/actions.js b/src/edly/data/actions.js new file mode 100644 index 0000000000..6e83240a25 --- /dev/null +++ b/src/edly/data/actions.js @@ -0,0 +1,27 @@ +import { + EMAIL_CHECK_REQUEST, + EMAIL_CHECK_SUCCESS, + EMAIL_CHECK_FAILURE, + EMAIL_CHECK_COMPLETE, +} from './constants'; + +export const checkEmailRequest = email => ({ + type: EMAIL_CHECK_REQUEST, + payload: { email }, +}); + +export const checkEmailSuccess = (redirectTo, email, errorCode = null, context = {}) => ({ + type: EMAIL_CHECK_SUCCESS, + payload: { redirectTo, email, errorCode, context }, +}); + +export const checkEmailFailure = errorMessage => ({ + type: EMAIL_CHECK_FAILURE, + payload: { errorMessage }, +}); + +export const emailCheckComplete = (email, errorCode = null) => ({ + type: EMAIL_CHECK_COMPLETE, + payload: { email, errorCode }, +}); + diff --git a/src/edly/data/constants.js b/src/edly/data/constants.js new file mode 100644 index 0000000000..b4cec2c774 --- /dev/null +++ b/src/edly/data/constants.js @@ -0,0 +1,13 @@ +// Action types +export const EMAIL_CHECK_REQUEST = 'EMAIL_CHECK_REQUEST'; +export const EMAIL_CHECK_SUCCESS = 'EMAIL_CHECK_SUCCESS'; +export const EMAIL_CHECK_FAILURE = 'EMAIL_CHECK_FAILURE'; +export const EMAIL_CHECK_COMPLETE = 'EMAIL_CHECK_COMPLETE'; + +// Store name +export const EDLY_STORE_NAME = 'edly'; + +// Error codes from backend +export const EMAIL_REGISTERED_DIFFERENT_ORG = 'email_registered_different_org'; +export const CROSS_TENANT_LOGIN = 'cross_tenant_login'; +export const REGISTRATION_REQUIRED_FOR_CURRENT_TENANT = 'registration_required_for_current_tenant'; diff --git a/src/edly/data/index.js b/src/edly/data/index.js new file mode 100644 index 0000000000..74c89def36 --- /dev/null +++ b/src/edly/data/index.js @@ -0,0 +1,4 @@ +export { default as reducer } from './reducers'; +export { default as saga } from './sagas'; +export { EDLY_STORE_NAME as storeName } from './constants'; +export * from './actions'; diff --git a/src/edly/data/reducers.js b/src/edly/data/reducers.js new file mode 100644 index 0000000000..a0412d2459 --- /dev/null +++ b/src/edly/data/reducers.js @@ -0,0 +1,61 @@ +import { + EMAIL_CHECK_REQUEST, + EMAIL_CHECK_SUCCESS, + EMAIL_CHECK_FAILURE, + EMAIL_CHECK_COMPLETE, +} from './constants'; +import { DEFAULT_STATE, PENDING_STATE } from '../../data/constants'; + +const initialState = { + email: '', + redirectTo: null, + message: null, + errorMessage: null, + submitState: DEFAULT_STATE, + showEmailCheck: true, + prefilledEmail: '', + errorCode: null, + context: {}, +}; + +const edlyReducer = (state = initialState, action) => { + switch (action.type) { + case EMAIL_CHECK_REQUEST: + return { + ...state, + email: action.payload.email, + submitState: PENDING_STATE, + errorMessage: null, + message: null, + }; + case EMAIL_CHECK_SUCCESS: + return { + ...state, + redirectTo: action.payload.redirectTo, + email: action.payload.email, + errorCode: action.payload.errorCode, + context: action.payload.context || {}, + submitState: DEFAULT_STATE, + errorMessage: null, + }; + case EMAIL_CHECK_FAILURE: + return { + ...state, + errorMessage: action.payload.errorMessage, + submitState: DEFAULT_STATE, + redirectTo: null, + message: null, + }; + case EMAIL_CHECK_COMPLETE: + return { + ...state, + showEmailCheck: false, + prefilledEmail: action.payload.email, + errorCode: action.payload.errorCode, + }; + default: + return state; + } +}; + +export default edlyReducer; diff --git a/src/edly/data/sagas.js b/src/edly/data/sagas.js new file mode 100644 index 0000000000..cc5552fcd6 --- /dev/null +++ b/src/edly/data/sagas.js @@ -0,0 +1,25 @@ +import { call, put, takeEvery } from 'redux-saga/effects'; + +import { checkEmailSuccess, checkEmailFailure } from './actions'; +import { EMAIL_CHECK_REQUEST } from './constants'; +import { checkEmailExists } from './service'; + +export function* handleEmailCheck(action) { + try { + const { email } = action.payload; + const result = yield call(checkEmailExists, email); + yield put(checkEmailSuccess(result.redirectTo, email, result.errorCode, result.context)); + } catch (error) { + const errorMessage = error?.response?.data?.error + || 'An error occurred. Please try again.'; + yield put(checkEmailFailure(errorMessage)); + } +} + +export function* watchEmailCheck() { + yield takeEvery(EMAIL_CHECK_REQUEST, handleEmailCheck); +} + +export default function* edlySaga() { + yield watchEmailCheck(); +} diff --git a/src/edly/data/service.js b/src/edly/data/service.js new file mode 100644 index 0000000000..18a3b9f880 --- /dev/null +++ b/src/edly/data/service.js @@ -0,0 +1,25 @@ +import { getConfig } from '@edx/frontend-platform'; +import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; + +export async function checkEmailExists(email) { + const requestConfig = { + headers: { 'Content-Type': 'application/json' }, + isPublic: true, + }; + + const { data } = await getAuthenticatedHttpClient() + .post( + `${getConfig().LMS_BASE_URL}/api/v1/user/validate-email/`, + { email }, + requestConfig, + ) + .catch((error) => { + throw error; + }); + + return { + redirectTo: data.redirect_to, + errorCode: data.error_code || null, + context: data.context || {}, + }; +} diff --git a/src/edly/index.js b/src/edly/index.js new file mode 100644 index 0000000000..dcae31e772 --- /dev/null +++ b/src/edly/index.js @@ -0,0 +1,3 @@ +export { default as EmailCheckPage } from './EmailCheckPage'; +export { default as EdlyAlert } from './EdlyAlert'; +export * from './data'; diff --git a/src/edly/messages.js b/src/edly/messages.js new file mode 100644 index 0000000000..c24a024066 --- /dev/null +++ b/src/edly/messages.js @@ -0,0 +1,37 @@ +import { defineMessages } from '@edx/frontend-platform/i18n'; +import registerMessages from '../register/messages.jsx'; +import progressiveProfilingMessages from '../progressive-profiling/messages.jsx'; + +const messages = defineMessages({ + 'email.check.heading': { + id: 'email.check.heading', + defaultMessage: 'Welcome! Let\'s get started', + description: 'Heading for email check page', + }, + 'email.check.subheading': { + id: 'email.check.subheading', + defaultMessage: 'Enter your email address to continue', + description: 'Subheading for email check page', + }, + 'email.check.button': progressiveProfilingMessages['optional.fields.next.button'], + 'email.validation.message': registerMessages['empty.email.field.error'], + 'email.format.validation.message': registerMessages['email.invalid.format.error'], + 'edly.email.registered.different.org': { + id: 'edly.email.registered.different.org', + defaultMessage: 'This email is already registered with another organization. Please use a different email address.', + description: 'Alert message when user email is registered with a different organization', + }, + 'edly.cross.tenant.login.enabled': { + id: 'edly.cross.tenant.login.enabled', + defaultMessage: 'You have already registered with this email on {registeredSite}. Please login to continue.', + description: 'Alert message when user can login across tenants in same organization', + }, + 'edly.registration.required.for.tenant': { + id: 'edly.registration.required.for.tenant', + defaultMessage: 'You have already registered with this email on {registeredSite}. Continuing will register you on this site with the same email address.', + description: 'Alert message when user needs to register for specific tenant', + }, +}); + +export default messages; + diff --git a/src/login/LoginPage.jsx b/src/login/LoginPage.jsx index 6281572675..342e5d1506 100644 --- a/src/login/LoginPage.jsx +++ b/src/login/LoginPage.jsx @@ -1,5 +1,5 @@ import React, { useEffect, useMemo, useState } from 'react'; -import { connect } from 'react-redux'; +import { connect, useSelector } from 'react-redux'; import { getConfig } from '@edx/frontend-platform'; import { sendPageEvent, sendTrackEvent } from '@edx/frontend-platform/analytics'; @@ -43,6 +43,7 @@ import { updatePathWithQueryParams, } from '../data/utils'; import ResetPasswordSuccess from '../reset-password/ResetPasswordSuccess'; +import { EdlyAlert } from '../edly'; const LoginPage = (props) => { const { @@ -72,7 +73,11 @@ const LoginPage = (props) => { const activationMsgType = getActivationStatus(); const queryParams = useMemo(() => getAllPossibleQueryParams(), []); - const [formFields, setFormFields] = useState({ ...backedUpFormData.formFields }); + const edlyPrefilledEmail = useSelector(state => state.edly.prefilledEmail); + const [formFields, setFormFields] = useState({ + ...backedUpFormData.formFields, + emailOrUsername: edlyPrefilledEmail || backedUpFormData.formFields.emailOrUsername, + }); const [errorCode, setErrorCode] = useState({ type: '', count: 0, context: {} }); const [errors, setErrors] = useState({ ...backedUpFormData.errors }); const tpaHint = getTpaHint(); @@ -223,11 +228,13 @@ const LoginPage = (props) => { messageType={activationMsgType} /> {showResetPasswordSuccessBanner && } + {!errorCode.type && }
{ - const { selectedPage, tpaProviders } = props; + const { selectedPage, tpaProviders, showEmailCheck } = props; const tpaHint = getTpaHint(); const { providers, secondaryProviders, @@ -96,6 +98,17 @@ const Logistration = (props) => { return !!provider; }; + const handleEmailCheckComplete = (redirectTo, email, errorCode) => { + props.emailCheckComplete(email, errorCode); + const targetPage = redirectTo === 'login' ? LOGIN_PAGE : REGISTER_PAGE; + if (selectedPage !== targetPage) { + navigate(updatePathWithQueryParams(targetPage)); + } + }; + + if (showEmailCheck && !tpaHint && !disablePublicAccountCreation) + return ; + return (
@@ -156,9 +169,11 @@ const Logistration = (props) => { Logistration.propTypes = { selectedPage: PropTypes.string, + showEmailCheck: PropTypes.bool.isRequired, backupLoginForm: PropTypes.func.isRequired, backupRegistrationForm: PropTypes.func.isRequired, clearThirdPartyAuthContextErrorMessage: PropTypes.func.isRequired, + emailCheckComplete: PropTypes.func.isRequired, tpaProviders: PropTypes.shape({ providers: PropTypes.arrayOf(PropTypes.shape({})), secondaryProviders: PropTypes.arrayOf(PropTypes.shape({})), @@ -178,6 +193,7 @@ Logistration.defaultProps = { const mapStateToProps = state => ({ tpaProviders: tpaProvidersSelector(state), + showEmailCheck: state.edly.showEmailCheck, }); export default connect( @@ -186,5 +202,6 @@ export default connect( backupLoginForm, backupRegistrationForm, clearThirdPartyAuthContextErrorMessage, + emailCheckComplete, }, )(Logistration); diff --git a/src/register/RegistrationPage.jsx b/src/register/RegistrationPage.jsx index 3230677080..b463395e88 100644 --- a/src/register/RegistrationPage.jsx +++ b/src/register/RegistrationPage.jsx @@ -12,6 +12,7 @@ import PropTypes from 'prop-types'; import { Helmet } from 'react-helmet'; import Skeleton from 'react-loading-skeleton'; +import { EdlyAlert } from '../edly'; import ConfigurableRegistrationForm from './components/ConfigurableRegistrationForm'; import RegistrationFailure from './components/RegistrationFailure'; import { @@ -307,6 +308,7 @@ const RegistrationPage = (props) => { failureCount={errorCode.count} context={{ provider: currentProvider, errorMessage: thirdPartyAuthErrorMessage }} /> + {!errorCode.type && } Date: Wed, 10 Dec 2025 17:48:20 +0500 Subject: [PATCH 2/5] refactor: simplify alert message rendering in EdlyAlert component - Updated EdlyAlert to directly render formatted messages without wrapping them in

tags initially. - Ensured consistent rendering by wrapping the final alert message in a

tag within the Alert component. --- src/edly/EdlyAlert.jsx | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/edly/EdlyAlert.jsx b/src/edly/EdlyAlert.jsx index 718f6e0efc..174ee584ef 100644 --- a/src/edly/EdlyAlert.jsx +++ b/src/edly/EdlyAlert.jsx @@ -24,25 +24,17 @@ const EdlyAlert = () => { let alertMessage; switch (errorCode) { case EMAIL_REGISTERED_DIFFERENT_ORG: - alertMessage =

{formatMessage(messages['edly.email.registered.different.org'])}

; + alertMessage = formatMessage(messages['edly.email.registered.different.org']); break; case CROSS_TENANT_LOGIN: - alertMessage = ( -

- {formatMessage(messages['edly.cross.tenant.login.enabled'], { - registeredSite: context.registered_site || 'a partner site', - })} -

- ); + alertMessage = formatMessage(messages['edly.cross.tenant.login.enabled'], { + registeredSite: context.registered_site || 'a partner site', + }); break; case REGISTRATION_REQUIRED_FOR_CURRENT_TENANT: - alertMessage = ( -

- {formatMessage(messages['edly.registration.required.for.tenant'], { - registeredSite: context.registered_site || 'a partner site', - })} -

- ); + alertMessage = formatMessage(messages['edly.registration.required.for.tenant'], { + registeredSite: context.registered_site || 'a partner site', + }); break; default: return null; @@ -50,7 +42,7 @@ const EdlyAlert = () => { return ( - {alertMessage} +

{alertMessage}

); }; From 9699b2e51278009cd4be8c88720b3425ce9dd8e5 Mon Sep 17 00:00:00 2001 From: "waleed.mujahid" Date: Wed, 10 Dec 2025 17:58:55 +0500 Subject: [PATCH 3/5] chore: add quality fixes --- src/edly/EdlyAlert.jsx | 3 +-- src/edly/EmailCheckPage.jsx | 3 +-- src/edly/data/actions.js | 9 +++++---- src/edly/data/reducers.js | 6 +++--- src/edly/data/sagas.js | 2 +- src/edly/messages.js | 6 +++--- src/login/LoginPage.jsx | 6 ++++-- src/logistration/Logistration.jsx | 4 ++-- 8 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/edly/EdlyAlert.jsx b/src/edly/EdlyAlert.jsx index 174ee584ef..48da13a840 100644 --- a/src/edly/EdlyAlert.jsx +++ b/src/edly/EdlyAlert.jsx @@ -6,8 +6,8 @@ import { Alert } from '@openedx/paragon'; import { Info } from '@openedx/paragon/icons'; import { - EMAIL_REGISTERED_DIFFERENT_ORG, CROSS_TENANT_LOGIN, + EMAIL_REGISTERED_DIFFERENT_ORG, REGISTRATION_REQUIRED_FOR_CURRENT_TENANT, } from './data/constants'; import messages from './messages'; @@ -48,4 +48,3 @@ const EdlyAlert = () => { }; export default EdlyAlert; - diff --git a/src/edly/EmailCheckPage.jsx b/src/edly/EmailCheckPage.jsx index 83dc17fc0f..79ff14a231 100644 --- a/src/edly/EmailCheckPage.jsx +++ b/src/edly/EmailCheckPage.jsx @@ -5,8 +5,8 @@ import { useIntl } from '@edx/frontend-platform/i18n'; import { Alert, Form, StatefulButton } from '@openedx/paragon'; import { Info } from '@openedx/paragon/icons'; import PropTypes from 'prop-types'; -import BaseContainer from '../base-container'; +import BaseContainer from '../base-container'; import { FormGroup } from '../common-components'; import { VALID_EMAIL_REGEX } from '../data/constants'; import { checkEmailRequest } from './data/actions'; @@ -122,4 +122,3 @@ EmailCheckPage.propTypes = { }; export default EmailCheckPage; - diff --git a/src/edly/data/actions.js b/src/edly/data/actions.js index 6e83240a25..6ec3aeb22a 100644 --- a/src/edly/data/actions.js +++ b/src/edly/data/actions.js @@ -1,8 +1,8 @@ import { + EMAIL_CHECK_COMPLETE, + EMAIL_CHECK_FAILURE, EMAIL_CHECK_REQUEST, EMAIL_CHECK_SUCCESS, - EMAIL_CHECK_FAILURE, - EMAIL_CHECK_COMPLETE, } from './constants'; export const checkEmailRequest = email => ({ @@ -12,7 +12,9 @@ export const checkEmailRequest = email => ({ export const checkEmailSuccess = (redirectTo, email, errorCode = null, context = {}) => ({ type: EMAIL_CHECK_SUCCESS, - payload: { redirectTo, email, errorCode, context }, + payload: { + redirectTo, email, errorCode, context, + }, }); export const checkEmailFailure = errorMessage => ({ @@ -24,4 +26,3 @@ export const emailCheckComplete = (email, errorCode = null) => ({ type: EMAIL_CHECK_COMPLETE, payload: { email, errorCode }, }); - diff --git a/src/edly/data/reducers.js b/src/edly/data/reducers.js index a0412d2459..faf388fa14 100644 --- a/src/edly/data/reducers.js +++ b/src/edly/data/reducers.js @@ -1,8 +1,8 @@ import { + EMAIL_CHECK_COMPLETE, + EMAIL_CHECK_FAILURE, EMAIL_CHECK_REQUEST, EMAIL_CHECK_SUCCESS, - EMAIL_CHECK_FAILURE, - EMAIL_CHECK_COMPLETE, } from './constants'; import { DEFAULT_STATE, PENDING_STATE } from '../../data/constants'; @@ -18,7 +18,7 @@ const initialState = { context: {}, }; -const edlyReducer = (state = initialState, action) => { +const edlyReducer = (state = initialState, action = {}) => { switch (action.type) { case EMAIL_CHECK_REQUEST: return { diff --git a/src/edly/data/sagas.js b/src/edly/data/sagas.js index cc5552fcd6..1b98a7149b 100644 --- a/src/edly/data/sagas.js +++ b/src/edly/data/sagas.js @@ -1,6 +1,6 @@ import { call, put, takeEvery } from 'redux-saga/effects'; -import { checkEmailSuccess, checkEmailFailure } from './actions'; +import { checkEmailFailure, checkEmailSuccess } from './actions'; import { EMAIL_CHECK_REQUEST } from './constants'; import { checkEmailExists } from './service'; diff --git a/src/edly/messages.js b/src/edly/messages.js index c24a024066..c309df42bc 100644 --- a/src/edly/messages.js +++ b/src/edly/messages.js @@ -1,6 +1,7 @@ import { defineMessages } from '@edx/frontend-platform/i18n'; -import registerMessages from '../register/messages.jsx'; -import progressiveProfilingMessages from '../progressive-profiling/messages.jsx'; + +import progressiveProfilingMessages from '../progressive-profiling/messages'; +import registerMessages from '../register/messages'; const messages = defineMessages({ 'email.check.heading': { @@ -34,4 +35,3 @@ const messages = defineMessages({ }); export default messages; - diff --git a/src/login/LoginPage.jsx b/src/login/LoginPage.jsx index 342e5d1506..df0aac9500 100644 --- a/src/login/LoginPage.jsx +++ b/src/login/LoginPage.jsx @@ -42,8 +42,8 @@ import { getTpaProvider, updatePathWithQueryParams, } from '../data/utils'; -import ResetPasswordSuccess from '../reset-password/ResetPasswordSuccess'; import { EdlyAlert } from '../edly'; +import ResetPasswordSuccess from '../reset-password/ResetPasswordSuccess'; const LoginPage = (props) => { const { @@ -305,7 +305,9 @@ const mapStateToProps = state => { LoginPage.propTypes = { backedUpFormData: PropTypes.shape({ - formFields: PropTypes.shape({}), + formFields: PropTypes.shape({ + emailOrUsername: PropTypes.string, + }), errors: PropTypes.shape({}), }), loginErrorCode: PropTypes.string, diff --git a/src/logistration/Logistration.jsx b/src/logistration/Logistration.jsx index c39498d8de..1c8e0755de 100644 --- a/src/logistration/Logistration.jsx +++ b/src/logistration/Logistration.jsx @@ -105,9 +105,9 @@ const Logistration = (props) => { navigate(updatePathWithQueryParams(targetPage)); } }; - - if (showEmailCheck && !tpaHint && !disablePublicAccountCreation) + if (showEmailCheck && !tpaHint && !disablePublicAccountCreation) { return ; + } return ( From ae1d766012b0cffa74226f3cf8fcf0950b382f70 Mon Sep 17 00:00:00 2001 From: "waleed.mujahid" Date: Mon, 15 Dec 2025 18:55:27 +0500 Subject: [PATCH 4/5] refactor: move components into edly-saas-widgets --- src/data/reducers.js | 4 +- src/data/sagas.js | 4 +- src/edly/EdlyAlert.jsx | 50 ------------ src/edly/EmailCheckPage.jsx | 124 ------------------------------ src/edly/data/actions.js | 28 ------- src/edly/data/constants.js | 13 ---- src/edly/data/index.js | 4 - src/edly/data/reducers.js | 61 --------------- src/edly/data/sagas.js | 25 ------ src/edly/data/service.js | 25 ------ src/edly/index.js | 3 - src/edly/messages.js | 37 --------- src/login/LoginPage.jsx | 6 +- src/logistration/Logistration.jsx | 19 +++-- src/register/RegistrationPage.jsx | 4 +- 15 files changed, 23 insertions(+), 384 deletions(-) delete mode 100644 src/edly/EdlyAlert.jsx delete mode 100644 src/edly/EmailCheckPage.jsx delete mode 100644 src/edly/data/actions.js delete mode 100644 src/edly/data/constants.js delete mode 100644 src/edly/data/index.js delete mode 100644 src/edly/data/reducers.js delete mode 100644 src/edly/data/sagas.js delete mode 100644 src/edly/data/service.js delete mode 100644 src/edly/index.js delete mode 100644 src/edly/messages.js diff --git a/src/data/reducers.js b/src/data/reducers.js index 4fcc08552a..6678c4d7a8 100755 --- a/src/data/reducers.js +++ b/src/data/reducers.js @@ -4,7 +4,7 @@ import { reducer as commonComponentsReducer, storeName as commonComponentsStoreName, } from '../common-components'; -import { reducer as edlyReducer, storeName as edlyStoreName } from '../edly'; +import { reducer as emailCheckReducer, storeName as emailCheckStoreName } from '@anas_hameed/edly-saas-widget'; import { reducer as forgotPasswordReducer, storeName as forgotPasswordStoreName, @@ -30,7 +30,7 @@ const createRootReducer = () => combineReducers({ [loginStoreName]: loginReducer, [registerStoreName]: registerReducer, [commonComponentsStoreName]: commonComponentsReducer, - [edlyStoreName]: edlyReducer, + [emailCheckStoreName]: emailCheckReducer, [forgotPasswordStoreName]: forgotPasswordReducer, [resetPasswordStoreName]: resetPasswordReducer, [authnProgressiveProfilingStoreName]: authnProgressiveProfilingReducers, diff --git a/src/data/sagas.js b/src/data/sagas.js index f1ea80ebf9..11ea3a2562 100644 --- a/src/data/sagas.js +++ b/src/data/sagas.js @@ -1,7 +1,7 @@ import { all } from 'redux-saga/effects'; import { saga as commonComponentsSaga } from '../common-components'; -import { saga as edlySaga } from '../edly'; +import { saga as emailCheckSaga } from '@anas_hameed/edly-saas-widget'; import { saga as forgotPasswordSaga } from '../forgot-password'; import { saga as loginSaga } from '../login'; import { saga as authnProgressiveProfilingSaga } from '../progressive-profiling'; @@ -13,7 +13,7 @@ export default function* rootSaga() { loginSaga(), registrationSaga(), commonComponentsSaga(), - edlySaga(), + emailCheckSaga(), forgotPasswordSaga(), resetPasswordSaga(), authnProgressiveProfilingSaga(), diff --git a/src/edly/EdlyAlert.jsx b/src/edly/EdlyAlert.jsx deleted file mode 100644 index 48da13a840..0000000000 --- a/src/edly/EdlyAlert.jsx +++ /dev/null @@ -1,50 +0,0 @@ -import React from 'react'; -import { useSelector } from 'react-redux'; - -import { useIntl } from '@edx/frontend-platform/i18n'; -import { Alert } from '@openedx/paragon'; -import { Info } from '@openedx/paragon/icons'; - -import { - CROSS_TENANT_LOGIN, - EMAIL_REGISTERED_DIFFERENT_ORG, - REGISTRATION_REQUIRED_FOR_CURRENT_TENANT, -} from './data/constants'; -import messages from './messages'; - -const EdlyAlert = () => { - const { formatMessage } = useIntl(); - const errorCode = useSelector(state => state.edly.errorCode); - const context = useSelector(state => state.edly.context); - - if (!errorCode) { - return null; - } - - let alertMessage; - switch (errorCode) { - case EMAIL_REGISTERED_DIFFERENT_ORG: - alertMessage = formatMessage(messages['edly.email.registered.different.org']); - break; - case CROSS_TENANT_LOGIN: - alertMessage = formatMessage(messages['edly.cross.tenant.login.enabled'], { - registeredSite: context.registered_site || 'a partner site', - }); - break; - case REGISTRATION_REQUIRED_FOR_CURRENT_TENANT: - alertMessage = formatMessage(messages['edly.registration.required.for.tenant'], { - registeredSite: context.registered_site || 'a partner site', - }); - break; - default: - return null; - } - - return ( - -

{alertMessage}

-
- ); -}; - -export default EdlyAlert; diff --git a/src/edly/EmailCheckPage.jsx b/src/edly/EmailCheckPage.jsx deleted file mode 100644 index 79ff14a231..0000000000 --- a/src/edly/EmailCheckPage.jsx +++ /dev/null @@ -1,124 +0,0 @@ -import React, { useEffect, useRef, useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; - -import { useIntl } from '@edx/frontend-platform/i18n'; -import { Alert, Form, StatefulButton } from '@openedx/paragon'; -import { Info } from '@openedx/paragon/icons'; -import PropTypes from 'prop-types'; - -import BaseContainer from '../base-container'; -import { FormGroup } from '../common-components'; -import { VALID_EMAIL_REGEX } from '../data/constants'; -import { checkEmailRequest } from './data/actions'; -import messages from './messages'; - -const EmailCheckPage = ({ onEmailCheckComplete }) => { - const { formatMessage } = useIntl(); - const dispatch = useDispatch(); - - const [email, setEmail] = useState(''); - const [emailError, setEmailError] = useState(''); - const hasCompletedRef = useRef(false); - - const submitState = useSelector(state => state.edly.submitState); - const redirectTo = useSelector(state => state.edly.redirectTo); - const checkedEmail = useSelector(state => state.edly.email); - const errorCode = useSelector(state => state.edly.errorCode); - const errorMessage = useSelector(state => state.edly.errorMessage); - - useEffect(() => { - if (redirectTo && checkedEmail && !hasCompletedRef.current) { - hasCompletedRef.current = true; - onEmailCheckComplete(redirectTo, checkedEmail, errorCode); - } - }, [redirectTo, checkedEmail, errorCode, onEmailCheckComplete]); - - const validateEmail = (emailValue) => { - if (!emailValue) { - return formatMessage(messages['email.validation.message']); - } - - const emailRegex = new RegExp(VALID_EMAIL_REGEX, 'i'); - if (!emailRegex.test(emailValue)) { - return formatMessage(messages['email.format.validation.message']); - } - - return ''; - }; - - const handleSubmit = (e) => { - e.preventDefault(); - - const error = validateEmail(email); - if (error) { - setEmailError(error); - return; - } - - dispatch(checkEmailRequest(email)); - }; - - const handleOnChange = (e) => { - const { value } = e.target; - setEmail(value); - if (emailError) { - setEmailError(''); - } - }; - - const handleOnFocus = () => { - if (emailError) { - setEmailError(''); - } - }; - - return ( - -
-
-

{formatMessage(messages['email.check.heading'])}

-

{formatMessage(messages['email.check.subheading'])}

- - {errorMessage && ( - - {errorMessage} - - )} - - - - e.preventDefault()} - /> - -
-
-
- ); -}; - -EmailCheckPage.propTypes = { - onEmailCheckComplete: PropTypes.func.isRequired, -}; - -export default EmailCheckPage; diff --git a/src/edly/data/actions.js b/src/edly/data/actions.js deleted file mode 100644 index 6ec3aeb22a..0000000000 --- a/src/edly/data/actions.js +++ /dev/null @@ -1,28 +0,0 @@ -import { - EMAIL_CHECK_COMPLETE, - EMAIL_CHECK_FAILURE, - EMAIL_CHECK_REQUEST, - EMAIL_CHECK_SUCCESS, -} from './constants'; - -export const checkEmailRequest = email => ({ - type: EMAIL_CHECK_REQUEST, - payload: { email }, -}); - -export const checkEmailSuccess = (redirectTo, email, errorCode = null, context = {}) => ({ - type: EMAIL_CHECK_SUCCESS, - payload: { - redirectTo, email, errorCode, context, - }, -}); - -export const checkEmailFailure = errorMessage => ({ - type: EMAIL_CHECK_FAILURE, - payload: { errorMessage }, -}); - -export const emailCheckComplete = (email, errorCode = null) => ({ - type: EMAIL_CHECK_COMPLETE, - payload: { email, errorCode }, -}); diff --git a/src/edly/data/constants.js b/src/edly/data/constants.js deleted file mode 100644 index b4cec2c774..0000000000 --- a/src/edly/data/constants.js +++ /dev/null @@ -1,13 +0,0 @@ -// Action types -export const EMAIL_CHECK_REQUEST = 'EMAIL_CHECK_REQUEST'; -export const EMAIL_CHECK_SUCCESS = 'EMAIL_CHECK_SUCCESS'; -export const EMAIL_CHECK_FAILURE = 'EMAIL_CHECK_FAILURE'; -export const EMAIL_CHECK_COMPLETE = 'EMAIL_CHECK_COMPLETE'; - -// Store name -export const EDLY_STORE_NAME = 'edly'; - -// Error codes from backend -export const EMAIL_REGISTERED_DIFFERENT_ORG = 'email_registered_different_org'; -export const CROSS_TENANT_LOGIN = 'cross_tenant_login'; -export const REGISTRATION_REQUIRED_FOR_CURRENT_TENANT = 'registration_required_for_current_tenant'; diff --git a/src/edly/data/index.js b/src/edly/data/index.js deleted file mode 100644 index 74c89def36..0000000000 --- a/src/edly/data/index.js +++ /dev/null @@ -1,4 +0,0 @@ -export { default as reducer } from './reducers'; -export { default as saga } from './sagas'; -export { EDLY_STORE_NAME as storeName } from './constants'; -export * from './actions'; diff --git a/src/edly/data/reducers.js b/src/edly/data/reducers.js deleted file mode 100644 index faf388fa14..0000000000 --- a/src/edly/data/reducers.js +++ /dev/null @@ -1,61 +0,0 @@ -import { - EMAIL_CHECK_COMPLETE, - EMAIL_CHECK_FAILURE, - EMAIL_CHECK_REQUEST, - EMAIL_CHECK_SUCCESS, -} from './constants'; -import { DEFAULT_STATE, PENDING_STATE } from '../../data/constants'; - -const initialState = { - email: '', - redirectTo: null, - message: null, - errorMessage: null, - submitState: DEFAULT_STATE, - showEmailCheck: true, - prefilledEmail: '', - errorCode: null, - context: {}, -}; - -const edlyReducer = (state = initialState, action = {}) => { - switch (action.type) { - case EMAIL_CHECK_REQUEST: - return { - ...state, - email: action.payload.email, - submitState: PENDING_STATE, - errorMessage: null, - message: null, - }; - case EMAIL_CHECK_SUCCESS: - return { - ...state, - redirectTo: action.payload.redirectTo, - email: action.payload.email, - errorCode: action.payload.errorCode, - context: action.payload.context || {}, - submitState: DEFAULT_STATE, - errorMessage: null, - }; - case EMAIL_CHECK_FAILURE: - return { - ...state, - errorMessage: action.payload.errorMessage, - submitState: DEFAULT_STATE, - redirectTo: null, - message: null, - }; - case EMAIL_CHECK_COMPLETE: - return { - ...state, - showEmailCheck: false, - prefilledEmail: action.payload.email, - errorCode: action.payload.errorCode, - }; - default: - return state; - } -}; - -export default edlyReducer; diff --git a/src/edly/data/sagas.js b/src/edly/data/sagas.js deleted file mode 100644 index 1b98a7149b..0000000000 --- a/src/edly/data/sagas.js +++ /dev/null @@ -1,25 +0,0 @@ -import { call, put, takeEvery } from 'redux-saga/effects'; - -import { checkEmailFailure, checkEmailSuccess } from './actions'; -import { EMAIL_CHECK_REQUEST } from './constants'; -import { checkEmailExists } from './service'; - -export function* handleEmailCheck(action) { - try { - const { email } = action.payload; - const result = yield call(checkEmailExists, email); - yield put(checkEmailSuccess(result.redirectTo, email, result.errorCode, result.context)); - } catch (error) { - const errorMessage = error?.response?.data?.error - || 'An error occurred. Please try again.'; - yield put(checkEmailFailure(errorMessage)); - } -} - -export function* watchEmailCheck() { - yield takeEvery(EMAIL_CHECK_REQUEST, handleEmailCheck); -} - -export default function* edlySaga() { - yield watchEmailCheck(); -} diff --git a/src/edly/data/service.js b/src/edly/data/service.js deleted file mode 100644 index 18a3b9f880..0000000000 --- a/src/edly/data/service.js +++ /dev/null @@ -1,25 +0,0 @@ -import { getConfig } from '@edx/frontend-platform'; -import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; - -export async function checkEmailExists(email) { - const requestConfig = { - headers: { 'Content-Type': 'application/json' }, - isPublic: true, - }; - - const { data } = await getAuthenticatedHttpClient() - .post( - `${getConfig().LMS_BASE_URL}/api/v1/user/validate-email/`, - { email }, - requestConfig, - ) - .catch((error) => { - throw error; - }); - - return { - redirectTo: data.redirect_to, - errorCode: data.error_code || null, - context: data.context || {}, - }; -} diff --git a/src/edly/index.js b/src/edly/index.js deleted file mode 100644 index dcae31e772..0000000000 --- a/src/edly/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export { default as EmailCheckPage } from './EmailCheckPage'; -export { default as EdlyAlert } from './EdlyAlert'; -export * from './data'; diff --git a/src/edly/messages.js b/src/edly/messages.js deleted file mode 100644 index c309df42bc..0000000000 --- a/src/edly/messages.js +++ /dev/null @@ -1,37 +0,0 @@ -import { defineMessages } from '@edx/frontend-platform/i18n'; - -import progressiveProfilingMessages from '../progressive-profiling/messages'; -import registerMessages from '../register/messages'; - -const messages = defineMessages({ - 'email.check.heading': { - id: 'email.check.heading', - defaultMessage: 'Welcome! Let\'s get started', - description: 'Heading for email check page', - }, - 'email.check.subheading': { - id: 'email.check.subheading', - defaultMessage: 'Enter your email address to continue', - description: 'Subheading for email check page', - }, - 'email.check.button': progressiveProfilingMessages['optional.fields.next.button'], - 'email.validation.message': registerMessages['empty.email.field.error'], - 'email.format.validation.message': registerMessages['email.invalid.format.error'], - 'edly.email.registered.different.org': { - id: 'edly.email.registered.different.org', - defaultMessage: 'This email is already registered with another organization. Please use a different email address.', - description: 'Alert message when user email is registered with a different organization', - }, - 'edly.cross.tenant.login.enabled': { - id: 'edly.cross.tenant.login.enabled', - defaultMessage: 'You have already registered with this email on {registeredSite}. Please login to continue.', - description: 'Alert message when user can login across tenants in same organization', - }, - 'edly.registration.required.for.tenant': { - id: 'edly.registration.required.for.tenant', - defaultMessage: 'You have already registered with this email on {registeredSite}. Continuing will register you on this site with the same email address.', - description: 'Alert message when user needs to register for specific tenant', - }, -}); - -export default messages; diff --git a/src/login/LoginPage.jsx b/src/login/LoginPage.jsx index df0aac9500..41dde9cf82 100644 --- a/src/login/LoginPage.jsx +++ b/src/login/LoginPage.jsx @@ -42,7 +42,7 @@ import { getTpaProvider, updatePathWithQueryParams, } from '../data/utils'; -import { EdlyAlert } from '../edly'; +import { EdlyLogistrationInfo } from '@anas_hameed/edly-saas-widget'; import ResetPasswordSuccess from '../reset-password/ResetPasswordSuccess'; const LoginPage = (props) => { @@ -73,7 +73,7 @@ const LoginPage = (props) => { const activationMsgType = getActivationStatus(); const queryParams = useMemo(() => getAllPossibleQueryParams(), []); - const edlyPrefilledEmail = useSelector(state => state.edly.prefilledEmail); + const edlyPrefilledEmail = useSelector(state => state.emailCheck?.prefilledEmail); const [formFields, setFormFields] = useState({ ...backedUpFormData.formFields, emailOrUsername: edlyPrefilledEmail || backedUpFormData.formFields.emailOrUsername, @@ -228,7 +228,7 @@ const LoginPage = (props) => { messageType={activationMsgType} /> {showResetPasswordSuccessBanner && } - {!errorCode.type && } + {!errorCode.type && }
{ } }; if (showEmailCheck && !tpaHint && !disablePublicAccountCreation) { - return ; + return ( + + ); } return ( @@ -193,7 +202,7 @@ Logistration.defaultProps = { const mapStateToProps = state => ({ tpaProviders: tpaProvidersSelector(state), - showEmailCheck: state.edly.showEmailCheck, + showEmailCheck: state.emailCheck?.showEmailCheck, }); export default connect( diff --git a/src/register/RegistrationPage.jsx b/src/register/RegistrationPage.jsx index b463395e88..520387e827 100644 --- a/src/register/RegistrationPage.jsx +++ b/src/register/RegistrationPage.jsx @@ -12,7 +12,7 @@ import PropTypes from 'prop-types'; import { Helmet } from 'react-helmet'; import Skeleton from 'react-loading-skeleton'; -import { EdlyAlert } from '../edly'; +import { EdlyLogistrationInfo } from '@anas_hameed/edly-saas-widget'; import ConfigurableRegistrationForm from './components/ConfigurableRegistrationForm'; import RegistrationFailure from './components/RegistrationFailure'; import { @@ -308,7 +308,7 @@ const RegistrationPage = (props) => { failureCount={errorCode.count} context={{ provider: currentProvider, errorMessage: thirdPartyAuthErrorMessage }} /> - {!errorCode.type && } + {!errorCode.type && } Date: Mon, 15 Dec 2025 19:33:36 +0500 Subject: [PATCH 5/5] refactor: add quality changes --- src/data/reducers.js | 2 +- src/data/sagas.js | 2 +- src/login/LoginPage.jsx | 2 +- src/logistration/Logistration.jsx | 10 ++++------ src/register/RegistrationPage.jsx | 2 +- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/data/reducers.js b/src/data/reducers.js index 6678c4d7a8..b54c665b16 100755 --- a/src/data/reducers.js +++ b/src/data/reducers.js @@ -1,10 +1,10 @@ +import { emailCheckReducer, emailCheckStoreName } from '@anas_hameed/edly-saas-widget'; import { combineReducers } from 'redux'; import { reducer as commonComponentsReducer, storeName as commonComponentsStoreName, } from '../common-components'; -import { reducer as emailCheckReducer, storeName as emailCheckStoreName } from '@anas_hameed/edly-saas-widget'; import { reducer as forgotPasswordReducer, storeName as forgotPasswordStoreName, diff --git a/src/data/sagas.js b/src/data/sagas.js index 11ea3a2562..9708eca8b1 100644 --- a/src/data/sagas.js +++ b/src/data/sagas.js @@ -1,7 +1,7 @@ +import { emailCheckSaga } from '@anas_hameed/edly-saas-widget'; import { all } from 'redux-saga/effects'; import { saga as commonComponentsSaga } from '../common-components'; -import { saga as emailCheckSaga } from '@anas_hameed/edly-saas-widget'; import { saga as forgotPasswordSaga } from '../forgot-password'; import { saga as loginSaga } from '../login'; import { saga as authnProgressiveProfilingSaga } from '../progressive-profiling'; diff --git a/src/login/LoginPage.jsx b/src/login/LoginPage.jsx index 41dde9cf82..610ab7c366 100644 --- a/src/login/LoginPage.jsx +++ b/src/login/LoginPage.jsx @@ -1,6 +1,7 @@ import React, { useEffect, useMemo, useState } from 'react'; import { connect, useSelector } from 'react-redux'; +import { EdlyLogistrationInfo } from '@anas_hameed/edly-saas-widget'; import { getConfig } from '@edx/frontend-platform'; import { sendPageEvent, sendTrackEvent } from '@edx/frontend-platform/analytics'; import { injectIntl, useIntl } from '@edx/frontend-platform/i18n'; @@ -42,7 +43,6 @@ import { getTpaProvider, updatePathWithQueryParams, } from '../data/utils'; -import { EdlyLogistrationInfo } from '@anas_hameed/edly-saas-widget'; import ResetPasswordSuccess from '../reset-password/ResetPasswordSuccess'; const LoginPage = (props) => { diff --git a/src/logistration/Logistration.jsx b/src/logistration/Logistration.jsx index fede53b69a..9a85aefe51 100644 --- a/src/logistration/Logistration.jsx +++ b/src/logistration/Logistration.jsx @@ -1,6 +1,7 @@ import React, { useEffect, useState } from 'react'; import { connect } from 'react-redux'; +import { emailCheckComplete, EmailCheckWidget } from '@anas_hameed/edly-saas-widget'; import { getConfig } from '@edx/frontend-platform'; import { sendPageEvent, sendTrackEvent } from '@edx/frontend-platform/analytics'; import { getAuthService } from '@edx/frontend-platform/auth'; @@ -14,20 +15,17 @@ import { ChevronLeft } from '@openedx/paragon/icons'; import PropTypes from 'prop-types'; import { Navigate, useNavigate } from 'react-router-dom'; +import BaseContainer from '../base-container'; +import { FormGroup } from '../common-components'; import { clearThirdPartyAuthContextErrorMessage } from '../common-components/data/actions'; import { tpaProvidersSelector, } from '../common-components/data/selectors'; import messages from '../common-components/messages'; -import { LOGIN_PAGE, REGISTER_PAGE } from '../data/constants'; +import { LOGIN_PAGE, REGISTER_PAGE, VALID_EMAIL_REGEX } from '../data/constants'; import { getTpaHint, getTpaProvider, updatePathWithQueryParams, } from '../data/utils'; -import { EmailCheckWidget } from '@anas_hameed/edly-saas-widget'; -import { emailCheckComplete } from '@anas_hameed/edly-saas-widget/src/EmailCheckWidget'; -import BaseContainer from '../base-container'; -import { FormGroup } from '../common-components'; -import { VALID_EMAIL_REGEX } from '../data/constants'; import { LoginPage } from '../login'; import { backupLoginForm } from '../login/data/actions'; import { RegistrationPage } from '../register'; diff --git a/src/register/RegistrationPage.jsx b/src/register/RegistrationPage.jsx index 520387e827..29bda79ddf 100644 --- a/src/register/RegistrationPage.jsx +++ b/src/register/RegistrationPage.jsx @@ -3,6 +3,7 @@ import React, { } from 'react'; import { useDispatch, useSelector } from 'react-redux'; +import { EdlyLogistrationInfo } from '@anas_hameed/edly-saas-widget'; import { getConfig } from '@edx/frontend-platform'; import { sendPageEvent, sendTrackEvent } from '@edx/frontend-platform/analytics'; import { useIntl } from '@edx/frontend-platform/i18n'; @@ -12,7 +13,6 @@ import PropTypes from 'prop-types'; import { Helmet } from 'react-helmet'; import Skeleton from 'react-loading-skeleton'; -import { EdlyLogistrationInfo } from '@anas_hameed/edly-saas-widget'; import ConfigurableRegistrationForm from './components/ConfigurableRegistrationForm'; import RegistrationFailure from './components/RegistrationFailure'; import {