all repos — caroster @ a440f7c732fd067d2cfa68ac51df9e0369252402

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

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

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