diff --git a/.env.development b/.env.development
index e144c5f..862832d 100644
--- a/.env.development
+++ b/.env.development
@@ -17,4 +17,4 @@ SITE_NAME='edX'
SUPPORT_URL='http://localhost:18000/support'
USER_INFO_COOKIE_NAME='edx-user-info'
# Temporary, Remove this once we are ready to release the feature.
-TEMP_COACHING_FEATURE_FLAG=true
+COACHING_ENABLED=true
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 6b0a274..894f14b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11519,6 +11519,11 @@
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
"integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
},
+ "lodash.debounce": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+ "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
+ },
"lodash.defaultsdeep": {
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz",
diff --git a/package.json b/package.json
index 8052921..4089a3c 100755
--- a/package.json
+++ b/package.json
@@ -45,6 +45,7 @@
"formdata-polyfill": "3.0.19",
"history": "4.10.1",
"lodash.camelcase": "4.3.0",
+ "lodash.debounce": "^4.0.8",
"lodash.findindex": "4.6.0",
"lodash.get": "4.4.2",
"lodash.isempty": "4.4.0",
diff --git a/src/account-settings/AccountSettingsPage.jsx b/src/account-settings/AccountSettingsPage.jsx
index c3cadcb..3677139 100644
--- a/src/account-settings/AccountSettingsPage.jsx
+++ b/src/account-settings/AccountSettingsPage.jsx
@@ -13,7 +13,7 @@ import {
getCountryList,
getLanguageList,
} from '@edx/frontend-platform/i18n';
-import { Hyperlink, Input, ValidationFormGroup } from '@edx/paragon';
+import { Hyperlink } from '@edx/paragon';
import messages from './AccountSettingsPage.messages';
import { fetchSettings, saveSettings, updateDraft } from './data/actions';
@@ -33,6 +33,7 @@ import {
GENDER_OPTIONS,
} from './data/constants';
import { fetchSiteLanguages } from './site-language';
+import CoachingToggle from './coaching/CoachingToggle';
class AccountSettingsPage extends React.Component {
constructor(props, context) {
@@ -327,44 +328,13 @@ class AccountSettingsPage extends React.Component {
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.language.proficiencies.empty'])}
{...editableFieldProps}
/>
- {process.env.TEMP_COACHING_FEATURE_FLAG &&
- <>
-
-
- {
- this.handleEditableFieldChange(e.target.name, e.target.checked);
- if (this.props.formValues.phone_number) {
- this.handleSubmit(e.target.name, e.target.checked);
- } else {
- this.handleSubmit(e.target.name, false);
- }
- }}
- />
-
-
- >
+ {getConfig().COACHING_ENABLED &&
+ this.props.formValues.coaching.eligible_for_coaching &&
+
}
@@ -519,6 +489,11 @@ AccountSettingsPage.propTypes = {
social_link_facebook: PropTypes.string,
social_link_twitter: PropTypes.string,
time_zone: PropTypes.string,
+ coaching: PropTypes.objectOf(PropTypes.shape({
+ coaching_consent: PropTypes.string.isRequired,
+ user: PropTypes.number.isRequired,
+ eligible_for_coaching: PropTypes.bool.isRequired,
+ })).isRequired,
}).isRequired,
siteLanguage: PropTypes.shape({
previousValue: PropTypes.string,
diff --git a/src/account-settings/AccountSettingsPage.messages.jsx b/src/account-settings/AccountSettingsPage.messages.jsx
index 42744d7..03c4692 100644
--- a/src/account-settings/AccountSettingsPage.messages.jsx
+++ b/src/account-settings/AccountSettingsPage.messages.jsx
@@ -257,31 +257,6 @@ const messages = defineMessages({
defaultMessage: 'Other',
description: 'The label for catch-all gender option.',
},
- 'account.settings.field.phone_number': {
- id: 'account.settings.field.phone_number',
- defaultMessage: 'Phone Number',
- description: 'The label for a phone numbers setting in the user profile',
- },
- 'account.settings.field.phone_number.empty': {
- id: 'account.settings.field.phone_number.empty',
- defaultMessage: 'Add a phone number',
- description: 'placeholder for a profiles empty phone number field',
- },
- 'account.settings.field.coaching_consent': {
- id: 'account.settings.field.coaching_consent',
- defaultMessage: 'Coaching consent',
- description: 'The label for the coaching consent setting in the user profile',
- },
- '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.',
- description: 'A tooltip explaining what coaching is and who it is for',
- },
- 'account.settings.field.coaching_consent.error': {
- id: 'account.settings.field.coaching_consent.error',
- defaultMessage: 'A phone number is required to sign up for coaching',
- description: 'An error message that displays when a user attempts to consent to coaching without first providing a phone number in their profile',
- },
'account.settings.field.language.proficiencies': {
id: 'account.settings.field.language.proficiencies',
defaultMessage: 'Spoken languages',
diff --git a/src/account-settings/coaching/CoachingToggle.jsx b/src/account-settings/coaching/CoachingToggle.jsx
new file mode 100644
index 0000000..333f4c4
--- /dev/null
+++ b/src/account-settings/coaching/CoachingToggle.jsx
@@ -0,0 +1,76 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
+import { ValidationFormGroup, Input } from '@edx/paragon';
+import messages from './CoachingToggle.messages';
+import { editableFieldSelector } from '../data/selectors';
+import { saveSettings, updateDraft } from '../data/actions';
+import EditableField from '../EditableField';
+
+
+const CoatchingToggle = props => (
+ <>
+
+
+ {
+ const { name } = e.target;
+ const value = {
+ ...props.coaching,
+ phone_number: props.phone_number,
+ coaching_consent: e.target.checked,
+ };
+ props.saveSettings(name, value);
+ }}
+ />
+
+
+ >
+);
+
+CoatchingToggle.defaultProps = {
+ phone_number: '',
+ error: '',
+};
+
+CoatchingToggle.propTypes = {
+ name: PropTypes.string.isRequired,
+ error: PropTypes.string,
+ coaching: PropTypes.objectOf(PropTypes.shape({
+ coaching_consent: PropTypes.string.isRequired,
+ user: PropTypes.number.isRequired,
+ eligible_for_coaching: PropTypes.bool.isRequired,
+ })).isRequired,
+ saveState: PropTypes.func.isRequired,
+ saveSettings: PropTypes.func.isRequired,
+ updateDraft: PropTypes.func.isRequired,
+ intl: intlShape.isRequired,
+ phone_number: PropTypes.string,
+};
+
+export default connect(editableFieldSelector, {
+ saveSettings,
+ updateDraft,
+})(injectIntl(CoatchingToggle));
diff --git a/src/account-settings/coaching/CoachingToggle.messages.js b/src/account-settings/coaching/CoachingToggle.messages.js
new file mode 100644
index 0000000..68b9c59
--- /dev/null
+++ b/src/account-settings/coaching/CoachingToggle.messages.js
@@ -0,0 +1,31 @@
+import { defineMessages } from '@edx/frontend-platform/i18n';
+
+const messages = defineMessages({
+ 'account.settings.field.phone_number': {
+ id: 'account.settings.field.phone_number',
+ defaultMessage: 'Phone Number',
+ description: 'The label for a phone numbers setting in the user profile',
+ },
+ 'account.settings.field.phone_number.empty': {
+ id: 'account.settings.field.phone_number.empty',
+ defaultMessage: 'Add a phone number',
+ description: 'placeholder for a profiles empty phone number field',
+ },
+ 'account.settings.field.coaching_consent': {
+ id: 'account.settings.field.coaching_consent',
+ defaultMessage: 'Coaching consent',
+ description: 'The label for the coaching consent setting in the user profile',
+ },
+ '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.',
+ description: 'A tooltip explaining what coaching is and who it is for',
+ },
+ 'account.settings.field.coaching_consent.error': {
+ id: 'account.settings.field.coaching_consent.error',
+ defaultMessage: 'A valid US phone number is required to opt into coaching',
+ description: 'An error message that displays when a user attempts to consent to coaching without first providing a phone number in their profile',
+ },
+});
+
+export default messages;
diff --git a/src/account-settings/coaching/data/service.js b/src/account-settings/coaching/data/service.js
new file mode 100644
index 0000000..b507622
--- /dev/null
+++ b/src/account-settings/coaching/data/service.js
@@ -0,0 +1,36 @@
+import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
+import { getConfig } from '@edx/frontend-platform';
+
+/**
+ * get all settings related to the coaching plugin. Settings used
+ * by Microbachelors students.
+ * @param {Number} userId users are identified in the api by LMS id
+ */
+export async function getCoachingPreferences(userId) {
+ const { data } = await getAuthenticatedHttpClient()
+ .get(`${getConfig().LMS_BASE_URL}/api/coaching/v1/users/${userId}/`);
+ return data;
+}
+
+/**
+ * patch all of the settings related to coaching.
+ * @param {Number} userId users are identified in the api by LMS id
+ * @param {Object} commitValues { coaching }
+ */
+export async function patchCoachingPreferences(userId, commitValues) {
+ const requestUrl = `${getConfig().LMS_BASE_URL}/api/coaching/v1/users/${userId}/`;
+ const { coaching } = commitValues;
+ coaching.user = userId;
+
+ await getAuthenticatedHttpClient()
+ .patch(requestUrl, coaching)
+ .catch((error) => {
+ const apiError = Object.create(error);
+ apiError.fieldErrors = JSON.parse(error.customAttributes.httpErrorResponseData);
+ // eslint-disable-next-line prefer-destructuring
+ apiError.fieldErrors.coaching = apiError.fieldErrors.phone_number[0];
+ delete apiError.fieldErrors.phone_number;
+ throw apiError;
+ });
+ return commitValues;
+}
diff --git a/src/account-settings/data/sagas.js b/src/account-settings/data/sagas.js
index eb94059..3f66773 100644
--- a/src/account-settings/data/sagas.js
+++ b/src/account-settings/data/sagas.js
@@ -1,4 +1,4 @@
-import { call, put, delay, takeEvery, all, debounce } from 'redux-saga/effects';
+import { call, put, delay, takeEvery, all } from 'redux-saga/effects';
import { publish } from '@edx/frontend-platform';
import { getLocale, handleRtl, LOCALE_CHANGED } from '@edx/frontend-platform/i18n';
@@ -108,7 +108,7 @@ export function* handleFetchTimeZones(action) {
export default function* saga() {
yield takeEvery(FETCH_SETTINGS.BASE, handleFetchSettings);
- yield debounce(500, SAVE_SETTINGS.BASE, handleSaveSettings);
+ yield takeEvery(SAVE_SETTINGS.BASE, handleSaveSettings);
yield takeEvery(FETCH_TIME_ZONES.BASE, handleFetchTimeZones);
yield all([
deleteAccountSaga(),
diff --git a/src/account-settings/data/service.js b/src/account-settings/data/service.js
index 9d83136..07a4871 100644
--- a/src/account-settings/data/service.js
+++ b/src/account-settings/data/service.js
@@ -6,6 +6,7 @@ import isEmpty from 'lodash.isempty';
import { handleRequestError, unpackFieldErrors } from './utils';
import { getThirdPartyAuthProviders } from '../third-party-auth';
+import { getCoachingPreferences, patchCoachingPreferences } from '../coaching/data/service';
const SOCIAL_PLATFORMS = [
{ id: 'twitter', key: 'social_link_twitter' },
@@ -149,34 +150,6 @@ export async function getProfileDataManager(username, userRoles) {
return null;
}
-/**
- * get all settings related to the coaching plugin. Settings used
- * by Microbachelors students.
- * @param {Number} userId users are identified in the api by LMS id
- */
-export async function getCoachingPreferences(userId) {
- const { data } = await getAuthenticatedHttpClient()
- .get(`${getConfig().LMS_BASE_URL}/api/coaching/v1/users/${userId}/`);
- return data.coaching_consent;
-}
-
-/**
- * patch all of the settings related to coaching.
- * @param {Number} userId users are identified in the api by LMS id
- * @param {Object} commitValues { coaching_consent }
- */
-export async function patchCoachingPreferences(userId, commitValues) {
- const requestUrl = `${getConfig().LMS_BASE_URL}/api/coaching/v1/users/${userId}/`;
- const body = {
- ...commitValues,
- user: userId,
- };
- const options = { headers: { 'Content-Type': 'application/json' } };
- getAuthenticatedHttpClient()
- .patch(requestUrl, body, options);
-
- return commitValues;
-}
/**
* A single function to GET everything considered a setting.
* Currently encapsulates Account, Preferences, Coaching, and ThirdPartyAuth
@@ -197,7 +170,7 @@ export async function getSettings(username, userRoles, userId) {
thirdPartyAuthProviders: results[2],
profileDataManager: results[3],
timeZones: results[4],
- coaching_consent: results[5],
+ coaching: results[5],
};
}
@@ -210,7 +183,7 @@ export async function patchSettings(username, commitValues, userId) {
// but it is always null and won't update. It also exists in
// user/v1/preferences where it does update. This is the one we use.
const preferenceKeys = ['time_zone'];
- const coachingKeys = ['coaching_consent'];
+ const coachingKeys = ['coaching'];
const accountCommitValues = omit(commitValues, preferenceKeys);
const preferenceCommitValues = pick(commitValues, preferenceKeys);
const coachingCommitValues = pick(commitValues, coachingKeys);
diff --git a/src/index.jsx b/src/index.jsx
index 87cac02..75a26d2 100755
--- a/src/index.jsx
+++ b/src/index.jsx
@@ -49,6 +49,7 @@ initialize({
config: () => {
mergeConfig({
SUPPORT_URL: process.env.SUPPORT_URL,
+ COACHING_ENABLED: process.env.COACHING_ENABLED,
}, 'App loadConfig override handler');
},
},