Skip to content

Commit 5c5fa67

Browse files
authored
Merge pull request #1265 from Northeastern-Electric-Racing/#1136-implement-new-sidebar
#1136 - Implemented Sidebar
2 parents 32eddda + 7378082 commit 5c5fa67

15 files changed

Lines changed: 297 additions & 159 deletions

File tree

src/frontend/src/app/AppAuthenticated.tsx

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,33 +21,55 @@ import LoadingIndicator from '../components/LoadingIndicator';
2121
import ErrorPage from '../pages/ErrorPage';
2222
import SetUserPreferences from '../pages/HomePage/SetUserPreferences';
2323
import Finance from '../pages/FinancePage/Finance';
24+
import { useState } from 'react';
25+
import NavTopBar from '../layouts/NavTopBar/NavTopBar';
26+
import Sidebar from '../layouts/Sidebar/Sidebar';
27+
import { Box } from '@mui/system';
28+
import DrawerHeader from '../components/DrawerHeader';
29+
import { Container } from '@mui/material';
2430

2531
interface AppAuthenticatedProps {
2632
userId: number;
2733
}
2834

2935
const AppAuthenticated: React.FC<AppAuthenticatedProps> = ({ userId }) => {
3036
const { isLoading, isError, error, data: userSettingsData } = useSingleUserSettings(userId);
37+
const [drawerOpen, setDrawerOpen] = useState(false);
38+
39+
const handleDrawerOpen = () => {
40+
setDrawerOpen(true);
41+
};
42+
43+
const handleDrawerClose = () => {
44+
setDrawerOpen(false);
45+
};
3146

3247
if (isLoading || !userSettingsData) return <LoadingIndicator />;
3348
if (isError) return <ErrorPage error={error} message={error.message} />;
3449

3550
return userSettingsData.slackId ? (
3651
<AppContextUser>
37-
<Switch>
38-
<Route path={routes.PROJECTS} component={Projects} />
39-
<Redirect from={routes.CR_BY_ID} to={routes.CHANGE_REQUESTS_BY_ID} />
40-
<Route path={routes.CHANGE_REQUESTS} component={ChangeRequests} />
41-
<Route path={routes.GANTT} component={GanttPageWrapper} />
42-
<Route path={routes.TEAMS} component={Teams} />
43-
<Route path={routes.SETTINGS} component={Settings} />
44-
<Route path={routes.ADMIN_TOOLS} component={AdminTools} />
45-
<Route path={routes.INFO} component={InfoPage} />
46-
<Route path={routes.CREDITS} component={Credits} />
47-
<Route path={routes.FINANCE} component={Finance} />
48-
<Route exact path={routes.HOME} component={Home} />
49-
<Route path="*" component={PageNotFound} />
50-
</Switch>
52+
<NavTopBar handleDrawerOpen={handleDrawerOpen} open={drawerOpen} />
53+
<Box display={'flex'}>
54+
<Sidebar handleDrawerClose={handleDrawerClose} open={drawerOpen} />
55+
<Container maxWidth={false}>
56+
<DrawerHeader />
57+
<Switch>
58+
<Route path={routes.PROJECTS} component={Projects} />
59+
<Redirect from={routes.CR_BY_ID} to={routes.CHANGE_REQUESTS_BY_ID} />
60+
<Route path={routes.CHANGE_REQUESTS} component={ChangeRequests} />
61+
<Route path={routes.GANTT} component={GanttPageWrapper} />
62+
<Route path={routes.TEAMS} component={Teams} />
63+
<Route path={routes.SETTINGS} component={Settings} />
64+
<Route path={routes.ADMIN_TOOLS} component={AdminTools} />
65+
<Route path={routes.INFO} component={InfoPage} />
66+
<Route path={routes.CREDITS} component={Credits} />
67+
<Route path={routes.FINANCE} component={Finance} />
68+
<Route exact path={routes.HOME} component={Home} />
69+
<Route path="*" component={PageNotFound} />
70+
</Switch>
71+
</Container>
72+
</Box>
5173
</AppContextUser>
5274
) : (
5375
<SetUserPreferences userSettings={userSettingsData} />
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* This file is part of NER's FinishLine and licensed under GNU AGPLv3.
3+
* See the LICENSE file in the repository root folder for details.
4+
*/
5+
6+
import { styled } from '@mui/material';
7+
8+
const DrawerHeader = styled('div')(({ theme }) => ({
9+
display: 'flex',
10+
alignItems: 'center',
11+
justifyContent: 'flex-end',
12+
padding: theme.spacing(0, 1),
13+
height: '68px',
14+
// necessary for content to be below app bar
15+
...theme.mixins.toolbar
16+
}));
17+
18+
export default DrawerHeader;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* This file is part of NER's FinishLine and licensed under GNU AGPLv3.
3+
* See the LICENSE file in the repository root folder for details.
4+
*/
5+
6+
import { styled } from '@mui/material';
7+
import AppBar, { AppBarProps as MuiAppBarProps } from '@mui/material/AppBar';
8+
import { DRAWERWIDTH } from './NERDrawer';
9+
10+
interface AppBarProps extends MuiAppBarProps {
11+
open?: boolean;
12+
}
13+
14+
const NERAppBar = styled(AppBar, {
15+
shouldForwardProp: (prop) => prop !== 'open'
16+
})<AppBarProps>(({ theme, open }) => ({
17+
zIndex: theme.zIndex.drawer + 1,
18+
transition: theme.transitions.create(['width', 'margin'], {
19+
easing: theme.transitions.easing.sharp,
20+
duration: theme.transitions.duration.leavingScreen
21+
}),
22+
...(open && {
23+
marginLeft: DRAWERWIDTH,
24+
width: `calc(100% - ${DRAWERWIDTH}px)`,
25+
transition: theme.transitions.create(['width', 'margin'], {
26+
easing: theme.transitions.easing.sharp,
27+
duration: theme.transitions.duration.enteringScreen
28+
})
29+
})
30+
}));
31+
32+
export default NERAppBar;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* This file is part of NER's FinishLine and licensed under GNU AGPLv3.
3+
* See the LICENSE file in the repository root folder for details.
4+
*/
5+
6+
import { CSSObject, Drawer, Theme, styled } from '@mui/material';
7+
8+
export const DRAWERWIDTH = 200;
9+
10+
const openedMixin = (theme: Theme): CSSObject => ({
11+
width: DRAWERWIDTH,
12+
transition: theme.transitions.create('width', {
13+
easing: theme.transitions.easing.sharp,
14+
duration: theme.transitions.duration.enteringScreen
15+
}),
16+
overflowX: 'hidden'
17+
});
18+
19+
const closedMixin = (theme: Theme): CSSObject => ({
20+
transition: theme.transitions.create('width', {
21+
easing: theme.transitions.easing.sharp,
22+
duration: theme.transitions.duration.leavingScreen
23+
}),
24+
overflowX: 'hidden',
25+
width: `calc(${theme.spacing(7)} + 1px)`,
26+
[theme.breakpoints.up('sm')]: {
27+
width: `calc(${theme.spacing(8)} + 1px)`
28+
}
29+
});
30+
31+
const NERDrawer = styled(Drawer, { shouldForwardProp: (prop) => prop !== 'open' })(({ theme, open }) => ({
32+
width: DRAWERWIDTH,
33+
flexShrink: 0,
34+
whiteSpace: 'nowrap',
35+
boxSizing: 'border-box',
36+
...(open && {
37+
...openedMixin(theme),
38+
'& .MuiDrawer-paper': openedMixin(theme)
39+
}),
40+
...(!open && {
41+
...closedMixin(theme),
42+
'& .MuiDrawer-paper': closedMixin(theme)
43+
})
44+
}));
45+
46+
export default NERDrawer;

src/frontend/src/components/PageLayout.tsx

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
1-
import { Box, Container } from '@mui/material';
1+
/*
2+
* This file is part of NER's FinishLine and licensed under GNU AGPLv3.
3+
* See the LICENSE file in the repository root folder for details.
4+
*/
5+
26
import { Helmet } from 'react-helmet';
3-
import NavTopBar from '../layouts/NavTopBar/NavTopBar';
4-
import Sidebar from '../layouts/Sidebar/Sidebar';
5-
import React, { ReactNode, ReactElement, Fragment } from 'react';
7+
import React, { ReactNode, ReactElement } from 'react';
68
import PageTitle from '../layouts/PageTitle/PageTitle';
7-
import { MUILinkItem } from '../utils/types';
9+
import { LinkItem } from '../utils/types';
10+
import { Box } from '@mui/system';
811

912
interface PageLayoutProps {
1013
title?: string;
1114
hidePageTitle?: boolean;
12-
previousPages?: MUILinkItem[];
15+
previousPages?: LinkItem[];
1316
headerRight?: ReactNode;
1417
tabs?: ReactElement;
1518
}
@@ -23,20 +26,14 @@ const PageLayout: React.FC<PageLayoutProps> = ({
2326
tabs
2427
}) => {
2528
return (
26-
<Fragment>
29+
<Box>
2730
<Helmet>
2831
<title>{`FinishLine ${title && `| ${title}`}`}</title>
2932
<meta name="description" content="FinishLine Project Management Dashboard" />
3033
</Helmet>
31-
<NavTopBar />
32-
<Sidebar />
33-
<Box sx={{ mt: '4rem', ml: '85px' }}>
34-
<Container maxWidth={false} sx={{ p: 1 }}>
35-
{!hidePageTitle && title && <PageTitle {...{ title, previousPages, headerRight, tabs }} />}
36-
{children}
37-
</Container>
38-
</Box>
39-
</Fragment>
34+
{!hidePageTitle && title && <PageTitle {...{ title, previousPages, headerRight, tabs }} />}
35+
{children}
36+
</Box>
4037
);
4138
};
4239

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/*
2+
* This file is part of NER's FinishLine and licensed under GNU AGPLv3.
3+
* See the LICENSE file in the repository root folder for details.
4+
*/
5+
6+
export interface LayoutProps {
7+
open?: boolean;
8+
}

src/frontend/src/layouts/NavTopBar/NavTopBar.tsx

Lines changed: 54 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,52 +3,72 @@
33
* See the LICENSE file in the repository root folder for details.
44
*/
55

6-
import AppBar from '@mui/material/AppBar';
76
import Box from '@mui/material/Box';
87
import Toolbar from '@mui/material/Toolbar';
98
import Typography from '@mui/material/Typography';
109
import { Link } from 'react-router-dom';
1110
import { routes } from '../../utils/routes';
12-
import { useAuth } from '../../hooks/auth.hooks';
1311
import { fullNamePipe } from '../../utils/pipes';
1412
import NavUserMenu from './NavUserMenu';
13+
import NERAppBar from '../../components/NERAppBar';
14+
import { LayoutProps } from '../LayoutProps';
15+
import { IconButton } from '@mui/material';
16+
import { GridMenuIcon } from '@mui/x-data-grid';
17+
import { useCurrentUser } from '../../hooks/users.hooks';
1518

1619
const textColor = 'white';
1720
const background = '#ef4345';
1821

19-
const NavTopBar: React.FC = () => {
20-
const auth = useAuth();
22+
interface NavTopBarProps extends LayoutProps {
23+
handleDrawerOpen: () => void;
24+
}
25+
26+
const NavTopBar: React.FC<NavTopBarProps> = ({ open, handleDrawerOpen }) => {
27+
const user = useCurrentUser();
2128
return (
22-
<Box sx={{ flexGrow: 1 }}>
23-
<AppBar position="fixed">
24-
<Toolbar disableGutters sx={{ height: 68, px: 1, background, color: textColor }}>
25-
<Box sx={{ flexGrow: 1 }}>
26-
<Link to={routes.HOME} style={{ textDecoration: 'none' }}>
27-
<Box sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
28-
<Box
29-
component="img"
30-
sx={{ height: 60 }}
31-
alt="Northeastern Electric Racing Logo"
32-
src="/NER-Logo-App-Icon.png"
33-
/>
34-
<Typography
35-
variant="h4"
36-
fontSize={30}
37-
component="div"
38-
sx={{ flexGrow: 1, paddingLeft: 2, color: textColor }}
39-
>
40-
FinishLine by NER
41-
</Typography>
42-
</Box>
43-
</Link>
44-
</Box>
45-
<Typography variant="body1" component="div" sx={{ color: textColor }}>
46-
{fullNamePipe(auth.user)}
47-
</Typography>
48-
<NavUserMenu />
49-
</Toolbar>
50-
</AppBar>
51-
</Box>
29+
<NERAppBar position="fixed" open={open}>
30+
<Toolbar disableGutters sx={{ height: 68, px: 1, background, color: textColor }}>
31+
<IconButton
32+
color="inherit"
33+
aria-label="open drawer"
34+
onClick={handleDrawerOpen}
35+
sx={{
36+
marginRight: 4,
37+
ml: '3px',
38+
...(open && { display: 'none' })
39+
}}
40+
>
41+
<GridMenuIcon />
42+
</IconButton>
43+
<Box sx={{ flexGrow: 1 }}>
44+
<Link to={routes.HOME} style={{ textDecoration: 'none' }}>
45+
<Box sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
46+
<Box
47+
component="img"
48+
sx={{ height: 60 }}
49+
alt="Northeastern Electric Racing Logo"
50+
src="/NER-Logo-App-Icon.png"
51+
/>
52+
<Typography variant="h4" fontSize={30} component="div" sx={{ flexGrow: 1, paddingLeft: 2, color: textColor }}>
53+
FinishLine by NER
54+
</Typography>
55+
</Box>
56+
</Link>
57+
</Box>
58+
<Typography
59+
variant="body1"
60+
sx={{
61+
color: textColor,
62+
'@media (max-width: 600px)': {
63+
display: 'none' // Hide the text on screens with width less than 600 pixels
64+
}
65+
}}
66+
>
67+
{fullNamePipe(user)}
68+
</Typography>
69+
<NavUserMenu />
70+
</Toolbar>
71+
</NERAppBar>
5272
);
5373
};
5474

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* This file is part of NER's FinishLine and licensed under GNU AGPLv3.
3+
* See the LICENSE file in the repository root folder for details.
4+
*/
5+
6+
import { NavLink } from 'react-router-dom';
7+
import { LinkItem } from '../../utils/types';
8+
import { routes } from '../../utils/routes';
9+
import { Typography, useTheme } from '@mui/material';
10+
11+
export interface NavPageLinkItemProps extends LinkItem {
12+
open?: boolean;
13+
}
14+
15+
const NavPageLink: React.FC<NavPageLinkItemProps> = ({ open, name, route, icon }) => {
16+
const theme = useTheme();
17+
return (
18+
<NavLink
19+
key={name}
20+
to={route}
21+
exact={route === routes.HOME}
22+
style={(isActive) => {
23+
return {
24+
textDecoration: 'none',
25+
color: isActive ? '#ef4345' : theme.palette.text.primary,
26+
backgroundColor: isActive ? 'white' : 'transparent',
27+
display: 'flex',
28+
flexDirection: 'row',
29+
justifyContent: open ? 'flex-start' : 'center',
30+
gap: '8px',
31+
borderRadius: '8px',
32+
padding: '8px',
33+
margin: '8px'
34+
};
35+
}}
36+
>
37+
{icon}
38+
{open && <Typography>{name}</Typography>}
39+
</NavLink>
40+
);
41+
};
42+
43+
export default NavPageLink;

0 commit comments

Comments
 (0)