all repos — caroster @ 63428b58d9a5b0438d9e4d8afb2fd46e9dab1a08

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

đź’„ UI fixes

#253
Simon Mulquin simon@octree.ch
Mon, 14 Feb 2022 08:00:52 +0000
commit

63428b58d9a5b0438d9e4d8afb2fd46e9dab1a08

parent

e70dda0ec966d182cfaf626d43aea6888cdb7634

M backend/api/travel/models/travel.jsbackend/api/travel/models/travel.js

@@ -6,6 +6,14 @@ const {STRAPI_URL = ''} = process.env;

module.exports = { lifecycles: { + async beforeUpdate(query, update) { + const travel = await strapi.services.travel.findOne(query); + if (update.passengers && travel?.vehicle) { + if (travel?.vehicle?.seats < update.passengers.length) + throw new Error('no_enough_seats'); + } + }, + async afterCreate(result) { sendEmailsToWaitingList(result); },
M frontend/containers/AddPassengerButtons/index.tsxfrontend/containers/AddPassengerButtons/index.tsx

@@ -8,9 +8,10 @@ interface Props {

getOnClickFunction: (addSelf: boolean) => () => void; canAddSelf: boolean; variant: 'waitingList' | 'travel'; + disabled?: boolean; } -const AddPassengerButtons = ({getOnClickFunction, canAddSelf, variant}: Props) => { +const AddPassengerButtons = ({getOnClickFunction, canAddSelf, variant, disabled}: Props) => { const classes = useStyles(); const {t} = useTranslation();

@@ -24,6 +25,7 @@ variant="contained"

color="secondary" fullWidth onClick={getOnClickFunction(true)} + disabled={disabled} > <Icon>person_add</Icon> {t('travel.passengers.add_me')}

@@ -37,6 +39,7 @@ variant="outlined"

color="primary" fullWidth onClick={getOnClickFunction(false)} + disabled={disabled} > <Icon>person_add</Icon> {t(`travel.passengers.add_to_${variant}`)}
M frontend/containers/NewPassengerDialog/AddPassengerToTravel.tsxfrontend/containers/NewPassengerDialog/AddPassengerToTravel.tsx

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

import {useTranslation} from 'react-i18next'; import useAddToEvents from '../../hooks/useAddToEvents'; import useEventStore from '../../stores/useEventStore'; -import { - Travel as TravelType, -} from '../../generated/graphql'; +import {Travel as TravelType} from '../../generated/graphql'; import SubmitButton from './SubmitButton'; import Transition from './Transition'; import AddPassengerCommonFields from './AddPassengerCommonFields';

@@ -42,11 +40,16 @@ email,

name, }; - return addPassengerToTravel({passenger, travel, onSucceed: () => { + try { + await addPassengerToTravel({passenger, travel}); addToEvent(event.id); addToast(t('passenger.success.added_to_car', {name})); toggle(); - }}); + } catch (error) { + console.error(error); + if (error.message === 'no_enough_seats') + addToast(t`passenger.errors.car_full`); + } }; return (
M frontend/containers/NewPassengerDialog/AddPassengerToWaitingList.tsxfrontend/containers/NewPassengerDialog/AddPassengerToWaitingList.tsx

@@ -53,24 +53,25 @@ email,

name, location, }; - - addPassengerToWaitingList({ - passenger, - event, - onError: () => addToast(t('passenger.errors.cant_add_passenger')), - onSucceed: () => { - addToEvent(event.id); - addToast( - t( - addSelf - ? 'passenger.success.added_self_to_waitlist' - : 'passenger.success.added_to_waitlist', - {name} - ) - ); - toggle(); - }, - }); + try { + await addPassengerToWaitingList({ + passenger, + event, + }); + addToEvent(event.id); + addToast( + t( + addSelf + ? 'passenger.success.added_self_to_waitlist' + : 'passenger.success.added_to_waitlist', + {name} + ) + ); + toggle(); + } catch (error) { + console.error(error); + addToast(t('passenger.errors.cant_add_passenger')); + } }; return (
M frontend/containers/PassengersList/index.tsxfrontend/containers/PassengersList/index.tsx

@@ -9,7 +9,13 @@ } from '../../generated/graphql';

interface Props { passengers: ComponentPassengerPassenger[]; - Button: JSX.Element; + Button: ({ + onClick, + disabled, + }: { + onClick: () => void; + disabled?: boolean; + }) => JSX.Element; disabled?: boolean; isVehicle?: boolean; places?: number;

@@ -47,7 +53,10 @@ key={index}

passenger={passenger} isVehicle={isVehicle} button={ - <Button onClick={() => onClick && onClick(passenger.id)} /> + <Button + onClick={() => onClick && onClick(passenger.id)} + disabled={disabled} + /> } /> </ListItem>
M frontend/containers/Travel/index.tsxfrontend/containers/Travel/index.tsx

@@ -23,6 +23,8 @@ const [isEditing, toggleEditing] = useReducer(i => !i, false);

const actions = useActions({travel}); if (!travel) return null; + const disableNewPassengers = + travel.passengers.length >= travel.vehicle?.seats; return ( <Paper className={classes.root}>

@@ -36,6 +38,7 @@ <AddPassengerButtons

getOnClickFunction={props.getAddPassengerFunction} canAddSelf={props.canAddSelf} variant="travel" + disabled={disableNewPassengers} /> <Divider /> {!isEditing && (
M frontend/containers/VehicleChoiceDialog/VehicleItem.tsxfrontend/containers/VehicleChoiceDialog/VehicleItem.tsx

@@ -38,7 +38,12 @@ <Box>

<Typography variant="overline" className={classes.label}> {t('travel.vehicle.name')} </Typography> - <Button color="primary" variant="text" onClick={() => unlinkUserCar()}> + <Button + color="primary" + variant="text" + size="small" + onClick={() => unlinkUserCar()} + > {t('generic.delete')} </Button> </Box>

@@ -78,27 +83,10 @@ fontWeight: 'bold',

opacity: 0.6, marginRight: theme.spacing(2), }, - delete: { - color: theme.palette.error.main, - position: 'absolute', - right: theme.spacing(2), - '& > span': { - '&::after': { - content: '""', - display: 'block', - position: 'absolute', - width: `calc(100% - ${theme.spacing(2)}px)`, - height: theme.spacing(0.25), - bottom: theme.spacing(1), - left: theme.spacing(1), - backgroundColor: theme.palette.error.light, - }, - }, - }, select: { display: 'block', maxWidth: '300px', - margin: '0 auto', + margin: `0 auto ${theme.spacing(1.5)}px auto`, }, }));
M frontend/containers/WaitingList/AssignButton.tsxfrontend/containers/WaitingList/AssignButton.tsx

@@ -7,16 +7,18 @@

interface Props { onClick: () => void; tabIndex?: number; + disabled: boolean } const AssignButton = (props: Props) => { const {onClick, tabIndex} = props; const classes = useStyles(); const {t} = useTranslation(); + console.log(props.disabled) return ( <ListItemSecondaryAction className={classes.action} onClick={onClick} tabIndex={tabIndex}> - <IconButton className={classes.button} color="primary"> + <IconButton className={classes.button} disabled={props.disabled}> {t('passenger.actions.place')} <Icon>chevron_right</Icon> </IconButton>

@@ -28,13 +30,14 @@ const useStyles = makeStyles(theme => ({

action: { top: theme.spacing(3), }, - button: { + button: ({disabled}) => ({ borderRadius: theme.spacing(1), margin: theme.spacing(1, 0, 0, 0), padding: 0, fontSize: '1rem', lineHeight: 1.5, - }, + color: disabled ? 'black' : theme.palette.primary.main + }), })); export default AssignButton;
M frontend/containers/WaitingList/TravelDialog.tsxfrontend/containers/WaitingList/TravelDialog.tsx

@@ -75,6 +75,7 @@ </Box>

<Button color="primary" variant="contained" + disabled={travel?.vehicle?.seats === passengers} onClick={() => onSelect(travel)} className={classes.button} >

@@ -109,6 +110,7 @@ },

}, info: { padding: theme.spacing(0, 4, 0, 0), + width: '350px', [theme.breakpoints.down('sm')]: { padding: theme.spacing(0.5, 1), width: '100%',

@@ -129,6 +131,7 @@ padding: theme.spacing(0, 0, 0.5, 0),

}, button: { padding: theme.spacing(1, 15), + margin: theme.spacing(1), }, }));
M frontend/containers/WaitingList/index.tsxfrontend/containers/WaitingList/index.tsx

