Skip to content

Commit d8e8236

Browse files
authored
Merge pull request #2222 from Northeastern-Electric-Racing/#2217-wire-drc-page
#2217 wire drc page
2 parents 7f69812 + 36ba00e commit d8e8236

5 files changed

Lines changed: 141 additions & 89 deletions

File tree

src/frontend/src/apis/transformers/design-reviews.tranformers.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export const designReviewTransformer = (designReview: DesignReview) => {
44
return {
55
...designReview,
66
dateCreated: new Date(designReview.dateCreated),
7-
dateDeleted: designReview.dateDeleted ? new Date(designReview.dateDeleted) : undefined
7+
dateDeleted: designReview.dateDeleted ? new Date(designReview.dateDeleted) : undefined,
8+
dateScheduled: designReview.dateScheduled ? new Date(designReview.dateScheduled) : undefined
89
};
910
};

src/frontend/src/pages/CalendarPage/CalendarComponents/CalendarDayCard.tsx

Lines changed: 88 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Box, Card, CardContent, Grid, IconButton, Stack, Typography } from '@mui/material';
1+
import { Box, Card, CardContent, Grid, IconButton, Link, Stack, Tooltip, Typography } from '@mui/material';
22
import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline';
33
import { DesignReview } from 'shared';
44
import { meetingStartTimePipe } from '../../../utils/pipes';
@@ -8,15 +8,16 @@ import ElectricalServicesIcon from '@mui/icons-material/ElectricalServices';
88
import TerminalIcon from '@mui/icons-material/Terminal';
99
import { useState } from 'react';
1010
import DRCSummaryModal from '../DesignReviewSummaryModal';
11+
import DynamicTooltip from '../../../components/DynamicTooltip';
1112

