Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions src/backend/src/services/calendar.services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2231,21 +2231,22 @@ export default class CalendarService {
}

// filters for selected calendars
const fromCalendar = calendarIds?.length
? {
eventType: {
is: {
organizationId: organization.organizationId,
calendars: {
some: {
calendarId: { in: calendarIds },
organizationId: organization.organizationId
const fromCalendar =
calendarIds !== undefined
? {
eventType: {
is: {
organizationId: organization.organizationId,
calendars: {
some: {
calendarId: { in: calendarIds },
organizationId: organization.organizationId
}
}
}
}
}
}
: undefined;
: undefined;

// get event using filter args
const events = await prisma.event.findMany({
Expand Down
205 changes: 155 additions & 50 deletions src/frontend/src/pages/NewCalendarPage/NewCalendarPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,19 @@
* See the LICENSE file in the repository root folder for details.
*/
import { useEffect, useMemo, useState } from 'react';
import { Box, Grid, Stack, Typography, useMediaQuery, useTheme, Button, Alert } from '@mui/material';
import {
Box,
Grid,
Stack,
Typography,
useMediaQuery,
useTheme,
Button,
Alert,
Checkbox,
FormControlLabel,
FormGroup
} from '@mui/material';
import PageLayout from '../../components/PageLayout';
import { Calendar, ConflictStatus, DayOfWeek, Event, EventType } from 'shared';
import CalendarDayCard from './CalendarDayCard';
Expand All @@ -27,6 +39,9 @@ import { filterEventTransformer } from '../../apis/transformers/calendar.transfo
import WarningIcon from '@mui/icons-material/Warning';
import { useHistory } from 'react-router-dom';
import UpcomingMeetingsCard from './UpcomingMeetingsCard';
import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
import RadioButtonUncheckedIcon from '@mui/icons-material/RadioButtonUnchecked';
import SchedulingConflictsWarning from './SchedulingConflictsWarning';

interface NewCalendarPageProps {
allEventTypes: EventType[];
Expand Down Expand Up @@ -60,6 +75,10 @@ const NewCalendarPage: React.FC<NewCalendarPageProps> = ({ allEventTypes, yourEv
const isLargerView = useMediaQuery(theme.breakpoints.up('md'));
const isExtraSmallView = useMediaQuery(theme.breakpoints.down('sm'));

const calendars = allCalendars ?? [];

const [selectedCalendarIds, setSelectedCalendarIds] = useState<string[]>(allCalendars.map((c) => c.calendarId));

const { data: allTeams, isLoading: allTeamsLoading, isError: allTeamsIsError, error: allTeamsError } = useGetUsersTeams();

const teamList = useMemo(() => allTeams?.map((team) => team.teamId) ?? [], [allTeams]);
Expand All @@ -70,17 +89,21 @@ const NewCalendarPage: React.FC<NewCalendarPageProps> = ({ allEventTypes, yourEv
}
}, [allTeams, teamList, additionalTeamIds.length, showTeamEvents]);

const startPeriod = new Date(displayMonthYear.getFullYear(), displayMonthYear.getMonth() - 1, 15);
const endPeriod = new Date(displayMonthYear.getFullYear(), displayMonthYear.getMonth() + 1, 15);

const {
isLoading,
isError,
error,
data: allEvents
} = useFilterEvents({
startPeriod: new Date(displayMonthYear.getFullYear(), displayMonthYear.getMonth() - 1, 15),
endPeriod: new Date(displayMonthYear.getFullYear(), displayMonthYear.getMonth() + 1, 15),
startPeriod,
endPeriod,
memberIds: memberIds.concat(additionalMemberIds),
teamIds: teamIds.concat(additionalTeamIds),
statuses: [ConflictStatus.APPROVED, ConflictStatus.NO_CONFLICT]
statuses: [ConflictStatus.APPROVED, ConflictStatus.NO_CONFLICT],
calendarIds: selectedCalendarIds
});

const history = useHistory();
Expand Down Expand Up @@ -132,23 +155,31 @@ const NewCalendarPage: React.FC<NewCalendarPageProps> = ({ allEventTypes, yourEv
const { mutateAsync: createEvent } = useCreateEvent();
const { isLoading: documentsIsLoading, mutateAsync: uploadDocuments } = useUploadManyDocuments();

const [startPeriod] = useState(() => new Date());
const [upcomingStartPeriod] = useState(() => new Date());

const [endPeriod] = useState(() => {
const [upcomingEndPeriod] = useState(() => {
const d = new Date();
d.setDate(d.getDate() + 7);
d.setHours(23, 59, 59, 999);
return d;
});

const { data: upcomingEvents } = useFilterEvents({
startPeriod,
endPeriod,
startPeriod: upcomingStartPeriod,
endPeriod: upcomingEndPeriod,
memberIds: memberIds.concat(additionalMemberIds),
teamIds: teamIds.concat(additionalTeamIds)
});

const upcomingOccurences = upcomingEvents ? getEventsFlattened(upcomingEvents, startPeriod, endPeriod) : [];
const upcomingOccurences = upcomingEvents
? getEventsFlattened(upcomingEvents, upcomingStartPeriod, upcomingEndPeriod)
: [];

const toggleCalendar = (calendarId: string) => {
setSelectedCalendarIds((prev) =>
prev.includes(calendarId) ? prev.filter((id) => id !== calendarId) : [...prev, calendarId]
);
};

const updateAdditionalTeamIds = (changed: boolean) => {
setShowTeamEvents(changed);
Expand Down Expand Up @@ -520,7 +551,7 @@ const NewCalendarPage: React.FC<NewCalendarPageProps> = ({ allEventTypes, yourEv
width: 320,
display: 'flex',
flexDirection: 'column',
minHeight: 0
gap: 2
}}
>
<DateCalendar
Expand All @@ -545,49 +576,123 @@ const NewCalendarPage: React.FC<NewCalendarPageProps> = ({ allEventTypes, yourEv
}
}}
/>
<Button
variant="outlined"
id="filter-events-button"
onClick={() => setOpenFilterModal(true)}
sx={{
color: 'white',
borderColor: 'white',
backgroundColor: 'transparent',
'&:hover': {
borderColor: 'white',
backgroundColor: 'rgba(255, 255, 255, 0.1)'
}
}}
>
More Filters
</Button>
<SchedulingConflictsWarning
memberIds={memberIds.concat(additionalMemberIds)}
teamIds={teamIds.concat(additionalTeamIds)}
startPeriod={startPeriod}
endPeriod={endPeriod}
/>
<Box sx={{ width: 320, display: 'flex', flexDirection: 'column', gap: 2 }}>
<Typography align="left" sx={{ fontWeight: 'bold', fontSize: 22, mb: 0.5 }}>
My Upcoming Meetings:
</Typography>

<Typography align="left" sx={{ fontWeight: 'bold', fontSize: 22, mb: 0.5 }}>
My Upcoming Meetings:
</Typography>
{upcomingOccurences && (
<Box
sx={{
mt: 2,
flex: 1,
flexDirection: 'column',
overflowX: 'hidden',
overflowY: 'auto',
scrollbarColor: `${theme.palette.primary.main} transparent`,
maxHeight: 'calc(50%)'
}}
>
{upcomingOccurences?.map((event) => (
<UpcomingMeetingsCard
key={event.eventId}
event={event}
calendars={allCalendars ?? []}
eventTypes={allEventTypes ?? []}
/>
))}
</Box>
)}
{/* Calendar Selector */}
<Box sx={{ p: 2, borderRadius: 2 }}>
<Stack direction="row" spacing={1} alignItems="center" justifyContent="space-between" sx={{ mb: 1 }}>
<Typography
variant="h6"
sx={{
fontFamily: (t) => t.typography.h4.fontFamily,
fontWeight: 400,
fontSize: 22
}}
>
Calendars:
</Typography>

{upcomingOccurences && (
<Box
sx={{
mt: 2,
flex: 1,
flexDirection: 'column',
overflowX: 'hidden',
overflowY: 'auto',
scrollbarColor: `${theme.palette.primary.main} transparent`,
maxHeight: 'calc(50%)'
}}
>
{upcomingOccurences?.map((event) => (
<UpcomingMeetingsCard
key={event.eventId}
event={event}
calendars={allCalendars ?? []}
eventTypes={allEventTypes ?? []}
/>
))}
<Button
size="small"
variant="outlined"
id="filter-events-button"
onClick={() => setOpenFilterModal(true)}
sx={{
px: 1,
py: 0,
color: 'white',
borderColor: 'white',
backgroundColor: 'transparent',
textTransform: 'none',
fontSize: 14,
fontFamily: (t) => t.typography.h4.fontFamily,
'&:hover': {
borderColor: 'white',
backgroundColor: 'rgba(255, 255, 255, 0.1)'
},
mb: 2
}}
>
More Filters
</Button>
</Stack>

{calendars.length > 0 && (
<FormGroup>
{calendars.map((cal) => {
const { calendarId, color } = cal;
const checked = selectedCalendarIds.includes(calendarId);
return (
<FormControlLabel
key={calendarId}
sx={{
ml: -1,
mb: 1.5
}}
control={
<Checkbox
checked={checked}
onChange={() => toggleCalendar(calendarId)}
icon={<RadioButtonUncheckedIcon />}
checkedIcon={<CheckCircleOutlineIcon />}
sx={{
p: 0.5,
color,
'&.Mui-checked': { color }
}}
/>
}
label={
<Typography
variant="body2"
sx={{
fontFamily: (t) => t.typography.h6.fontFamily,
fontSize: 16,
color,
fontWeight: 500
}}
>
{cal.name}
</Typography>
}
/>
);
})}
</FormGroup>
)}
</Box>
)}
</Box>
</Box>
</Box>

Expand Down