@okta-dfuhriman/okta-auth-js
Version:
The Okta Auth SDK
188 lines (173 loc) • 5.25 kB
JSX
import React, { useState, useEffect } from 'react';
import { getProfile, updateProfile } from '@okta/okta-auth-js/myaccount';
import { useOktaAuth } from '@okta/okta-react';
import {
Box,
Heading,
Icon,
Button,
TextInput,
Infobox,
Text,
} from '@okta/odyssey-react';
import InfoBox from '../InfoBox';
import Spinner from '../Spinner';
import { useMyAccountContext } from '../../contexts';
const ProfileSection = () => {
const { oktaAuth } = useOktaAuth();
const { profile, setProfile, startReAuthentication, setCorsError } = useMyAccountContext();
const [inputs, setInputs] = useState([
{ label: 'Given name', name: 'firstName', type: 'text', value: '' },
{ label: 'Family name', name: 'lastName', type: 'text', value: '' },
{ label: 'Age', name: 'age', type: 'number', value: '' }
]);
const [editing, setEditing] = useState(false);
const [updated, setUpdated] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
if (editing) {
return;
}
const fetchProfile = async () => {
try {
const profile = await getProfile(oktaAuth);
setProfile(profile.profile);
}
catch(err) {
// xhr will be empty when /myaccount returns CORs error
if (err.xhr && Object.keys(err.xhr).length === 0) {
setCorsError(err);
return;
}
throw err;
}
};
if (!profile) {
fetchProfile();
}
}, [oktaAuth, editing, profile, setProfile, setCorsError]);
const handleEditNames = () => {
const newInputs = inputs.map(input => ({ ...input, value: profile[input.name] || '' }));
setInputs(newInputs);
setEditing(true);
setUpdated(false);
setError(null);
};
const handleCancelEditNames = () => {
const newInputs = inputs.map(input => ({ ...input, value: '' }));
setInputs(newInputs);
setEditing(false);
};
const handleUpdateProfiles = async () => {
const updatedProfile = inputs.reduce((acc, curr) => {
if (curr.type === 'number') {
const castedNumber = Number(curr.value);
if (Number.isNaN(castedNumber)) {
// assign invalid value for server side validation
acc[curr.name] = curr.value;
} else {
acc[curr.name] = castedNumber
}
} else {
acc[curr.name] = curr.value;
}
return acc;
}, profile);
try {
const newProfile = await updateProfile(oktaAuth, {
payload: {
profile: updatedProfile
}
});
setProfile(newProfile.profile);
setUpdated(true);
} catch (err) {
if (err.errorSummary === 'insufficient_authentication_context') {
startReAuthentication(err);
} else {
setError(err);
}
setProfile(null);
}
handleCancelEditNames();
};
const handleChange = ({ target: { name, value } }) => {
const newInputs = inputs.map(input => {
if (input.name === name) {
input.value = value;
}
return input;
});
setInputs(newInputs);
};
if (!profile) {
return <Spinner />;
}
return (
<Box padding="m">
<Box
display="flex"
justifyContent="space-between"
alignItems="center"
marginTop="s"
marginBottom="s"
>
<Heading level="1">
<Icon name="user" />
Your Profile
</Heading>
{ editing ? (
<Box display="flex" alignItems="center">
<Button variant="clear" onClick={handleCancelEditNames}>Cancel</Button>
<Button name="saveProfile" onClick={handleUpdateProfiles}>Save</Button>
</Box>
) : (
<Button name="editProfile" variant="secondary" onClick={handleEditNames}>Edit</Button>
)}
</Box>
{!!(updated || error) && (
<Box id="profile-messages-container" marginTop="s" marginBottom="s">
{updated && (
<Infobox
variant="success"
content="The profile was updated successfully" />
)}
{!!error && (
<Infobox
key={error.errorSummary}
variant="danger"
content={error.errorSummary} />
)}
</Box>
)}
<Box display="flex" className="pure-g">
<Box className="pure-u-1 pure-u-sm-1-2" paddingRight="s">
{inputs.map(({ label, name, value }) => (
<Box key={name} paddingBottom="s">
<TextInput
disabled={!editing}
label={label}
name={name}
value={editing ? value : (profile[name] || '')}
onChange={handleChange}
/>
</Box>
))}
</Box>
<Box className="pure-u-1 pure-u-sm-1-2">
<InfoBox
id="profile-tip"
heading="Tip"
icon="information-circle-filled"
renderInfo={() => (
<Text as="p">
The profile attributes in this example are driven by the Profile Enrollment policy.
</Text>
)}
/>
</Box>
</Box>
</Box>
);
};
export default ProfileSection;