all repos — caroster @ 124dfa21a7100f628775f5a0f49841048f8584ea

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

feat: ✨ Add to passengers list as authenticated user


#217
Simon Mulquin simon@octree.ch
Tue, 01 Feb 2022 09:39:16 +0000
commit

124dfa21a7100f628775f5a0f49841048f8584ea

parent

5e8d53baf4f6676069cef7da12605ec9ae0375ed

A backend/.nvmrc

@@ -0,0 +1,1 @@

+v14.18
M backend/.strapi-updater.jsonbackend/.strapi-updater.json

@@ -1,5 +1,5 @@

{ "latest": "3.6.8", - "lastUpdateCheck": 1637226107128, + "lastUpdateCheck": 1643280018387, "lastNotification": 1637085344214 }
M backend/api/event/services/event.jsbackend/api/event/services/event.js

@@ -24,7 +24,7 @@ module.exports = {

sanitize: event => { const cars = event?.cars?.map(strapi.services.car.sanitize); const waitingList = event?.waitingList?.map(list => - _pick(list, ['id', 'name', 'location']) + _pick(list, ['id', 'name', 'location', 'user']) ); const sanitizedEvent = _pick(event, PUBLIC_FIELDS); return {...sanitizedEvent, cars, waitingList};
M frontend/containers/AddPassengerButtons/index.tsxfrontend/containers/AddPassengerButtons/index.tsx

@@ -2,31 +2,70 @@ import Icon from '@material-ui/core/Icon';

import Box from '@material-ui/core/Box'; import Button from '@material-ui/core/Button'; import {makeStyles} from '@material-ui/core/styles'; -import { useTranslation } from 'react-i18next'; +import {useTranslation} from 'react-i18next'; -const AddPassengerButtons = ({toggleNewPassenger}) => { +interface Props { + getOnClickFunction: (addSelf: boolean) => () => void; + canAddSelf: boolean; +} + +const AddPassengerButtons = ({getOnClickFunction, canAddSelf}: Props) => { const classes = useStyles(); const {t} = useTranslation(); return ( <Box className={classes.addButtonsContainer}> - <Button - variant="outlined" - color="primary" - fullWidth - startIcon={<Icon>person_add</Icon>} - onClick={toggleNewPassenger} - > - {t('travel.passengers.add')} - </Button> + {canAddSelf && ( + <Box className={classes.addButtonsContainer}> + <Button + className={classes.textContainer} + variant="contained" + color="secondary" + fullWidth + onClick={getOnClickFunction(true)} + > + <Icon>person_add</Icon> + {t('travel.passengers.add_me')} + </Button> + </Box> + )} + <Box className={classes.addButtonsContainer}> + <Button + className={classes.textContainer} + variant="outlined" + color="primary" + fullWidth + onClick={getOnClickFunction(false)} + > + <Icon>person_add</Icon> + {t('travel.passengers.add')} + </Button> + </Box> </Box> ); }; const useStyles = makeStyles(theme => ({ addButtonsContainer: { - padding: theme.spacing(2), + padding: theme.spacing(1), textAlign: 'center', + }, + textContainer: { + padding: theme.spacing(1, 8), + [theme.breakpoints.down(440)]: { + padding: theme.spacing(1, 4), + }, + '& > .MuiButton-label': { + '& > .material-icons': { + width: theme.spacing(3), + textAlign: 'center', + position: 'absolute', + left: theme.spacing(4), + [theme.breakpoints.down(440)]: { + left: theme.spacing(1), + }, + }, + }, }, }));
M frontend/containers/NewPassengerDialog/AddPassengerToTravel.tsxfrontend/containers/NewPassengerDialog/AddPassengerToTravel.tsx

@@ -7,13 +7,14 @@ import {useTranslation} from 'react-i18next';

import useAddToEvents from '../../hooks/useAddToEvents'; import useEventStore from '../../stores/useEventStore'; import { - useUpdateTravelMutation, Travel as TravelType, } from '../../generated/graphql'; import SubmitButton from './SubmitButton'; import Transition from './Transition'; import AddPassengerCommonFields from './AddPassengerCommonFields'; import useStyles from './useStyles'; +import useToastStore from '../../stores/useToastStore'; +import usePassengersActions from '../../hooks/usePassengersActions'; interface Props { travel: TravelType;

@@ -25,38 +26,27 @@ const NewPassengerDialog = ({open, toggle, travel}: Props) => {

const {t} = useTranslation(); const classes = useStyles(); const event = useEventStore(s => s.event); - const [updateTravel] = useUpdateTravelMutation(); const {addToEvent} = useAddToEvents(); + const addToast = useToastStore(s => s.addToast); // States const [name, setName] = useState(''); const [email, setEmail] = useState(''); const canAddPassenger = !!name && !!email; + const {addPassengerToTravel} = usePassengersActions(); - const addPassenger = async (e: FormEvent) => { + const onSubmit = async (e: FormEvent) => { e.preventDefault(); const passenger = { email, name, }; - try { - const existingPassengers = - travel.passengers?.map(({__typename, ...item}) => item) || []; - const passengers = [...existingPassengers, passenger]; - await updateTravel({ - variables: { - id: travel.id, - travelUpdate: { - passengers, - }, - }, - }); + return addPassengerToTravel({passenger, travel, onSucceed: () => { addToEvent(event.id); + addToast(t('passenger.success.added_to_car', {name})); toggle(); - } catch (error) { - console.error(error); - } + }}); }; return (

@@ -67,7 +57,7 @@ open={open}

onClose={toggle} TransitionComponent={Transition} > - <form onSubmit={addPassenger}> + <form onSubmit={onSubmit}> <DialogTitle className={classes.title}> {travel.vehicle.name} <Icon
M frontend/containers/NewPassengerDialog/AddPassengerToWaitingList.tsxfrontend/containers/NewPassengerDialog/AddPassengerToWaitingList.tsx

@@ -10,56 +10,67 @@ import {useTranslation} from 'react-i18next';

import useToastStore from '../../stores/useToastStore'; import useEventStore from '../../stores/useEventStore'; import useAddToEvents from '../../hooks/useAddToEvents'; +import usepassengersActions from '../../hooks/usePassengersActions'; +import useProfile from '../../hooks/useProfile'; import SubmitButton from './SubmitButton'; import Transition from './Transition'; import AddPassengerCommonFields from './AddPassengerCommonFields'; import useStyles from './useStyles'; -import {useUpdateEventMutation} from '../../generated/graphql'; interface Props { toggle: () => void; open: boolean; + addSelf: boolean; } -const NewPassengerDialog = ({ - open, - toggle, -}: Props) => { +const NewPassengerDialog = ({open, toggle, addSelf}: Props) => { const {t} = useTranslation(); const classes = useStyles(); const event = useEventStore(s => s.event); const addToast = useToastStore(s => s.addToast); const {addToEvent} = useAddToEvents(); - const [updateEvent] = useUpdateEventMutation(); // States const [name, setName] = useState(''); const [email, setEmail] = useState(''); const [location, setlocation] = useState(''); const canAddPassenger = !!name && !!email; + const {user} = useProfile(); + const {addPassengerToWaitingList} = usepassengersActions(); const addPassenger = async (e: FormEvent) => { e.preventDefault(); - const passenger = { - email, - name, - location, - }; + const passenger = + addSelf && user + ? { + user: user, + email: user.email, + name: user.username, + location, + } + : { + email, + name, + location, + }; - try { - const waitingList = [...event.waitingList, passenger].map( - ({__typename, ...item}) => item - ); - await updateEvent({ - variables: {uuid: event.uuid, eventUpdate: {waitingList}}, - refetchQueries: ['eventByUUID'], - }); - addToEvent(event.id); - toggle(); - } catch (error) { - console.error(error); - addToast(t('passenger.errors.cant_add_passenger')); - } + addPassengerToWaitingList({ + passenger, + event, + onError: () => addToast(t('passenger.errors.cant_add_passenger')), + onSucceed: () => { + addToEvent(event.id); + addToast( + t( + addSelf + ? 'passenger.success.added_self_to_waitlist' + : 'passenger.success.added_to_waitlist', + {name} + ) + ); + toggle(); + }, + }); }; return (

@@ -82,18 +93,18 @@ close

</Icon> </DialogTitle> <DialogContent className={classes.dialogContent}> - <AddPassengerCommonFields - email={email} - setEmail={setEmail} - name={name} - setName={setName} - /> + {!addSelf && ( + <AddPassengerCommonFields + email={email} + setEmail={setEmail} + name={name} + setName={setName} + /> + )} <Box className={classes.inputBox}> <label htmlFor="location"> <Typography> - <Icon className={classes.labelIcon}> - place - </Icon>{' '} + <Icon className={classes.labelIcon}>place</Icon>{' '} {t('travel.passengers.location')} </Typography> </label>

@@ -112,8 +123,12 @@ <Typography variant="caption">

{t('travel.passengers.location_helper')} </Typography> </Box> - <SubmitButton disabled={!canAddPassenger}> - {t('travel.passengers.add_someone')} + <SubmitButton + disabled={!addSelf && !canAddPassenger} + important={addSelf} + > + {!addSelf && t('travel.passengers.add_someone')} + {addSelf && t('travel.passengers.add_me')} </SubmitButton> </DialogContent> </form>
M frontend/containers/NewPassengerDialog/SubmitButton.tsxfrontend/containers/NewPassengerDialog/SubmitButton.tsx

@@ -4,19 +4,19 @@ import Icon from '@material-ui/core/Icon';

import {ReactNode} from 'react'; import useStyles from './useStyles'; -const SubmitButton = ({ - disabled, - children, -}: { +interface Props { disabled: boolean; children: ReactNode; -}) => { + important?: boolean; +} + +const SubmitButton = ({disabled, children, important}: Props) => { const classes = useStyles(); return ( <Box className={classes.buttonBox}> <Button - color="primary" - variant="outlined" + color={important ? 'secondary' : 'primary'} + variant={important ? 'contained' : 'outlined'} fullWidth type="submit" disabled={disabled}
M frontend/containers/PassengersList/index.tsxfrontend/containers/PassengersList/index.tsx

@@ -15,7 +15,6 @@ isVehicle?: boolean;

places?: number; onPress?: (passengerId: string) => void; onClick?: (passengerId: string) => void; - addPassenger: (passenger: PassengerInput) => void; } const PassengersList = (props: Props) => {
M frontend/containers/Travel/index.tsxfrontend/containers/Travel/index.tsx

@@ -12,7 +12,8 @@ import ClearButton from '../ClearButton';

interface Props { travel: TravelType; - toggleNewPassenger: () => void; + getAddPassengerFunction: (addSelf: boolean) => () => void; + canAddSelf: boolean; } const Travel = (props: Props) => {

@@ -31,14 +32,16 @@ ) : (

<Header travel={travel} toggleEditing={toggleEditing} /> )} <Divider /> - <AddPassengerButtons toggleNewPassenger={props.toggleNewPassenger} /> + <AddPassengerButtons + getOnClickFunction={props.getAddPassengerFunction} + canAddSelf={props.canAddSelf} + /> <Divider /> {!isEditing && ( <PassengersList passengers={travel.passengers} places={travel?.vehicle?.seats} - addPassenger={actions.addPassenger} - onClick={actions.removePassenger} + onClick={actions.sendPassengerToWaitingList} isVehicle Button={({onClick}: {onClick: () => void}) => ( <ClearButton icon="close" onClick={onClick} tabIndex={-1} />
M frontend/containers/Travel/useActions.tsfrontend/containers/Travel/useActions.ts

@@ -1,15 +1,13 @@

-import {useTranslation} from 'react-i18next'; import moment from 'moment'; +import {useTranslation} from 'react-i18next'; import useEventStore from '../../stores/useEventStore'; import useToastStore from '../../stores/useToastStore'; -import useAddToEvents from '../../hooks/useAddToEvents'; import { useUpdateTravelMutation, useUpdateEventMutation, useUpdateVehicleMutation, useDeleteTravelMutation, EventByUuidDocument, - EditComponentPassengerPassengerInput as PassengerInput, Travel, } from '../../generated/graphql';

@@ -26,36 +24,23 @@ const [updateEventMutation] = useUpdateEventMutation();

const [updateTravelMutation] = useUpdateTravelMutation(); const [updateVehicleMutation] = useUpdateVehicleMutation(); const [deleteTravelMutation] = useDeleteTravelMutation(); - const {addToEvent} = useAddToEvents(); - const addPassenger = async (passenger: PassengerInput) => { - try { - const existingPassengers = - travel.passengers?.map(({__typename, ...item}) => item) || []; - const passengers = [...existingPassengers, passenger]; - await updateTravelMutation({ - variables: { - id: travel.id, - travelUpdate: { - passengers, - }, - }, - }); - addToEvent(event.id); - } catch (error) { - console.error(error); - } - }; - - const removePassenger = async (passengerId: string) => { + const sendPassengerToWaitingList = async (passengerId: string) => { if (travel?.passengers) { try { - const {id, ...removedPassenger} = - travel.passengers?.find(item => item.id === passengerId) || {}; + const {id, ...removedPassenger} = travel.passengers?.find( + item => item.id === passengerId + ); + if (!removedPassenger) { + throw 'No corresponding passenger'; + } const existingPassengers = - travel.passengers?.map(({__typename, ...item}) => item) || []; + travel.passengers?.map(({__typename, user, ...item}) => + user && user.id ? {...item, user: user.id} : item + ) || []; const waitingList = [...event.waitingList, removedPassenger].map( - ({__typename, ...item}) => item + ({__typename, user, ...item}) => + user && user.id ? {...item, user: user.id} : item ); const passengers = existingPassengers.filter( item => item.id !== passengerId

@@ -166,7 +151,7 @@ addToast('travel.errors.cant_remove');

} }; - return {removePassenger, addPassenger, updateTravel, removeTravel}; + return {sendPassengerToWaitingList, updateTravel, removeTravel}; }; const formatPassengers = (passengers = [], seats: number = 1000) => {
M frontend/containers/TravelColumns/index.tsxfrontend/containers/TravelColumns/index.tsx

@@ -1,10 +1,17 @@

-import {useEffect, useReducer, useRef, useState} from 'react'; +import {useEffect, useMemo, useRef, useState} from 'react'; import {makeStyles} from '@material-ui/core/styles'; import Container from '@material-ui/core/Container'; import Slider from 'react-slick'; -import {Travel as TravelType} from '../../generated/graphql'; +import {useTranslation} from 'react-i18next'; +import { + Travel as TravelType, + useUpdateTravelMutation, +} from '../../generated/graphql'; import useEventStore from '../../stores/useEventStore'; import useTourStore from '../../stores/useTourStore'; +import useToastStore from '../../stores/useToastStore'; +import useProfile from '../../hooks/useProfile'; +import useAddToEvents from '../../hooks/useAddToEvents'; import { AddPassengerToTravel, AddPassengerToWaitingList,

@@ -13,6 +20,11 @@ import WaitingList from '../WaitingList';

import Travel from '../Travel'; import AddTravel from './AddTravel'; import sliderSettings from './_SliderSettings'; +import usePassengersActions from '../../hooks/usePassengersActions'; + +interface NewPassengerDialogContext { + addSelf: boolean; +} interface Props { toggleNewTravel: () => void;

@@ -22,11 +34,46 @@ const TravelColumns = (props: Props) => {

const event = useEventStore(s => s.event); const {travels = []} = event || {}; const slider = useRef(null); + const {t} = useTranslation(); const tourStep = useTourStore(s => s.step); + const addToast = useToastStore(s => s.addToast); + const [updateTravel] = useUpdateTravelMutation(); + const {addToEvent} = useAddToEvents(); + const {user} = useProfile(); const classes = useStyles(); - const [newPassengerTravel, toggleNewPassengerToTravel] = useState<TravelType | null>(null); - const [openNewPassengerToWaitingList, toggleNewPassengerToWaitingList] = - useReducer(i => !i, false); + const [newPassengerTravelContext, toggleNewPassengerToTravel] = useState<{ + travel: TravelType; + } | null>(null); + const [addPassengerToWaitingListContext, toggleNewPassengerToWaitingList] = + useState<NewPassengerDialogContext | null>(null); + const {addPassengerToTravel} = usePassengersActions(); + + const canAddSelf = useMemo(() => { + const isInWaitingList = event?.waitingList?.some( + passenger => passenger.user?.id === user.id + ); + const isInTravel = event?.travels.some(travel => + travel.passengers.some(passenger => passenger.user?.id === user.id) + ); + return !(isInWaitingList || isInTravel); + }, [event, user]); + + const addSelfToTravel = async (travel: TravelType) => { + const passenger = { + user: user, + email: user.email, + name: user.username, + }; + + return addPassengerToTravel({ + passenger, + travel, + onSucceed: () => { + addToEvent(event.id); + addToast(t('passenger.success.added_self_to_car')); + }, + }); + }; // On tour step changes : component update useEffect(() => {

@@ -39,7 +86,11 @@ <div className={classes.dots} id="slider-dots" />

<div className={classes.slider}> <Slider ref={slider} {...sliderSettings}> <Container maxWidth="sm" className={classes.slide}> - <WaitingList toggleNewPassenger={toggleNewPassengerToWaitingList} /> + <WaitingList + canAddSelf={canAddSelf} + getToggleNewPassengerDialogFunction={(addSelf: boolean) => () => + toggleNewPassengerToWaitingList({addSelf})} + /> </Container> {travels ?.slice()

@@ -53,9 +104,14 @@ >

<Travel travel={travel} {...props} - toggleNewPassenger={() => - toggleNewPassengerToTravel(travel) - } + canAddSelf={canAddSelf} + getAddPassengerFunction={(addSelf: boolean) => () => { + if (addSelf) { + return addSelfToTravel(travel); + } else { + return toggleNewPassengerToTravel({travel}); + } + }} /> </Container> ))}

@@ -64,17 +120,20 @@ <AddTravel {...props} />

</Container> </Slider> </div> - {!!newPassengerTravel && ( + {!!newPassengerTravelContext && ( <AddPassengerToTravel - travel={newPassengerTravel} - open={!!newPassengerTravel} + open={!!newPassengerTravelContext} toggle={() => toggleNewPassengerToTravel(null)} + travel={newPassengerTravelContext.travel} /> )} - <AddPassengerToWaitingList - open={openNewPassengerToWaitingList} - toggle={toggleNewPassengerToWaitingList} - /> + {!!addPassengerToWaitingListContext && ( + <AddPassengerToWaitingList + open={!!addPassengerToWaitingListContext} + toggle={() => toggleNewPassengerToWaitingList(null)} + addSelf={addPassengerToWaitingListContext.addSelf} + /> + )} </div> ); };
M frontend/containers/WaitingList/index.tsxfrontend/containers/WaitingList/index.tsx

@@ -7,11 +7,6 @@ import Paper from '@material-ui/core/Paper';

import Divider from '@material-ui/core/Divider'; import clsx from 'clsx'; import {Trans, useTranslation} from 'react-i18next'; -import { - useUpdateEventMutation, - useUpdateTravelMutation, - ComponentPassengerPassenger, -} from '../../generated/graphql'; import useToastStore from '../../stores/useToastStore'; import useEventStore from '../../stores/useEventStore'; import useAddToEvents from '../../hooks/useAddToEvents';

@@ -21,12 +16,17 @@ import AddPassengerButtons from '../AddPassengerButtons';

import TravelDialog from './TravelDialog'; import ClearButton from '../ClearButton'; import AssignButton from './AssignButton'; +import usePassengersActions from '../../hooks/usePassengersActions'; + +interface Props { + getToggleNewPassengerDialogFunction: (addSelf: boolean) => () => void; + canAddSelf: boolean; +} const WaitingList = ({ - toggleNewPassenger, -}: { - toggleNewPassenger: () => void; -}) => { + getToggleNewPassengerDialogFunction, + canAddSelf, +}: Props) => { const classes = useStyles(); const {t} = useTranslation(); const event = useEventStore(s => s.event);

@@ -37,87 +37,40 @@ const [removingPassenger, setRemovingPassenger] = useState(null);

const [addingPassenger, setAddingPassenger] = useState(null); const travels = event?.travels?.length > 0 ? event.travels.slice().sort(sortTravels) : []; - const [updateEvent] = useUpdateEventMutation(); - const [updateTravel] = useUpdateTravelMutation(); + const {addPassengerToTravel, removePassengerFromWaitingList} = usePassengersActions(); const availability = useMemo(() => { if (!travels) return; - return travels.reduce((count, {seats, passengers = []}) => { - if (!passengers) return count + seats; - return count + seats - passengers.length; + return travels.reduce((count, {vehicle, passengers = []}) => { + if (!passengers) return count + vehicle.seats; + return count + vehicle.seats - passengers.length; }, 0); }, [travels]); - const addPassenger = useCallback( - async passenger => { - try { - const waitingList = [...event.waitingList, passenger].map( - ({__typename, ...item}) => item - ); - await updateEvent({ - variables: {uuid: event.uuid, eventUpdate: {waitingList}}, - refetchQueries: ['eventByUUID'], - }); - addToEvent(event.id); - } catch (error) { - console.error(error); - addToast(t('passenger.errors.cant_add_passenger')); - } - }, - [event] - ); - - const removePassenger = useCallback( - async (removingPassenger: ComponentPassengerPassenger) => { - try { - const waitingList = event.waitingList - .filter(passenger => passenger.id !== removingPassenger?.id) - .map(({__typename, ...item}) => item); - await updateEvent({ - variables: {uuid: event.uuid, eventUpdate: {waitingList}}, - refetchQueries: ['eventByUUID'], - }); - addToEvent(event.id); - } catch (error) { - console.error(error); - addToast(t('passenger.errors.cant_remove_passenger')); - } - }, - [event] - ); + const removePassengerFromWaitingListFallBack = useCallback(removePassengerFromWaitingList, [event]); const selectTravel = useCallback( async travel => { - try { - const {id, ...passenger} = addingPassenger; - const travelPassengers = [...(travel.passengers || []), passenger].map( - ({__typename, ...item}) => item - ); - await updateTravel({ - variables: { - id: travel.id, - travelUpdate: { - passengers: travelPassengers, - }, - }, - }); - const waitingList = event.waitingList - .filter(item => item.id !== id) - .map(({__typename, ...item}) => item); - await updateEvent({ - variables: { - uuid: event.uuid, - eventUpdate: { - waitingList, + const {id, ...passenger} = addingPassenger; + const onError = () => addToast(t('passenger.errors.cant_select_travel')); + addPassengerToTravel({ + travel, + passenger, + onError, + onSucceed: () => + removePassengerFromWaitingListFallBack({ + passenger: addingPassenger, + event: { + ...event, + waitingList: event.waitingList.filter( + item => item.id !== addingPassenger.id + ), }, - }, - refetchQueries: ['eventByUUID'], - }); - } catch (error) { - console.error(error); - addToast(t('passenger.errors.cant_select_travel')); - } - setAddingPassenger(null); + + onError, + onSucceed: () => setAddingPassenger(null), + }), + }); }, [event, addingPassenger] // eslint-disable-line );

@@ -160,11 +113,13 @@ {t('passenger.availability.seats', {count: availability})}

</Typography> </div> <Divider /> - <AddPassengerButtons toggleNewPassenger={toggleNewPassenger} /> + <AddPassengerButtons + getOnClickFunction={getToggleNewPassengerDialogFunction} + canAddSelf={canAddSelf} + /> <Divider /> <PassengersList passengers={event.waitingList} - addPassenger={addPassenger} onPress={onPress} Button={ListButton} disabled={!isEditing && availability <= 0}

@@ -182,7 +137,15 @@ />

} open={!!removingPassenger} onClose={() => setRemovingPassenger(null)} - onRemove={() => removePassenger(removingPassenger)} + onRemove={() => + removePassengerFromWaitingListFallBack({ + passenger: removingPassenger, + event, + onSucceed: () => addToEvent(event.id), + onError: () => + addToast(t('passenger.errors.cant_remove_passenger')), + }) + } /> <TravelDialog travels={travels}
A frontend/hooks/usePassengersActions.ts

@@ -0,0 +1,119 @@

+import { + Travel, + Event, + useUpdateTravelMutation, + useUpdateEventMutation, + ComponentPassengerPassenger, + ComponentPassengerPassengerInput, +} from '../generated/graphql'; + +interface AddPassengerToTravelArgs { + passenger: ComponentPassengerPassengerInput; + travel: Travel; + onSucceed?: () => void; + onError?: (error: string) => void; +} + +interface AddPassengerToWaitingList { + passenger: ComponentPassengerPassenger; + event: Event; + onSucceed?: () => void; + onError?: (error: string) => void; +} + +interface RemovePassengerArgs { + passenger: ComponentPassengerPassenger; + event: Event; + onSucceed?: () => void; + onError?: (error: string) => void; +} + +const usepassengersActions = () => { + const [updateEvent] = useUpdateEventMutation(); + const [updateTravel] = useUpdateTravelMutation(); + + const addPassengerToTravel = async ({ + passenger, + travel, + onSucceed, + onError, + }: AddPassengerToTravelArgs): Promise<void> => { + try { + const passengers = + [...travel.passengers, passenger].map(({__typename, user, ...item}) => { + return user && typeof user !== 'string' + ? { + ...item, + user: user.id, + } + : item; + }) || []; + + await updateTravel({ + variables: { + id: travel.id, + travelUpdate: { + passengers, + }, + }, + }); + return onSucceed && onSucceed(); + } catch (error) { + console.error(error); + return onError && onError(error); + } + }; + + const addPassengerToWaitingList = async ({ + passenger, + event, + onSucceed, + onError, + }: AddPassengerToWaitingList): Promise<void> => { + try { + const waitingList = [...event.waitingList, passenger].map( + ({__typename, user, ...item}) => + user ? {...item, user: user.id} : item + ); + await updateEvent({ + variables: {uuid: event.uuid, eventUpdate: {waitingList}}, + refetchQueries: ['eventByUUID'], + }); + return onSucceed && onSucceed(); + } catch (error) { + console.error(error); + return onError && onError(error); + } + }; + + const removePassengerFromWaitingList = async ({ + passenger, + event, + onSucceed, + onError, + }: RemovePassengerArgs): Promise<void> => { + try { + const waitingList = event.waitingList + .filter( + waitingListPassenger => waitingListPassenger.id !== passenger?.id + ) + .map(({__typename, user, ...item}) => item); + await updateEvent({ + variables: {uuid: event.uuid, eventUpdate: {waitingList}}, + refetchQueries: ['eventByUUID'], + }); + return onSucceed && onSucceed(); + } catch (error) { + console.error(error); + return onError && onError(error); + } + }; + + return { + addPassengerToTravel, + addPassengerToWaitingList, + removePassengerFromWaitingList, + }; +}; + +export default usepassengersActions;
M frontend/locales/en.jsonfrontend/locales/en.json

@@ -142,6 +142,7 @@ "passengers": {

"empty": "Available seat", "add": "Add a passenger", "add_to_car": "Add to car", + "add_me": "Add me", "register_to_waiting_list": "Register to waiting list", "add_someone": "Add someone", "location": "Meeting place",

@@ -221,6 +222,12 @@ "cant_add_passenger": "Unable to add a passenger",

"cant_save_passengers": "Unable to update passengers", "cant_remove_passenger": "Unable to remove the passenger", "cant_select_car": "Unable to select the car" + }, + "success": { + "added_self_to_car": "You have been added to this car", + "added_to_car": "{{name}} has been added to this car", + "added_self_to_waitlist": "You have been added to the waitlist. You'll be notified when new cars will be added.", + "added_to_waitlist": "{{name}} has been added to the waitlist" }, "input": { "email": "Your email",
M frontend/locales/fr.jsonfrontend/locales/fr.json

@@ -141,6 +141,7 @@ },

"passengers": { "empty": "Place disponible", "add": "Ajouter un passager", + "add_me": "S'ajouter", "add_to_car": "Ajouter à la voiture", "register_to_waiting_list": "Inscription à la liste d'attente", "add_someone": "Ajouter quelqu'un",

@@ -220,6 +221,12 @@ "cant_add_passenger": "Impossible d'ajouter un passager",

"cant_save_passengers": "Impossible de mettre à jour passagers", "cant_remove_passenger": "Impossible de retirer le passager", "cant_select_car": "Impossible de sélectionner la voiture" + }, + "success": { + "added_self_to_car": "Vous avez été ajouté à la voiture", + "added_to_car": "{{name}} a été ajouté à la voiture", + "added_self_to_waitlist": "Vous avez été ajouté à la liste d’attente. Vous serez notifié à l’ajout de nouvelles voitures", + "added_to_waitlist": "{{name}} ajouté à la liste d'attente" }, "input": { "email": "Votre email",