+
+
+
+ {firstNameFieldAttributes.label}
+
+ { handleChange(e, firstNameFieldAttributes.name); }}
+ {...others}
+ />
+
+
+
+ {lastNameFieldAttributes.label}
+
+ { handleChange(e, lastNameFieldAttributes.name); }}
+ {...others}
+ />
+
+ {!!helpText && {helpText} }
+ {fieldError != null && {fieldError} }
+ {others.children}
+
+
+ {
+ // Swallow clicks if the state is pending.
+ // We do this instead of disabling the button to prevent
+ // it from losing focus (disabled elements cannot have focus).
+ // Disabling it would causes upstream issues in focus management.
+ // Swallowing the onSubmit event on the form would be better, but
+ // we would have to add that logic for every field given our
+ // current structure of the application.
+ if (saveState === 'pending') { e.preventDefault(); }
+ }}
+ disabledStates={[]}
+ />
+
+ {intl.formatMessage(messages['account.settings.editable.field.action.cancel'])}
+
+
+
+ {['name', 'verified_name'].includes(name) && }
+ >
+ ),
+ default: (
+
+
+
{label}
+ {isEditable ? (
+
+ {intl.formatMessage(messages['account.settings.editable.field.action.edit'])}
+
+ ) : null}
+
+
+ {renderValue(fullName)}
+
+
{renderConfirmationMessage() || helpText}
+
+ ),
+ }}
+ />
+ );
+};
+
+NameField.propTypes = {
+ name: PropTypes.string.isRequired,
+ label: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.node]),
+ emptyLabel: PropTypes.node,
+ type: PropTypes.string.isRequired,
+ fullNameValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ firstNameValue: PropTypes.string,
+ lastNameValue: PropTypes.string,
+ pendingNameChange: PropTypes.string,
+ verifiedName: PropTypes.shape({
+ verified_name: PropTypes.string,
+ status: PropTypes.string,
+ proctored_exam_attempt_id: PropTypes.number,
+ }),
+ userSuppliedValue: PropTypes.string,
+ saveState: PropTypes.oneOf(['default', 'pending', 'complete', 'error']),
+ error: PropTypes.string,
+ firstNameError: PropTypes.string,
+ lastNameError: PropTypes.string,
+ confirmationMessageDefinition: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ defaultMessage: PropTypes.string.isRequired,
+ description: PropTypes.string,
+ }),
+ confirmationValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ helpText: PropTypes.node,
+ onEdit: PropTypes.func.isRequired,
+ onCancel: PropTypes.func.isRequired,
+ onSubmit: PropTypes.func.isRequired,
+ onChange: PropTypes.func.isRequired,
+ isEditing: PropTypes.bool,
+ isEditable: PropTypes.bool,
+ isGrayedOut: PropTypes.bool,
+ intl: intlShape.isRequired,
+};
+
+NameField.defaultProps = {
+ fullNameValue: undefined,
+ firstNameValue: undefined,
+ lastNameValue: undefined,
+ pendingNameChange: null,
+ verifiedName: null,
+ saveState: undefined,
+ label: undefined,
+ emptyLabel: undefined,
+ error: undefined,
+ firstNameError: undefined,
+ lastNameError: undefined,
+ confirmationMessageDefinition: undefined,
+ confirmationValue: undefined,
+ helpText: undefined,
+ isEditing: false,
+ isEditable: true,
+ isGrayedOut: false,
+ userSuppliedValue: undefined,
+};
+
+export default connect(nameFieldSelector, {
+ onEdit: openForm,
+ onCancel: closeForm,
+})(injectIntl(NameField));
diff --git a/src/account-settings/data/actions.js b/src/account-settings/data/actions.js
index 27d8f7682..0f80510bf 100644
--- a/src/account-settings/data/actions.js
+++ b/src/account-settings/data/actions.js
@@ -105,9 +105,9 @@ export const savePreviousSiteLanguage = previousSiteLanguage => ({
payload: { previousSiteLanguage },
});
-export const saveMultipleSettings = (settingsArray, form = null) => ({
+export const saveMultipleSettings = (settingsArray, form = null, saveInSeparateCalls = true) => ({
type: SAVE_MULTIPLE_SETTINGS.BASE,
- payload: { settingsArray, form },
+ payload: { settingsArray, form, saveInSeparateCalls },
});
export const saveMultipleSettingsBegin = () => ({
diff --git a/src/account-settings/data/sagas.js b/src/account-settings/data/sagas.js
index c27cbe9ef..0ea94f6e6 100644
--- a/src/account-settings/data/sagas.js
+++ b/src/account-settings/data/sagas.js
@@ -124,14 +124,26 @@ export function* handleSaveMultipleSettings(action) {
try {
yield put(saveMultipleSettingsBegin());
const { username, userId } = getAuthenticatedUser();
- const { settingsArray, form } = action.payload;
- for (let i = 0; i < settingsArray.length; i += 1) {
- const { formId, commitValues } = settingsArray[i];
+ const { settingsArray, form, saveInSeparateCalls } = action.payload;
+ if (saveInSeparateCalls) {
+ for (let i = 0; i < settingsArray.length; i += 1) {
+ const { formId, commitValues } = settingsArray[i];
+ yield put(saveSettingsBegin());
+ const commitData = { [formId]: commitValues };
+ console.log('settingsArray', settingsArray[i]);
+ console.log('commitData', commitData);
+ const savedSettings = yield call(patchSettings, username, commitData, userId);
+ yield put(saveSettingsSuccess(savedSettings, commitData));
+ }
+ } else {
+ const commitData = settingsArray.reduce((data, setting) => (
+ { ...data, [setting.formId]: setting.commitValues }
+ ), {});
yield put(saveSettingsBegin());
- const commitData = { [formId]: commitValues };
const savedSettings = yield call(patchSettings, username, commitData, userId);
yield put(saveSettingsSuccess(savedSettings, commitData));
}
+
yield put(saveMultipleSettingsSuccess(action));
if (form) {
yield delay(1000);
diff --git a/src/account-settings/data/selectors.js b/src/account-settings/data/selectors.js
index 7650aa01e..b5ddf25ac 100644
--- a/src/account-settings/data/selectors.js
+++ b/src/account-settings/data/selectors.js
@@ -94,6 +94,14 @@ const editableFieldErrorSelector = createSelector(
(name, accountSettings) => accountSettings.errors[name],
);
+const firstAndLastNameErrorsSelector = createSelector(
+ accountSettingsSelector,
+ (accountSettings) => ({
+ firstNameError: accountSettings.errors?.first_name,
+ lastNameError: accountSettings.errors?.last_name,
+ }),
+);
+
const editableFieldConfirmationValuesSelector = createSelector(
editableFieldNameSelector,
accountSettingsSelector,
@@ -128,6 +136,15 @@ export const editableFieldSelector = createStructuredSelector({
isEditing: isEditingSelector,
});
+export const nameFieldSelector = createSelector(
+ editableFieldSelector,
+ firstAndLastNameErrorsSelector,
+ (editableFieldSettings, firstAndLastNameErrors) => ({
+ ...editableFieldSettings,
+ ...firstAndLastNameErrors,
+ }),
+);
+
export const profileDataManagerSelector = createSelector(
accountSettingsSelector,
accountSettings => accountSettings.profileDataManager,
diff --git a/src/account-settings/data/service.js b/src/account-settings/data/service.js
index d2f2e6118..c577e3a04 100644
--- a/src/account-settings/data/service.js
+++ b/src/account-settings/data/service.js
@@ -83,6 +83,8 @@ export async function patchAccount(username, commitValues) {
headers: { 'Content-Type': 'application/merge-patch+json' },
};
+ console.log('patchAccount packAccountCommitData(commitValues)', packAccountCommitData(commitValues));
+
const { data } = await getAuthenticatedHttpClient()
.patch(
`${getConfig().LMS_BASE_URL}/api/user/v1/accounts/${username}`,
@@ -271,6 +273,11 @@ export async function patchSettings(username, commitValues, userId) {
const certCommitValues = pick(commitValues, certificateKeys);
const patchRequests = [];
+ console.log('accountCommitValues', accountCommitValues);
+ console.log('preferenceCommitValues', preferenceCommitValues);
+ console.log('demographicsCommitValues', demographicsCommitValues);
+ console.log('certCommitValues', certCommitValues);
+
if (!isEmpty(accountCommitValues)) {
patchRequests.push(patchAccount(username, accountCommitValues));
}
diff --git a/src/account-settings/name-change/NameChange.jsx b/src/account-settings/name-change/NameChange.jsx
index d08a840c5..10d56a9fe 100644
--- a/src/account-settings/name-change/NameChange.jsx
+++ b/src/account-settings/name-change/NameChange.jsx
@@ -62,7 +62,10 @@ const NameChangeModal = ({
}));
} else {
const draftProfileName = targetFormId === 'name' ? formValues.name : null;
- dispatch(requestNameChange(username, draftProfileName, verifiedNameInput));
+ const draftFirstName = targetFormId === 'name' ? formValues?.first_name : null;
+ const draftLastName = targetFormId === 'name' ? formValues?.last_name : null;
+
+ dispatch(requestNameChange(username, draftProfileName, verifiedNameInput, draftFirstName, draftLastName));
}
};
@@ -190,6 +193,8 @@ NameChangeModal.propTypes = {
errors: PropTypes.shape({}).isRequired,
formValues: PropTypes.shape({
name: PropTypes.string,
+ first_name: PropTypes.string,
+ last_name: PropTypes.string,
verified_name: PropTypes.string,
}).isRequired,
saveState: PropTypes.string,
diff --git a/src/account-settings/name-change/data/actions.js b/src/account-settings/name-change/data/actions.js
index f80fbbbf5..efd0a0935 100644
--- a/src/account-settings/name-change/data/actions.js
+++ b/src/account-settings/name-change/data/actions.js
@@ -2,9 +2,11 @@ import { AsyncActionType } from '../../data/utils';
export const REQUEST_NAME_CHANGE = new AsyncActionType('ACCOUNT_SETTINGS', 'REQUEST_NAME_CHANGE');
-export const requestNameChange = (username, profileName, verifiedName) => ({
+export const requestNameChange = (username, profileName, verifiedName, firstName, lastName) => ({
type: REQUEST_NAME_CHANGE.BASE,
- payload: { username, profileName, verifiedName },
+ payload: {
+ username, profileName, verifiedName, firstName, lastName,
+ },
});
export const requestNameChangeBegin = () => ({
diff --git a/src/account-settings/name-change/data/sagas.js b/src/account-settings/name-change/data/sagas.js
index dfbae8e87..4971a8807 100644
--- a/src/account-settings/name-change/data/sagas.js
+++ b/src/account-settings/name-change/data/sagas.js
@@ -17,7 +17,7 @@ export function* handleRequestNameChange(action) {
try {
yield put(requestNameChangeBegin());
if (action.payload.profileName) {
- yield call(postNameChange, action.payload.profileName);
+ yield call(postNameChange, action.payload.profileName, action.payload.firstName, action.payload.lastName);
profileName = action.payload.profileName;
}
yield call(postVerifiedName, {
diff --git a/src/account-settings/name-change/data/service.js b/src/account-settings/name-change/data/service.js
index 70a6cfc15..565850589 100644
--- a/src/account-settings/name-change/data/service.js
+++ b/src/account-settings/name-change/data/service.js
@@ -4,13 +4,19 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { handleRequestError } from '../../data/utils';
// eslint-disable-next-line import/prefer-default-export
-export async function postNameChange(name) {
+export async function postNameChange(name, firstName, lastName) {
// Requests a pending name change, rather than saving the account name immediately
const requestConfig = { headers: { Accept: 'application/json' } };
const requestUrl = `${getConfig().LMS_BASE_URL}/api/user/v1/accounts/name_change/`;
+ const nameChangePayload = { name };
+ if (firstName && lastName) {
+ nameChangePayload.first_name = firstName;
+ nameChangePayload.last_name = lastName;
+ }
+
const { data } = await getAuthenticatedHttpClient()
- .post(requestUrl, { name }, requestConfig)
+ .post(requestUrl, nameChangePayload, requestConfig)
.catch(error => handleRequestError(error));
return data;