all repos — caroster @ c980d3dded6ff51fbddeaa6e023b48ebab4b2a7c

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

🐛 Fixes after PO review #509 #460 #281
Simon Mulquin simon@octree.ch
Wed, 06 Mar 2024 12:32:54 +0000
commit

c980d3dded6ff51fbddeaa6e023b48ebab4b2a7c

parent

1410b54dc70ad59a92a3fe31c76237c46a37c1d8

M frontend/containers/Travel/Header.tsxfrontend/containers/Travel/Header.tsx

@@ -11,6 +11,8 @@ import getMapsLink from '../../lib/getMapsLink';

import useMapStore from '../../stores/useMapStore'; import {TravelEntity} from '../../generated/graphql'; import usePermissions from '../../hooks/usePermissions'; +import Chip from '@mui/material/Chip'; +import useProfile from '../../hooks/useProfile'; interface Props { travel: TravelEntity;

@@ -25,6 +27,8 @@ const {

userPermissions: {canEditTravel}, } = usePermissions(); const {setFocusOnTravel, focusedTravel} = useMapStore(); + const {userId} = useProfile(); + const isUserTripCreator = userId && userId === travel.attributes.user?.data?.id; const passengersCount = travel?.attributes.passengers?.data.length || 0; const availableSeats = travel?.attributes.seats - passengersCount || 0;

@@ -44,7 +48,7 @@ size="small"

color="primary" sx={{ position: 'absolute', - top: 0, + top: theme.spacing(1), right: 0, margin: theme.spacing(1), }}

@@ -68,7 +72,11 @@ </Typography>

)} <Typography variant="subtitle1"> {travel.attributes.vehicleName} + {isUserTripCreator && ( + <Chip sx={{mx: 1}} label={t`generic.me`} variant="outlined" /> + )} </Typography> + {!!travel.attributes.phone_number && ( <Box sx={{marginTop: 2}}> <Typography variant="overline" sx={{color: 'GrayText'}}>
D frontend/containers/TravelColumns/Dots.tsx

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

-import {createPortal} from 'react-dom'; -import Box from '@mui/material/Box'; - -const Dots = ({children}) => { - const element = document.getElementById('slider-dots'); - if (!element) return null; - - return createPortal( - <Box - className="slick-dots" - component="ul" - height={1} - display="flex" - alignItems="center" - > - <Box display="flex" margin="0 auto"> - {children} - </Box> - </Box>, - document.getElementById('slider-dots') - ); -}; - -export default Dots;
A frontend/containers/TravelColumns/LoginToAttend.tsx

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

+import Link from 'next/link'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; +import Typography from '@mui/material/Typography'; +import {useRouter} from 'next/router'; +import {useTranslation} from 'react-i18next'; + +const LoginToAttend = () => { + const {t} = useTranslation(); + const router = useRouter(); + + return ( + <Box my={4} mx="auto" maxWidth="100%" width={340}> + <Typography variant="h6" align="center" color="textSecondary"> + {t('event.loginToAttend')} + </Typography> + <Typography + sx={{whiteSpace: 'pre-line', mt: 2}} + align="center" + color="textSecondary" + > + {t('event.loginToAttend.desc')} + </Typography> + <Box display='flex' justifyContent='space-between' pt={2}> + <Link + href={`/auth/login?redirectPath=${router.asPath}`} + passHref + style={{width: '100%'}} + > + <Button fullWidth sx={{mr: 0.5}} variant="outlined"> + {t('event.loginToAttend.login')} + </Button> + </Link> + <Link + href={`/auth/register?redirectPath=${router.asPath}`} + passHref + style={{width: '100%'}} + > + <Button fullWidth sx={{ml: 0.5}} variant="contained"> + {t('event.loginToAttend.signup')} + </Button> + </Link> + </Box> + </Box> + ); +}; + +export default LoginToAttend;
A frontend/containers/TravelColumns/MasonryContainer.tsx

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

+import Container from '@mui/material/Container'; +import {useTheme} from '@mui/material/styles'; + +const MasonryContainer = ({children}) => { + const theme = useTheme(); + return ( + <Container + maxWidth="sm" + sx={{ + p: 1, + mb: 10, + outline: 'none', + '& > *': { + cursor: 'default', + }, + + [theme.breakpoints.down('md')]: { + marginBottom: `calc(${theme.spacing(10)} + 56px)`, + }, + }} + > + {children} + </Container> + ); +}; + +export default MasonryContainer;
M frontend/containers/TravelColumns/index.tsxfrontend/containers/TravelColumns/index.tsx

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

import {useState} from 'react'; import dynamic from 'next/dynamic'; -import Container from '@mui/material/Container'; import Masonry from '@mui/lab/Masonry'; import Box from '@mui/material/Box'; import {useTranslation} from 'react-i18next';

@@ -16,6 +15,9 @@ import Travel from '../Travel';

import NoCar from './NoCar'; import {TravelEntity} from '../../generated/graphql'; import {AddPassengerToTravel} from '../NewPassengerDialog'; +import MasonryContainer from './MasonryContainer'; +import LoginToAttend from './LoginToAttend'; +import usePermissions from '../../hooks/usePermissions'; const EventMarker = dynamic(() => import('../EventMarker'), {ssr: false}); const TravelMarker = dynamic(() => import('../TravelMarker'), {ssr: false});

@@ -39,9 +41,40 @@ const {t} = useTranslation();

const addToast = useToastStore(s => s.addToast); const {addToEvent} = useAddToEvents(); const {profile, userId} = useProfile(); + const { + userPermissions: {canAddTravel}, + } = usePermissions(); const [selectedTravel, setSelectedTravel] = useState<TravelEntity>(); const {addPassenger} = usePassengersActions(); + + const sortTravels = ( + {attributes: a}: TravelEntity, + {attributes: b}: TravelEntity + ) => { + if (a?.user?.data?.id === userId && b?.user?.data?.id !== userId) return -1; + else if (a?.user?.data?.id !== userId && b?.user?.data?.id == userId) + return 1; + + const passengerFirst = + Number( + b?.passengers?.data?.some( + passenger => passenger.attributes.user?.data?.id === userId + ) + ) - + Number( + a?.passengers?.data?.some( + passenger => passenger.attributes.user?.data?.id === userId + ) + ); + if (passengerFirst !== 0) return passengerFirst; + const dateA = new Date(a.departure).getTime(); + const dateB = new Date(b.departure).getTime(); + if (dateA === dateB) + return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(); + else return dateA - dateB; + }; + const sortedTravels = travels?.slice().sort(sortTravels); const addSelfToTravel = async (travel: TravelEntity) => {

@@ -136,53 +169,29 @@ },

}} > <Masonry columns={{xl: 4, lg: 3, md: 2, sm: 2, xs: 1}} spacing={0}> + {!canAddTravel() && ( + <MasonryContainer key="no_other_travel"> + <LoginToAttend /> + </MasonryContainer> + )} {sortedTravels?.map(travel => { return ( - <Container - key={travel.id} - maxWidth="sm" - sx={{ - p: 1, - mb: 10, - outline: 'none', - '& > *': { - cursor: 'default', - }, - - [theme.breakpoints.down('md')]: { - marginBottom: `calc(${theme.spacing(10)} + 56px)`, - }, - }} - > + <MasonryContainer key={travel.id}> <Travel travel={travel} onAddSelf={() => addSelfToTravel(travel)} onAddOther={() => setSelectedTravel(travel)} {...props} /> - </Container> + </MasonryContainer> ); })} - <Container - maxWidth="sm" - sx={{ - padding: theme.spacing(1), - marginBottom: theme.spacing(10), - outline: 'none', - '& > *': { - cursor: 'default', - }, - - [theme.breakpoints.down('md')]: { - marginBottom: `calc(${theme.spacing(10)} + 56px)`, - }, - }} - > + <MasonryContainer key="no_other_travel"> <NoCar eventName={event?.name} title={t('event.no_other_travel.title')} /> - </Container> + </MasonryContainer> </Masonry> </Box> {!!selectedTravel && (

@@ -194,18 +203,6 @@ />

)} </> ); -}; - -const sortTravels = ( - {attributes: a}: TravelEntity, - {attributes: b}: TravelEntity -) => { - if (!b) return 1; - const dateA = new Date(a.departure).getTime(); - const dateB = new Date(b.departure).getTime(); - if (dateA === dateB) - return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(); - else return dateA - dateB; }; export default TravelColumns;
M frontend/locales/en.jsonfrontend/locales/en.json

