feat: add DOB month and year field
This commit is contained in:
1
.env
1
.env
@@ -26,6 +26,7 @@ STUDIO_BASE_URL=''
|
||||
SUPPORT_URL=''
|
||||
USER_INFO_COOKIE_NAME=''
|
||||
ENABLE_COPPA_COMPLIANCE=''
|
||||
ENABLE_DOB_UPDATE=''
|
||||
MARKETING_EMAILS_OPT_IN=''
|
||||
APP_ID=
|
||||
MFE_CONFIG_API_URL=
|
||||
|
||||
@@ -27,6 +27,7 @@ STUDIO_BASE_URL=''
|
||||
SUPPORT_URL='http://localhost:18000/support'
|
||||
USER_INFO_COOKIE_NAME='edx-user-info'
|
||||
ENABLE_COPPA_COMPLIANCE=''
|
||||
ENABLE_DOB_UPDATE=''
|
||||
MARKETING_EMAILS_OPT_IN=''
|
||||
APP_ID=
|
||||
MFE_CONFIG_API_URL=
|
||||
|
||||
@@ -26,6 +26,7 @@ STUDIO_BASE_URL=''
|
||||
SUPPORT_URL='http://localhost:18000/support'
|
||||
USER_INFO_COOKIE_NAME='edx-user-info'
|
||||
ENABLE_COPPA_COMPLIANCE=''
|
||||
ENABLE_DOB_UPDATE=''
|
||||
MARKETING_EMAILS_OPT_IN=''
|
||||
APP_ID=
|
||||
MFE_CONFIG_API_URL=
|
||||
|
||||
@@ -37,11 +37,13 @@ import ThirdPartyAuth from './third-party-auth';
|
||||
import BetaLanguageBanner from './BetaLanguageBanner';
|
||||
import EmailField from './EmailField';
|
||||
import OneTimeDismissibleAlert from './OneTimeDismissibleAlert';
|
||||
import DOBModal from './DOBForm';
|
||||
import {
|
||||
YEAR_OF_BIRTH_OPTIONS,
|
||||
EDUCATION_LEVELS,
|
||||
GENDER_OPTIONS,
|
||||
COUNTRY_WITH_STATES,
|
||||
COPPA_COMPLIANCE_YEAR,
|
||||
getStatesList,
|
||||
} from './data/constants';
|
||||
import { fetchSiteLanguages } from './site-language';
|
||||
@@ -494,13 +496,37 @@ class AccountSettingsPage extends React.Component {
|
||||
);
|
||||
|
||||
const hasLinkedTPA = findIndex(this.props.tpaProviders, provider => provider.connected) >= 0;
|
||||
|
||||
// if user is under 13 and does not have cookie set
|
||||
const shouldUpdateDOB = (
|
||||
getConfig().ENABLE_COPPA_COMPLIANCE
|
||||
&& getConfig().ENABLE_DOB_UPDATE
|
||||
&& this.props.formValues.year_of_birth.toString() >= COPPA_COMPLIANCE_YEAR.toString()
|
||||
&& !localStorage.getItem('submittedDOB')
|
||||
);
|
||||
return (
|
||||
<>
|
||||
{ shouldUpdateDOB
|
||||
&& (
|
||||
<DOBModal
|
||||
{...editableFieldProps}
|
||||
/>
|
||||
)}
|
||||
<div className="account-section pt-3 mb-5" id="basic-information" ref={this.navLinkRefs['#basic-information']}>
|
||||
{
|
||||
this.props.mostRecentVerifiedName
|
||||
&& this.renderVerifiedNameMessage(this.props.mostRecentVerifiedName)
|
||||
}
|
||||
{localStorage.getItem('submittedDOB')
|
||||
&& (
|
||||
<OneTimeDismissibleAlert
|
||||
id="updated-dob"
|
||||
variant="success"
|
||||
icon={CheckCircle}
|
||||
header={this.props.intl.formatMessage(messages['account.settings.field.dob.form.success'])}
|
||||
body=""
|
||||
/>
|
||||
)}
|
||||
|
||||
<h2 className="section-heading h4 mb-3">
|
||||
{this.props.intl.formatMessage(messages['account.settings.section.account.information'])}
|
||||
|
||||
@@ -266,6 +266,46 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Select a year of birth',
|
||||
description: 'Option for empty value on account settings year of birth field.',
|
||||
},
|
||||
'account.settings.field.dob.month': {
|
||||
id: 'account.settings.field.dob.month',
|
||||
defaultMessage: 'Month',
|
||||
description: 'Label for account settings month of birth field.',
|
||||
},
|
||||
'account.settings.field.dob.year': {
|
||||
id: 'account.settings.field.dob.year',
|
||||
defaultMessage: 'Year',
|
||||
description: 'Label for account settings year of birth field.',
|
||||
},
|
||||
'account.settings.field.dob.form.button': {
|
||||
id: 'account.settings.field.dob.form.button',
|
||||
defaultMessage: 'Please confirm your date of birth',
|
||||
description: 'Message to prompt user to enter dob',
|
||||
},
|
||||
'account.settings.field.dob.form.title': {
|
||||
id: 'account.settings.field.dob.form.title',
|
||||
defaultMessage: 'Confirm your date of birth',
|
||||
description: 'Title of DOB form',
|
||||
},
|
||||
'account.settings.field.dob.form.help.text': {
|
||||
id: 'account.settings.field.dob.form.help.text',
|
||||
defaultMessage: 'We ask for birthday information to ensure that underage people aren’t using edX.',
|
||||
description: 'Help text for DOB form',
|
||||
},
|
||||
'account.settings.field.dob.form.success': {
|
||||
id: 'account.settings.field.dob.form.success',
|
||||
defaultMessage: 'Thank you for entering your birthday information.',
|
||||
description: 'Title of banner when date of birth is successfully entered',
|
||||
},
|
||||
'account.settings.field.month_of_birth.options.empty': {
|
||||
id: 'account.settings.field.month_of_birth.options.empty',
|
||||
defaultMessage: 'Select a month of birth',
|
||||
description: 'Option for empty value on account settings month of birth field.',
|
||||
},
|
||||
'account.settingsfield.dob.error.general': {
|
||||
id: 'account.settingsfield.dob.error.general',
|
||||
defaultMessage: 'A technical error occurred. Please try again.',
|
||||
description: 'Generic error message.',
|
||||
},
|
||||
'account.settings.field.country': {
|
||||
id: 'account.settings.field.country',
|
||||
defaultMessage: 'Country',
|
||||
|
||||
148
src/account-settings/DOBForm.jsx
Normal file
148
src/account-settings/DOBForm.jsx
Normal file
@@ -0,0 +1,148 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import {
|
||||
Form, StatefulButton, ModalDialog, ActionRow, useToggle, Button,
|
||||
} from '@edx/paragon';
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { connect, useDispatch } from 'react-redux';
|
||||
import messages from './AccountSettingsPage.messages';
|
||||
import { YEAR_OF_BIRTH_OPTIONS } from './data/constants';
|
||||
import { editableFieldSelector } from './data/selectors';
|
||||
import { saveSettingsReset } from './data/actions';
|
||||
|
||||
function DOBModal(props) {
|
||||
const {
|
||||
saveState,
|
||||
error,
|
||||
onSubmit,
|
||||
intl,
|
||||
} = props;
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const [isOpen, open, close, toggle] = useToggle(true, {});
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const month = e.target.month.value;
|
||||
const year = e.target.year.value;
|
||||
const data = month !== '' && year !== '' ? [{ field_name: 'DOB', field_value: `${year}-${month}` }] : [];
|
||||
onSubmit('extended_profile', data);
|
||||
};
|
||||
|
||||
const handleComplete = useCallback(() => {
|
||||
localStorage.setItem('submittedDOB', 'true');
|
||||
close();
|
||||
dispatch(saveSettingsReset());
|
||||
}, [dispatch, close]);
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
close();
|
||||
dispatch(saveSettingsReset());
|
||||
}, [dispatch, close]);
|
||||
|
||||
function renderErrors() {
|
||||
if (saveState === 'error' || error) {
|
||||
return (
|
||||
<Form.Control.Feedback type="invalid" key="general-error">
|
||||
{intl.formatMessage(messages['account.settingsfield.dob.error.general'])}
|
||||
</Form.Control.Feedback>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (saveState === 'complete' && isOpen) {
|
||||
handleComplete();
|
||||
}
|
||||
}, [handleComplete, saveState, isOpen]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button variant="primary" onClick={open}>
|
||||
{intl.formatMessage(messages['account.settings.field.dob.form.button'])}
|
||||
</Button>
|
||||
<ModalDialog
|
||||
title={intl.formatMessage(messages['account.settings.field.dob.form.title'])}
|
||||
isOpen={isOpen}
|
||||
onClose={handleClose}
|
||||
hasCloseButton={false}
|
||||
variant="default"
|
||||
>
|
||||
<form onSubmit={handleSubmit}>
|
||||
|
||||
<ModalDialog.Header>
|
||||
<ModalDialog.Title>
|
||||
{intl.formatMessage(messages['account.settings.field.dob.form.title'])}
|
||||
</ModalDialog.Title>
|
||||
</ModalDialog.Header>
|
||||
|
||||
<ModalDialog.Body className="overflow-hidden" style={{ padding: '1.5rem' }}>
|
||||
<p>{intl.formatMessage(messages['account.settings.field.dob.form.help.text'])}</p>
|
||||
<Form.Group>
|
||||
<Form.Label>
|
||||
{intl.formatMessage(messages['account.settings.field.dob.month'])}
|
||||
</Form.Label>
|
||||
<Form.Control
|
||||
as="select"
|
||||
name="month"
|
||||
>
|
||||
{[...Array(12).keys()].map(month => (
|
||||
<option key={month + 1} value={month + 1}>{month + 1}</option>
|
||||
))}
|
||||
</Form.Control>
|
||||
</Form.Group>
|
||||
<Form.Group>
|
||||
<Form.Label>
|
||||
{intl.formatMessage(messages['account.settings.field.dob.year'])}
|
||||
</Form.Label>
|
||||
<Form.Control
|
||||
as="select"
|
||||
name="year"
|
||||
>
|
||||
{YEAR_OF_BIRTH_OPTIONS.map(year => (
|
||||
<option key={year.value} value={year.value}>{year.label}</option>
|
||||
))}
|
||||
</Form.Control>
|
||||
</Form.Group>
|
||||
{renderErrors()}
|
||||
</ModalDialog.Body>
|
||||
|
||||
<ModalDialog.Footer>
|
||||
<ActionRow>
|
||||
<ModalDialog.CloseButton variant="tertiary">
|
||||
Cancel
|
||||
</ModalDialog.CloseButton>
|
||||
<StatefulButton
|
||||
type="submit"
|
||||
state={saveState}
|
||||
labels={{
|
||||
default: intl.formatMessage(messages['account.settings.editable.field.action.save']),
|
||||
}}
|
||||
disabledStates={[]}
|
||||
/>
|
||||
</ActionRow>
|
||||
</ModalDialog.Footer>
|
||||
|
||||
</form>
|
||||
</ModalDialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
DOBModal.propTypes = {
|
||||
saveState: PropTypes.oneOf(['default', 'pending', 'complete', 'error']),
|
||||
error: PropTypes.string,
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
DOBModal.defaultProps = {
|
||||
saveState: undefined,
|
||||
error: undefined,
|
||||
};
|
||||
|
||||
export default connect(editableFieldSelector)(injectIntl(DOBModal));
|
||||
@@ -10,6 +10,11 @@ export const YEAR_OF_BIRTH_OPTIONS = (() => {
|
||||
return years.reverse();
|
||||
})();
|
||||
|
||||
export const COPPA_COMPLIANCE_YEAR = (() => {
|
||||
const currentYear = new Date().getFullYear();
|
||||
return currentYear - 13;
|
||||
})();
|
||||
|
||||
export const EDUCATION_LEVELS = [
|
||||
'',
|
||||
'p',
|
||||
|
||||
@@ -66,6 +66,7 @@ initialize({
|
||||
ENABLE_DEMOGRAPHICS_COLLECTION: (process.env.ENABLE_DEMOGRAPHICS_COLLECTION || false),
|
||||
DEMOGRAPHICS_BASE_URL: process.env.DEMOGRAPHICS_BASE_URL,
|
||||
ENABLE_COPPA_COMPLIANCE: (process.env.ENABLE_COPPA_COMPLIANCE || false),
|
||||
ENABLE_DOB_UPDATE: (process.env.ENABLE_DOB_UPDATE || false),
|
||||
MARKETING_EMAILS_OPT_IN: (process.env.MARKETING_EMAILS_OPT_IN || false),
|
||||
}, 'App loadConfig override handler');
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user