@@ -61,33 +61,32 @@

const selectTravel = useCallback( async travel => { const {id, ...passenger} = addingPassenger; - const onError = () => addToast(t('passenger.errors.cant_select_travel')); - addPassengerToTravel({ - travel, - passenger, - onError, - onSucceed: () => - removePassengerFromWaitingListFallBack({ - passenger: addingPassenger, - event: { - ...event, - waitingList: event.waitingList.filter( - item => item.id !== addingPassenger.id - ), - }, - onError, - onSucceed: () => { - setAddingPassenger(null); - slideToTravel(travel.id); - addToast( - t('passenger.success.added_to_car', { - name: addingPassenger.name, - }) - ); - }, - }), - }); + try { + await addPassengerToTravel({ + travel, + passenger, + }); + await removePassengerFromWaitingListFallBack({ + passenger: addingPassenger, + event: { + ...event, + waitingList: event.waitingList.filter( + item => item.id !== addingPassenger.id + ), + }, + }); + setAddingPassenger(null); + slideToTravel(travel.id); + addToast( + t('passenger.success.added_to_car', { + name: addingPassenger.name, + }) + ); + } catch (error) { + console.error(error); + addToast(t('passenger.errors.cant_select_travel')); + } }, [event, addingPassenger] // eslint-disable-line );

@@ -103,12 +102,25 @@ },

