Skip to content

Commit

Permalink
#347: First pass at making everything editable
Browse files Browse the repository at this point in the history
The full name swaps out for a text field, the social profiles get shoved inside a form group, and the bio field becomes editable.
  • Loading branch information
seidior committed Jun 12, 2022
2 parents 5c40e5c + d1c0fa5 commit e8b662e
Show file tree
Hide file tree
Showing 6 changed files with 396 additions and 250 deletions.
220 changes: 121 additions & 99 deletions webapp/src/components/UserProfile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import {
IconButton,
Typography,
Button,
Stack
Stack,
TextField,
} from '@mui/material';

import CheckIcon from '@mui/icons-material/Check';
import EditIcon from '@mui/icons-material/Edit';
import ProgressBar from './ProgressBar';
import CompetencyRatings from './CompetencyRatings';
Expand All @@ -31,10 +31,10 @@ import SessionContext from './SessionContext';
*/
let user_data: UserData = {
id: -1,
full_name: "",
bio: "",
avatar_url: "",
email_address: "",
full_name: '',
bio: '',
avatar_url: '',
email_address: '',
github_accounts: [],
social_profiles: [],
};
Expand All @@ -46,7 +46,7 @@ enum SaveStatus {
NO_STATUS,
SAVING,
SAVED,
SAVE_FAILURE
SAVE_FAILURE,
}

/**
Expand All @@ -61,15 +61,15 @@ const SAVE_STATUS_DELAY = 3500;
* profile page.) When editing mode is triggered, the editable data on the page
* switches from a static display to a form element display, allowing the user
* to edit the data saved in the database to be displayed on the page.
*
*
* @returns Profile component for the user
*/
const UserProfile = () => {
// Store the state of editing: true if we're editing, false if we're not.
const [ editingMode, setEditingMode ] = React.useState(false);
const [editingMode, setEditingMode] = React.useState(false);

// Store the state of the saving of edits.
const [ saveStatus, setSaveStatus ] = React.useState(SaveStatus.NO_STATUS);
const [saveStatus, setSaveStatus] = React.useState(SaveStatus.NO_STATUS);

// Grab the principalID that we're currently logged in as.
const { principalId: sessionPrincipalId } = React.useContext(SessionContext);
Expand All @@ -83,52 +83,55 @@ const UserProfile = () => {
});

// Store what we know about the user into a state variable.
const [ userData, setUserData ] = React.useState<UserData>(user_data);
const [userData, setUserData] = React.useState<UserData>(user_data);

// Once we get the information back from the API (which we know because
// api_user_data will have changed in value, triggering this useEffect), set
// the userData state variable and trigger a re-draw.
useEffect(() => {
const resetToAPIData = () => {
user_data = parseServerProfileResponse(api_user_data);
setUserData(user_data);
}, [api_user_data]);
setEditingMode(false);
};

useEffect(resetToAPIData, [api_user_data]);

// In order to load the form elements, we need a list of all possible social
// networks from which the user can select to enter a social profile.
const { data: social_networks_list } = useApiData<SocialNetwork[]>({
path: `/social_networks`,
sendCredentials: false
sendCredentials: false,
});

/**
* This function determines which message to show the user: a time-of-day-
* based greeting or a status update about whether or not their edited
* information is being saved.
*
*
* @param save_status The current status of the page's save operation.
* @returns The message that is displayed to the user at the top of the page.
*/
const userMessage = (save_status: SaveStatus) => {
switch(save_status) {
const userMessage = (save_status: SaveStatus) => {
switch (save_status) {
case SaveStatus.NO_STATUS:
return getGreeting(userData.full_name)
return getGreeting(userData.full_name);
case SaveStatus.SAVING:
return "📝 Saving..."
return '📝 Saving...';
case SaveStatus.SAVED:
return "✅ Saved successfully."
return '✅ Saved successfully.';
case SaveStatus.SAVE_FAILURE:
return "❌ Error occurred while trying to save edits."
return '❌ Error occurred while trying to save edits.';
default:
return ""
return '';
}
}
};

/**
* The updateUser function should get called when the user submits the edit
* form on the page. This sends the userData object back to the API, which
* detects which fields have been edited and updates the database
* accordingly.
*
*
* @param event Save event to be suppressed.
*/
const updateUser = (event: React.MouseEvent) => {
Expand All @@ -142,10 +145,10 @@ const UserProfile = () => {
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
"data": [submittedUserData],
"summary": {
"total_count": 1
}
data: [submittedUserData],
summary: {
total_count: 1,
},
}),
})
.then(res => res.json())
Expand All @@ -162,41 +165,26 @@ const UserProfile = () => {
}
if (data) {
setSaveStatus(SaveStatus.SAVED);

setEditingMode(false);
setUserData(prev => {
return { ...prev, userData: submittedUserData}});

return { ...prev, userData: submittedUserData };
});
}
})
.catch(error => {
// top-level error catch
console.error("uncaught error", error)
console.error('uncaught error', error);
});

};

