diff --git a/client/src/components/AttendeeInput.tsx b/client/src/components/AttendeeInput.tsx index fec429b..c579a74 100644 --- a/client/src/components/AttendeeInput.tsx +++ b/client/src/components/AttendeeInput.tsx @@ -30,6 +30,27 @@ export default function AttendeeInput({ id, onChange, value, type }: AttendeeInp } }; + const handleSelectionChange = (_: React.SyntheticEvent, newValue: Array) => { + const emails = newValue.map((option) => (typeof option === 'object' && option.email ? option.email : (option as string))); + const filteredEmails = emails + .join(' ') + .split(/\s+/) + .map((email) => email.trim()) + .filter((email) => email !== ''); + }; + + const validateEmails = (emails: string[]) => { + const validEmails = emails.filter(isEmailValid); + const invalidEmails = emails.filter((email) => !isEmailValid(email)); + return { validEmails, invalidEmails }; + }; + + const handleInvalidEmails = (invalidEmails: string[]) => { + invalidEmails.forEach((email: string) => { + toast.error(`${email} is invalid.`); + }); + }; + const handleSelectionChange = (_: React.SyntheticEvent, newValue: Array) => { const emails = newValue.map((option) => (typeof option === 'object' && option.email ? option.email : (option as string))); const filteredEmails = emails diff --git a/client/src/pages/Home/BookRoomView/index.tsx b/client/src/pages/Home/BookRoomView/index.tsx index fbedb01..538cf73 100644 --- a/client/src/pages/Home/BookRoomView/index.tsx +++ b/client/src/pages/Home/BookRoomView/index.tsx @@ -1,8 +1,10 @@ -import { Box, Checkbox, Typography } from '@mui/material'; -import { useEffect, useRef, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; +import AttendeeInput from '@/components/AttendeeInput'; +import StyledTextField from '@/components/StyledTextField'; +import { useApi } from '@/context/ApiContext'; +import { usePreferences } from '@/context/PreferencesContext'; import Dropdown, { DropdownOption } from '@components/Dropdown'; -import LoadingButton from '@mui/lab/LoadingButton'; +import RoomsDropdown, { RoomsDropdownOption } from '@components/RoomsDropdown'; +import { FormData } from '@helpers/types'; import { convertToRFC3339, createDropdownOptions, @@ -13,20 +15,21 @@ import { populateTimeOptions, renderError, } from '@helpers/utility'; -import toast from 'react-hot-toast'; import AccessTimeFilledRoundedIcon from '@mui/icons-material/AccessTimeFilledRounded'; import EventSeatRoundedIcon from '@mui/icons-material/EventSeatRounded'; -import { FormData } from '@helpers/types'; -import { BookRoomDto, EventResponse, IConferenceRoom } from '@quickmeet/shared'; -import MeetingRoomRoundedIcon from '@mui/icons-material/MeetingRoomRounded'; import HourglassBottomRoundedIcon from '@mui/icons-material/HourglassBottomRounded'; -import RoomsDropdown, { RoomsDropdownOption } from '@components/RoomsDropdown'; -import { usePreferences } from '@/context/PreferencesContext'; -import StyledTextField from '@/components/StyledTextField'; +import MeetingRoomRoundedIcon from '@mui/icons-material/MeetingRoomRounded'; import TitleIcon from '@mui/icons-material/Title'; -import { useApi } from '@/context/ApiContext'; -import AttendeeInput from '@/components/AttendeeInput'; - +import LoadingButton from '@mui/lab/LoadingButton'; +import { Box, Checkbox, Typography } from '@mui/material'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DatePicker } from '@mui/x-date-pickers/DatePicker'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { BookRoomDto, EventResponse, IConferenceRoom } from '@quickmeet/shared'; +import dayjs from 'dayjs'; +import { useEffect, useRef, useState } from 'react'; +import toast from 'react-hot-toast'; +import { useNavigate } from 'react-router-dom'; const createRoomDropdownOptions = (rooms: IConferenceRoom[]) => { return (rooms || []).map((room) => ({ value: room.email, text: room.name, seats: room.seats, floor: room.floor }) as RoomsDropdownOption); }; @@ -58,6 +61,8 @@ export default function BookRoomView({ onRoomBooked }: BookRoomViewProps) { seats: preferences.seats, }); + const [date, setDate] = useState(dayjs()); + // Utilities and hooks const navigate = useNavigate(); const abortControllerRef = useRef(null); @@ -119,10 +124,8 @@ export default function BookRoomView({ onRoomBooked }: BookRoomViewProps) { async function setAvailableRooms() { const { startTime, duration, seats } = formData; const { floor } = preferences; - - const date = new Date(Date.now()).toISOString().split('T')[0]; - const formattedStartTime = convertToRFC3339(date, startTime); - + const currentDate = date.toISOString().split('T')[0]; + const formattedStartTime = convertToRFC3339(currentDate, startTime); setRoomLoading(true); if (abortControllerRef.current) { @@ -167,8 +170,7 @@ export default function BookRoomView({ onRoomBooked }: BookRoomViewProps) { return; } - const date = new Date(Date.now()).toISOString().split('T')[0]; - const formattedStartTime = convertToRFC3339(date, startTime); + const formattedStartTime = convertToRFC3339(date.toISOString().split('T')[0], startTime); const { floor, title: preferredTitle } = preferences; const payload: BookRoomDto = { @@ -215,30 +217,71 @@ export default function BookRoomView({ onRoomBooked }: BookRoomViewProps) { > theme.palette.common.white, + border: 'none', + width: '100%', }} > - ({ - color: theme.palette.grey[50], - }), - ]} + + ({ + color: theme.palette.grey[50], + }), + ]} + /> + } + /> + + + + { + if (newDate) { + setDate(newDate); + } + }} + slotProps={{ + inputAdornment: { + position: 'start', + }, + }} + sx={{ + '.MuiOutlinedInput-root': { + '& fieldset': { + border: 'none', + }, + }, + '.MuiInputBase-input': { + color: (theme) => theme.palette.common.black, + fontFamily: 'inherit', + fontSize: '1.125rem', + fontWeight: 400, + }, + '.MuiSvgIcon-root': { + color: (theme) => theme.palette.grey[50], + }, + ml: 0.2, + }} /> - } - /> - + + + + =14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.9.0", + "@emotion/styled": "^11.8.1", + "@mui/material": "^5.15.14 || ^6.0.0", + "@mui/system": "^5.15.14 || ^6.0.0", + "date-fns": "^2.25.0 || ^3.2.0 || ^4.0.0", + "date-fns-jalali": "^2.13.0-0 || ^3.2.0-0 || ^4.0.0-0", + "dayjs": "^1.10.7", + "luxon": "^3.0.2", + "moment": "^2.29.4", + "moment-hijri": "^2.1.2 || ^3.0.0", + "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "date-fns": { + "optional": true + }, + "date-fns-jalali": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + }, + "moment-hijri": { + "optional": true + }, + "moment-jalaali": { + "optional": true + } + } + }, + "node_modules/@mui/x-internals": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.24.0.tgz", + "integrity": "sha512-lYa/XLltxNMY8YAFDopIHrXda2EAoqMCilyGMuPMz+WTG+b+StlUKqtj8cgFPQ/sa5dQ2fR7R3KJdjLREKUrlQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.25.7", + "@mui/utils": "^5.16.6 || ^6.0.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/@nestjs/cache-manager": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@nestjs/cache-manager/-/cache-manager-2.3.0.tgz", @@ -4512,6 +4602,12 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "license": "MIT" + }, "node_modules/debug": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", diff --git a/package.json b/package.json index 239615c..1b8aade 100644 --- a/package.json +++ b/package.json @@ -27,5 +27,9 @@ ], "devDependencies": { "npm-run-all2": "^7.0.1" + }, + "dependencies": { + "@mui/x-date-pickers": "^7.24.0", + "dayjs": "^1.11.13" } }