all repos — caroster @ 1b873bd29e3743cca69f0237124963dae6cfeab9

[Octree] Group carpool to your event https://caroster.io

frontend/containers/AddTravelButton/NewTravelDialog.tsx (view raw)

  1import {useState, forwardRef, useMemo} from 'react';
  2import Dialog from '@mui/material/Dialog';
  3import DialogActions from '@mui/material/DialogActions';
  4import DialogContent from '@mui/material/DialogContent';
  5import DialogTitle from '@mui/material/DialogTitle';
  6import Button from '@mui/material/Button';
  7import Slide from '@mui/material/Slide';
  8import TextField from '@mui/material/TextField';
  9import Slider from '@mui/material/Slider';
 10import Typography from '@mui/material/Typography';
 11import moment from 'moment';
 12import {Box, Divider, Stack} from '@mui/material';
 13import {DatePicker} from '@mui/x-date-pickers/DatePicker';
 14import {TimePicker} from '@mui/x-date-pickers/TimePicker';
 15import {useTranslation} from 'next-i18next';
 16import PhoneInput from '../../components/PhoneInput';
 17import PlaceInput from '../PlaceInput';
 18import useEventStore from '../../stores/useEventStore';
 19import useActions from './useActions';
 20import FAQLink from './FAQLink';
 21import {useSession} from 'next-auth/react';
 22
 23interface Props {
 24  open: boolean;
 25  toggle: () => void;
 26}
 27
 28const NewTravelDialog = ({open, toggle}: Props) => {
 29  const {t} = useTranslation();
 30  const event = useEventStore(s => s.event);
 31  const {createTravel} = useActions({event});
 32  const session = useSession();
 33  const profile = session?.data?.profile;
 34  const dateMoment = useMemo(
 35    () => (event?.date ? moment(event.date) : null),
 36    [event?.date]
 37  );
 38
 39  // States
 40  const [firstname, setFirstname] = useState(profile?.firstName || '');
 41  const [lastname, setLastname] = useState(profile?.lastName || '');
 42  const [seats, setSeats] = useState(4);
 43  const [meeting, setMeeting] = useState('');
 44  const [meeting_latitude, setMeetingLatitude] = useState(null);
 45  const [meeting_longitude, setMeetingLongitude] = useState(null);
 46  const [date, setDate] = useState(dateMoment);
 47  const [time, setTime] = useState(dateMoment);
 48  const [phone, setPhone] = useState('');
 49  const [phoneCountry, setPhoneCountry] = useState('');
 50  const [phoneError, setPhoneError] = useState(false);
 51  const [details, setDetails] = useState('');
 52
 53  const canCreate =
 54    !!firstname?.trim() &&
 55    !!lastname?.trim() &&
 56    !!seats &&
 57    !phoneError &&
 58    phone;
 59
 60  const clearState = () => {
 61    setSeats(4);
 62    setMeeting('');
 63    setMeetingLatitude(null);
 64    setMeetingLongitude(null);
 65    setDate(moment());
 66    setDetails('');
 67  };
 68
 69  const onCreate = async e => {
 70    if (e.preventDefault) e.preventDefault();
 71
 72    const travel = {
 73      firstname,
 74      lastname,
 75      meeting,
 76      meeting_latitude,
 77      meeting_longitude,
 78      details,
 79      seats,
 80      phone_number: phone,
 81      phoneCountry: phoneCountry,
 82      departureDate: date ? moment(date).format('YYYY-MM-DD') : '',
 83      departureTime: time ? moment(time).format('HH:mm') : '',
 84      event: event.id,
 85    };
 86
 87    await createTravel(travel);
 88    toggle();
 89
 90    clearState();
 91  };
 92
 93  return (
 94    <Dialog
 95      fullWidth
 96      maxWidth="xs"
 97      open={open}
 98      onClose={() => {
 99        toggle();
100        clearState();
101      }}
102      TransitionComponent={Transition}
103    >
104      <form onSubmit={onCreate}>
105        <DialogTitle sx={{paddingBottom: 0}}>
106          {t('travel.creation.title')}
107        </DialogTitle>
108        <DialogContent sx={{px: 0}}>
109          <Stack px={3} py={2} spacing={2}>
110            <Typography>{t('travel.creation.car.title')}</Typography>
111            <Box display="flex" gap={2}>
112              <TextField
113                fullWidth
114                required
115                variant="outlined"
116                size="small"
117                label={t`profile.firstName`}
118                value={firstname}
119                onChange={e => setFirstname(e.target.value)}
120                name="firstname"
121                id="NewTravelFirstname"
122                error={!firstname?.trim()}
123              />
124              <TextField
125                fullWidth
126                required
127                variant="outlined"
128                size="small"
129                label={t`profile.lastName`}
130                value={lastname}
131                onChange={e => setLastname(e.target.value)}
132                name="lastname"
133                id="NewTravelLastname"
134                error={!lastname?.trim()}
135              />
136            </Box>
137            <PhoneInput
138              required
139              value={phone}
140              onChange={({phone, country, error}) => {
141                setPhone(phone);
142                setPhoneCountry(country);
143                setPhoneError(error);
144              }}
145              label={t('travel.creation.phone')}
146              name="phone"
147              variant="outlined"
148              size="small"
149              helperText={
150                <Typography variant="caption">
151                  <FAQLink
152                    link={t('travel.creation.phoneHelper.faq')}
153                    text={t('travel.creation.phoneHelper.why')}
154                  />
155                </Typography>
156              }
157              id="NewTravelPhone"
158            />
159            <Box>
160              <Typography variant="caption">
161                {t('travel.creation.seats')}
162              </Typography>
163              <Slider
164                size="small"
165                value={seats}
166                onChange={(e, value) => setSeats(value as number)}
167                step={1}
168                marks={MARKS}
169                min={1}
170                max={MARKS.length}
171                valueLabelDisplay="auto"
172                id="NewTravelSeats"
173              />
174            </Box>
175          </Stack>
176          <Divider />
177          <Stack px={3} pt={2} spacing={2}>
178            <Typography>{t('travel.creation.travel.title')}</Typography>
179            <Box display="flex" gap={2}>
180              <DatePicker
181                slotProps={{
182                  textField: {
183                    variant: 'outlined',
184                    size: 'small',
185                    helperText: !date
186                      ? t('travel.creation.travel.dateHelper')
187                      : '',
188                    error: !date,
189                    FormHelperTextProps: {sx: {color: 'warning.main'}},
190                  },
191                }}
192                format="DD/MM/YYYY"
193                label={t('travel.creation.date')}
194                value={date}
195                onChange={setDate}
196                autoFocus
197              />
198              <TimePicker
199                slotProps={{
200                  textField: {
201                    variant: 'outlined',
202                    size: 'small',
203                    helperText: '',
204                  },
205                }}
206                label={t('travel.creation.time')}
207                value={time}
208                onChange={setTime}
209                ampm={false}
210                minutesStep={5}
211              />
212            </Box>
213            <PlaceInput
214              label={t(
215                event?.isReturnEvent ? 'travel.destination' : 'travel.meeting'
216              )}
217              textFieldProps={{
218                variant: 'outlined',
219              }}
220              place={meeting}
221              latitude={meeting_latitude}
222              longitude={meeting_longitude}
223              onSelect={({place, latitude, longitude}) => {
224                setMeeting(place);
225                setMeetingLatitude(latitude);
226                setMeetingLongitude(longitude);
227              }}
228            />
229            <TextField
230              variant="outlined"
231              size="small"
232              label={t('travel.creation.notes')}
233              fullWidth
234              multiline
235              maxRows={4}
236              inputProps={{maxLength: 250}}
237              helperText={`${details.length}/250`}
238              value={details}
239              onChange={e => setDetails(e.target.value)}
240              name="details"
241              id="NewTravelDetails"
242            />
243          </Stack>
244        </DialogContent>
245        <DialogActions
246          sx={{
247            paddingTop: 0,
248          }}
249        >
250          <Button
251            color="primary"
252            id="NewTravelCancel"
253            onClick={toggle}
254            tabIndex={-1}
255          >
256            {t('generic.cancel')}
257          </Button>
258          <Button
259            color="primary"
260            variant="contained"
261            type="submit"
262            disabled={!canCreate}
263            aria-disabled={!canCreate}
264            id="NewTravelSubmit"
265          >
266            {t('travel.creation.submit')}
267          </Button>
268        </DialogActions>
269      </form>
270    </Dialog>
271  );
272};
273
274const Transition = forwardRef(function Transition(props, ref) {
275  return <Slide direction="up" ref={ref} {...props} />;
276});
277
278const MARKS = [1, 2, 3, 4, 5, 6, 7, 8].map(value => ({
279  value,
280  label: value,
281}));
282
283export default NewTravelDialog;