import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import {
Input,
StatefulButton,
Hyperlink,
ValidationFormGroup,
} from '@edx/paragon';
import {
injectIntl, intlShape,
} from '@edx/frontend-platform/i18n';
import camelCase from 'lodash.camelcase';
import {
getThirdPartyAuthContext,
registerNewUser,
fetchRegistrationForm,
fetchRealtimeValidations,
} from './data/actions';
import { registrationRequestSelector, thirdPartyAuthContextSelector } from './data/selectors';
import { RedirectLogistration } from '../common-components';
import RegistrationFailure from './RegistrationFailure';
import {
DEFAULT_REDIRECT_URL,
DEFAULT_STATE,
LOGIN_PAGE,
REGISTER_PAGE,
REGISTRATION_VALIDITY_MAP,
REGISTRATION_OPTIONAL_MAP,
REGISTRATION_EXTRA_FIELDS,
} from '../data/constants';
import SocialAuthProviders from './SocialAuthProviders';
import ThirdPartyAuthAlert from './ThirdPartyAuthAlert';
import InstitutionLogistration, { RenderInstitutionButton } from './InstitutionLogistration';
import messages from './messages';
import { processLink } from '../data/utils/dataUtils';
class RegistrationPage extends React.Component {
constructor(props, context) {
super(props, context);
this.intl = props.intl;
this.state = {
email: '',
name: '',
username: '',
password: '',
country: '',
city: '',
gender: '',
yearOfBirth: '',
mailingAddress: '',
goals: '',
honorCode: true,
termsOfService: true,
levelOfEducation: '',
confirmEmail: '',
enableOptionalField: false,
validationFieldName: '',
errors: {
email: '',
name: '',
username: '',
password: '',
country: '',
honorCode: '',
termsOfService: '',
},
emailValid: false,
nameValid: false,
usernameValid: false,
passwordValid: false,
countryValid: false,
honorCodeValid: true,
termsOfServiceValid: false,
formValid: false,
institutionLogin: false,
};
}
componentDidMount() {
const params = (new URL(document.location)).searchParams;
const payload = {
redirect_to: params.get('next') || DEFAULT_REDIRECT_URL,
};
this.props.getThirdPartyAuthContext(payload);
this.props.fetchRegistrationForm();
}
shouldComponentUpdate(nextProps) {
if (this.props.validations !== nextProps.validations) {
const { errors } = this.state;
const errorMsg = nextProps.validations.validation_decisions[this.state.validationFieldName];
errors[this.state.validationFieldName] = errorMsg;
this.setState({
errors,
});
return false;
}
return true;
}
handleInstitutionLogin = () => {
this.setState(prevState => ({ institutionLogin: !prevState.institutionLogin }));
}
handleSubmit = (e) => {
e.preventDefault();
const params = (new URL(document.location)).searchParams;
const payload = {
email: this.state.email,
username: this.state.username,
password: this.state.password,
name: this.state.name,
};
const fieldMap = { ...REGISTRATION_VALIDITY_MAP, ...REGISTRATION_OPTIONAL_MAP };
Object.entries(fieldMap).forEach(([key, value]) => {
if (value) {
payload[key] = this.state[camelCase(key)];
}
});
const next = params.get('next');
const courseId = params.get('course_id');
if (next) {
payload.next = params.next;
}
if (courseId) {
payload.course_id = params.course_id;
}
if (!this.state.formValid) {
// Special case where honor code and tos is a single field, true by default. We don't need
// to validate this field
Object.entries(payload).filter(([key]) => (key !== 'honor_code' || !('terms_of_service' in REGISTRATION_EXTRA_FIELDS)))
.forEach(([key, value]) => {
this.validateInput(key, value);
});
return;
}
this.props.registerNewUser(payload);
}
handleOnBlur(e) {
this.setState({
validationFieldName: e.target.name,
});
const payload = {
email: this.state.email,
username: this.state.username,
password: this.state.password,
name: this.state.name,
honor_code: this.state.honorCode,
country: this.state.country,
};
this.props.fetchRealtimeValidations(payload);
}
handleOnChange(e) {
const targetValue = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
this.setState({
[camelCase(e.target.name)]: targetValue,
});
this.validateInput(e.target.name, targetValue);
}
handleOnOptional(e) {
const optionalEnable = this.state.enableOptionalField;
const targetValue = e.target.id === 'additionalFields' ? !optionalEnable : e.target.checked;
this.setState({
enableOptionalField: targetValue,
});
}
validateInput(inputName, value) {
const { errors } = this.state;
let {
emailValid,
nameValid,
usernameValid,
passwordValid,
countryValid,
honorCodeValid,
termsOfServiceValid,
} = this.state;
switch (inputName) {
case 'email':
emailValid = value.match(/^([\w.%+-]+)@([\w-]+\.)+([\w]{2,})$/i);
errors.email = emailValid ? '' : this.intl.formatMessage(messages['logistration.email.validation.message']);
break;
case 'name':
nameValid = value.length >= 1;
errors.name = nameValid ? '' : this.intl.formatMessage(messages['logistration.fullname.validation.message']);
break;
case 'username':
usernameValid = value.length >= 2 && value.length <= 30;
errors.username = usernameValid ? '' : this.intl.formatMessage(messages['logistration.username.validation.message']);
break;
case 'password':
passwordValid = !!(value.length >= 8 && value.match(/\d+/g));
errors.password = passwordValid ? '' : this.intl.formatMessage(messages['logistration.register.page.password.validation.message']);
break;
case 'country':
countryValid = value !== '';
errors.country = countryValid ? '' : this.intl.formatMessage(messages['logistration.country.validation.message']);
break;
case 'honor_code':
honorCodeValid = value !== false;
errors.honorCode = honorCodeValid ? '' : null;
break;
case 'terms_of_service':
termsOfServiceValid = value !== false;
errors.termsOfService = termsOfServiceValid ? '' : null;
break;
default:
break;
}
this.setState({
errors,
emailValid,
nameValid,
usernameValid,
passwordValid,
countryValid,
honorCodeValid,
termsOfServiceValid,
}, this.validateForm);
}
validateForm() {
const {
emailValid,
nameValid,
usernameValid,
passwordValid,
} = this.state;
const validityMap = REGISTRATION_VALIDITY_MAP;
let extraFieldsValid = true;
Object.entries(validityMap).forEach(([key, value]) => {
if (value) {
const stateValid = `${camelCase(key)}Valid`;
extraFieldsValid = extraFieldsValid && this.state[stateValid];
}
});
this.setState({
formValid: emailValid && nameValid && usernameValid && passwordValid && extraFieldsValid,
});
}
addExtraRequiredFields() {
const fields = this.props.formData.fields.map((field) => {
let options = null;
if (REGISTRATION_EXTRA_FIELDS.includes(field.name)) {
if (field.required) {
const stateVar = camelCase(field.name);
let beforeLink;
let link;
let linkText;
let afterLink;
const props = {
id: field.name,
name: field.name,
type: field.type,
value: this.state[stateVar],
required: true,
onChange: e => this.handleOnChange(e),
};
REGISTRATION_VALIDITY_MAP[field.name] = true;
if (field.type === 'plaintext' && field.name === 'honor_code') { // special case where honor code and tos are combined
afterLink = field.label;
props.type = 'hidden';
const nodes = [];
do {
const matches = processLink(afterLink);
[beforeLink, link, linkText, afterLink] = matches;
nodes.push(