@@ -68,6 +68,10 @@ "event.fields.link_desc": "Share this link with other people",

"event.fields.name": "Name of the event", "event.fields.lang": "Language", "event.fields.share": "Share", + "event.loginToAttend": "Do you want to attend this event ?", + "event.loginToAttend.desc": "Signup or log in to add it to your Carosters", + "event.loginToAttend.login": "$t(menu.login)", + "event.loginToAttend.signup": "$t(signup.title)", "event.no_other_travel.title": "There are currently no other car", "event.no_travel.desc": "1. Subscribe to the waiting list\n2. Share the event\n3. You will be notified when a new travel is added", "event.no_travel.title": "There are currently no cars",
M frontend/locales/fr.jsonfrontend/locales/fr.json

@@ -68,6 +68,10 @@ "event.fields.link_desc": "Partager l'évènement à d'autres personnes",

"event.fields.name": "Nom de l'événement", "event.fields.lang": "Langue", "event.fields.share": "Partager", + "event.loginToAttend": "Voulez-vous rejoindre cet évènement ?", + "event.loginToAttend.desc": "Créez un compte ou connectez-vous pour l'ajouter à vos Carosters", + "event.loginToAttend.login": "$t(menu.login)", + "event.loginToAttend.signup": "$t(signup.title)", "event.no_other_travel.title": "Pas d'autres voitures pour le moment", "event.no_travel.desc": "1. Inscrivez-vous dans la liste d’attente \n2. Partagez l’événement \n3. Vous serez notifié lorsqu’un nouveau trajet sera ajouté", "event.no_travel.title": "Pas de voitures pour le moment",
M frontend/locales/nl.jsonfrontend/locales/nl.json

