Merge pull request #242 from edx/ork/MICROBA-309_collect-demographics-on-account-page
MICROBA-309 Collect demographics on account page
This commit is contained in:
1
.env
1
.env
@@ -5,6 +5,7 @@ CSRF_TOKEN_API_PATH=null
|
||||
ECOMMERCE_BASE_URL=null
|
||||
LANGUAGE_PREFERENCE_COOKIE_NAME=null
|
||||
LMS_BASE_URL=null
|
||||
DEMOGRAPHICS_BASE_URL=null
|
||||
LOGIN_URL=null
|
||||
LOGOUT_URL=null
|
||||
MARKETING_SITE_BASE_URL=null
|
||||
|
||||
@@ -8,6 +8,7 @@ LMS_BASE_URL='http://localhost:18000'
|
||||
LOGIN_URL='http://localhost:18000/login'
|
||||
LOGOUT_URL='http://localhost:18000/logout'
|
||||
MARKETING_SITE_BASE_URL='http://localhost:18000'
|
||||
DEMOGRAPHICS_BASE_URL='http://localhost:18360'
|
||||
NODE_ENV='development'
|
||||
ORDER_HISTORY_URL='localhost:1996/orders'
|
||||
PORT=1997
|
||||
@@ -18,3 +19,4 @@ SUPPORT_URL='http://localhost:18000/support'
|
||||
USER_INFO_COOKIE_NAME='edx-user-info'
|
||||
# Temporary, Remove this once we are ready to release the feature.
|
||||
COACHING_ENABLED=true
|
||||
ENABLE_DEMOGRAPHICS_COLLECTION=true
|
||||
|
||||
@@ -8,6 +8,7 @@ LMS_BASE_URL='http://localhost:18000'
|
||||
LOGIN_URL='http://localhost:18000/login'
|
||||
LOGOUT_URL='http://localhost:18000/logout'
|
||||
MARKETING_SITE_BASE_URL='http://localhost:18000'
|
||||
DEMOGRAPHICS_BASE_URL='http://localhost:18360'
|
||||
NODE_ENV=null
|
||||
ORDER_HISTORY_URL='localhost:1996/orders'
|
||||
REFRESH_ACCESS_TOKEN_ENDPOINT='http://localhost:18000/login_refresh'
|
||||
@@ -16,3 +17,4 @@ SITE_NAME='edX'
|
||||
SUPPORT_URL='http://localhost:18000/support'
|
||||
USER_INFO_COOKIE_NAME='edx-user-info'
|
||||
COACHING_ENABLED=''
|
||||
ENABLE_DEMOGRAPHICS_COLLECTION=''
|
||||
|
||||
5
package-lock.json
generated
5
package-lock.json
generated
@@ -11646,6 +11646,11 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
|
||||
"integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM="
|
||||
},
|
||||
"lodash.pickby": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz",
|
||||
"integrity": "sha1-feoh2MGNdwOifHBMFdO4SmfjOv8="
|
||||
},
|
||||
"lodash.snakecase": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
"lodash.merge": "4.6.2",
|
||||
"lodash.omit": "4.5.0",
|
||||
"lodash.pick": "4.4.0",
|
||||
"lodash.pickby": "^4.6.0",
|
||||
"lodash.snakecase": "4.1.1",
|
||||
"memoize-one": "5.1.1",
|
||||
"newrelic": "5.13.1",
|
||||
|
||||
@@ -36,6 +36,7 @@ import {
|
||||
} from './data/constants';
|
||||
import { fetchSiteLanguages } from './site-language';
|
||||
import CoachingToggle from './coaching/CoachingToggle';
|
||||
import DemographicsSection from './demographics/DemographicsSection';
|
||||
|
||||
class AccountSettingsPage extends React.Component {
|
||||
constructor(props, context) {
|
||||
@@ -363,7 +364,9 @@ class AccountSettingsPage extends React.Component {
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
||||
{getConfig().ENABLE_DEMOGRAPHICS_COLLECTION &&
|
||||
<DemographicsSection />
|
||||
}
|
||||
<div className="account-section" id="social-media">
|
||||
<h2 className="section-heading">
|
||||
{this.props.intl.formatMessage(messages['account.settings.section.social.media'])}
|
||||
|
||||
@@ -46,6 +46,11 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Profile Information',
|
||||
description: 'The profile information section heading.',
|
||||
},
|
||||
'account.settings.section.demographics.information': {
|
||||
id: 'account.settings.section.demographics.information',
|
||||
defaultMessage: 'Optional Information',
|
||||
description: 'The optional information section heading.',
|
||||
},
|
||||
'account.settings.section.site.preferences': {
|
||||
id: 'account.settings.section.site.preferences',
|
||||
defaultMessage: 'Site Preferences',
|
||||
@@ -287,6 +292,7 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Select a Language',
|
||||
description: 'Option for an empty value on account settings spoken languages field.',
|
||||
},
|
||||
|
||||
'account.settings.field.time.zone': {
|
||||
id: 'account.settings.field.time.zone',
|
||||
defaultMessage: 'Time zone',
|
||||
|
||||
@@ -23,6 +23,7 @@ function EditableField(props) {
|
||||
emptyLabel,
|
||||
type,
|
||||
value,
|
||||
userSuppliedValue,
|
||||
options,
|
||||
saveState,
|
||||
error,
|
||||
@@ -66,15 +67,22 @@ function EditableField(props) {
|
||||
|
||||
const renderValue = (rawValue) => {
|
||||
if (!rawValue) return renderEmptyLabel();
|
||||
let value = rawValue;
|
||||
|
||||
if (options) {
|
||||
// Use == instead of === to prevent issues when HTML casts numbers as strings
|
||||
// eslint-disable-next-line eqeqeq
|
||||
const selectedOption = options.find(option => option.value == rawValue);
|
||||
if (selectedOption) return selectedOption.label;
|
||||
if (selectedOption) {
|
||||
value = selectedOption.label;
|
||||
};
|
||||
}
|
||||
|
||||
return rawValue;
|
||||
if (userSuppliedValue) {
|
||||
value += `: ${userSuppliedValue}`;
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
const renderConfirmationMessage = () => {
|
||||
@@ -106,6 +114,7 @@ function EditableField(props) {
|
||||
options={options}
|
||||
{...others}
|
||||
/>
|
||||
<>{others.children}</>
|
||||
</ValidationFormGroup>
|
||||
<p>
|
||||
<StatefulButton
|
||||
@@ -162,6 +171,7 @@ EditableField.propTypes = {
|
||||
emptyLabel: PropTypes.node,
|
||||
type: PropTypes.string.isRequired,
|
||||
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
userSuppliedValue: PropTypes.string,
|
||||
options: PropTypes.arrayOf(PropTypes.shape({
|
||||
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
|
||||
@@ -13,6 +13,7 @@ function JumpNav({ intl }) {
|
||||
items={[
|
||||
'basic-information',
|
||||
'profile-information',
|
||||
'demographics-information',
|
||||
'social-media',
|
||||
'site-preferences',
|
||||
'linked-accounts',
|
||||
@@ -31,6 +32,11 @@ function JumpNav({ intl }) {
|
||||
{intl.formatMessage(messages['account.settings.section.profile.information'])}
|
||||
</NavHashLink>
|
||||
</li>
|
||||
<li>
|
||||
<NavHashLink to="#demographics-information">
|
||||
{intl.formatMessage(messages['account.settings.section.demographics.information'])}
|
||||
</NavHashLink>
|
||||
</li>
|
||||
<li>
|
||||
<NavHashLink to="#social-media">
|
||||
{intl.formatMessage(messages['account.settings.section.social.media'])}
|
||||
|
||||
@@ -100,9 +100,9 @@ export const savePreviousSiteLanguage = previousSiteLanguage => ({
|
||||
payload: { previousSiteLanguage },
|
||||
});
|
||||
|
||||
export const saveMultipleSettings = settingsArray => ({
|
||||
export const saveMultipleSettings = (settingsArray, form = null) => ({
|
||||
type: SAVE_MULTIPLE_SETTINGS.BASE,
|
||||
payload: { settingsArray },
|
||||
payload: { settingsArray, form },
|
||||
});
|
||||
|
||||
export const saveMultipleSettingsBegin = () => ({
|
||||
|
||||
@@ -112,3 +112,92 @@ const COUNTRY_STATES_MAP = {
|
||||
export function getStatesList(country) {
|
||||
return country && COUNTRY_STATES_MAP[country.toUpperCase()];
|
||||
}
|
||||
|
||||
export const SELF_DESCRIBE = 'self-describe';
|
||||
export const DEMOGRAPHICS_GENDER_OPTIONS = [
|
||||
'',
|
||||
'woman',
|
||||
'man',
|
||||
'non-binary',
|
||||
SELF_DESCRIBE,
|
||||
];
|
||||
|
||||
export const OTHER = 'other';
|
||||
export const DEMOGRAPHICS_ETHNICITY_OPTIONS = [
|
||||
'american-indian-or-alaska-native',
|
||||
'asian',
|
||||
'black-or-african-american',
|
||||
'hispanic-latin-spanish',
|
||||
'middle-eastern-or-north-african',
|
||||
'native-hawaiian-or-pacific-islander',
|
||||
'white',
|
||||
OTHER,
|
||||
];
|
||||
|
||||
export const DEMOGRAPHICS_INCOME_OPTIONS = [
|
||||
'',
|
||||
'less-than-10k',
|
||||
'10k-25k',
|
||||
'25k-50k',
|
||||
'50k-75k',
|
||||
'over-100k',
|
||||
'unsure',
|
||||
];
|
||||
|
||||
export const DEMOGRAPHICS_MILITARY_HISTORY_OPTIONS = [
|
||||
'',
|
||||
'never-served',
|
||||
'training',
|
||||
'active',
|
||||
'previously-active',
|
||||
];
|
||||
|
||||
export const DEMOGRAPHICS_EDUCATION_LEVEL_OPTIONS = [
|
||||
'',
|
||||
'no-high-school',
|
||||
'some-high-school',
|
||||
'high-school-ged-equivalent',
|
||||
'some-college',
|
||||
'associates',
|
||||
'bachelors',
|
||||
'masters',
|
||||
'professional',
|
||||
'doctorate',
|
||||
];
|
||||
|
||||
export const DEMOGRAPHICS_WORK_STATUS_OPTIONS = [
|
||||
'',
|
||||
'full-time',
|
||||
'part-time',
|
||||
'not-employed-looking',
|
||||
'not-employed-not-looking',
|
||||
'unable',
|
||||
'retired',
|
||||
'other',
|
||||
];
|
||||
|
||||
export const DEMOGRAPHICS_WORK_SECTOR_OPTIONS = [
|
||||
'',
|
||||
'accommodation-food',
|
||||
'administrative-support-waste-remediation',
|
||||
'agriculture-forestry-fishing-hunting',
|
||||
'arts-entertainment-recreation',
|
||||
'construction',
|
||||
'educational',
|
||||
'finance-insurance',
|
||||
'healthcare-social',
|
||||
'information',
|
||||
'management',
|
||||
'manufacturing',
|
||||
'mining-quarry-oil-gas',
|
||||
'professional-scientific-technical',
|
||||
'public-admin',
|
||||
'real-estate',
|
||||
'retail',
|
||||
'transport-warehousing',
|
||||
'utilities',
|
||||
'trade',
|
||||
'other',
|
||||
];
|
||||
|
||||
export const DECLINED = 'declined';
|
||||
|
||||
@@ -106,11 +106,11 @@ export function* handleSaveSettings(action) {
|
||||
|
||||
|
||||
// handles mutiple settings saved at once, in order, and stops executing on first failure.
|
||||
export function* handleSaveMultipleSettings(settings) {
|
||||
export function* handleSaveMultipleSettings(action) {
|
||||
try {
|
||||
yield put(saveMultipleSettingsBegin());
|
||||
const { username, userId } = getAuthenticatedUser();
|
||||
const { settingsArray } = settings.payload;
|
||||
const { settingsArray, form } = action.payload;
|
||||
for (let i = 0; i < settingsArray.length; i += 1) {
|
||||
const { formId, commitValues } = settingsArray[i];
|
||||
yield put(saveSettingsBegin());
|
||||
@@ -118,7 +118,11 @@ export function* handleSaveMultipleSettings(settings) {
|
||||
const savedSettings = yield call(patchSettings, username, commitData, userId);
|
||||
yield put(saveSettingsSuccess(savedSettings, commitData));
|
||||
}
|
||||
yield put(saveMultipleSettingsSuccess(settings));
|
||||
yield put(saveMultipleSettingsSuccess(action));
|
||||
if (form) {
|
||||
yield delay(1000);
|
||||
yield put(closeForm(form));
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.fieldErrors) {
|
||||
yield put(saveMultipleSettingsFailure({ fieldErrors: e.fieldErrors }));
|
||||
|
||||
@@ -191,3 +191,15 @@ export const coachingConsentPageSelector = createSelector(
|
||||
formErrors: errors,
|
||||
}),
|
||||
);
|
||||
|
||||
export const demographicsSectionSelector = createSelector(
|
||||
formValuesSelector,
|
||||
draftsSelector,
|
||||
(
|
||||
formValues,
|
||||
drafts,
|
||||
) => ({
|
||||
formValues,
|
||||
drafts,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import pick from 'lodash.pick';
|
||||
import pickBy from 'lodash.pickby';
|
||||
import omit from 'lodash.omit';
|
||||
import isEmpty from 'lodash.isempty';
|
||||
|
||||
import { handleRequestError, unpackFieldErrors } from './utils';
|
||||
import { getThirdPartyAuthProviders } from '../third-party-auth';
|
||||
import { getCoachingPreferences, patchCoachingPreferences } from '../coaching/data/service';
|
||||
import { getDemographics, patchDemographics } from '../demographics/data/service';
|
||||
|
||||
const SOCIAL_PLATFORMS = [
|
||||
{ id: 'twitter', key: 'social_link_twitter' },
|
||||
@@ -161,6 +163,7 @@ export async function getSettings(username, userRoles, userId) {
|
||||
getProfileDataManager(username, userRoles),
|
||||
getTimeZones(),
|
||||
getConfig().COACHING_ENABLED && getCoachingPreferences(userId),
|
||||
getConfig().ENABLE_DEMOGRAPHICS_COLLECTION && getDemographics(userId),
|
||||
]);
|
||||
|
||||
return {
|
||||
@@ -170,6 +173,7 @@ export async function getSettings(username, userRoles, userId) {
|
||||
profileDataManager: results[3],
|
||||
timeZones: results[4],
|
||||
coaching: results[5],
|
||||
...results[6], // demographics
|
||||
};
|
||||
}
|
||||
|
||||
@@ -183,9 +187,11 @@ export async function patchSettings(username, commitValues, userId) {
|
||||
// user/v1/preferences where it does update. This is the one we use.
|
||||
const preferenceKeys = ['time_zone'];
|
||||
const coachingKeys = ['coaching'];
|
||||
const isDemographicsKey = (value, key) => key.includes('demographics');
|
||||
const accountCommitValues = omit(commitValues, preferenceKeys, coachingKeys);
|
||||
const preferenceCommitValues = pick(commitValues, preferenceKeys);
|
||||
const coachingCommitValues = pick(commitValues, coachingKeys);
|
||||
const demographicsCommitValues = pickBy(commitValues, isDemographicsKey);
|
||||
const patchRequests = [];
|
||||
|
||||
if (!isEmpty(accountCommitValues)) {
|
||||
@@ -197,6 +203,9 @@ export async function patchSettings(username, commitValues, userId) {
|
||||
if (!isEmpty(coachingCommitValues)) {
|
||||
patchRequests.push(patchCoachingPreferences(userId, coachingCommitValues));
|
||||
}
|
||||
if (!isEmpty(demographicsCommitValues)) {
|
||||
patchRequests.push(patchDemographics(userId, demographicsCommitValues));
|
||||
}
|
||||
|
||||
const results = await Promise.all(patchRequests);
|
||||
// Assigns in order of requests. Preference keys
|
||||
|
||||
78
src/account-settings/demographics/Checkboxes.jsx
Normal file
78
src/account-settings/demographics/Checkboxes.jsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { CheckBox } from '@edx/paragon';
|
||||
import { DECLINED } from '../data/constants';
|
||||
|
||||
export const Checkboxes = (props) => {
|
||||
const {
|
||||
id,
|
||||
options,
|
||||
values,
|
||||
onChange,
|
||||
} = props;
|
||||
|
||||
const [selected, setSelected] = useState(values);
|
||||
useEffect(() => {
|
||||
onChange(id, selected)
|
||||
}, [selected])
|
||||
|
||||
const handleToggle = (value, option) => {
|
||||
// If the user checked 'declined', uncheck all other options
|
||||
if (value && option == DECLINED) {
|
||||
setSelected([DECLINED]);
|
||||
return;
|
||||
}
|
||||
|
||||
// If option checked, make sure this option is in `selected` (and remove 'declined')
|
||||
if (value && !selected.includes(option)) {
|
||||
const newSelected = selected.filter(i => i !== DECLINED).concat(option);
|
||||
setSelected(newSelected);
|
||||
}
|
||||
|
||||
// If unchecked, make sure this option is NOT in `seleted`
|
||||
if (!value) {
|
||||
setSelected(selected.filter(i => i !== option));
|
||||
}
|
||||
}
|
||||
|
||||
const renderCheckboxes = () => {
|
||||
return options.map((option, index) => {
|
||||
const isFirst = index == 0;
|
||||
const isChecked = selected.includes(option.value);
|
||||
return (
|
||||
<div key={index} className="checkboxOption">
|
||||
<CheckBox
|
||||
type="checkbox"
|
||||
id={option.value}
|
||||
name={option.value}
|
||||
value={option.value}
|
||||
checked={isChecked}
|
||||
autoFocus={isFirst}
|
||||
label={option.label}
|
||||
onChange={(value) => handleToggle(value, option.value)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div role="group">
|
||||
{renderCheckboxes()}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Checkboxes.propTypes = {
|
||||
id: PropTypes.string,
|
||||
options: PropTypes.array,
|
||||
values: PropTypes.array,
|
||||
onChange: PropTypes.func,
|
||||
};
|
||||
|
||||
Checkboxes.defaultProps = {
|
||||
options: [],
|
||||
values: [],
|
||||
}
|
||||
|
||||
export default Checkboxes;
|
||||
263
src/account-settings/demographics/DemographicsSection.jsx
Normal file
263
src/account-settings/demographics/DemographicsSection.jsx
Normal file
@@ -0,0 +1,263 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Input } from '@edx/paragon';
|
||||
import memoize from 'memoize-one';
|
||||
|
||||
import { demographicsSectionSelector } from '../data/selectors';
|
||||
import { saveMultipleSettings, updateDraft } from '../data/actions';
|
||||
import EditableField from '../EditableField';
|
||||
import Checkboxes from './Checkboxes';
|
||||
import messages from './DemographicsSection.messages';
|
||||
import {
|
||||
SELF_DESCRIBE,
|
||||
DEMOGRAPHICS_GENDER_OPTIONS,
|
||||
DEMOGRAPHICS_ETHNICITY_OPTIONS,
|
||||
DEMOGRAPHICS_INCOME_OPTIONS,
|
||||
DEMOGRAPHICS_MILITARY_HISTORY_OPTIONS,
|
||||
DEMOGRAPHICS_EDUCATION_LEVEL_OPTIONS,
|
||||
OTHER,
|
||||
DEMOGRAPHICS_WORK_STATUS_OPTIONS,
|
||||
DEMOGRAPHICS_WORK_SECTOR_OPTIONS,
|
||||
DECLINED,
|
||||
} from '../data/constants';
|
||||
|
||||
class DemographicsSection extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context)
|
||||
}
|
||||
|
||||
getLocalizedOptions = memoize((locale) => ({
|
||||
demographicsGenderOptions: DEMOGRAPHICS_GENDER_OPTIONS.map(key => ({
|
||||
value: key,
|
||||
label: this.props.intl.formatMessage(messages[`account.settings.field.demographics.gender.options.${key || 'empty'}`]),
|
||||
})).concat(this.getDeclinedOption()),
|
||||
demographicsEthnicityOptions: DEMOGRAPHICS_ETHNICITY_OPTIONS.map(key => ({
|
||||
value: key,
|
||||
label: this.props.intl.formatMessage(messages[`account.settings.field.demographics.ethnicity.options.${key || 'empty'}`]),
|
||||
})).concat(this.getDeclinedOption()),
|
||||
demographicsIncomeOptions: DEMOGRAPHICS_INCOME_OPTIONS.map(key => ({
|
||||
value: key,
|
||||
label: this.props.intl.formatMessage(messages[`account.settings.field.demographics.income.options.${key || 'empty'}`]),
|
||||
})).concat(this.getDeclinedOption()),
|
||||
demographicsMilitaryHistoryOptions: DEMOGRAPHICS_MILITARY_HISTORY_OPTIONS.map(key => ({
|
||||
value: key,
|
||||
label: this.props.intl.formatMessage(messages[`account.settings.field.demographics.military_history.options.${key || 'empty'}`]),
|
||||
})).concat(this.getDeclinedOption()),
|
||||
demographicsEducationLevelOptions: DEMOGRAPHICS_EDUCATION_LEVEL_OPTIONS.map(key => ({
|
||||
value: key,
|
||||
label: this.props.intl.formatMessage(messages[`account.settings.field.demographics.education_level.options.${key || 'empty'}`]),
|
||||
})).concat(this.getDeclinedOption()),
|
||||
demographicsWorkStatusOptions: DEMOGRAPHICS_WORK_STATUS_OPTIONS.map(key => ({
|
||||
value: key,
|
||||
label: this.props.intl.formatMessage(messages[`account.settings.field.demographics.work_status.options.${key || 'empty'}`]),
|
||||
})).concat(this.getDeclinedOption()),
|
||||
demographicsWorkSectorOptions: DEMOGRAPHICS_WORK_SECTOR_OPTIONS.map(key => ({
|
||||
value: key,
|
||||
label: this.props.intl.formatMessage(messages[`account.settings.field.demographics.work_sector.options.${key || 'empty'}`]),
|
||||
})).concat(this.getDeclinedOption()),
|
||||
}));
|
||||
|
||||
getDeclinedOption() {
|
||||
return [{
|
||||
value: DECLINED,
|
||||
label: this.props.intl.formatMessage(messages[`account.settings.field.demographics.options.declined`])
|
||||
}]
|
||||
}
|
||||
|
||||
ethnicityFieldDisplay = () => {
|
||||
const ethnicities = this.props.formValues.demographics_user_ethnicity;
|
||||
return ethnicities.map((e) => {
|
||||
if (e == DECLINED) {
|
||||
return this.props.intl.formatMessage(messages[`account.settings.field.demographics.options.declined`]);
|
||||
}
|
||||
return this.props.intl.formatMessage(messages[`account.settings.field.demographics.ethnicity.options.${e}`])
|
||||
}).join(", ")
|
||||
}
|
||||
|
||||
handleEditableFieldChange = (name, value) => {
|
||||
this.props.updateDraft(name, value);
|
||||
};
|
||||
|
||||
handleSubmit = (formId, values) => {
|
||||
// We have some custom fields in this section. Instead of relying on the
|
||||
// submitted values, submit the values stored in 'drafts'.
|
||||
const drafts = this.props.drafts;
|
||||
const settingsArray = []
|
||||
for (let field in drafts) {
|
||||
settingsArray.push({
|
||||
formId: field,
|
||||
commitValues: drafts[field]
|
||||
})
|
||||
}
|
||||
|
||||
this.props.saveMultipleSettings(settingsArray, formId);
|
||||
};
|
||||
|
||||
render() {
|
||||
const editableFieldProps = {
|
||||
onChange: this.handleEditableFieldChange,
|
||||
onSubmit: this.handleSubmit,
|
||||
};
|
||||
|
||||
const {
|
||||
demographicsGenderOptions,
|
||||
demographicsEthnicityOptions,
|
||||
demographicsIncomeOptions,
|
||||
demographicsMilitaryHistoryOptions,
|
||||
demographicsEducationLevelOptions,
|
||||
demographicsWorkStatusOptions,
|
||||
demographicsWorkSectorOptions,
|
||||
} = this.getLocalizedOptions(this.context.locale);
|
||||
|
||||
const showSelfDescribe = this.props.formValues.demographics_gender == SELF_DESCRIBE;
|
||||
const showWorkStatusDescribe = this.props.formValues.demographics_work_status == OTHER;
|
||||
|
||||
return (
|
||||
<div className="account-section" id="demographics-information">
|
||||
<h2 className="section-heading">
|
||||
{this.props.intl.formatMessage(messages['account.settings.section.demographics.information'])}
|
||||
</h2>
|
||||
|
||||
<EditableField
|
||||
name="demographics_gender"
|
||||
type="select"
|
||||
value={this.props.formValues.demographics_gender}
|
||||
userSuppliedValue={showSelfDescribe ? this.props.formValues.demographics_gender_description : null}
|
||||
options={demographicsGenderOptions}
|
||||
label={this.props.intl.formatMessage(messages['account.settings.field.demographics.gender'])}
|
||||
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.demographics.gender.empty'])}
|
||||
{...editableFieldProps}
|
||||
>
|
||||
{showSelfDescribe &&
|
||||
<Input
|
||||
name='demographics_gender_description'
|
||||
id='field-demographics_gender_description'
|
||||
type='text'
|
||||
placeholder={this.props.intl.formatMessage(messages['account.settings.field.demographics.gender_description.empty'])}
|
||||
value={this.props.formValues.demographics_gender_description}
|
||||
onChange={(e) => this.handleEditableFieldChange(`demographics_gender_description`, e.target.value)}
|
||||
aria-label={this.props.intl.formatMessage(messages['account.settings.field.demographics.gender_description'])}
|
||||
className="mt-1"
|
||||
/>
|
||||
}
|
||||
</EditableField>
|
||||
<EditableField
|
||||
name="demographics_user_ethnicity"
|
||||
type="select"
|
||||
hidden
|
||||
value={this.ethnicityFieldDisplay()}
|
||||
label={this.props.intl.formatMessage(messages['account.settings.field.demographics.ethnicity'])}
|
||||
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.demographics.ethnicity.empty'])}
|
||||
{...editableFieldProps}
|
||||
>
|
||||
<Checkboxes
|
||||
id="demographics_user_ethnicity"
|
||||
options={demographicsEthnicityOptions}
|
||||
values={this.props.formValues.demographics_user_ethnicity}
|
||||
{...editableFieldProps}
|
||||
/>
|
||||
</EditableField>
|
||||
<EditableField
|
||||
name="demographics_income"
|
||||
type="select"
|
||||
value={this.props.formValues.demographics_income}
|
||||
options={demographicsIncomeOptions}
|
||||
label={this.props.intl.formatMessage(messages['account.settings.field.demographics.income'])}
|
||||
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.demographics.income.empty'])}
|
||||
{...editableFieldProps}
|
||||
/>
|
||||
<EditableField
|
||||
name="demographics_military_history"
|
||||
type="select"
|
||||
value={this.props.formValues.demographics_military_history}
|
||||
options={demographicsMilitaryHistoryOptions}
|
||||
label={this.props.intl.formatMessage(messages['account.settings.field.demographics.military_history'])}
|
||||
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.demographics.military_history.empty'])}
|
||||
{...editableFieldProps}
|
||||
/>
|
||||
<EditableField
|
||||
name="demographics_learner_education_level"
|
||||
type="select"
|
||||
value={this.props.formValues.demographics_learner_education_level}
|
||||
options={demographicsEducationLevelOptions}
|
||||
label={this.props.intl.formatMessage(messages['account.settings.field.demographics.learner_education_level'])}
|
||||
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.demographics.learner_education_level.empty'])}
|
||||
{...editableFieldProps}
|
||||
/>
|
||||
<EditableField
|
||||
name="demographics_parent_education_level"
|
||||
type="select"
|
||||
value={this.props.formValues.demographics_parent_education_level}
|
||||
options={demographicsEducationLevelOptions}
|
||||
label={this.props.intl.formatMessage(messages['account.settings.field.demographics.parent_education_level'])}
|
||||
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.demographics.parent_education_level.empty'])}
|
||||
{...editableFieldProps}
|
||||
/>
|
||||
<EditableField
|
||||
name="demographics_work_status"
|
||||
type="select"
|
||||
value={this.props.formValues.demographics_work_status}
|
||||
userSuppliedValue={showWorkStatusDescribe ? this.props.formValues.demographics_work_status_description : null}
|
||||
options={demographicsWorkStatusOptions}
|
||||
label={this.props.intl.formatMessage(messages['account.settings.field.demographics.work_status'])}
|
||||
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.demographics.work_status.empty'])}
|
||||
{...editableFieldProps}
|
||||
>
|
||||
{showWorkStatusDescribe &&
|
||||
<Input
|
||||
name='demographics_work_status_description'
|
||||
id='field-demographics_work_status_description'
|
||||
type='text'
|
||||
placeholder={this.props.intl.formatMessage(messages['account.settings.field.demographics.work_status_description.empty'])}
|
||||
value={this.props.formValues.demographics_work_status_description}
|
||||
onChange={(e) => this.handleEditableFieldChange(`demographics_work_status_description`, e.target.value)}
|
||||
aria-label={this.props.intl.formatMessage(messages['account.settings.field.demographics.work_status_description'])}
|
||||
className="mt-1"
|
||||
/>
|
||||
}
|
||||
</EditableField>
|
||||
<EditableField
|
||||
name="demographics_current_work_sector"
|
||||
type="select"
|
||||
value={this.props.formValues.demographics_current_work_sector}
|
||||
options={demographicsWorkSectorOptions}
|
||||
label={this.props.intl.formatMessage(messages['account.settings.field.demographics.current_work_sector'])}
|
||||
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.demographics.current_work_sector.empty'])}
|
||||
{...editableFieldProps}
|
||||
/>
|
||||
<EditableField
|
||||
name="demographics_future_work_sector"
|
||||
type="select"
|
||||
value={this.props.formValues.demographics_future_work_sector}
|
||||
options={demographicsWorkSectorOptions}
|
||||
label={this.props.intl.formatMessage(messages['account.settings.field.demographics.future_work_sector'])}
|
||||
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.demographics.future_work_sector.empty'])}
|
||||
{...editableFieldProps}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
DemographicsSection.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
formValues: PropTypes.shape({
|
||||
demographics_gender: PropTypes.string,
|
||||
demographics_user_ethnicity: PropTypes.array,
|
||||
demographics_income: PropTypes.string,
|
||||
demographics_military_history: PropTypes.string,
|
||||
demographics_learner_education_level: PropTypes.string,
|
||||
demographics_parent_education_level: PropTypes.string,
|
||||
demographics_work_status: PropTypes.string,
|
||||
demographics_current_work_sector: PropTypes.string,
|
||||
demographics_future_work_sector: PropTypes.string,
|
||||
}).isRequired,
|
||||
updateDraft: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default connect(demographicsSectionSelector, {
|
||||
saveMultipleSettings,
|
||||
updateDraft,
|
||||
})(injectIntl(DemographicsSection))
|
||||
@@ -0,0 +1,456 @@
|
||||
import { defineMessages } from '@edx/frontend-platform/i18n';
|
||||
|
||||
const messages = defineMessages({
|
||||
'account.settings.section.demographics.information': {
|
||||
id: 'account.settings.section.demographics.information',
|
||||
defaultMessage: 'Optional Information',
|
||||
description: 'The optional information section heading.',
|
||||
},
|
||||
'account.settings.field.demographics.gender': {
|
||||
id: 'account.settings.field.demographics.gender',
|
||||
defaultMessage: 'Gender identity',
|
||||
description: 'Label for account settings gender identity field.',
|
||||
},
|
||||
'account.settings.field.demographics.gender.empty': {
|
||||
id: 'account.settings.field.demographics.gender.empty',
|
||||
defaultMessage: 'Add gender identity',
|
||||
description: 'Placeholder for empty account settings gender identity field.',
|
||||
},
|
||||
'account.settings.field.demographics.gender.options.empty': {
|
||||
id: 'account.settings.field.demographics.gender.options.empty',
|
||||
defaultMessage: 'Select a gender identity',
|
||||
description: 'Placeholder for the gender identity options dropdown.',
|
||||
},
|
||||
'account.settings.field.demographics.gender.options.woman': {
|
||||
id: 'account.settings.field.demographics.gender.options.woman',
|
||||
defaultMessage: 'Woman',
|
||||
description: 'The label for the woman gender identity option.',
|
||||
},
|
||||
'account.settings.field.demographics.gender.options.man': {
|
||||
id: 'account.settings.field.demographics.gender.options.man',
|
||||
defaultMessage: 'Man',
|
||||
description: 'The label for the man gender identity option.',
|
||||
},
|
||||
'account.settings.field.demographics.gender.options.non-binary': {
|
||||
id: 'account.settings.field.demographics.gender.options.non-binary',
|
||||
defaultMessage: 'Non-binary',
|
||||
description: 'The label for the non-binary gender identity option.',
|
||||
},
|
||||
'account.settings.field.demographics.gender.options.self-describe': {
|
||||
id: 'account.settings.field.demographics.gender.options.self-describe',
|
||||
defaultMessage: 'Prefer to self-describe',
|
||||
description: 'The label for self-describe gender identity option.',
|
||||
},
|
||||
'account.settings.field.demographics.gender_description': {
|
||||
id: 'account.settings.field.demographics.gender_description',
|
||||
defaultMessage: 'Gender identity description',
|
||||
description: 'Label for account settings gender identity description field.',
|
||||
},
|
||||
'account.settings.field.demographics.gender_description.empty': {
|
||||
id: 'account.settings.field.demographics.gender_description.empty',
|
||||
defaultMessage: 'Enter description',
|
||||
description: 'Placeholder for empty account settings gender identity field.',
|
||||
},
|
||||
'account.settings.field.demographics.ethnicity': {
|
||||
id: 'account.settings.field.demographics.ethnicity',
|
||||
defaultMessage: 'Race/Ethnicity identity',
|
||||
description: 'Label for account settings ethnic background field.',
|
||||
},
|
||||
'account.settings.field.demographics.ethnicity.empty': {
|
||||
id: 'account.settings.field.demographics.ethnicity.empty',
|
||||
defaultMessage: 'Add race/ethnicity identity',
|
||||
description: 'Placeholder for empty account settings ethnic background field.',
|
||||
},
|
||||
'account.settings.field.demographics.ethnicity.options.empty': {
|
||||
id: 'account.settings.field.demographics.ethnicity.options.empty',
|
||||
defaultMessage: 'Select all that apply', // TODO: Is this the desired text?
|
||||
description: 'Placeholder for the ethnic background options field.',
|
||||
},
|
||||
'account.settings.field.demographics.ethnicity.options.american-indian-or-alaska-native': {
|
||||
id: 'account.settings.field.demographics.ethnicity.options.american-indian-or-alaska-native',
|
||||
defaultMessage: 'American Indian or Alaska Native',
|
||||
description: 'The label for the American Indian or Alaska Native ethnicity option.',
|
||||
},
|
||||
'account.settings.field.demographics.ethnicity.options.asian': {
|
||||
id: 'account.settings.field.demographics.ethnicity.options.asian',
|
||||
defaultMessage: 'Asian',
|
||||
description: 'The label for the Asian ethnicity option.',
|
||||
},
|
||||
'account.settings.field.demographics.ethnicity.options.black-or-african-american': {
|
||||
id: 'account.settings.field.demographics.ethnicity.options.black-or-african-american',
|
||||
defaultMessage: 'Black or African American',
|
||||
description: 'The label for the Black or African American ethnicity option.',
|
||||
},
|
||||
'account.settings.field.demographics.ethnicity.options.hispanic-latin-spanish': {
|
||||
id: 'account.settings.field.demographics.ethnicity.options.hispanic-latin-spanish',
|
||||
defaultMessage: 'Hispanic, Latin, or Spanish origin',
|
||||
description: 'The label for the Hispanic, Latin, or Spanish origin ethnicity option.',
|
||||
},
|
||||
'account.settings.field.demographics.ethnicity.options.middle-eastern-or-north-african': {
|
||||
id: 'account.settings.field.demographics.ethnicity.options.middle-eastern-or-north-african',
|
||||
defaultMessage: 'Middle Eastern or North African',
|
||||
description: 'The label for the Middle Eastern or North African ethnicity option.',
|
||||
},
|
||||
'account.settings.field.demographics.ethnicity.options.native-hawaiian-or-pacific-islander': {
|
||||
id: 'account.settings.field.demographics.ethnicity.options.native-hawaiian-or-pacific-islander',
|
||||
defaultMessage: 'Native Hawaiian or Other Pacific Islander',
|
||||
description: 'The label for the Native Hawaiian or Other Pacific Islander ethnicity option.',
|
||||
},
|
||||
'account.settings.field.demographics.ethnicity.options.white': {
|
||||
id: 'account.settings.field.demographics.ethnicity.options.white',
|
||||
defaultMessage: 'White',
|
||||
description: 'The label for the White ethnicity option.',
|
||||
},
|
||||
'account.settings.field.demographics.ethnicity.options.other': {
|
||||
id: 'account.settings.field.demographics.ethnicity.options.other',
|
||||
defaultMessage: 'Some other race, ethnicity, or origin',
|
||||
description: 'The label for the Some other race, ethnicity, or origin ethnicity option.',
|
||||
},
|
||||
'account.settings.field.demographics.income': {
|
||||
id: 'account.settings.field.demographics.income',
|
||||
defaultMessage: 'Family income',
|
||||
description: 'Label for account settings household income field.',
|
||||
},
|
||||
'account.settings.field.demographics.income.empty': {
|
||||
id: 'account.settings.field.demographics.income.empty',
|
||||
defaultMessage: 'Add family income',
|
||||
description: 'Placeholder for empty account settings household income field.',
|
||||
},
|
||||
'account.settings.field.demographics.income.options.empty': {
|
||||
id: 'account.settings.field.demographics.income.options.empty',
|
||||
defaultMessage: 'Select a family income range',
|
||||
description: 'Placeholder for the household income dropdown.',
|
||||
},
|
||||
'account.settings.field.demographics.income.options.less-than-10k': {
|
||||
id: 'account.settings.field.demographics.income.options.less-than-10k',
|
||||
defaultMessage: 'Less than US $10,000',
|
||||
description: 'The label for the less than US $10,000 income option.',
|
||||
},
|
||||
'account.settings.field.demographics.income.options.10k-25k': {
|
||||
id: 'account.settings.field.demographics.income.options.10k-25k',
|
||||
defaultMessage: 'US $10,000 - $25,000',
|
||||
description: 'The label for the US $10,000 - $25,000 income option.',
|
||||
},
|
||||
'account.settings.field.demographics.income.options.25k-50k': {
|
||||
id: 'account.settings.field.demographics.income.options.25k-50k',
|
||||
defaultMessage: 'US $25,000 - $50,000',
|
||||
description: 'The label for the US $25,000 - $50,000 income option.',
|
||||
},
|
||||
'account.settings.field.demographics.income.options.50k-75k': {
|
||||
id: 'account.settings.field.demographics.income.options.50k-75k',
|
||||
defaultMessage: 'US $50,000 - $75,000',
|
||||
description: 'The label for the US $50,000 - $75,000 income option.',
|
||||
},
|
||||
'account.settings.field.demographics.income.options.over-100k': {
|
||||
id: 'account.settings.field.demographics.income.options.over-100k',
|
||||
defaultMessage: 'Over US $100,000',
|
||||
description: 'The label for the over US $100,000 income option.',
|
||||
},
|
||||
'account.settings.field.demographics.income.options.unsure': {
|
||||
id: 'account.settings.field.demographics.income.options.unsure',
|
||||
defaultMessage: 'I don\'t know',
|
||||
description: 'The label for the I don\'t know income option.',
|
||||
},
|
||||
'account.settings.field.demographics.military_history': {
|
||||
id: 'account.settings.field.demographics.military_history',
|
||||
defaultMessage: 'U.S. Military status',
|
||||
description: 'Label for account settings military history field.',
|
||||
},
|
||||
'account.settings.field.demographics.military_history.empty': {
|
||||
id: 'account.settings.field.demographics.military_history.empty',
|
||||
defaultMessage: 'Add military status',
|
||||
description: 'Placeholder for empty account settings military history field.',
|
||||
},
|
||||
'account.settings.field.demographics.military_history.options.empty': {
|
||||
id: 'account.settings.field.demographics.military_history.options.empty',
|
||||
defaultMessage: 'Select military status',
|
||||
description: 'Placeholder for the military history dropdown.',
|
||||
},
|
||||
'account.settings.field.demographics.military_history.options.never-served': {
|
||||
id: 'account.settings.field.demographics.income.options.never-served',
|
||||
defaultMessage: 'Never served in the military',
|
||||
description: 'The label for the never served in the military military history option.',
|
||||
},
|
||||
'account.settings.field.demographics.military_history.options.training': {
|
||||
id: 'account.settings.field.demographics.income.options.training',
|
||||
defaultMessage: 'Only on active duty for training',
|
||||
description: 'The label for the only on active duty for training military history option.',
|
||||
},
|
||||
'account.settings.field.demographics.military_history.options.active': {
|
||||
id: 'account.settings.field.demographics.income.options.active',
|
||||
defaultMessage: 'Now on active duty',
|
||||
description: 'The label for the now on active duty military history option.',
|
||||
},
|
||||
'account.settings.field.demographics.military_history.options.previously-active': {
|
||||
id: 'account.settings.field.demographics.income.options.previously-active',
|
||||
defaultMessage: 'On active duty in the past, but not now',
|
||||
description: 'The label for the on active duty in the past, but not now military history option.',
|
||||
},
|
||||
'account.settings.field.demographics.learner_education_level': {
|
||||
id: 'account.settings.field.demographics.learner_education_level',
|
||||
defaultMessage: 'Your education level',
|
||||
description: 'Label for account settings learner education level field.',
|
||||
},
|
||||
'account.settings.field.demographics.learner_education_level.empty': {
|
||||
id: 'account.settings.field.demographics.learner_education_level.empty',
|
||||
defaultMessage: 'Add education level',
|
||||
description: 'Placeholder for empty account settings learner education level field.',
|
||||
},
|
||||
'account.settings.field.demographics.parent_education_level': {
|
||||
id: 'account.settings.field.demographics.parent_education_level',
|
||||
defaultMessage: 'Parents/Guardians education level',
|
||||
description: 'Label for account settings parent education level field.',
|
||||
},
|
||||
'account.settings.field.demographics.parent_education_level.empty': {
|
||||
id: 'account.settings.field.demographics.parent_education_level.empty',
|
||||
defaultMessage: 'Add education level',
|
||||
description: 'Placeholder for empty account settings parent education level field.',
|
||||
},
|
||||
'account.settings.field.demographics.education_level.options.empty': {
|
||||
id: 'account.settings.field.demographics.education_level.options.empty',
|
||||
defaultMessage: 'Select education level',
|
||||
description: 'Placeholder for the education level options dropdown.',
|
||||
},
|
||||
'account.settings.field.demographics.education_level.options.no-high-school': {
|
||||
id: 'account.settings.field.demographics.education_level.options.no-high-school',
|
||||
defaultMessage: 'No High School',
|
||||
description: 'The label for the no high school education level option.',
|
||||
},
|
||||
'account.settings.field.demographics.education_level.options.some-high-school': {
|
||||
id: 'account.settings.field.demographics.education_level.options.some-high-school',
|
||||
defaultMessage: 'Some High School',
|
||||
description: 'The label for the some high school education level option.',
|
||||
},
|
||||
'account.settings.field.demographics.education_level.options.high-school-ged-equivalent': {
|
||||
id: 'account.settings.field.demographics.education_level.options.high-school-ged-equivalent',
|
||||
defaultMessage: 'High School diploma, GED, or equivalent',
|
||||
description: 'The label for the high school diploma, GED, or equivalent education level option.',
|
||||
},
|
||||
'account.settings.field.demographics.education_level.options.some-college': {
|
||||
id: 'account.settings.field.demographics.education_level.options.some-college',
|
||||
defaultMessage: 'Some college, but no degree',
|
||||
description: 'The label for the some college, but no degree education level option.',
|
||||
},
|
||||
'account.settings.field.demographics.education_level.options.some-college': {
|
||||
id: 'account.settings.field.demographics.education_level.options.some-college',
|
||||
defaultMessage: 'Some college, but no degree',
|
||||
description: 'The label for the some college, but no degree education level option.',
|
||||
},
|
||||
'account.settings.field.demographics.education_level.options.associates': {
|
||||
id: 'account.settings.field.demographics.education_level.options.associates',
|
||||
defaultMessage: 'Associates degree',
|
||||
description: 'The label for the Associates degree education level option.',
|
||||
},
|
||||
'account.settings.field.demographics.education_level.options.bachelors': {
|
||||
id: 'account.settings.field.demographics.education_level.options.bachelors',
|
||||
defaultMessage: 'Bachelors degree',
|
||||
description: 'The label for the Bachelors degree education level option.',
|
||||
},
|
||||
'account.settings.field.demographics.education_level.options.masters': {
|
||||
id: 'account.settings.field.demographics.education_level.options.masters',
|
||||
defaultMessage: 'Masters degree',
|
||||
description: 'The label for the Masters degree education level option.',
|
||||
},
|
||||
'account.settings.field.demographics.education_level.options.professional': {
|
||||
id: 'account.settings.field.demographics.education_level.options.professional',
|
||||
defaultMessage: 'Professional degree',
|
||||
description: 'The label for the Professional degree education level option.',
|
||||
},
|
||||
'account.settings.field.demographics.education_level.options.doctorate': {
|
||||
id: 'account.settings.field.demographics.education_level.options.doctorate',
|
||||
defaultMessage: 'Doctorate degree',
|
||||
description: 'The label for the Doctorate degree education level option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_status': {
|
||||
id: 'account.settings.field.demographics.work_status',
|
||||
defaultMessage: 'Employment status',
|
||||
description: 'Label for account settings work status field.',
|
||||
},
|
||||
'account.settings.field.demographics.work_status.empty': {
|
||||
id: 'account.settings.field.demographics.work_status.empty',
|
||||
defaultMessage: 'Add employment status',
|
||||
description: 'Placeholder for empty account settings work status field.',
|
||||
},
|
||||
'account.settings.field.demographics.work_status.options.empty': {
|
||||
id: 'account.settings.field.demographics.work_status.options.empty',
|
||||
defaultMessage: 'Select employment status',
|
||||
description: 'Placeholder for the work status options dropdown.',
|
||||
},
|
||||
'account.settings.field.demographics.work_status.options.full-time': {
|
||||
id: 'account.settings.field.demographics.work_status.options.full-time',
|
||||
defaultMessage: 'Employed, working full-time',
|
||||
description: 'The label for the employed, working full-time work status option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_status.options.part-time': {
|
||||
id: 'account.settings.field.demographics.work_status.options.part-time',
|
||||
defaultMessage: 'Employed, working part-time',
|
||||
description: 'The label for the employed, working part-time work status option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_status.options.not-employed-looking': {
|
||||
id: 'account.settings.field.demographics.work_status.options.not-employed-looking',
|
||||
defaultMessage: 'Not employed, looking for work',
|
||||
description: 'The label for the not employed, looking for work work status option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_status.options.not-employed-not-looking': {
|
||||
id: 'account.settings.field.demographics.work_status.options.not-employed-not-looking',
|
||||
defaultMessage: 'Not employed, not looking for work',
|
||||
description: 'The label for the not employed, not looking for work work status option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_status.options.unable': {
|
||||
id: 'account.settings.field.demographics.work_status.options.unable',
|
||||
defaultMessage: 'Unable to work',
|
||||
description: 'The label for the unable to work work status option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_status.options.retired': {
|
||||
id: 'account.settings.field.demographics.work_status.options.retired',
|
||||
defaultMessage: 'Retired',
|
||||
description: 'The label for the retired work status option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_status.options.other': {
|
||||
id: 'account.settings.field.demographics.work_status.options.other',
|
||||
defaultMessage: 'Other',
|
||||
description: 'The label for the other work status option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_status_description': {
|
||||
id: 'account.settings.field.demographics.work_status_description',
|
||||
defaultMessage: 'Employment status description',
|
||||
description: 'Label for account settings work status description field.',
|
||||
},
|
||||
'account.settings.field.demographics.work_status_description.empty': {
|
||||
id: 'account.settings.field.demographics.work_status_description.empty',
|
||||
defaultMessage: 'Enter description',
|
||||
description: 'Placeholder for empty account settings work status description field.',
|
||||
},
|
||||
'account.settings.field.demographics.current_work_sector': {
|
||||
id: 'account.settings.field.demographics.current_work_sector',
|
||||
defaultMessage: 'Current work industry',
|
||||
description: 'Label for account settings current work sector field.',
|
||||
},
|
||||
'account.settings.field.demographics.current_work_sector.empty': {
|
||||
id: 'account.settings.field.demographics.current_work_sector.empty',
|
||||
defaultMessage: 'Add work industry',
|
||||
description: 'Placeholder for empty account settings current work sector field.',
|
||||
},
|
||||
'account.settings.field.demographics.future_work_sector': {
|
||||
id: 'account.settings.field.demographics.future_work_sector',
|
||||
defaultMessage: 'Future work industry',
|
||||
description: 'Label for account settings future work sector field.',
|
||||
},
|
||||
'account.settings.field.demographics.future_work_sector.empty': {
|
||||
id: 'account.settings.field.demographics.future_work_sector.empty',
|
||||
defaultMessage: 'Add work industry',
|
||||
description: 'Placeholder for empty account settings future work sector field.',
|
||||
},
|
||||
'account.settings.field.demographics.work_sector.options.empty': {
|
||||
id: 'account.settings.field.demographics.work_sector.options.empty',
|
||||
defaultMessage: 'Select work industry',
|
||||
description: 'Placeholder for the work sector options dropdown.',
|
||||
},
|
||||
'account.settings.field.demographics.work_sector.options.accommodation-food': {
|
||||
id: 'account.settings.field.demographics.work_sector.options.accommodation-food',
|
||||
defaultMessage: 'Accommodation and Food Services',
|
||||
description: 'The label for the Accommodation and Food Services work sector option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_sector.options.administrative-support-waste-remediation': {
|
||||
id: 'account.settings.field.demographics.work_sector.options.administrative-support-waste-remediation',
|
||||
defaultMessage: 'Administrative and Support and Waste Management and Remediation Services',
|
||||
description: 'The label for the Administrative and Support and Waste Management and Remediation Services work sector option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_sector.options.agriculture-forestry-fishing-hunting': {
|
||||
id: 'account.settings.field.demographics.work_sector.options.agriculture-forestry-fishing-hunting',
|
||||
defaultMessage: 'Agriculture, Forestry, Fishing and Hunting',
|
||||
description: 'The label for the Agriculture, Forestry, Fishing and Hunting work sector option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_sector.options.arts-entertainment-recreation': {
|
||||
id: 'account.settings.field.demographics.work_sector.options.arts-entertainment-recreation',
|
||||
defaultMessage: 'Arts, Entertainment, and Recreation',
|
||||
description: 'The label for the Arts, Entertainment, and Recreation work sector option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_sector.options.construction': {
|
||||
id: 'account.settings.field.demographics.work_sector.options.construction',
|
||||
defaultMessage: 'Construction',
|
||||
description: 'The label for the Construction work sector option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_sector.options.educational': {
|
||||
id: 'account.settings.field.demographics.work_sector.options.educational',
|
||||
defaultMessage: 'Education Services',
|
||||
description: 'The label for the Education Services work sector option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_sector.options.finance-insurance': {
|
||||
id: 'account.settings.field.demographics.work_sector.options.finance-insurance',
|
||||
defaultMessage: 'Finance and Insurance',
|
||||
description: 'The label for the Finance and Insurance work sector option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_sector.options.healthcare-social': {
|
||||
id: 'account.settings.field.demographics.work_sector.options.healthcare-social',
|
||||
defaultMessage: 'Health Care and Social Assistance',
|
||||
description: 'The label for the Health Care and Social Assistance work sector option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_sector.options.information': {
|
||||
id: 'account.settings.field.demographics.work_sector.options.information',
|
||||
defaultMessage: 'Information',
|
||||
description: 'The label for the Information work sector option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_sector.options.management': {
|
||||
id: 'account.settings.field.demographics.work_sector.options.management',
|
||||
defaultMessage: 'Management of Companies and Enterprises',
|
||||
description: 'The label for the Management of Companies and Enterprises work sector option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_sector.options.manufacturing': {
|
||||
id: 'account.settings.field.demographics.work_sector.options.manufacturing',
|
||||
defaultMessage: 'Manufacturing',
|
||||
description: 'The label for the Manufacturing work sector option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_sector.options.mining-quarry-oil-gas': {
|
||||
id: 'account.settings.field.demographics.work_sector.options.mining-quarry-oil-gas',
|
||||
defaultMessage: 'Mining, Quarrying, and Oil and Gas Extraction',
|
||||
description: 'The label for the Mining, Quarrying, and Oil and Gas Extraction work sector option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_sector.options.professional-scientific-technical': {
|
||||
id: 'account.settings.field.demographics.work_sector.options.professional-scientific-technical',
|
||||
defaultMessage: 'Professional, Scientific, and Technical Services',
|
||||
description: 'The label for the Professional, Scientific, and Technical Services work sector option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_sector.options.public-admin': {
|
||||
id: 'account.settings.field.demographics.work_sector.options.public-admin',
|
||||
defaultMessage: 'Public Administration',
|
||||
description: 'The label for the Public Administration work sector option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_sector.options.real-estate': {
|
||||
id: 'account.settings.field.demographics.work_sector.options.real-estate',
|
||||
defaultMessage: 'Real Estate and Rental and Leasing',
|
||||
description: 'The label for the Real Estate and Rental and Leasing work sector option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_sector.options.retail': {
|
||||
id: 'account.settings.field.demographics.work_sector.options.retail',
|
||||
defaultMessage: 'Retail Trade',
|
||||
description: 'The label for the Retail Trade work sector option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_sector.options.transport-warehousing': {
|
||||
id: 'account.settings.field.demographics.work_sector.options.transport-warehousing',
|
||||
defaultMessage: 'Transportation and Warehousing',
|
||||
description: 'The label for the Transportation and Warehousing work sector option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_sector.options.utilities': {
|
||||
id: 'account.settings.field.demographics.work_sector.options.utilities',
|
||||
defaultMessage: 'Utilities',
|
||||
description: 'The label for the Utilities work sector option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_sector.options.trade': {
|
||||
id: 'account.settings.field.demographics.work_sector.options.trade',
|
||||
defaultMessage: 'Wholesale Trade',
|
||||
description: 'The label for the Wholesale Trade work sector option.',
|
||||
},
|
||||
'account.settings.field.demographics.work_sector.options.other': {
|
||||
id: 'account.settings.field.demographics.work_sector.options.other',
|
||||
defaultMessage: 'Other',
|
||||
description: 'The label for the Other work sector option.',
|
||||
},
|
||||
'account.settings.field.demographics.options.declined': {
|
||||
id: 'account.settings.field.demographics.options.declined',
|
||||
defaultMessage: 'Prefer not to respond',
|
||||
description: 'The label for the declined option.',
|
||||
},
|
||||
});
|
||||
|
||||
export default messages;
|
||||
63
src/account-settings/demographics/data/service.js
Normal file
63
src/account-settings/demographics/data/service.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { convertData, TO, FROM } from './utils';
|
||||
|
||||
/**
|
||||
* post all of the data related to demographics.
|
||||
* @param {Number} userId users are identified in the api by LMS id
|
||||
* @param {Object} commitValues { demographics }
|
||||
*/
|
||||
export async function postDemographics(userId) {
|
||||
const requestUrl = `${getConfig().DEMOGRAPHICS_BASE_URL}/demographics/api/v1/demographics/`;
|
||||
const commitValues = { user: userId };
|
||||
let data = {};
|
||||
|
||||
({ data } = await getAuthenticatedHttpClient()
|
||||
.post(requestUrl, commitValues)
|
||||
.catch((error) => {
|
||||
const apiError = Object.create(error);
|
||||
throw apiError;
|
||||
}));
|
||||
|
||||
return convertData(data, FROM);
|
||||
}
|
||||
|
||||
/**
|
||||
* get all data related to the demographics.
|
||||
* @param {Number} userId users are identified in the api by LMS id
|
||||
*/
|
||||
export async function getDemographics(userId) {
|
||||
const requestUrl = `${getConfig().DEMOGRAPHICS_BASE_URL}/demographics/api/v1/demographics/${userId}/`;
|
||||
let data = {};
|
||||
|
||||
try {
|
||||
({ data } = await getAuthenticatedHttpClient()
|
||||
.get(requestUrl));
|
||||
|
||||
data = convertData(data, FROM);
|
||||
} catch (error) {
|
||||
data = await postDemographics(userId);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* patch all of the data related to demographics.
|
||||
* @param {Number} userId users are identified in the api by LMS id
|
||||
* @param {Object} commitValues { demographics }
|
||||
*/
|
||||
export async function patchDemographics(userId, commitValues) {
|
||||
const requestUrl = `${getConfig().DEMOGRAPHICS_BASE_URL}/demographics/api/v1/demographics/${userId}/`;
|
||||
const convertedCommitValues = convertData(commitValues, TO);
|
||||
let data = {};
|
||||
|
||||
({ data } = await getAuthenticatedHttpClient()
|
||||
.patch(requestUrl, convertedCommitValues)
|
||||
.catch((error) => {
|
||||
const apiError = Object.create(error);
|
||||
throw apiError;
|
||||
}));
|
||||
|
||||
return convertData(data, FROM);
|
||||
}
|
||||
50
src/account-settings/demographics/data/utils.js
Normal file
50
src/account-settings/demographics/data/utils.js
Normal file
@@ -0,0 +1,50 @@
|
||||
export const TO = 'to';
|
||||
export const FROM = 'from';
|
||||
|
||||
// Frontend wants (example):
|
||||
// demographics_user_ethnicity: ["asian", "white", "other"]
|
||||
//
|
||||
// Demographics wants (example):
|
||||
// user_ethnicity: [
|
||||
// { ethnicity: "asian" },
|
||||
// { ethnicity: "white" },
|
||||
// { ethnicity: "other" }
|
||||
// ]
|
||||
function convertEthnicity(ethnicityData, direction) {
|
||||
if (direction === FROM) {
|
||||
return ethnicityData.map(e => e.ethnicity);
|
||||
}
|
||||
|
||||
if (direction === TO) {
|
||||
return ethnicityData.map(e => ({ ethnicity: e }));
|
||||
}
|
||||
|
||||
return ethnicityData;
|
||||
}
|
||||
|
||||
// Handles conversion of data to/from Demographics IDA to/from format needed for
|
||||
// frontend
|
||||
// * handles ethnicity field
|
||||
// * adds/removes 'demographics' to/from key
|
||||
// * replace `null` with empty string or empty string with null
|
||||
export function convertData(dataObject, direction) {
|
||||
const converted = {};
|
||||
|
||||
Object.entries(dataObject).forEach(([key, value]) => {
|
||||
let newValue = value;
|
||||
|
||||
if (key.includes('ethnicity')) {
|
||||
newValue = convertEthnicity(value, direction);
|
||||
}
|
||||
|
||||
if (direction === TO) {
|
||||
converted[key.replace('demographics_', '')] = newValue || null;
|
||||
}
|
||||
|
||||
if (direction === FROM) {
|
||||
converted[`demographics_${key}`] = newValue || '';
|
||||
}
|
||||
});
|
||||
|
||||
return converted;
|
||||
}
|
||||
@@ -64,6 +64,8 @@ initialize({
|
||||
mergeConfig({
|
||||
SUPPORT_URL: process.env.SUPPORT_URL,
|
||||
COACHING_ENABLED: (process.env.COACHING_ENABLED || false),
|
||||
ENABLE_DEMOGRAPHICS_COLLECTION: (process.env.ENABLE_DEMOGRAPHICS_COLLECTION || false),
|
||||
DEMOGRAPHICS_BASE_URL: process.env.DEMOGRAPHICS_BASE_URL,
|
||||
}, 'App loadConfig override handler');
|
||||
},
|
||||
},
|
||||
|
||||
@@ -51,3 +51,13 @@ $fa-font-path: "~font-awesome/fonts";
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
.checkboxOption {
|
||||
input:focus {
|
||||
outline: -webkit-focus-ring-color auto 5px;
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user