all repos — caroster @ 7cb2fa527ba3985be0656d729ca079ff779fb3a2

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

app/src/containers/Car/HeaderEditing.js (view raw)

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