@@ -58,6 +58,10 @@ "event.fields.link_desc": "Deel deze link met anderen",

"event.fields.name": "Afspraaknaam", "event.fields.lang": "", "event.fields.share": "Delen", + "event.loginToAttend": "", + "event.loginToAttend.desc": "", + "event.loginToAttend.login": "$t(menu.login)", + "event.loginToAttend.signup": "$t(signup.title)", "event.no_other_travel.title": "Er is momenteel geen andere auto", "event.no_travel.desc": "1. Zet uzelf op de wachtlijst;\n2. Deel de afspraak;\n3. Ontvang een melding zodra er een reis beschikbaar is.", "event.no_travel.title": "Er zijn momenteel geen auto's",
M frontend/locales/pl.jsonfrontend/locales/pl.json

@@ -61,6 +61,10 @@ "event.fields.link_desc": "Udostępnij ten link innym osobom",

"event.fields.name": "Nazwa wydarzenia", "event.fields.lang": "", "event.fields.share": "Udostępnij", + "event.loginToAttend": "", + "event.loginToAttend.desc": "", + "event.loginToAttend.login": "$t(menu.login)", + "event.loginToAttend.signup": "$t(signup.title)", "event.no_other_travel.title": "Obecnie nie ma żadnych innych samochodów", "event.no_travel.desc": "1. Zasubskrybuj listę oczekujących\n2. Udostępnij wydarzenie\n3. Dostaniesz powiadomienie kiedy zostanie dodana nowa podróż", "event.no_travel.title": "Obecnie nie ma żadnych samochodów",
M frontend/locales/sv.jsonfrontend/locales/sv.json

@@ -58,6 +58,10 @@ "event.fields.link_desc": "",

"event.fields.name": "", "event.fields.lang": "", "event.fields.share": "", + "event.loginToAttend": "", + "event.loginToAttend.desc": "", + "event.loginToAttend.login": "$t(menu.login)", + "event.loginToAttend.signup": "$t(signup.title)", "event.no_other_travel.title": "", "event.no_travel.desc": "", "event.no_travel.title": "",
M frontend/pages/e/[uuid]/options.tsxfrontend/pages/e/[uuid]/options.tsx

@@ -34,6 +34,7 @@

if (!event) return null; const carosterPlusActivated = + modulesSettings?.caroster_plus_enabled && event.enabled_modules?.includes('caroster-plus'); return (

@@ -50,7 +51,7 @@ },

}} > {carosterPlusActivated && <CarosterPlusSettings event={event} />}{' '} - {modulesSettings?.caroster_plus_enabled && ( + {modulesSettings?.caroster_plus_enabled && !carosterPlusActivated && ( <CarosterPlusOption event={event} modulesSettings={modulesSettings} /> )} {!modulesSettings?.caroster_plus_enabled && (