diff --git a/src/frontend/src/apis/transformers/design-reviews.tranformers.ts b/src/frontend/src/apis/transformers/design-reviews.tranformers.ts index 8ebf99e573..aa792f28b8 100644 --- a/src/frontend/src/apis/transformers/design-reviews.tranformers.ts +++ b/src/frontend/src/apis/transformers/design-reviews.tranformers.ts @@ -4,6 +4,7 @@ export const designReviewTransformer = (designReview: DesignReview) => { return { ...designReview, dateCreated: new Date(designReview.dateCreated), - dateDeleted: designReview.dateDeleted ? new Date(designReview.dateDeleted) : undefined + dateDeleted: designReview.dateDeleted ? new Date(designReview.dateDeleted) : undefined, + dateScheduled: designReview.dateScheduled ? new Date(designReview.dateScheduled) : undefined }; }; diff --git a/src/frontend/src/pages/CalendarPage/CalendarComponents/CalendarDayCard.tsx b/src/frontend/src/pages/CalendarPage/CalendarComponents/CalendarDayCard.tsx index 67eec84dd2..9390f3bad3 100644 --- a/src/frontend/src/pages/CalendarPage/CalendarComponents/CalendarDayCard.tsx +++ b/src/frontend/src/pages/CalendarPage/CalendarComponents/CalendarDayCard.tsx @@ -1,4 +1,4 @@ -import { Box, Card, CardContent, Grid, IconButton, Stack, Typography } from '@mui/material'; +import { Box, Card, CardContent, Grid, IconButton, Link, Stack, Tooltip, Typography } from '@mui/material'; import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline'; import { DesignReview } from 'shared'; import { meetingStartTimePipe } from '../../../utils/pipes'; @@ -8,15 +8,16 @@ import ElectricalServicesIcon from '@mui/icons-material/ElectricalServices'; import TerminalIcon from '@mui/icons-material/Terminal'; import { useState } from 'react'; import DRCSummaryModal from '../DesignReviewSummaryModal'; +import DynamicTooltip from '../../../components/DynamicTooltip'; -export const getTeamTypeIcon = (teamTypeId: string, isLarge?: boolean) => { +export const getTeamTypeIcon = (teamTypeName: string, isLarge?: boolean) => { const teamIcons: Map = new Map([ ['Software', ], ['Business', ], ['Electrical', ], ['Mechanical', ] ]); - return teamIcons.get(teamTypeId); + return teamIcons.get(teamTypeName); }; interface CalendarDayCardProps { @@ -40,19 +41,23 @@ const CalendarDayCard: React.FC = ({ cardDate, events }) = ); - const EventCard = (event: DesignReview) => { + const EventCard = ({ event }: { event: DesignReview }) => { const [isSummaryModalOpen, setIsSummaryModalOpen] = useState(false); - const name = event.designReviewId; + const name = event.wbsName; return ( <> setIsSummaryModalOpen(false)} designReview={event} /> setIsSummaryModalOpen(true)} sx={{ cursor: 'pointer' }}> - {getTeamTypeIcon(event.teamType.teamTypeId)} - - {name + ' ' + meetingStartTimePipe(event.meetingTimes)} - + {getTeamTypeIcon(event.teamType.name)} + 0 ? ' - ' + meetingStartTimePipe(event.meetingTimes) : '')} + > + + {name + (event.meetingTimes.length > 0 ? ' ' + meetingStartTimePipe(event.meetingTimes) : '')} + + @@ -60,13 +65,79 @@ const CalendarDayCard: React.FC = ({ cardDate, events }) = ); }; - const ExtraEventsCard = (extraEvents: number) => { + const ExtraEventNote = ({ event }: { event: DesignReview }) => { + const [isSummaryModalOpen, setIsSummaryModalOpen] = useState(false); + + return ( + <> + setIsSummaryModalOpen(false)} designReview={event} /> + { + setIsSummaryModalOpen(true); + }} + > + {event.wbsName + (event.meetingTimes.length > 0 ? ' - ' + meetingStartTimePipe(event.meetingTimes) : '')} + + + ); + }; + + const ExtraEventsCard = ({ extraEvents }: { extraEvents: DesignReview[] }) => { + const [showTooltip, setShowTooltip] = useState(false); return ( - - - {'+' + extraEvents} - + + setShowTooltip(!showTooltip)} + placement="right" + sx={{ cursor: 'pointer' }} + PopperProps={{ + popperOptions: { + modifiers: [ + { + name: 'flip', + options: { + fallbackPlacements: ['top', 'bottom'], + padding: -1, + rootBoundary: 'document' + } + }, + { + name: 'offset', + options: { + offset: [0, -1] + } + } + ] + } + }} + arrow + title={ + + {extraEvents.map((event) => ( + + ))} + + } + > + + {'+' + extraEvents.length} + + ); @@ -77,11 +148,11 @@ const CalendarDayCard: React.FC = ({ cardDate, events }) = {events.length < 3 ? ( - events.map((event) => EventCard(event)) + events.map((event) => ) ) : ( <> - {EventCard(events[1])} - {ExtraEventsCard(events.length - 1)} + + )} diff --git a/src/frontend/src/pages/CalendarPage/CalendarPage.tsx b/src/frontend/src/pages/CalendarPage/CalendarPage.tsx index 31d0e36ec1..5384034f3d 100644 --- a/src/frontend/src/pages/CalendarPage/CalendarPage.tsx +++ b/src/frontend/src/pages/CalendarPage/CalendarPage.tsx @@ -7,29 +7,43 @@ import { Box, Grid, Stack, Typography, useTheme } from '@mui/material'; import PageLayout from '../../components/PageLayout'; import { DesignReview } from 'shared'; import MonthSelector from './CalendarComponents/MonthSelector'; -import CalendarDayCard from './CalendarComponents/CalendarDayCard'; +import CalendarDayCard, { getTeamTypeIcon } from './CalendarComponents/CalendarDayCard'; import FillerCalendarDayCard from './CalendarComponents/FillerCalendarDayCard'; -import { - DAY_NAMES, - EnumToArray, - calendarPaddingDays, - daysInMonth, - exampleDesignReview1, - testDesignReview1 -} from '../../utils/design-review.utils'; +import { DAY_NAMES, EnumToArray, calendarPaddingDays, daysInMonth, isConfirmed } from '../../utils/design-review.utils'; import ActionsMenu from '../../components/ActionsMenu'; +import { useAllDesignReviews } from '../../hooks/design-reviews.hooks'; +import LoadingIndicator from '../../components/LoadingIndicator'; +import ErrorPage from '../ErrorPage'; +import { useCurrentUser } from '../../hooks/users.hooks'; +import { datePipe } from '../../utils/pipes'; const CalendarPage = () => { const theme = useTheme(); - const [displayMonthYear, setDisplayMonthYear] = useState(new Date()); + const { isLoading, isError, error, data: allDesignReviews } = useAllDesignReviews(); + const user = useCurrentUser(); + + if (isLoading || !allDesignReviews) return ; + if (isError) return ; + + const confirmedDesignReviews = allDesignReviews.filter(isConfirmed); - const EventDict = new Map(); - // TODO remove during wire up ticket - EventDict.set(new Date().getDate(), [exampleDesignReview1]); - EventDict.set(new Date().getDate() + 3, [testDesignReview1, testDesignReview1]); - EventDict.set(new Date().getDate() + 4, [testDesignReview1, testDesignReview1, testDesignReview1]); - const designReviewData: DesignReview[] = [testDesignReview1, testDesignReview1]; + const eventDict = new Map(); + confirmedDesignReviews.forEach((designReview) => { + // Accessing the date actually converts it to local time, which causes the date to be off. This is a workaround. + const date = datePipe( + new Date(designReview.dateScheduled.getTime() - designReview.dateScheduled.getTimezoneOffset() * -60000) + ); + if (eventDict.has(date)) { + eventDict.get(date)?.push(designReview); + } else { + eventDict.set(date, [designReview]); + } + }); + + const unconfirmedDesignReviews = allDesignReviews.filter( + (designReview) => designReview.userCreated.userId === user.userId && !isConfirmed(designReview) + ); const startOfEachWeek = [0, 7, 14, 21, 28, 35]; @@ -40,7 +54,8 @@ const CalendarPage = () => { const designReviewButtons = (designReviews: DesignReview[]) => { return designReviews.map((designReview) => { return { - title: designReview.designReviewId, + icon: getTeamTypeIcon(designReview.teamType.name), + title: designReview.wbsName, onClick: () => {}, disabled: false }; @@ -62,7 +77,7 @@ const CalendarPage = () => { .concat(paddingArrayEnd.length < 7 ? paddingArrayEnd : []); const unconfirmedDRSDropdown = ( - + My Unconfirmed DRs ); @@ -98,7 +113,13 @@ const CalendarPage = () => { {isDayInDifferentMonth(day, week) ? ( ) : ( - + )} diff --git a/src/frontend/src/utils/design-review.utils.ts b/src/frontend/src/utils/design-review.utils.ts index 3892348d85..7868668b70 100644 --- a/src/frontend/src/utils/design-review.utils.ts +++ b/src/frontend/src/utils/design-review.utils.ts @@ -80,53 +80,6 @@ export const calendarPaddingDays = (month: Date): number => { return new Date(month.getFullYear(), month.getMonth(), 0).getDay(); }; -// TODO remove during wire up ticket -export const testDesignReview1: DesignReview = { - designReviewId: 'Meeting', - dateScheduled: new Date(), - meetingTimes: [16], - dateCreated: new Date(), - userCreated: batman, - status: DesignReviewStatus.UNCONFIRMED, - teamType: { teamTypeId: 'Mechanical', name: 'Mechanical', iconName: '' }, - requiredMembers: [], - optionalMembers: [], - confirmedMembers: [], - deniedMembers: [], - isOnline: false, - isInPerson: false, - attendees: [], - wbsName: 'bruh', - wbsNum: { carNumber: 1, workPackageNumber: 1, projectNumber: 1 } -}; - -// TODO remove during wire up ticket -export const exampleDesignReview1: DesignReview = { - designReviewId: 'Wiring', - dateScheduled: new Date(), - meetingTimes: [1, 2, 5], - dateCreated: new Date(), - userCreated: superman, - status: DesignReviewStatus.DONE, - teamType: { - teamTypeId: 'Electrical', - name: 'thisteam', - iconName: '' - }, - requiredMembers: [batman, superman, greenlantern, flash, aquaman], - optionalMembers: [wonderwoman, alfred], - confirmedMembers: [], - deniedMembers: [], - location: 'Room 101', - isOnline: true, - isInPerson: false, - zoomLink: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', - attendees: [], - wbsName: 'Battery', - wbsNum: { carNumber: 1, projectNumber: 1, workPackageNumber: 1 }, - docTemplateLink: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ' -}; - // TODO: We will have to make a call to the backend to get this data export const usersToAvailabilities = new Map([ [superman, [1, 2, 3, 4, 5, 6, 7]], @@ -145,3 +98,11 @@ existingMeetingData.set(5, 'warning'); existingMeetingData.set(10, 'build'); existingMeetingData.set(20, 'computer'); existingMeetingData.set(50, 'electrical'); + +export const isConfirmed = (designReview: DesignReview): boolean => { + return ( + designReview.status === DesignReviewStatus.CONFIRMED || + designReview.status === DesignReviewStatus.SCHEDULED || + designReview.status === DesignReviewStatus.DONE + ); +}; diff --git a/src/frontend/src/utils/pipes.ts b/src/frontend/src/utils/pipes.ts index 557c29a006..c7ab4e3bd4 100644 --- a/src/frontend/src/utils/pipes.ts +++ b/src/frontend/src/utils/pipes.ts @@ -4,7 +4,6 @@ */ import { WbsNumber, User, wbsPipe, WbsElement, isProject, WorkPackage, ClubAccount, ExpenseType } from 'shared'; -import { NOON_IN_MINUTES } from './design-review.utils'; /** * Pipes: @@ -165,10 +164,9 @@ export const displayEnum = (enumString: string) => { }; export const meetingStartTimePipe = (times: number[]) => { - const time = times[0] * 15 + 9 * 60; - const minutes = time % 60; - const hours = ((time - minutes) / 60) % 12 === 0 ? 12 : ((time - minutes) / 60) % 12; - return hours + (minutes !== 0 ? ':' + minutes : '') + (time >= NOON_IN_MINUTES ? 'pm' : 'am'); + const time = (times[0] % 12) + 10; + + return time <= 12 ? time + 'am' : time - 12 + 'pm'; }; // takes in a Date and returns it as a string in the form mm/dd/yy