all repos — caroster @ a35bb0c7f225b13d8268326ad157f3a3b5215b99

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

✨ Add passenger to a car
Tim Izzo tim@octree.ch
Wed, 01 Jul 2020 14:29:25 +0200
commit

a35bb0c7f225b13d8268326ad157f3a3b5215b99

parent

a5cf0e5ec409bf5e3c76c95bccb10adaad3758e2

M app/src/containers/CarColumns/Car.jsapp/src/containers/CarColumns/Car.js

@@ -1,43 +1,85 @@

import React from "react"; import Typography from "@material-ui/core/Typography"; 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 { useStrapi } from "strapi-react-context"; import moment from "moment"; -import Paper from "../../components/Paper"; +import PassengersList from "../PassengersList"; +import { useToast } from "../../contexts/Toast"; const Car = ({ car }) => { const classes = useStyles(); const { t } = useTranslation(); + const { addToast } = useToast(); + const strapi = useStrapi(); if (!car) return null; + const addPassenger = async (passenger) => { + try { + await strapi.services.cars.update(car.id, { + passengers: [...(car.passengers || []), passenger], + }); + } catch (error) { + console.error(error); + addToast(t("car.errors.cant_add_passenger")); + } + }; + + const removePassenger = async (passenger) => { + if (!car?.passengers) return false; + try { + return await strapi.services.cars.update(car.id, { + passengers: car.passengers.filter((pssngr) => passenger !== pssngr), + }); + } catch (error) { + console.error(error); + addToast(t("car.errors.cant_remove_passenger")); + return false; + } + }; + return ( <Paper> - {!!car.departure && ( - <Typography variant="overline"> - {moment(car.departure).format("LLLL")} - </Typography> - )} - <Typography variant="h5">{car.name}</Typography> - {!!car.meeting && ( - <div className={classes.section}> - <Typography variant="subtitle2"> - {t("car.fields.meeting_point")} + <div className={classes.header}> + {!!car.departure && ( + <Typography variant="overline"> + {moment(car.departure).format("LLLL")} </Typography> - <Typography variant="body2">{car.meeting}</Typography> - </div> - )} - {!!car.details && ( - <div className={classes.section}> - <Typography variant="subtitle2">{t("car.fields.details")}</Typography> - <Typography variant="body2">{car.details}</Typography> - </div> - )} + )} + <Typography variant="h5">{car.name}</Typography> + {!!car.meeting && ( + <div className={classes.section}> + <Typography variant="subtitle2"> + {t("car.fields.meeting_point")} + </Typography> + <Typography variant="body2">{car.meeting}</Typography> + </div> + )} + {!!car.details && ( + <div className={classes.section}> + <Typography variant="subtitle2"> + {t("car.fields.details")} + </Typography> + <Typography variant="body2">{car.details}</Typography> + </div> + )} + </div> + <Divider /> + <PassengersList + passengers={car.passengers} + places={car.seats} + addPassenger={addPassenger} + removePassenger={removePassenger} + /> </Paper> ); }; const useStyles = makeStyles((theme) => ({ + header: { padding: theme.spacing(2) }, section: { marginTop: theme.spacing(2), },
M app/src/containers/CarColumns/index.jsapp/src/containers/CarColumns/index.js

@@ -24,21 +24,37 @@ settings: {

slidesToShow: 1, }, }, + { + breakpoint: 1200, + settings: { + slidesToShow: 3, + }, + }, ], }; +const sortCars = (a, b) => { + const dateA = new Date(a.departure).getTime(); + const dateB = new Date(b.departure).getTime(); + if (dateA === dateB) return new Date(a.createdAt) - new Date(b.createdAt); + else return dateA - dateB; +}; + const CarColumns = ({ ...props }) => { const classes = useStyles(); const strapi = useStrapi(); const { event } = useEvent(); const cars = useMemo( - () => strapi.stores.cars?.filter((car) => car?.event?.id === event?.id), + () => + strapi.stores.cars + ?.filter((car) => car?.event?.id === event?.id) + .sort(sortCars), [strapi.stores.cars, event] ); return ( - <div> + <div className={classes.root}> <Slider {...settings}> {cars && cars.map((car) => (

@@ -55,10 +71,12 @@ );

}; const useStyles = makeStyles((theme) => ({ + root: {}, slide: { height: `calc(100vh - ${theme.mixins.toolbar.minHeight}px)`, outline: "none", padding: theme.spacing(2), + marginBottom: theme.spacing(4), }, }));
A app/src/containers/PassengersList/Input.js

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

+import React, { useState } from "react"; +import TextField from "@material-ui/core/TextField"; +import { useTranslation } from "react-i18next"; +import { makeStyles } from "@material-ui/core/styles"; +import Divider from "@material-ui/core/Divider"; + +const Input = ({ addPassenger }) => { + const classes = useStyles(); + const [name, setName] = useState(""); + const { t } = useTranslation(); + + const onSave = () => { + if (!!name) { + addPassenger(name); + setName(""); + } + }; + + const onKeyDown = (e) => { + if (e.keyCode === 13) onSave(); + }; + + return ( + <> + <div className={classes.container}> + <TextField + value={name} + onChange={(e) => setName(e.target.value)} + onKeyDown={onKeyDown} + fullWidth + label={t("car.passengers.add")} + id="NewPassenger" + name="passenger" + /> + </div> + <Divider /> + </> + ); +}; + +const useStyles = makeStyles((theme) => ({ + container: { padding: theme.spacing(0, 2, 2) }, +})); + +export default Input;
A app/src/containers/PassengersList/Passenger.js

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

+import React 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 { useTranslation } from "react-i18next"; + +const Passenger = ({ passenger, removePassenger }) => { + const classes = useStyles(); + const { t } = useTranslation(); + if (!!passenger) + return ( + <div className={classes.item}> + <Typography variant="body2" className={classes.name}> + {passenger} + </Typography> + <IconButton + edge="end" + size="small" + onClick={() => removePassenger(passenger)} + > + <Icon>close</Icon> + </IconButton> + </div> + ); + else return <div className={classes.item}>{t("car.passengers.empty")}</div>; +}; + +const useStyles = makeStyles((theme) => ({ + item: { + padding: theme.spacing(1, 2), + display: "flex", + alignItems: "center", + height: "46px", + }, + name: { + flexGrow: 1, + }, +})); + +export default Passenger;
A app/src/containers/PassengersList/index.js

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

+import React from "react"; +import { makeStyles } from "@material-ui/core/styles"; +import Passenger from "./Passenger"; +import Input from "./Input"; + +const PassengersList = ({ + passengers, + places = 0, + addPassenger, + removePassenger, +}) => { + const classes = useStyles(); + + const emptyList = Array.apply(null, Array(places)); + const list = Array.isArray(passengers) + ? emptyList.map((u, index) => passengers[index]) + : emptyList; + const emptyPlaces = !!passengers ? places - passengers.length : places; + + return ( + <div className={classes.container}> + {emptyPlaces > 0 && <Input addPassenger={addPassenger} />} + {!!places && + list.map((passenger, index) => ( + <Passenger + key={index} + passenger={passenger} + removePassenger={removePassenger} + /> + ))} + </div> + ); +}; + +const useStyles = makeStyles((theme) => ({ + container: { padding: theme.spacing(1, 0) }, +})); + +export default PassengersList;
M app/src/locales/fr.jsonapp/src/locales/fr.json

@@ -45,9 +45,15 @@ "phone": "Numéro de téléphone",

"notes": "Infos complémentaires", "created": "La voiture a été créée" }, + "passengers": { + "empty": "Place disponible", + "add": "Ajouter un passager" + }, "errors": { "cant_create": "Impossible de créer la voiture", - "cant_update": "Impossible de modifier la voiture" + "cant_update": "Impossible de modifier la voiture", + "cant_add_passenger": "Impossible d'ajouter un passager", + "cant_remove_passenger": "Impossible de supprimer le passager" } } }
M app/src/pages/Event.jsapp/src/pages/Event.js

@@ -51,7 +51,7 @@ if (!event) return <div>{t("generic.loading")}</div>;

return ( <Layout> - <AppBar position="static" className={classes.appbar}> + <AppBar position="sticky" className={classes.appbar}> <Toolbar> {isEditing ? ( <TextField

@@ -119,7 +119,6 @@ transition: "height 0.3s ease",

overflow: "hidden", height: detailsOpen ? "100vh" : theme.mixins.toolbar.minHeight, zIndex: theme.zIndex.appBar, - position: "relative", }), name: { flexGrow: 1,