all repos — caroster @ 410b0ab8fa0d9107427256118c989d4c27bee5a1

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

feat:đź’„ Clarify dialog "no available places"

#297
Simon Mulquin simon@octree.ch
Fri, 20 May 2022 11:47:16 +0000
commit

410b0ab8fa0d9107427256118c989d4c27bee5a1

parent

77927f1b1b32d25b9e0eff2b035da214e0cb4a3d

A frontend/components/CopyLink/index.tsx

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

+import Button, { ButtonProps } from '@material-ui/core/Button'; +import Icon from '@material-ui/core/Icon'; + +interface Props { + buttonText: string; + title: string; + url: string; + onShare: () => void; +} + +const CopyLink = ({buttonText, title, url, onShare, ...buttonProps}: ButtonProps & Props) => { + const share = async () => { + if (!url || !title) return null; + // If navigator share capability + if (!!navigator.share) + return await navigator.share({ + title, + url, + }); + // Else copy URL in clipboard + else if (!!navigator.clipboard) { + await navigator.clipboard.writeText(url); + onShare(); + return true; + } + }; + + return ( + <Button + variant="outlined" + startIcon={<Icon>share</Icon>} + onClick={share} + {...buttonProps} + > + {buttonText} + </Button> + ); +}; + +export default CopyLink;
M frontend/containers/EventDetails/index.jsfrontend/containers/EventDetails/index.tsx

@@ -2,19 +2,20 @@ import {useRef} from 'react';

import {makeStyles, createMuiTheme, ThemeProvider} from '@material-ui/core'; import Typography from '@material-ui/core/Typography'; import TextField from '@material-ui/core/TextField'; -import Button from '@material-ui/core/Button'; import Link from '@material-ui/core/Link'; -import Icon from '@material-ui/core/Icon'; import Box from '@material-ui/core/Box'; import {DatePicker} from '@material-ui/pickers'; import {useTranslation} from 'react-i18next'; import moment from 'moment'; import useEventStore from '../../stores/useEventStore'; import {caroster} from '../../theme'; +import CopyLink from '../../components/CopyLink'; +import useToastStore from '../../stores/useToastStore'; -const EventDetails = ({onShare}) => { +const EventDetails = () => { const {t} = useTranslation(); const event = useEventStore(s => s.event); + const addToast = useToastStore(s => s.addToast); const setEventUpdate = useEventStore(s => s.setEventUpdate); const isEditing = useEventStore(s => s.isEditing); const shareInput = useRef(null);

@@ -136,17 +137,14 @@ name="eventShareLink"

id="ShareLink" /> - <Button - className={'tour_event_share'} - variant="outlined" - startIcon={<Icon>share</Icon>} - onClick={() => { - if (shareInput) shareInput.current.select(); - onShare(); + <CopyLink + buttonText={t('event.fields.share')} + title={`Caroster ${event.name}`} + url={`${window.location.href}`} + onShare={() => { + addToast(t('event.actions.copied')); }} - > - {t('event.fields.share')} - </Button> + /> </Box> </ThemeProvider> );
M frontend/containers/WaitingList/TravelDialog.tsxfrontend/containers/WaitingList/TravelDialog.tsx

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

import {useTranslation} from 'react-i18next'; import {ComponentPassengerPassenger, Travel} from '../../generated/graphql'; import getMapsLink from '../../utils/getMapsLink'; +import Copylink from '../../components/CopyLink'; +import useToastStore from '../../stores/useToastStore'; interface Props { + eventName: string; travels: Array<Travel>; passenger: ComponentPassengerPassenger; open: boolean;

@@ -25,13 +28,20 @@ onClose: () => void;

onSelect: (travel: Travel) => void; } -const TravelDialog = ({travels, passenger, open, onClose, onSelect}: Props) => { +const TravelDialog = ({ + eventName, + travels, + passenger, + open, + onClose, + onSelect, +}: Props) => { const classes = useStyles(); const {t} = useTranslation(); + const addToast = useToastStore(s => s.addToast); const availableTravels = travels?.filter( - travel => - travel.passengers && travel?.seats > travel.passengers.length + travel => travel.passengers && travel?.seats > travel.passengers.length ); return (

@@ -52,17 +62,29 @@ </Typography>

</Toolbar> </AppBar> {(availableTravels.length === 0 && ( - <Typography className={classes.noTravel}> - {t('passenger.creation.no_travel', {name: passenger?.name})} - </Typography> + <Box className={classes.noTravel}> + <Typography variant="h5"> + {t('passenger.creation.no_travel.title')} + </Typography> + <img className={classes.noTravelImage} src="/assets/car.png" /> + <Typography> + {t('passenger.creation.no_travel.desc', {name: passenger?.name})} + </Typography> + <Copylink + color="primary" + className={classes.share} + buttonText={t('event.fields.share')} + title={`Caroster ${eventName}`} + url={`${window.location.href}`} + onShare={() => addToast(t('event.actions.copied'))} + /> + </Box> )) || ( <div className={classes.offset}> <List disablePadding> {availableTravels.map((travel, i) => { const passengersCount = travel?.passengers?.length || 0; - const counter = `${passengersCount} / ${ - travel?.seats || 0 - }`; + const counter = `${passengersCount} / ${travel?.seats || 0}`; return ( <ListItem key={i} divider className={classes.listItem}> <Box className={classes.rtlBox}>

@@ -81,9 +103,7 @@ {travel.meeting}

</Link> </Box> <Box className={classes.info}> - <Typography variant="h6"> - {travel.vehicleName} - </Typography> + <Typography variant="h6">{travel.vehicleName}</Typography> <Typography variant="body2"> {t('passenger.creation.seats', {seats: counter})} </Typography>

@@ -151,9 +171,17 @@ padding: theme.spacing(1, 15),

margin: theme.spacing(1), }, noTravel: { - margin: '45vh auto', + margin: '120px auto 0 auto', + width: '330px', + maxWidth: '100%', textAlign: 'center', }, + noTravelImage: { + width: '100%', + }, + share: { + marginTop: theme.spacing(2) + } })); export default TravelDialog;
M frontend/containers/WaitingList/index.tsxfrontend/containers/WaitingList/index.tsx

@@ -39,10 +39,8 @@ const [removingPassenger, setRemovingPassenger] = useState(null);

const [addingPassenger, setAddingPassenger] = useState(null); const travels = event?.travels?.length > 0 ? event.travels.slice().sort(sortTravels) : []; - const { - addPassengerToTravel, - removePassengerFromWaitingList, - } = usePassengersActions(); + const {addPassengerToTravel, removePassengerFromWaitingList} = + usePassengersActions(); const availability = useMemo(() => { if (!travels) return;

@@ -119,8 +117,8 @@ const ListButton = isEditing

? ({onClick}: {onClick: () => void}) => ( <ClearButton icon="close" onClick={onClick} tabIndex={-1} /> ) - : ({onClick, disabled}: {onClick: () => void, disabled: boolean}) => ( - <AssignButton onClick={onClick} tabIndex={-1} disabled={disabled}/> + : ({onClick, disabled}: {onClick: () => void; disabled: boolean}) => ( + <AssignButton onClick={onClick} tabIndex={-1} disabled={disabled} /> ); return (

@@ -169,6 +167,7 @@ onClose={() => setRemovingPassenger(null)}

onRemove={onRemove} /> <TravelDialog + eventName={event.name} travels={travels} passenger={addingPassenger} open={!!addingPassenger}
M frontend/locales/en.jsonfrontend/locales/en.json

@@ -233,7 +233,8 @@ "seats": "Number of passengers: {{seats}}",

"departure": "Departure: ", "assign": "Assign", "available_cars": "Available cars", - "no_travel": "No available cars. {{name}} will receive an email when new cars will be available." + "no_travel.title": "No available seats at the moment...", + "no_travel.desc": "{{name}} will receive an email when new cars will be available. You can share the event in the meantime." }, "actions": { "remove_alert": "Are you sure you want to remove <italic> <bold> {{name}} </bold> </italic> from the waitlist?",
M frontend/locales/fr.jsonfrontend/locales/fr.json

@@ -232,7 +232,8 @@ "seats": "Nombre de passagers: {{seats}}",

"departure": "Depart: ", "assign": "Placer", "available_cars": "Voitures disponibles", - "no_travel": "Aucune place disponible. {{name}} recevra un email lorsque des places seront ajoutées." + "no_travel.title": "Pas de place disponible en ce moment...", + "no_travel.desc": "{{name}} recevra un email lorsqu’une voiture sera ajoutée. En attendant, partagez l’événement" }, "actions": { "remove_alert": "Voulez-vous vraiment supprimer <italic><bold>{{name}}</bold></italic> de la liste d'attente ?",
M frontend/pages/e/[uuid].tsxfrontend/pages/e/[uuid].tsx

@@ -85,22 +85,6 @@ addToast(t('event.errors.cant_update'));

} }; - const onShare = async () => { - if (!event) return null; - // If navigator share capability - if (!!navigator.share) - return await navigator.share({ - title: `Caroster ${event.name}`, - url: `${window.location.href}`, - }); - // Else copy URL in clipboard - else if (!!navigator.clipboard) { - await navigator.clipboard.writeText(window.location.href); - addToast(t('event.actions.copied')); - return true; - } - }; - const addTravelClickHandler = user && vehicles?.length != 0 ? toggleVehicleChoice

@@ -118,7 +102,6 @@ <EventBar

event={event} onAdd={setIsAddToMyEvent} onSave={onSave} - onShare={onShare} /> <TravelColumns toggle={addTravelClickHandler} /> <Box className={classes.bottomRight}>