return (
<Container fixed>
<Grid
container
sx={{
justifyContent: 'center',
alignItems: 'center',
display: 'flex',
mb: 4,
}}
spacing={2}
>
<Grid item>
<Typography component="h2" variant="h6" color="primary">
{ userMessage(saveStatus) }
</Typography>
</Grid>
</Grid>
<p>{sessionPrincipalId === userData.id ? userMessage(saveStatus) : ''}</p>
<Grid
container
justifyContent={{ md: 'center', sm: 'flex-start' }}
sx={{
marginTop: '1em',
justifyContent: 'center',
alignItems: 'center',
display: 'flex',
Expand Down Expand Up @@ -237,29 +225,32 @@ const UserProfile = () => {
flexDirection="column"
>
<Grid item display="flex">
<Typography component="h2" variant="h4">
{userData.full_name}
</Typography>
{ editingMode ? (
<Tooltip title="Save">
<IconButton
component="button"
sx={{ ml: 2 }}
onClick={() => {setEditingMode(!editingMode)}}
>
<CheckIcon color="primary" />
</IconButton>
</Tooltip>
{editingMode ? (
<TextField
id="full-name"
label="Full Name"
fullWidth
variant="filled"
value={userData?.full_name}
margin="normal"
/>
) : (
<Tooltip title="Edit">
<IconButton
component="button"
sx={{ ml: 2 }}
onClick={() => {setEditingMode(!editingMode)}}
>
<>
<Typography component="h2" variant="h4">
{userData.full_name}
</Typography>
<Tooltip title="Edit">
<IconButton
component="button"
sx={{ ml: 2 }}
onClick={() => {
setEditingMode(!editingMode);
}}
>
<EditIcon color="primary" />
</IconButton>
</Tooltip>
</IconButton>
</Tooltip>
</>
)}
</Grid>
<Grid
Expand All @@ -270,11 +261,15 @@ const UserProfile = () => {
sx={{ mt: 3 }}
ml={{ md: -1, sm: -2 }}
>
{ (editingMode) ?
<UserProfileEditingForm networksList={social_networks_list} userData={userData} setUserData={setUserData} updateUserFunction={updateUser} /> :
<UserProfileSocialLinks profileList={userData.social_profiles} />
}

{editingMode ? (
<UserProfileEditingForm
networksList={social_networks_list}
userData={userData}
setUserData={setUserData}
/>
) : (
<UserProfileSocialLinks profileList={userData.social_profiles} />
)}
</Grid>
</Grid>
</Grid>
Expand All @@ -284,31 +279,58 @@ const UserProfile = () => {
<Typography component="h3" variant="h4" sx={{ my: 2 }}>
Summary
</Typography>
<Typography component="div">
{userData.bio}
</Typography>
</Grid>
<Stack spacing={2} direction="row" sx={{ mt: 4 }}>
<Button variant="outlined" component="a" href={'/'}>
Journal
</Button>
<Button variant="outlined" component="a" href={'/competencies'}>
Competencies
</Button>
</Stack>
<Grid
item
xs={12}
display="flex"
flexDirection={{ md: 'row', xs: 'column' }}
>
<Grid item xs={12} md={6}>
<CompetencyRatings />
</Grid>
<Grid item xs={12} md={6}>
<ProgressBar />
</Grid>
{editingMode ? (
<Typography component="div">
<TextField
id="filled-basic"
label="User bio"
fullWidth
multiline
rows={4}
variant="filled"
value={userData.bio}
margin="normal"
/>
<Stack direction="row" spacing={2}>
<Button variant="contained" onClick={updateUser}>
Submit Edits
</Button>
<Button variant="outlined" onClick={resetToAPIData}>
Cancel Editing
</Button>
</Stack>
</Typography>
) : (
<Typography component="div">{userData.bio}</Typography>
)}
</Grid>
{editingMode ? (
''
) : (
<>
<Stack spacing={2} direction="row" sx={{ mt: 4 }}>
<Button variant="outlined" component="a" href={'/'}>
Journal
</Button>
<Button variant="outlined" component="a" href={'/competencies'}>
Competencies
</Button>
</Stack>
<Grid
item
xs={12}
display="flex"
flexDirection={{ md: 'row', xs: 'column' }}
>
<Grid item xs={12} md={6}>
<CompetencyRatings />
</Grid>
<Grid item xs={12} md={6}>
<ProgressBar />
</Grid>
</Grid>
</>
)}
</Grid>
</Container>
);
Expand Down
Loading

0 comments on commit e8b662e

Please sign in to comment.