all repos — caroster @ 13d9c127957995a4b4451e509236ff82153976f2

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

feat: đŸ’„ Split car model to travel & vehicle

#240
Tim Izzo tim@octree.ch
Mon, 24 Jan 2022 14:41:56 +0100
commit

13d9c127957995a4b4451e509236ff82153976f2

parent

ac7c129ce3a5f103886fee0c738d5aab39af722a

40 files changed, 2007 insertions(+), 868 deletions(-)

jump to
M backend/api/event/models/event.settings.jsonbackend/api/event/models/event.settings.json

@@ -45,6 +45,10 @@ "waitingList": {

"type": "component", "repeatable": true, "component": "passenger.passenger" + }, + "travels": { + "via": "event", + "collection": "travel" } } }
A backend/api/travel/config/routes.json

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

+{ + "routes": [ + { + "method": "GET", + "path": "/travels", + "handler": "travel.find", + "config": { + "policies": [] + } + }, + { + "method": "GET", + "path": "/travels/count", + "handler": "travel.count", + "config": { + "policies": [] + } + }, + { + "method": "GET", + "path": "/travels/:id", + "handler": "travel.findOne", + "config": { + "policies": [] + } + }, + { + "method": "POST", + "path": "/travels", + "handler": "travel.create", + "config": { + "policies": [] + } + }, + { + "method": "PUT", + "path": "/travels/:id", + "handler": "travel.update", + "config": { + "policies": [] + } + }, + { + "method": "DELETE", + "path": "/travels/:id", + "handler": "travel.delete", + "config": { + "policies": [] + } + } + ] +}
A backend/api/travel/controllers/travel.js

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

+'use strict'; + +/** + * Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#core-controllers) + * to customize this controller + */ + +module.exports = {};
A backend/api/travel/models/travel.js

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

+'use strict'; + +const _uniq = require('lodash/uniq'); + +const {STRAPI_URL = ''} = process.env; + +module.exports = { + lifecycles: { + async afterCreate(result) { + sendEmailsToWaitingList(result); + }, + }, +}; + +const sendEmailsToWaitingList = async travel => { + const event = travel.event; + const eventWaitingList = event?.waitingList || []; + const userEmails = eventWaitingList.map(user => user.email).filter(Boolean); + const templateId = await strapi.plugins[ + 'email-designer' + ].services.template.getId('waitinglist_notif'); + + if (userEmails?.length > 0) + try { + await strapi.plugins['email-designer'].services.email.sendTemplatedEmail( + { + to: _uniq(userEmails), + }, + { + templateId, + }, + { + event, + travel, + eventLink: `${STRAPI_URL}/e/${event.uuid}`, + } + ); + } catch (error) { + console.error(error); + strapi.log.error( + `Impossible to send email waiting list notification for event #${ + event.id + }. Error: ${JSON.stringify(error)}` + ); + } +};
A backend/api/travel/models/travel.settings.json

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

+{ + "kind": "collectionType", + "collectionName": "travels", + "info": { + "name": "travel", + "description": "" + }, + "options": { + "increments": true, + "timestamps": true, + "draftAndPublish": false + }, + "attributes": { + "meeting": { + "type": "string" + }, + "departure": { + "type": "datetime" + }, + "details": { + "type": "text" + }, + "passengers": { + "type": "component", + "repeatable": true, + "component": "passenger.passenger" + }, + "event": { + "model": "event", + "via": "travels" + }, + "vehicle": { + "via": "travels", + "model": "vehicle" + } + } +}
A backend/api/travel/services/travel.js

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

+'use strict'; + +/** + * Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#core-services) + * to customize this service + */ + +module.exports = {};
A backend/api/vehicle/config/routes.json

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

+{ + "routes": [ + { + "method": "GET", + "path": "/vehicles", + "handler": "vehicle.find", + "config": { + "policies": [] + } + }, + { + "method": "GET", + "path": "/vehicles/count", + "handler": "vehicle.count", + "config": { + "policies": [] + } + }, + { + "method": "GET", + "path": "/vehicles/:id", + "handler": "vehicle.findOne", + "config": { + "policies": [] + } + }, + { + "method": "POST", + "path": "/vehicles", + "handler": "vehicle.create", + "config": { + "policies": [] + } + }, + { + "method": "PUT", + "path": "/vehicles/:id", + "handler": "vehicle.update", + "config": { + "policies": [] + } + }, + { + "method": "DELETE", + "path": "/vehicles/:id", + "handler": "vehicle.delete", + "config": { + "policies": [] + } + } + ] +}
A backend/api/vehicle/controllers/vehicle.js

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

+'use strict'; + +/** + * Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#core-controllers) + * to customize this controller + */ + +module.exports = {};
A backend/api/vehicle/models/vehicle.js

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

+'use strict'; + +/** + * Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#lifecycle-hooks) + * to customize this model + */ + +module.exports = {};
A backend/api/vehicle/models/vehicle.settings.json

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

+{ + "kind": "collectionType", + "collectionName": "vehicles", + "info": { + "name": "vehicle", + "description": "" + }, + "options": { + "increments": true, + "timestamps": true, + "draftAndPublish": false + }, + "attributes": { + "name": { + "type": "string", + "required": true + }, + "seats": { + "type": "integer", + "min": 0, + "default": 4 + }, + "user": { + "plugin": "users-permissions", + "model": "user", + "via": "vehicles" + }, + "travels": { + "via": "vehicle", + "collection": "travel" + }, + "phone_number": { + "type": "string" + } + } +}
A backend/api/vehicle/services/vehicle.js

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

+'use strict'; + +/** + * Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#core-services) + * to customize this service + */ + +module.exports = {};
M backend/config/permissions.jsonbackend/config/permissions.json

@@ -9,6 +9,14 @@ "name": "car",

"actions": ["create", "delete", "update"] }, { + "name": "travel", + "actions": ["create", "delete", "update"] + }, + { + "name": "vehicle", + "actions": ["create", "delete", "update"] + }, + { "name": "event", "actions": ["create", "update", "findone"] },

@@ -29,6 +37,14 @@ "type": "application",

