diff --git a/.env.development b/.env.development
index 5bbfc73..e144c5f 100644
--- a/.env.development
+++ b/.env.development
@@ -16,3 +16,5 @@ SEGMENT_KEY=null
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
diff --git a/src/account-settings/AccountSettingsPage.jsx b/src/account-settings/AccountSettingsPage.jsx
index ab5b48f..c3cadcb 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 } from '@edx/paragon';
+import { Hyperlink, Input, ValidationFormGroup } from '@edx/paragon';
import messages from './AccountSettingsPage.messages';
import { fetchSettings, saveSettings, updateDraft } from './data/actions';
@@ -327,6 +327,45 @@ 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);
+ }
+ }}
+ />
+
+
+ >
+ }
@@ -474,6 +513,8 @@ AccountSettingsPage.propTypes = {
level_of_education: PropTypes.string,
gender: PropTypes.string,
language_proficiencies: PropTypes.string,
+ phone_number: PropTypes.string,
+ coaching_consent: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
social_link_linkedin: PropTypes.string,
social_link_facebook: PropTypes.string,
social_link_twitter: PropTypes.string,
diff --git a/src/account-settings/AccountSettingsPage.messages.jsx b/src/account-settings/AccountSettingsPage.messages.jsx
index 03c4692..42744d7 100644
--- a/src/account-settings/AccountSettingsPage.messages.jsx
+++ b/src/account-settings/AccountSettingsPage.messages.jsx
@@ -257,6 +257,31 @@ 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/_style.scss b/src/account-settings/_style.scss
index 1b3013d..a869a9e 100644
--- a/src/account-settings/_style.scss
+++ b/src/account-settings/_style.scss
@@ -35,4 +35,13 @@
margin-bottom: map-get($spacers, 5);
padding-top: 1rem;
}
+
+ .custom-switch {
+ padding: 0;
+ max-width: 500px;
+ .custom-control-label {
+ left: 2.25rem;
+ line-height: 1.6rem;
+ }
+ }
}
diff --git a/src/account-settings/data/sagas.js b/src/account-settings/data/sagas.js
index fe9c8b6..eb94059 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 } from 'redux-saga/effects';
+import { call, put, delay, takeEvery, all, debounce } from 'redux-saga/effects';
import { publish } from '@edx/frontend-platform';
import { getLocale, handleRtl, LOCALE_CHANGED } from '@edx/frontend-platform/i18n';
@@ -37,7 +37,7 @@ import { getSettings, patchSettings, getTimeZones } from './service';
export function* handleFetchSettings() {
try {
yield put(fetchSettingsBegin());
- const { username, roles: userRoles } = getAuthenticatedUser();
+ const { username, userId, roles: userRoles } = getAuthenticatedUser();
const {
thirdPartyAuthProviders, profileDataManager, timeZones, ...values
@@ -45,6 +45,7 @@ export function* handleFetchSettings() {
getSettings,
username,
userRoles,
+ userId,
);
if (values.country) yield put(fetchTimeZones(values.country));
@@ -65,7 +66,7 @@ export function* handleSaveSettings(action) {
try {
yield put(saveSettingsBegin());
- const { username } = getAuthenticatedUser();
+ const { username, userId } = getAuthenticatedUser();
const { commitValues, formId } = action.payload;
const commitData = { [formId]: commitValues };
let savedValues = null;
@@ -83,7 +84,7 @@ export function* handleSaveSettings(action) {
handleRtl();
savedValues = commitData;
} else {
- savedValues = yield call(patchSettings, username, commitData);
+ savedValues = yield call(patchSettings, username, commitData, userId);
}
yield put(saveSettingsSuccess(savedValues, commitData));
if (savedValues.country) yield put(fetchTimeZones(savedValues.country));
@@ -107,7 +108,7 @@ export function* handleFetchTimeZones(action) {
export default function* saga() {
yield takeEvery(FETCH_SETTINGS.BASE, handleFetchSettings);
- yield takeEvery(SAVE_SETTINGS.BASE, handleSaveSettings);
+ yield debounce(500, 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 4345d60..9d83136 100644
--- a/src/account-settings/data/service.js
+++ b/src/account-settings/data/service.js
@@ -150,16 +150,45 @@ export async function getProfileDataManager(username, userRoles) {
}
/**
- * A single function to GET everything considered a setting.
- * Currently encapsulates Account, Preferences, and ThirdPartyAuth
+ * 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 getSettings(username, userRoles) {
+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
+ */
+export async function getSettings(username, userRoles, userId) {
const results = await Promise.all([
getAccount(username),
getPreferences(username),
getThirdPartyAuthProviders(),
getProfileDataManager(username, userRoles),
getTimeZones(),
+ getCoachingPreferences(userId),
]);
return {
@@ -168,20 +197,23 @@ export async function getSettings(username, userRoles) {
thirdPartyAuthProviders: results[2],
profileDataManager: results[3],
timeZones: results[4],
+ coaching_consent: results[5],
};
}
/**
* A single function to PATCH everything considered a setting.
- * Currently encapsulates Account, Preferences, and ThirdPartyAuth
+ * Currently encapsulates Account, Preferences, coaching and ThirdPartyAuth
*/
-export async function patchSettings(username, commitValues) {
+export async function patchSettings(username, commitValues, userId) {
// Note: time_zone exists in the return value from user/v1/accounts
// 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 accountCommitValues = omit(commitValues, preferenceKeys);
const preferenceCommitValues = pick(commitValues, preferenceKeys);
+ const coachingCommitValues = pick(commitValues, coachingKeys);
const patchRequests = [];
if (!isEmpty(accountCommitValues)) {
@@ -190,6 +222,9 @@ export async function patchSettings(username, commitValues) {
if (!isEmpty(preferenceCommitValues)) {
patchRequests.push(patchPreferences(username, preferenceCommitValues));
}
+ if (!isEmpty(coachingCommitValues)) {
+ patchRequests.push(patchCoachingPreferences(userId, coachingCommitValues));
+ }
const results = await Promise.all(patchRequests);
// Assigns in order of requests. Preference keys