Skip to content

Commit

Permalink
Merge pull request #3058 from Northeastern-Electric-Racing/#2817-Upco…
Browse files Browse the repository at this point in the history
…ming-Design-Reviews

#2817 upcoming design reviews
  • Loading branch information
walker-sean authored Dec 19, 2024
2 parents bc93cf1 + 07180f9 commit 4b28cc8
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 17 deletions.
6 changes: 5 additions & 1 deletion src/frontend/src/pages/HomePage/LeadHomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import PageLayout, { PAGE_GRID_HEIGHT } from '../../components/PageLayout';
import { AuthenticatedUser } from 'shared';
import ChangeRequestsToReview from './components/ChangeRequestsToReview';
import MyTeamsOverdueTasks from './components/MyTeamsOverdueTasks';
import UpcomingDesignReviews from './components/UpcomingDesignReviews';

interface LeadHomePageProps {
user: AuthenticatedUser;
Expand Down Expand Up @@ -39,10 +40,13 @@ const LeadHomePage = ({ user }: LeadHomePageProps) => {
<Box height={'40%'}>
<ChangeRequestsToReview user={user} />
</Box>
<Grid container height={'60%'}>
<Grid container height={'60%'} spacing={2}>
<Grid item xs={12} md={6} height={'100%'}>
<MyTeamsOverdueTasks user={user} />
</Grid>
<Grid item xs={12} md={6} height={'100%'}>
<UpcomingDesignReviews user={user} />
</Grid>
</Grid>
</Box>
</PageLayout>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const NoChangeRequestsToReview: React.FC = () => {
<EmptyPageBlockDisplay
icon={<CheckCircleOutlineOutlinedIcon sx={{ fontSize: 70 }} />}
heading={`You're all caught up!`}
message={'You have no unreviewed changre requests!'}
message={'You have no unreviewed change requests!'}
/>
);
};
Expand Down
117 changes: 117 additions & 0 deletions src/frontend/src/pages/HomePage/components/DesignReviewCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { Box, Card, CardContent, Link, Stack, Typography, useTheme } from '@mui/material';
import { DesignReview, User } from 'shared';
import { datePipe, projectWbsPipe } from '../../../utils/pipes';
import { routes } from '../../../utils/routes';
import { Link as RouterLink } from 'react-router-dom';
import CalendarMonthIcon from '@mui/icons-material/CalendarMonth';
import { LocationOnOutlined, Computer } from '@mui/icons-material';
import { useHistory } from 'react-router-dom';
import { NERButton } from '../../../components/NERButton';
import { meetingStartTimePipe } from '../../../../../backend/src/utils/design-reviews.utils';
import { timezoneOffset } from '../../../utils/datetime.utils';

interface DesignReviewProps {
designReview: DesignReview;
user: User;
}

const DesignReviewInfo = ({ icon, text, link }: { icon: React.ReactNode; text: string; link?: boolean }) => {
return (
<Stack direction="row" spacing={1}>
<Typography sx={{ fontSize: 21 }}>{icon}</Typography>
{link ? (
<Link href={text}>
<Typography fontWeight={'regular'} variant="body2">
{text}
</Typography>
</Link>
) : (
<Typography fontWeight={'regular'} variant="body2">
{text}
</Typography>
)}
</Stack>
);
};

const DisplayStatus: React.FC<DesignReviewProps> = ({ designReview, user }) => {
const history = useHistory();
const confirmedMemberIds = designReview.confirmedMembers.map((user) => user.userId);

return (
<>
{!confirmedMemberIds.includes(user.userId) ? (
<NERButton
variant="contained"
size="small"
sx={{ color: 'white', padding: 1 }}
onClick={() => {
history.push(`${routes.SETTINGS_PREFERENCES}?drId=${designReview.designReviewId}`);
}}
component={RouterLink}
>
Confirm Availibility
</NERButton>
) : (
<Typography mr={1}>{designReview.status}</Typography>
)}
</>
);
};

const getWeekday = (date: Date): string => {
const weekdays: string[] = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
return weekdays[date.getDay()];
};

const removeYear = (str: string): string => {
return str.substring(0, str.length - 5);
};

const UpcomingDesignReviewsCard: React.FC<DesignReviewProps> = ({ designReview, user }) => {
const theme = useTheme();
const timezoneAdjustedDate = timezoneOffset(designReview.dateScheduled);
return (
<Card
variant="outlined"
sx={{
width: '100%',
minHeight: 'fit-content',
mr: 3,
background: theme.palette.background.default,
borderRadius: 2
}}
>
<CardContent>
<Stack direction="row" justifyContent="space-between" alignItems={'center'}>
<Box sx={{ scrollbarWidth: 'auto', scrollbarColor: `${theme.palette.primary.main} transparent` }}>
<Typography fontWeight={'regular'} variant="h5" noWrap>
<Link component={RouterLink} to={`${routes.PROJECTS}/${projectWbsPipe(designReview.wbsNum)}`}>
{designReview.wbsName}
</Link>
</Typography>
<Stack direction="row" spacing={1} sx={{ mt: 0.5 }}>
<Typography>{<CalendarMonthIcon sx={{ fontSize: 21 }} />}</Typography>
<Typography fontWeight={'regular'} variant="body2">
{getWeekday(timezoneAdjustedDate) +
', ' +
removeYear(datePipe(timezoneAdjustedDate)) +
' @ ' +
meetingStartTimePipe(designReview.meetingTimes)}
</Typography>
</Stack>
{designReview.isInPerson && !!designReview.location && (
<DesignReviewInfo icon={<LocationOnOutlined />} text={designReview.location} />
)}
{designReview.isOnline && !!designReview.zoomLink && (
<DesignReviewInfo icon={<Computer />} text={designReview.zoomLink} link />
)}
</Box>
<DisplayStatus designReview={designReview} user={user} />
</Stack>
</CardContent>
</Card>
);
};

export default UpcomingDesignReviewsCard;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Box, Card, CardContent, Stack, Typography, useTheme } from '@mui/material';
import { Box, Card, CardContent, Typography, useTheme } from '@mui/material';
import { WorkPackage } from 'shared';
import EmptyPageBlockDisplay from './EmptyPageBlockDisplay';
import CheckCircleOutlineOutlinedIcon from '@mui/icons-material/CheckCircleOutlineOutlined';
Expand Down Expand Up @@ -57,32 +57,36 @@ const OverdueWorkPackagesView: React.FC<OverdueWorkPackagesViewProps> = ({ workP
<CardContent
sx={{
mt: isEmpty ? 0 : 4,
height: '100%',
display: 'flex',
flexDirection: 'column'
height: '100%'
}}
>
<Stack
spacing={2}
<Box
sx={{
mt: 2,
display: 'flex',
flexDirection: 'row',
flexWrap: 'wrap',
gap: 2,
height: '100%',
overflowY: 'auto',
'&::-webkit-scrollbar': {
height: '20px'
width: '20px'
},
'&::-webkit-scrollbar-track': {
backgroundColor: 'transparent'
},
'&::-webkit-scrollbar-thumb': {
backgroundColor: theme.palette.error.dark,
backgroundColor: theme.palette.primary.main,
borderRadius: '20px',
border: '6px solid transparent',
backgroundClip: 'content-box'
},
height: '100%'
scrollbarWidth: 'auto',
scrollbarColor: `${theme.palette.primary.main} transparent`
}}
>
{isEmpty ? <NoOverdueWPsDisplay /> : workPackages.map((wp) => <OverdueWorkPackageCard wp={wp} />)}
</Stack>
</Box>
</CardContent>
</Card>
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,26 @@ const ScrollablePageBlock: React.FC<ScrollablePageBlockProps> = ({ children, tit
sx={{
mt: 2,
display: 'flex',
flexDirection: horizontal ? 'row' : 'column',
flexDirection: 'row',
flexWrap: 'wrap',
gap: 2,
height: '100%',
overflowX: horizontal ? 'auto' : 'hidden',
overflowY: horizontal ? 'hidden' : 'auto',
'&::-webkit-scrollbar': {
height: '20px'
width: '20px'
},
'&::-webkit-scrollbar-track': {
backgroundColor: 'transparent'
},
'&::-webkit-scrollbar-thumb': {
backgroundColor: theme.palette.error.dark,
backgroundColor: theme.palette.primary.main,
borderRadius: '20px',
border: '6px solid transparent',
backgroundClip: 'content-box'
}
},
scrollbarWidth: 'auto',
scrollbarColor: `${theme.palette.primary.main} transparent`
}}
>
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const TeamTaskCard: React.FC<TeamTaskCardProps> = ({ task, taskNumber }) => {
<Card
variant="outlined"
sx={{
minWidth: 'fit-content',
width: '100%',
minHeight: 'fit-content',
mr: 3,
background: theme.palette.background.default,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* This file is part of NER's FinishLine and licensed under GNU AGPLv3.
* See the LICENSE file in the repository root folder for details.
*/

import DesignReviewCard from './DesignReviewCard';
import { useAllDesignReviews } from '../../../hooks/design-reviews.hooks';
import ErrorPage from '../../ErrorPage';
import { AuthenticatedUser, DesignReviewStatus, wbsPipe } from 'shared';
import LoadingIndicator from '../../../components/LoadingIndicator';
import ScrollablePageBlock from './ScrollablePageBlock';
import EmptyPageBlockDisplay from './EmptyPageBlockDisplay';
import { Error } from '@mui/icons-material';

interface UpcomingDesignReviewProps {
user: AuthenticatedUser;
}

const NoUpcomingDesignReviewsDisplay: React.FC = () => {
return (
<EmptyPageBlockDisplay
icon={<Error sx={{ fontSize: 70 }} />}
heading={'No Upcoming Design Reviews'}
message={'There are no Upcoming Design Reviews to Display'}
/>
);
};

const UpcomingDesignReviews: React.FC<UpcomingDesignReviewProps> = ({ user }) => {
const { data: designReviews, isLoading, isError, error } = useAllDesignReviews();

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

const filteredDesignReviews = designReviews.filter((review) => {
const scheduledDate = review.dateScheduled;
const currentDate = new Date();
const inTwoWeeks = new Date();
inTwoWeeks.setDate(currentDate.getDate() + 14);
const memberUserIds = [
...review.requiredMembers.map((user) => user.userId),
...review.optionalMembers.map((user) => user.userId)
];
// added in case the person who created the design review forgets to add their name onto the required members
memberUserIds.concat(review.userCreated.userId);
return (
scheduledDate >= currentDate &&
scheduledDate <= inTwoWeeks &&
review.status !== DesignReviewStatus.DONE &&
memberUserIds.includes(user.userId)
);
});

const fullDisplay = (
<ScrollablePageBlock title={`Upcoming Design Reviews (${filteredDesignReviews.length})`}>
{filteredDesignReviews.length === 0 ? (
<NoUpcomingDesignReviewsDisplay />
) : (
filteredDesignReviews.map((d) => <DesignReviewCard key={wbsPipe(d.wbsNum)} designReview={d} user={user} />)
)}
</ScrollablePageBlock>
);

return fullDisplay;
};

export default UpcomingDesignReviews;
5 changes: 5 additions & 0 deletions src/frontend/src/utils/datetime.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,8 @@ export const formatDate = (date: Date) => {
export const daysOverdue = (deadline: Date) => {
return Math.round((new Date().getTime() - deadline.getTime()) / (1000 * 60 * 60 * 24));
};

export const timezoneOffset = (date: Date) => {
const timestamp = new Date(date).getTime() - new Date(date).getTimezoneOffset() * -60000;
return new Date(timestamp);
};

0 comments on commit 4b28cc8

Please sign in to comment.