Skip to content

Commit

Permalink
Merge pull request #5 from slacgismo/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
Thistleman authored Sep 5, 2024
2 parents 9afdc56 + d693a61 commit 4dd5763
Show file tree
Hide file tree
Showing 11 changed files with 440 additions and 332 deletions.
2 changes: 1 addition & 1 deletion __tests__/app.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ describe('Footer', () => {

// Filter this array to get the specific links
const blogLink = allLinks.find((link) => (
link.textContent === 'Blog')
link.textContent === 'News')
);
expect(blogLink).toBeInTheDocument();
});
Expand Down
Binary file added public/static/assets/profilecovers/admin.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
304 changes: 104 additions & 200 deletions src/app/(profile)/profile/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,7 @@
// *********** START OF IMPORTS ***********

import React, {useEffect, useState} from 'react';
import {
Avatar,
Card,
CardContent,
CardMedia,
CircularProgress,
Grid, Typography,
TextField,
Button,
CardActions,
} from '@mui/material';
import {Box} from '@mui/system';
import {styled} from '@mui/material/styles';
import Dialog from '@mui/material/Dialog';
import DialogContent from '@mui/material/DialogContent';
import DialogAction from '@mui/material/DialogActions';
import {CircularProgress} from '@mui/material';
import {useRouter} from 'next/navigation';
import {useSearchParams} from 'next/navigation';

Expand All @@ -27,69 +12,43 @@ import Header from '@/app/modules/header/header';
import Footer from '@/app/modules/footer/footer';
import CookieService from '@/services/cookie_service';
import UserService from '@/services/user_service';
import Elink from '@/app/modules/elink/elink';
import PersonalDetails from '@/app/modules/profile/personal';
import ProfileActions from '@/app/modules/profile/actions';
import ProfileTasks from '@/app/modules/profile/tasks';

// *********** REDUX IMPORTS ***********

// *********** END OF IMPORTS ***********


// *********** START OF TYPES ***********

type UserDetails = {
username: string;
email: string;
firstName: string;
lastName: string;
githubLink: string;
addtlLinks: string[];
webLinks: { [key: string]: string };
organization: string;
title: string;
profileImage: string;
}

const UpdateDialog = styled(Dialog)(({theme}) => ({
'& .MuiDialogContent-root': {
padding: theme.spacing(2),
},
'& .MuiDialogActions-root': {
padding: theme.spacing(1),
},
}));