"controllers": [ { "name": "car", + "actions": ["create", "delete", "update"] + }, + { + "name": "travel", + "actions": ["create", "delete", "update"] + }, + { + "name": "vehicle", "actions": ["create", "delete", "update"] }, {
A backend/migrations/car-travels.js

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

+/** + * AVANT DE LANCER CE SCRIPT + * DÉSACTIVER L'ENVOI DE MAIL LORS DE LA CRÉATION D'UN TRAVEL + */ + +const Strapi = require('strapi'); + +// Commenter les lignes suivantes si les emails sur le hook sont dĂ©sactivĂ©s +console.log( + "Avez-vous bien dĂ©sactivĂ© l'envoi d'emails Ă  la crĂ©ation d'un travel ?" +); +process.exit(1); + +const main = async () => { + await Strapi().load(); + + const cars = await strapi.services.car.find({_limit: -1}); + + for (let i = 0; i < cars.length; i++) { + try { + await splitCar(cars[i]); + } catch (error) { + console.error(error); + } + } + + strapi.log.debug('Done.'); + process.exit(0); +}; + +const splitCar = async car => { + const vehicle = { + name: car.name, + seats: car.seats, + phone_number: car.phone_number, + }; + + const newVehicle = await strapi.services.vehicle.create(vehicle); + + const travel = { + meeting: car.meeting, + departure: car.departure, + details: car.details, + passengers: car.passengers, + event: car.event?.id, + vehicle: newVehicle.id, + }; + + await strapi.services.travel.create(travel); +}; + +main();
D frontend/containers/Car/Header.tsx

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

-import Typography from '@material-ui/core/Typography'; -import IconButton from '@material-ui/core/IconButton'; -import Icon from '@material-ui/core/Icon'; -import {makeStyles} from '@material-ui/core/styles'; -import moment from 'moment'; -import {useTranslation} from 'react-i18next'; -import Link from '@material-ui/core/Link'; -import {Car} from '../../generated/graphql'; - -interface Props { - car: Car; - toggleEditing: () => void; -} - -const Header = (props: Props) => { - const {car, toggleEditing} = props; - const classes = useStyles(); - const {t} = useTranslation(); - - return ( - <div className={classes.header}> - <IconButton - size="small" - color="primary" - className={classes.editBtn} - onClick={toggleEditing} - id="EditCarBtn" - > - <Icon>edit</Icon> - </IconButton> - {!!car.departure && ( - <Typography variant="overline" id="CarDeparture"> - {moment(car.departure).format('LLLL')} - </Typography> - )} - <Typography variant="h5" id="CarName"> - {car.name} - </Typography> - {!!car.phone_number && ( - <div className={classes.section}> - <Typography variant="subtitle2">{t('car.fields.phone')}</Typography> - <Typography variant="body2" id="CarPhone"> - {car.phone_number} - </Typography> - </div> - )} - {!!car.meeting && ( - <div className={classes.section}> - <Typography variant="subtitle2"> - {t('car.fields.meeting_point')} - </Typography> - <Typography variant="body2" id="CarMeeting"> - <Link - component="a" - target="_blank" - rel="noopener noreferrer" - href={`https://maps.google.com/?q=${encodeURI(car.meeting)}`} - > - {car.meeting} - </Link> - </Typography> - </div> - )} - {!!car.details && ( - <div className={classes.section}> - <Typography variant="subtitle2">{t('car.fields.details')}</Typography> - <Typography variant="body2" id="CarDetails"> - {car.details} - </Typography> - </div> - )} - </div> - ); -}; - -const useStyles = makeStyles(theme => ({ - header: { - padding: theme.spacing(2), - }, - editBtn: { - position: 'absolute', - top: 0, - right: 0, - margin: theme.spacing(1), - zIndex: theme.zIndex.speedDial, - }, - section: { - marginTop: theme.spacing(2), - }, -})); - -export default Header;
D frontend/containers/Car/HeaderEditing.tsx

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

-import {useState, useReducer, useCallback, useEffect, useMemo} from 'react'; -import {makeStyles} from '@material-ui/core/styles'; -import Typography from '@material-ui/core/Typography'; -import IconButton from '@material-ui/core/IconButton'; -import Icon from '@material-ui/core/Icon'; -import Button from '@material-ui/core/Button'; -import TextField from '@material-ui/core/TextField'; -import Slider from '@material-ui/core/Slider'; -import {DatePicker, TimePicker} from '@material-ui/pickers'; -import moment from 'moment'; -import {useTranslation} from 'react-i18next'; -import useToastStore from '../../stores/useToastStore'; -import useEventStore from '../../stores/useEventStore'; -import RemoveDialog from '../RemoveDialog'; -import { - useUpdateEventMutation, - useUpdateCarMutation, - useDeleteCarMutation, -} from '../../generated/graphql'; - -const HeaderEditing = ({car, toggleEditing}) => { - const classes = useStyles(); - const {t} = useTranslation(); - const event = useEventStore(s => s.event); - const addToast = useToastStore(s => s.addToast); - const [updateEvent] = useUpdateEventMutation(); - const [updateCar] = useUpdateCarMutation(); - const [deleteCar] = useDeleteCarMutation({refetchQueries: ['eventByUUID']}); - const [removing, toggleRemoving] = useReducer(i => !i, false); - const dateMoment = useMemo(() => { - if (!car?.departure) return moment(); - else return moment(car.departure); - }, [car?.departure]); - - // States - const [name, setName] = useState(car?.name ?? ''); - const [seats, setSeats] = useState(car?.seats ?? 4); - const [meeting, setMeeting] = useState(car?.meeting ?? ''); - const [date, setDate] = useState(dateMoment); - const [time, setTime] = useState(dateMoment); - const [phone, setPhone] = useState(car ? car['phone_number'] : ''); - const [details, setDetails] = useState(car?.details ?? ''); - - // Click on ESQ closes the form - const escFunction = useCallback( - evt => { - if (evt.keyCode === 27) toggleEditing(); - }, - [toggleEditing] - ); - - useEffect(() => { - document.addEventListener('keydown', escFunction, false); - return () => { - document.removeEventListener('keydown', escFunction, false); - }; - }, [escFunction]); - - const onSave = async evt => { - if (evt.preventDefault) evt.preventDefault(); - try { - // If new seats count is under current passengers count, put excedent in event waiting list - if (!!car.passengers && car.passengers.length > seats) { - const lostPassengers = car.passengers.slice(seats); - if (lostPassengers.length > 0) - await updateEvent({ - variables: { - uuid: event.uuid, - eventUpdate: { - waitingList: formatPassengers([ - ...(event.waitingList || []), - ...lostPassengers.map(({name}) => ({name})), - ]), - }, - }, - refetchQueries: ['eventByUUID'], - }); - } - const departure = moment( - `${moment(date).format('YYYY-MM-DD')} ${moment(time).format('HH:mm')}`, - 'YYYY-MM-DD HH:mm' - ).toISOString(); - await updateCar({ - variables: { - id: car.id, - carUpdate: { - name, - seats, - meeting, - departure, - phone_number: phone, - details, - passengers: formatPassengers(car.passengers, seats), - }, - }, - }); - toggleEditing(); - } catch (error) { - console.error(error); - addToast('car.errors.cant_update'); - } - return false; - }; - - const onRemove = async () => { - try { - // Put passengers in event waiting list (if any) - if (Array.isArray(car?.passengers) && car.passengers.length > 0) - await updateEvent({ - variables: { - uuid: event.uuid, - eventUpdate: { - waitingList: formatPassengers([ - ...(event.waitingList || []), - ...car.passengers.map(({name}) => ({name})), - ]), - }, - }, - refetchQueries: ['eventByUUID'], - }); - await deleteCar({ - variables: { - id: car.id, - }, - }); - addToast(t('car.actions.removed')); - toggleEditing(); - } catch (error) { - console.error(error); - addToast('car.errors.cant_remove'); - } - }; - - return ( - <div className={classes.header}> - <form onSubmit={onSave}> - <IconButton - size="small" - color="primary" - type="submit" - className={classes.edit} - > - <Icon>done</Icon> - </IconButton> - <DatePicker - label={t('car.creation.date')} - fullWidth - helperText=" " - value={date} - onChange={setDate} - format="DD/MM/YYYY" - cancelLabel={t('generic.cancel')} - autoFocus - id="NewCarDate" - /> - <TimePicker - label={t('car.creation.time')} - fullWidth - helperText=" " - value={time} - onChange={setTime} - cancelLabel={t('generic.cancel')} - ampm={false} - minutesStep={5} - id="NewCarTime" - /> - <TextField - label={t('car.creation.name')} - fullWidth - helperText=" " - value={name} - onChange={e => setName(e.target.value)} - name="name" - id="EditCarName" - /> - <TextField - label={t('car.creation.phone')} - fullWidth - helperText=" " - value={phone} - onChange={e => setPhone(e.target.value)} - name="phone" - id="EditCarPhone" - /> - <TextField - label={t('car.creation.meeting')} - fullWidth - multiline - rowsMax={4} - inputProps={{maxLength: 250}} - helperText={`${meeting.length}/250`} - value={meeting} - onChange={e => setMeeting(e.target.value)} - name="meeting" - id="EditCarMeeting" - /> - <TextField - label={t('car.creation.notes')} - fullWidth - multiline - rowsMax={4} - inputProps={{maxLength: 250}} - helperText={`${details.length}/250`} - value={details} - onChange={e => setDetails(e.target.value)} - name="details" - id="EditCarDetails" - /> - <div className={classes.slider}> - <Typography variant="caption">{t('car.creation.seats')}</Typography> - <Slider - value={seats} - onChange={(e, value) => setSeats(value)} - step={1} - marks={[1, 2, 3, 4, 5, 6, 7, 8].map(value => ({ - value, - label: value, - }))} - min={1} - max={8} - valueLabelDisplay="auto" - id="EditCarSeats" - /> - </div> - </form> - <div className={classes.actions}> - <Button - variant="outlined" - color="primary" - onClick={onSave} - id="CarSave" - > - {t('generic.save')} - </Button> - <Button - variant="outlined" - color="primary" - onClick={toggleRemoving} - id="CarRemove" - > - {t('generic.remove')} - </Button> - </div> - <RemoveDialog - text={t('car.actions.remove_alert')} - open={removing} - onClose={toggleRemoving} - onRemove={onRemove} - /> - </div> - ); -}; - -const formatPassengers = (passengers = [], seats: number = 1000) => { - if (!passengers) return []; - - return passengers - .slice(0, seats) - .map(({__typename, ...passenger}) => passenger); -}; - -const useStyles = makeStyles(theme => ({ - header: { - padding: theme.spacing(2), - }, - edit: { - position: 'absolute', - top: 0, - right: 0, - margin: theme.spacing(1), - zIndex: theme.zIndex.speedDial, - }, - section: { - marginTop: theme.spacing(2), - }, - slider: { - marginTop: theme.spacing(2), - }, - actions: { - display: 'flex', - flexDirection: 'column', - justifyContent: 'center', - margin: theme.spacing(2, 0), - '& > *:first-child': { - marginBottom: theme.spacing(2), - }, - }, -})); - -export default HeaderEditing;
D frontend/containers/Car/index.tsx

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

-import {useReducer} from 'react'; -import {makeStyles} from '@material-ui/core/styles'; -import Divider from '@material-ui/core/Divider'; -import Paper from '@material-ui/core/Paper'; -import {useTranslation} from 'react-i18next'; -import PassengersList from '../PassengersList'; -import HeaderEditing from './HeaderEditing'; -import Header from './Header'; -import useEventStore from '../../stores/useEventStore'; -import useToastStore from '../../stores/useToastStore'; -import useAddToEvents from '../../hooks/useAddToEvents'; -import { - useUpdateCarMutation, - useUpdateEventMutation, - Car as CarType, - EditComponentPassengerPassengerInput as PassengerInput, -} from '../../generated/graphql'; - -interface Props { - car: CarType; -} - -const Car = (props: Props) => { - const {car} = props; - const classes = useStyles(); - const {t} = useTranslation(); - const event = useEventStore(s => s.event); - const addToast = useToastStore(s => s.addToast); - const [isEditing, toggleEditing] = useReducer(i => !i, false); - const [updateEvent] = useUpdateEventMutation(); - const [updateCar] = useUpdateCarMutation(); - const {addToEvent} = useAddToEvents(); - - if (!car) return null; - - const addPassenger = async (passenger: PassengerInput) => { - try { - const existingPassengers = - car.passengers?.map(({__typename, ...item}) => item) || []; - const passengers = [...existingPassengers, passenger]; - await updateCar({ - variables: { - id: car.id, - carUpdate: { - passengers, - }, - }, - }); - addToEvent(event.id); - } catch (error) { - console.error(error); - } - }; - - const removePassenger = async (passengerId: string) => { - if (car?.passengers) { - try { - const {id, ...removedPassenger} = - car.passengers?.find(item => item.id === passengerId) || {}; - const existingPassengers = - car.passengers?.map(({__typename, ...item}) => item) || []; - const waitingList = [...event.waitingList, removedPassenger].map( - ({__typename, ...item}) => item - ); - const passengers = existingPassengers.filter( - item => item.id !== passengerId - ); - await updateEvent({ - variables: { - uuid: event.uuid, - eventUpdate: { - waitingList, - }, - }, - }); - await updateCar({ - variables: { - id: car.id, - carUpdate: { - passengers, - }, - }, - refetchQueries: ['eventByUUID'], - }); - } catch (error) { - console.error(error); - addToast(t('car.errors.cant_remove_passenger')); - } - } - }; - - return ( - <Paper className={classes.root}> - {isEditing ? ( - <HeaderEditing car={car} toggleEditing={toggleEditing} /> - ) : ( - <Header car={car} toggleEditing={toggleEditing} /> - )} - <Divider /> - {!isEditing && ( - <PassengersList - passengers={car.passengers} - places={car.seats} - addPassenger={addPassenger} - onClick={removePassenger} - icon="close" - isCar - /> - )} - </Paper> - ); -}; - -const useStyles = makeStyles(theme => ({ - root: { - position: 'relative', - }, -})); - -export default Car;
M frontend/containers/CarColumns/AddCar.tsxfrontend/containers/TravelColumns/AddTravel.tsx

@@ -5,11 +5,11 @@ import {makeStyles} from '@material-ui/core';

import {useTranslation} from 'react-i18next'; interface Props { - toggleNewCar: () => void; + toggleNewTravel: () => void; } -const AddCar = (props: Props) => { - const {toggleNewCar} = props; +const AddTravel = (props: Props) => { + const {toggleNewTravel} = props; const classes = useStyles(); const {t} = useTranslation(); return (

@@ -19,9 +19,9 @@ classes={{containedSecondary: classes.button}}

fullWidth variant="contained" color="primary" - onClick={toggleNewCar} + onClick={toggleNewTravel} > - {t('car.creation.title')} + {t('travel.creation.title')} </Button> </Container> );

@@ -40,4 +40,4 @@ '&:hover': {color: theme.palette.secondary.contrastText},

}, })); -export default AddCar; +export default AddTravel;
M frontend/containers/CarColumns/index.tsxfrontend/containers/TravelColumns/index.tsx

@@ -2,21 +2,21 @@ import {useEffect, useRef} from 'react';

import {makeStyles} from '@material-ui/core/styles'; import Container from '@material-ui/core/Container'; import Slider from 'react-slick'; -import {Car as CarType} from '../../generated/graphql'; +import {Travel as TravelType} from '../../generated/graphql'; import useEventStore from '../../stores/useEventStore'; import useTourStore from '../../stores/useTourStore'; import WaitingList from '../WaitingList'; -import Car from '../Car'; -import AddCar from './AddCar'; +import Travel from '../Travel'; +import AddTravel from './AddTravel'; import sliderSettings from './_SliderSettings'; interface Props { - toggleNewCar: () => void; + toggleNewTravel: () => void; } -const CarColumns = (props: Props) => { +const TravelColumns = (props: Props) => { const event = useEventStore(s => s.event); - const {cars} = event || {}; + const {travels = []} = event || {}; const slider = useRef(null); const tourStep = useTourStore(s => s.step); const classes = useStyles();

@@ -34,16 +34,20 @@ <Slider ref={slider} {...sliderSettings}>

<Container maxWidth="sm" className={classes.slide}> <WaitingList /> </Container> - {cars + {travels ?.slice() - .sort(sortCars) - .map(car => ( - <Container key={car.id} maxWidth="sm" className={classes.slide}> - <Car car={car} {...props} /> + .sort(sortTravels) + .map(travel => ( + <Container + key={travel.id} + maxWidth="sm" + className={classes.slide} + > + <Travel travel={travel} {...props} /> </Container> ))} <Container maxWidth="sm" className={classes.slide}> - <AddCar {...props} /> + <AddTravel {...props} /> </Container> </Slider> </div>

@@ -61,7 +65,7 @@ if (fromTo(2, 3) || fromTo(4, 3)) slider?.slickGoTo(0, true);

} else if (fromTo(0, 1)) slider?.slickGoTo(0, true); }; -const sortCars = (a: CarType, b: CarType) => { +const sortTravels = (a: TravelType, b: TravelType) => { if (!b) return 1; const dateA = new Date(a.departure).getTime(); const dateB = new Date(b.departure).getTime();

@@ -124,4 +128,4 @@ },

}, })); -export default CarColumns; +export default TravelColumns;
M frontend/containers/NewCarDialog/index.tsxfrontend/containers/NewTravelDialog/index.tsx

@@ -13,17 +13,14 @@ import {DatePicker, TimePicker} from '@material-ui/pickers';

import moment from 'moment'; import {useTranslation} from 'react-i18next'; import useEventStore from '../../stores/useEventStore'; -import useToastsStore from '../../stores/useToastStore'; -import useAddToEvents from '../../hooks/useAddToEvents'; -import {useCreateCarMutation} from '../../generated/graphql'; +import useActions from './useActions'; -const NewCarDialog = ({open, toggle}) => { +const NewTravelDialog = ({open, toggle}) => { const {t} = useTranslation(); const classes = useStyles(); - const addToast = useToastsStore(s => s.addToast); - const {addToEvent} = useAddToEvents(); const event = useEventStore(s => s.event); - const [createCar] = useCreateCarMutation({refetchQueries: ['eventByUUID']}); + const actions = useActions({event}); + const dateMoment = useMemo(() => { if (!event?.date) return moment(); else return moment(event.date);

@@ -41,40 +38,28 @@ const canCreate = !!name && !!seats;

const onCreate = async e => { if (e.preventDefault) e.preventDefault(); - try { - const departure = moment( - `${moment(date).format('YYYY-MM-DD')} ${moment(time).format('HH:mm')}`, - 'YYYY-MM-DD HH:mm' - ).toISOString(); - await createCar({ - variables: { - car: { - name, - seats, - meeting, - departure, - phone_number: phone, - details, - event: event.id, - }, - }, - }); - addToEvent(event.id); - addToast(t('car.creation.created')); - toggle(); - // Clear states - setName(''); - setSeats(4); - setMeeting(''); - setDate(moment()); - setPhone(''); - setDetails(''); - } catch (error) { - console.error(error); - addToast(t('car.errors.cant_create')); - } - return false; + const travel = { + meeting, + date, + time, + details, + vehicle: { + name, + seats, + phone_number: phone, + }, + }; + await actions.createTravel(travel); + toggle(); + + // Clear states + setName(''); + setSeats(4); + setMeeting(''); + setDate(moment()); + setPhone(''); + setDetails(''); }; return (

@@ -86,10 +71,10 @@ onClose={toggle}

TransitionComponent={Transition} > <form onSubmit={onCreate}> - <DialogTitle>{t('car.creation.title')}</DialogTitle> + <DialogTitle>{t('travel.creation.title')}</DialogTitle> <DialogContent> <DatePicker - label={t('car.creation.date')} + label={t('travel.creation.date')} fullWidth helperText=" " value={date}

@@ -97,10 +82,10 @@ onChange={setDate}

format="DD/MM/YYYY" cancelLabel={t('generic.cancel')} autoFocus - id="NewCarDateTime" + id="NewTravelDateTime" /> <TimePicker - label={t('car.creation.time')} + label={t('travel.creation.time')} fullWidth helperText=" " value={time}

@@ -108,28 +93,28 @@ onChange={setTime}

cancelLabel={t('generic.cancel')} ampm={false} minutesStep={5} - id="NewCarTime" + id="NewTravelTime" /> <TextField - label={t('car.creation.name')} + label={t('travel.creation.name')} fullWidth helperText=" " value={name} onChange={e => setName(e.target.value)} name="name" - id="NewCarName" + id="NewTravelName" /> <TextField - label={t('car.creation.phone')} + label={t('travel.creation.phone')} fullWidth helperText=" " value={phone} onChange={e => setPhone(e.target.value)} name="phone" - id="NewCarPhone" + id="NewTravelPhone" /> <TextField - label={t('car.creation.meeting')} + label={t('travel.creation.meeting')} fullWidth multiline rowsMax={4}

@@ -138,10 +123,10 @@ helperText={`${meeting.length}/250`}

value={meeting} onChange={e => setMeeting(e.target.value)} name="meeting" - id="NewCarMeeting" + id="NewTravelMeeting" /> <TextField - label={t('car.creation.notes')} + label={t('travel.creation.notes')} fullWidth multiline rowsMax={4}

@@ -150,10 +135,12 @@ helperText={`${details.length}/250`}

value={details} onChange={e => setDetails(e.target.value)} name="details" - id="NewCarDetails" + id="NewTravelDetails" /> <div className={classes.slider}> - <Typography variant="caption">{t('car.creation.seats')}</Typography> + <Typography variant="caption"> + {t('travel.creation.seats')} + </Typography> <Slider value={seats} onChange={(e, value) => setSeats(value)}

@@ -162,14 +149,14 @@ marks={MARKS}

min={1} max={MARKS.length} valueLabelDisplay="auto" - id="NewCarSeats" + id="NewTravelSeats" /> </div> </DialogContent> <DialogActions> <Button color="primary" - id="NewCarCancel" + id="NewTravelCancel" onClick={toggle} tabIndex={-1} >

@@ -181,7 +168,7 @@ variant="contained"

type="submit" disabled={!canCreate} aria-disabled={!canCreate} - id="NewCarSubmit" + id="NewTravelSubmit" > {t('generic.create')} </Button>

@@ -206,4 +193,4 @@ marginTop: theme.spacing(2),

}, })); -export default NewCarDialog; +export default NewTravelDialog;
A frontend/containers/NewTravelDialog/useActions.ts

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

+import moment from 'moment'; +import {useTranslation} from 'react-i18next'; +import useToastsStore from '../../stores/useToastStore'; +import useAddToEvents from '../../hooks/useAddToEvents'; +import { + Event, + EventByUuidDocument, + useCreateTravelMutation, + useCreateVehicleMutation, +} from '../../generated/graphql'; + +interface Props { + event: Event; +} + +const useActions = (props: Props) => { + const {event} = props; + const {t} = useTranslation(); + const addToast = useToastsStore(s => s.addToast); + const {addToEvent} = useAddToEvents(); + const [createVehicleMutation] = useCreateVehicleMutation(); + const [createTravelMutation] = useCreateTravelMutation(); + + // TODO Move these logics to backend + const createTravel = async ({vehicle, date, time, ...travel}) => { + try { + const departure = moment( + `${moment(date).format('YYYY-MM-DD')} ${moment(time).format('HH:mm')}`, + 'YYYY-MM-DD HH:mm' + ).toISOString(); + const {data: {createVehicle} = {}} = await createVehicleMutation({ + variables: { + vehicle, + }, + }); + + await createTravelMutation({ + variables: { + travel: { + ...travel, + departure, + event: event.id, + vehicle: createVehicle?.vehicle?.id, + }, + }, + refetchQueries: [ + { + query: EventByUuidDocument, + variables: { + uuid: event.uuid, + }, + }, + ], + }); + + addToEvent(event.id); + addToast(t('travel.creation.created')); + } catch (error) { + console.error(error); + addToast(t('travel.errors.cant_create')); + } + return false; + }; + + return {createTravel}; +}; + +export default useActions;
M frontend/containers/PassengersList/Input.tsxfrontend/containers/PassengersList/Input.tsx

@@ -12,11 +12,11 @@

interface Props { addPassenger: (passenger: PassengerInput) => void; id: number; - isCar?: boolean; + isTravel?: boolean; } const Input = (props: Props) => { - const {addPassenger, id, isCar} = props; + const {addPassenger, id, isTravel} = props; const [name, setName] = useState(''); const [email, setEmail] = useState(''); const [error, setError] = useState<string>();

@@ -45,7 +45,7 @@ value={name}

onChange={e => setName(e.target.value)} onKeyDown={onKeyDown} fullWidth - label={t('car.passengers.add')} + label={t('travel.passengers.add')} id={`NewPassenger-${id}-name`} name={`passenger-${id}-name`} />

@@ -70,8 +70,8 @@ label={t`passenger.input.email`}

id={`NewPassenger-${id}-email`} name={`passenger-${id}-email`} helperText={ - isCar - ? t`passenger.input.email_helper_car` + isTravel + ? t`passenger.input.email_helper_travel` : t`passenger.input.email_helper` } error={error === 'email'}
M frontend/containers/PassengersList/Passenger.tsxfrontend/containers/PassengersList/Passenger.tsx

@@ -33,7 +33,7 @@ <Icon>person</Icon>

</ListItemIcon> </ListItemAvatar> <ListItemText - primary={t('car.passengers.empty')} + primary={t('travel.passengers.empty')} classes={{ root: classes.empty, }}
M frontend/containers/PassengersList/index.tsxfrontend/containers/PassengersList/index.tsx

@@ -14,7 +14,7 @@ interface Props {

passengers: ComponentPassengerPassenger[]; icon: string; disabled?: boolean; - isCar?: boolean; + isVehicle?: boolean; places?: number; onPress?: (passengerId: string) => void; onClick?: (passengerId: string) => void;

@@ -30,7 +30,7 @@ icon,

onClick, onPress, disabled, - isCar, + isVehicle, } = props; const classes = useStyles(); let list = passengers;

@@ -51,8 +51,8 @@ : places > 0

: true) && ( <Input addPassenger={addPassenger} - id={!!places ? 'Car' : 'Waiting'} - isCar={isCar} + id={!!places ? 'Vehicle' : 'Waiting'} + isVehicle={isVehicle} /> )} <List disablePadding>
A frontend/containers/Travel/Header.tsx

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

+import Typography from '@material-ui/core/Typography'; +import IconButton from '@material-ui/core/IconButton'; +import Icon from '@material-ui/core/Icon'; +import {makeStyles} from '@material-ui/core/styles'; +import moment from 'moment'; +import {useTranslation} from 'react-i18next'; +import Link from '@material-ui/core/Link'; +import {Travel} from '../../generated/graphql'; + +interface Props { + travel: Travel; + toggleEditing: () => void; +} + +const Header = (props: Props) => { + const {travel, toggleEditing} = props; + const classes = useStyles(); + const {t} = useTranslation(); + + return ( + <div className={classes.header}> + <IconButton + size="small" + color="primary" + className={classes.editBtn} + onClick={toggleEditing} + id="EditTravelBtn" + > + <Icon>edit</Icon> + </IconButton> + {!!travel.departure && ( + <Typography variant="overline" id="TravelDeparture"> + {moment(travel.departure).format('LLLL')} + </Typography> + )} + <Typography variant="h5" id="TravelName"> + {travel.vehicle?.name} + </Typography> + {!!travel.vehicle?.phone_number && ( + <div className={classes.section}> + <Typography variant="subtitle2"> + {t('travel.fields.phone')} + </Typography> + <Typography variant="body2" id="TravelPhone"> + {travel.vehicle?.phone_number} + </Typography> + </div> + )} + {!!travel.meeting && ( + <div className={classes.section}> + <Typography variant="subtitle2"> + {t('travel.fields.meeting_point')} + </Typography> + <Typography variant="body2" id="TravelMeeting"> + <Link + component="a" + target="_blank" + rel="noopener noreferrer" + href={`https://maps.google.com/?q=${encodeURI(travel.meeting)}`} + > + {travel.meeting} + </Link> + </Typography> + </div> + )} + {!!travel.details && ( + <div className={classes.section}> + <Typography variant="subtitle2"> + {t('travel.fields.details')} + </Typography> + <Typography variant="body2" id="TravelDetails"> + {travel.details} + </Typography> + </div> + )} + </div> + ); +}; + +const useStyles = makeStyles(theme => ({ + header: { + padding: theme.spacing(2), + }, + editBtn: { + position: 'absolute', + top: 0, + right: 0, + margin: theme.spacing(1), + zIndex: theme.zIndex.speedDial, + }, + section: { + marginTop: theme.spacing(2), + }, +})); + +export default Header;
A frontend/containers/Travel/HeaderEditing.tsx

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

+import {useState, useReducer, useCallback, useEffect, useMemo} from 'react'; +import {makeStyles} from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; +import IconButton from '@material-ui/core/IconButton'; +import Icon from '@material-ui/core/Icon'; +import Button from '@material-ui/core/Button'; +import TextField from '@material-ui/core/TextField'; +import Slider from '@material-ui/core/Slider'; +import {DatePicker, TimePicker} from '@material-ui/pickers'; +import moment from 'moment'; +import {useTranslation} from 'react-i18next'; +import useToastStore from '../../stores/useToastStore'; +import useEventStore from '../../stores/useEventStore'; +import RemoveDialog from '../RemoveDialog'; +import { + useUpdateEventMutation, + useDeleteTravelMutation, +} from '../../generated/graphql'; +import useActions from './useActions'; + +const HeaderEditing = ({travel, toggleEditing}) => { + const classes = useStyles(); + const {t} = useTranslation(); + const event = useEventStore(s => s.event); + const addToast = useToastStore(s => s.addToast); + const actions = useActions({travel}); + const [updateEvent] = useUpdateEventMutation(); + const [deleteTravel] = useDeleteTravelMutation({ + refetchQueries: ['eventByUUID'], + }); + const [removing, toggleRemoving] = useReducer(i => !i, false); + const dateMoment = useMemo(() => { + if (!travel?.departure) return moment(); + else return moment(travel.departure); + }, [travel?.departure]); + + // States + const [name, setName] = useState(travel?.vehicle?.name ?? ''); + const [seats, setSeats] = useState(travel?.vehicle?.seats ?? 4); + const [meeting, setMeeting] = useState(travel?.meeting ?? ''); + const [date, setDate] = useState(dateMoment); + const [time, setTime] = useState(dateMoment); + const [phone, setPhone] = useState(travel?.vehicle?.phone_number ?? ''); + const [details, setDetails] = useState(travel?.details ?? ''); + + // Click on ESQ closes the form + const escFunction = useCallback( + evt => { + if (evt.keyCode === 27) toggleEditing(); + }, + [toggleEditing] + ); + + useEffect(() => { + document.addEventListener('keydown', escFunction, false); + return () => { + document.removeEventListener('keydown', escFunction, false); + }; + }, [escFunction]); + + const onSave = async event => { + if (event.preventDefault) event.preventDefault(); + const update = { + vehicle: { + name, + seats, + phone_number: phone, + }, + travel: {meeting, date, time, details}, + }; + await actions.updateTravel(update); + toggleEditing(); + }; + + const onRemove = async () => { + await actions.removeTravel(); + toggleEditing(); + }; + + return ( + <div className={classes.header}> + <form onSubmit={onSave}> + <IconButton + size="small" + color="primary" + type="submit" + className={classes.edit} + > + <Icon>done</Icon> + </IconButton> + <DatePicker + label={t('travel.creation.date')} + fullWidth + helperText=" " + value={date} + onChange={setDate} + format="DD/MM/YYYY" + cancelLabel={t('generic.cancel')} + autoFocus + id="NewTravelDate" + /> + <TimePicker + label={t('travel.creation.time')} + fullWidth + helperText=" " + value={time} + onChange={setTime} + cancelLabel={t('generic.cancel')} + ampm={false} + minutesStep={5} + id="NewTravelTime" + /> + <TextField + label={t('travel.creation.name')} + fullWidth + helperText=" " + value={name} + onChange={e => setName(e.target.value)} + name="name" + id="EditTravelName" + /> + <TextField + label={t('travel.creation.phone')} + fullWidth + helperText=" " + value={phone} + onChange={e => setPhone(e.target.value)} + name="phone" + id="EditTravelPhone" + /> + <TextField + label={t('travel.creation.meeting')} + fullWidth + multiline + rowsMax={4} + inputProps={{maxLength: 250}} + helperText={`${meeting.length}/250`} + value={meeting} + onChange={e => setMeeting(e.target.value)} + name="meeting" + id="EditTravelMeeting" + /> + <TextField + label={t('travel.creation.notes')} + fullWidth + multiline + rowsMax={4} + inputProps={{maxLength: 250}} + helperText={`${details.length}/250`} + value={details} + onChange={e => setDetails(e.target.value)} + name="details" + id="EditTravelDetails" + /> + <div className={classes.slider}> + <Typography variant="caption"> + {t('travel.creation.seats')} + </Typography> + <Slider + value={seats} + onChange={(e, value) => setSeats(value)} + step={1} + marks={[1, 2, 3, 4, 5, 6, 7, 8].map(value => ({ + value, + label: value, + }))} + min={1} + max={8} + valueLabelDisplay="auto" + id="EditTravelSeats" + /> + </div> + </form> + <div className={classes.actions}> + <Button + variant="outlined" + color="primary" + onClick={onSave} + id="TravelSave" + > + {t('generic.save')} + </Button> + <Button + variant="outlined" + color="primary" + onClick={toggleRemoving} + id="TravelRemove" + > + {t('generic.remove')} + </Button> + </div> + <RemoveDialog + text={t('travel.actions.remove_alert')} + open={removing} + onClose={toggleRemoving} + onRemove={onRemove} + /> + </div> + ); +}; + +const useStyles = makeStyles(theme => ({ + header: { + padding: theme.spacing(2), + }, + edit: { + position: 'absolute', + top: 0, + right: 0, + margin: theme.spacing(1), + zIndex: theme.zIndex.speedDial, + }, + section: { + marginTop: theme.spacing(2), + }, + slider: { + marginTop: theme.spacing(2), + }, + actions: { + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + margin: theme.spacing(2, 0), + '& > *:first-child': { + marginBottom: theme.spacing(2), + }, + }, +})); + +export default HeaderEditing;
A frontend/containers/Travel/index.tsx

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

+import {useReducer} from 'react'; +import {makeStyles} from '@material-ui/core/styles'; +import Divider from '@material-ui/core/Divider'; +import Paper from '@material-ui/core/Paper'; +import PassengersList from '../PassengersList'; +import HeaderEditing from './HeaderEditing'; +import Header from './Header'; + +import {Travel as TravelType} from '../../generated/graphql'; +import useActions from './useActions'; + +interface Props { + travel: TravelType; +} + +const Travel = (props: Props) => { + const {travel} = props; + const classes = useStyles(); + const [isEditing, toggleEditing] = useReducer(i => !i, false); + const actions = useActions({travel}); + + if (!travel) return null; + + return ( + <Paper className={classes.root}> + {isEditing ? ( + <HeaderEditing travel={travel} toggleEditing={toggleEditing} /> + ) : ( + <Header travel={travel} toggleEditing={toggleEditing} /> + )} + <Divider /> + {!isEditing && ( + <PassengersList + passengers={travel.passengers} + places={travel?.vehicle?.seats} + addPassenger={actions.addPassenger} + onClick={actions.removePassenger} + icon="close" + isTravel + /> + )} + </Paper> + ); +}; + +const useStyles = makeStyles(theme => ({ + root: { + position: 'relative', + }, +})); + +export default Travel;
A frontend/containers/Travel/useActions.ts

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

+import {useTranslation} from 'react-i18next'; +import moment from 'moment'; +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'; + +interface Props { + travel: Travel; +} + +const useActions = (props: Props) => { + const {travel} = props; + const {t} = useTranslation(); + const event = useEventStore(s => s.event); + const addToast = useToastStore(s => s.addToast); + 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) => { + if (travel?.passengers) { + try { + const {id, ...removedPassenger} = + travel.passengers?.find(item => item.id === passengerId) || {}; + const existingPassengers = + travel.passengers?.map(({__typename, ...item}) => item) || []; + const waitingList = [...event.waitingList, removedPassenger].map( + ({__typename, ...item}) => item + ); + const passengers = existingPassengers.filter( + item => item.id !== passengerId + ); + await updateEventMutation({ + variables: { + uuid: event.uuid, + eventUpdate: { + waitingList, + }, + }, + }); + await updateTravelMutation({ + variables: { + id: travel.id, + travelUpdate: { + passengers, + }, + }, + refetchQueries: ['eventByUUID'], + }); + } catch (error) { + console.error(error); + addToast(t('travel.errors.cant_remove_passenger')); + } + } + }; + + const updateTravel = async update => { + try { + // If new seats count is under current passengers count, put excedent in event waiting list + // TODO: Move these logics to backend + if (travel.passengers?.length > update.vehicle?.seats) { + const lostPassengers = travel.passengers.slice(update.vehicle?.seats); + if (lostPassengers.length > 0) + await updateEventMutation({ + variables: { + uuid: event.uuid, + eventUpdate: { + waitingList: formatPassengers([ + ...(event.waitingList || []), + ...lostPassengers.map(({name}) => ({name})), + ]), + }, + }, + refetchQueries: ['eventByUUID'], + }); + } + const departure = moment( + `${moment(update.travel.date).format('YYYY-MM-DD')} ${moment( + update.travel.time + ).format('HH:mm')}`, + 'YYYY-MM-DD HH:mm' + ).toISOString(); + await updateTravelMutation({ + variables: { + id: travel.id, + travelUpdate: { + departure, + passengers: formatPassengers( + travel.passengers, + travel.vehicle?.seats + ), + }, + }, + }); + await updateVehicleMutation({ + variables: { + id: travel?.vehicle?.id, + vehicleUpdate: update.vehicle, + }, + }); + } catch (error) { + console.error(error); + addToast('travel.errors.cant_update'); + } + return false; + }; + + const removeTravel = async () => { + try { + // Put passengers in event waiting list (if any) + // TODO Move these logics to backend and delete vehicle if no user linked + if (Array.isArray(travel?.passengers) && travel.passengers.length > 0) + await updateEventMutation({ + variables: { + uuid: event.uuid, + eventUpdate: { + waitingList: formatPassengers([ + ...(event.waitingList || []), + ...travel.passengers.map(({name}) => ({name})), + ]), + }, + }, + refetchQueries: [ + {query: EventByUuidDocument, variables: {uuid: event.uuid}}, + ], + }); + await deleteTravelMutation({ + variables: { + id: travel.id, + }, + }); + addToast(t('travel.actions.removed')); + } catch (error) { + console.error(error); + addToast('travel.errors.cant_remove'); + } + }; + + return {removePassenger, addPassenger, updateTravel, removeTravel}; +}; + +const formatPassengers = (passengers = [], seats: number = 1000) => { + if (!passengers) return []; + return passengers + .slice(0, seats) + .map(({__typename, ...passenger}) => passenger); +}; + +export default useActions;
M frontend/containers/WaitingList/CarDialog.jsfrontend/containers/WaitingList/TravelDialog.tsx

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

import {makeStyles} from '@material-ui/core/styles'; import {useTranslation} from 'react-i18next'; -const CarDialog = ({cars, open, onClose, onSelect}) => { +const TravelDialog = ({travels, open, onClose, onSelect}) => { const classes = useStyles(); const {t} = useTranslation();

@@ -31,19 +31,19 @@ </Toolbar>

</AppBar> <div className={classes.offset}> <List disablePadding> - {cars?.map((car, i) => { - const passengers = car.passengers ? car.passengers.length : 0; - const counter = `${passengers} / ${car.seats}`; + {travels?.map((travel, i) => { + const passengers = travel.passengers ? travel.passengers.length : 0; + const counter = `${passengers} / ${travel?.vehicle?.seats}`; return ( <ListItem key={i} button divider - disabled={passengers === car.seats} - onClick={() => onSelect(car)} + disabled={passengers === travel.seats} + onClick={() => onSelect(travel)} > <ListItemText - primary={car.name} + primary={travel.vehicle?.name} secondary={t('passenger.creation.seats', {seats: counter})} /> </ListItem>

@@ -65,4 +65,4 @@ paddingTop: theme.spacing(7),

}, })); -export default CarDialog; +export default TravelDialog;
M frontend/containers/WaitingList/index.tsxfrontend/containers/WaitingList/index.tsx

@@ -9,7 +9,7 @@ import clsx from 'clsx';

import {Trans, useTranslation} from 'react-i18next'; import { useUpdateEventMutation, - useUpdateCarMutation, + useUpdateTravelMutation, ComponentPassengerPassenger, } from '../../generated/graphql'; import useToastStore from '../../stores/useToastStore';

@@ -17,7 +17,7 @@ import useEventStore from '../../stores/useEventStore';

import useAddToEvents from '../../hooks/useAddToEvents'; import PassengersList from '../PassengersList'; import RemoveDialog from '../RemoveDialog'; -import CarDialog from './CarDialog'; +import TravelDialog from './TravelDialog'; const WaitingList = () => { const classes = useStyles();

@@ -28,17 +28,18 @@ const {addToEvent} = useAddToEvents();

const [isEditing, toggleEditing] = useReducer(i => !i, false); const [removingPassenger, setRemovingPassenger] = useState(null); const [addingPassenger, setAddingPassenger] = useState(null); - const cars = event?.cars?.length > 0 ? event.cars.slice().sort(sortCars) : []; + const travels = + event?.travels?.length > 0 ? event.travels.slice().sort(sortTravels) : []; const [updateEvent] = useUpdateEventMutation(); - const [updateCar] = useUpdateCarMutation(); + const [updateTravel] = useUpdateTravelMutation(); const availability = useMemo(() => { - if (!cars) return; - return cars.reduce((count, {seats, passengers = []}) => { + if (!travels) return; + return travels.reduce((count, {seats, passengers = []}) => { if (!passengers) return count + seats; return count + seats - passengers.length; }, 0); - }, [cars]); + }, [travels]); const addPassenger = useCallback( async passenger => {

@@ -78,18 +79,18 @@ },

[event] ); - const selectCar = useCallback( - async car => { + const selectTravel = useCallback( + async travel => { try { const {id, ...passenger} = addingPassenger; - const carPassengers = [...(car.passengers || []), passenger].map( + const travelPassengers = [...(travel.passengers || []), passenger].map( ({__typename, ...item}) => item ); - await updateCar({ + await updateTravel({ variables: { - id: car.id, - carUpdate: { - passengers: carPassengers, + id: travel.id, + travelUpdate: { + passengers: travelPassengers, }, }, });

@@ -107,7 +108,7 @@ refetchQueries: ['eventByUUID'],

}); } catch (error) { console.error(error); - addToast(t('passenger.errors.cant_select_car')); + addToast(t('passenger.errors.cant_select_travel')); } setAddingPassenger(null); },

@@ -166,17 +167,17 @@ open={!!removingPassenger}

onClose={() => setRemovingPassenger(null)} onRemove={() => removePassenger(removingPassenger)} /> - <CarDialog - cars={cars} + <TravelDialog + travels={travels} open={!!addingPassenger} onClose={() => setAddingPassenger(null)} - onSelect={selectCar} + onSelect={selectTravel} /> </> ); }; -const sortCars = (a, b) => { +const sortTravels = (a, b) => { const dateA = new Date(a.departure).getTime(); const dateB = new Date(b.departure).getTime(); if (dateA === dateB)
M frontend/generated/graphql.tsxfrontend/generated/graphql.tsx

@@ -46,6 +46,7 @@ phone_number?: Maybe<Scalars['String']>;

details?: Maybe<Scalars['String']>; event?: Maybe<Event>; passengers?: Maybe<Array<Maybe<ComponentPassengerPassenger>>>; + user?: Maybe<UsersPermissionsUser>; }; export type CarAggregator = {

@@ -145,6 +146,12 @@ key?: Maybe<Scalars['DateTime']>;

connection?: Maybe<CarConnection>; }; +export type CarConnectionUser = { + __typename?: 'CarConnectionUser'; + key?: Maybe<Scalars['ID']>; + connection?: Maybe<CarConnection>; +}; + export type CarGroupBy = { __typename?: 'CarGroupBy'; id?: Maybe<Array<Maybe<CarConnectionId>>>;

@@ -157,6 +164,7 @@ departure?: Maybe<Array<Maybe<CarConnectionDeparture>>>;

phone_number?: Maybe<Array<Maybe<CarConnectionPhone_Number>>>; details?: Maybe<Array<Maybe<CarConnectionDetails>>>; event?: Maybe<Array<Maybe<CarConnectionEvent>>>; + user?: Maybe<Array<Maybe<CarConnectionUser>>>; }; export type CarInput = {

@@ -168,6 +176,7 @@ phone_number?: Maybe<Scalars['String']>;

details?: Maybe<Scalars['String']>; event?: Maybe<Scalars['ID']>; passengers?: Maybe<Array<Maybe<ComponentPassengerPassengerInput>>>; + user?: Maybe<Scalars['ID']>; created_by?: Maybe<Scalars['ID']>; updated_by?: Maybe<Scalars['ID']>; };

@@ -177,11 +186,15 @@ __typename?: 'ComponentPassengerPassenger';

id: Scalars['ID']; name: Scalars['String']; email?: Maybe<Scalars['String']>; + location?: Maybe<Scalars['String']>; + user?: Maybe<UsersPermissionsUser>; }; export type ComponentPassengerPassengerInput = { name: Scalars['String']; email?: Maybe<Scalars['String']>; + location?: Maybe<Scalars['String']>; + user?: Maybe<Scalars['ID']>; };

@@ -206,6 +219,7 @@ __typename?: 'EmailDesignerEmailTemplate';

id: Scalars['ID']; created_at: Scalars['DateTime']; updated_at: Scalars['DateTime']; + sourceCodeToTemplateId?: Maybe<Scalars['Int']>; design?: Maybe<Scalars['JSON']>; name?: Maybe<Scalars['String']>; subject?: Maybe<Scalars['String']>;

@@ -216,6 +230,7 @@ tags?: Maybe<Scalars['JSON']>;

}; export type EmailTemplateInput = { + sourceCodeToTemplateId?: Maybe<Scalars['Int']>; design?: Maybe<Scalars['JSON']>; name?: Maybe<Scalars['String']>; subject?: Maybe<Scalars['String']>;

@@ -241,6 +256,7 @@ uuid?: Maybe<Scalars['String']>;

waitingList?: Maybe<Array<Maybe<ComponentPassengerPassenger>>>; cars?: Maybe<Array<Maybe<Car>>>; users?: Maybe<Array<Maybe<UsersPermissionsUser>>>; + travels?: Maybe<Array<Maybe<Travel>>>; };

@@ -259,6 +275,14 @@ start?: Maybe<Scalars['Int']>;

where?: Maybe<Scalars['JSON']>; }; + +export type EventTravelsArgs = { + sort?: Maybe<Scalars['String']>; + limit?: Maybe<Scalars['Int']>; + start?: Maybe<Scalars['Int']>; + where?: Maybe<Scalars['JSON']>; +}; + export type EventAggregator = { __typename?: 'EventAggregator'; count?: Maybe<Scalars['Int']>;

@@ -349,6 +373,7 @@ position?: Maybe<Scalars['JSON']>;

users?: Maybe<Array<Maybe<Scalars['ID']>>>; uuid?: Maybe<Scalars['String']>; waitingList?: Maybe<Array<Maybe<ComponentPassengerPassengerInput>>>; + travels?: Maybe<Array<Maybe<Scalars['ID']>>>; created_by?: Maybe<Scalars['ID']>; updated_by?: Maybe<Scalars['ID']>; newsletter?: Maybe<Scalars['Boolean']>;

@@ -408,7 +433,7 @@ };

-export type Morph = Dependency | Info | UsersPermissionsMe | UsersPermissionsMeRole | UsersPermissionsLoginPayload | UserPermissionsPasswordPayload | Car | CarConnection | CarAggregator | CarAggregatorSum | CarAggregatorAvg | CarAggregatorMin | CarAggregatorMax | CarGroupBy | CarConnectionId | CarConnectionCreated_At | CarConnectionUpdated_At | CarConnectionName | CarConnectionSeats | CarConnectionMeeting | CarConnectionDeparture | CarConnectionPhone_Number | CarConnectionDetails | CarConnectionEvent | CreateCarPayload | UpdateCarPayload | DeleteCarPayload | Event | EventConnection | EventAggregator | EventGroupBy | EventConnectionId | EventConnectionCreated_At | EventConnectionUpdated_At | EventConnectionName | EventConnectionEmail | EventConnectionDate | EventConnectionAddress | EventConnectionPosition | EventConnectionUuid | CreateEventPayload | UpdateEventPayload | DeleteEventPayload | Page | PageConnection | PageAggregator | PageGroupBy | PageConnectionId | PageConnectionCreated_At | PageConnectionUpdated_At | PageConnectionName | PageConnectionContent | PageConnectionType | CreatePagePayload | UpdatePagePayload | DeletePagePayload | Settings | UpdateSettingPayload | DeleteSettingPayload | EmailDesignerEmailTemplate | UploadFile | UploadFileConnection | UploadFileAggregator | UploadFileAggregatorSum | UploadFileAggregatorAvg | UploadFileAggregatorMin | UploadFileAggregatorMax | UploadFileGroupBy | UploadFileConnectionId | UploadFileConnectionCreated_At | UploadFileConnectionUpdated_At | UploadFileConnectionName | UploadFileConnectionAlternativeText | UploadFileConnectionCaption | UploadFileConnectionWidth | UploadFileConnectionHeight | UploadFileConnectionFormats | UploadFileConnectionHash | UploadFileConnectionExt | UploadFileConnectionMime | UploadFileConnectionSize | UploadFileConnectionUrl | UploadFileConnectionPreviewUrl | UploadFileConnectionProvider | UploadFileConnectionProvider_Metadata | DeleteFilePayload | UsersPermissionsPermission | UsersPermissionsRole | UsersPermissionsRoleConnection | UsersPermissionsRoleAggregator | UsersPermissionsRoleGroupBy | UsersPermissionsRoleConnectionId | UsersPermissionsRoleConnectionName | UsersPermissionsRoleConnectionDescription | UsersPermissionsRoleConnectionType | CreateRolePayload | UpdateRolePayload | DeleteRolePayload | UsersPermissionsUser | UsersPermissionsUserConnection | UsersPermissionsUserAggregator | UsersPermissionsUserGroupBy | UsersPermissionsUserConnectionId | UsersPermissionsUserConnectionCreated_At | UsersPermissionsUserConnectionUpdated_At | UsersPermissionsUserConnectionUsername | UsersPermissionsUserConnectionFirstName | UsersPermissionsUserConnectionLastName | UsersPermissionsUserConnectionEmail | UsersPermissionsUserConnectionProvider | UsersPermissionsUserConnectionConfirmed | UsersPermissionsUserConnectionBlocked | UsersPermissionsUserConnectionRole | UsersPermissionsUserConnectionOnboardingUser | UsersPermissionsUserConnectionOnboardingCreator | UsersPermissionsUserConnectionLang | CreateUserPayload | UpdateUserPayload | DeleteUserPayload | ComponentPassengerPassenger; +export type Morph = Dependency | Info | UsersPermissionsMe | UsersPermissionsMeRole | UsersPermissionsLoginPayload | UserPermissionsPasswordPayload | Car | CarConnection | CarAggregator | CarAggregatorSum | CarAggregatorAvg | CarAggregatorMin | CarAggregatorMax | CarGroupBy | CarConnectionId | CarConnectionCreated_At | CarConnectionUpdated_At | CarConnectionName | CarConnectionSeats | CarConnectionMeeting | CarConnectionDeparture | CarConnectionPhone_Number | CarConnectionDetails | CarConnectionEvent | CarConnectionUser | CreateCarPayload | UpdateCarPayload | DeleteCarPayload | Event | EventConnection | EventAggregator | EventGroupBy | EventConnectionId | EventConnectionCreated_At | EventConnectionUpdated_At | EventConnectionName | EventConnectionEmail | EventConnectionDate | EventConnectionAddress | EventConnectionPosition | EventConnectionUuid | CreateEventPayload | UpdateEventPayload | DeleteEventPayload | Page | PageConnection | PageAggregator | PageGroupBy | PageConnectionId | PageConnectionCreated_At | PageConnectionUpdated_At | PageConnectionName | PageConnectionContent | PageConnectionType | CreatePagePayload | UpdatePagePayload | DeletePagePayload | Settings | UpdateSettingPayload | DeleteSettingPayload | Travel | TravelConnection | TravelAggregator | TravelGroupBy | TravelConnectionId | TravelConnectionCreated_At | TravelConnectionUpdated_At | TravelConnectionMeeting | TravelConnectionDeparture | TravelConnectionDetails | TravelConnectionEvent | TravelConnectionVehicle | CreateTravelPayload | UpdateTravelPayload | DeleteTravelPayload | Vehicle | VehicleConnection | VehicleAggregator | VehicleAggregatorSum | VehicleAggregatorAvg | VehicleAggregatorMin | VehicleAggregatorMax | VehicleGroupBy | VehicleConnectionId | VehicleConnectionCreated_At | VehicleConnectionUpdated_At | VehicleConnectionName | VehicleConnectionSeats | VehicleConnectionUser | VehicleConnectionPhone_Number | CreateVehiclePayload | UpdateVehiclePayload | DeleteVehiclePayload | EmailDesignerEmailTemplate | UploadFile | UploadFileConnection | UploadFileAggregator | UploadFileAggregatorSum | UploadFileAggregatorAvg | UploadFileAggregatorMin | UploadFileAggregatorMax | UploadFileGroupBy | UploadFileConnectionId | UploadFileConnectionCreated_At | UploadFileConnectionUpdated_At | UploadFileConnectionName | UploadFileConnectionAlternativeText | UploadFileConnectionCaption | UploadFileConnectionWidth | UploadFileConnectionHeight | UploadFileConnectionFormats | UploadFileConnectionHash | UploadFileConnectionExt | UploadFileConnectionMime | UploadFileConnectionSize | UploadFileConnectionUrl | UploadFileConnectionPreviewUrl | UploadFileConnectionProvider | UploadFileConnectionProvider_Metadata | DeleteFilePayload | UsersPermissionsPermission | UsersPermissionsRole | UsersPermissionsRoleConnection | UsersPermissionsRoleAggregator | UsersPermissionsRoleGroupBy | UsersPermissionsRoleConnectionId | UsersPermissionsRoleConnectionName | UsersPermissionsRoleConnectionDescription | UsersPermissionsRoleConnectionType | CreateRolePayload | UpdateRolePayload | DeleteRolePayload | UsersPermissionsUser | UsersPermissionsUserConnection | UsersPermissionsUserAggregator | UsersPermissionsUserGroupBy | UsersPermissionsUserConnectionId | UsersPermissionsUserConnectionCreated_At | UsersPermissionsUserConnectionUpdated_At | UsersPermissionsUserConnectionUsername | UsersPermissionsUserConnectionFirstName | UsersPermissionsUserConnectionLastName | UsersPermissionsUserConnectionEmail | UsersPermissionsUserConnectionProvider | UsersPermissionsUserConnectionConfirmed | UsersPermissionsUserConnectionBlocked | UsersPermissionsUserConnectionRole | UsersPermissionsUserConnectionOnboardingUser | UsersPermissionsUserConnectionOnboardingCreator | UsersPermissionsUserConnectionLang | CreateUserPayload | UpdateUserPayload | DeleteUserPayload | ComponentPassengerPassenger; export type Mutation = { __typename?: 'Mutation';

@@ -423,6 +448,12 @@ updatePage?: Maybe<UpdatePagePayload>;

deletePage?: Maybe<DeletePagePayload>; updateSetting?: Maybe<UpdateSettingPayload>; deleteSetting?: Maybe<DeleteSettingPayload>; + createTravel?: Maybe<CreateTravelPayload>; + updateTravel?: Maybe<UpdateTravelPayload>; + deleteTravel?: Maybe<DeleteTravelPayload>; + createVehicle?: Maybe<CreateVehiclePayload>; + updateVehicle?: Maybe<UpdateVehiclePayload>; + deleteVehicle?: Maybe<DeleteVehiclePayload>; /** Delete one file */ deleteFile?: Maybe<DeleteFilePayload>; /** Create a new role */

@@ -500,6 +531,36 @@ input?: Maybe<UpdateSettingInput>;

}; +export type MutationCreateTravelArgs = { + input?: Maybe<CreateTravelInput>; +}; + + +export type MutationUpdateTravelArgs = { + input?: Maybe<UpdateTravelInput>; +}; + + +export type MutationDeleteTravelArgs = { + input?: Maybe<DeleteTravelInput>; +}; + + +export type MutationCreateVehicleArgs = { + input?: Maybe<CreateVehicleInput>; +}; + + +export type MutationUpdateVehicleArgs = { + input?: Maybe<UpdateVehicleInput>; +}; + + +export type MutationDeleteVehicleArgs = { + input?: Maybe<DeleteVehicleInput>; +}; + + export type MutationDeleteFileArgs = { input?: Maybe<DeleteFileInput>; };

@@ -690,6 +751,12 @@ page?: Maybe<Page>;

pages?: Maybe<Array<Maybe<Page>>>; pagesConnection?: Maybe<PageConnection>; setting?: Maybe<Settings>; + travel?: Maybe<Travel>; + travels?: Maybe<Array<Maybe<Travel>>>; + travelsConnection?: Maybe<TravelConnection>; + vehicle?: Maybe<Vehicle>; + vehicles?: Maybe<Array<Maybe<Vehicle>>>; + vehiclesConnection?: Maybe<VehicleConnection>; files?: Maybe<Array<Maybe<UploadFile>>>; filesConnection?: Maybe<UploadFileConnection>; role?: Maybe<UsersPermissionsRole>;

@@ -779,6 +846,52 @@ publicationState?: Maybe<PublicationState>;

}; +export type QueryTravelArgs = { + id: Scalars['ID']; + publicationState?: Maybe<PublicationState>; +}; + + +export type QueryTravelsArgs = { + sort?: Maybe<Scalars['String']>; + limit?: Maybe<Scalars['Int']>; + start?: Maybe<Scalars['Int']>; + where?: Maybe<Scalars['JSON']>; + publicationState?: Maybe<PublicationState>; +}; + + +export type QueryTravelsConnectionArgs = { + sort?: Maybe<Scalars['String']>; + limit?: Maybe<Scalars['Int']>; + start?: Maybe<Scalars['Int']>; + where?: Maybe<Scalars['JSON']>; +}; + + +export type QueryVehicleArgs = { + id: Scalars['ID']; + publicationState?: Maybe<PublicationState>; +}; + + +export type QueryVehiclesArgs = { + sort?: Maybe<Scalars['String']>; + limit?: Maybe<Scalars['Int']>; + start?: Maybe<Scalars['Int']>; + where?: Maybe<Scalars['JSON']>; + publicationState?: Maybe<PublicationState>; +}; + + +export type QueryVehiclesConnectionArgs = { + sort?: Maybe<Scalars['String']>; + limit?: Maybe<Scalars['Int']>; + start?: Maybe<Scalars['Int']>; + where?: Maybe<Scalars['JSON']>; +}; + + export type QueryFilesArgs = { sort?: Maybe<Scalars['String']>; limit?: Maybe<Scalars['Int']>;

@@ -873,6 +986,103 @@ about_link?: Maybe<Scalars['String']>;

}; +export type Travel = { + __typename?: 'Travel'; + id: Scalars['ID']; + created_at: Scalars['DateTime']; + updated_at: Scalars['DateTime']; + meeting?: Maybe<Scalars['String']>; + departure?: Maybe<Scalars['DateTime']>; + details?: Maybe<Scalars['String']>; + passengers?: Maybe<Array<Maybe<ComponentPassengerPassenger>>>; + event?: Maybe<Event>; + vehicle?: Maybe<Vehicle>; +}; + +export type TravelAggregator = { + __typename?: 'TravelAggregator'; + count?: Maybe<Scalars['Int']>; + totalCount?: Maybe<Scalars['Int']>; +}; + +export type TravelConnection = { + __typename?: 'TravelConnection'; + values?: Maybe<Array<Maybe<Travel>>>; + groupBy?: Maybe<TravelGroupBy>; + aggregate?: Maybe<TravelAggregator>; +}; + +export type TravelConnectionCreated_At = { + __typename?: 'TravelConnectionCreated_at'; + key?: Maybe<Scalars['DateTime']>; + connection?: Maybe<TravelConnection>; +}; + +export type TravelConnectionDeparture = { + __typename?: 'TravelConnectionDeparture'; + key?: Maybe<Scalars['DateTime']>; + connection?: Maybe<TravelConnection>; +}; + +export type TravelConnectionDetails = { + __typename?: 'TravelConnectionDetails'; + key?: Maybe<Scalars['String']>; + connection?: Maybe<TravelConnection>; +}; + +export type TravelConnectionEvent = { + __typename?: 'TravelConnectionEvent'; + key?: Maybe<Scalars['ID']>; + connection?: Maybe<TravelConnection>; +}; + +export type TravelConnectionId = { + __typename?: 'TravelConnectionId'; + key?: Maybe<Scalars['ID']>; + connection?: Maybe<TravelConnection>; +}; + +export type TravelConnectionMeeting = { + __typename?: 'TravelConnectionMeeting'; + key?: Maybe<Scalars['String']>; + connection?: Maybe<TravelConnection>; +}; + +export type TravelConnectionUpdated_At = { + __typename?: 'TravelConnectionUpdated_at'; + key?: Maybe<Scalars['DateTime']>; + connection?: Maybe<TravelConnection>; +}; + +export type TravelConnectionVehicle = { + __typename?: 'TravelConnectionVehicle'; + key?: Maybe<Scalars['ID']>; + connection?: Maybe<TravelConnection>; +}; + +export type TravelGroupBy = { + __typename?: 'TravelGroupBy'; + id?: Maybe<Array<Maybe<TravelConnectionId>>>; + created_at?: Maybe<Array<Maybe<TravelConnectionCreated_At>>>; + updated_at?: Maybe<Array<Maybe<TravelConnectionUpdated_At>>>; + meeting?: Maybe<Array<Maybe<TravelConnectionMeeting>>>; + departure?: Maybe<Array<Maybe<TravelConnectionDeparture>>>; + details?: Maybe<Array<Maybe<TravelConnectionDetails>>>; + event?: Maybe<Array<Maybe<TravelConnectionEvent>>>; + vehicle?: Maybe<Array<Maybe<TravelConnectionVehicle>>>; +}; + +export type TravelInput = { + meeting?: Maybe<Scalars['String']>; + departure?: Maybe<Scalars['DateTime']>; + details?: Maybe<Scalars['String']>; + passengers?: Maybe<Array<Maybe<ComponentPassengerPassengerInput>>>; + event?: Maybe<Scalars['ID']>; + vehicle?: Maybe<Scalars['ID']>; + created_by?: Maybe<Scalars['ID']>; + updated_by?: Maybe<Scalars['ID']>; +}; + export type UploadFile = { __typename?: 'UploadFile';

@@ -1088,6 +1298,8 @@ events?: Maybe<Array<Maybe<Scalars['ID']>>>;

onboardingUser?: Maybe<Scalars['Boolean']>; onboardingCreator?: Maybe<Scalars['Boolean']>; lang?: Maybe<Enum_Userspermissionsuser_Lang>; + cars?: Maybe<Array<Maybe<Scalars['ID']>>>; + vehicles?: Maybe<Array<Maybe<Scalars['ID']>>>; created_by?: Maybe<Scalars['ID']>; updated_by?: Maybe<Scalars['ID']>; };

@@ -1236,6 +1448,8 @@ onboardingUser?: Maybe<Scalars['Boolean']>;

onboardingCreator?: Maybe<Scalars['Boolean']>; lang?: Maybe<Enum_Userspermissionsuser_Lang>; events?: Maybe<Array<Maybe<Event>>>; + cars?: Maybe<Array<Maybe<Car>>>; + vehicles?: Maybe<Array<Maybe<Vehicle>>>; };

@@ -1246,6 +1460,22 @@ start?: Maybe<Scalars['Int']>;

where?: Maybe<Scalars['JSON']>; }; + +export type UsersPermissionsUserCarsArgs = { + sort?: Maybe<Scalars['String']>; + limit?: Maybe<Scalars['Int']>; + start?: Maybe<Scalars['Int']>; + where?: Maybe<Scalars['JSON']>; +}; + + +export type UsersPermissionsUserVehiclesArgs = { + sort?: Maybe<Scalars['String']>; + limit?: Maybe<Scalars['Int']>; + start?: Maybe<Scalars['Int']>; + where?: Maybe<Scalars['JSON']>; +}; + export type UsersPermissionsUserAggregator = { __typename?: 'UsersPermissionsUserAggregator'; count?: Maybe<Scalars['Int']>;

@@ -1361,6 +1591,126 @@ onboardingCreator?: Maybe<Array<Maybe<UsersPermissionsUserConnectionOnboardingCreator>>>;

lang?: Maybe<Array<Maybe<UsersPermissionsUserConnectionLang>>>; }; +export type Vehicle = { + __typename?: 'Vehicle'; + id: Scalars['ID']; + created_at: Scalars['DateTime']; + updated_at: Scalars['DateTime']; + name: Scalars['String']; + seats?: Maybe<Scalars['Int']>; + user?: Maybe<UsersPermissionsUser>; + phone_number?: Maybe<Scalars['String']>; + travels?: Maybe<Array<Maybe<Travel>>>; +}; + + +export type VehicleTravelsArgs = { + sort?: Maybe<Scalars['String']>; + limit?: Maybe<Scalars['Int']>; + start?: Maybe<Scalars['Int']>; + where?: Maybe<Scalars['JSON']>; +}; + +export type VehicleAggregator = { + __typename?: 'VehicleAggregator'; + count?: Maybe<Scalars['Int']>; + totalCount?: Maybe<Scalars['Int']>; + sum?: Maybe<VehicleAggregatorSum>; + avg?: Maybe<VehicleAggregatorAvg>; + min?: Maybe<VehicleAggregatorMin>; + max?: Maybe<VehicleAggregatorMax>; +}; + +export type VehicleAggregatorAvg = { + __typename?: 'VehicleAggregatorAvg'; + seats?: Maybe<Scalars['Float']>; +}; + +export type VehicleAggregatorMax = { + __typename?: 'VehicleAggregatorMax'; + seats?: Maybe<Scalars['Float']>; +}; + +export type VehicleAggregatorMin = { + __typename?: 'VehicleAggregatorMin'; + seats?: Maybe<Scalars['Float']>; +}; + +export type VehicleAggregatorSum = { + __typename?: 'VehicleAggregatorSum'; + seats?: Maybe<Scalars['Float']>; +}; + +export type VehicleConnection = { + __typename?: 'VehicleConnection'; + values?: Maybe<Array<Maybe<Vehicle>>>; + groupBy?: Maybe<VehicleGroupBy>; + aggregate?: Maybe<VehicleAggregator>; +}; + +export type VehicleConnectionCreated_At = { + __typename?: 'VehicleConnectionCreated_at'; + key?: Maybe<Scalars['DateTime']>; + connection?: Maybe<VehicleConnection>; +}; + +export type VehicleConnectionId = { + __typename?: 'VehicleConnectionId'; + key?: Maybe<Scalars['ID']>; + connection?: Maybe<VehicleConnection>; +}; + +export type VehicleConnectionName = { + __typename?: 'VehicleConnectionName'; + key?: Maybe<Scalars['String']>; + connection?: Maybe<VehicleConnection>; +}; + +export type VehicleConnectionPhone_Number = { + __typename?: 'VehicleConnectionPhone_number'; + key?: Maybe<Scalars['String']>; + connection?: Maybe<VehicleConnection>; +}; + +export type VehicleConnectionSeats = { + __typename?: 'VehicleConnectionSeats'; + key?: Maybe<Scalars['Int']>; + connection?: Maybe<VehicleConnection>; +}; + +export type VehicleConnectionUpdated_At = { + __typename?: 'VehicleConnectionUpdated_at'; + key?: Maybe<Scalars['DateTime']>; + connection?: Maybe<VehicleConnection>; +}; + +export type VehicleConnectionUser = { + __typename?: 'VehicleConnectionUser'; + key?: Maybe<Scalars['ID']>; + connection?: Maybe<VehicleConnection>; +}; + +export type VehicleGroupBy = { + __typename?: 'VehicleGroupBy'; + id?: Maybe<Array<Maybe<VehicleConnectionId>>>; + created_at?: Maybe<Array<Maybe<VehicleConnectionCreated_At>>>; + updated_at?: Maybe<Array<Maybe<VehicleConnectionUpdated_At>>>; + name?: Maybe<Array<Maybe<VehicleConnectionName>>>; + seats?: Maybe<Array<Maybe<VehicleConnectionSeats>>>; + user?: Maybe<Array<Maybe<VehicleConnectionUser>>>; + phone_number?: Maybe<Array<Maybe<VehicleConnectionPhone_Number>>>; +}; + +export type VehicleInput = { + name: Scalars['String']; + seats?: Maybe<Scalars['Int']>; + user?: Maybe<Scalars['ID']>; + travels?: Maybe<Array<Maybe<Scalars['ID']>>>; + phone_number?: Maybe<Scalars['String']>; + created_by?: Maybe<Scalars['ID']>; + updated_by?: Maybe<Scalars['ID']>; +}; + export type CreateCarInput = { data?: Maybe<CarInput>; };

@@ -1397,6 +1747,15 @@ __typename?: 'createRolePayload';

role?: Maybe<UsersPermissionsRole>; }; +export type CreateTravelInput = { + data?: Maybe<TravelInput>; +}; + +export type CreateTravelPayload = { + __typename?: 'createTravelPayload'; + travel?: Maybe<Travel>; +}; + export type CreateUserInput = { data?: Maybe<UserInput>; };

@@ -1404,6 +1763,15 @@

export type CreateUserPayload = { __typename?: 'createUserPayload'; user?: Maybe<UsersPermissionsUser>; +}; + +export type CreateVehicleInput = { + data?: Maybe<VehicleInput>; +}; + +export type CreateVehiclePayload = { + __typename?: 'createVehiclePayload'; + vehicle?: Maybe<Vehicle>; }; export type DeleteCarInput = {

@@ -1456,6 +1824,15 @@ __typename?: 'deleteSettingPayload';

setting?: Maybe<Settings>; }; +export type DeleteTravelInput = { + where?: Maybe<InputId>; +}; + +export type DeleteTravelPayload = { + __typename?: 'deleteTravelPayload'; + travel?: Maybe<Travel>; +}; + export type DeleteUserInput = { where?: Maybe<InputId>; };

@@ -1465,6 +1842,15 @@ __typename?: 'deleteUserPayload';

user?: Maybe<UsersPermissionsUser>; }; +export type DeleteVehicleInput = { + where?: Maybe<InputId>; +}; + +export type DeleteVehiclePayload = { + __typename?: 'deleteVehiclePayload'; + vehicle?: Maybe<Vehicle>; +}; + export type EditCarInput = { name?: Maybe<Scalars['String']>; seats?: Maybe<Scalars['Int']>;

@@ -1474,6 +1860,7 @@ phone_number?: Maybe<Scalars['String']>;

details?: Maybe<Scalars['String']>; event?: Maybe<Scalars['ID']>; passengers?: Maybe<Array<Maybe<EditComponentPassengerPassengerInput>>>; + user?: Maybe<Scalars['ID']>; created_by?: Maybe<Scalars['ID']>; updated_by?: Maybe<Scalars['ID']>; };

@@ -1482,9 +1869,12 @@ export type EditComponentPassengerPassengerInput = {

id?: Maybe<Scalars['ID']>; name?: Maybe<Scalars['String']>; email?: Maybe<Scalars['String']>; + location?: Maybe<Scalars['String']>; + user?: Maybe<Scalars['ID']>; }; export type EditEmailTemplateInput = { + sourceCodeToTemplateId?: Maybe<Scalars['Int']>; design?: Maybe<Scalars['JSON']>; name?: Maybe<Scalars['String']>; subject?: Maybe<Scalars['String']>;

@@ -1506,6 +1896,7 @@ position?: Maybe<Scalars['JSON']>;

users?: Maybe<Array<Maybe<Scalars['ID']>>>; uuid?: Maybe<Scalars['String']>; waitingList?: Maybe<Array<Maybe<EditComponentPassengerPassengerInput>>>; + travels?: Maybe<Array<Maybe<Scalars['ID']>>>; created_by?: Maybe<Scalars['ID']>; updated_by?: Maybe<Scalars['ID']>; };

@@ -1555,6 +1946,17 @@ created_by?: Maybe<Scalars['ID']>;

updated_by?: Maybe<Scalars['ID']>; }; +export type EditTravelInput = { + meeting?: Maybe<Scalars['String']>; + departure?: Maybe<Scalars['DateTime']>; + details?: Maybe<Scalars['String']>; + passengers?: Maybe<Array<Maybe<EditComponentPassengerPassengerInput>>>; + event?: Maybe<Scalars['ID']>; + vehicle?: Maybe<Scalars['ID']>; + created_by?: Maybe<Scalars['ID']>; + updated_by?: Maybe<Scalars['ID']>; +}; + export type EditUserInput = { username?: Maybe<Scalars['String']>; firstName?: Maybe<Scalars['String']>;

@@ -1571,9 +1973,21 @@ events?: Maybe<Array<Maybe<Scalars['ID']>>>;

onboardingUser?: Maybe<Scalars['Boolean']>; onboardingCreator?: Maybe<Scalars['Boolean']>; lang?: Maybe<Enum_Userspermissionsuser_Lang>; + cars?: Maybe<Array<Maybe<Scalars['ID']>>>; + vehicles?: Maybe<Array<Maybe<Scalars['ID']>>>; created_by?: Maybe<Scalars['ID']>; updated_by?: Maybe<Scalars['ID']>; old_password?: Maybe<Scalars['String']>; +}; + +export type EditVehicleInput = { + name?: Maybe<Scalars['String']>; + seats?: Maybe<Scalars['Int']>; + user?: Maybe<Scalars['ID']>; + travels?: Maybe<Array<Maybe<Scalars['ID']>>>; + phone_number?: Maybe<Scalars['String']>; + created_by?: Maybe<Scalars['ID']>; + updated_by?: Maybe<Scalars['ID']>; }; export type UpdateCarInput = {

@@ -1630,6 +2044,16 @@ __typename?: 'updateSettingPayload';

setting?: Maybe<Settings>; }; +export type UpdateTravelInput = { + where?: Maybe<InputId>; + data?: Maybe<EditTravelInput>; +}; + +export type UpdateTravelPayload = { + __typename?: 'updateTravelPayload'; + travel?: Maybe<Travel>; +}; + export type UpdateUserInput = { where?: Maybe<InputId>; data?: Maybe<EditUserInput>;

@@ -1638,6 +2062,16 @@

export type UpdateUserPayload = { __typename?: 'updateUserPayload'; user?: Maybe<UsersPermissionsUser>; +}; + +export type UpdateVehicleInput = { + where?: Maybe<InputId>; + data?: Maybe<EditVehicleInput>; +}; + +export type UpdateVehiclePayload = { + __typename?: 'updateVehiclePayload'; + vehicle?: Maybe<Vehicle>; }; export type MeFieldsFragment = (

@@ -1712,79 +2146,29 @@ ) }

)> } ); -export type CarFieldsFragment = ( - { __typename?: 'Car' } - & Pick<Car, 'id' | 'name' | 'seats' | 'meeting' | 'departure' | 'phone_number' | 'details'> - & { passengers?: Maybe<Array<Maybe<( - { __typename?: 'ComponentPassengerPassenger' } - & Pick<ComponentPassengerPassenger, 'id' | 'name'> - )>>>, event?: Maybe<( - { __typename?: 'Event' } - & Pick<Event, 'id' | 'name'> - )> } -); - -export type CreateCarMutationVariables = Exact<{ - car: CarInput; -}>; - - -export type CreateCarMutation = ( - { __typename?: 'Mutation' } - & { createCar?: Maybe<( - { __typename?: 'createCarPayload' } - & { car?: Maybe<( - { __typename?: 'Car' } - & CarFieldsFragment - )> } - )> } -); - -export type UpdateCarMutationVariables = Exact<{ - id: Scalars['ID']; - carUpdate: EditCarInput; -}>; - - -export type UpdateCarMutation = ( - { __typename?: 'Mutation' } - & { updateCar?: Maybe<( - { __typename?: 'updateCarPayload' } - & { car?: Maybe<( - { __typename?: 'Car' } - & CarFieldsFragment - )> } - )> } -); - -export type DeleteCarMutationVariables = Exact<{ - id: Scalars['ID']; -}>; - - -export type DeleteCarMutation = ( - { __typename?: 'Mutation' } - & { deleteCar?: Maybe<( - { __typename?: 'deleteCarPayload' } - & { car?: Maybe<( - { __typename?: 'Car' } - & Pick<Car, 'id' | 'name'> - )> } - )> } -); - export type EventFieldsFragment = ( { __typename?: 'Event' } & Pick<Event, 'id' | 'uuid' | 'name' | 'email' | 'date' | 'address' | 'position'> & { waitingList?: Maybe<Array<Maybe<( { __typename?: 'ComponentPassengerPassenger' } - & Pick<ComponentPassengerPassenger, 'id' | 'name'> - )>>>, cars?: Maybe<Array<Maybe<( - { __typename?: 'Car' } - & Pick<Car, 'id' | 'name' | 'seats' | 'meeting' | 'departure' | 'details' | 'phone_number'> - & { passengers?: Maybe<Array<Maybe<( + & Pick<ComponentPassengerPassenger, 'id' | 'name' | 'location'> + & { user?: Maybe<( + { __typename?: 'UsersPermissionsUser' } + & Pick<UsersPermissionsUser, 'id' | 'firstName' | 'lastName'> + )> } + )>>>, travels?: Maybe<Array<Maybe<( + { __typename?: 'Travel' } + & Pick<Travel, 'id' | 'meeting' | 'departure' | 'details'> + & { vehicle?: Maybe<( + { __typename?: 'Vehicle' } + & Pick<Vehicle, 'id' | 'name' | 'phone_number' | 'seats'> + )>, passengers?: Maybe<Array<Maybe<( { __typename?: 'ComponentPassengerPassenger' } - & Pick<ComponentPassengerPassenger, 'id' | 'name'> + & Pick<ComponentPassengerPassenger, 'id' | 'name' | 'location'> + & { user?: Maybe<( + { __typename?: 'UsersPermissionsUser' } + & Pick<UsersPermissionsUser, 'id' | 'firstName' | 'lastName'> + )> } )>>> } )>>> } );

@@ -1850,6 +2234,71 @@ & Pick<Settings, 'id' | 'gtm_id' | 'about_link'>

)> } ); +export type TravelFieldsFragment = ( + { __typename?: 'Travel' } + & Pick<Travel, 'id' | 'meeting' | 'departure' | 'details'> + & { vehicle?: Maybe<( + { __typename?: 'Vehicle' } + & Pick<Vehicle, 'id' | 'name' | 'phone_number' | 'seats'> + )>, passengers?: Maybe<Array<Maybe<( + { __typename?: 'ComponentPassengerPassenger' } + & Pick<ComponentPassengerPassenger, 'id' | 'name' | 'location'> + & { user?: Maybe<( + { __typename?: 'UsersPermissionsUser' } + & Pick<UsersPermissionsUser, 'id' | 'firstName' | 'lastName'> + )> } + )>>> } +); + +export type CreateTravelMutationVariables = Exact<{ + travel: TravelInput; +}>; + + +export type CreateTravelMutation = ( + { __typename?: 'Mutation' } + & { createTravel?: Maybe<( + { __typename?: 'createTravelPayload' } + & { travel?: Maybe<( + { __typename?: 'Travel' } + & TravelFieldsFragment + )> } + )> } +); + +export type UpdateTravelMutationVariables = Exact<{ + id: Scalars['ID']; + travelUpdate: EditTravelInput; +}>; + + +export type UpdateTravelMutation = ( + { __typename?: 'Mutation' } + & { updateTravel?: Maybe<( + { __typename?: 'updateTravelPayload' } + & { travel?: Maybe<( + { __typename?: 'Travel' } + & TravelFieldsFragment + )> } + )> } +); + +export type DeleteTravelMutationVariables = Exact<{ + id: Scalars['ID']; +}>; + + +export type DeleteTravelMutation = ( + { __typename?: 'Mutation' } + & { deleteTravel?: Maybe<( + { __typename?: 'deleteTravelPayload' } + & { travel?: Maybe<( + { __typename?: 'Travel' } + & Pick<Travel, 'id'> + )> } + )> } +); + export type UserFieldsFragment = ( { __typename?: 'UsersPermissionsUser' } & Pick<UsersPermissionsUser, 'id' | 'username' | 'email' | 'confirmed' | 'lastName' | 'firstName' | 'lang' | 'onboardingUser' | 'onboardingCreator'>

@@ -1890,6 +2339,60 @@ )> }

) } ); +export type VehicleFieldsFragment = ( + { __typename?: 'Vehicle' } + & Pick<Vehicle, 'id' | 'name' | 'seats' | 'phone_number'> +); + +export type CreateVehicleMutationVariables = Exact<{ + vehicle: VehicleInput; +}>; + + +export type CreateVehicleMutation = ( + { __typename?: 'Mutation' } + & { createVehicle?: Maybe<( + { __typename?: 'createVehiclePayload' } + & { vehicle?: Maybe<( + { __typename?: 'Vehicle' } + & VehicleFieldsFragment + )> } + )> } +); + +export type UpdateVehicleMutationVariables = Exact<{ + id: Scalars['ID']; + vehicleUpdate: EditVehicleInput; +}>; + + +export type UpdateVehicleMutation = ( + { __typename?: 'Mutation' } + & { updateVehicle?: Maybe<( + { __typename?: 'updateVehiclePayload' } + & { vehicle?: Maybe<( + { __typename?: 'Vehicle' } + & VehicleFieldsFragment + )> } + )> } +); + +export type DeleteVehicleMutationVariables = Exact<{ + id: Scalars['ID']; +}>; + + +export type DeleteVehicleMutation = ( + { __typename?: 'Mutation' } + & { deleteVehicle?: Maybe<( + { __typename?: 'deleteVehiclePayload' } + & { vehicle?: Maybe<( + { __typename?: 'Vehicle' } + & Pick<Vehicle, 'id' | 'name'> + )> } + )> } +); + export const MeFieldsFragmentDoc = gql` fragment MeFields on UsersPermissionsMe { id

@@ -1898,25 +2401,6 @@ email

confirmed } `; -export const CarFieldsFragmentDoc = gql` - fragment CarFields on Car { - id - name - seats - meeting - departure - phone_number - details - passengers { - id - name - } - event { - id - name - } -} - `; export const EventFieldsFragmentDoc = gql` fragment EventFields on Event { id

@@ -1929,18 +2413,57 @@ position

waitingList { id name + location + user { + id + firstName + lastName + } } - cars { + travels { id - name - seats meeting departure details - phone_number + vehicle { + id + name + phone_number + seats + } passengers { id name + location + user { + id + firstName + lastName + } + } + } +} + `; +export const TravelFieldsFragmentDoc = gql` + fragment TravelFields on Travel { + id + meeting + departure + details + vehicle { + id + name + phone_number + seats + } + passengers { + id + name + location + user { + id + firstName + lastName } } }

@@ -1963,6 +2486,14 @@ name

date address } +} + `; +export const VehicleFieldsFragmentDoc = gql` + fragment VehicleFields on Vehicle { + id + name + seats + phone_number } `; export const RegisterDocument = gql`

@@ -2109,110 +2640,6 @@ }

export type ResetPasswordMutationHookResult = ReturnType<typeof useResetPasswordMutation>; export type ResetPasswordMutationResult = Apollo.MutationResult<ResetPasswordMutation>; export type ResetPasswordMutationOptions = Apollo.BaseMutationOptions<ResetPasswordMutation, ResetPasswordMutationVariables>; -export const CreateCarDocument = gql` - mutation createCar($car: CarInput!) { - createCar(input: {data: $car}) { - car { - ...CarFields - } - } -} - ${CarFieldsFragmentDoc}`; -export type CreateCarMutationFn = Apollo.MutationFunction<CreateCarMutation, CreateCarMutationVariables>; - -/** - * __useCreateCarMutation__ - * - * To run a mutation, you first call `useCreateCarMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useCreateCarMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [createCarMutation, { data, loading, error }] = useCreateCarMutation({ - * variables: { - * car: // value for 'car' - * }, - * }); - */ -export function useCreateCarMutation(baseOptions?: Apollo.MutationHookOptions<CreateCarMutation, CreateCarMutationVariables>) { - return Apollo.useMutation<CreateCarMutation, CreateCarMutationVariables>(CreateCarDocument, baseOptions); - } -export type CreateCarMutationHookResult = ReturnType<typeof useCreateCarMutation>; -export type CreateCarMutationResult = Apollo.MutationResult<CreateCarMutation>; -export type CreateCarMutationOptions = Apollo.BaseMutationOptions<CreateCarMutation, CreateCarMutationVariables>; -export const UpdateCarDocument = gql` - mutation updateCar($id: ID!, $carUpdate: editCarInput!) { - updateCar(input: {where: {id: $id}, data: $carUpdate}) { - car { - ...CarFields - } - } -} - ${CarFieldsFragmentDoc}`; -export type UpdateCarMutationFn = Apollo.MutationFunction<UpdateCarMutation, UpdateCarMutationVariables>; - -/** - * __useUpdateCarMutation__ - * - * To run a mutation, you first call `useUpdateCarMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useUpdateCarMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [updateCarMutation, { data, loading, error }] = useUpdateCarMutation({ - * variables: { - * id: // value for 'id' - * carUpdate: // value for 'carUpdate' - * }, - * }); - */ -export function useUpdateCarMutation(baseOptions?: Apollo.MutationHookOptions<UpdateCarMutation, UpdateCarMutationVariables>) { - return Apollo.useMutation<UpdateCarMutation, UpdateCarMutationVariables>(UpdateCarDocument, baseOptions); - } -export type UpdateCarMutationHookResult = ReturnType<typeof useUpdateCarMutation>; -export type UpdateCarMutationResult = Apollo.MutationResult<UpdateCarMutation>; -export type UpdateCarMutationOptions = Apollo.BaseMutationOptions<UpdateCarMutation, UpdateCarMutationVariables>; -export const DeleteCarDocument = gql` - mutation deleteCar($id: ID!) { - deleteCar(input: {where: {id: $id}}) { - car { - id - name - } - } -} - `; -export type DeleteCarMutationFn = Apollo.MutationFunction<DeleteCarMutation, DeleteCarMutationVariables>; - -/** - * __useDeleteCarMutation__ - * - * To run a mutation, you first call `useDeleteCarMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useDeleteCarMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [deleteCarMutation, { data, loading, error }] = useDeleteCarMutation({ - * variables: { - * id: // value for 'id' - * }, - * }); - */ -export function useDeleteCarMutation(baseOptions?: Apollo.MutationHookOptions<DeleteCarMutation, DeleteCarMutationVariables>) { - return Apollo.useMutation<DeleteCarMutation, DeleteCarMutationVariables>(DeleteCarDocument, baseOptions); - } -export type DeleteCarMutationHookResult = ReturnType<typeof useDeleteCarMutation>; -export type DeleteCarMutationResult = Apollo.MutationResult<DeleteCarMutation>; -export type DeleteCarMutationOptions = Apollo.BaseMutationOptions<DeleteCarMutation, DeleteCarMutationVariables>; export const CreateEventDocument = gql` mutation createEvent($name: String!, $email: String!, $date: Date, $address: String, $newsletter: Boolean) { createEvent(

@@ -2355,6 +2782,109 @@ }

export type SettingQueryHookResult = ReturnType<typeof useSettingQuery>; export type SettingLazyQueryHookResult = ReturnType<typeof useSettingLazyQuery>; export type SettingQueryResult = Apollo.QueryResult<SettingQuery, SettingQueryVariables>; +export const CreateTravelDocument = gql` + mutation createTravel($travel: TravelInput!) { + createTravel(input: {data: $travel}) { + travel { + ...TravelFields + } + } +} + ${TravelFieldsFragmentDoc}`; +export type CreateTravelMutationFn = Apollo.MutationFunction<CreateTravelMutation, CreateTravelMutationVariables>; + +/** + * __useCreateTravelMutation__ + * + * To run a mutation, you first call `useCreateTravelMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useCreateTravelMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [createTravelMutation, { data, loading, error }] = useCreateTravelMutation({ + * variables: { + * travel: // value for 'travel' + * }, + * }); + */ +export function useCreateTravelMutation(baseOptions?: Apollo.MutationHookOptions<CreateTravelMutation, CreateTravelMutationVariables>) { + return Apollo.useMutation<CreateTravelMutation, CreateTravelMutationVariables>(CreateTravelDocument, baseOptions); + } +export type CreateTravelMutationHookResult = ReturnType<typeof useCreateTravelMutation>; +export type CreateTravelMutationResult = Apollo.MutationResult<CreateTravelMutation>; +export type CreateTravelMutationOptions = Apollo.BaseMutationOptions<CreateTravelMutation, CreateTravelMutationVariables>; +export const UpdateTravelDocument = gql` + mutation updateTravel($id: ID!, $travelUpdate: editTravelInput!) { + updateTravel(input: {where: {id: $id}, data: $travelUpdate}) { + travel { + ...TravelFields + } + } +} + ${TravelFieldsFragmentDoc}`; +export type UpdateTravelMutationFn = Apollo.MutationFunction<UpdateTravelMutation, UpdateTravelMutationVariables>; + +/** + * __useUpdateTravelMutation__ + * + * To run a mutation, you first call `useUpdateTravelMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useUpdateTravelMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [updateTravelMutation, { data, loading, error }] = useUpdateTravelMutation({ + * variables: { + * id: // value for 'id' + * travelUpdate: // value for 'travelUpdate' + * }, + * }); + */ +export function useUpdateTravelMutation(baseOptions?: Apollo.MutationHookOptions<UpdateTravelMutation, UpdateTravelMutationVariables>) { + return Apollo.useMutation<UpdateTravelMutation, UpdateTravelMutationVariables>(UpdateTravelDocument, baseOptions); + } +export type UpdateTravelMutationHookResult = ReturnType<typeof useUpdateTravelMutation>; +export type UpdateTravelMutationResult = Apollo.MutationResult<UpdateTravelMutation>; +export type UpdateTravelMutationOptions = Apollo.BaseMutationOptions<UpdateTravelMutation, UpdateTravelMutationVariables>; +export const DeleteTravelDocument = gql` + mutation deleteTravel($id: ID!) { + deleteTravel(input: {where: {id: $id}}) { + travel { + id + } + } +} + `; +export type DeleteTravelMutationFn = Apollo.MutationFunction<DeleteTravelMutation, DeleteTravelMutationVariables>; + +/** + * __useDeleteTravelMutation__ + * + * To run a mutation, you first call `useDeleteTravelMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useDeleteTravelMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [deleteTravelMutation, { data, loading, error }] = useDeleteTravelMutation({ + * variables: { + * id: // value for 'id' + * }, + * }); + */ +export function useDeleteTravelMutation(baseOptions?: Apollo.MutationHookOptions<DeleteTravelMutation, DeleteTravelMutationVariables>) { + return Apollo.useMutation<DeleteTravelMutation, DeleteTravelMutationVariables>(DeleteTravelDocument, baseOptions); + } +export type DeleteTravelMutationHookResult = ReturnType<typeof useDeleteTravelMutation>; +export type DeleteTravelMutationResult = Apollo.MutationResult<DeleteTravelMutation>; +export type DeleteTravelMutationOptions = Apollo.BaseMutationOptions<DeleteTravelMutation, DeleteTravelMutationVariables>; export const ProfileDocument = gql` query profile { me {

@@ -2424,4 +2954,108 @@ return Apollo.useMutation<UpdateMeMutation, UpdateMeMutationVariables>(UpdateMeDocument, baseOptions);

} export type UpdateMeMutationHookResult = ReturnType<typeof useUpdateMeMutation>; export type UpdateMeMutationResult = Apollo.MutationResult<UpdateMeMutation>; -export type UpdateMeMutationOptions = Apollo.BaseMutationOptions<UpdateMeMutation, UpdateMeMutationVariables>;+export type UpdateMeMutationOptions = Apollo.BaseMutationOptions<UpdateMeMutation, UpdateMeMutationVariables>; +export const CreateVehicleDocument = gql` + mutation createVehicle($vehicle: VehicleInput!) { + createVehicle(input: {data: $vehicle}) { + vehicle { + ...VehicleFields + } + } +} + ${VehicleFieldsFragmentDoc}`; +export type CreateVehicleMutationFn = Apollo.MutationFunction<CreateVehicleMutation, CreateVehicleMutationVariables>; + +/** + * __useCreateVehicleMutation__ + * + * To run a mutation, you first call `useCreateVehicleMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useCreateVehicleMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [createVehicleMutation, { data, loading, error }] = useCreateVehicleMutation({ + * variables: { + * vehicle: // value for 'vehicle' + * }, + * }); + */ +export function useCreateVehicleMutation(baseOptions?: Apollo.MutationHookOptions<CreateVehicleMutation, CreateVehicleMutationVariables>) { + return Apollo.useMutation<CreateVehicleMutation, CreateVehicleMutationVariables>(CreateVehicleDocument, baseOptions); + } +export type CreateVehicleMutationHookResult = ReturnType<typeof useCreateVehicleMutation>; +export type CreateVehicleMutationResult = Apollo.MutationResult<CreateVehicleMutation>; +export type CreateVehicleMutationOptions = Apollo.BaseMutationOptions<CreateVehicleMutation, CreateVehicleMutationVariables>; +export const UpdateVehicleDocument = gql` + mutation updateVehicle($id: ID!, $vehicleUpdate: editVehicleInput!) { + updateVehicle(input: {where: {id: $id}, data: $vehicleUpdate}) { + vehicle { + ...VehicleFields + } + } +} + ${VehicleFieldsFragmentDoc}`; +export type UpdateVehicleMutationFn = Apollo.MutationFunction<UpdateVehicleMutation, UpdateVehicleMutationVariables>; + +/** + * __useUpdateVehicleMutation__ + * + * To run a mutation, you first call `useUpdateVehicleMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useUpdateVehicleMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [updateVehicleMutation, { data, loading, error }] = useUpdateVehicleMutation({ + * variables: { + * id: // value for 'id' + * vehicleUpdate: // value for 'vehicleUpdate' + * }, + * }); + */ +export function useUpdateVehicleMutation(baseOptions?: Apollo.MutationHookOptions<UpdateVehicleMutation, UpdateVehicleMutationVariables>) { + return Apollo.useMutation<UpdateVehicleMutation, UpdateVehicleMutationVariables>(UpdateVehicleDocument, baseOptions); + } +export type UpdateVehicleMutationHookResult = ReturnType<typeof useUpdateVehicleMutation>; +export type UpdateVehicleMutationResult = Apollo.MutationResult<UpdateVehicleMutation>; +export type UpdateVehicleMutationOptions = Apollo.BaseMutationOptions<UpdateVehicleMutation, UpdateVehicleMutationVariables>; +export const DeleteVehicleDocument = gql` + mutation deleteVehicle($id: ID!) { + deleteVehicle(input: {where: {id: $id}}) { + vehicle { + id + name + } + } +} + `; +export type DeleteVehicleMutationFn = Apollo.MutationFunction<DeleteVehicleMutation, DeleteVehicleMutationVariables>; + +/** + * __useDeleteVehicleMutation__ + * + * To run a mutation, you first call `useDeleteVehicleMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useDeleteVehicleMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [deleteVehicleMutation, { data, loading, error }] = useDeleteVehicleMutation({ + * variables: { + * id: // value for 'id' + * }, + * }); + */ +export function useDeleteVehicleMutation(baseOptions?: Apollo.MutationHookOptions<DeleteVehicleMutation, DeleteVehicleMutationVariables>) { + return Apollo.useMutation<DeleteVehicleMutation, DeleteVehicleMutationVariables>(DeleteVehicleDocument, baseOptions); + } +export type DeleteVehicleMutationHookResult = ReturnType<typeof useDeleteVehicleMutation>; +export type DeleteVehicleMutationResult = Apollo.MutationResult<DeleteVehicleMutation>; +export type DeleteVehicleMutationOptions = Apollo.BaseMutationOptions<DeleteVehicleMutation, DeleteVehicleMutationVariables>;
D frontend/graphql/car.gql

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

-fragment CarFields on Car { - id - name - seats - meeting - departure - phone_number - details - passengers { - id - name - } - event { - id - name - } -} - -mutation createCar($car: CarInput!) { - createCar(input: {data: $car}) { - car { - ...CarFields - } - } -} - -mutation updateCar($id: ID!, $carUpdate: editCarInput!) { - updateCar(input: {where: {id: $id}, data: $carUpdate}) { - car { - ...CarFields - } - } -} - -mutation deleteCar($id: ID!) { - deleteCar(input: {where: {id: $id}}) { - car { - id - name - } - } -}
M frontend/graphql/event.gqlfrontend/graphql/event.gql

@@ -9,18 +9,33 @@ position

waitingList { id name + location + user { + id + firstName + lastName + } } - cars { + travels { id - name - seats meeting departure details - phone_number + vehicle { + id + name + phone_number + seats + } passengers { id name + location + user { + id + firstName + lastName + } } } }
A frontend/graphql/travel.gql

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

+fragment TravelFields on Travel { + id + meeting + departure + details + vehicle { + id + name + phone_number + seats + } + passengers { + id + name + location + user { + id + firstName + lastName + } + } +} + +mutation createTravel($travel: TravelInput!) { + createTravel(input: {data: $travel}) { + travel { + ...TravelFields + } + } +} + +mutation updateTravel($id: ID!, $travelUpdate: editTravelInput!) { + updateTravel(input: {where: {id: $id}, data: $travelUpdate}) { + travel { + ...TravelFields + } + } +} + +mutation deleteTravel($id: ID!) { + deleteTravel(input: {where: {id: $id}}) { + travel { + id + } + } +}
A frontend/graphql/vehicle.gql

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

+fragment VehicleFields on Vehicle { + id + name + seats + phone_number +} + +mutation createVehicle($vehicle: VehicleInput!) { + createVehicle(input: {data: $vehicle}) { + vehicle { + ...VehicleFields + } + } +} + +mutation updateVehicle($id: ID!, $vehicleUpdate: editVehicleInput!) { + updateVehicle(input: {where: {id: $id}, data: $vehicleUpdate}) { + vehicle { + ...VehicleFields + } + } +} + +mutation deleteVehicle($id: ID!) { + deleteVehicle(input: {where: {id: $id}}) { + vehicle { + id + name + } + } +}
M frontend/locales/en.jsonfrontend/locales/en.json

@@ -117,7 +117,7 @@ "title": "You must be logged in",

"text_html": "To add <strong> {{eventName}} </strong> to your carosters you must be logged in or create an account." } }, - "car": { + "travel": { "fields": { "meeting_point": "Meeting place", "details": "Notes",
M frontend/locales/fr.jsonfrontend/locales/fr.json

@@ -117,7 +117,7 @@ "title": "Vous devez ĂȘtre connectĂ©",

"text_html": "Pour ajouter <strong>{{eventName}}</strong> Ă  vos carosters vous devez ĂȘtre connectĂ© ou crĂ©er un compte." } }, - "car": { + "travel": { "fields": { "meeting_point": "Lieu de rencontre", "details": "Notes",
M frontend/pages/e/[uuid].tsxfrontend/pages/e/[uuid].tsx

@@ -5,8 +5,8 @@ import useToastStore from '../../stores/useToastStore';

import useEventStore from '../../stores/useEventStore'; import Layout from '../../layouts/Default'; import AddToMyEventDialog from '../../containers/AddToMyEventDialog'; -import CarColumns from '../../containers/CarColumns'; -import NewCarDialog from '../../containers/NewCarDialog'; +import TravelColumns from '../../containers/TravelColumns'; +import NewTravelDialog from '../../containers/NewTravelDialog'; import WelcomeDialog from '../../containers/WelcomeDialog'; import EventBar from '../../containers/EventBar'; import Loading from '../../containers/Loading';

@@ -44,7 +44,7 @@ const eventUpdate = useEventStore(s => s.event);

const setIsEditing = useEventStore(s => s.setIsEditing); const [updateEvent] = useUpdateEventMutation(); const [isAddToMyEvent, setIsAddToMyEvent] = useState(false); - const [openNewCar, toggleNewCar] = useReducer(i => !i, false); + const [openNewTravel, toggleNewTravel] = useReducer(i => !i, false); const {data: {eventByUUID: event} = {}} = useEventByUuidQuery({ pollInterval: POLL_INTERVAL, variables: {uuid: eventUUID},

@@ -57,7 +57,7 @@

const onSave = async e => { try { const {uuid, ...data} = eventUpdate; - const {id, __typename, cars, users, waitingList, ...input} = data; + const {id, __typename, travels, users, waitingList, ...input} = data; await updateEvent({ variables: {uuid, eventUpdate: input as EditEventInput}, refetchQueries: ['eventByUUID'],

@@ -99,9 +99,14 @@ onAdd={setIsAddToMyEvent}

onSave={onSave} onShare={onShare} /> - <CarColumns toggleNewCar={toggleNewCar} /> - <Fab color="primary" open={openNewCar} onClick={toggleNewCar} aria-label="add-car" /> - <NewCarDialog open={openNewCar} toggle={toggleNewCar} /> + <TravelColumns toggleNewTravel={toggleNewTravel} /> + <Fab + color="primary" + open={openNewTravel} + onClick={toggleNewTravel} + aria-label="add-travel" + /> + <NewTravelDialog open={openNewTravel} toggle={toggleNewTravel} /> <AddToMyEventDialog event={event} open={isAddToMyEvent}