Skip to content

Commit a7b25f2

Browse files
committed
show more events at once, proper error handling
1 parent 8c0ebcb commit a7b25f2

4 files changed

Lines changed: 93 additions & 35 deletions

File tree

src/backend/src/services/calendar.services.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@ export default class CalendarService {
453453
}))
454454
},
455455
initialDateScheduled,
456-
status: foundEventType.requiresConfirmation ? Event_Status.UNCONFIRMED : Event_Status.CONFIRMED,
456+
status: foundEventType.requiresConfirmation ? Event_Status.UNCONFIRMED : Event_Status.SCHEDULED,
457457
approved: hasConflict ? Conflict_Status.PENDING : Conflict_Status.NO_CONFLICT,
458458
approvalRequiredFromUserId: hasConflict ? conflictingEvent?.userCreated.userId : null,
459459
location,
@@ -523,9 +523,7 @@ export default class CalendarService {
523523
projects.map((project) => project.wbsElement.name).join(', ')
524524
);
525525
} catch (err: unknown) {
526-
if (err instanceof Error) {
527-
throw new HttpException(500, `Failed to send slack notification: ${err.message}`);
528-
}
526+
console.error('Failed to send slack notification for event:', err);
529527
}
530528
}
531529
}
@@ -537,13 +535,17 @@ export default class CalendarService {
537535
for (const project of projects) {
538536
const projectTeams = project.teams;
539537
if (projectTeams.length > 0) {
540-
await sendSlackEventNotifications(
541-
projectTeams,
542-
createdEvent,
543-
submitter,
544-
workPackageNames,
545-
project.wbsElement.name
546-
);
538+
try {
539+
await sendSlackEventNotifications(
540+
projectTeams,
541+
createdEvent,
542+
submitter,
543+
workPackageNames,
544+
project.wbsElement.name
545+
);
546+
} catch (err: unknown) {
547+
console.error('Failed to send slack notification for event:', err);
548+
}
547549
}
548550
}
549551
}

src/frontend/src/apis/transformers/calendar.transformer.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ export const shopTransformer = (shop: Shop): Shop => {
1010
};
1111