// Dialog that shows whether user profile successfully updated or not.
/**
* ProfileUpdateDialog component
* @param {boolean} open - whether the dialog is open
* @param {function} onClose - function to handle the close
* @return {JSX.Element} - ProfileUpdateDialog component
*/
function ProfileUpdateDialog({open, onClose}:
{open: boolean, onClose: any}) {
return (
<UpdateDialog open={open}>
<DialogContent dividers>
<Typography gutterBottom>
Update Succeed!
</Typography>
</DialogContent>
<DialogAction>
<Button autoFocus onClick={onClose}>
<Typography variant="body2">
Done
</Typography>
</Button>
</DialogAction>
</UpdateDialog>
);
type AllDetails = {
username: string;
email: string;
firstName: string;
lastName: string;
githubLink: string;
webLinks: { [key: string]: string };
organization: string;
title: string;
profileImage: string;
submitted_tasks: { [key: string]: string };
}

const ProfileCardContent = styled(CardContent)(({theme}) => ({
'& .MuiTypography-root': {
marginTop: theme.spacing(2),
marginBottom: theme.spacing(2),
},
'& .MuiTypography-body1': {
align: 'center',
justifyContent: 'flex-end',
},
'marginBottom': 0.5,
'marginTop': 0.5,
}));
// *********** END OF TYPES ***********

const ProfilePage: React.FC = () => {
const router = useRouter();
Expand All @@ -99,30 +58,58 @@ const ProfilePage: React.FC = () => {

const [user, setUser] = useState(userProfile);
const [userToken, setUserToken] = useState({token: ''});

// prepare for user profile fields update
const [githubLink, setUserGithubLink] = useState('');
const [emailLink, setUserEmailLink] = useState('');
const [updateDialogOpen, setUpdateDialogOpen] = useState(false);
const [isLoading, setIsLoading] = useState(true);

const url = '/account/public/';

const urlUpdate = (
userToken !== null
) && (
userToken !== undefined
) ? '/account/update/' : '';

const [userDetails, setUserDetails] = useState<UserDetails>({
username: '',
email: '',
firstName: '',
lastName: '',
githubLink: '',
addtlLinks: [],
webLinks: {},
organization: '',
title: '',
profileImage: 'ducky.jpg',
});

const [submittedTasks, setSubmittedTasks] = useState<[string, string][]>([]);

const formatUserDetails = (userDetails: AllDetails) => {
const details: UserDetails = {
username: '',
email: '',
firstName: '',
lastName: '',
githubLink: '',
webLinks: {},
organization: '',
title: '',
profileImage: 'ducky.jpg',
};
details.username = userDetails.username;
details.email = userDetails.email;
details.firstName = userDetails.firstName;
details.lastName = userDetails.lastName;
details.githubLink = userDetails.githubLink;
details.webLinks = userDetails.webLinks;
details.organization = userDetails.organization;
details.title = userDetails.title;
details.profileImage = userDetails.profileImage;
if (details.title === '') {
details.title = 'User';
}
if (details.organization === '') {
details.organization = 'N/A';
}
setUserDetails(details);

// Convert submitted_tasks to an array of tuples
const submittedTasksArray = Object.entries(userDetails.submitted_tasks);
setSubmittedTasks(submittedTasksArray);
};

useEffect(() => {
const retUser = CookieService.getUserCookie();
if (retUser === null || retUser === undefined) {
Expand All @@ -139,7 +126,7 @@ const ProfilePage: React.FC = () => {
UserService.getUserDetails(url, user)
.then((response) => {
console.log('userInfo: ', response);
setUserDetails(response.data);
formatUserDetails(response.data);
setIsLoading(false);
})
.catch((error) => {
Expand All @@ -148,26 +135,6 @@ const ProfilePage: React.FC = () => {
});
}, [userProfile, user, url]);

const handleTextChange = (setState: any) => (
event: React.ChangeEvent<HTMLInputElement>
) => {
setState(event.target.value);
};

const handleProfileUpdateClick = (userToken: any) => {
const updatedProfile = {
email: emailLink === '' ? userDetails.email : emailLink,
githubLink: githubLink === '' ?
userDetails.githubLink : githubLink,
};
// todo: check return value
// eslint-disable-next-line no-unused-vars
const ret = UserService.updateUserProfile(
userToken.token, updatedProfile, urlUpdate
);
setUpdateDialogOpen(true);
};

const renderContent = () => {
switch (true) {
case (userToken === undefined) || (userToken === null):
Expand All @@ -177,70 +144,44 @@ const ProfilePage: React.FC = () => {
return <CircularProgress />;
default:
return (
<Grid container spacing={2}>
<Grid item xs={12} md={3}>
<Card variant="outlined">
<CardMedia
sx={{marginTop: 1, marginBottom: 2}}>
<Avatar
sx={{height: 170, width: 174}}
alt={userDetails.firstName}
src={'/static/assets/profilecovers/ducky.jpg'}
/>
</CardMedia>
<CardContent >
<Typography variant="h5">
{`${userDetails.firstName}
${userDetails.lastName}`}
</Typography>
<br />
</CardContent>
</Card>
</Grid>
<Grid item xs={12} md={9}>
<Card variant="outlined">
<ProfileCardContent>
<InfoRow
title="Full Name"
defaultValue={`${userDetails.firstName}
${userDetails.lastName}`}
disabled
/>
<InfoRow
title="Email"
defaultValue={userDetails.email}
disabled={false}
onChange={handleTextChange(setUserEmailLink)}
/>
<InfoRow
title="Username"
defaultValue={userDetails.username}
disabled
/>
<InfoRow
title="Github"
defaultValue={userDetails.githubLink}
disabled={false}
onChange={handleTextChange(setUserGithubLink)}
/>
<Elink url={userDetails.githubLink} />
</ProfileCardContent>
<CardActions>
<Button
variant="contained"
onClick={handleProfileUpdateClick}>
<Typography >Update profile</Typography>
</Button>
</CardActions>
</Card>
</Grid>
<ProfileUpdateDialog
open={updateDialogOpen}
onClose={() => {
setUpdateDialogOpen(false);
}}
/>
</Grid>
<div className="flex flex-col tableBorder bg-white">
<div className="
b-border
pal-border
flex-1
p-3
">
<h1 className="text-xl">Personal Details</h1>
</div>
<div className="flex-1 min-h-80">
<PersonalDetails
userDetails={userDetails}
userToken={userToken.token}
/>
</div>
<div className="
bt-border
pal-border
flex-1
p-3
">
<h1 className="text-xl">Submitted Tasks</h1>
</div>
<div className="flex-1 min-h-80">
<ProfileTasks submittedTasks={submittedTasks} />
</div>
<div className="
bt-border
pal-border
flex-1
p-3
">
<h1 className="text-xl">More Actions</h1>
</div>
<div className="flex-1 min-h-80">
<ProfileActions />
</div>
</div>
);
}
};
Expand All @@ -250,50 +191,13 @@ const ProfilePage: React.FC = () => {
<Header />
<main className="flex min-h-screen flex-col
items-center justify-between p-24">
<Box sx={{marginTop: 5, marginLeft: 4, marginRight: 4}}>
<div>
{ renderContent() }
</Box>
</div>
</main>
<Footer />
</div>
);
};

/**
* InfoRow component
* @param {string} title - title of the row
* @param {string} defaultValue - default value of the row
* @param {boolean} disabled - whether the row is disabled
* @param {function} onChange - function to handle the change
* @return {JSX.Element} - InfoRow component
*/
function InfoRow({
title, defaultValue, disabled, onChange,
}: {
title: string, defaultValue: string, disabled: boolean, onChange?: any,
}) {
return (
<Box>
<Grid container spacing={5}>
<Grid item xs={2} alignItems="center" justifyContent="center">
<Typography variant="body1">
{title}
</Typography>
</Grid>
<Grid item xs={8}>
<TextField
fullWidth
hiddenLabel
size="small"
defaultValue={defaultValue}
disabled={disabled}
variant="filled"
onChange={onChange}
/>
</Grid>
</Grid>
</Box>
);
}

export default ProfilePage;
Loading

0 comments on commit 4dd5763

Please sign in to comment.