diff --git a/src/account-settings/AccountSettingsPage.jsx b/src/account-settings/AccountSettingsPage.jsx
index 8a6e54d05..a391c461d 100644
--- a/src/account-settings/AccountSettingsPage.jsx
+++ b/src/account-settings/AccountSettingsPage.jsx
@@ -52,6 +52,7 @@ import { fetchSiteLanguages } from './site-language';
import DemographicsSection from './demographics/DemographicsSection';
import { fetchCourseList } from '../notification-preferences/data/thunks';
import { withLocation, withNavigate } from './hoc';
+import NameField from './NameField';
class AccountSettingsPage extends React.Component {
constructor(props, context) {
@@ -167,6 +168,34 @@ class AccountSettingsPage extends React.Component {
this.props.saveSettings(formId, values, extendedProfileObject);
};
+ handleSubmitFirstAndLastName = (formId, fullName, firstName, lastName) => {
+ const settingsToBeSaved = [];
+
+ if (Object.keys(this.props.drafts).includes('useVerifiedNameForCerts')) {
+ settingsToBeSaved.push({
+ formId: 'useVerifiedNameForCerts',
+ commitValues: this.props.formValues.useVerifiedNameForCerts,
+ });
+ }
+
+ settingsToBeSaved.push({
+ formId: 'first_name',
+ commitValues: firstName,
+ });
+
+ settingsToBeSaved.push({
+ formId: 'last_name',
+ commitValues: lastName,
+ });
+
+ settingsToBeSaved.push({
+ formId: 'name',
+ commitValues: fullName,
+ });
+
+ this.props.saveMultipleSettings(settingsToBeSaved, formId, false);
+ };
+
handleSubmitProfileName = (formId, values) => {
if (Object.keys(this.props.drafts).includes('useVerifiedNameForCerts')) {
this.props.saveMultipleSettings([
@@ -552,37 +581,71 @@ class AccountSettingsPage extends React.Component {
isEditable={false}
{...editableFieldProps}
/>
-
+ ) : (
+
+ onChange={this.handleEditableFieldChange}
+ onSubmit={this.handleSubmitProfileName}
+ />
+ )}
+
{verifiedName
&& (
{
+ const {
+ name,
+ label,
+ emptyLabel,
+ type,
+ fullNameValue,
+ firstNameValue,
+ lastNameValue,
+ verifiedName,
+ pendingNameChange,
+ userSuppliedValue,
+ saveState,
+ error,
+ firstNameError,
+ lastNameError,
+ confirmationMessageDefinition,
+ confirmationValue,
+ helpText,
+ onEdit,
+ onCancel,
+ onSubmit,
+ onChange,
+ isEditing,
+ isEditable,
+ isGrayedOut,
+ intl,
+ ...others
+ } = props;
+
+ const id = `field-${name}`;
+
+ const firstNameFieldAttributes = {
+ name: 'first_name',
+ id: 'field-firstName',
+ label: intl.formatMessage(messages['account.settings.field.first.name']),
+ };
+
+ const lastNameFieldAttributes = {
+ name: 'last_name',
+ id: 'field-lastName',
+ label: intl.formatMessage(messages['account.settings.field.last.name']),
+ };
+
+ const [fullName, setFullName] = useState('');
+ const [firstName, setFirstName] = useState('');
+ const [lastName, setLastName] = useState('');
+ const [fieldError, setFieldError] = useState('');
+
+ /**
+ * Concatenates first and last name and generates full name.
+ * @param first
+ * @param last
+ * @returns {`${string} ${string}`}
+ */
+ const generateFullName = (first, last) => {
+ if (first && last) {
+ return `${first} ${last}`;
+ }
+ return first || last;
+ };
+
+ /**
+ * Splits a full name into first name and last name such that the first word
+ * is the firstName and rest of the name is last name.
+ * - If the full name is "John Doe Hamilton", the splitting will be
+ * e.g., fullName = John Doe => firstName = John, lastName = Doe Hamilton
+ * @param {string} nameValue The full name to split.
+ * @returns {object} An object containing the firstName and lastName.
+ */
+ const splitFullName = (nameValue) => {
+ const [first, ...lastNameArr] = nameValue.trim().split(' ');
+ const last = lastNameArr.join(' ');
+ return { first, last };
+ };
+
+ /**
+ * UseEffect for setting first and last name.
+ */
+ useEffect(() => {
+ if (firstNameValue || lastNameValue) {
+ setFirstName(firstNameValue);
+ setLastName(lastNameValue);
+ } else {
+ const { first, last } = splitFullName(fullNameValue);
+ setFirstName(first);
+ setLastName(last);
+ }
+ }, [firstNameValue, fullNameValue, lastNameValue]);
+
+ /**
+ * UseEffect for setting full name.
+ */
+ useEffect(() => {
+ if (verifiedName?.status === 'submitted' && pendingNameChange) {
+ setFullName(pendingNameChange);
+ } else if (firstNameValue || lastNameValue) {
+ setFullName(generateFullName(firstNameValue, lastNameValue));
+ } else {
+ setFullName(fullNameValue);
+ }
+ }, [firstNameValue, fullNameValue, lastNameValue, pendingNameChange, verifiedName?.status]);
+
+ /**
+ * UseEffect for setting error
+ */
+ useEffect(() => {
+ setFieldError(error || firstNameError || lastNameError);
+ }, [error, firstNameError, lastNameError]);
+
+ const handleSubmit = (e) => {
+ e.preventDefault();
+ const formData = new FormData(e.target);
+ const firstNameVal = formData.get(firstNameFieldAttributes.name).trim();
+ const lastNameVal = formData.get(lastNameFieldAttributes.name).trim();
+ const fullNameVal = generateFullName(firstName, lastName);
+
+ onSubmit(name, fullNameVal, firstNameVal, lastNameVal);
+ };
+
+ const handleChange = (e, fieldName) => {
+ onChange(fieldName, e.target.value);
+ // Updating full name along with the updates to first and last name
+ if (fieldName === firstNameFieldAttributes.name) {
+ onChange(name, generateFullName(e.target.value.trim(), lastNameValue));
+ } else if (fieldName === lastNameFieldAttributes.name) {
+ onChange(name, generateFullName(firstNameValue, e.target.value.trim()));
+ }
+ };
+
+ const handleEdit = () => {
+ onEdit(name);
+ };
+
+ const handleCancel = () => {
+ onCancel(name);
+ };
+
+ const renderEmptyLabel = () => {
+ if (isEditable) {
+ return ;
+ }
+ return {emptyLabel};
+ };
+
+ const renderValue = (rawValue) => {
+ if (!rawValue) {
+ return renderEmptyLabel();
+ }
+ let finalValue = rawValue;
+
+ if (userSuppliedValue) {
+ finalValue += `: ${userSuppliedValue}`;
+ }
+
+ return finalValue;
+ };
+
+ const renderConfirmationMessage = () => {
+ if (!confirmationMessageDefinition || !confirmationValue) {
+ return null;
+ }
+ return intl.formatMessage(confirmationMessageDefinition, {
+ value: confirmationValue,
+ });
+ };
+
+ return (
+
+
+
+
+ {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={[]}
+ />
+
+