all repos — caroster @ f5c854ad35aa58a77481d22c3554680f8828f45f

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

frontend/containers/Travel/HeaderEditing.tsx (view raw)

  1import {useState, useReducer, useCallback, useEffect} from 'react';
  2import Typography from '@mui/material/Typography';
  3import Button from '@mui/material/Button';
  4import TextField from '@mui/material/TextField';
  5import Slider from '@mui/material/Slider';
  6import Box from '@mui/material/Box';
  7import moment from 'moment';
  8import {useTheme} from '@mui/material/styles';
  9import {DatePicker} from '@mui/x-date-pickers/DatePicker';
 10import {TimePicker} from '@mui/x-date-pickers/TimePicker';
 11import {CountryIso2} from 'react-international-phone';
 12import {useTranslation} from 'next-i18next';
 13import RemoveDialog from '../RemoveDialog';
 14import useActions from './useActions';
 15import PlaceInput from '../PlaceInput';
 16import PhoneInput from '../../components/PhoneInput';
 17import useEventStore from '../../stores/useEventStore';
 18import {TravelEntity} from '../../generated/graphql';
 19
 20interface Props {
 21  travel: TravelEntity;
 22  toggleEditing: () => void;
 23}
 24
 25const HeaderEditing = ({travel, toggleEditing}: Props) => {
 26  const {t} = useTranslation();
 27  const theme = useTheme();
 28  const actions = useActions({travel});
 29  const isCarosterPlus = useEventStore(s =>
 30    s.event.enabled_modules?.includes('caroster-plus')
 31  );
 32  const isReturnEvent = useEventStore(s => s.event?.isReturnEvent);
 33  const [removing, toggleRemoving] = useReducer(i => !i, false);
 34
 35  // States
 36  const [name, setName] = useState(travel?.attributes.vehicleName ?? '');
 37  const [seats, setSeats] = useState(travel?.attributes.seats ?? 4);
 38  const [meeting, setMeeting] = useState(travel?.attributes.meeting ?? '');
 39  const [meeting_latitude, setMeetingLatitude] = useState(
 40    travel?.attributes.meeting_latitude
 41  );
 42  const [meeting_longitude, setMeetingLongitude] = useState(
 43    travel?.attributes.meeting_longitude
 44  );
 45  const [date, setDate] = useState(
 46    travel?.attributes.departureDate
 47      ? moment(travel.attributes.departureDate)
 48      : null
 49  );
 50  const [time, setTime] = useState(
 51    travel?.attributes.departureTime
 52      ? moment(travel.attributes.departureTime, 'HH:mm')
 53      : null
 54  );
 55  const [phone, setPhone] = useState(travel?.attributes.phone_number ?? '');
 56  const [phoneCountry, setPhoneCountry] = useState<CountryIso2>(
 57    travel?.attributes.phoneCountry ?? ''
 58  );
 59  const [phoneError, setPhoneError] = useState(false);
 60  const [details, setDetails] = useState(travel?.attributes.details ?? '');
 61
 62  // Click on ESQ closes the form
 63  const escFunction = useCallback(
 64    evt => {
 65      if (evt.keyCode === 27) toggleEditing();
 66    },
 67    [toggleEditing]
 68  );
 69
 70  useEffect(() => {
 71    document.addEventListener('keydown', escFunction, false);
 72    return () => {
 73      document.removeEventListener('keydown', escFunction, false);
 74    };
 75  }, [escFunction]);
 76
 77  const onSave = async event => {
 78    if (event.preventDefault) event.preventDefault();
 79    const travelUpdate = {
 80      meeting,
 81      meeting_latitude,
 82      meeting_longitude,
 83      details,
 84      seats,
 85      phone_number: phone,
 86      phoneCountry,
 87      vehicleName: name,
 88      departureDate: date ? moment(date).format('YYYY-MM-DD') : '',
 89      departureTime: time ? moment(time).format('HH:mm') : '',
 90    };
 91    await actions.updateTravel(travelUpdate);
 92    toggleEditing();
 93  };
 94
 95  const onRemove = async () => {
 96    await actions.removeTravel(
 97      isCarosterPlus
 98        ? t`travel.actions.removed.caroster_plus`
 99        : t`travel.actions.removed`
100    );
101    toggleEditing();
102  };
103
104  return (
105    <Box sx={{padding: 2}}>
106      <form onSubmit={onSave}>
107        <DatePicker
108          slotProps={{
109            textField: {
110              sx: {width: '100%', pb: 2},
111            },
112          }}
113          format="DD/MM/YYYY"
114          label={t('travel.creation.date')}
115          value={date}
116          onChange={setDate}
117        />
118        <TimePicker
119          label={t('travel.creation.time')}
120          slotProps={{
121            textField: {
122              sx: {width: '100%', pb: 2},
123            },
124          }}
125          value={time}
126          onChange={setTime}
127          ampm={false}
128          minutesStep={5}
129        />
130        <TextField
131          label={t('travel.creation.name')}
132          fullWidth
133          sx={{pb: 2}}
134          value={name}
135          onChange={e => setName(e.target.value)}
136          name="name"
137          id="EditTravelName"
138        />
139        <PhoneInput
140          value={phone}
141          onChange={({phone, country, error}) => {
142            setPhone(phone);
143            setPhoneCountry(country);
144            setPhoneError(error);
145          }}
146          label={t('travel.creation.phone')}
147          sx={{pb: 2}}
148          name="phone"
149          id="EditTravelPhone"
150        />
151        <PlaceInput
152          label={t(isReturnEvent ? 'travel.destination' : 'travel.meeting')}
153          textFieldProps={{sx: {pb: 2}}}
154          place={meeting}
155          latitude={meeting_latitude}
156          longitude={meeting_longitude}
157          onSelect={({place, latitude, longitude}) => {
158            setMeeting(place);
159            setMeetingLatitude(latitude);
160            setMeetingLongitude(longitude);
161          }}
162        />
163        <TextField
164          label={t('travel.creation.notes')}
165          fullWidth
166          sx={{pb: 2}}
167          multiline
168          maxRows={4}
169          inputProps={{maxLength: 250}}
170          helperText={`${details.length}/250`}
171          value={details}
172          onChange={e => setDetails(e.target.value)}
173          name="details"
174          id="EditTravelDetails"
175        />
176        <Box sx={{marginTop: theme.spacing(2)}}>
177          <Typography variant="caption">
178            {t('travel.creation.seats')}
179          </Typography>
180          <Slider
181            value={seats}
182            onChange={(e, value) => setSeats(value)}
183            step={1}
184            marks={[1, 2, 3, 4, 5, 6, 7, 8].map(value => ({
185              value,
186              label: value,
187            }))}
188            min={1}
189            max={8}
190            valueLabelDisplay="auto"
191            id="EditTravelSeats"
192          />
193        </Box>
194      </form>
195      <Box
196        sx={{
197          display: 'flex',
198          flexDirection: 'column',
199          justifyContent: 'center',
200          margin: theme.spacing(2, 0),
201          '& > *:first-child': {
202            marginBottom: theme.spacing(2),
203          },
204        }}
205      >
206        <Button
207          variant="contained"
208          color="primary"
209          onClick={onSave}
210          id="TravelSave"
211          disabled={phoneError || !phone}
212        >
213          {t('generic.save')}
214        </Button>
215        <Button
216          variant="outlined"
217          color="primary"
218          onClick={toggleRemoving}
219          id="TravelRemove"
220        >
221          {t('generic.remove')}
222        </Button>
223      </Box>
224      <RemoveDialog
225        text={
226          isCarosterPlus
227            ? t`travel.actions.remove_alert.caroster_plus`
228            : t`travel.actions.remove_alert`
229        }
230        open={removing}
231        onClose={toggleRemoving}
232        onRemove={onRemove}
233      />
234    </Box>
235  );
236};
237
238export default HeaderEditing;