Skip to content

Commit ce39c3f

Browse files
committed
issue-198: added custom date picker in date select views
1 parent c43921e commit ce39c3f

File tree

5 files changed

+97
-79
lines changed

5 files changed

+97
-79
lines changed

client/src/components/EventCard.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import MeetingRoomRoundedIcon from '@mui/icons-material/MeetingRoomRounded';
66
import StairsIcon from '@mui/icons-material/Stairs';
77
import AccessTimeFilledRoundedIcon from '@mui/icons-material/AccessTimeFilledRounded';
88
import EventSeatRoundedIcon from '@mui/icons-material/EventSeatRounded';
9-
import { convertToLocaleTime } from '@helpers/utility';
9+
import { convertToLocaleTime, isPastDate } from '@helpers/utility';
1010
import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
1111
import LocationOnRoundedIcon from '@mui/icons-material/LocationOnRounded';
1212
import EmailRoundedIcon from '@mui/icons-material/EmailRounded';
@@ -227,7 +227,7 @@ const EventCard = ({ sx, event, onDelete, handleEditClick, isEditable, handleEve
227227
const menuItems: JSX.Element[] = [];
228228
if (isEditable) {
229229
menuItems.push(
230-
<MenuItem key="edit" onClick={onEditClick}>
230+
<MenuItem key="edit" onClick={onEditClick} disabled={isPastDate(event.start) ? true : false}>
231231
Edit
232232
</MenuItem>,
233233
);

client/src/helpers/utility.ts

+12
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import Api from '@/api/api';
22
import { ROUTES } from '@config/routes';
33
import { secrets } from '@config/secrets';
44
import { ApiResponse } from '@quickmeet/shared';
5+
import dayjs, { Dayjs } from 'dayjs';
56
import { toast } from 'react-hot-toast';
67
import { NavigateFunction } from 'react-router-dom';
78

@@ -192,3 +193,14 @@ export const chromeBackground = {
192193
const params = new URLSearchParams(window.location.search);
193194

194195
export const isChromeExt = Boolean(params.get('chrome')) || secrets.appEnvironment === 'chrome';
196+
197+
export const isPastDate = (date: string | Dayjs | undefined): boolean => {
198+
if (!date || !dayjs(date).isValid()) {
199+
return false;
200+
}
201+
202+
const selectedDate = dayjs(date).startOf('day');
203+
const today = dayjs().startOf('day');
204+
205+
return selectedDate.isBefore(today);
206+
};

client/src/pages/Home/BookRoomView/index.tsx

+40-38
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@ import HourglassBottomRoundedIcon from '@mui/icons-material/HourglassBottomRound
2222
import MeetingRoomRoundedIcon from '@mui/icons-material/MeetingRoomRounded';
2323
import TitleIcon from '@mui/icons-material/Title';
2424
import LoadingButton from '@mui/lab/LoadingButton';
25-
import { Box, Checkbox, Typography } from '@mui/material';
26-
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
25+
import { Box, Checkbox, IconButton, Typography } from '@mui/material';
26+
import CalendarMonthIcon from '@mui/icons-material/CalendarMonth';
2727
import dayjs from 'dayjs';
2828
import 'dayjs/locale/en-gb';
2929
import { useEffect, useRef, useState } from 'react';
3030
import toast from 'react-hot-toast';
3131
import { useNavigate } from 'react-router-dom';
32+
import DatePickerPopper from '@/pages/Home/MyEventsView/DatePickerPopper';
3233

3334
const createRoomDropdownOptions = (rooms: IConferenceRoom[]) => {
3435
return (rooms || []).map((room) => ({ value: room.email, text: room.name, seats: room.seats, floor: room.floor }) as RoomsDropdownOption);
@@ -64,6 +65,8 @@ export default function BookRoomView({ onRoomBooked }: BookRoomViewProps) {
6465
});
6566

6667
const [date, setDate] = useState(dayjs());
68+
const [datePickerOpen, setDatePickerOpen] = useState(false);
69+
const [datePickerAnchorEl, setDatePickerAnchorEl] = useState<null | HTMLElement>(null);
6770

6871
// Utilities and hooks
6972
const navigate = useNavigate();
@@ -199,6 +202,11 @@ export default function BookRoomView({ onRoomBooked }: BookRoomViewProps) {
199202
});
200203
}
201204

205+
const handleDatePopperClick = (event: React.MouseEvent<HTMLElement>) => {
206+
setDatePickerAnchorEl(event.currentTarget);
207+
setDatePickerOpen(true);
208+
};
209+
202210
async function onBookClick() {
203211
setBookClickLoading(true);
204212
const { startTime, duration, seats, conference, attendees, title, room } = formData;
@@ -283,42 +291,28 @@ export default function BookRoomView({ onRoomBooked }: BookRoomViewProps) {
283291
/>
284292
</Box>
285293
<Box sx={{ flex: 1, display: 'flex' }}>
286-
<DatePicker
287-
disablePast
288-
defaultValue={date}
289-
onChange={(newDate) => {
290-
if (newDate) {
291-
setDate(newDate);
292-
}
293-
}}
294-
slotProps={{
295-
inputAdornment: {
296-
position: 'start',
297-
sx: {
298-
input: {
299-
cursor: 'pointer',
300-
},
301-
},
302-
},
303-
}}
304-
sx={{
305-
'.MuiOutlinedInput-root': {
306-
'& fieldset': {
307-
border: 'none',
308-
},
309-
},
310-
'.MuiInputBase-input': {
311-
color: (theme) => theme.palette.common.black,
312-
fontFamily: 'inherit',
313-
fontSize: '1.125rem',
314-
fontWeight: 400,
315-
},
316-
'.MuiSvgIcon-root': {
317-
color: (theme) => theme.palette.grey[50],
318-
},
319-
'.MuiButtonBase-root': { cursor: 'pointer' },
320-
}}
321-
/>
294+
<Box sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
295+
<IconButton aria-label="calender" size="small" onClick={handleDatePopperClick}>
296+
<CalendarMonthIcon
297+
sx={[
298+
(theme) => ({
299+
color: theme.palette.grey[50],
300+
}),
301+
]}
302+
fontSize="medium"
303+
/>
304+
</IconButton>
305+
<Typography
306+
sx={{
307+
cursor: 'pointer',
308+
ml: 2,
309+
}}
310+
variant="subtitle1"
311+
onClick={handleDatePopperClick}
312+
>
313+
{date.format('DD/MM/YYYY')}
314+
</Typography>
315+
</Box>
322316
</Box>
323317
</Box>
324318
<Box>
@@ -456,6 +450,14 @@ export default function BookRoomView({ onRoomBooked }: BookRoomViewProps) {
456450
</Typography>
457451
</LoadingButton>
458452
</Box>
453+
<DatePickerPopper
454+
disablePast={true}
455+
currentDate={date}
456+
setCurrentDate={setDate}
457+
open={datePickerOpen}
458+
setOpen={setDatePickerOpen}
459+
anchorEl={datePickerAnchorEl}
460+
/>
459461
</Box>
460462
);
461463
}

client/src/pages/Home/MyEventsView/DatePickerPopper.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Box, Chip, ClickAwayListener, Divider, Fade, GlobalStyles, List, ListItem, Popper } from '@mui/material';
2-
import { StaticDatePicker, PickersShortcutsItem, PickersShortcutsProps, type PickerChangeHandlerContext, type DateValidationError } from '@mui/x-date-pickers';
2+
import { StaticDatePicker, PickersShortcutsItem, PickersShortcutsProps, PickerChangeHandlerContext, DateValidationError } from '@mui/x-date-pickers';
33
import dayjs from 'dayjs';
44
import { useEffect, useRef } from 'react';
55

@@ -67,14 +67,15 @@ function DatePickerShortcuts(props: PickersShortcutsProps<dayjs.Dayjs | null>) {
6767
}
6868

6969
interface DatePickerPopperProps {
70+
disablePast?: boolean;
7071
currentDate: dayjs.Dayjs;
7172
setCurrentDate: React.Dispatch<React.SetStateAction<dayjs.Dayjs>>;
7273
open: boolean;
7374
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
7475
anchorEl: HTMLElement | null;
7576
}
7677

77-
const DatePickerPopper = ({ open, setOpen, currentDate, setCurrentDate, anchorEl }: DatePickerPopperProps) => {
78+
const DatePickerPopper = ({ open, setOpen, currentDate, setCurrentDate, anchorEl, disablePast }: DatePickerPopperProps) => {
7879
const hasMounted = useRef(false);
7980

8081
useEffect(() => {
@@ -119,6 +120,7 @@ const DatePickerPopper = ({ open, setOpen, currentDate, setCurrentDate, anchorEl
119120
/>
120121
{/* https://mui.com/x/react-date-pickers/date-picker/#customization */}
121122
<StaticDatePicker
123+
disablePast={disablePast}
122124
onChange={onDateChange}
123125
value={currentDate}
124126
slots={{

client/src/pages/Home/MyEventsView/EditEventsView.tsx

+39-37
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,13 @@ import TitleIcon from '@mui/icons-material/Title';
2626
import { FormData, IAvailableRoomsDropdownOption } from '@helpers/types';
2727
import { LoadingButton } from '@mui/lab';
2828
import { AppBar, Box, Button, Checkbox, IconButton, Skeleton, Stack, Typography } from '@mui/material';
29-
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
29+
import CalendarMonthIcon from '@mui/icons-material/CalendarMonth';
3030
import dayjs from 'dayjs';
3131
import 'dayjs/locale/en-gb';
3232
import { useEffect, useRef, useState } from 'react';
3333
import { EventResponse, IConferenceRoom, IAvailableRooms, IPeopleInformation } from '@quickmeet/shared';
3434
import { useNavigate } from 'react-router-dom';
35+
import DatePickerPopper from '@/pages/Home/MyEventsView/DatePickerPopper';
3536

3637
const createRoomDropdownOptions = (rooms: IConferenceRoom[]) => {
3738
return (rooms || []).map((room) => ({ value: room.email, text: room.name, seats: room.seats, floor: room.floor }) as RoomsDropdownOption);
@@ -85,6 +86,8 @@ export default function EditEventsView({ open, event, handleClose, currentRoom,
8586
const [formData, setFormData] = useState<FormData>(initFormData(event));
8687

8788
const [date, setDate] = useState(dayjs(event.start!));
89+
const [datePickerOpen, setDatePickerOpen] = useState(false);
90+
const [datePickerAnchorEl, setDatePickerAnchorEl] = useState<null | HTMLElement>(null);
8891

8992
// Utilities and hooks
9093
const api = useApi();
@@ -186,6 +189,11 @@ export default function EditEventsView({ open, event, handleClose, currentRoom,
186189
setAvailableRoomOptions({ others: unPreferredRoomOptions, preferred: preferredRoomOptions });
187190
}
188191

192+
const handleDatePopperClick = (event: React.MouseEvent<HTMLElement>) => {
193+
setDatePickerAnchorEl(event.currentTarget);
194+
setDatePickerOpen(true);
195+
};
196+
189197
async function setPreferences() {
190198
const eventTime = new Date(event.start!);
191199
const currentTime = new Date(new Date().toUTCString());
@@ -315,42 +323,28 @@ export default function EditEventsView({ open, event, handleClose, currentRoom,
315323
/>
316324
</Box>
317325
<Box sx={{ flex: 1, display: 'flex' }}>
318-
<DatePicker
319-
disablePast
320-
defaultValue={dayjs(event.start!)}
321-
onChange={(newDate) => {
322-
if (newDate) {
323-
setDate(newDate);
324-
}
325-
}}
326-
slotProps={{
327-
inputAdornment: {
328-
position: 'start',
329-
sx: {
330-
input: {
331-
cursor: 'pointer',
332-
},
333-
},
334-
},
335-
}}
336-
sx={{
337-
'.MuiOutlinedInput-root': {
338-
'& fieldset': {
339-
border: 'none',
340-
},
341-
},
342-
'.MuiInputBase-input': {
343-
color: (theme) => theme.palette.common.black,
344-
fontFamily: 'inherit',
345-
fontSize: '1.125rem',
346-
fontWeight: 400,
347-
},
348-
'.MuiSvgIcon-root': {
349-
color: (theme) => theme.palette.grey[50],
350-
},
351-
'.MuiButtonBase-root': { cursor: 'pointer' },
352-
}}
353-
/>
326+
<Box sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
327+
<IconButton aria-label="calender" size="small" onClick={handleDatePopperClick}>
328+
<CalendarMonthIcon
329+
sx={[
330+
(theme) => ({
331+
color: theme.palette.grey[50],
332+
}),
333+
]}
334+
fontSize="medium"
335+
/>
336+
</IconButton>
337+
<Typography
338+
sx={{
339+
cursor: 'pointer',
340+
ml: 2,
341+
}}
342+
variant="subtitle1"
343+
onClick={handleDatePopperClick}
344+
>
345+
{date.format('DD/MM/YYYY')}
346+
</Typography>
347+
</Box>
354348
</Box>
355349
</Box>
356350

@@ -509,6 +503,14 @@ export default function EditEventsView({ open, event, handleClose, currentRoom,
509503
</Typography>
510504
</Button>
511505
</Box>
506+
<DatePickerPopper
507+
disablePast={true}
508+
currentDate={date}
509+
setCurrentDate={setDate}
510+
open={datePickerOpen}
511+
setOpen={setDatePickerOpen}
512+
anchorEl={datePickerAnchorEl}
513+
/>
512514
</Box>
513515
);
514516
}

0 commit comments

Comments
 (0)