all repos — caroster @ 8a4f9e069b406ad3b9c2405dbef767b2a5304477

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