diff --git a/client/src/components/AttendeeInput.tsx b/client/src/components/AttendeeInput.tsx index 235183f..41f064c 100644 --- a/client/src/components/AttendeeInput.tsx +++ b/client/src/components/AttendeeInput.tsx @@ -31,7 +31,9 @@ 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 emails = newValue.map((option) => + typeof option === 'object' && option.email ? option.email : (option as string) + ); const filteredEmails = emails .join(' ') .split(/\s+/) @@ -44,7 +46,9 @@ export default function AttendeeInput({ id, onChange, value, type }: AttendeeInp uniqueEmails.forEach((email) => { if (isEmailValid(email)) { - const existingPerson = newValue.find((option) => typeof option === 'object' && option.email === email) as IPeopleInformation; + const existingPerson = newValue.find( + (option) => typeof option === 'object' && option.email === email + ) as IPeopleInformation; if (existingPerson) { validPeople.push(existingPerson); } else { @@ -63,45 +67,54 @@ export default function AttendeeInput({ id, onChange, value, type }: AttendeeInp } setTextInput(''); }; + const debouncedInputChange = debounce(handleInputChange, 300); + return ( ({ - gap: '8px', - padding: '10px', + display: 'flex', + flexDirection: 'column', borderRadius: 1, backgroundColor: theme.palette.common.white, - '&:focus-within': { - border: 'none', - }, - maxHeight: '65px', - overflowY: 'auto', + p: 1.5, + width: '100%', + boxSizing: 'border-box', }), ]} > ({ - color: theme.palette.grey[50], - position: 'sticky', - top: 0, - bottom: 0, - }), - ]} + sx={{ + color: 'text.secondary', + mr: 1, + }} /> + + Attendees + + + + {/* Scrollable list area */} + ({ - bgcolor: theme.palette.grey[50], - }), - ]} + sx={{ + bgcolor: 'grey.200', + }} /> } variant="outlined" @@ -160,33 +171,19 @@ export default function AttendeeInput({ id, onChange, value, type }: AttendeeInp onChange={(e) => setTextInput(e.target.value)} type={type} variant="standard" - placeholder="Attendees" - slotProps={{ - input: { - ...params.InputProps, - endAdornment: null, + placeholder="Enter attendee emails" + fullWidth + sx={{ + '& .MuiInputBase-input': { + fontSize: '0.9rem', + }, + '& .MuiInput-underline:before, & .MuiInput-underline:hover:before': { + borderBottom: 'none !important', + }, + '& .MuiInput-underline:after': { + borderBottom: 'none', }, }} - sx={[ - (theme) => ({ - flex: 1, - py: 0, - px: 1, - '& .MuiInputBase-input': { - fontSize: theme.typography.subtitle1, - }, - '& .MuiInputBase-input::placeholder': { - color: theme.palette.primary.main, - fontSize: theme.typography.subtitle1, - }, - '& .MuiInput-underline:before, & .MuiInput-underline:hover:before': { - borderBottom: 'none !important', - }, - '& .MuiInput-underline:after': { - borderBottom: 'none', - }, - }), - ]} /> )} renderOption={(props, option) => { @@ -196,21 +193,19 @@ export default function AttendeeInput({ id, onChange, value, type }: AttendeeInp ({ - '& > img': { mr: 2, flexShrink: 0 }, - backgroundColor: isSelected ? theme.palette.grey[100] : 'transparent', - }), - ]} + sx={{ + '& > img': { mr: 2, flexShrink: 0 }, + backgroundColor: isSelected ? 'grey.100' : 'transparent', + }} {...optionProps} gap={1} > - + {option.name} - + {option.email} @@ -222,3 +217,4 @@ export default function AttendeeInput({ id, onChange, value, type }: AttendeeInp ); } + diff --git a/client/src/pages/Login/index.tsx b/client/src/pages/Login/index.tsx index 530bd2f..99b1d32 100644 --- a/client/src/pages/Login/index.tsx +++ b/client/src/pages/Login/index.tsx @@ -1,5 +1,6 @@ import { Box, Button, Typography } from '@mui/material'; import { GoogleIcon } from '@components/CustomIcons'; +import { useEffect } from 'react'; import { useLocation } from 'react-router-dom'; import { secrets } from '@config/secrets'; import toast from 'react-hot-toast'; @@ -11,9 +12,12 @@ const Login = () => { const { state } = useLocation(); const errorMessage = state?.message; - if (errorMessage) { - toast.error(errorMessage); - } + // ✅ Show toast only once on mount when there's an error + useEffect(() => { + if (errorMessage) { + toast.error(errorMessage); + } + }, [errorMessage]); async function onSignInClick(): Promise { await api.login(); @@ -36,6 +40,7 @@ const Login = () => { {secrets.appSlogan} +