[isEditing, event] ); + const onRemove = async () => { + try { + await removePassengerFromWaitingListFallBack({ + passenger: removingPassenger, + event, + }); + addToEvent(event.id); + } catch (error) { + console.error(error); + addToast(t('passenger.errors.cant_remove_passenger')); + } + }; + const ListButton = isEditing ? ({onClick}: {onClick: () => void}) => ( <ClearButton icon="close" onClick={onClick} tabIndex={-1} /> ) - : ({onClick}: {onClick: () => void}) => ( - <AssignButton onClick={onClick} tabIndex={-1} /> + : ({onClick, disabled}: {onClick: () => void, disabled: boolean}) => ( + <AssignButton onClick={onClick} tabIndex={-1} disabled={disabled}/> ); return (

@@ -155,15 +167,7 @@ />

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

@@ -10,22 +10,16 @@

interface AddPassengerToTravelArgs { passenger: ComponentPassengerPassengerInput; travel: Travel; - onSucceed?: () => void; - onError?: (error: string) => void; } interface AddPassengerToWaitingList { passenger: ComponentPassengerPassenger; event: Event; - onSucceed?: () => void; - onError?: (error: string) => void; } interface RemovePassengerArgs { passenger: ComponentPassengerPassenger; event: Event; - onSucceed?: () => void; - onError?: (error: string) => void; } const usepassengersActions = () => {

@@ -35,78 +29,54 @@

const addPassengerToTravel = async ({ passenger, travel, - onSucceed, - onError, - }: AddPassengerToTravelArgs): Promise<void> => { - try { - const passengers = - [...travel.passengers, passenger].map(({__typename, user, ...item}) => { - return user && typeof user !== 'string' - ? { - ...item, - user: user.id, - } - : item; - }) || []; + }: AddPassengerToTravelArgs) => { + const passengers = + [...travel.passengers, passenger].map(({__typename, user, ...item}) => { + return user && typeof user !== 'string' + ? { + ...item, + user: user.id, + } + : item; + }) || []; - await updateTravel({ - variables: { - id: travel.id, - travelUpdate: { - passengers, - }, + return updateTravel({ + variables: { + id: travel.id, + travelUpdate: { + passengers, }, - }); - return onSucceed && onSucceed(); - } catch (error) { - console.error(error); - return onError && onError(error); - } + }, + }); }; const addPassengerToWaitingList = async ({ passenger, event, - onSucceed, - onError, - }: AddPassengerToWaitingList): Promise<void> => { - try { - const waitingList = [...event.waitingList, passenger].map( - ({__typename, user, ...item}) => - user ? {...item, user: user.id} : item - ); - await updateEvent({ - variables: {uuid: event.uuid, eventUpdate: {waitingList}}, - refetchQueries: ['eventByUUID'], - }); - return onSucceed && onSucceed(); - } catch (error) { - console.error(error); - return onError && onError(error); - } + }: AddPassengerToWaitingList) => { + const waitingList = [ + ...event.waitingList, + passenger, + ].map(({__typename, user, ...item}) => + user ? {...item, user: user.id} : item + ); + return updateEvent({ + variables: {uuid: event.uuid, eventUpdate: {waitingList}}, + refetchQueries: ['eventByUUID'], + }); }; const removePassengerFromWaitingList = async ({ passenger, event, - onSucceed, - onError, - }: RemovePassengerArgs): Promise<void> => { - try { - const waitingList = event.waitingList - .filter( - waitingListPassenger => waitingListPassenger.id !== passenger?.id - ) - .map(({__typename, user, ...item}) => item); - await updateEvent({ - variables: {uuid: event.uuid, eventUpdate: {waitingList}}, - refetchQueries: ['eventByUUID'], - }); - return onSucceed && onSucceed(); - } catch (error) { - console.error(error); - return onError && onError(error); - } + }: RemovePassengerArgs) => { + const waitingList = event.waitingList + .filter(waitingListPassenger => waitingListPassenger.id !== passenger?.id) + .map(({__typename, user, ...item}) => item); + return updateEvent({ + variables: {uuid: event.uuid, eventUpdate: {waitingList}}, + refetchQueries: ['eventByUUID'], + }); }; return {
M frontend/locales/fr.jsonfrontend/locales/fr.json

@@ -239,7 +239,8 @@ "errors": {

"cant_add_passenger": "Impossible d'ajouter un passager", "cant_save_passengers": "Impossible de mettre à jour passagers", "cant_remove_passenger": "Impossible de retirer le passager", - "cant_select_car": "Impossible de sélectionner la voiture" + "cant_select_car": "Impossible de sélectionner la voiture", + "car_full": "Voiture pleine. Impossible d'ajouter un passager supplémentaire" }, "success": { "added_self_to_car": "Vous avez été ajouté à la voiture",