12-
export const getTeamTypeIcon = (teamTypeId: string, isLarge?: boolean) => {
13+
export const getTeamTypeIcon = (teamTypeName: string, isLarge?: boolean) => {
1314
const teamIcons: Map<string, JSX.Element> = new Map([
1415
['Software', <TerminalIcon fontSize={isLarge ? 'large' : 'small'} />],
1516
['Business', <WorkOutlineIcon fontSize={isLarge ? 'large' : 'small'} />],
1617
['Electrical', <ElectricalServicesIcon fontSize={isLarge ? 'large' : 'small'} />],
1718
['Mechanical', <ConstructionIcon fontSize={isLarge ? 'large' : 'small'} />]
1819
]);
19-
return teamIcons.get(teamTypeId);
20+
return teamIcons.get(teamTypeName);
2021
};
2122

2223
interface CalendarDayCardProps {
@@ -40,33 +41,103 @@ const CalendarDayCard: React.FC<CalendarDayCardProps> = ({ cardDate, events }) =
4041
</Grid>
4142
);
4243

43-
const EventCard = (event: DesignReview) => {
44+
const EventCard = ({ event }: { event: DesignReview }) => {
4445
const [isSummaryModalOpen, setIsSummaryModalOpen] = useState(false);
45-
const name = event.designReviewId;
46+
const name = event.wbsName;
4647
return (
4748
<>
4849
<DRCSummaryModal open={isSummaryModalOpen} onHide={() => setIsSummaryModalOpen(false)} designReview={event} />
4950
<Box marginLeft={0.5} marginBottom={0.5} onClick={() => setIsSummaryModalOpen(true)} sx={{ cursor: 'pointer' }}>
5051
<Card sx={{ backgroundColor: 'red', borderRadius: 1, minWidth: 140, maxWidth: 140, minHeight: 20, maxHeight: 20 }}>
5152
<Stack direction="row">
52-
{getTeamTypeIcon(event.teamType.teamTypeId)}
53-
<Typography marginLeft={0.5} marginBottom={0.3} fontSize={14}>
54-
{name + ' ' + meetingStartTimePipe(event.meetingTimes)}
55-
</Typography>
53+
{getTeamTypeIcon(event.teamType.name)}
54+
<DynamicTooltip
55+
title={name + (event.meetingTimes.length > 0 ? ' - ' + meetingStartTimePipe(event.meetingTimes) : '')}
56+
>
57+
<Typography marginLeft={0.5} marginBottom={0.3} fontSize={14} noWrap>
58+
{name + (event.meetingTimes.length > 0 ? ' ' + meetingStartTimePipe(event.meetingTimes) : '')}
59+
</Typography>
60+
</DynamicTooltip>
5661
</Stack>
5762
</Card>
5863
</Box>
5964
</>
6065
);
6166
};
6267

63-
const ExtraEventsCard = (extraEvents: number) => {
68+
const ExtraEventNote = ({ event }: { event: DesignReview }) => {
69+
const [isSummaryModalOpen, setIsSummaryModalOpen] = useState(false);
70+
71+
return (
72+
<>
73+
<DRCSummaryModal open={isSummaryModalOpen} onHide={() => setIsSummaryModalOpen(false)} designReview={event} />
74+
<Link
75+
style={{ cursor: 'pointer' }}
76+
fontSize={15}
77+
onClick={() => {
78+
setIsSummaryModalOpen(true);
79+
}}
80+
>
81+
{event.wbsName + (event.meetingTimes.length > 0 ? ' - ' + meetingStartTimePipe(event.meetingTimes) : '')}
82+
</Link>
83+
</>
84+
);
85+
};
86+
87+
const ExtraEventsCard = ({ extraEvents }: { extraEvents: DesignReview[] }) => {
88+
const [showTooltip, setShowTooltip] = useState(false);
6489
return (
6590
<Box marginLeft={0.5} marginBottom={0.2}>
66-
<Card sx={{ backgroundColor: 'grey', borderRadius: 1, minWidth: 140, maxWidth: 140, minHeight: 20, maxHeight: 20 }}>
67-
<Typography marginLeft={0.5} marginBottom={0.3} align="center">
68-
{'+' + extraEvents}
69-
</Typography>
91+
<Card
92+
sx={{
93+
backgroundColor: 'grey',
94+
borderRadius: 1,
95+
minWidth: 140,
96+
maxWidth: 140,
97+
minHeight: 20,
98+
maxHeight: 20
99+
}}
100+
>
101+
<Tooltip
102+
id="tooltip"
103+
open={showTooltip}
104+
disableHoverListener
105+
onClick={() => setShowTooltip(!showTooltip)}
106+
placement="right"
107+
sx={{ cursor: 'pointer' }}
108+
PopperProps={{
109+
popperOptions: {
110+
modifiers: [
111+
{
112+
name: 'flip',
113+
options: {
114+
fallbackPlacements: ['top', 'bottom'],
115+
padding: -1,
116+
rootBoundary: 'document'
117+
}
118+
},
119+
{
120+
name: 'offset',
121+
options: {
122+
offset: [0, -1]
123+
}
124+
}
125+
]
126+
}
127+
}}
128+
arrow
129+
title={
130+
<Stack direction="column">
131+
{extraEvents.map((event) => (
132+
<ExtraEventNote event={event} />
133+
))}
134+
</Stack>
135+
}
136+
>
137+
<Typography marginLeft={0.5} marginBottom={0.3} align="center">
138+
{'+' + extraEvents.length}
139+
</Typography>
140+
</Tooltip>
70141
</Card>
71142
</Box>
72143
);
@@ -77,11 +148,11 @@ const CalendarDayCard: React.FC<CalendarDayCardProps> = ({ cardDate, events }) =
77148
<CardContent sx={{ padding: 0 }}>
78149
<DayCardTitle />
79150
{events.length < 3 ? (
80-
events.map((event) => EventCard(event))
151+
events.map((event) => <EventCard event={event} />)
81152
) : (
82153
<>
83-
{EventCard(events[1])}
84-
{ExtraEventsCard(events.length - 1)}
154+
<EventCard event={events[0]} />
155+
<ExtraEventsCard extraEvents={events.slice(1)} />
85156
</>
86157
)}
87158
</CardContent>

src/frontend/src/pages/CalendarPage/CalendarPage.tsx

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,43 @@ import { Box, Grid, Stack, Typography, useTheme } from '@mui/material';
77
import PageLayout from '../../components/PageLayout';
88
import { DesignReview } from 'shared';
99
import MonthSelector from './CalendarComponents/MonthSelector';
10-
import CalendarDayCard from './CalendarComponents/CalendarDayCard';
10+
import CalendarDayCard, { getTeamTypeIcon } from './CalendarComponents/CalendarDayCard';
1111
import FillerCalendarDayCard from './CalendarComponents/FillerCalendarDayCard';
12-
import {
13-
DAY_NAMES,
14-
EnumToArray,
15-
calendarPaddingDays,
16-
daysInMonth,
17-
exampleDesignReview1,
18-
testDesignReview1
19-
} from '../../utils/design-review.utils';
12+
import { DAY_NAMES, EnumToArray, calendarPaddingDays, daysInMonth, isConfirmed } from '../../utils/design-review.utils';
2013
import ActionsMenu from '../../components/ActionsMenu';
14+
import { useAllDesignReviews } from '../../hooks/design-reviews.hooks';
15+
import LoadingIndicator from '../../components/LoadingIndicator';
16+
import ErrorPage from '../ErrorPage';
17+
import { useCurrentUser } from '../../hooks/users.hooks';
18+
import { datePipe } from '../../utils/pipes';
2119

2220
const CalendarPage = () => {
2321
const theme = useTheme();
24-
2522
const [displayMonthYear, setDisplayMonthYear] = useState<Date>(new Date());
23+
const { isLoading, isError, error, data: allDesignReviews } = useAllDesignReviews();
24+
const user = useCurrentUser();
25+
26+
if (isLoading || !allDesignReviews) return <LoadingIndicator />;
27+
if (isError) return <ErrorPage message={error.message} />;
28+
29+
const confirmedDesignReviews = allDesignReviews.filter(isConfirmed);
2630

27-
const EventDict = new Map<Number, DesignReview[]>();
28-
// TODO remove during wire up ticket
29-
EventDict.set(new Date().getDate(), [exampleDesignReview1]);
30-
EventDict.set(new Date().getDate() + 3, [testDesignReview1, testDesignReview1]);
31-
EventDict.set(new Date().getDate() + 4, [testDesignReview1, testDesignReview1, testDesignReview1]);
32-
const designReviewData: DesignReview[] = [testDesignReview1, testDesignReview1];
31+
const eventDict = new Map<string, DesignReview[]>();
32+
confirmedDesignReviews.forEach((designReview) => {
33+
// Accessing the date actually converts it to local time, which causes the date to be off. This is a workaround.
34+
const date = datePipe(
35+
new Date(designReview.dateScheduled.getTime() - designReview.dateScheduled.getTimezoneOffset() * -60000)
36+
);
37+
if (eventDict.has(date)) {
38+
eventDict.get(date)?.push(designReview);
39+
} else {
40+
eventDict.set(date, [designReview]);
41+
}
42+
});
43+
44+
const unconfirmedDesignReviews = allDesignReviews.filter(
45+
(designReview) => designReview.userCreated.userId === user.userId && !isConfirmed(designReview)
46+
);
3347

3448
const startOfEachWeek = [0, 7, 14, 21, 28, 35];
3549

@@ -40,7 +54,8 @@ const CalendarPage = () => {
4054
const designReviewButtons = (designReviews: DesignReview[]) => {
4155
return designReviews.map((designReview) => {
4256
return {
43-
title: designReview.designReviewId,
57+
icon: getTeamTypeIcon(designReview.teamType.name),
58+
title: designReview.wbsName,
4459
onClick: () => {},
4560
disabled: false
4661
};
@@ -62,7 +77,7 @@ const CalendarPage = () => {
6277
.concat(paddingArrayEnd.length < 7 ? paddingArrayEnd : []);
6378

6479
const unconfirmedDRSDropdown = (
65-
<ActionsMenu title="My Unconfirmed DRS" buttons={designReviewButtons(designReviewData)}>
80+
<ActionsMenu title="My Unconfirmed DRS" buttons={designReviewButtons(unconfirmedDesignReviews)}>
6681
My Unconfirmed DRs
6782
</ActionsMenu>
6883
);
@@ -98,7 +113,13 @@ const CalendarPage = () => {
98113
{isDayInDifferentMonth(day, week) ? (
99114
<FillerCalendarDayCard day={day} />
100115
) : (
101-
<CalendarDayCard cardDate={cardDate} events={EventDict.get(cardDate.getDate()) ?? []} />
116+
<CalendarDayCard
117+
cardDate={cardDate}
118+
events={
119+
eventDict.get(datePipe(new Date(cardDate.getTime() - cardDate.getTimezoneOffset() * -60000))) ??
120+
[]
121+
}
122+
/>
102123
)}
103124
</Box>
104125
</Grid>

src/frontend/src/utils/design-review.utils.ts

Lines changed: 8 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -80,53 +80,6 @@ export const calendarPaddingDays = (month: Date): number => {
8080
return new Date(month.getFullYear(), month.getMonth(), 0).getDay();
8181
};
8282

83-
// TODO remove during wire up ticket
84-
export const testDesignReview1: DesignReview = {
85-
designReviewId: 'Meeting',
86-
dateScheduled: new Date(),
87-
meetingTimes: [16],
88-
dateCreated: new Date(),
89-
userCreated: batman,
90-
status: DesignReviewStatus.UNCONFIRMED,
91-
teamType: { teamTypeId: 'Mechanical', name: 'Mechanical', iconName: '' },
92-
requiredMembers: [],
93-
optionalMembers: [],
94-
confirmedMembers: [],
95-
deniedMembers: [],
96-
isOnline: false,
97-
isInPerson: false,
98-
attendees: [],
99-
wbsName: 'bruh',
100-
wbsNum: { carNumber: 1, workPackageNumber: 1, projectNumber: 1 }
101-
};
102-
103-
// TODO remove during wire up ticket
104-
export const exampleDesignReview1: DesignReview = {
105-
designReviewId: 'Wiring',
106-
dateScheduled: new Date(),
107-
meetingTimes: [1, 2, 5],
108-
dateCreated: new Date(),
109-
userCreated: superman,
110-
status: DesignReviewStatus.DONE,
111-
teamType: {
112-
teamTypeId: 'Electrical',
113-
name: 'thisteam',
114-
iconName: ''
115-
},
116-
requiredMembers: [batman, superman, greenlantern, flash, aquaman],
117-
optionalMembers: [wonderwoman, alfred],
118-
confirmedMembers: [],
119-
deniedMembers: [],
120-
location: 'Room 101',
121-
isOnline: true,
122-
isInPerson: false,
123-
zoomLink: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
124-
attendees: [],
125-
wbsName: 'Battery',
126-
wbsNum: { carNumber: 1, projectNumber: 1, workPackageNumber: 1 },
127-
docTemplateLink: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'
128-
};
129-
13083
// TODO: We will have to make a call to the backend to get this data
13184
export const usersToAvailabilities = new Map([
13285
[superman, [1, 2, 3, 4, 5, 6, 7]],
@@ -145,3 +98,11 @@ existingMeetingData.set(5, 'warning');
14598
existingMeetingData.set(10, 'build');
14699
existingMeetingData.set(20, 'computer');
147100
existingMeetingData.set(50, 'electrical');
101+
102+
export const isConfirmed = (designReview: DesignReview): boolean => {
103+
return (
104+
designReview.status === DesignReviewStatus.CONFIRMED ||
105+
designReview.status === DesignReviewStatus.SCHEDULED ||
106+
designReview.status === DesignReviewStatus.DONE
107+
);
108+
};

src/frontend/src/utils/pipes.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
*/
55

66
import { WbsNumber, User, wbsPipe, WbsElement, isProject, WorkPackage, ClubAccount, ExpenseType } from 'shared';
7-
import { NOON_IN_MINUTES } from './design-review.utils';
87

98
/**
109
* Pipes:
@@ -165,10 +164,9 @@ export const displayEnum = (enumString: string) => {
165164
};
166165

167166
export const meetingStartTimePipe = (times: number[]) => {
168-
const time = times[0] * 15 + 9 * 60;
169-
const minutes = time % 60;
170-
const hours = ((time - minutes) / 60) % 12 === 0 ? 12 : ((time - minutes) / 60) % 12;
171-
return hours + (minutes !== 0 ? ':' + minutes : '') + (time >= NOON_IN_MINUTES ? 'pm' : 'am');
167+
const time = (times[0] % 12) + 10;
168+
169+
return time <= 12 ? time + 'am' : time - 12 + 'pm';
172170
};
173171

174172
// takes in a Date and returns it as a string in the form mm/dd/yy

0 commit comments

Comments
 (0)