Compare commits
29 Commits
kdmccormic
...
open-relea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6aedbea29 | ||
|
|
6e099457db | ||
|
|
54fa5b970a | ||
|
|
38371d1a46 | ||
|
|
f805480e21 | ||
|
|
cc3bf06a6f | ||
|
|
0e8d7622c6 | ||
|
|
9faf28203b | ||
|
|
e94d05d9e2 | ||
|
|
8b4f7818fc | ||
|
|
f18c71d13a | ||
|
|
a1aeb7035e | ||
|
|
ef85448e27 | ||
|
|
741cfb6aac | ||
|
|
c808c8c0a1 | ||
|
|
4eb96e64c7 | ||
|
|
f1ec989054 | ||
|
|
54032e6ec5 | ||
|
|
ef5c303fbc | ||
|
|
caa06a08b0 | ||
|
|
5e4278ea5a | ||
|
|
96dc3f7e3f | ||
|
|
5726be2805 | ||
|
|
73c66d5d18 | ||
|
|
19ef66cf42 | ||
|
|
c83d76e1a9 | ||
|
|
2f2abd54ff | ||
|
|
0b00fa21a2 | ||
|
|
1aa6a22c1c |
@@ -6,7 +6,7 @@ ECOMMERCE_BASE_URL='http://localhost:18130'
|
|||||||
LANGUAGE_PREFERENCE_COOKIE_NAME='openedx-language-preference'
|
LANGUAGE_PREFERENCE_COOKIE_NAME='openedx-language-preference'
|
||||||
LMS_BASE_URL='http://localhost:18000'
|
LMS_BASE_URL='http://localhost:18000'
|
||||||
LOGIN_URL='http://localhost:18000/login'
|
LOGIN_URL='http://localhost:18000/login'
|
||||||
LOGOUT_URL='http://localhost:18000/login'
|
LOGOUT_URL='http://localhost:18000/logout'
|
||||||
MARKETING_SITE_BASE_URL='http://localhost:18000'
|
MARKETING_SITE_BASE_URL='http://localhost:18000'
|
||||||
NODE_ENV='development'
|
NODE_ENV='development'
|
||||||
ORDER_HISTORY_URL='localhost:1996/orders'
|
ORDER_HISTORY_URL='localhost:1996/orders'
|
||||||
@@ -17,4 +17,4 @@ SITE_NAME='edX'
|
|||||||
SUPPORT_URL='http://localhost:18000/support'
|
SUPPORT_URL='http://localhost:18000/support'
|
||||||
USER_INFO_COOKIE_NAME='edx-user-info'
|
USER_INFO_COOKIE_NAME='edx-user-info'
|
||||||
# Temporary, Remove this once we are ready to release the feature.
|
# Temporary, Remove this once we are ready to release the feature.
|
||||||
COACHING_ENABLED=''
|
COACHING_ENABLED=true
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ ECOMMERCE_BASE_URL='http://localhost:18130'
|
|||||||
LANGUAGE_PREFERENCE_COOKIE_NAME='openedx-language-preference'
|
LANGUAGE_PREFERENCE_COOKIE_NAME='openedx-language-preference'
|
||||||
LMS_BASE_URL='http://localhost:18000'
|
LMS_BASE_URL='http://localhost:18000'
|
||||||
LOGIN_URL='http://localhost:18000/login'
|
LOGIN_URL='http://localhost:18000/login'
|
||||||
LOGOUT_URL='http://localhost:18000/login'
|
LOGOUT_URL='http://localhost:18000/logout'
|
||||||
MARKETING_SITE_BASE_URL='http://localhost:18000'
|
MARKETING_SITE_BASE_URL='http://localhost:18000'
|
||||||
NODE_ENV=null
|
NODE_ENV=null
|
||||||
ORDER_HISTORY_URL='localhost:1996/orders'
|
ORDER_HISTORY_URL='localhost:1996/orders'
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ import {
|
|||||||
YEAR_OF_BIRTH_OPTIONS,
|
YEAR_OF_BIRTH_OPTIONS,
|
||||||
EDUCATION_LEVELS,
|
EDUCATION_LEVELS,
|
||||||
GENDER_OPTIONS,
|
GENDER_OPTIONS,
|
||||||
|
COUNTRY_WITH_STATES,
|
||||||
|
getStatesList,
|
||||||
} from './data/constants';
|
} from './data/constants';
|
||||||
import { fetchSiteLanguages } from './site-language';
|
import { fetchSiteLanguages } from './site-language';
|
||||||
import CoachingToggle from './coaching/CoachingToggle';
|
import CoachingToggle from './coaching/CoachingToggle';
|
||||||
@@ -82,11 +84,15 @@ class AccountSettingsPage extends React.Component {
|
|||||||
return concatTimeZoneOptions;
|
return concatTimeZoneOptions;
|
||||||
});
|
});
|
||||||
|
|
||||||
getLocalizedOptions = memoize(locale => ({
|
getLocalizedOptions = memoize((locale, country) => ({
|
||||||
countryOptions: [{
|
countryOptions: [{
|
||||||
value: '',
|
value: '',
|
||||||
label: this.props.intl.formatMessage(messages['account.settings.field.country.options.empty']),
|
label: this.props.intl.formatMessage(messages['account.settings.field.country.options.empty']),
|
||||||
}].concat(getCountryList(locale).map(({ code, name }) => ({ value: code, label: name }))),
|
}].concat(getCountryList(locale).map(({ code, name }) => ({ value: code, label: name }))),
|
||||||
|
stateOptions: [{
|
||||||
|
value: '',
|
||||||
|
label: this.props.intl.formatMessage(messages['account.settings.field.state.options.empty']),
|
||||||
|
}].concat(getStatesList(country)),
|
||||||
languageProficiencyOptions: [{
|
languageProficiencyOptions: [{
|
||||||
value: '',
|
value: '',
|
||||||
label: this.props.intl.formatMessage(messages['account.settings.field.language_proficiencies.options.empty']),
|
label: this.props.intl.formatMessage(messages['account.settings.field.language_proficiencies.options.empty']),
|
||||||
@@ -184,7 +190,7 @@ class AccountSettingsPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderSecondaryEmailField(editableFieldProps) {
|
renderSecondaryEmailField(editableFieldProps) {
|
||||||
if (this.props.hiddenFields.includes('secondary_email')) {
|
if (!Boolean(this.props.formValues.secondary_email_enabled)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,11 +215,15 @@ class AccountSettingsPage extends React.Component {
|
|||||||
// Memoized options lists
|
// Memoized options lists
|
||||||
const {
|
const {
|
||||||
countryOptions,
|
countryOptions,
|
||||||
|
stateOptions,
|
||||||
languageProficiencyOptions,
|
languageProficiencyOptions,
|
||||||
yearOfBirthOptions,
|
yearOfBirthOptions,
|
||||||
educationLevelOptions,
|
educationLevelOptions,
|
||||||
genderOptions,
|
genderOptions,
|
||||||
} = this.getLocalizedOptions(this.context.locale);
|
} = this.getLocalizedOptions(this.context.locale, this.props.formValues.country);
|
||||||
|
|
||||||
|
// Show State field only if the country is US (could include Canada later)
|
||||||
|
const showState = this.props.formValues.country == COUNTRY_WITH_STATES;
|
||||||
|
|
||||||
const timeZoneOptions = this.getLocalizedTimeZoneOptions(
|
const timeZoneOptions = this.getLocalizedTimeZoneOptions(
|
||||||
this.props.timeZoneOptions,
|
this.props.timeZoneOptions,
|
||||||
@@ -294,6 +304,22 @@ class AccountSettingsPage extends React.Component {
|
|||||||
isEditable={this.isEditable('country')}
|
isEditable={this.isEditable('country')}
|
||||||
{...editableFieldProps}
|
{...editableFieldProps}
|
||||||
/>
|
/>
|
||||||
|
{showState &&
|
||||||
|
<EditableField
|
||||||
|
name="state"
|
||||||
|
type="select"
|
||||||
|
value={this.props.formValues.state}
|
||||||
|
options={stateOptions}
|
||||||
|
label={this.props.intl.formatMessage(messages['account.settings.field.state'])}
|
||||||
|
emptyLabel={
|
||||||
|
this.isEditable('state') ?
|
||||||
|
this.props.intl.formatMessage(messages['account.settings.field.state.empty']) :
|
||||||
|
this.renderEmptyStaticFieldMessage()
|
||||||
|
}
|
||||||
|
isEditable={this.isEditable('state')}
|
||||||
|
{...editableFieldProps}
|
||||||
|
/>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="account-section" id="profile-information">
|
<div className="account-section" id="profile-information">
|
||||||
@@ -488,11 +514,11 @@ AccountSettingsPage.propTypes = {
|
|||||||
social_link_facebook: PropTypes.string,
|
social_link_facebook: PropTypes.string,
|
||||||
social_link_twitter: PropTypes.string,
|
social_link_twitter: PropTypes.string,
|
||||||
time_zone: PropTypes.string,
|
time_zone: PropTypes.string,
|
||||||
coaching: PropTypes.objectOf(PropTypes.shape({
|
coaching: PropTypes.shape({
|
||||||
coaching_consent: PropTypes.string.isRequired,
|
coaching_consent: PropTypes.bool.isRequired,
|
||||||
user: PropTypes.number.isRequired,
|
user: PropTypes.number.isRequired,
|
||||||
eligible_for_coaching: PropTypes.bool.isRequired,
|
eligible_for_coaching: PropTypes.bool.isRequired,
|
||||||
})),
|
}),
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
siteLanguage: PropTypes.shape({
|
siteLanguage: PropTypes.shape({
|
||||||
previousValue: PropTypes.string,
|
previousValue: PropTypes.string,
|
||||||
@@ -504,8 +530,8 @@ AccountSettingsPage.propTypes = {
|
|||||||
})),
|
})),
|
||||||
profileDataManager: PropTypes.string,
|
profileDataManager: PropTypes.string,
|
||||||
staticFields: PropTypes.arrayOf(PropTypes.string),
|
staticFields: PropTypes.arrayOf(PropTypes.string),
|
||||||
hiddenFields: PropTypes.arrayOf(PropTypes.string),
|
|
||||||
isActive: PropTypes.bool,
|
isActive: PropTypes.bool,
|
||||||
|
secondary_email_enabled: PropTypes.bool,
|
||||||
|
|
||||||
timeZoneOptions: PropTypes.arrayOf(PropTypes.shape({
|
timeZoneOptions: PropTypes.arrayOf(PropTypes.shape({
|
||||||
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||||
@@ -532,9 +558,9 @@ AccountSettingsPage.defaultProps = {
|
|||||||
countryTimeZoneOptions: [],
|
countryTimeZoneOptions: [],
|
||||||
profileDataManager: null,
|
profileDataManager: null,
|
||||||
staticFields: [],
|
staticFields: [],
|
||||||
hiddenFields: ['secondary_email'],
|
|
||||||
tpaProviders: [],
|
tpaProviders: [],
|
||||||
isActive: true,
|
isActive: true,
|
||||||
|
secondary_email_enabled: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(accountSettingsPageSelector, {
|
export default connect(accountSettingsPageSelector, {
|
||||||
|
|||||||
@@ -156,6 +156,21 @@ const messages = defineMessages({
|
|||||||
defaultMessage: 'Select a Country',
|
defaultMessage: 'Select a Country',
|
||||||
description: 'Option for empty value on account settings country field.',
|
description: 'Option for empty value on account settings country field.',
|
||||||
},
|
},
|
||||||
|
'account.settings.field.state': {
|
||||||
|
id: 'account.settings.field.state',
|
||||||
|
defaultMessage: 'State',
|
||||||
|
description: 'Label for account settings state field.',
|
||||||
|
},
|
||||||
|
'account.settings.field.state.empty': {
|
||||||
|
id: 'account.settings.field.state.empty',
|
||||||
|
defaultMessage: 'Add state',
|
||||||
|
description: 'Placeholder for empty account settings state field.',
|
||||||
|
},
|
||||||
|
'account.settings.field.state.options.empty': {
|
||||||
|
id: 'account.settings.field.state.options.empty',
|
||||||
|
defaultMessage: 'Select a State',
|
||||||
|
description: 'Option for empty value on account settings state field.',
|
||||||
|
},
|
||||||
'account.settings.field.site.language': {
|
'account.settings.field.site.language': {
|
||||||
id: 'account.settings.field.site.language',
|
id: 'account.settings.field.site.language',
|
||||||
defaultMessage: 'Site language',
|
defaultMessage: 'Site language',
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import PageLoading from '../PageLoading';
|
|||||||
import CoachingConsentForm from './CoachingConsentForm';
|
import CoachingConsentForm from './CoachingConsentForm';
|
||||||
import messages from './CoachingConsent.messages';
|
import messages from './CoachingConsent.messages';
|
||||||
import LogoSVG from '../../logo.svg';
|
import LogoSVG from '../../logo.svg';
|
||||||
import { fetchSettings, saveSettings } from '../data/actions';
|
import { fetchSettings, saveSettings, saveMultipleSettings } from '../data/actions';
|
||||||
import { coachingConsentPageSelector } from '../data/selectors';
|
import { coachingConsentPageSelector } from '../data/selectors';
|
||||||
|
|
||||||
const Logo = ({ src, alt, ...attributes }) => (
|
const Logo = ({ src, alt, ...attributes }) => (
|
||||||
@@ -96,9 +96,7 @@ class CoachingConsent extends React.Component {
|
|||||||
// Check if all values from the form have confirmation values
|
// Check if all values from the form have confirmation values
|
||||||
if (
|
if (
|
||||||
this.state.formSubmitted &&
|
this.state.formSubmitted &&
|
||||||
this.props.confirmationValues.coaching &&
|
this.props.saveState === 'complete'
|
||||||
this.props.confirmationValues.name &&
|
|
||||||
this.props.confirmationValues.phone_number
|
|
||||||
) {
|
) {
|
||||||
allSubmissionsComplete = true;
|
allSubmissionsComplete = true;
|
||||||
}
|
}
|
||||||
@@ -125,21 +123,39 @@ class CoachingConsent extends React.Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
formErrors: {},
|
formErrors: {},
|
||||||
formSubmitted: true,
|
formSubmitted: true,
|
||||||
|
declineSubmitted: false,
|
||||||
});
|
});
|
||||||
// Must store target values or they disappear before the async function can use them.
|
// Must store target values or they disappear before the async function can use them.
|
||||||
const fullName = e.target.fullName.value;
|
const fullName = e.target.fullName.value;
|
||||||
const phoneNumber = e.target.phoneNumber.value;
|
const phoneNumber = e.target.phoneNumber.value;
|
||||||
const coachingValues = this.props.formValues.coaching;
|
const coachingValues = this.props.formValues.coaching;
|
||||||
|
|
||||||
// These will overwrite each other's redux states (see componentDidUpdate note)
|
// !important: The order of this data matters!
|
||||||
this.props.saveSettings('name', fullName);
|
// The order that this data is in, is the order that the saveSettings() function
|
||||||
this.props.saveSettings('phone_number', phoneNumber);
|
// is called.
|
||||||
this.props.saveSettings('coaching', {
|
const settingsSubmissions = [];
|
||||||
...coachingValues,
|
if (!this.props.profileDataManager) {
|
||||||
phone_number: phoneNumber,
|
settingsSubmissions.push({
|
||||||
coaching_consent: true,
|
formId: 'name',
|
||||||
consent_form_seen: true,
|
commitValues: fullName,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
Array.prototype.push.apply(settingsSubmissions, [
|
||||||
|
{
|
||||||
|
formId: 'coaching',
|
||||||
|
commitValues: {
|
||||||
|
...coachingValues,
|
||||||
|
phone_number: phoneNumber,
|
||||||
|
coaching_consent: true,
|
||||||
|
consent_form_seen: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
formId: 'phone_number',
|
||||||
|
commitValues: phoneNumber,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
this.props.saveMultipleSettings(settingsSubmissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
async declineCoaching(e) {
|
async declineCoaching(e) {
|
||||||
@@ -147,6 +163,7 @@ class CoachingConsent extends React.Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
formErrors: {},
|
formErrors: {},
|
||||||
declineSubmitted: true,
|
declineSubmitted: true,
|
||||||
|
formSubmitted: false,
|
||||||
});
|
});
|
||||||
// Must store target values or they disappear before the async function can use them.
|
// Must store target values or they disappear before the async function can use them.
|
||||||
const coachingValues = this.props.formValues.coaching;
|
const coachingValues = this.props.formValues.coaching;
|
||||||
@@ -168,6 +185,7 @@ class CoachingConsent extends React.Component {
|
|||||||
formErrors={this.state.formErrors}
|
formErrors={this.state.formErrors}
|
||||||
formValues={this.props.formValues}
|
formValues={this.props.formValues}
|
||||||
redirectUrl={this.state.redirectUrl}
|
redirectUrl={this.state.redirectUrl}
|
||||||
|
profileDataManager={this.props.profileDataManager}
|
||||||
/>);
|
/>);
|
||||||
case VIEWS.SUCCESS_PENDING:
|
case VIEWS.SUCCESS_PENDING:
|
||||||
return <PageLoading srMessage="Submitting..." />;
|
return <PageLoading srMessage="Submitting..." />;
|
||||||
@@ -191,7 +209,6 @@ class CoachingConsent extends React.Component {
|
|||||||
const { loaded } = this.props;
|
const { loaded } = this.props;
|
||||||
const formHasErrors = Object.keys(this.state.formErrors).length > 0;
|
const formHasErrors = Object.keys(this.state.formErrors).length > 0;
|
||||||
let currentView = null;
|
let currentView = null;
|
||||||
|
|
||||||
// This amount of logic was making the template very hard to read, so I broke it out into views.
|
// This amount of logic was making the template very hard to read, so I broke it out into views.
|
||||||
if (!loaded) {
|
if (!loaded) {
|
||||||
currentView = VIEWS.NOT_LOADED;
|
currentView = VIEWS.NOT_LOADED;
|
||||||
@@ -260,6 +277,8 @@ AutoRedirect.propTypes = {
|
|||||||
|
|
||||||
CoachingConsent.defaultProps = {
|
CoachingConsent.defaultProps = {
|
||||||
loaded: false,
|
loaded: false,
|
||||||
|
saveState: undefined,
|
||||||
|
profileDataManager: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
CoachingConsent.propTypes = {
|
CoachingConsent.propTypes = {
|
||||||
@@ -285,9 +304,13 @@ CoachingConsent.propTypes = {
|
|||||||
}).isRequired,
|
}).isRequired,
|
||||||
fetchSettings: PropTypes.func.isRequired,
|
fetchSettings: PropTypes.func.isRequired,
|
||||||
saveSettings: PropTypes.func.isRequired,
|
saveSettings: PropTypes.func.isRequired,
|
||||||
|
saveMultipleSettings: PropTypes.func.isRequired,
|
||||||
|
saveState: PropTypes.string,
|
||||||
|
profileDataManager: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(coachingConsentPageSelector, {
|
export default connect(coachingConsentPageSelector, {
|
||||||
fetchSettings,
|
fetchSettings,
|
||||||
saveSettings,
|
saveSettings,
|
||||||
|
saveMultipleSettings,
|
||||||
})(injectIntl(CoachingConsent));
|
})(injectIntl(CoachingConsent));
|
||||||
|
|||||||
@@ -56,6 +56,11 @@ const messages = defineMessages({
|
|||||||
defaultMessage: 'Start my course',
|
defaultMessage: 'Start my course',
|
||||||
description: 'Text that the user will be sent back to the courseware',
|
description: 'Text that the user will be sent back to the courseware',
|
||||||
},
|
},
|
||||||
|
'account.settings.coaching.managed.support': {
|
||||||
|
id: 'account.settings.coaching.managed.support',
|
||||||
|
defaultMessage: 'support',
|
||||||
|
description: 'website support',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default messages;
|
export default messages;
|
||||||
|
|||||||
@@ -1,15 +1,30 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
import { getConfig } from '@edx/frontend-platform';
|
||||||
|
import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||||
import { Input, Button, Hyperlink } from '@edx/paragon';
|
import { Input, Button, Hyperlink } from '@edx/paragon';
|
||||||
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import Alert from '../Alert';
|
||||||
import messages from './CoachingConsent.messages';
|
import messages from './CoachingConsent.messages';
|
||||||
|
|
||||||
const ErrorMessage = props => (
|
const ErrorMessage = props => (
|
||||||
<div className="alert-warning mb-2">{props.message}</div>
|
<div className="alert-warning mb-2">{props.message}</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const ManagedProfileAlert = ({ profileDataManager }) => (
|
||||||
|
<Alert className="alert alert-primary" role="alert">
|
||||||
|
<FormattedMessage
|
||||||
|
id="account.settings.coaching.managed.alert"
|
||||||
|
defaultMessage="Your name is managed by {managerTitle}. Contact your administrator for help."
|
||||||
|
description="alert message informing the user their account data is managed by a third party"
|
||||||
|
values={{
|
||||||
|
managerTitle: <b>{profileDataManager}</b>,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Alert>
|
||||||
|
);
|
||||||
const CoachingForm = props => (
|
const CoachingForm = props => (
|
||||||
<div className="col-12 col-md-6 col-xl-5 mx-auto mt-4 p-5 shadow-lg">
|
<div className="col-12 col-md-6 col-xl-5 mx-auto mt-4 p-5 shadow-lg">
|
||||||
<h2 className="h2">
|
<h2 className="h2">
|
||||||
@@ -19,12 +34,17 @@ const CoachingForm = props => (
|
|||||||
<div>
|
<div>
|
||||||
<form onSubmit={props.onSubmit}>
|
<form onSubmit={props.onSubmit}>
|
||||||
<div className="py-3">
|
<div className="py-3">
|
||||||
|
{
|
||||||
|
!!props.profileDataManager &&
|
||||||
|
<ManagedProfileAlert profileDataManager={props.profileDataManager} />
|
||||||
|
}
|
||||||
<ErrorMessage message={props.formErrors.name} />
|
<ErrorMessage message={props.formErrors.name} />
|
||||||
<label className="h6" htmlFor="fullName">{props.intl.formatMessage(messages['account.settings.coaching.consent.label.name'])}</label>
|
<label className="h6" htmlFor="fullName">{props.intl.formatMessage(messages['account.settings.coaching.consent.label.name'])}</label>
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
name="full-name"
|
name="full-name"
|
||||||
id="fullName"
|
id="fullName"
|
||||||
|
disabled={!!props.profileDataManager}
|
||||||
defaultValue={props.formValues.name}
|
defaultValue={props.formValues.name}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -91,6 +111,7 @@ CoachingForm.propTypes = {
|
|||||||
phone_number: PropTypes.string,
|
phone_number: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
redirectUrl: PropTypes.string.isRequired,
|
redirectUrl: PropTypes.string.isRequired,
|
||||||
|
profileDataManager: PropTypes.string.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
ErrorMessage.defaultProps = {
|
ErrorMessage.defaultProps = {
|
||||||
@@ -101,4 +122,9 @@ ErrorMessage.propTypes = {
|
|||||||
message: PropTypes.string,
|
message: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ManagedProfileAlert.propTypes = {
|
||||||
|
profileDataManager: PropTypes.string.isRequired,
|
||||||
|
intl: intlShape.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
export default injectIntl(CoachingForm);
|
export default injectIntl(CoachingForm);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
|||||||
import { ValidationFormGroup, Input } from '@edx/paragon';
|
import { ValidationFormGroup, Input } from '@edx/paragon';
|
||||||
import messages from './CoachingToggle.messages';
|
import messages from './CoachingToggle.messages';
|
||||||
import { editableFieldSelector } from '../data/selectors';
|
import { editableFieldSelector } from '../data/selectors';
|
||||||
import { saveSettings, updateDraft } from '../data/actions';
|
import { saveSettings, updateDraft, saveMultipleSettings } from '../data/actions';
|
||||||
import EditableField from '../EditableField';
|
import EditableField from '../EditableField';
|
||||||
|
|
||||||
|
|
||||||
@@ -18,7 +18,25 @@ const CoachingToggle = props => (
|
|||||||
label={props.intl.formatMessage(messages['account.settings.field.phone_number'])}
|
label={props.intl.formatMessage(messages['account.settings.field.phone_number'])}
|
||||||
emptyLabel={props.intl.formatMessage(messages['account.settings.field.phone_number.empty'])}
|
emptyLabel={props.intl.formatMessage(messages['account.settings.field.phone_number.empty'])}
|
||||||
onChange={props.updateDraft}
|
onChange={props.updateDraft}
|
||||||
onSubmit={props.saveSettings}
|
onSubmit={() => {
|
||||||
|
const { coaching } = props;
|
||||||
|
if (coaching.coaching_consent === true) {
|
||||||
|
return props.saveMultipleSettings([
|
||||||
|
{
|
||||||
|
formId: 'coaching',
|
||||||
|
commitValues: {
|
||||||
|
...coaching,
|
||||||
|
phone_number: props.phone_number,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
formId: 'phone_number',
|
||||||
|
commitValues: props.phone_number,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return props.saveSettings('phone_number', props.phone_number);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<ValidationFormGroup
|
<ValidationFormGroup
|
||||||
for="coachingConsent"
|
for="coachingConsent"
|
||||||
@@ -37,9 +55,11 @@ const CoachingToggle = props => (
|
|||||||
value={props.coaching.coaching_consent}
|
value={props.coaching.coaching_consent}
|
||||||
onChange={async (e) => {
|
onChange={async (e) => {
|
||||||
const { name } = e.target;
|
const { name } = e.target;
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
const { user, eligible_for_coaching } = props.coaching;
|
||||||
const value = {
|
const value = {
|
||||||
...props.coaching,
|
user,
|
||||||
phone_number: props.phone_number,
|
eligible_for_coaching,
|
||||||
coaching_consent: e.target.checked,
|
coaching_consent: e.target.checked,
|
||||||
};
|
};
|
||||||
props.saveSettings(name, value);
|
props.saveSettings(name, value);
|
||||||
@@ -53,18 +73,20 @@ const CoachingToggle = props => (
|
|||||||
CoachingToggle.defaultProps = {
|
CoachingToggle.defaultProps = {
|
||||||
phone_number: '',
|
phone_number: '',
|
||||||
error: '',
|
error: '',
|
||||||
|
saveState: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
CoachingToggle.propTypes = {
|
CoachingToggle.propTypes = {
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
error: PropTypes.string,
|
error: PropTypes.string,
|
||||||
coaching: PropTypes.objectOf(PropTypes.shape({
|
coaching: PropTypes.shape({
|
||||||
coaching_consent: PropTypes.string.isRequired,
|
coaching_consent: PropTypes.bool.isRequired,
|
||||||
user: PropTypes.number.isRequired,
|
user: PropTypes.number.isRequired,
|
||||||
eligible_for_coaching: PropTypes.bool.isRequired,
|
eligible_for_coaching: PropTypes.bool.isRequired,
|
||||||
})).isRequired,
|
}).isRequired,
|
||||||
saveState: PropTypes.func.isRequired,
|
saveState: PropTypes.oneOf(['default', 'pending', 'complete', 'error']),
|
||||||
saveSettings: PropTypes.func.isRequired,
|
saveSettings: PropTypes.func.isRequired,
|
||||||
|
saveMultipleSettings: PropTypes.func.isRequired,
|
||||||
updateDraft: PropTypes.func.isRequired,
|
updateDraft: PropTypes.func.isRequired,
|
||||||
intl: intlShape.isRequired,
|
intl: intlShape.isRequired,
|
||||||
phone_number: PropTypes.string,
|
phone_number: PropTypes.string,
|
||||||
@@ -73,4 +95,5 @@ CoachingToggle.propTypes = {
|
|||||||
export default connect(editableFieldSelector, {
|
export default connect(editableFieldSelector, {
|
||||||
saveSettings,
|
saveSettings,
|
||||||
updateDraft,
|
updateDraft,
|
||||||
|
saveMultipleSettings,
|
||||||
})(injectIntl(CoachingToggle));
|
})(injectIntl(CoachingToggle));
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const messages = defineMessages({
|
|||||||
},
|
},
|
||||||
'account.settings.field.coaching_consent.tooltip': {
|
'account.settings.field.coaching_consent.tooltip': {
|
||||||
id: 'account.settings.field.coaching_consent.tooltip',
|
id: 'account.settings.field.coaching_consent.tooltip',
|
||||||
defaultMessage: 'MicroBachelors programs include text message based coaching that helps you pair educational experiences with your career goals through one-on-one advice. Coaching services are included at no additional cost, and are available in English and Spanish languages. Standard messaging rates apply. Text ‘STOP’ at anytime to opt-out of messages.',
|
defaultMessage: 'MicroBachelors programs include text message based coaching that helps you pair educational experiences with your career goals through one-on-one advice. Coaching services are included at no additional cost, and are available to learners with U.S. mobile phone numbers. Standard messaging rates apply. Text ‘STOP’ at anytime to opt-out of messages.',
|
||||||
description: 'A tooltip explaining what coaching is and who it is for',
|
description: 'A tooltip explaining what coaching is and who it is for',
|
||||||
},
|
},
|
||||||
'account.settings.field.coaching_consent.error': {
|
'account.settings.field.coaching_consent.error': {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||||
import { getConfig } from '@edx/frontend-platform';
|
import { getConfig } from '@edx/frontend-platform';
|
||||||
|
import get from 'lodash.get';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get all settings related to the coaching plugin. Settings used
|
* get all settings related to the coaching plugin. Settings used
|
||||||
@@ -7,21 +8,20 @@ import { getConfig } from '@edx/frontend-platform';
|
|||||||
* @param {Number} userId users are identified in the api by LMS id
|
* @param {Number} userId users are identified in the api by LMS id
|
||||||
*/
|
*/
|
||||||
export async function getCoachingPreferences(userId) {
|
export async function getCoachingPreferences(userId) {
|
||||||
let data = null;
|
let data = {};
|
||||||
try {
|
try {
|
||||||
({ data } = await getAuthenticatedHttpClient()
|
({ data } = await getAuthenticatedHttpClient()
|
||||||
.get(`${getConfig().LMS_BASE_URL}/api/coaching/v1/users/${userId}/`));
|
.get(`${getConfig().LMS_BASE_URL}/api/coaching/v1/users/${userId}/`));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Default values so the client doesn't fail if the user doesn't have an entry in the
|
// If a user isn't active the API call will fail with a lack of credentials.
|
||||||
// UserCoaching model yet, with the assumption that they'll be eligible for coaching
|
|
||||||
// when they hit this form.
|
|
||||||
data = {
|
data = {
|
||||||
coaching_consent: false,
|
coaching_consent: false,
|
||||||
user: userId,
|
user: userId,
|
||||||
eligible_for_coaching: true,
|
eligible_for_coaching: false,
|
||||||
consent_form_seen: false,
|
consent_form_seen: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,9 +40,11 @@ export async function patchCoachingPreferences(userId, commitValues) {
|
|||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
const apiError = Object.create(error);
|
const apiError = Object.create(error);
|
||||||
apiError.fieldErrors = JSON.parse(error.customAttributes.httpErrorResponseData);
|
apiError.fieldErrors = JSON.parse(error.customAttributes.httpErrorResponseData);
|
||||||
// eslint-disable-next-line prefer-destructuring
|
if (get(apiError, 'fieldErrors.phone_number')) {
|
||||||
apiError.fieldErrors.coaching = apiError.fieldErrors.phone_number[0];
|
// eslint-disable-next-line prefer-destructuring
|
||||||
delete apiError.fieldErrors.phone_number;
|
apiError.fieldErrors.coaching = apiError.fieldErrors.phone_number[0];
|
||||||
|
delete apiError.fieldErrors.phone_number;
|
||||||
|
}
|
||||||
throw apiError;
|
throw apiError;
|
||||||
});
|
});
|
||||||
return commitValues;
|
return commitValues;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { AsyncActionType } from './utils';
|
|||||||
|
|
||||||
export const FETCH_SETTINGS = new AsyncActionType('ACCOUNT_SETTINGS', 'FETCH_SETTINGS');
|
export const FETCH_SETTINGS = new AsyncActionType('ACCOUNT_SETTINGS', 'FETCH_SETTINGS');
|
||||||
export const SAVE_SETTINGS = new AsyncActionType('ACCOUNT_SETTINGS', 'SAVE_SETTINGS');
|
export const SAVE_SETTINGS = new AsyncActionType('ACCOUNT_SETTINGS', 'SAVE_SETTINGS');
|
||||||
|
export const SAVE_MULTIPLE_SETTINGS = new AsyncActionType('ACCOUNT_SETTINGS', 'SAVE_MULTIPLE_SETTINGS');
|
||||||
export const FETCH_TIME_ZONES = new AsyncActionType('ACCOUNT_SETTINGS', 'FETCH_TIME_ZONES');
|
export const FETCH_TIME_ZONES = new AsyncActionType('ACCOUNT_SETTINGS', 'FETCH_TIME_ZONES');
|
||||||
export const SAVE_PREVIOUS_SITE_LANGUAGE = 'SAVE_PREVIOUS_SITE_LANGUAGE';
|
export const SAVE_PREVIOUS_SITE_LANGUAGE = 'SAVE_PREVIOUS_SITE_LANGUAGE';
|
||||||
export const OPEN_FORM = 'OPEN_FORM';
|
export const OPEN_FORM = 'OPEN_FORM';
|
||||||
@@ -99,6 +100,25 @@ export const savePreviousSiteLanguage = previousSiteLanguage => ({
|
|||||||
payload: { previousSiteLanguage },
|
payload: { previousSiteLanguage },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const saveMultipleSettings = settingsArray => ({
|
||||||
|
type: SAVE_MULTIPLE_SETTINGS.BASE,
|
||||||
|
payload: { settingsArray },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const saveMultipleSettingsBegin = () => ({
|
||||||
|
type: SAVE_MULTIPLE_SETTINGS.BEGIN,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const saveMultipleSettingsSuccess = settingsArray => ({
|
||||||
|
type: SAVE_MULTIPLE_SETTINGS.SUCCESS,
|
||||||
|
payload: { settingsArray },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const saveMultipleSettingsFailure = ({ fieldErrors, message }) => ({
|
||||||
|
type: SAVE_MULTIPLE_SETTINGS.FAILURE,
|
||||||
|
payload: { errors: fieldErrors, message },
|
||||||
|
});
|
||||||
|
|
||||||
// FETCH TIME_ZONE ACTIONS
|
// FETCH TIME_ZONE ACTIONS
|
||||||
|
|
||||||
export const fetchTimeZones = country => ({
|
export const fetchTimeZones = country => ({
|
||||||
|
|||||||
@@ -31,4 +31,84 @@ export const GENDER_OPTIONS = [
|
|||||||
'o',
|
'o',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const COUNTRY_WITH_STATES = 'US';
|
||||||
|
|
||||||
export const TRANSIFEX_LANGUAGE_BASE_URL = 'https://www.transifex.com/open-edx/edx-platform/language/';
|
export const TRANSIFEX_LANGUAGE_BASE_URL = 'https://www.transifex.com/open-edx/edx-platform/language/';
|
||||||
|
|
||||||
|
const COUNTRY_STATES_MAP = {
|
||||||
|
CA: [
|
||||||
|
{ value: 'AB', label: 'Alberta' },
|
||||||
|
{ value: 'BC', label: 'British Columbia' },
|
||||||
|
{ value: 'MB', label: 'Manitoba' },
|
||||||
|
{ value: 'NB', label: 'New Brunswick' },
|
||||||
|
{ value: 'NL', label: 'Newfoundland and Labrador' },
|
||||||
|
{ value: 'NS', label: 'Nova Scotia' },
|
||||||
|
{ value: 'NT', label: 'Northwest Territories' },
|
||||||
|
{ value: 'NU', label: 'Nunavut' },
|
||||||
|
{ value: 'ON', label: 'Ontario' },
|
||||||
|
{ value: 'PE', label: 'Prince Edward Island' },
|
||||||
|
{ value: 'QC', label: 'Québec' },
|
||||||
|
{ value: 'SK', label: 'Saskatchewan' },
|
||||||
|
{ value: 'YT', label: 'Yukon' },
|
||||||
|
],
|
||||||
|
US: [
|
||||||
|
{ value: 'AL', label: 'Alabama' },
|
||||||
|
{ value: 'AK', label: 'Alaska' },
|
||||||
|
{ value: 'AZ', label: 'Arizona' },
|
||||||
|
{ value: 'AR', label: 'Arkansas' },
|
||||||
|
{ value: 'AA', label: 'Armed Forces Americas' },
|
||||||
|
{ value: 'AE', label: 'Armed Forces Europe' },
|
||||||
|
{ value: 'AP', label: 'Armed Forces Pacific' },
|
||||||
|
{ value: 'CA', label: 'California' },
|
||||||
|
{ value: 'CO', label: 'Colorado' },
|
||||||
|
{ value: 'CT', label: 'Connecticut' },
|
||||||
|
{ value: 'DE', label: 'Delaware' },
|
||||||
|
{ value: 'DC', label: 'District Of Columbia' },
|
||||||
|
{ value: 'FL', label: 'Florida' },
|
||||||
|
{ value: 'GA', label: 'Georgia' },
|
||||||
|
{ value: 'HI', label: 'Hawaii' },
|
||||||
|
{ value: 'ID', label: 'Idaho' },
|
||||||
|
{ value: 'IL', label: 'Illinois' },
|
||||||
|
{ value: 'IN', label: 'Indiana' },
|
||||||
|
{ value: 'IA', label: 'Iowa' },
|
||||||
|
{ value: 'KS', label: 'Kansas' },
|
||||||
|
{ value: 'KY', label: 'Kentucky' },
|
||||||
|
{ value: 'LA', label: 'Louisiana' },
|
||||||
|
{ value: 'ME', label: 'Maine' },
|
||||||
|
{ value: 'MD', label: 'Maryland' },
|
||||||
|
{ value: 'MA', label: 'Massachusetts' },
|
||||||
|
{ value: 'MI', label: 'Michigan' },
|
||||||
|
{ value: 'MN', label: 'Minnesota' },
|
||||||
|
{ value: 'MS', label: 'Mississippi' },
|
||||||
|
{ value: 'MO', label: 'Missouri' },
|
||||||
|
{ value: 'MT', label: 'Montana' },
|
||||||
|
{ value: 'NE', label: 'Nebraska' },
|
||||||
|
{ value: 'NV', label: 'Nevada' },
|
||||||
|
{ value: 'NH', label: 'New Hampshire' },
|
||||||
|
{ value: 'NJ', label: 'New Jersey' },
|
||||||
|
{ value: 'NM', label: 'New Mexico' },
|
||||||
|
{ value: 'NY', label: 'New York' },
|
||||||
|
{ value: 'NC', label: 'North Carolina' },
|
||||||
|
{ value: 'ND', label: 'North Dakota' },
|
||||||
|
{ value: 'OH', label: 'Ohio' },
|
||||||
|
{ value: 'OK', label: 'Oklahoma' },
|
||||||
|
{ value: 'OR', label: 'Oregon' },
|
||||||
|
{ value: 'PA', label: 'Pennsylvania' },
|
||||||
|
{ value: 'RI', label: 'Rhode Island' },
|
||||||
|
{ value: 'SC', label: 'South Carolina' },
|
||||||
|
{ value: 'SD', label: 'South Dakota' },
|
||||||
|
{ value: 'TN', label: 'Tennessee' },
|
||||||
|
{ value: 'TX', label: 'Texas' },
|
||||||
|
{ value: 'UT', label: 'Utah' },
|
||||||
|
{ value: 'VT', label: 'Vermont' },
|
||||||
|
{ value: 'VA', label: 'Virginia' },
|
||||||
|
{ value: 'WA', label: 'Washington' },
|
||||||
|
{ value: 'WV', label: 'West Virginia' },
|
||||||
|
{ value: 'WI', label: 'Wisconsin' },
|
||||||
|
{ value: 'WY', label: 'Wyoming' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getStatesList(country) {
|
||||||
|
return country && COUNTRY_STATES_MAP[country.toUpperCase()];
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
SAVE_PREVIOUS_SITE_LANGUAGE,
|
SAVE_PREVIOUS_SITE_LANGUAGE,
|
||||||
UPDATE_DRAFT,
|
UPDATE_DRAFT,
|
||||||
RESET_DRAFTS,
|
RESET_DRAFTS,
|
||||||
|
SAVE_MULTIPLE_SETTINGS,
|
||||||
} from './actions';
|
} from './actions';
|
||||||
|
|
||||||
import { reducer as deleteAccountReducer, DELETE_ACCOUNT } from '../delete-account';
|
import { reducer as deleteAccountReducer, DELETE_ACCOUNT } from '../delete-account';
|
||||||
@@ -144,6 +145,24 @@ const reducer = (state = defaultState, action) => {
|
|||||||
...state,
|
...state,
|
||||||
previousSiteLanguage: action.payload.previousSiteLanguage,
|
previousSiteLanguage: action.payload.previousSiteLanguage,
|
||||||
};
|
};
|
||||||
|
case SAVE_MULTIPLE_SETTINGS.BEGIN:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
saveState: 'pending',
|
||||||
|
};
|
||||||
|
|
||||||
|
case SAVE_MULTIPLE_SETTINGS.SUCCESS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
saveState: 'complete',
|
||||||
|
};
|
||||||
|
|
||||||
|
case SAVE_MULTIPLE_SETTINGS.FAILURE:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
saveState: 'error',
|
||||||
|
errors: Object.assign({}, state.errors, action.payload.errors),
|
||||||
|
};
|
||||||
|
|
||||||
case FETCH_TIME_ZONES.SUCCESS:
|
case FETCH_TIME_ZONES.SUCCESS:
|
||||||
return {
|
return {
|
||||||
@@ -177,6 +196,7 @@ const reducer = (state = defaultState, action) => {
|
|||||||
|
|
||||||
case RESET_PASSWORD.BEGIN:
|
case RESET_PASSWORD.BEGIN:
|
||||||
case RESET_PASSWORD.SUCCESS:
|
case RESET_PASSWORD.SUCCESS:
|
||||||
|
case RESET_PASSWORD.FORBIDDEN:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
resetPassword: resetPasswordReducer(state.resetPassword, action),
|
resetPassword: resetPasswordReducer(state.resetPassword, action),
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
fetchSettingsFailure,
|
fetchSettingsFailure,
|
||||||
closeForm,
|
closeForm,
|
||||||
SAVE_SETTINGS,
|
SAVE_SETTINGS,
|
||||||
|
SAVE_MULTIPLE_SETTINGS,
|
||||||
saveSettingsBegin,
|
saveSettingsBegin,
|
||||||
saveSettingsSuccess,
|
saveSettingsSuccess,
|
||||||
saveSettingsFailure,
|
saveSettingsFailure,
|
||||||
@@ -19,6 +20,9 @@ import {
|
|||||||
FETCH_TIME_ZONES,
|
FETCH_TIME_ZONES,
|
||||||
fetchTimeZones,
|
fetchTimeZones,
|
||||||
fetchTimeZonesSuccess,
|
fetchTimeZonesSuccess,
|
||||||
|
saveMultipleSettingsBegin,
|
||||||
|
saveMultipleSettingsSuccess,
|
||||||
|
saveMultipleSettingsFailure,
|
||||||
} from './actions';
|
} from './actions';
|
||||||
|
|
||||||
// Sub-modules
|
// Sub-modules
|
||||||
@@ -100,6 +104,31 @@ export function* handleSaveSettings(action) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// handles mutiple settings saved at once, in order, and stops executing on first failure.
|
||||||
|
export function* handleSaveMultipleSettings(settings) {
|
||||||
|
try {
|
||||||
|
yield put(saveMultipleSettingsBegin());
|
||||||
|
const { username, userId } = getAuthenticatedUser();
|
||||||
|
const { settingsArray } = settings.payload;
|
||||||
|
for (let i = 0; i < settingsArray.length; i += 1) {
|
||||||
|
const { formId, commitValues } = settingsArray[i];
|
||||||
|
yield put(saveSettingsBegin());
|
||||||
|
const commitData = { [formId]: commitValues };
|
||||||
|
const savedSettings = yield call(patchSettings, username, commitData, userId);
|
||||||
|
yield put(saveSettingsSuccess(savedSettings, commitData));
|
||||||
|
}
|
||||||
|
yield put(saveMultipleSettingsSuccess(settings));
|
||||||
|
} catch (e) {
|
||||||
|
if (e.fieldErrors) {
|
||||||
|
yield put(saveMultipleSettingsFailure({ fieldErrors: e.fieldErrors }));
|
||||||
|
} else {
|
||||||
|
yield put(saveMultipleSettingsFailure(e.message));
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function* handleFetchTimeZones(action) {
|
export function* handleFetchTimeZones(action) {
|
||||||
const response = yield call(getTimeZones, action.payload.country);
|
const response = yield call(getTimeZones, action.payload.country);
|
||||||
yield put(fetchTimeZonesSuccess(response, action.payload.country));
|
yield put(fetchTimeZonesSuccess(response, action.payload.country));
|
||||||
@@ -109,6 +138,7 @@ export function* handleFetchTimeZones(action) {
|
|||||||
export default function* saga() {
|
export default function* saga() {
|
||||||
yield takeEvery(FETCH_SETTINGS.BASE, handleFetchSettings);
|
yield takeEvery(FETCH_SETTINGS.BASE, handleFetchSettings);
|
||||||
yield takeEvery(SAVE_SETTINGS.BASE, handleSaveSettings);
|
yield takeEvery(SAVE_SETTINGS.BASE, handleSaveSettings);
|
||||||
|
yield takeEvery(SAVE_MULTIPLE_SETTINGS.BASE, handleSaveMultipleSettings);
|
||||||
yield takeEvery(FETCH_TIME_ZONES.BASE, handleFetchTimeZones);
|
yield takeEvery(FETCH_TIME_ZONES.BASE, handleFetchTimeZones);
|
||||||
yield all([
|
yield all([
|
||||||
deleteAccountSaga(),
|
deleteAccountSaga(),
|
||||||
|
|||||||
@@ -73,10 +73,6 @@ export const staticFieldsSelector = createSelector(
|
|||||||
accountSettings => (accountSettings.profileDataManager ? ['name', 'email', 'country'] : []),
|
accountSettings => (accountSettings.profileDataManager ? ['name', 'email', 'country'] : []),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const hiddenFieldsSelector = createSelector(
|
|
||||||
accountSettingsSelector,
|
|
||||||
accountSettings => (accountSettings.profileDataManager ? [] : ['secondary_email']),
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If there's no draft present at all (undefined), use the original committed value.
|
* If there's no draft present at all (undefined), use the original committed value.
|
||||||
@@ -138,7 +134,6 @@ export const accountSettingsPageSelector = createSelector(
|
|||||||
formValuesSelector,
|
formValuesSelector,
|
||||||
profileDataManagerSelector,
|
profileDataManagerSelector,
|
||||||
staticFieldsSelector,
|
staticFieldsSelector,
|
||||||
hiddenFieldsSelector,
|
|
||||||
timeZonesSelector,
|
timeZonesSelector,
|
||||||
countryTimeZonesSelector,
|
countryTimeZonesSelector,
|
||||||
activeAccountSelector,
|
activeAccountSelector,
|
||||||
@@ -149,7 +144,6 @@ export const accountSettingsPageSelector = createSelector(
|
|||||||
formValues,
|
formValues,
|
||||||
profileDataManager,
|
profileDataManager,
|
||||||
staticFields,
|
staticFields,
|
||||||
hiddenFields,
|
|
||||||
timeZoneOptions,
|
timeZoneOptions,
|
||||||
countryTimeZoneOptions,
|
countryTimeZoneOptions,
|
||||||
activeAccount,
|
activeAccount,
|
||||||
@@ -165,7 +159,6 @@ export const accountSettingsPageSelector = createSelector(
|
|||||||
formValues,
|
formValues,
|
||||||
profileDataManager,
|
profileDataManager,
|
||||||
staticFields,
|
staticFields,
|
||||||
hiddenFields,
|
|
||||||
tpaProviders: accountSettings.thirdPartyAuth.providers,
|
tpaProviders: accountSettings.thirdPartyAuth.providers,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -173,16 +166,16 @@ export const accountSettingsPageSelector = createSelector(
|
|||||||
export const coachingConsentPageSelector = createSelector(
|
export const coachingConsentPageSelector = createSelector(
|
||||||
accountSettingsSelector,
|
accountSettingsSelector,
|
||||||
formValuesSelector,
|
formValuesSelector,
|
||||||
hiddenFieldsSelector,
|
|
||||||
activeAccountSelector,
|
activeAccountSelector,
|
||||||
|
profileDataManagerSelector,
|
||||||
saveStateSelector,
|
saveStateSelector,
|
||||||
confirmationValuesSelector,
|
confirmationValuesSelector,
|
||||||
errorSelector,
|
errorSelector,
|
||||||
(
|
(
|
||||||
accountSettings,
|
accountSettings,
|
||||||
formValues,
|
formValues,
|
||||||
hiddenFields,
|
|
||||||
activeAccount,
|
activeAccount,
|
||||||
|
profileDataManager,
|
||||||
saveState,
|
saveState,
|
||||||
confirmationValues,
|
confirmationValues,
|
||||||
errors,
|
errors,
|
||||||
@@ -191,8 +184,8 @@ export const coachingConsentPageSelector = createSelector(
|
|||||||
loaded: accountSettings.loaded,
|
loaded: accountSettings.loaded,
|
||||||
loadingError: accountSettings.loadingError,
|
loadingError: accountSettings.loadingError,
|
||||||
isActive: activeAccount,
|
isActive: activeAccount,
|
||||||
|
profileDataManager,
|
||||||
formValues,
|
formValues,
|
||||||
hiddenFields,
|
|
||||||
saveState,
|
saveState,
|
||||||
confirmationValues,
|
confirmationValues,
|
||||||
formErrors: errors,
|
formErrors: errors,
|
||||||
|
|||||||
@@ -137,12 +137,11 @@ export async function getProfileDataManager(username, userRoles) {
|
|||||||
const url = `${getConfig().LMS_BASE_URL}/enterprise/api/v1/enterprise-learner/?username=${username}`;
|
const url = `${getConfig().LMS_BASE_URL}/enterprise/api/v1/enterprise-learner/?username=${username}`;
|
||||||
const { data } = await getAuthenticatedHttpClient().get(url).catch(handleRequestError);
|
const { data } = await getAuthenticatedHttpClient().get(url).catch(handleRequestError);
|
||||||
|
|
||||||
if ('results' in data) {
|
if (data.results.length > 0) {
|
||||||
for (let i = 0; i < data.results.length; i += 1) {
|
const enterprise = data.results[0] && data.results[0].enterprise_customer;
|
||||||
const enterprise = data.results[i].enterprise_customer;
|
// To ensure that enterprise returned is current enterprise & it manages profile settings
|
||||||
if (enterprise.sync_learner_profile_data) {
|
if (enterprise && enterprise.sync_learner_profile_data) {
|
||||||
return enterprise.name;
|
return enterprise.name;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,7 +183,7 @@ export async function patchSettings(username, commitValues, userId) {
|
|||||||
// user/v1/preferences where it does update. This is the one we use.
|
// user/v1/preferences where it does update. This is the one we use.
|
||||||
const preferenceKeys = ['time_zone'];
|
const preferenceKeys = ['time_zone'];
|
||||||
const coachingKeys = ['coaching'];
|
const coachingKeys = ['coaching'];
|
||||||
const accountCommitValues = omit(commitValues, preferenceKeys);
|
const accountCommitValues = omit(commitValues, preferenceKeys, coachingKeys);
|
||||||
const preferenceCommitValues = pick(commitValues, preferenceKeys);
|
const preferenceCommitValues = pick(commitValues, preferenceKeys);
|
||||||
const coachingCommitValues = pick(commitValues, coachingKeys);
|
const coachingCommitValues = pick(commitValues, coachingKeys);
|
||||||
const patchRequests = [];
|
const patchRequests = [];
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ export class AsyncActionType {
|
|||||||
get RESET() {
|
get RESET() {
|
||||||
return `${this.topic}__${this.name}__RESET`;
|
return `${this.topic}__${this.name}__RESET`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get FORBIDDEN() {
|
||||||
|
return `${this.topic}__${this.name}__FORBIDDEN`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ describe('AsyncActionType', () => {
|
|||||||
expect(actionType.SUCCESS).toBe('HOUSE_CATS__START_THE_RACE__SUCCESS');
|
expect(actionType.SUCCESS).toBe('HOUSE_CATS__START_THE_RACE__SUCCESS');
|
||||||
expect(actionType.FAILURE).toBe('HOUSE_CATS__START_THE_RACE__FAILURE');
|
expect(actionType.FAILURE).toBe('HOUSE_CATS__START_THE_RACE__FAILURE');
|
||||||
expect(actionType.RESET).toBe('HOUSE_CATS__START_THE_RACE__RESET');
|
expect(actionType.RESET).toBe('HOUSE_CATS__START_THE_RACE__RESET');
|
||||||
|
expect(actionType.FORBIDDEN).toBe('HOUSE_CATS__START_THE_RACE__FORBIDDEN');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
|
||||||
|
import Alert from '../Alert';
|
||||||
|
|
||||||
|
const RequestInProgressAlert = (props) => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Alert
|
||||||
|
className="alert-warning mt-n2"
|
||||||
|
icon={<FontAwesomeIcon className="mr-2" icon={faExclamationTriangle} />}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id="account.settings.editable.field.password.reset.button.forbidden"
|
||||||
|
defaultMessage="Your previous request is in progress, please try again in few moments."
|
||||||
|
description="A message displayed when a previous password reset request is still in progress."
|
||||||
|
/>
|
||||||
|
</Alert>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RequestInProgressAlert;
|
||||||
@@ -7,6 +7,7 @@ import { StatefulButton } from '@edx/paragon';
|
|||||||
import { resetPassword } from './data/actions';
|
import { resetPassword } from './data/actions';
|
||||||
import messages from './messages';
|
import messages from './messages';
|
||||||
import ConfirmationAlert from './ConfirmationAlert';
|
import ConfirmationAlert from './ConfirmationAlert';
|
||||||
|
import RequestInProgressAlert from './RequestInProgressAlert';
|
||||||
|
|
||||||
const ResetPassword = (props) => {
|
const ResetPassword = (props) => {
|
||||||
const { email, intl, status } = props;
|
const { email, intl, status } = props;
|
||||||
@@ -43,6 +44,7 @@ const ResetPassword = (props) => {
|
|||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
{status === 'complete' ? <ConfirmationAlert email={email} /> : null}
|
{status === 'complete' ? <ConfirmationAlert email={email} /> : null}
|
||||||
|
{status === 'forbidden' ? <RequestInProgressAlert /> : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,3 +18,7 @@ export const resetPasswordSuccess = () => ({
|
|||||||
export const resetPasswordReset = () => ({
|
export const resetPasswordReset = () => ({
|
||||||
type: RESET_PASSWORD.RESET,
|
type: RESET_PASSWORD.RESET,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const resetPasswordForbidden = () => ({
|
||||||
|
type: RESET_PASSWORD.FORBIDDEN,
|
||||||
|
});
|
||||||
|
|||||||
@@ -17,6 +17,11 @@ const reducer = (state = defaultState, action = null) => {
|
|||||||
...state,
|
...state,
|
||||||
status: 'complete',
|
status: 'complete',
|
||||||
};
|
};
|
||||||
|
case RESET_PASSWORD.FORBIDDEN:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
status: 'forbidden',
|
||||||
|
};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
import { put, call, takeEvery } from 'redux-saga/effects';
|
import { put, call, takeEvery } from 'redux-saga/effects';
|
||||||
|
|
||||||
import { resetPasswordBegin, resetPasswordSuccess, RESET_PASSWORD } from './actions';
|
import { resetPasswordBegin, resetPasswordForbidden, resetPasswordSuccess, RESET_PASSWORD } from './actions';
|
||||||
import { postResetPassword } from './service';
|
import { postResetPassword } from './service';
|
||||||
|
|
||||||
function* handleResetPassword(action) {
|
function* handleResetPassword(action) {
|
||||||
yield put(resetPasswordBegin());
|
yield put(resetPasswordBegin());
|
||||||
const response = yield call(postResetPassword, action.payload.email);
|
try {
|
||||||
yield put(resetPasswordSuccess(response));
|
const response = yield call(postResetPassword, action.payload.email);
|
||||||
|
yield put(resetPasswordSuccess(response));
|
||||||
|
} catch (error) {
|
||||||
|
if (error.response && error.response.status === 403) {
|
||||||
|
yield put(resetPasswordForbidden(error));
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function* saga() {
|
export default function* saga() {
|
||||||
|
|||||||
@@ -33,6 +33,9 @@
|
|||||||
"account.settings.field.country": "Country",
|
"account.settings.field.country": "Country",
|
||||||
"account.settings.field.country.empty": "Add country",
|
"account.settings.field.country.empty": "Add country",
|
||||||
"account.settings.field.country.options.empty": "Select a Country",
|
"account.settings.field.country.options.empty": "Select a Country",
|
||||||
|
"account.settings.field.state": "State",
|
||||||
|
"account.settings.field.state.empty": "Add state",
|
||||||
|
"account.settings.field.state.options.empty": "Select a State",
|
||||||
"account.settings.field.site.language": "Site language",
|
"account.settings.field.site.language": "Site language",
|
||||||
"account.settings.field.site.language.help.text": "The language used throughout this site. This site is currently available in a limited number of languages.",
|
"account.settings.field.site.language.help.text": "The language used throughout this site. This site is currently available in a limited number of languages.",
|
||||||
"account.settings.field.education": "Education",
|
"account.settings.field.education": "Education",
|
||||||
@@ -76,10 +79,23 @@
|
|||||||
"account.settings.editable.field.action.edit": "Edit",
|
"account.settings.editable.field.action.edit": "Edit",
|
||||||
"account.settings.static.field.empty": "No value set. Contact your {enterprise} administrator to make changes.",
|
"account.settings.static.field.empty": "No value set. Contact your {enterprise} administrator to make changes.",
|
||||||
"account.settings.static.field.empty.no.admin": "No value set.",
|
"account.settings.static.field.empty.no.admin": "No value set.",
|
||||||
|
"account.settings.coaching.consent.welcome.header": "Let’s get started.",
|
||||||
|
"account.settings.coaching.consent.welcome.subheader": "We're here for you from start to finish",
|
||||||
|
"account.settings.coaching.consent.description": "MicroBachelors programs include coaching that focuses on your career, education, and how you'll achieve results through one-on-one communication with an experienced professional. If you’re interested, provide the information below and click “Submit,” and our coaching partner will connect with you via email and/or text message to help you move forward. Terms and conditions apply.*",
|
||||||
|
"account.settings.coaching.consent.text-messaging.disclaimer": "* Coaching services are included at no additional cost to learners with US phone numbers. Coaching includes recurring text messages. Message and data rates may apply. Text STOP to opt-out.",
|
||||||
|
"account.settings.coaching.consent.accept-coaching": "Sign up for coaching",
|
||||||
|
"account.settings.coaching.consent.decline-coaching": "I prefer not to be contacted with free coaching services",
|
||||||
|
"account.settings.coaching.consent.label.name": "Please confirm your name",
|
||||||
|
"account.settings.coaching.consent.label.phone-number": "Enter your mobile number",
|
||||||
|
"account.settings.coaching.consent.success.header": "Success!",
|
||||||
|
"account.settings.coaching.consent.success.message": "You're signed up for coaching. You will receive a text message confirmation.",
|
||||||
|
"account.settings.coaching.consent.success.continue": "Start my course",
|
||||||
|
"account.settings.coaching.managed.support": "support",
|
||||||
|
"account.settings.coaching.managed.alert": "Your name is managed by {managerTitle}. Contact your administrator for help.",
|
||||||
"account.settings.field.phone_number": "Phone Number",
|
"account.settings.field.phone_number": "Phone Number",
|
||||||
"account.settings.field.phone_number.empty": "Add a phone number",
|
"account.settings.field.phone_number.empty": "Add a phone number",
|
||||||
"account.settings.field.coaching_consent": "Coaching consent",
|
"account.settings.field.coaching_consent": "Coaching consent",
|
||||||
"account.settings.field.coaching_consent.tooltip": "MicroBachelors programs include text message based coaching that helps you pair educational experiences with your career goals through one-on-one advice. Coaching services are included at no additional cost, and are available in English and Spanish languages. Standard messaging rates apply. Text ‘STOP’ at anytime to opt-out of messages.",
|
"account.settings.field.coaching_consent.tooltip": "MicroBachelors programs include text message based coaching that helps you pair educational experiences with your career goals through one-on-one advice. Coaching services are included at no additional cost, and are available to learners with U.S. mobile phone numbers. Standard messaging rates apply. Text ‘STOP’ at anytime to opt-out of messages.",
|
||||||
"account.settings.field.coaching_consent.error": "A valid US phone number is required to opt into coaching",
|
"account.settings.field.coaching_consent.error": "A valid US phone number is required to opt into coaching",
|
||||||
"account.settings.delete.account.before.proceeding": "Before proceeding, please {actionLink}.",
|
"account.settings.delete.account.before.proceeding": "Before proceeding, please {actionLink}.",
|
||||||
"account.settings.delete.account.header": "Delete My Account",
|
"account.settings.delete.account.header": "Delete My Account",
|
||||||
@@ -109,6 +125,7 @@
|
|||||||
"account.settings.editable.field.password.reset.button.confirmation.support.link": "technical support",
|
"account.settings.editable.field.password.reset.button.confirmation.support.link": "technical support",
|
||||||
"account.settings.editable.field.password.reset.button.confirmation": "We've sent a message to {email}. Click the link in the message to reset your password. Didn't receive the message? Contact {technicalSupportLink}.",
|
"account.settings.editable.field.password.reset.button.confirmation": "We've sent a message to {email}. Click the link in the message to reset your password. Didn't receive the message? Contact {technicalSupportLink}.",
|
||||||
"account.settings.editable.field.password.reset.button": "Reset Password",
|
"account.settings.editable.field.password.reset.button": "Reset Password",
|
||||||
|
"account.settings.editable.field.password.reset.button.forbidden": "Your previous request is in progress, please try again in few moments.",
|
||||||
"account.settings.editable.field.password.reset.label": "Password",
|
"account.settings.editable.field.password.reset.label": "Password",
|
||||||
"account.settings.sso.link.account": "Sign in with {name}",
|
"account.settings.sso.link.account": "Sign in with {name}",
|
||||||
"account.settings.sso.account.connected": "Linked",
|
"account.settings.sso.account.connected": "Linked",
|
||||||
|
|||||||
@@ -33,6 +33,9 @@
|
|||||||
"account.settings.field.country": "País",
|
"account.settings.field.country": "País",
|
||||||
"account.settings.field.country.empty": "Agregar país",
|
"account.settings.field.country.empty": "Agregar país",
|
||||||
"account.settings.field.country.options.empty": "Seleccionar un país",
|
"account.settings.field.country.options.empty": "Seleccionar un país",
|
||||||
|
"account.settings.field.state": "Estado",
|
||||||
|
"account.settings.field.state.empty": "Add state",
|
||||||
|
"account.settings.field.state.options.empty": "Select a State",
|
||||||
"account.settings.field.site.language": "Idioma del sitio",
|
"account.settings.field.site.language": "Idioma del sitio",
|
||||||
"account.settings.field.site.language.help.text": "El idioma que se usará para el sitio. Actualmente solo hay disponibilidad de usar un número limitado de idiomas.",
|
"account.settings.field.site.language.help.text": "El idioma que se usará para el sitio. Actualmente solo hay disponibilidad de usar un número limitado de idiomas.",
|
||||||
"account.settings.field.education": "Educación",
|
"account.settings.field.education": "Educación",
|
||||||
@@ -74,12 +77,25 @@
|
|||||||
"account.settings.editable.field.action.save": "Guardar",
|
"account.settings.editable.field.action.save": "Guardar",
|
||||||
"account.settings.editable.field.action.cancel": "Cancelar",
|
"account.settings.editable.field.action.cancel": "Cancelar",
|
||||||
"account.settings.editable.field.action.edit": "Editar",
|
"account.settings.editable.field.action.edit": "Editar",
|
||||||
"account.settings.static.field.empty": "No value set. Contact your {enterprise} administrator to make changes.",
|
"account.settings.static.field.empty": "No hay valor establecido. Contacte su administrador {enterprise} para hacer cambios.",
|
||||||
"account.settings.static.field.empty.no.admin": "No value set.",
|
"account.settings.static.field.empty.no.admin": "No hay valor establecido.",
|
||||||
"account.settings.field.phone_number": "Phone Number",
|
"account.settings.coaching.consent.welcome.header": "Let’s get started.",
|
||||||
|
"account.settings.coaching.consent.welcome.subheader": "We're here for you from start to finish",
|
||||||
|
"account.settings.coaching.consent.description": "MicroBachelors programs include coaching that focuses on your career, education, and how you'll achieve results through one-on-one communication with an experienced professional. If you’re interested, provide the information below and click “Submit,” and our coaching partner will connect with you via email and/or text message to help you move forward. Terms and conditions apply.*",
|
||||||
|
"account.settings.coaching.consent.text-messaging.disclaimer": "* Coaching services are included at no additional cost to learners with US phone numbers. Coaching includes recurring text messages. Message and data rates may apply. Text STOP to opt-out.",
|
||||||
|
"account.settings.coaching.consent.accept-coaching": "Sign up for coaching",
|
||||||
|
"account.settings.coaching.consent.decline-coaching": "I prefer not to be contacted with free coaching services",
|
||||||
|
"account.settings.coaching.consent.label.name": "Please confirm your name",
|
||||||
|
"account.settings.coaching.consent.label.phone-number": "Enter your mobile number",
|
||||||
|
"account.settings.coaching.consent.success.header": "Success!",
|
||||||
|
"account.settings.coaching.consent.success.message": "You're signed up for coaching. You will receive a text message confirmation.",
|
||||||
|
"account.settings.coaching.consent.success.continue": "Start my course",
|
||||||
|
"account.settings.coaching.managed.support": "soporte",
|
||||||
|
"account.settings.coaching.managed.alert": "Your name is managed by {managerTitle}. Contact your administrator for help.",
|
||||||
|
"account.settings.field.phone_number": "Teléfono",
|
||||||
"account.settings.field.phone_number.empty": "Add a phone number",
|
"account.settings.field.phone_number.empty": "Add a phone number",
|
||||||
"account.settings.field.coaching_consent": "Coaching consent",
|
"account.settings.field.coaching_consent": "Coaching consent",
|
||||||
"account.settings.field.coaching_consent.tooltip": "MicroBachelors programs include text message based coaching that helps you pair educational experiences with your career goals through one-on-one advice. Coaching services are included at no additional cost, and are available in English and Spanish languages. Standard messaging rates apply. Text ‘STOP’ at anytime to opt-out of messages.",
|
"account.settings.field.coaching_consent.tooltip": "MicroBachelors programs include text message based coaching that helps you pair educational experiences with your career goals through one-on-one advice. Coaching services are included at no additional cost, and are available to learners with U.S. mobile phone numbers. Standard messaging rates apply. Text ‘STOP’ at anytime to opt-out of messages.",
|
||||||
"account.settings.field.coaching_consent.error": "A valid US phone number is required to opt into coaching",
|
"account.settings.field.coaching_consent.error": "A valid US phone number is required to opt into coaching",
|
||||||
"account.settings.delete.account.before.proceeding": "Antes de continuar, por favor {actionLink}.",
|
"account.settings.delete.account.before.proceeding": "Antes de continuar, por favor {actionLink}.",
|
||||||
"account.settings.delete.account.header": "Eliminar mi cuenta",
|
"account.settings.delete.account.header": "Eliminar mi cuenta",
|
||||||
@@ -91,7 +107,7 @@
|
|||||||
"account.settings.delete.account.text.change.instead": "En lugar de eso, ¿quieres cambiar tu correo electrónico, nombre o contraseña?",
|
"account.settings.delete.account.text.change.instead": "En lugar de eso, ¿quieres cambiar tu correo electrónico, nombre o contraseña?",
|
||||||
"account.settings.delete.account.button": "Eliminar mi cuenta",
|
"account.settings.delete.account.button": "Eliminar mi cuenta",
|
||||||
"account.settings.delete.account.please.activate": "activar su cuenta",
|
"account.settings.delete.account.please.activate": "activar su cuenta",
|
||||||
"account.settings.delete.account.please.unlink": "unlink all social media accounts",
|
"account.settings.delete.account.please.unlink": "Desvincular todas las cuentas de redes sociales.",
|
||||||
"account.settings.delete.account.modal.header": "¿Está seguro?",
|
"account.settings.delete.account.modal.header": "¿Está seguro?",
|
||||||
"account.settings.delete.account.modal.text.1": "Has seleccionado “Eliminar mi cuenta”. La eliminación de tu cuenta y datos personales es permanente e irreversible. edX no será capaz de recuperar tu cuenta o los datos que se hayan borrado.",
|
"account.settings.delete.account.modal.text.1": "Has seleccionado “Eliminar mi cuenta”. La eliminación de tu cuenta y datos personales es permanente e irreversible. edX no será capaz de recuperar tu cuenta o los datos que se hayan borrado.",
|
||||||
"account.settings.delete.account.modal.text.2": "Si procedes, no será posible usar esta cuenta para tomar cursos ni en la aplicación móvil de edX, ni en edx.org, ni en cualquier otro sitio hospedado por edX. Esto incluye el acceso a edx.org desde el sistema de tu empleador o universidad, y el acceso a sitios privados ofrecidos por MIT Open Learning, Wharton Executive Education, y Harvard Medical School.",
|
"account.settings.delete.account.modal.text.2": "Si procedes, no será posible usar esta cuenta para tomar cursos ni en la aplicación móvil de edX, ni en edx.org, ni en cualquier otro sitio hospedado por edX. Esto incluye el acceso a edx.org desde el sistema de tu empleador o universidad, y el acceso a sitios privados ofrecidos por MIT Open Learning, Wharton Executive Education, y Harvard Medical School.",
|
||||||
@@ -109,10 +125,11 @@
|
|||||||
"account.settings.editable.field.password.reset.button.confirmation.support.link": "soporte técnico",
|
"account.settings.editable.field.password.reset.button.confirmation.support.link": "soporte técnico",
|
||||||
"account.settings.editable.field.password.reset.button.confirmation": "Hemos mandado un mensaje a {email}. Haz clic en el enlace en el mensaje para restablecer tu contraseña. ¿No recibiste el mensaje? Contáctate con {technicalSupportLink}.",
|
"account.settings.editable.field.password.reset.button.confirmation": "Hemos mandado un mensaje a {email}. Haz clic en el enlace en el mensaje para restablecer tu contraseña. ¿No recibiste el mensaje? Contáctate con {technicalSupportLink}.",
|
||||||
"account.settings.editable.field.password.reset.button": "Restablecer contraseña",
|
"account.settings.editable.field.password.reset.button": "Restablecer contraseña",
|
||||||
|
"account.settings.editable.field.password.reset.button.forbidden": "Your previous request is in progress, please try again in few moments.",
|
||||||
"account.settings.editable.field.password.reset.label": "Contraseña",
|
"account.settings.editable.field.password.reset.label": "Contraseña",
|
||||||
"account.settings.sso.link.account": "Iniciar sesión con {name}",
|
"account.settings.sso.link.account": "Iniciar sesión con {name}",
|
||||||
"account.settings.sso.account.connected": "Vinculado",
|
"account.settings.sso.account.connected": "Vinculado",
|
||||||
"account.settings.sso.account.disconnect.error": "There was a problem disconnecting this account. Contact support if the problem persists.",
|
"account.settings.sso.account.disconnect.error": "Hubo un problema al desconectar esta Cuenta. Si el problema persiste, contacte soporte.",
|
||||||
"account.settings.sso.unlink.account": "Desvincular la cuenta de {accountName} ",
|
"account.settings.sso.unlink.account": "Desvincular la cuenta de {accountName} ",
|
||||||
"account.settings.sso.no.providers": "No accounts can be linked at this time."
|
"account.settings.sso.no.providers": "No se pueden vincular cuentas en este momento."
|
||||||
}
|
}
|
||||||
@@ -33,6 +33,9 @@
|
|||||||
"account.settings.field.country": "Country",
|
"account.settings.field.country": "Country",
|
||||||
"account.settings.field.country.empty": "Add country",
|
"account.settings.field.country.empty": "Add country",
|
||||||
"account.settings.field.country.options.empty": "Select a Country",
|
"account.settings.field.country.options.empty": "Select a Country",
|
||||||
|
"account.settings.field.state": "State",
|
||||||
|
"account.settings.field.state.empty": "Add state",
|
||||||
|
"account.settings.field.state.options.empty": "Select a State",
|
||||||
"account.settings.field.site.language": "Site language",
|
"account.settings.field.site.language": "Site language",
|
||||||
"account.settings.field.site.language.help.text": "The language used throughout this site. This site is currently available in a limited number of languages.",
|
"account.settings.field.site.language.help.text": "The language used throughout this site. This site is currently available in a limited number of languages.",
|
||||||
"account.settings.field.education": "Education",
|
"account.settings.field.education": "Education",
|
||||||
@@ -76,10 +79,23 @@
|
|||||||
"account.settings.editable.field.action.edit": "Edit",
|
"account.settings.editable.field.action.edit": "Edit",
|
||||||
"account.settings.static.field.empty": "No value set. Contact your {enterprise} administrator to make changes.",
|
"account.settings.static.field.empty": "No value set. Contact your {enterprise} administrator to make changes.",
|
||||||
"account.settings.static.field.empty.no.admin": "No value set.",
|
"account.settings.static.field.empty.no.admin": "No value set.",
|
||||||
|
"account.settings.coaching.consent.welcome.header": "Let’s get started.",
|
||||||
|
"account.settings.coaching.consent.welcome.subheader": "We're here for you from start to finish",
|
||||||
|
"account.settings.coaching.consent.description": "MicroBachelors programs include coaching that focuses on your career, education, and how you'll achieve results through one-on-one communication with an experienced professional. If you’re interested, provide the information below and click “Submit,” and our coaching partner will connect with you via email and/or text message to help you move forward. Terms and conditions apply.*",
|
||||||
|
"account.settings.coaching.consent.text-messaging.disclaimer": "* Coaching services are included at no additional cost to learners with US phone numbers. Coaching includes recurring text messages. Message and data rates may apply. Text STOP to opt-out.",
|
||||||
|
"account.settings.coaching.consent.accept-coaching": "Sign up for coaching",
|
||||||
|
"account.settings.coaching.consent.decline-coaching": "I prefer not to be contacted with free coaching services",
|
||||||
|
"account.settings.coaching.consent.label.name": "Please confirm your name",
|
||||||
|
"account.settings.coaching.consent.label.phone-number": "Enter your mobile number",
|
||||||
|
"account.settings.coaching.consent.success.header": "Success!",
|
||||||
|
"account.settings.coaching.consent.success.message": "You're signed up for coaching. You will receive a text message confirmation.",
|
||||||
|
"account.settings.coaching.consent.success.continue": "Start my course",
|
||||||
|
"account.settings.coaching.managed.support": "support",
|
||||||
|
"account.settings.coaching.managed.alert": "Your name is managed by {managerTitle}. Contact your administrator for help.",
|
||||||
"account.settings.field.phone_number": "Phone Number",
|
"account.settings.field.phone_number": "Phone Number",
|
||||||
"account.settings.field.phone_number.empty": "Add a phone number",
|
"account.settings.field.phone_number.empty": "Add a phone number",
|
||||||
"account.settings.field.coaching_consent": "Coaching consent",
|
"account.settings.field.coaching_consent": "Coaching consent",
|
||||||
"account.settings.field.coaching_consent.tooltip": "MicroBachelors programs include text message based coaching that helps you pair educational experiences with your career goals through one-on-one advice. Coaching services are included at no additional cost, and are available in English and Spanish languages. Standard messaging rates apply. Text ‘STOP’ at anytime to opt-out of messages.",
|
"account.settings.field.coaching_consent.tooltip": "MicroBachelors programs include text message based coaching that helps you pair educational experiences with your career goals through one-on-one advice. Coaching services are included at no additional cost, and are available to learners with U.S. mobile phone numbers. Standard messaging rates apply. Text ‘STOP’ at anytime to opt-out of messages.",
|
||||||
"account.settings.field.coaching_consent.error": "A valid US phone number is required to opt into coaching",
|
"account.settings.field.coaching_consent.error": "A valid US phone number is required to opt into coaching",
|
||||||
"account.settings.delete.account.before.proceeding": "Before proceeding, please {actionLink}.",
|
"account.settings.delete.account.before.proceeding": "Before proceeding, please {actionLink}.",
|
||||||
"account.settings.delete.account.header": "Delete My Account",
|
"account.settings.delete.account.header": "Delete My Account",
|
||||||
@@ -109,6 +125,7 @@
|
|||||||
"account.settings.editable.field.password.reset.button.confirmation.support.link": "technical support",
|
"account.settings.editable.field.password.reset.button.confirmation.support.link": "technical support",
|
||||||
"account.settings.editable.field.password.reset.button.confirmation": "We've sent a message to {email}. Click the link in the message to reset your password. Didn't receive the message? Contact {technicalSupportLink}.",
|
"account.settings.editable.field.password.reset.button.confirmation": "We've sent a message to {email}. Click the link in the message to reset your password. Didn't receive the message? Contact {technicalSupportLink}.",
|
||||||
"account.settings.editable.field.password.reset.button": "Reset Password",
|
"account.settings.editable.field.password.reset.button": "Reset Password",
|
||||||
|
"account.settings.editable.field.password.reset.button.forbidden": "Your previous request is in progress, please try again in few moments.",
|
||||||
"account.settings.editable.field.password.reset.label": "Password",
|
"account.settings.editable.field.password.reset.label": "Password",
|
||||||
"account.settings.sso.link.account": "Sign in with {name}",
|
"account.settings.sso.link.account": "Sign in with {name}",
|
||||||
"account.settings.sso.account.connected": "Linked",
|
"account.settings.sso.account.connected": "Linked",
|
||||||
|
|||||||
@@ -33,6 +33,9 @@
|
|||||||
"account.settings.field.country": "Country",
|
"account.settings.field.country": "Country",
|
||||||
"account.settings.field.country.empty": "Add country",
|
"account.settings.field.country.empty": "Add country",
|
||||||
"account.settings.field.country.options.empty": "Select a Country",
|
"account.settings.field.country.options.empty": "Select a Country",
|
||||||
|
"account.settings.field.state": "State",
|
||||||
|
"account.settings.field.state.empty": "Add state",
|
||||||
|
"account.settings.field.state.options.empty": "Select a State",
|
||||||
"account.settings.field.site.language": "Site language",
|
"account.settings.field.site.language": "Site language",
|
||||||
"account.settings.field.site.language.help.text": "The language used throughout this site. This site is currently available in a limited number of languages.",
|
"account.settings.field.site.language.help.text": "The language used throughout this site. This site is currently available in a limited number of languages.",
|
||||||
"account.settings.field.education": "Education",
|
"account.settings.field.education": "Education",
|
||||||
@@ -76,10 +79,23 @@
|
|||||||
"account.settings.editable.field.action.edit": "Edit",
|
"account.settings.editable.field.action.edit": "Edit",
|
||||||
"account.settings.static.field.empty": "No value set. Contact your {enterprise} administrator to make changes.",
|
"account.settings.static.field.empty": "No value set. Contact your {enterprise} administrator to make changes.",
|
||||||
"account.settings.static.field.empty.no.admin": "No value set.",
|
"account.settings.static.field.empty.no.admin": "No value set.",
|
||||||
|
"account.settings.coaching.consent.welcome.header": "Let’s get started.",
|
||||||
|
"account.settings.coaching.consent.welcome.subheader": "We're here for you from start to finish",
|
||||||
|
"account.settings.coaching.consent.description": "MicroBachelors programs include coaching that focuses on your career, education, and how you'll achieve results through one-on-one communication with an experienced professional. If you’re interested, provide the information below and click “Submit,” and our coaching partner will connect with you via email and/or text message to help you move forward. Terms and conditions apply.*",
|
||||||
|
"account.settings.coaching.consent.text-messaging.disclaimer": "* Coaching services are included at no additional cost to learners with US phone numbers. Coaching includes recurring text messages. Message and data rates may apply. Text STOP to opt-out.",
|
||||||
|
"account.settings.coaching.consent.accept-coaching": "Sign up for coaching",
|
||||||
|
"account.settings.coaching.consent.decline-coaching": "I prefer not to be contacted with free coaching services",
|
||||||
|
"account.settings.coaching.consent.label.name": "Please confirm your name",
|
||||||
|
"account.settings.coaching.consent.label.phone-number": "Enter your mobile number",
|
||||||
|
"account.settings.coaching.consent.success.header": "Success!",
|
||||||
|
"account.settings.coaching.consent.success.message": "You're signed up for coaching. You will receive a text message confirmation.",
|
||||||
|
"account.settings.coaching.consent.success.continue": "Start my course",
|
||||||
|
"account.settings.coaching.managed.support": "support",
|
||||||
|
"account.settings.coaching.managed.alert": "Your name is managed by {managerTitle}. Contact your administrator for help.",
|
||||||
"account.settings.field.phone_number": "Phone Number",
|
"account.settings.field.phone_number": "Phone Number",
|
||||||
"account.settings.field.phone_number.empty": "Add a phone number",
|
"account.settings.field.phone_number.empty": "Add a phone number",
|
||||||
"account.settings.field.coaching_consent": "Coaching consent",
|
"account.settings.field.coaching_consent": "Coaching consent",
|
||||||
"account.settings.field.coaching_consent.tooltip": "MicroBachelors programs include text message based coaching that helps you pair educational experiences with your career goals through one-on-one advice. Coaching services are included at no additional cost, and are available in English and Spanish languages. Standard messaging rates apply. Text ‘STOP’ at anytime to opt-out of messages.",
|
"account.settings.field.coaching_consent.tooltip": "MicroBachelors programs include text message based coaching that helps you pair educational experiences with your career goals through one-on-one advice. Coaching services are included at no additional cost, and are available to learners with U.S. mobile phone numbers. Standard messaging rates apply. Text ‘STOP’ at anytime to opt-out of messages.",
|
||||||
"account.settings.field.coaching_consent.error": "A valid US phone number is required to opt into coaching",
|
"account.settings.field.coaching_consent.error": "A valid US phone number is required to opt into coaching",
|
||||||
"account.settings.delete.account.before.proceeding": "Before proceeding, please {actionLink}.",
|
"account.settings.delete.account.before.proceeding": "Before proceeding, please {actionLink}.",
|
||||||
"account.settings.delete.account.header": "Delete My Account",
|
"account.settings.delete.account.header": "Delete My Account",
|
||||||
@@ -109,6 +125,7 @@
|
|||||||
"account.settings.editable.field.password.reset.button.confirmation.support.link": "technical support",
|
"account.settings.editable.field.password.reset.button.confirmation.support.link": "technical support",
|
||||||
"account.settings.editable.field.password.reset.button.confirmation": "We've sent a message to {email}. Click the link in the message to reset your password. Didn't receive the message? Contact {technicalSupportLink}.",
|
"account.settings.editable.field.password.reset.button.confirmation": "We've sent a message to {email}. Click the link in the message to reset your password. Didn't receive the message? Contact {technicalSupportLink}.",
|
||||||
"account.settings.editable.field.password.reset.button": "Reset Password",
|
"account.settings.editable.field.password.reset.button": "Reset Password",
|
||||||
|
"account.settings.editable.field.password.reset.button.forbidden": "Your previous request is in progress, please try again in few moments.",
|
||||||
"account.settings.editable.field.password.reset.label": "Password",
|
"account.settings.editable.field.password.reset.label": "Password",
|
||||||
"account.settings.sso.link.account": "Sign in with {name}",
|
"account.settings.sso.link.account": "Sign in with {name}",
|
||||||
"account.settings.sso.account.connected": "Linked",
|
"account.settings.sso.account.connected": "Linked",
|
||||||
|
|||||||
Reference in New Issue
Block a user