all repos — caroster @ v8.1

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