Skip to content

Commit 3932dc3

Browse files
authored
Merge pull request #4138 from Northeastern-Electric-Racing/#4087-Individual-Teamtype-Page
#4087 individual teamtype page
2 parents 08e8a51 + e94bf8c commit 3932dc3

6 files changed

Lines changed: 203 additions & 33 deletions

File tree

src/frontend/src/layouts/Sidebar/Sidebar.tsx

Lines changed: 26 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@ import { useHomePageContext } from '../../app/HomePageContext';
2727
// once divisions developed, import TeamType from shared
2828
import { isGuest } from 'shared';
2929
// To be uncommented after divisions page is developed
30-
// import * as MuiIcons from '@mui/icons-material';
31-
// import { useAllTeamTypes } from '../../hooks/team-types.hooks';
32-
// import ErrorPage from '../../pages/ErrorPage';
30+
import * as MuiIcons from '@mui/icons-material';
31+
import { useAllTeamTypes } from '../../hooks/team-types.hooks';
32+
import { TeamType } from 'shared';
33+
import ErrorPage from '../../pages/ErrorPage';
3334
import BarChartIcon from '@mui/icons-material/BarChart';
3435
import { useCurrentUser } from '../../hooks/users.hooks';
3536
import QueryStatsIcon from '@mui/icons-material/QueryStats';
@@ -50,19 +51,18 @@ const Sidebar = ({ drawerOpen, setDrawerOpen, moveContent, setMoveContent }: Sid
5051
const { onPNMHomePage, onOnboardingHomePage } = useHomePageContext();
5152
const user = useCurrentUser();
5253
const { onGuestHomePage } = useHomePageContext();
53-
// const { isError: teamsError, error: teamsErrorMsg, data: teams } = useAllTeamTypes();
54+
const { isError: teamsError, error: teamsErrorMsg, data: teams } = useAllTeamTypes();
5455

55-
// To be uncommented once guest divisions pages are developed
56-
// const allTeams: LinkItem[] = (teams ?? []).map((team: TeamType) => {
57-
// const IconComponent = MuiIcons[(team.iconName in MuiIcons ? team.iconName : 'Circle') as keyof typeof MuiIcons];
58-
// return {
59-
// name: team.name,
60-
// icon: <IconComponent />,
61-
// route: routes.TEAMS + '/' + team.teamTypeId
62-
// };
63-
// });
56+
const allTeams: LinkItem[] = (teams ?? []).map((team: TeamType) => {
57+
const IconComponent = MuiIcons[(team.iconName in MuiIcons ? team.iconName : 'Circle') as keyof typeof MuiIcons];
58+
return {
59+
name: team.name,
60+
icon: <IconComponent />,
61+
route: routes.TEAMS + '/' + team.teamTypeId
62+
};
63+
});
6464

65-
// if (teamsError) return <ErrorPage error={teamsErrorMsg} />;
65+
if (teamsError) return <ErrorPage error={teamsErrorMsg} />;
6666
const memberLinkItems: LinkItem[] = [
6767
{
6868
name: 'Home',
@@ -136,23 +136,18 @@ const Sidebar = ({ drawerOpen, setDrawerOpen, moveContent, setMoveContent }: Sid
136136
},
137137

138138
// Teams tab here to be replaced with below code once guest divisions is developed
139-
!onGuestHomePage && {
140-
name: 'Teams',
141-
icon: <GroupIcon />,
142-
route: routes.TEAMS
143-
},
144-
// !onGuestHomePage
145-
// ? {
146-
// name: 'Teams',
147-
// icon: <GroupIcon />,
148-
// route: routes.TEAMS
149-
// }
150-
// : {
151-
// name: 'Divisions',
152-
// icon: <GroupIcon />,
153-
// route: routes.TEAMS,
154-
// subItems: allTeams
155-
// },
139+
!onGuestHomePage
140+
? {
141+
name: 'Teams',
142+
icon: <GroupIcon />,
143+
route: routes.TEAMS
144+
}
145+
: {
146+
name: 'Divisions',
147+
icon: <GroupIcon />,
148+
route: routes.TEAMS,
149+
subItems: allTeams
150+
},
156151
!onGuestHomePage && {
157152
name: 'Calendar',
158153
icon: <CalendarTodayIcon />,
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { Box, Card, CardContent, Stack, Typography, useTheme, Link } from '@mui/material';
2+
import { TeamPreview } from 'shared';
3+
import { NERButton } from '../../components/NERButton';
4+
import { Link as RouterLink } from 'react-router-dom';
5+
6+
interface GuestSubteamCardProps {
7+
team: TeamPreview;
8+
}
9+
10+
const GuestSubteamCard: React.FC<GuestSubteamCardProps> = ({ team }) => {
11+
const theme = useTheme();
12+
13+
return (
14+
<Card
15+
variant="outlined"
16+
sx={{
17+
width: '100%',
18+
height: '100%',
19+
display: 'flex',
20+
flexDirection: 'column',
21+
background: theme.palette.background.paper,
22+
borderRadius: 2
23+
}}
24+
>
25+
<CardContent sx={{ padding: 2, display: 'flex', flexDirection: 'column', flexGrow: 1 }}>
26+
<Stack direction="row" justifyContent="space-between">
27+
<Box width={'100%'}>
28+
<Box display="flex" justifyContent="space-between" alignItems="center">
29+
<Typography
30+
fontWeight={'regular'}
31+
variant="h5"
32+
sx={{ marginBottom: '0.2rem', fontSize: { xs: '1.15rem', sm: '1.5rem' }, flexGrow: 1 }}
33+
>
34+
{team.teamName}
35+
</Typography>
36+
</Box>
37+
<Typography fontSize={12} color="text.secondary" sx={{ marginBottom: 1 }}>
38+
Project Lead:{' '}
39+
{team.head?.firstName && team.head?.lastName ? `${team.head.firstName} ${team.head.lastName}` : 'N/A'}
40+
{' • '}
41+
{team.leads.length} {team.leads.length === 1 ? 'lead' : 'leads'}
42+
{' • '}
43+
{team.members.length} {team.members.length === 1 ? 'member' : 'members'}
44+
</Typography>
45+
</Box>
46+
</Stack>
47+
<Typography
48+
sx={{
49+
fontSize: 15,
50+
lineHeight: 1.4,
51+
flexGrow: 1,
52+
overflow: 'hidden',
53+
textOverflow: 'ellipsis',
54+
display: '-webkit-box',
55+
WebkitLineClamp: 3,
56+
WebkitBoxOrient: 'vertical'
57+
}}
58+
>
59+
{team.description}
60+
</Typography>
61+
<Link component={RouterLink} to={`/teams/${team.teamId}`} sx={{ width: '100%', textDecoration: 'none' }}>
62+
<NERButton
63+
fullWidth
64+
sx={{
65+
marginTop: 2,
66+
backgroundColor: theme.palette.error.main,
67+
color: theme.palette.error.contrastText,
68+
'&:hover': {
69+
backgroundColor: theme.palette.error.dark
70+
}
71+
}}
72+
>
73+
Learn more
74+
</NERButton>
75+
</Link>
76+
</CardContent>
77+
</Card>
78+
);
79+
};
80+
81+
export default GuestSubteamCard;
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import LoadingIndicator from '../../components/LoadingIndicator';
2+
import ErrorPage from '../ErrorPage';
3+
import { Box, useMediaQuery } from '@mui/system';
4+
import PageLayout from '../../components/PageLayout';
5+
import GuestSubteamCard from './GuestSubteamCard';
6+
import { useAllTeams } from '../../hooks/teams.hooks';
7+
import { useAllTeamTypes } from '../../hooks/team-types.hooks';
8+
import { Typography } from '@mui/material';
9+
10+
interface GuestTeamPageProps {
11+
teamTypeId: string;
12+
}
13+
14+
const GuestTeamPage: React.FC<GuestTeamPageProps> = ({ teamTypeId }) => {
15+
const isMobilePortrait = useMediaQuery('(max-width:480px)');
16+
const { isLoading: teamsIsLoading, isError: teamsIsError, data: allTeams, error: teamsError } = useAllTeams();
17+
const {
18+
isLoading: teamTypesIsLoading,
19+
isError: teamTypesIsError,
20+
data: allTeamTypes,
21+
error: teamTypesError
22+
} = useAllTeamTypes();
23+
24+
if (teamsIsError) return <ErrorPage message={teamsError.message} />;
25+
if (teamTypesIsError) return <ErrorPage message={teamTypesError.message} />;
26+
if (teamsIsLoading || !allTeams || teamTypesIsLoading || !allTeamTypes) return <LoadingIndicator />;
27+
28+
const teams = allTeams.filter((team) => team.teamType?.teamTypeId === teamTypeId);
29+
const teamTypeName = allTeamTypes.find((tt) => tt.teamTypeId === teamTypeId)?.name ?? '';
30+
31+
if (teams.length === 0) {
32+
return (
33+
<PageLayout title={teamTypeName}>
34+
<Box
35+
sx={{
36+
display: 'flex',
37+
justifyContent: 'center',
38+
alignItems: 'center',
39+
height: '70vh'
40+
}}
41+
>
42+
<Typography>No Teams found for this Division</Typography>
43+
</Box>
44+
</PageLayout>
45+
);
46+
}
47+
48+
return (
49+
<PageLayout title={teamTypeName}>
50+
<Box
51+
sx={{
52+
display: 'grid',
53+
gridTemplateColumns: isMobilePortrait ? '1fr' : 'repeat(3, 1fr)',
54+
gap: isMobilePortrait ? 2 : 3,
55+
width: '100%',
56+
px: isMobilePortrait ? 1 : 0
57+
}}
58+
>
59+
{teams.map((team) => (
60+
<GuestSubteamCard key={team.teamId} team={team} />
61+
))}
62+
</Box>
63+
</PageLayout>
64+
);
65+
};
66+
67+
export default GuestTeamPage;

src/frontend/src/pages/GuestProjectsPage/GuestProjectsCard.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ const GuestProjectsCard: React.FC<ProjectCardProps> = ({ project }) => {
7878
</Stack>
7979
<Typography
8080
sx={{
81+
fontSize: 15,
82+
lineHeight: 1.4,
8183
flexGrow: 1,
8284
overflow: 'hidden',
8385
textOverflow: 'ellipsis',

src/frontend/src/pages/TeamsPage/TeamSpecificPage.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,11 @@ const TeamSpecificPage: React.FC = () => {
194194
</Box>
195195
) : null
196196
}
197-
previousPages={[{ name: 'Teams', route: routes.TEAMS }]}
197+
previousPages={
198+
isGuest(user.role) && data.teamType
199+
? [{ name: data.teamType.name, route: `${routes.TEAMS}/${data.teamType.teamTypeId}` }]
200+
: [{ name: 'Teams', route: routes.TEAMS }]
201+
}
198202
>
199203
<Grid container spacing={2}>
200204
<Grid item xs={12}>

src/frontend/src/pages/TeamsPage/Teams.tsx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,32 @@ import { Route, Switch } from 'react-router-dom';
77
import { routes } from '../../utils/routes';
88
import TeamsPage from './TeamsPage';
99
import TeamSpecificPage from './TeamSpecificPage';
10+
import { useParams } from 'react-router-dom';
11+
import { useCurrentUser } from '../../hooks/users.hooks';
12+
import { isGuest } from 'shared';
13+
import { useAllTeamTypes } from '../../hooks/team-types.hooks';
14+
import GuestTeamPage from '../GuestDivisionPage/GuestTeamPage';
15+
import LoadingIndicator from '../../components/LoadingIndicator';
16+
import ErrorPage from '../ErrorPage';
17+
18+
const TeamOrDivisionPage: React.FC = () => {
19+
const { teamId } = useParams<{ teamId: string }>();
20+
const user = useCurrentUser();
21+
const { isLoading: teamsLoading, isError: isTeamsError, data: teamTypes, error: teamsError } = useAllTeamTypes();
22+
23+
if (isTeamsError) return <ErrorPage message={teamsError.message} />;
24+
if (teamsLoading || !teamTypes) return <LoadingIndicator />;
25+
26+
if (isGuest(user.role) && teamTypes?.some((t) => t.teamTypeId === teamId)) {
27+
return <GuestTeamPage teamTypeId={teamId} />;
28+
}
29+
return <TeamSpecificPage />;
30+
};
1031

1132
const Teams: React.FC = () => {
1233
return (
1334
<Switch>
14-
<Route path={routes.TEAMS_BY_ID} component={TeamSpecificPage} />
35+
<Route path={routes.TEAMS_BY_ID} component={TeamOrDivisionPage} />
1536
<Route path={routes.TEAMS} component={TeamsPage} />
1637
</Switch>
1738
);

0 commit comments

Comments
 (0)