1212
export const filterEventTransformer = (event: Event): Event => {
13+
// Guard against error responses being passed through transformResponse
14+
if (!event || !event.scheduledTimes) {
15+
return event;
16+
}
1317
return {
1418
...event,
1519
dateCreated: new Date(event.dateCreated),
@@ -22,6 +26,10 @@ export const filterEventTransformer = (event: Event): Event => {
2226
};
2327

2428
export const eventTransformer = (event: Event): Event => {
29+
// Guard against error responses being passed through transformResponse
30+
if (!event || !event.scheduledTimes) {
31+
return event;
32+
}
2533
return {
2634
...event,
2735
dateCreated: new Date(event.dateCreated),
@@ -35,6 +43,10 @@ export const eventTransformer = (event: Event): Event => {
3543
};
3644

3745
export const eventWithMembersTransformer = (event: EventWithMembers): EventWithMembers => {
46+
// Guard against error responses being passed through transformResponse
47+
if (!event || !event.scheduledTimes) {
48+
return event;
49+
}
3850
return {
3951
...event,
4052
dateCreated: new Date(event.dateCreated),

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

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState } from 'react';
1+
import { useState, useRef, useEffect } from 'react';
22
import { Box, Card, CardContent, Grid, Stack, Tooltip, Typography, useTheme } from '@mui/material';
33
import { Calendar, ConflictStatus, DayOfWeek, EventInstance, EventStatus, EventType } from 'shared';
44
import ConstructionIcon from '@mui/icons-material/Construction';
@@ -48,6 +48,11 @@ interface CalendarDayCardProps {
4848
onCreateEventClick: (date: Date) => void;
4949
}
5050

51+
// Constants for dynamic event display calculation
52+
const TITLE_HEIGHT = 28; // Height of the day number title
53+
const EVENT_CARD_HEIGHT = 24; // Height of each event card
54+
const EVENT_MARGIN = 4; // Margin between events
55+
5156
const CalendarDayCard: React.FC<CalendarDayCardProps> = ({
5257
cardDate,
5358
displayMonth,
@@ -78,6 +83,28 @@ const CalendarDayCard: React.FC<CalendarDayCardProps> = ({
7883
const [selectedEvent, setSelectedEvent] = useState<EventInstance | null>(null);
7984
const toast = useToast();
8085

86+
// Ref and state for dynamic event count calculation
87+
const containerRef = useRef<HTMLDivElement>(null);
88+
const [maxVisibleEvents, setMaxVisibleEvents] = useState(2);
89+
90+
// Calculate how many events can fit based on container height
91+
useEffect(() => {
92+
const calculateMaxEvents = () => {
93+
if (containerRef.current) {
94+
const containerHeight = containerRef.current.clientHeight;
95+
const availableHeight = containerHeight - TITLE_HEIGHT;
96+
const eventSlotHeight = EVENT_CARD_HEIGHT + EVENT_MARGIN;
97+
// Reserve space for the "+N more" card if there are extra events
98+
const maxEvents = Math.max(1, Math.floor(availableHeight / eventSlotHeight));
99+
setMaxVisibleEvents(maxEvents);
100+
}
101+
};
102+
103+
calculateMaxEvents();
104+
window.addEventListener('resize', calculateMaxEvents);
105+
return () => window.removeEventListener('resize', calculateMaxEvents);
106+
}, []);
107+
81108
const { mutateAsync: deleteEvent } = useDeleteEvent(selectedEvent?.eventId ?? '');
82109
const { mutateAsync: deleteScheduleSlot } = useDeleteScheduleSlot(
83110
selectedEvent?.eventId ?? '',
@@ -169,7 +196,7 @@ const CalendarDayCard: React.FC<CalendarDayCardProps> = ({
169196
return (
170197
<Box
171198
marginLeft={0.5}
172-
marginBottom={0.5}
199+
marginBottom={0.25}
173200
marginRight={0.5}
174201
onMouseEnter={() => setIsHovered(true)}
175202
onMouseLeave={() => {
@@ -194,8 +221,8 @@ const CalendarDayCard: React.FC<CalendarDayCardProps> = ({
194221
backgroundColor: bgColor,
195222
borderRadius: 1,
196223
width: '100%',
197-
minHeight: 30,
198-
maxHeight: 30,
224+
minHeight: EVENT_CARD_HEIGHT,
225+
maxHeight: EVENT_CARD_HEIGHT,
199226
...(isPending && {
200227
border: `1px dashed ${baseColor}`,
201228
opacity: 0.8
@@ -259,7 +286,7 @@ const CalendarDayCard: React.FC<CalendarDayCardProps> = ({
259286
>
260287
<Typography
261288
marginX={0.5}
262-
marginY={0.6}
289+
marginY={0.3}
263290
lineHeight="120%"
264291
fontSize={14}
265292
fontWeight="bold"
@@ -359,7 +386,7 @@ const CalendarDayCard: React.FC<CalendarDayCardProps> = ({
359386

360387
const ExtraEventsCard = ({ extraEvents }: { extraEvents: EventInstance[] }) => {
361388
return (
362-
<Box marginLeft={0.5} marginRight={0.5} marginBottom={0.2} sx={{ position: 'relative', zIndex: 2 }}>
389+
<Box marginLeft={0.5} marginRight={0.5} marginBottom={0.25} sx={{ position: 'relative', zIndex: 2 }}>
363390
<Tooltip
364391
placement="right"
365392
arrow
@@ -406,8 +433,8 @@ const CalendarDayCard: React.FC<CalendarDayCardProps> = ({
406433
backgroundColor: theme.palette.grey[800],
407434
borderRadius: 1,
408435
width: '100%',
409-
minHeight: 30,
410-
maxHeight: 30,
436+
minHeight: EVENT_CARD_HEIGHT,
437+
maxHeight: EVENT_CARD_HEIGHT,
411438
display: 'flex',
412439
alignItems: 'center',
413440
justifyContent: 'center'
@@ -441,6 +468,7 @@ const CalendarDayCard: React.FC<CalendarDayCardProps> = ({
441468
)}
442469

443470
<Card
471+
ref={containerRef}
444472
onMouseEnter={() => isClickable && setIsHovered(true)}
445473
onMouseLeave={() => setIsHovered(false)}
446474
sx={{
@@ -470,19 +498,22 @@ const CalendarDayCard: React.FC<CalendarDayCardProps> = ({
470498

471499
<CardContent sx={{ padding: 0 }}>
472500
<DayCardTitle />
473-
{events.length === 1 ? (
474-
<EventCard event={events[0]} />
475-
) : events.length === 2 ? (
476-
<>
477-
<EventCard event={events[0]} />
478-
<EventCard event={events[1]} />
479-
</>
480-
) : events.length >= 3 ? (
501+
{events.length > 0 && (
481502
<>
482-
<EventCard event={events[0]} />
483-
<ExtraEventsCard extraEvents={events.slice(1)} />
503+
{events.length <= maxVisibleEvents ? (
504+
// All events fit - show them all
505+
events.map((event) => <EventCard key={event.eventId} event={event} />)
506+
) : (
507+
// Too many events - show as many as possible with "+N more"
508+
<>
509+
{events.slice(0, maxVisibleEvents - 1).map((event) => (
510+
<EventCard key={event.eventId} event={event} />
511+
))}
512+
<ExtraEventsCard extraEvents={events.slice(maxVisibleEvents - 1)} />
513+
</>
514+
)}
484515
</>
485-
) : null}
516+
)}
486517
</CardContent>
487518
</Card>
488519

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

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useState } from 'react';
22
import { Alert, Box, Button, IconButton, Link, Popover, Stack, Typography, useTheme } from '@mui/material';
3-
import { Calendar, DayOfWeek, EventInstance, EventStatus, EventType, isAdmin, isHead } from 'shared';
3+
import { Calendar, DayOfWeek, EventInstance, EventStatus, EventType, isAdmin, isHead, wbsPipe } from 'shared';
44
import { useCurrentUser } from '../../hooks/users.hooks';
55
import { Link as RouterLink } from 'react-router-dom';
66
import { routes } from '../../utils/routes';
@@ -121,8 +121,6 @@ export const EventClickContent: React.FC<EventClickContentProps> = ({
121121
const teamsText = event.teams.length > 0 ? event.teams.map((t) => t.teamName).join(', ') : '';
122122
const machineryText = event.machinery.length > 0 ? event.machinery.map((m) => m.name || 'Machinery').join(', ') : '';
123123
const shopsText = event.shops.length > 0 ? event.shops.map((s) => s.name).join(', ') : '';
124-
const workPackagesText =
125-
event.workPackages.length > 0 ? event.workPackages.map((wp) => wp.wbsElement?.name || 'Work package').join(', ') : '';
126124

127125
const descriptionText = (event.description ?? '').trim();
128126
const locationText = (event.location ?? '').trim();
@@ -331,11 +329,26 @@ export const EventClickContent: React.FC<EventClickContentProps> = ({
331329
)}
332330

333331
{/* Work packages */}
334-
{hasValue(workPackagesText) && (
332+
{event.workPackages.length > 0 && (
335333
<Stack direction="row" spacing={1.25} alignItems="flex-start">
336334
<BusinessCenterIcon fontSize="small" sx={{ mt: 0.3 }} />
337335
<Typography variant="body2" sx={{ flex: 1 }}>
338-
<b>Work packages:</b> {workPackagesText}
336+
<b>Work packages:</b>{' '}
337+
{event.workPackages.map((wp, index) => {
338+
const wbsNum = {
339+
carNumber: wp.wbsElement.carNumber,
340+
projectNumber: wp.wbsElement.projectNumber,
341+
workPackageNumber: wp.wbsElement.workPackageNumber
342+
};
343+
return (
344+
<span key={wp.workPackageId}>
345+
{index > 0 && ', '}
346+
<Link component={RouterLink} to={`${routes.PROJECTS}/${wbsPipe(wbsNum)}`} onClick={stopClick}>
347+
{wp.wbsElement?.name || 'Work package'}
348+
</Link>
349+
</span>
350+
);
351+
})}
339352
</Typography>
340353
</Stack>
341354
)}

0 commit comments

Comments
 (0)