all repos — caroster @ a781a5fafbf60b3c6587213cf3c9bfb735bdf933

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

frontend/containers/Car/HeaderEditing.js (view raw)

  1import {useState, useReducer, useCallback, useEffect} from 'react';
  2import Typography from '@material-ui/core/Typography';
  3import IconButton from '@material-ui/core/IconButton';
  4import Icon from '@material-ui/core/Icon';
  5import Button from '@material-ui/core/Button';
  6import moment from 'moment';
  7import {makeStyles} from '@material-ui/core/styles';
  8import {useTranslation} from 'react-i18next';
  9import TextField from '@material-ui/core/TextField';
 10import Slider from '@material-ui/core/Slider';
 11import RemoveDialog from '../RemoveDialog';
 12import useToastStore from '../../stores/useToastStore';
 13import useEventStore from '../../stores/useEventStore';
 14import {
 15  useUpdateEventMutation,
 16  useUpdateCarMutation,
 17  useDeleteCarMutation,
 18} from '../../generated/graphql';
 19
 20const HeaderEditing = ({car, toggleEditing}) => {
 21  const classes = useStyles();
 22  const {t} = useTranslation();
 23  const event = useEventStore(s => s.event);
 24  const addToast = useToastStore(s => s.addToast);
 25  const [updateEvent] = useUpdateEventMutation();
 26  const [updateCar] = useUpdateCarMutation();
 27  const [deleteCar] = useDeleteCarMutation({refetchQueries: ['event']});
 28  const [removing, toggleRemoving] = useReducer(i => !i, false);
 29
 30  // States
 31  const [name, setName] = useState(car?.name ?? '');
 32  const [seats, setSeats] = useState(car?.seats ?? 4);
 33  const [meeting, setMeeting] = useState(car?.meeting ?? '');
 34  const [date, setDate] = useState(
 35    car?.departure
 36      ? moment(car.departure).format('YYYY-MM-DD')
 37      : moment().format('YYYY-MM-DD')
 38  );
 39  const [phone, setPhone] = useState(car ? car['phone_number'] : '');
 40  const [details, setDetails] = useState(car?.details ?? '');
 41
 42  // Click on ESQ closes the form
 43  const escFunction = useCallback(
 44    evt => {
 45      if (evt.keyCode === 27) toggleEditing();
 46    },
 47    [toggleEditing]
 48  );
 49
 50  useEffect(() => {
 51    document.addEventListener('keydown', escFunction, false);
 52    return () => {
 53      document.removeEventListener('keydown', escFunction, false);
 54    };
 55  }, [escFunction]);
 56
 57  const onSave = async evt => {
 58    if (evt.preventDefault) evt.preventDefault();
 59    try {
 60      // If new seats count is under current passengers count, put excedent in event waiting list
 61      if (!!car.passengers && car.passengers.length > seats) {
 62        const lostPassengers = car.passengers.slice(seats);
 63        if (lostPassengers.length > 0)
 64          await updateEvent({
 65            variables: {
 66              id: event.id,
 67              eventUpdate: {
 68                waiting_list: [
 69                  ...(event.waiting_list ?? []),
 70                  ...lostPassengers,
 71                ],
 72              },
 73            },
 74          });
 75      }
 76      await updateCar({
 77        variables: {
 78          id: car.id,
 79          carUpdate: {
 80            name,
 81            seats,
 82            meeting,
 83            departure: moment(date).toISOString(),
 84            phone_number: phone,
 85            details,
 86            passengers: car.passengers ? car.passengers.slice(0, seats) : [],
 87          },
 88        },
 89      });
 90      toggleEditing();
 91    } catch (error) {
 92      console.error(error);
 93      addToast('car.errors.cant_update');
 94    }
 95    return false;
 96  };
 97
 98  const onRemove = async () => {
 99    try {
100      // Put passengers in event waiting list (if any)
101      if (Array.isArray(car?.passengers) && car.passengers.length > 0)
102        await updateEvent({
103          variables: {
104            id: event.id,
105            eventUpdate: {
106              waiting_list: [...(event.waiting_list ?? []), ...car.passengers],
107            },
108          },
109        });
110      await deleteCar({
111        variables: {
112          id: car.id,
113        },
114      });
115      addToast(t('car.actions.removed'));
116      toggleEditing();
117    } catch (error) {
118      console.error(error);
119      addToast('car.errors.cant_remove');
120    }
121  };
122
123  return (
124    <div className={classes.header}>
125      <form onSubmit={onSave}>
126        <IconButton
127          size="small"
128          color="primary"
129          type="submit"
130          className={classes.edit}
131        >
132          <Icon>done</Icon>
133        </IconButton>
134        {/* TODO Add time input */}
135        <TextField
136          label={t('car.creation.date')}
137          value={date}
138          onChange={e => setDate(e.target.value)}
139          className={classes.picker}
140          fullWidth
141          id="NewCarDateTime"
142          name="date"
143          type="date"
144          InputLabelProps={{
145            shrink: true,
146          }}
147        />
148        <TextField
149          label={t('car.creation.name')}
150          fullWidth
151          autoFocus
152          margin="dense"
153          value={name}
154          onChange={e => setName(e.target.value)}
155          id="EditCarName"
156          name="name"
157        />
158        <TextField
159          label={t('car.creation.phone')}
160          fullWidth
161          autoFocus
162          margin="dense"
163          value={phone}
164          onChange={e => setPhone(e.target.value)}
165          id="EditCarPhone"
166          name="phone"
167        />
168        <TextField
169          label={t('car.creation.meeting')}
170          fullWidth
171          margin="dense"
172          multiline
173          rows={2}
174          value={meeting}
175          onChange={e => setMeeting(e.target.value)}
176          id="EditCarMeeting"
177          name="meeting"
178        />
179        <TextField
180          label={t('car.creation.notes')}
181          fullWidth
182          margin="dense"
183          inputProps={{maxLength: 250}}
184          helperText={`${details.length}/250`}
185          multiline
186          rows={2}
187          value={details}
188          onChange={e => setDetails(e.target.value)}
189          id="EditCarDetails"
190          name="details"
191        />
192        <div className={classes.slider}>
193          <Typography variant="caption">{t('car.creation.seats')}</Typography>
194          <Slider
195            value={seats}
196            onChange={(e, value) => setSeats(value)}
197            step={1}
198            marks={[1, 2, 3, 4, 5, 6, 7, 8].map(value => ({
199              value,
200              label: value,
201            }))}
202            min={1}
203            max={8}
204            valueLabelDisplay="auto"
205            id="EditCarSeats"
206          />
207        </div>
208      </form>
209      <div className={classes.actions}>
210        <Button
211          variant="outlined"
212          color="primary"
213          onClick={onSave}
214          id="CarSave"
215        >
216          {t('generic.save')}
217        </Button>
218        <Button
219          variant="outlined"
220          color="primary"
221          onClick={toggleRemoving}
222          id="CarRemove"
223        >
224          {t('generic.remove')}
225        </Button>
226      </div>
227      <RemoveDialog
228        text={t('car.actions.remove_alert')}
229        open={removing}
230        onClose={toggleRemoving}
231        onRemove={onRemove}
232      />
233    </div>
234  );
235};
236
237const useStyles = makeStyles(theme => ({
238  header: {
239    padding: theme.spacing(2),
240  },
241  edit: {
242    position: 'absolute',
243    top: 0,
244    right: 0,
245    margin: theme.spacing(1),
246    zIndex: theme.zIndex.speedDial,
247  },
248  section: {
249    marginTop: theme.spacing(2),
250  },
251  slider: {
252    marginTop: theme.spacing(2),
253  },
254  actions: {
255    display: 'flex',
256    flexDirection: 'column',
257    justifyContent: 'center',
258    margin: theme.spacing(2, 0),
259    '& > *:first-child': {
260      marginBottom: theme.spacing(2),
261    },
262  },
263  picker: {
264    marginBottom: theme.spacing(2),
265  },
266}));
267
268export default HeaderEditing;