all repos — caroster @ 832452704d5eae9e2164e58c086cdf365e51e5e7

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

feat: ✨ Replace travels slider by masonry
Simon Mulquin simon@octree.ch
Wed, 19 Oct 2022 09:40:50 +0000
commit

832452704d5eae9e2164e58c086cdf365e51e5e7

parent

be8f93ea0966f0bc0587c2c127ee1ba15594a049

90 files changed, 2286 insertions(+), 1962 deletions(-)

jump to
M frontend/components/Banner/index.tsxfrontend/components/Banner/index.tsx

@@ -1,9 +1,10 @@

-import {Icon} from '@material-ui/core'; -import Button from '@material-ui/core/Button'; -import {makeStyles} from '@material-ui/core/styles'; import {useState} from 'react'; -import {hashText, setCookie} from '../../lib/cookies'; +import Icon from '@mui/material/Icon'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; +import {useTheme} from '@mui/material/styles'; import Markdown from '../Markdown'; +import {hashText, setCookie} from '../../lib/cookies'; const ANNOUNCEMENT_COOKIE = 'lastAnnouncementSeen';

@@ -13,7 +14,8 @@ }

const Banner = (props: Props) => { const {announcement} = props; - const classes = useStyles(); + const theme = useTheme(); + const [showBanner, setShowBanner] = useState(!!announcement); const onBannerClear = () => {

@@ -25,10 +27,40 @@

if (!showBanner) return null; return ( - <div className={classes.banner}> - <Markdown className={classes.htmlReset}>{announcement}</Markdown> + <Box + sx={{ + position: 'relative', + background: `linear-gradient(90deg, #FCDC61 20%, #78B2AC 90%)`, + width: '100%', + padding: '12px 60px', + textAlign: 'center', + zIndex: theme.zIndex.appBar - 1, + color: 'black', + }} + > + <Markdown + sx={{ + '& a': { + color: 'inherit', + margin: 0, + }, + '& p': { + margin: 0, + }, + }} + > + {announcement} + </Markdown> <Button - className={classes.clear} + sx={{ + position: 'absolute', + right: '12px', + bottom: '50%', + transform: 'translateY(50%)', + minWidth: '44px', + padding: '12px', + lineHeight: '1.4em', + }} onClick={e => { e.stopPropagation(); onBannerClear();

@@ -36,38 +68,8 @@ }}

> <Icon>close</Icon> </Button> - </div> + </Box> ); }; - -const useStyles = makeStyles(theme => ({ - banner: { - position: 'relative', - background: `linear-gradient(90deg, #FCDC61 20%, #78B2AC 90%)`, - width: '100%', - padding: '12px 60px', - textAlign: 'center', - zIndex: theme.zIndex.appBar - 1, - color: 'black', - }, - clear: { - position: 'absolute', - right: '12px', - bottom: '50%', - transform: 'translateY(50%)', - minWidth: '44px', - padding: '12px', - lineHeight: '1.4em', - }, - htmlReset: { - '& a': { - color: 'inherit', - margin: 0, - }, - '& p': { - margin: 0, - }, - }, -})); export default Banner;
D frontend/components/Logo/index.js

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

-import Link from 'next/link'; -import {makeStyles} from '@material-ui/core/styles'; -import useProfile from '../../hooks/useProfile'; -import useSettings from '../../hooks/useSettings'; - -const Logo = () => { - const classes = useStyles(); - const {connected} = useProfile(); - const settings = useSettings(); - const appLink = connected ? '/dashboard' : settings?.['about_link'] || ''; - return ( - <div className={classes.layout}> - <Link href={appLink} className={classes.link}> - <img - src={'/assets/Caroster_beta.png'} - alt="Caroster" - className={classes.logo} - /> - </Link> - </div> - ); -}; - -const useStyles = makeStyles(theme => ({ - layout: { - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - paddingTop: theme.spacing(4), - paddingBottom: theme.spacing(4), - }, - link: { - width: '100%', - }, - logo: { - display: 'block', - width: '55%', - height: 'auto', - margin: '0 auto', - }, -})); -export default Logo;
A frontend/components/Logo/index.tsx

@@ -0,0 +1,30 @@

+import {useTheme} from '@mui/material/styles'; +import {Box} from '@mui/material'; + +const Logo = () => { + const theme = useTheme(); + return ( + <Box + sx={{ + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + paddingTop: theme.spacing(4), + }} + > + <Box + component="img" + src={'/assets/Caroster_beta.png'} + alt="Caroster" + sx={{ + display: 'block', + width: '55%', + height: 'auto', + margin: '0 auto', + }} + /> + </Box> + ); +}; + +export default Logo;
M frontend/components/Markdown/index.tsxfrontend/components/Markdown/index.tsx

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

-import {styled} from '@material-ui/core/styles'; -import Typography, {TypographyProps} from '@material-ui/core/Typography'; +import {styled} from '@mui/material/styles'; +import Typography, {TypographyProps} from '@mui/material/Typography'; import {marked} from 'marked'; const Markdown = (props: TypographyProps) => {
D frontend/components/Paper/index.js

@@ -1,19 +0,0 @@

-import React from 'react'; -import PaperMUI from '@material-ui/core/Paper'; -import {makeStyles} from '@material-ui/core/styles'; - -const Paper = ({className, ...props}) => { - const classes = useStyles(); - - return ( - <PaperMUI classes={{root: classes.root, parent: className}} {...props} /> - ); -}; - -const useStyles = makeStyles(theme => ({ - root: { - padding: theme.spacing(2), - }, -})); - -export default Paper;
M frontend/components/Toasts/index.tsxfrontend/components/Toasts/index.tsx

@@ -1,16 +1,16 @@

-import {makeStyles} from '@material-ui/core/styles'; -import Snackbar from '@material-ui/core/Snackbar'; +import {useTheme} from '@mui/material/styles'; +import Snackbar from '@mui/material/Snackbar'; import useToastStore from '../../stores/useToastStore'; const Toasts = () => { + const theme = useTheme(); const toast = useToastStore(s => s.toast); const action = useToastStore(s => s.action); const clearToast = useToastStore(s => s.clearToast); - const classes = useStyles(); return ( <Snackbar - className={classes.withMobile} + sx={{bottom: theme.spacing(8)}} anchorOrigin={{ vertical: 'bottom', horizontal: 'left',

@@ -23,13 +23,5 @@ action={action}

/> ); }; - -const useStyles = makeStyles(theme => ({ - withMobile: { - [theme.breakpoints.down('sm')]: { - bottom: theme.spacing(8), - }, - } -})); export default Toasts;
M frontend/containers/AddPassengerButtons/index.tsxfrontend/containers/AddPassengerButtons/index.tsx

@@ -1,76 +1,75 @@

-import Icon from '@material-ui/core/Icon'; -import Box from '@material-ui/core/Box'; -import Button from '@material-ui/core/Button'; -import {makeStyles} from '@material-ui/core/styles'; -import {useTranslation} from 'react-i18next'; - -interface Props { - getOnClickFunction: (addSelf: boolean) => () => void; - canAddSelf: boolean; - variant: 'waitingList' | 'travel'; - disabled?: boolean; -} - -const AddPassengerButtons = ({getOnClickFunction, canAddSelf, variant, disabled}: Props) => { - const classes = useStyles(); - const {t} = useTranslation(); - - return ( - <Box className={classes.addButtonsContainer}> - {canAddSelf && ( - <Box className={classes.addButtonsContainer}> - <Button - className={classes.textContainer} - variant="contained" - color="secondary" - fullWidth - onClick={getOnClickFunction(true)} - disabled={disabled} - > - <Icon>person_add</Icon> - {t('travel.passengers.add_me')} - </Button> - </Box> - )} - <Box className={classes.addButtonsContainer}> - <Button - className={classes.textContainer} - variant="outlined" - color="primary" - fullWidth - onClick={getOnClickFunction(false)} - disabled={disabled} - > - <Icon>person_add</Icon> - {t(`travel.passengers.add_to_${variant}`)} - </Button> - </Box> - </Box> - ); -}; - -const useStyles = makeStyles(theme => ({ - addButtonsContainer: { - padding: theme.spacing(1), - textAlign: 'center', - }, - textContainer: { - padding: theme.spacing(1, 8), - [theme.breakpoints.down(440)]: { - padding: theme.spacing(1, 4), - }, - '& > .MuiButton-label': { - '& > .material-icons': { - width: theme.spacing(3), - textAlign: 'center', - position: 'absolute', - left: theme.spacing(4), - [theme.breakpoints.down(440)]: { - left: theme.spacing(1), - }, - }, - }, - }, -})); - -export default AddPassengerButtons; +import Icon from '@mui/material/Icon'; +import {useTheme} from '@mui/material/styles'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; +import {useTranslation} from 'react-i18next'; + +interface Props { + getOnClickFunction: (addSelf: boolean) => () => void; + canAddSelf: boolean; + variant: 'waitingList' | 'travel'; + disabled?: boolean; +} + +const AddPassengerButtons = ({ + getOnClickFunction, + canAddSelf, + variant, + disabled, +}: Props) => { + const theme = useTheme(); + const {t} = useTranslation(); + + const containerSx = {padding: theme.spacing(1), textAlign: 'center'}; + const textSx = { + padding: theme.spacing(1, 8), + [theme.breakpoints.down(440)]: { + padding: theme.spacing(1, 4), + }, + '& > .material-icons': { + width: theme.spacing(3), + textAlign: 'center', + position: 'absolute', + left: theme.spacing(4), + marginRight: theme.spacing(1), + [theme.breakpoints.down(440)]: { + left: theme.spacing(1), + }, + }, + }; + + return ( + <Box sx={containerSx}> + {canAddSelf && ( + <Box sx={containerSx}> + <Button + sx={textSx} + variant="contained" + color="secondary" + fullWidth + onClick={getOnClickFunction(true)} + disabled={disabled} + > + <Icon>person_add</Icon> + {t('travel.passengers.add_me')} + </Button> + </Box> + )} + <Box sx={containerSx}> + <Button + sx={textSx} + variant="outlined" + color="primary" + fullWidth + onClick={getOnClickFunction(false)} + disabled={disabled} + > + <Icon>person_add</Icon> + {t(`travel.passengers.add_to_${variant}`)} + </Button> + </Box> + </Box> + ); +}; + +export default AddPassengerButtons;
M frontend/containers/AddToMyEventDialog/index.tsxfrontend/containers/AddToMyEventDialog/index.tsx

@@ -1,22 +1,40 @@

import {forwardRef} from 'react'; +import { styled } from '@mui/material/styles'; import {useRouter} from 'next/router'; -import Dialog from '@material-ui/core/Dialog'; -import DialogTitle from '@material-ui/core/DialogTitle'; -import DialogActions from '@material-ui/core/DialogActions'; -import DialogContent from '@material-ui/core/DialogContent'; -import {makeStyles} from '@material-ui/core/styles'; -import DialogContentText from '@material-ui/core/DialogContentText'; -import Icon from '@material-ui/core/Icon'; -import Slide from '@material-ui/core/Slide'; -import Button from '@material-ui/core/Button'; -import IconButton from '@material-ui/core/IconButton'; +import Dialog from '@mui/material/Dialog'; +import DialogTitle from '@mui/material/DialogTitle'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogContentText from '@mui/material/DialogContentText'; +import Icon from '@mui/material/Icon'; +import Slide from '@mui/material/Slide'; +import Button from '@mui/material/Button'; +import IconButton from '@mui/material/IconButton'; import {useTranslation} from 'react-i18next'; import useAddToEvents from '../../hooks/useAddToEvents'; +const PREFIX = 'AddToMyEventDialog'; + +const classes = { + close: `${PREFIX}-close` +}; + +const StyledSlide = styled(Slide)(( + { + theme + } +) => ({ + [`& .${classes.close}`]: { + position: 'absolute', + top: theme.spacing(1), + right: theme.spacing(0.5), + } +})); + const AddToMyEventDialog = ({event, open, onClose}) => { const {t} = useTranslation(); const router = useRouter(); - const classes = useStyles(); + const {addToEvent} = useAddToEvents(); const onRedirect = path => {

@@ -28,7 +46,7 @@ if (!event) return null;

return ( <Dialog open={open} TransitionComponent={Transition} onClose={onClose}> - <IconButton onClick={onClose} className={classes.close}> + <IconButton onClick={onClose} className={classes.close} size="large"> <Icon>close</Icon> </IconButton> <DialogTitle>

@@ -63,15 +81,7 @@ );

}; const Transition = forwardRef(function Transition(props, ref) { - return <Slide direction="up" ref={ref} {...props} />; + return <StyledSlide direction="up" ref={ref} {...props} />; }); - -const useStyles = makeStyles(theme => ({ - close: { - position: 'absolute', - top: theme.spacing(1), - right: theme.spacing(0.5), - }, -})); export default AddToMyEventDialog;
M frontend/containers/ClearButton/index.tsxfrontend/containers/ClearButton/index.tsx

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

-import IconButton from '@material-ui/core/IconButton'; -import Icon from '@material-ui/core/Icon'; -import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction'; +import IconButton from '@mui/material/IconButton'; +import Icon from '@mui/material/Icon'; +import ListItemSecondaryAction from '@mui/material/ListItemSecondaryAction'; interface Props { onClick?: () => void;
M frontend/containers/CreateEvent/Step1.tsxfrontend/containers/CreateEvent/Step1.tsx

@@ -1,23 +1,24 @@

import React, {useState, useEffect, useMemo} from 'react'; -import {makeStyles} from '@material-ui/core/styles'; -import Typography from '@material-ui/core/Typography'; -import TextField from '@material-ui/core/TextField'; -import Button from '@material-ui/core/Button'; -import Checkbox from '@material-ui/core/Checkbox'; -import FormControlLabel from '@material-ui/core/FormControlLabel'; -import NextLink from 'next/link' +import Typography from '@mui/material/Typography'; +import TextField from '@mui/material/TextField'; +import Button from '@mui/material/Button'; +import Checkbox from '@mui/material/Checkbox'; +import Box from '@mui/material/Box'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import NextLink from 'next/link'; +import CardActions from '@mui/material/CardActions'; +import {useTheme} from '@mui/material/styles'; import {useTranslation} from 'react-i18next'; -import {CardActions} from '@material-ui/core'; import {useSession} from 'next-auth/react'; import useDebounce from '../../hooks/useDebounce'; import {isValidEmail} from '../../lib/formValidation'; const Step1 = ({nextStep, event, addToEvent}) => { + const theme = useTheme(); const {t} = useTranslation(); const session = useSession(); const user = session?.data?.user; const isAuthenticated = session.status === 'authenticated'; - const classes = useStyles({connected: isAuthenticated}); // States const [name, setName] = useState(event.name ?? '');

@@ -48,12 +49,13 @@ return false;

}; return ( - <form onSubmit={onNext}> + <Box component="form" onSubmit={onNext}> <TextField label={t('event.creation.name')} fullWidth autoFocus margin="dense" + variant="standard" value={name} onChange={e => setName(e.target.value)} id="NewEventName"

@@ -64,6 +66,7 @@ <>

<TextField label={t('event.creation.creator_email')} fullWidth + variant="standard" value={email} onChange={e => setEmail(e.target.value)} name="email"

@@ -71,7 +74,7 @@ type="email"

id="NewEventEmail" /> <FormControlLabel - className={classes.newsletter} + sx={{marginTop: theme.spacing(2)}} label={t('event.creation.newsletter')} control={ <Checkbox

@@ -86,7 +89,7 @@ />

</> )} <Button - className={classes.button} + sx={{marginTop: theme.spacing(2)}} type="submit" variant="contained" color="secondary"

@@ -98,14 +101,20 @@ {t('event.creation.next')}

</Button> {!isAuthenticated && ( - <div className={classes.addFromAccountSection}> + <Box sx={{marginTop: theme.spacing(8), textAlign: 'center'}}> <Typography variant="body1"> {t('event.creation.addFromAccount.title')} </Typography> <Typography variant="body2"> {t('event.creation.addFromAccount.subtitle')} </Typography> - <CardActions className={classes.actions}> + <CardActions + sx={{ + marginTop: theme.spacing(1), + justifyContent: 'space-evenly', + textAlign: 'center', + }} + > <NextLink href="/auth/register" passHref> <Button variant="text"> {t('event.creation.addFromAccount.actions.register')}

@@ -117,28 +126,10 @@ {t('event.creation.addFromAccount.actions.login')}

</Button> </NextLink> </CardActions> - </div> + </Box> )} - </form> + </Box> ); }; - -const useStyles = makeStyles(theme => ({ - button: { - marginTop: theme.spacing(2), - }, - newsletter: { - marginTop: theme.spacing(2), - }, - addFromAccountSection: { - marginTop: theme.spacing(8), - textAlign: 'center', - }, - actions: { - marginTop: theme.spacing(1), - justifyContent: 'space-evenly', - textAlign: 'center', - }, -})); export default Step1;
M frontend/containers/CreateEvent/Step2.tsxfrontend/containers/CreateEvent/Step2.tsx

@@ -1,17 +1,17 @@

import {useState} from 'react'; -import {useRouter} from 'next/router'; -import {makeStyles} from '@material-ui/core/styles'; -import TextField from '@material-ui/core/TextField'; -import Button from '@material-ui/core/Button'; -import {CircularProgress} from '@material-ui/core'; -import {DatePicker} from '@material-ui/pickers'; import moment from 'moment'; +import TextField from '@mui/material/TextField'; +import Button from '@mui/material/Button'; +import {useTheme} from '@mui/material/styles'; +import {useRouter} from 'next/router'; +import {Box, CircularProgress} from '@mui/material'; +import {DatePicker} from '@mui/x-date-pickers/DatePicker'; import {useTranslation} from 'react-i18next'; import useToastStore from '../../stores/useToastStore'; const Step2 = ({event, addToEvent, createEvent}) => { - const classes = useStyles(); const {t} = useTranslation(); + const theme = useTheme(); const router = useRouter(); const addToast = useToastStore(s => s.addToast);

@@ -39,23 +39,21 @@ return;

}; return ( - <form onSubmit={onCreate}> + <Box component="form" onSubmit={onCreate}> <DatePicker - fullWidth + renderInput={props => ( + <TextField {...props} fullWidth variant="standard" /> + )} label={t('event.creation.date')} value={date} onChange={setDate} - format="DD/MM/YYYY" - cancelLabel={t('generic.cancel')} - clearable - clearLabel={t('generic.clear')} - id="NewEventDate" /> <TextField label={t('event.creation.address')} fullWidth multiline - rowsMax={4} + variant="standard" + maxRows={4} inputProps={{maxLength: 250}} helperText={`${address.length}/250`} value={address}

@@ -67,7 +65,8 @@ <TextField

label={t('event.creation.description')} fullWidth multiline - rowsMax={4} + variant="standard" + maxRows={4} inputProps={{maxLength: 250}} helperText={ description.length === 0

@@ -81,7 +80,7 @@ id="NewEventDescription"

/> <Button disabled={loading} - className={classes.button} + sx={{marginTop: theme.spacing(2)}} variant="contained" color="secondary" fullWidth

@@ -94,14 +93,8 @@ ) : (

t('generic.create') )} </Button> - </form> + </Box> ); }; - -const useStyles = makeStyles(theme => ({ - button: { - marginTop: theme.spacing(2), - }, -})); export default Step2;
M frontend/containers/DashboardEmpty/index.tsxfrontend/containers/DashboardEmpty/index.tsx

@@ -1,20 +1,36 @@

import {useRouter} from 'next/router'; -import {makeStyles} from '@material-ui/core/styles'; -import Container from '@material-ui/core/Container'; -import Card from '@material-ui/core/Card'; -import CardActions from '@material-ui/core/CardActions'; -import CardContent from '@material-ui/core/CardContent'; -import Typography from '@material-ui/core/Typography'; -import Button from '@material-ui/core/Button'; +import { styled } from '@mui/material/styles'; +import Container from '@mui/material/Container'; +import Card from '@mui/material/Card'; +import CardActions from '@mui/material/CardActions'; +import CardContent from '@mui/material/CardContent'; +import Typography from '@mui/material/Typography'; +import Button from '@mui/material/Button'; import {useTranslation} from 'react-i18next'; +const PREFIX = 'DashboardEmpty'; + +const classes = { + container: `${PREFIX}-container` +}; + +const StyledContainer = styled(Container)(( + { + theme + } +) => ({ + [`&.${classes.container}`]: { + paddingTop: theme.spacing(8), + } +})); + const DashboardEmpty = () => { const {t} = useTranslation(); const router = useRouter(); - const classes = useStyles(); + return ( - <Container maxWidth="sm" className={classes.container}> + <StyledContainer maxWidth="sm" className={classes.container}> <Card> <CardContent> <Typography gutterBottom variant="h5" component="h1">

@@ -38,14 +54,8 @@ {t('dashboard.noEvent.create_event')}

</Button> </CardActions> </Card> - </Container> + </StyledContainer> ); }; - -const useStyles = makeStyles(theme => ({ - container: { - paddingTop: theme.spacing(8), - }, -})); export default DashboardEmpty;
M frontend/containers/DashboardEvents/EventCard.tsxfrontend/containers/DashboardEvents/EventCard.tsx

@@ -1,23 +1,41 @@

import router from 'next/router'; -import Card from '@material-ui/core/Card'; -import CardActions from '@material-ui/core/CardActions'; -import CardContent from '@material-ui/core/CardContent'; -import Typography from '@material-ui/core/Typography'; -import Button from '@material-ui/core/Button'; -import {makeStyles} from '@material-ui/styles'; +import { styled } from '@mui/material/styles'; +import Card from '@mui/material/Card'; +import CardActions from '@mui/material/CardActions'; +import CardContent from '@mui/material/CardContent'; +import Typography from '@mui/material/Typography'; +import Button from '@mui/material/Button'; import {useTranslation} from 'react-i18next'; import {EventEntity} from '../../generated/graphql'; +const PREFIX = 'EventCard'; + +const classes = { + clickable: `${PREFIX}-clickable`, + name: `${PREFIX}-name` +}; + +const StyledCard = styled(Card)({ + [`&.${classes.clickable}`]: { + cursor: 'pointer', + }, + [`& .${classes.name}`]: { + whiteSpace: 'nowrap', + textOverflow: 'ellipsis', + overflow: 'hidden', + }, +}); + interface Props { event: EventEntity; } const EventCard = ({event}: Props) => { const {t} = useTranslation(); - const classes = useStyles(); + return ( - <Card + <StyledCard className={classes.clickable} onClick={() => router.push(`/e/${event.attributes.uuid}`, undefined, {shallow: true})

@@ -44,18 +62,8 @@ </CardContent>

<CardActions> <Button color="primary">{t('dashboard.actions.see_event')}</Button> </CardActions> - </Card> + </StyledCard> ); }; -const useStyles = makeStyles({ - clickable: { - cursor: 'pointer', - }, - name: { - whiteSpace: 'nowrap', - textOverflow: 'ellipsis', - overflow: 'hidden', - }, -}); export default EventCard;
M frontend/containers/DashboardEvents/Section.tsxfrontend/containers/DashboardEvents/Section.tsx

@@ -1,7 +1,7 @@

import React from 'react'; -import Grid from '@material-ui/core/Grid'; -import Typography from '@material-ui/core/Typography'; -import Box from '@material-ui/core/Box'; +import Grid from '@mui/material/Grid'; +import Typography from '@mui/material/Typography'; +import Box from '@mui/material/Box'; import EventCard from './EventCard'; import { EventEntity } from '../../generated/graphql';
M frontend/containers/DashboardEvents/index.tsxfrontend/containers/DashboardEvents/index.tsx

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

import {useTranslation} from 'react-i18next'; -import Box from '@material-ui/core/Box'; +import Box from '@mui/material/Box'; import Section from './Section'; import { EventEntity } from '../../generated/graphql';
M frontend/containers/DrawerMenu/DrawerMenuItem.tsxfrontend/containers/DrawerMenu/DrawerMenuItem.tsx

@@ -1,7 +1,7 @@

-import Typography from '@material-ui/core/Typography'; -import Button from '@material-ui/core/Button'; -import Box from '@material-ui/core/Box'; -import useStyles from './styles'; +import Typography from '@mui/material/Typography'; +import {useTheme} from '@mui/material/styles'; +import Button from '@mui/material/Button'; +import Box from '@mui/material/Box'; interface Props { Icon: JSX.Element;

@@ -11,13 +11,58 @@ active: boolean;

} const DrawerMenuItem = ({Icon, title, onClick, active}: Props) => { - const classes = useStyles({active}); + const theme = useTheme(); + return ( - <Button className={classes.drawerMenuButton} onClick={onClick}> - <Box className={classes.icon} color="inherit"> + <Button + sx={{ + display: 'block', + position: 'relative', + minWidth: 0, + margin: 0, + width: '84px', + height: '84px', + textAlign: 'center', + color: active + ? theme.palette.background.default + : 'rgba(256, 256, 256, .76)', + + [theme.breakpoints.down('md')]: { + margin: '0 auto', + height: '56px', + width: '100%', + }, + }} + onClick={onClick} + > + <Box + sx={{ + position: 'relative', + display: 'block', + width: '100%', + padding: 0, + }} + color="inherit" + > {Icon} </Box> - <Typography color="inherit" className={classes.drawerText}> + <Typography + color="inherit" + sx={{ + position: 'relative', + fontSize: '11px', + lineHeight: '1.1em', + height: 'auto', + display: 'flex', + justifyContent: 'center', + textTransform: 'none', + + [theme.breakpoints.down('md')]: { + whiteSpace: 'nowrap', + lineHeight: '.5em', + }, + }} + > {title} </Typography> </Button>
M frontend/containers/DrawerMenu/index.tsxfrontend/containers/DrawerMenu/index.tsx

@@ -1,20 +1,56 @@

-import Drawer from '@material-ui/core/Drawer'; -import Icon from '@material-ui/core/Icon'; +import Drawer from '@mui/material/Drawer'; +import {useTheme} from '@mui/material/styles'; +import Icon from '@mui/material/Icon'; import {useTranslation} from 'react-i18next'; import {useRouter} from 'next/router'; import DrawerMenuItem from './DrawerMenuItem'; -import useStyles from './styles'; const DrawerMenu = () => { const {t} = useTranslation(); - const classes = useStyles(); + const theme = useTheme(); + const router = useRouter(); const { query: {uuid}, } = router; return ( - <Drawer variant="permanent" className={classes.drawer}> + <Drawer + variant="permanent" + sx={{ + width: '85px', + + [theme.breakpoints.down('md')]: { + width: '100%', + position: 'fixed', + bottom: 0, + zIndex: 1, + }, + + '& .MuiDrawer-paper': { + zIndex: theme.zIndex.appBar - 1, + width: '84px', + display: 'flex', + flexDirection: 'column', + boxSizing: 'border-box', + left: 0, + top: 0, + backgroundColor: '#242424', + color: 'white', + overflowX: 'hidden', + position: 'static', + + [theme.breakpoints.down('md')]: { + bottom: 0, + top: 'auto', + paddingTop: 0, + height: '56px', + width: '100%', + flexDirection: 'row', + }, + }, + }} + > <DrawerMenuItem title={t('drawer.travels')} onClick={() => {
D frontend/containers/DrawerMenu/styles.ts

@@ -1,78 +0,0 @@

-import {makeStyles} from '@material-ui/core/styles'; - -const useStyles = makeStyles(theme => ({ - drawer: () => ({ - width: '85px', - - [theme.breakpoints.down('sm')]: { - width: '100%', - position: 'fixed', - bottom: 0, - zIndex: 1, - }, - - '& .MuiDrawer-paper': { - zIndex: theme.zIndex.appBar - 1, - width: '84px', - display: 'flex', - flexDirection: 'column', - boxSizing: 'border-box', - left: 0, - top: 0, - backgroundColor: theme.overrides.MuiAppBar.colorPrimary.backgroundColor, - color: theme.overrides.MuiAppBar.colorPrimary.color, - overflowX: 'hidden', - position: 'static', - - [theme.breakpoints.down('sm')]: { - bottom: 0, - top: 'auto', - paddingTop: 0, - height: '56px', - width: '100%', - flexDirection: 'row', - }, - }, - }), - icon: { - position: 'relative', - display: 'block', - width: '100%', - height: '100%', - padding: 0, - }, - drawerMenuButton: ({active}) => ({ - display: 'block', - position: 'relative', - minWidth: 0, - margin: 0, - width: '84px', - height: '84px', - textAlign: 'center', - color: active - ? theme.palette.background.default - : 'rgba(256, 256, 256, .76)', - - [theme.breakpoints.down('sm')]: { - margin: '0 auto', - height: '56px', - width: '100%', - }, - }), - drawerText: { - position: 'relative', - fontSize: '11px', - lineHeight: '1.1em', - height: 'auto', - display: 'flex', - justifyContent: 'center', - textTransform: 'none', - - [theme.breakpoints.down('sm')]: { - whiteSpace: 'nowrap', - lineHeight: '.5em', - }, - }, -})); - -export default useStyles;
M frontend/containers/EventBar/UserIcon.tsxfrontend/containers/EventBar/UserIcon.tsx

@@ -1,27 +1,21 @@

-import Avatar from '@material-ui/core/Avatar'; -import Icon from '@material-ui/core/Icon'; -import {makeStyles} from '@material-ui/core/styles'; +import Avatar from '@mui/material/Avatar'; +import Icon from '@mui/material/Icon'; +import {useTheme} from '@mui/material/styles'; import useProfile from '../../hooks/useProfile'; const UserIcon = () => { const {profile} = useProfile(); - const classes = useStyles(); + const theme = useTheme(); if (profile) return ( - <Avatar className={classes.avatar}> + <Avatar + sx={{width: theme.spacing(3), height: theme.spacing(3), fontSize: 16}} + > {`${profile.username[0]}`.toUpperCase()} </Avatar> ); else return <Icon>more_vert</Icon>; }; - -const useStyles = makeStyles(theme => ({ - avatar: { - width: theme.spacing(3), - height: theme.spacing(3), - fontSize: 16, - }, -})); export default UserIcon;
M frontend/containers/EventBar/index.tsxfrontend/containers/EventBar/index.tsx

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

import Link from 'next/link'; -import AppBar from '@material-ui/core/AppBar'; -import Toolbar from '@material-ui/core/Toolbar'; -import Typography from '@material-ui/core/Typography'; -import IconButton from '@material-ui/core/IconButton'; -import Tooltip from '@material-ui/core/Tooltip'; -import Icon from '@material-ui/core/Icon'; -import {makeStyles} from '@material-ui/core/styles'; +import AppBar from '@mui/material/AppBar'; +import Toolbar from '@mui/material/Toolbar'; +import Typography from '@mui/material/Typography'; +import IconButton from '@mui/material/IconButton'; +import Tooltip from '@mui/material/Tooltip'; +import Icon from '@mui/material/Icon'; +import Box from '@mui/material/Box'; +import {useTheme} from '@mui/material/styles'; import {useState} from 'react'; import useProfile from '../../hooks/useProfile'; import useShare from '../../hooks/useShare';

@@ -15,42 +16,55 @@ import UserIcon from './UserIcon';

const EventBar = ({event, onAdd}) => { const {share} = useShare(); + const theme = useTheme(); const [anchorEl, setAnchorEl] = useState(null); const {connected} = useProfile(); - const classes = useStyles(); + const menuActions = useActions({onAdd, eventId: event?.id}); const appLink = connected ? '/dashboard' : `/e/${event.uuid}` || ''; return ( <AppBar - className={classes.appbar} + sx={{ + overflow: 'hidden', + minHeight: theme.mixins.toolbar.minHeight, + overflowY: 'hidden', + transition: 'height 0.3s ease', + backgroundColor: '#242424', + color: 'white', + }} color="primary" position="static" id="Menu" > <Toolbar> - <div className={classes.name}> + <Box sx={{flexGrow: 1, display: 'flex', alignItems: 'center'}}> <Link href={appLink}> - <img - className={classes.logo} - src="/assets/Logo_in_beta.svg" - alt="Logo" - /> + <Box + sx={{ + marginRight: theme.spacing(2), + width: 64, + height: 32, + cursor: 'pointer', + }} + > + <img src="/assets/Logo_in_beta.svg" alt="Logo" /> + </Box> </Link> <Tooltip title={event.name || ''}> <Typography variant="h6" noWrap id="MenuHeaderTitle" - className={classes.title} + sx={{maxWidth: `calc(100vw - ${theme.spacing(30)})`}} > {event.name} </Typography> </Tooltip> - </div> + </Box> <> <IconButton - className={classes.shareIcon} + sx={{marginRight: 0}} color="inherit" edge="end" id="ShareBtn"

@@ -60,21 +74,21 @@ title: `Caroster ${event.name}`,

url: `${window.location.href}`, }) } + size="large" > <Icon>share</Icon> </IconButton> - { - <GenericMenu - anchorEl={anchorEl} - setAnchorEl={setAnchorEl} - actions={menuActions} - /> - } + <GenericMenu + anchorEl={anchorEl} + setAnchorEl={setAnchorEl} + actions={menuActions} + /> <IconButton color="inherit" edge="end" id="MenuMoreInfo" onClick={e => setAnchorEl(e.currentTarget)} + size="large" > <UserIcon /> </IconButton>

@@ -83,34 +97,5 @@ </Toolbar>

</AppBar> ); }; - -const useStyles = makeStyles(theme => ({ - appbar: () => ({ - overflow: 'hidden', - minHeight: theme.mixins.toolbar.minHeight, - overflowY: 'hidden', - transition: 'height 0.3s ease', - }), - logo: { - marginRight: theme.spacing(2), - width: 64, - height: 32, - cursor: 'pointer', - }, - name: { - flexGrow: 1, - display: 'flex', - alignItems: 'center', - }, - title: { - maxWidth: `calc(100vw - ${theme.spacing(30)}px)`, - }, - iconButtons: { - margin: theme.spacing(0), - }, - shareIcon: { - marginRight: 0, - }, -})); export default EventBar;
M frontend/containers/Fab/index.tsxfrontend/containers/Fab/index.tsx

@@ -1,39 +1,42 @@

import React from 'react'; -import Icon from '@material-ui/core/Icon'; -import FabMui from '@material-ui/core/Fab'; -import {makeStyles} from '@material-ui/core/styles'; +import {useTheme} from '@mui/material/styles'; +import Icon from '@mui/material/Icon'; +import FabMui, {FabProps} from '@mui/material/Fab'; +import useMinimizedFab from '../../hooks/useMinimizedFab'; -const Fab = ({children = null, ...props}) => { - const variant = children ? 'extended' : 'round'; - const classes = useStyles({variant}); +const Fab = ({children = null, ...props}: FabProps) => { + const theme = useTheme(); + const isFabMinimized = useMinimizedFab(); + const variant = !isFabMinimized && children ? 'extended' : 'circular'; return ( <FabMui color="secondary" variant={variant} {...props} - className={classes.fab} + sx={{ + transition: 'all 0.1s ease', + position: 'fixed', + right: theme.spacing(3), + bottom: theme.spacing(3), + + [theme.breakpoints.down('md')]: { + right: theme.spacing(2), + bottom: theme.spacing(9), + }, + }} > - <Icon className={classes.icon}>add</Icon> - {children} + <Icon + sx={{ + marginRight: + variant === 'extended' ? theme.spacing(1) : theme.spacing(0), + }} + > + add + </Icon> + {!isFabMinimized && children} </FabMui> ); }; - -const useStyles = makeStyles(theme => ({ - fab: { - position: 'fixed', - right: theme.spacing(3), - bottom: theme.spacing(3), - - [theme.breakpoints.down('sm')]: { - right: theme.spacing(2), - bottom: theme.spacing(9), - }, - }, - icon: ({variant}) => ({ - marginRight: variant === 'extended' ? theme.spacing(1) : theme.spacing(0), - }), -})); export default Fab;
M frontend/containers/GenericMenu/Action.tsxfrontend/containers/GenericMenu/Action.tsx

@@ -1,8 +1,29 @@

import {isValidElement} from 'react'; -import Divider from '@material-ui/core/Divider'; -import Typography from '@material-ui/core/Typography'; -import MenuItem from '@material-ui/core/MenuItem'; -import {makeStyles} from '@material-ui/core/styles'; +import { styled } from '@mui/material/styles'; +import Divider from '@mui/material/Divider'; +import Typography from '@mui/material/Typography'; +import MenuItem from '@mui/material/MenuItem'; +const PREFIX = 'Action'; + +const classes = { + divider: `${PREFIX}-divider`, + textItem: `${PREFIX}-textItem` +}; + +const StyledTypography = styled(Typography)(( + { + theme + } +) => ({ + [`& .${classes.divider}`]: { + margin: theme.spacing(1, 0), + }, + + [`&.${classes.textItem}`]: { + margin: theme.spacing(1, 2), + '&:focus': {outline: 0}, + } +})); export type ActionType = { divider?: boolean;

@@ -18,7 +39,7 @@

const Action = (props: Props): JSX.Element => { const {action} = props; const {divider, onClick, id, label, ...menuItemProps} = action; - const classes = useStyles(); + if (divider) return <Divider variant="fullWidth" className={classes.divider} />;

@@ -31,20 +52,10 @@ </MenuItem>

); else return ( - <Typography variant="body1" id={id} className={classes.textItem}> + <StyledTypography variant="body1" id={id} className={classes.textItem}> {label} - </Typography> + </StyledTypography> ); }; - -const useStyles = makeStyles(theme => ({ - divider: { - margin: theme.spacing(1, 0), - }, - textItem: { - margin: theme.spacing(1, 2), - '&:focus': {outline: 0}, - }, -})); export default Action;
M frontend/containers/GenericMenu/index.tsxfrontend/containers/GenericMenu/index.tsx

@@ -1,9 +1,9 @@

-import Menu from '@material-ui/core/Menu'; +import Menu from '@mui/material/Menu'; import {useTranslation} from 'react-i18next'; +import {signOut, useSession} from 'next-auth/react'; import useSettings from '../../hooks/useSettings'; import Languages from '../Languages/MenuItem'; import Action, {ActionType} from './Action'; -import {signOut, useSession} from 'next-auth/react'; interface Props { anchorEl: Element;
M frontend/containers/GenericToolbar/index.tsxfrontend/containers/GenericToolbar/index.tsx

@@ -1,12 +1,12 @@

import {useState, useEffect} from 'react'; +import {useTheme} from '@mui/material/styles'; import {useRouter} from 'next/router'; -import {makeStyles} from '@material-ui/core/styles'; -import AppBar from '@material-ui/core/AppBar'; -import Toolbar from '@material-ui/core/Toolbar'; -import Typography from '@material-ui/core/Typography'; -import IconButton from '@material-ui/core/IconButton'; -import Avatar from '@material-ui/core/Avatar'; -import Icon from '@material-ui/core/Icon'; +import Toolbar from '@mui/material/Toolbar'; +import Typography from '@mui/material/Typography'; +import IconButton from '@mui/material/IconButton'; +import Avatar from '@mui/material/Avatar'; +import Icon from '@mui/material/Icon'; +import AppBar from '@mui/material/AppBar'; import useProfile from '../../hooks/useProfile'; import GenericMenu from '../GenericMenu'; import {ActionType} from '../GenericMenu/Action';

@@ -21,8 +21,9 @@ actions: Array<ActionType>;

goBack: () => void | null; }) => { const router = useRouter(); + const theme = useTheme(); const [anchorEl, setAnchorEl] = useState(null); - const classes = useStyles(); + const {profile} = useProfile(); useEffect(() => {

@@ -33,24 +34,31 @@ return (

<AppBar position="static" color="primary" - className={classes.appbar} + sx={{ + minHeight: theme.mixins.toolbar.minHeight, + transition: 'height 0.3s ease', + display: 'block', + backgroundColor: '#242424', + color: 'white', + }} id="Menu" > - <Toolbar> + <Toolbar sx={{display: 'flex', justifyContent: 'space-between'}}> {goBack && ( <IconButton edge="start" - className={classes.goBack} + sx={{color: theme.palette.common.white}} onClick={() => router.basePath.split('/').length > 2 ? router.back() : router.push('/dashboard') } + size="large" > <Icon>arrow_back</Icon> </IconButton> )} - <div className={classes.name}> + <div sx={{flexGrow: 1, display: 'flex', alignItems: 'center'}}> <Typography variant="h6" noWrap id="MenuHeaderTitle"> {title} </Typography>

@@ -62,9 +70,16 @@ color="inherit"

edge="end" id="MenuMoreInfo" onClick={e => setAnchorEl(e.currentTarget)} + size="large" > {profile ? ( - <Avatar className={classes.avatar}> + <Avatar + sx={{ + width: theme.spacing(3), + height: theme.spacing(3), + fontSize: 16, + }} + > {`${profile.username[0]}`.toUpperCase()} </Avatar> ) : (

@@ -83,26 +98,5 @@ </Toolbar>

</AppBar> ); }; - -const useStyles = makeStyles(theme => ({ - appbar: () => ({ - minHeight: theme.mixins.toolbar.minHeight, - transition: 'height 0.3s ease', - display: 'block', - }), - name: { - flexGrow: 1, - display: 'flex', - alignItems: 'center', - }, - avatar: { - width: theme.spacing(3), - height: theme.spacing(3), - fontSize: 16, - }, - goBack: { - color: theme.palette.common.white, - }, -})); export default GenericToolbar;
M frontend/containers/Languages/Icon.tsxfrontend/containers/Languages/Icon.tsx

@@ -1,9 +1,9 @@

import {useState} from 'react'; -import Box from '@material-ui/core/Box'; -import IconButton from '@material-ui/core/IconButton'; -import Icon from '@material-ui/core/Icon'; -import Menu from '@material-ui/core/Menu'; -import MenuItem from '@material-ui/core/MenuItem'; +import Box from '@mui/material/Box'; +import IconButton from '@mui/material/IconButton'; +import Icon from '@mui/material/Icon'; +import Menu from '@mui/material/Menu'; +import MenuItem from '@mui/material/MenuItem'; import {useTranslation} from 'react-i18next'; import {Enum_Userspermissionsuser_Lang as SupportedLocales} from '../../generated/graphql'; import withLanguagesSelection, {

@@ -27,41 +27,35 @@ setAnchorEl(null);

onChangeLang(lang); }; - return ( - <> - <Box - position="absolute" - top={displayMenu ? 56 : 0} - right={0} - zIndex={1050} - p={1} - > - <IconButton - color="primary" - aria-label="Languages" - onClick={handleClick} - > - <Icon>language</Icon> - </IconButton> - </Box> - <Menu - id="LanguagesMenu" - anchorEl={anchorEl} - keepMounted - open={Boolean(anchorEl)} - onClose={() => setAnchorEl(null)} - > - <MenuItem - disabled={language === SupportedLocales['fr']} - onClick={() => onConfirm(SupportedLocales['fr'])} - >{t`languages.fr`}</MenuItem> - <MenuItem - disabled={language === SupportedLocales['en']} - onClick={() => onConfirm(SupportedLocales['en'])} - >{t`languages.en`}</MenuItem> - </Menu> - </> - ); + return <> + <Box + position="absolute" + top={displayMenu ? 56 : 0} + right={0} + zIndex={1050} + p={1} + > + <IconButton color="primary" aria-label="Languages" onClick={handleClick} size="large"> + <Icon>language</Icon> + </IconButton> + </Box> + <Menu + id="LanguagesMenu" + anchorEl={anchorEl} + keepMounted + open={Boolean(anchorEl)} + onClose={() => setAnchorEl(null)} + > + <MenuItem + disabled={language === SupportedLocales['fr']} + onClick={() => onConfirm(SupportedLocales['fr'])} + >{t`languages.fr`}</MenuItem> + <MenuItem + disabled={language === SupportedLocales['en']} + onClick={() => onConfirm(SupportedLocales['en'])} + >{t`languages.en`}</MenuItem> + </Menu> + </>; }; export default withLanguagesSelection(IconLanguageSelection);
M frontend/containers/Languages/MenuItem.tsxfrontend/containers/Languages/MenuItem.tsx

@@ -1,7 +1,8 @@

import {useState} from 'react'; -import MenuList from '@material-ui/core/MenuList'; -import MenuItem from '@material-ui/core/MenuItem'; -import {makeStyles} from '@material-ui/core/styles'; +import MenuList from '@mui/material/MenuList'; +import MenuItem from '@mui/material/MenuItem'; +import Box from '@mui/material/Box'; +import {useTheme} from '@mui/material/styles'; import {useTranslation} from 'react-i18next'; import {Enum_Userspermissionsuser_Lang as SupportedLocales} from '../../generated/graphql'; import withLanguagesSelection, {

@@ -12,9 +13,9 @@ const Languages = ({

language, onChangeLang, }: LanguageSelectionComponentProps) => { + const theme = useTheme(); const {t} = useTranslation(); const [isSelecting, setSelecting] = useState(false); - const {languagesList} = useStyles({isSelecting}); const handleClick = event => { setSelecting(!isSelecting);

@@ -26,9 +27,17 @@ onChangeLang(lang);

}; return ( - <> + <Box> <MenuItem onClick={handleClick}>{t('menu.language')}</MenuItem> - <MenuList className={languagesList} dense> + <MenuList + sx={{ + visibility: isSelecting ? 'visible' : 'hidden', + maxHeight: isSelecting ? 'none' : 0, + padding: isSelecting ? `0 ${theme.spacing(0.5)}` : 0, + overflow: 'hidden', + }} + dense + > <MenuItem disabled={language === SupportedLocales['fr']} onClick={() => onConfirm(SupportedLocales['fr'])}

@@ -38,17 +47,8 @@ disabled={language === SupportedLocales['en']}

onClick={() => onConfirm(SupportedLocales['en'])} >{t`languages.en`}</MenuItem> </MenuList> - </> + </Box> ); }; - -const useStyles = makeStyles(theme => ({ - languagesList: ({isSelecting}: {isSelecting: boolean}) => ({ - visibility: isSelecting ? 'visible' : 'hidden', - maxHeight: isSelecting ? 'none' : 0, - padding: isSelecting ? `0 ${theme.spacing(0.5)}px` : 0, - overflow: 'hidden', - }), -})); export default withLanguagesSelection(Languages);
M frontend/containers/Loading/index.tsxfrontend/containers/Loading/index.tsx

@@ -1,23 +1,32 @@

-import CircularProgress from '@material-ui/core/CircularProgress'; -import Container from '@material-ui/core/Container'; -import {makeStyles} from '@material-ui/core/styles'; +import CircularProgress from '@mui/material/CircularProgress'; +import { styled } from '@mui/material/styles'; +import Container from '@mui/material/Container'; +const PREFIX = 'Loading'; -const Loading = () => { - const classes = useStyles(); - return ( - <Container className={classes.container}> - <CircularProgress /> - </Container> - ); +const classes = { + container: `${PREFIX}-container` }; -const useStyles = makeStyles(theme => ({ - container: { +const StyledContainer = styled(Container)(( + { + theme + } +) => ({ + [`&.${classes.container}`]: { display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100vh', - }, + } })); + +const Loading = () => { + + return ( + <StyledContainer className={classes.container}> + <CircularProgress /> + </StyledContainer> + ); +}; export default Loading;
M frontend/containers/LoginGoogle/index.tsxfrontend/containers/LoginGoogle/index.tsx

@@ -1,11 +1,11 @@

+import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; +import {useTheme} from '@mui/material/styles'; import {useTranslation} from 'react-i18next'; -import Button from '@material-ui/core/Button'; -import {makeStyles} from '@material-ui/core'; -import theme from '../../theme'; const LoginGoogle = () => { const {t} = useTranslation(); - const classes = useStyles(); + const theme = useTheme(); return ( <Button

@@ -14,8 +14,9 @@ color="primary"

fullWidth href="/api/connect/google" > - <img - className={classes.googleLogo} + <Box + component="img" + sx={{marginRight: theme.spacing(1)}} height="25px" src="/assets/google-icon.svg" alt="Google Login"

@@ -24,11 +25,5 @@ {t('signin.withGoogle')}

</Button> ); }; - -const useStyles = makeStyles((theme) => ({ - googleLogo: { - marginRight: theme.spacing(1) - } -})); export default LoginGoogle;
M frontend/containers/LostPassword/Success.jsfrontend/containers/LostPassword/Success.js

@@ -1,19 +1,45 @@

import {useTranslation} from 'react-i18next'; -import Button from '@material-ui/core/Button'; -import Icon from '@material-ui/core/Icon'; -import CardContent from '@material-ui/core/CardContent'; -import Card from '@material-ui/core/Card'; -import Typography from '@material-ui/core/Typography'; -import CardActions from '@material-ui/core/CardActions'; -import {makeStyles} from '@material-ui/core/styles'; +import { styled } from '@mui/material/styles'; +import Button from '@mui/material/Button'; +import Icon from '@mui/material/Icon'; +import CardContent from '@mui/material/CardContent'; +import Card from '@mui/material/Card'; +import Typography from '@mui/material/Typography'; +import CardActions from '@mui/material/CardActions'; import Link from 'next/link'; +const PREFIX = 'Success'; + +const classes = { + successCard: `${PREFIX}-successCard`, + successIcon: `${PREFIX}-successIcon`, + actions: `${PREFIX}-actions` +}; + +const StyledCard = styled(Card)(( + { + theme + } +) => ({ + [`&.${classes.successCard}`]: { + textAlign: 'center', + }, + + [`& .${classes.successIcon}`]: { + fontSize: theme.spacing(14), + }, + + [`& .${classes.actions}`]: { + justifyContent: 'center', + } +})); + const Success = ({email}) => { const {t} = useTranslation(); - const classes = useStyles(); + return ( - <Card className={classes.successCard}> + <StyledCard className={classes.successCard}> <CardContent> <Icon size="large" color="primary" className={classes.successIcon}> done

@@ -31,19 +57,8 @@ {t('lost_password.actions.login')}

</Button> </Link> </CardActions> - </Card> + </StyledCard> ); }; -const useStyles = makeStyles(theme => ({ - successCard: { - textAlign: 'center', - }, - successIcon: { - fontSize: theme.spacing(14), - }, - actions: { - justifyContent: 'center', - }, -})); export default Success;
M frontend/containers/LostPassword/index.jsfrontend/containers/LostPassword/index.js

@@ -1,22 +1,44 @@

import {useCallback, useState, useEffect} from 'react'; +import { styled } from '@mui/material/styles'; import {useTranslation} from 'react-i18next'; import router from 'next/router'; -import TextField from '@material-ui/core/TextField'; -import Button from '@material-ui/core/Button'; -import CardContent from '@material-ui/core/CardContent'; -import Card from '@material-ui/core/Card'; -import CircularProgress from '@material-ui/core/CircularProgress'; -import CardActions from '@material-ui/core/CardActions'; -import Link from '@material-ui/core/Link'; -import {makeStyles} from '@material-ui/core/styles'; +import TextField from '@mui/material/TextField'; +import Button from '@mui/material/Button'; +import CardContent from '@mui/material/CardContent'; +import Card from '@mui/material/Card'; +import CircularProgress from '@mui/material/CircularProgress'; +import CardActions from '@mui/material/CardActions'; +import Link from '@mui/material/Link'; import LostPasswordSuccess from './Success'; import useToastStore from '../../stores/useToastStore'; import useProfile from '../../hooks/useProfile'; import {useForgotPasswordMutation} from '../../generated/graphql'; +const PREFIX = 'LostPassword'; + +const classes = { + loader: `${PREFIX}-loader`, + actions: `${PREFIX}-actions` +}; + +const Root = styled('form')(( + { + theme + } +) => ({ + [`& .${classes.loader}`]: { + marginLeft: theme.spacing(4), + }, + + [`& .${classes.actions}`]: { + marginTop: theme.spacing(2), + justifyContent: 'flex-end', + } +})); + const LostPassword = () => { const {t} = useTranslation(); - const classes = useStyles(); + const addToast = useToastStore(s => s.addToast); const {profile} = useProfile(); const [sendForgotPassword, {loading}] = useForgotPasswordMutation();

@@ -53,7 +75,7 @@

if (!loading && isSent) return <LostPasswordSuccess email={email} />; return ( - <form onSubmit={onSubmit}> + <Root onSubmit={onSubmit}> <Card> <CardContent> <TextField

@@ -103,17 +125,8 @@ )}

</Button> </CardActions> </Card> - </form> + </Root> ); }; -const useStyles = makeStyles(theme => ({ - loader: { - marginLeft: theme.spacing(4), - }, - actions: { - marginTop: theme.spacing(2), - justifyContent: 'flex-end', - }, -})); export default LostPassword;
M frontend/containers/MailSignUpForm/SignupActions.tsxfrontend/containers/MailSignUpForm/SignupActions.tsx

@@ -1,29 +1,38 @@

import Link from 'next/link'; -import Button from '@material-ui/core/Button'; -import CardActions from '@material-ui/core/CardActions'; +import { styled } from '@mui/material/styles'; +import Button from '@mui/material/Button'; +import CardActions from '@mui/material/CardActions'; import {useTranslation} from 'react-i18next'; -import {makeStyles} from '@material-ui/core/styles'; +const PREFIX = 'SignUpActions'; + +const classes = { + actions: `${PREFIX}-actions` +}; + +const StyledCardActions = styled(CardActions)(( + { + theme + } +) => ({ + [`&.${classes.actions}`]: { + justifyContent: 'center', + marginBottom: theme.spacing(2), + } +})); const SignUpActions = () => { const {t} = useTranslation(); - const classes = useStyles(); + return ( - <CardActions className={classes.actions}> + <StyledCardActions className={classes.actions}> <Link href="/auth/login" passHref> <Button id="SignUpLogin" variant="text"> {t('signup.login')} </Button> </Link> - </CardActions> + </StyledCardActions> ); }; - -const useStyles = makeStyles(theme => ({ - actions: { - justifyContent: 'center', - marginBottom: theme.spacing(2), - }, -})); export default SignUpActions;
M frontend/containers/MailSignUpForm/index.tsxfrontend/containers/MailSignUpForm/index.tsx

@@ -1,23 +1,23 @@

import {useState, useMemo} from 'react'; -import Box from '@material-ui/core/Box'; -import Divider from '@material-ui/core/Divider'; -import Checkbox from '@material-ui/core/Checkbox'; -import FormControlLabel from '@material-ui/core/FormControlLabel'; -import TextField from '@material-ui/core/TextField'; -import Button from '@material-ui/core/Button'; -import CardContent from '@material-ui/core/CardContent'; -import Typography from '@material-ui/core/Typography'; -import CircularProgress from '@material-ui/core/CircularProgress'; +import Box from '@mui/material/Box'; +import Divider from '@mui/material/Divider'; +import Checkbox from '@mui/material/Checkbox'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import TextField from '@mui/material/TextField'; +import Button from '@mui/material/Button'; +import CardContent from '@mui/material/CardContent'; +import Typography from '@mui/material/Typography'; +import CircularProgress from '@mui/material/CircularProgress'; +import {useTheme} from '@mui/material/styles'; import {useTranslation} from 'react-i18next'; import {useRouter} from 'next/router'; -import {makeStyles} from '@material-ui/core/styles'; import useToastsStore from '../../stores/useToastStore'; -import {useRegisterMutation} from '../../generated/graphql'; import SignUpActions from './SignupActions'; +import {useRegisterMutation} from '../../generated/graphql'; const SignUp = () => { const {t, i18n} = useTranslation(); - const classes = useStyles(); + const theme = useTheme(); const addToast = useToastsStore(s => s.addToast); const router = useRouter(); const [isLoading, setIsLoading] = useState(false);

@@ -67,24 +67,35 @@ setIsLoading(false);

return false; }; + const contentSx = { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + width: '100%', + padding: theme.spacing(0, 4), + }; + return ( <form onSubmit={onSubmit}> - <CardContent className={classes.content}> + <CardContent sx={contentSx}> <Typography - variant="overline" - component="h5" + variant="h6" align="center" - className={classes.lineBreak} + sx={{ + whiteSpace: 'pre-line', + paddingBottom: theme.spacing(4), + }} > {t('signup.createForm')} </Typography> - <Box className={classes.content}> + <Box sx={contentSx}> <TextField label={t('signup.firstName')} fullWidth autoFocus margin="dense" value={firstName} + InputLabelProps={{required: false}} required={true} onChange={({target: {value = ''}}) => setFirstName(value)} id="SignUpFirstName"

@@ -93,6 +104,7 @@ />

<TextField label={t('signup.lastName')} fullWidth + InputLabelProps={{required: false}} required={true} margin="dense" value={lastName}

@@ -103,6 +115,7 @@ />

<TextField label={t('signup.email')} fullWidth + InputLabelProps={{required: false}} required={true} error={!!error} helperText={error}

@@ -116,6 +129,7 @@ />

<TextField label={t('signup.password')} fullWidth + InputLabelProps={{required: false}} required={true} margin="dense" value={password}

@@ -126,10 +140,11 @@ type="password"

/> </Box> <FormControlLabel - className={classes.newsletter} + sx={{width: '100%', margin: theme.spacing(2, 0)}} + componentsProps={{typography: {align: 'center', variant: 'body2'}}} control={ <Checkbox - className={classes.checkbox} + sx={{padding: 0, marginRight: theme.spacing(2)}} color="primary" value={newsletterConsent} onChange={({target: {checked = false}}) =>

@@ -140,20 +155,23 @@ }

label={t('signup.newsletter.consent')} /> - <Box className={classes.content}> + <Box sx={contentSx}> <Button color="primary" variant="contained" fullWidth type="submit" disabled={!canSubmit} - className={classes.button} + sx={{margin: theme.spacing(1)}} aria-disabled={!canSubmit} id="SignUpSubmit" endIcon={ isLoading && ( <CircularProgress - className={classes.loader} + sx={{ + marginLeft: '14px', + color: theme.palette.background.paper, + }} size={20} color="secondary" />

@@ -163,7 +181,9 @@ >

{t('signup.submit')} </Button> </Box> - <Box className={classes.divider}> + <Box + sx={{width: '100%', textAlign: 'center', margin: theme.spacing(2, 0)}} + > <Divider /> </Box> <Typography align="center" variant="body2">

@@ -174,40 +194,5 @@ <SignUpActions />

</form> ); }; - -const useStyles = makeStyles(theme => ({ - content: { - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - width: '100%', - padding: theme.spacing(0, 4), - }, - lineBreak: { - whiteSpace: 'pre-line', - lineHeight: 1.8, - paddingBottom: theme.spacing(4), - }, - loader: { - marginLeft: '14px', - color: theme.palette.background.paper, - }, - button: { - margin: theme.spacing(1), - }, - divider: { - width: '100%', - textAlign: 'center', - margin: theme.spacing(2, 0), - }, - newsletter: { - width: '100%', - margin: theme.spacing(2, 0), - }, - checkbox: { - padding: 0, - marginRight: theme.spacing(2), - }, -})); export default SignUp;
M frontend/containers/NewPassengerDialog/AddPassengerCommonFields.tsxfrontend/containers/NewPassengerDialog/AddPassengerCommonFields.tsx

@@ -1,9 +1,9 @@

import {Fragment} from 'react'; -import TextField from '@material-ui/core/TextField'; +import TextField from '@mui/material/TextField'; import {useTranslation} from 'react-i18next'; -import Icon from '@material-ui/core/Icon'; -import Box from '@material-ui/core/Box'; -import Typography from '@material-ui/core/Typography'; +import Icon from '@mui/material/Icon'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; import useStyles from './useStyles'; interface Props {
M frontend/containers/NewPassengerDialog/AddPassengerToTravel.tsxfrontend/containers/NewPassengerDialog/AddPassengerToTravel.tsx

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

import {FormEvent, useState} from 'react'; -import Dialog from '@material-ui/core/Dialog'; -import DialogContent from '@material-ui/core/DialogContent'; -import DialogTitle from '@material-ui/core/DialogTitle'; -import Icon from '@material-ui/core/Icon'; +import Dialog from '@mui/material/Dialog'; +import DialogContent from '@mui/material/DialogContent'; +import DialogTitle from '@mui/material/DialogTitle'; +import Icon from '@mui/material/Icon'; import {useTranslation} from 'react-i18next'; import useAddToEvents from '../../hooks/useAddToEvents'; import useEventStore from '../../stores/useEventStore';
M frontend/containers/NewPassengerDialog/AddPassengerToWaitingList.tsxfrontend/containers/NewPassengerDialog/AddPassengerToWaitingList.tsx

@@ -1,11 +1,11 @@

import {FormEvent, useState} from 'react'; -import Dialog from '@material-ui/core/Dialog'; -import DialogContent from '@material-ui/core/DialogContent'; -import DialogTitle from '@material-ui/core/DialogTitle'; -import TextField from '@material-ui/core/TextField'; -import Box from '@material-ui/core/Box'; -import Typography from '@material-ui/core/Typography'; -import Icon from '@material-ui/core/Icon'; +import Dialog from '@mui/material/Dialog'; +import DialogContent from '@mui/material/DialogContent'; +import DialogTitle from '@mui/material/DialogTitle'; +import TextField from '@mui/material/TextField'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Icon from '@mui/material/Icon'; import {useTranslation} from 'react-i18next'; import useToastStore from '../../stores/useToastStore'; import useEventStore from '../../stores/useEventStore';
M frontend/containers/NewPassengerDialog/SubmitButton.tsxfrontend/containers/NewPassengerDialog/SubmitButton.tsx

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

-import Box from '@material-ui/core/Box'; -import Button from '@material-ui/core/Button'; -import Icon from '@material-ui/core/Icon'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; +import Icon from '@mui/material/Icon'; import {ReactNode} from 'react'; import useStyles from './useStyles';
M frontend/containers/NewPassengerDialog/Transition.tsxfrontend/containers/NewPassengerDialog/Transition.tsx

@@ -1,4 +1,4 @@

-import Slide from '@material-ui/core/Slide'; +import Slide from '@mui/material/Slide'; import {forwardRef} from 'react'; const Transition = forwardRef(function Transition(props, ref) {
M frontend/containers/NewPassengerDialog/useStyles.tsfrontend/containers/NewPassengerDialog/useStyles.ts

@@ -1,4 +1,4 @@

-import { makeStyles } from "@material-ui/core/styles"; +import makeStyles from '@mui/styles/makeStyles'; const useStyles = makeStyles(theme => ({ dialogContent: {
M frontend/containers/NewTravelDialog/FAQLink.tsxfrontend/containers/NewTravelDialog/FAQLink.tsx

@@ -1,16 +1,19 @@

-import Link from '@material-ui/core/Link'; +import Link, {LinkProps} from '@mui/material/Link'; import useSettings from '../../hooks/useSettings'; interface Props { text: string; link: string; - className: string; } -const FAQLink = ({text, link, className}: Props) => { +const FAQLink = ({text, link, sx}: Props & LinkProps) => { const settings = useSettings(); - return <Link className={className} target="_blank" href={`${settings?.faq_link}${link}`}>{text}</Link>; + return ( + <Link sx={sx} target="_blank" href={`${settings?.faq_link}${link}`}> + {text} + </Link> + ); }; export default FAQLink;
M frontend/containers/NewTravelDialog/index.tsxfrontend/containers/NewTravelDialog/index.tsx

@@ -1,22 +1,23 @@

import {useState, forwardRef, useMemo, useEffect} from 'react'; -import {makeStyles} from '@material-ui/core/styles'; -import Dialog from '@material-ui/core/Dialog'; -import DialogActions from '@material-ui/core/DialogActions'; -import DialogContent from '@material-ui/core/DialogContent'; -import DialogTitle from '@material-ui/core/DialogTitle'; -import Button from '@material-ui/core/Button'; -import Slide from '@material-ui/core/Slide'; -import TextField from '@material-ui/core/TextField'; -import Slider from '@material-ui/core/Slider'; -import Typography from '@material-ui/core/Typography'; -import {DatePicker, TimePicker} from '@material-ui/pickers'; +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogTitle from '@mui/material/DialogTitle'; +import Button from '@mui/material/Button'; +import Slide from '@mui/material/Slide'; +import TextField from '@mui/material/TextField'; +import Slider from '@mui/material/Slider'; +import Typography from '@mui/material/Typography'; +import {Box, Divider} from '@mui/material'; +import {useTheme} from '@mui/material/styles'; +import {DatePicker} from '@mui/x-date-pickers/DatePicker'; +import {TimePicker} from '@mui/x-date-pickers/TimePicker'; import moment, {Moment} from 'moment'; import {useTranslation} from 'react-i18next'; import useEventStore from '../../stores/useEventStore'; import useActions from './useActions'; +import FAQLink from './FAQLink'; import {Vehicle} from '../../generated/graphql'; -import {Box, Divider} from '@material-ui/core'; -import FAQLink from './FAQLink'; interface Props { context: {

@@ -28,7 +29,8 @@ }

const NewTravelDialog = ({context, toggle}: Props) => { const {t} = useTranslation(); - const classes = useStyles(); + const theme = useTheme(); + const event = useEventStore(s => s.event); const {createTravel} = useActions({event});

@@ -84,6 +86,18 @@

clearState(); }; + const halfWidthFieldSx = { + margin: `0 ${theme.spacing(1.5)}`, + width: `calc(50% - ${theme.spacing(3)})`, + + '& > .MuiFormLabel-root': { + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + width: '100%', + overflow: 'hidden', + }, + }; + return ( <Dialog fullWidth

@@ -96,17 +110,19 @@ }}

TransitionComponent={Transition} > <form onSubmit={onCreate}> - <DialogTitle className={classes.title}> + <DialogTitle sx={{paddingBottom: 0}}> {t('travel.creation.title')} </DialogTitle> - <DialogContent className={classes.content}> - <Typography className={classes.sectionTitle}> + <DialogContent sx={{padding: `${theme.spacing(2)} 0`}}> + <Typography + sx={{...addSpacing(theme, 1), paddingBottom: theme.spacing(1.5)}} + > {t('travel.creation.car.title')} </Typography> <TextField variant="outlined" size="small" - className={classes.field} + sx={{...addSpacing(theme, 1), paddingBottom: theme.spacing(1)}} label={t('travel.creation.name')} fullWidth helperText=" "

@@ -118,7 +134,7 @@ />

<TextField variant="outlined" size="small" - className={classes.field} + sx={{...addSpacing(theme, 1), paddingBottom: theme.spacing(1)}} label={t('travel.creation.phone')} fullWidth inputProps={{type: 'tel'}}

@@ -128,20 +144,23 @@ onChange={e => setPhone(e.target.value)}

name="phone" FormHelperTextProps={{ component: () => ( - <FAQLink - className={classes.faqHelper} - link={t('travel.creation.phoneHelper.faq')} - text={t('travel.creation.phoneHelper.why')} - /> + <Typography variant="caption"> + <FAQLink + sx={{textDecoration: 'none'}} + link={t('travel.creation.phoneHelper.faq')} + text={t('travel.creation.phoneHelper.why')} + /> + </Typography> ), }} id="NewTravelPhone" /> - <div className={classes.slider}> + <Box sx={addSpacing(theme, 1)}> <Typography variant="caption"> {t('travel.creation.seats')} </Typography> <Slider + size="small" value={seats} onChange={(e, value) => setSeats(value)} step={1}

@@ -151,47 +170,58 @@ max={MARKS.length}

valueLabelDisplay="auto" id="NewTravelSeats" /> - </div> - <Divider className={classes.divider} /> - <Typography className={classes.sectionTitle}> + </Box> + <Divider + sx={{ + margin: `${theme.spacing(2)} 0`, + }} + /> + <Typography + sx={{...addSpacing(theme, 1), paddingBottom: theme.spacing(1.5)}} + > {t('travel.creation.travel.title')} </Typography> - <Box className={classes.halfWidthWrapper}> + <Box sx={addSpacing(theme, 0.5)}> <DatePicker - className={classes.halfWidthField} - inputVariant="outlined" - size="small" + renderInput={props => ( + <TextField + {...props} + variant="outlined" + size="small" + helperText=" " + sx={halfWidthFieldSx} + /> + )} label={t('travel.creation.date')} - helperText=" " value={date} onChange={setDate} - format="DD/MM/YYYY" - cancelLabel={t('generic.cancel')} autoFocus - id="NewTravelDateTime" /> <TimePicker - className={classes.halfWidthField} - inputVariant="outlined" - size="small" + renderInput={props => ( + <TextField + {...props} + variant="outlined" + size="small" + helperText=" " + sx={halfWidthFieldSx} + /> + )} label={t('travel.creation.time')} - helperText=" " value={time} onChange={setTime} - cancelLabel={t('generic.cancel')} ampm={false} minutesStep={5} - id="NewTravelTime" /> </Box> <TextField variant="outlined" size="small" - className={classes.field} + sx={{...addSpacing(theme, 1), paddingBottom: theme.spacing(1)}} label={t('travel.creation.meeting')} fullWidth multiline - rowsMax={4} + maxRows={4} inputProps={{maxLength: 250}} helperText={`${meeting.length}/250`} value={meeting}

@@ -202,11 +232,11 @@ />

<TextField variant="outlined" size="small" - className={classes.field} + sx={{...addSpacing(theme, 1), paddingBottom: theme.spacing(1)}} label={t('travel.creation.notes')} fullWidth multiline - rowsMax={4} + maxRows={4} inputProps={{maxLength: 250}} helperText={`${details.length}/250`} value={details}

@@ -215,7 +245,11 @@ name="details"

id="NewTravelDetails" /> </DialogContent> - <DialogActions className={classes.actions}> + <DialogActions + sx={{ + paddingTop: 0, + }} + > <Button color="primary" id="NewTravelCancel"

@@ -257,51 +291,8 @@ label: value,

})); const addSpacing = (theme, ratio) => ({ - margin: `0 ${theme.spacing(3 * ratio)}px`, - width: `calc(100% - ${theme.spacing(6 * ratio)}px)`, + margin: `0 ${theme.spacing(3 * ratio)}`, + width: `calc(100% - ${theme.spacing(6 * ratio)})`, }); - -const useStyles = makeStyles(theme => ({ - title: { - paddingBottom: 0, - }, - sectionTitle: { - ...addSpacing(theme, 1), - paddingBottom: theme.spacing(1.5), - }, - content: { - padding: `${theme.spacing(2)}px 0`, - }, - faqHelper: { - fontSize: '12px', - }, - field: { - ...addSpacing(theme, 1), - paddingBottom: theme.spacing(1), - }, - halfWidthWrapper: { - ...addSpacing(theme, 0.5), - }, - halfWidthField: { - margin: `0 ${theme.spacing(1.5)}px`, - width: `calc(50% - ${theme.spacing(3)}px)`, - - '& > .MuiFormLabel-root': { - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - width: '100%', - overflow: 'hidden', - }, - }, - slider: { - ...addSpacing(theme, 1), - }, - divider: { - margin: `${theme.spacing(2)}px 0`, - }, - actions: { - paddingTop: 0, - }, -})); export default NewTravelDialog;
M frontend/containers/OnBoardingTour/index.tsxfrontend/containers/OnBoardingTour/index.tsx

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

import Joyride from 'react-joyride'; -import {useTheme} from '@material-ui/core/styles'; +import {useTheme} from '@mui/material/styles'; import {useTranslation} from 'react-i18next'; import useTour from '../../hooks/useTour';
M frontend/containers/PassengersList/Passenger.tsxfrontend/containers/PassengersList/Passenger.tsx

@@ -1,13 +1,14 @@

import {ReactNode} from 'react'; -import ListItemAvatar from '@material-ui/core/ListItemAvatar'; -import ListItemIcon from '@material-ui/core/ListItemIcon'; -import ListItemText from '@material-ui/core/ListItemText'; -import Icon from '@material-ui/core/Icon'; -import {makeStyles} from '@material-ui/core/styles'; +import {useTheme} from '@mui/material/styles'; +import ListItemAvatar from '@mui/material/ListItemAvatar'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import Icon from '@mui/material/Icon'; import {useTranslation} from 'react-i18next'; import {PassengerEntity} from '../../generated/graphql'; import useProfile from '../../hooks/useProfile'; -import Chip from '@material-ui/core/Chip'; +import Chip from '@mui/material/Chip'; +import Box from '@mui/material/Box'; interface Props { passenger?: PassengerEntity;

@@ -17,8 +18,9 @@ }

const Passenger = (props: Props) => { const {passenger, button, isTravel} = props; + const theme = useTheme(); const {t} = useTranslation(); - const classes = useStyles(); + const {userId} = useProfile(); const isUser = `${userId}` === passenger?.attributes.user?.data?.id;

@@ -26,14 +28,14 @@ const showLocation = isTravel ? false : passenger.attributes.location;

if (passenger) { return ( - <> + <Box> <ListItemText primary={ <> {passenger.attributes.name} {isUser && ( <Chip - className={classes.me} + sx={{marginLeft: theme.spacing(2)}} label={t('generic.me')} variant="outlined" />

@@ -43,7 +45,7 @@ }

secondary={showLocation} /> {button} - </> + </Box> ); } else return (

@@ -55,21 +57,10 @@ </ListItemIcon>

</ListItemAvatar> <ListItemText primary={t('travel.passengers.empty')} - classes={{ - root: classes.empty, - }} + sx={{color: theme.palette.text.secondary}} /> </> ); }; - -const useStyles = makeStyles(theme => ({ - empty: { - color: theme.palette.text.secondary, - }, - me: { - marginLeft: theme.spacing(2), - }, -})); export default Passenger;
M frontend/containers/PassengersList/index.tsxfrontend/containers/PassengersList/index.tsx

@@ -1,9 +1,26 @@

-import List from '@material-ui/core/List'; -import ListItem from '@material-ui/core/ListItem'; -import {makeStyles} from '@material-ui/core/styles'; +import List from '@mui/material/List'; +import {styled, useTheme} from '@mui/material/styles'; +import ListItem from '@mui/material/ListItem'; import Passenger from './Passenger'; import {PassengerEntity} from '../../generated/graphql'; +const PREFIX = 'PassengersList'; + +const classes = { + container: `${PREFIX}-container`, + passenger: `${PREFIX}-passenger`, +}; + +const Root = styled('div')(({theme}) => ({ + [`&.${classes.container}`]: { + padding: theme.spacing(0, 0, 1, 0), + }, + + [`& .${classes.passenger}`]: { + paddingRight: theme.spacing(12), + }, +})); + interface Props { passengers: PassengerEntity[]; Button: ({

@@ -23,7 +40,8 @@

const PassengersList = (props: Props) => { const {passengers, places, Button, onClick, onPress, disabled, isTravel} = props; - const classes = useStyles(); + const theme = useTheme(); + let list = passengers; if (places) {

@@ -34,12 +52,12 @@ : emptyList;

} return ( - <div className={classes.container}> + <Root sx={{padding: theme.spacing(0, 0, 1, 0)}}> <List disablePadding> {!!list && list.map((passenger, index) => ( <ListItem - className={classes.passenger} + sx={{paddingRight: theme.spacing(12)}} key={index} disabled={disabled} button={!!onPress}

@@ -59,17 +77,8 @@ />

</ListItem> ))} </List> - </div> + </Root> ); }; - -const useStyles = makeStyles(theme => ({ - container: { - padding: theme.spacing(0, 0, 1, 0), - }, - passenger: { - paddingRight: theme.spacing(12), - }, -})); export default PassengersList;
M frontend/containers/Profile/EditPassword.tsxfrontend/containers/Profile/EditPassword.tsx

@@ -1,11 +1,27 @@

import React from 'react'; -import Card from '@material-ui/core/Card'; -import {makeStyles} from '@material-ui/core/styles'; -import CardContent from '@material-ui/core/CardContent'; -import CardActions from '@material-ui/core/CardActions'; -import Button from '@material-ui/core/Button'; +import { styled } from '@mui/material/styles'; +import Card from '@mui/material/Card'; +import CardContent from '@mui/material/CardContent'; +import CardActions from '@mui/material/CardActions'; +import Button from '@mui/material/Button'; import {useTranslation} from 'react-i18next'; -import TextField from '@material-ui/core/TextField'; +import TextField from '@mui/material/TextField'; + +const PREFIX = 'EditPassword'; + +const classes = { + actions: `${PREFIX}-actions` +}; + +const Root = styled('form')(( + { + theme + } +) => ({ + [`& .${classes.actions}`]: { + justifyContent: 'flex-end', + } +})); const EditPassword = ({ oldPassword,

@@ -17,9 +33,9 @@ save,

cancel, }) => { const {t} = useTranslation(); - const classes = useStyles(); + return ( - <form + <Root onSubmit={evt => { evt?.preventDefault?.(); save();

@@ -65,13 +81,8 @@ {t('profile.actions.save_new_password')}

</Button> </CardActions> </Card> - </form> + </Root> ); }; -const useStyles = makeStyles(theme => ({ - actions: { - justifyContent: 'flex-end', - }, -})); export default EditPassword;
M frontend/containers/Profile/ProfileField.tsxfrontend/containers/Profile/ProfileField.tsx

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

-import Typography from '@material-ui/core/Typography'; -import TextField, {TextFieldProps} from '@material-ui/core/TextField'; +import Typography from '@mui/material/Typography'; +import TextField, {TextFieldProps} from '@mui/material/TextField'; type Props = TextFieldProps & { isEditing: boolean;
M frontend/containers/Profile/index.tsxfrontend/containers/Profile/index.tsx

@@ -1,19 +1,37 @@

import {useState} from 'react'; -import Card from '@material-ui/core/Card'; -import CardContent from '@material-ui/core/CardContent'; -import CardActions from '@material-ui/core/CardActions'; -import Button from '@material-ui/core/Button'; -import {makeStyles} from '@material-ui/core'; +import { styled } from '@mui/material/styles'; +import Card from '@mui/material/Card'; +import CardContent from '@mui/material/CardContent'; +import CardActions from '@mui/material/CardActions'; +import Button from '@mui/material/Button'; import {useTranslation} from 'react-i18next'; import EditPassword from './EditPassword'; import ProfileField from './ProfileField'; import useToastStore from '../../stores/useToastStore'; import {useUpdateMeMutation} from '../../generated/graphql'; +const PREFIX = 'Profile'; + +const classes = { + actions: `${PREFIX}-actions` +}; + +// TODO jss-to-styled codemod: The Fragment root was replaced by div. Change the tag if needed. +const Root = styled('div')(( + { + theme + } +) => ({ + [`& .${classes.actions}`]: { + marginTop: theme.spacing(2), + justifyContent: 'flex-end', + } +})); + const Profile = ({profile, logout}) => { const {t} = useTranslation(); const addToast = useToastStore(s => s.addToast); - const classes = useStyles(); + const [updateProfile] = useUpdateMeMutation(); const [isEditing, setIsEditing] = useState(false); const [isEditingPassword, setIsEditingPassword] = useState(false);

@@ -76,7 +94,7 @@ />

); return ( - <> + (<Root> <Card> <CardContent> <ProfileField

@@ -150,15 +168,8 @@ </Button>

)} </CardActions> </Card> - </> + </Root>) ); }; - -const useStyles = makeStyles(theme => ({ - actions: { - marginTop: theme.spacing(2), - justifyContent: 'flex-end', - }, -})); export default Profile;
M frontend/containers/RemoveDialog/index.tsxfrontend/containers/RemoveDialog/index.tsx

@@ -1,10 +1,10 @@

import React from 'react'; -import Dialog from '@material-ui/core/Dialog'; -import DialogActions from '@material-ui/core/DialogActions'; -import DialogContent from '@material-ui/core/DialogContent'; -import DialogContentText from '@material-ui/core/DialogContentText'; -import Slide from '@material-ui/core/Slide'; -import Button from '@material-ui/core/Button'; +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogContentText from '@mui/material/DialogContentText'; +import Slide from '@mui/material/Slide'; +import Button from '@mui/material/Button'; import {useTranslation} from 'react-i18next'; const Transition = React.forwardRef(function Transition(props, ref) {
M frontend/containers/ResetPassword/index.tsxfrontend/containers/ResetPassword/index.tsx

@@ -1,12 +1,29 @@

import React from 'react'; -import Card from '@material-ui/core/Card'; -import CardHeader from '@material-ui/core/CardHeader'; -import CardContent from '@material-ui/core/CardContent'; -import CardActions from '@material-ui/core/CardActions'; -import Button from '@material-ui/core/Button'; +import { styled } from '@mui/material/styles'; +import Card from '@mui/material/Card'; +import CardHeader from '@mui/material/CardHeader'; +import CardContent from '@mui/material/CardContent'; +import CardActions from '@mui/material/CardActions'; +import Button from '@mui/material/Button'; import {useTranslation} from 'react-i18next'; -import TextField from '@material-ui/core/TextField'; -import {makeStyles} from '@material-ui/core/styles'; +import TextField from '@mui/material/TextField'; +const PREFIX = 'ResetPassword'; + +const classes = { + actions: `${PREFIX}-actions` +}; + +const StyledCard = styled(Card)(( + { + theme + } +) => ({ + [`& .${classes.actions}`]: { + justifyContent: 'flex-end', + marginTop: theme.spacing(2), + } +})); + const ResetPassword = ({ password, setPassword,

@@ -16,9 +33,9 @@ error,

isLoading, }) => { const {t} = useTranslation(); - const classes = useStyles(); + return ( - <Card> + <StyledCard> <CardHeader title={t('profile.actions.change_password')} /> <CardContent> <TextField

@@ -59,13 +76,7 @@ >

{t('lost_password.actions.save_new_password')} </Button> </CardActions> - </Card> + </StyledCard> ); }; -const useStyles = makeStyles(theme => ({ - actions: { - justifyContent: 'flex-end', - marginTop: theme.spacing(2), - }, -})); export default ResetPassword;
M frontend/containers/ShareEvent/index.tsxfrontend/containers/ShareEvent/index.tsx

@@ -1,15 +1,14 @@

-import Icon from '@material-ui/core/Icon'; -import Button, {ButtonProps} from '@material-ui/core/Button'; +import Icon from '@mui/material/Icon'; +import Button, {ButtonProps} from '@mui/material/Button'; import {useTranslation} from 'react-i18next'; import useShare from '../../hooks/useShare'; interface Props { title: string; url: string; - className?: string; } -const ShareEvent = ({title, url, className}: ButtonProps & Props) => { +const ShareEvent = ({title, url, sx}: ButtonProps & Props) => { const {t} = useTranslation(); const {share, navigatorHasShareCapability} = useShare();

@@ -23,7 +22,7 @@ variant="outlined"

color="primary" startIcon={<Icon>share</Icon>} onClick={() => share({title, url})} - className={className} + sx={sx} > {text} </Button>
M frontend/containers/SignInForm/index.tsxfrontend/containers/SignInForm/index.tsx

@@ -1,20 +1,19 @@

import {useState, useMemo} from 'react'; import NextLink from 'next/link'; -import {makeStyles} from '@material-ui/core/styles'; -import TextField from '@material-ui/core/TextField'; -import Button from '@material-ui/core/Button'; -import Link from '@material-ui/core/Link'; -import Typography from '@material-ui/core/Typography'; -import CardContent from '@material-ui/core/CardContent'; -import FormHelperText from '@material-ui/core/FormHelperText'; -import CardActions from '@material-ui/core/CardActions'; +import TextField from '@mui/material/TextField'; +import Button from '@mui/material/Button'; +import Link from '@mui/material/Link'; +import Typography from '@mui/material/Typography'; +import CardContent from '@mui/material/CardContent'; +import FormHelperText from '@mui/material/FormHelperText'; +import CardActions from '@mui/material/CardActions'; +import Divider from '@mui/material/Divider'; +import Box from '@mui/material/Box'; +import {useTheme} from '@mui/material/styles'; import {useTranslation} from 'react-i18next'; import {signIn} from 'next-auth/react'; import useAddToEvents from '../../hooks/useAddToEvents'; -import useRedirectUrlStore from '../../stores/useRedirectUrl'; import LoginGoogle from '../LoginGoogle'; -import Divider from '@material-ui/core/Divider'; -import Box from '@material-ui/core/Box'; interface Props { error?: string;

@@ -23,10 +22,10 @@

const SignIn = (props: Props) => { const {error} = props; const {t} = useTranslation(); + const theme = useTheme(); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const {saveStoredEvents} = useAddToEvents(); - const classes = useStyles(); const canSubmit = useMemo( () => [email, password].filter(s => s.length < 4).length === 0,

@@ -49,9 +48,21 @@

return false; }; + const spaceAround = { + width: '100%', + textAlign: 'center', + margin: theme.spacing(2, 0), + }; + return ( <form onSubmit={onSubmit}> - <CardContent className={classes.content}> + <CardContent + sx={{ + display: 'flex', + flexDirection: 'column', + padding: theme.spacing(0, 6), + }} + > <Typography variant="h6" align="center"> {t('signin.title')} </Typography>

@@ -60,11 +71,18 @@ <FormHelperText error={true}>

{t(`signin.errors.${error}`)} </FormHelperText> )} - <Box className={classes.content}> + <Box + sx={{ + display: 'flex', + flexDirection: 'column', + padding: {sm: theme.spacing(0, 6), xs: 0}, + }} + > <TextField label={t('signin.email')} fullWidth required={true} + InputLabelProps={{required: false}} margin="dense" value={email} onChange={({target: {value = ''}}) => setEmail(value)}

@@ -76,6 +94,7 @@ />

<TextField label={t('signin.password')} fullWidth + InputLabelProps={{required: false}} required={true} margin="dense" value={password}

@@ -87,7 +106,7 @@ error={!!error}

/> </Box> - <Box className={classes.divider}> + <Box sx={spaceAround}> <NextLink href="/auth/lost-password" passHref> <Link> <Typography align="center" variant="body2">

@@ -96,28 +115,45 @@ </Typography>

</Link> </NextLink> </Box> - <Button - color="primary" - variant="contained" - type="submit" - disabled={!canSubmit} - aria-disabled={!canSubmit} - id="SignInSubmit" + <Box + sx={{ + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + width: '100%', + padding: {md: theme.spacing(0, 16), sm: theme.spacing(0, 6)}, + }} > - {t('signin.login')} - </Button> - <Box className={classes.divider}> - <Typography>{t('signin.or')}</Typography> + <Button + color="primary" + variant="contained" + fullWidth + type="submit" + disabled={!canSubmit} + aria-disabled={!canSubmit} + id="SignInSubmit" + > + {t('signin.login')} + </Button> + <Box sx={spaceAround}> + <Typography>{t('signin.or')}</Typography> + </Box> + <LoginGoogle /> </Box> - <LoginGoogle /> - <Box className={classes.divider}> + <Box sx={spaceAround}> <Divider /> </Box> <Typography align="center" variant="body2"> {t('signin.no_account')} </Typography> </CardContent> - <CardActions className={classes.actions} align="center"> + <CardActions + sx={{ + justifyContent: 'center', + marginBottom: theme.spacing(2), + textAlign: 'center', + }} + > <NextLink href="/auth/register" passHref> <Button size="small" id="SignInRegister"> {t('signin.register')}

@@ -128,20 +164,4 @@ </form>

); }; -const useStyles = makeStyles(theme => ({ - content: { - display: 'flex', - flexDirection: 'column', - padding: theme.spacing(0, 6), - }, - actions: { - justifyContent: 'center', - marginBottom: theme.spacing(2), - }, - divider: { - width: '100%', - textAlign: 'center', - margin: theme.spacing(2, 0), - }, -})); export default SignIn;
M frontend/containers/Travel/Header.tsxfrontend/containers/Travel/Header.tsx

@@ -1,12 +1,13 @@

-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 Typography from '@mui/material/Typography'; +import IconButton from '@mui/material/IconButton'; +import Icon from '@mui/material/Icon'; +import Box from '@mui/material/Box'; +import Link from '@mui/material/Link'; +import { useTheme } from '@mui/material/styles'; import {useTranslation} from 'react-i18next'; -import Link from '@material-ui/core/Link'; +import getMapsLink from '../../lib/getMapsLink'; import {Travel} from '../../generated/graphql'; -import getMapsLink from '../../lib/getMapsLink'; interface Props { travel: Travel;

@@ -15,15 +16,19 @@ }

const Header = (props: Props) => { const {travel, toggleEditing} = props; - const classes = useStyles(); + const theme = useTheme() const {t} = useTranslation(); return ( - <div className={classes.header}> + <Box sx={{padding: theme.spacing(2)}}> <IconButton size="small" color="primary" - className={classes.editBtn} + sx={{position: 'absolute', + top: 0, + right: 0, + margin: theme.spacing(1), + zIndex: theme.zIndex.speedDial}} onClick={toggleEditing} id="EditTravelBtn" >

@@ -38,17 +43,17 @@ <Typography variant="h5" id="TravelName">

{travel.vehicleName} </Typography> {!!travel.phone_number && ( - <div className={classes.section}> + <Box sx={{marginTop: theme.spacing(2),}}> <Typography variant="subtitle2"> {t('travel.fields.phone')} </Typography> <Typography variant="body2" id="TravelPhone"> {travel.phone_number} </Typography> - </div> + </Box> )} {!!travel.meeting && ( - <div className={classes.section}> + <Box sx={{marginTop: theme.spacing(2),}}> <Typography variant="subtitle2"> {t('travel.fields.meeting_point')} </Typography>

@@ -62,36 +67,20 @@ >

{travel.meeting} </Link> </Typography> - </div> + </Box> )} {!!travel.details && ( - <div className={classes.section}> + <Box sx={{marginTop: theme.spacing(2),}}> <Typography variant="subtitle2"> {t('travel.fields.details')} </Typography> <Typography variant="body2" id="TravelDetails"> {travel.details} </Typography> - </div> + </Box> )} - </div> + </Box> ); }; - -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;
M frontend/containers/Travel/HeaderEditing.tsxfrontend/containers/Travel/HeaderEditing.tsx

@@ -1,20 +1,22 @@

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 Typography from '@mui/material/Typography'; +import IconButton from '@mui/material/IconButton'; +import Icon from '@mui/material/Icon'; +import Button from '@mui/material/Button'; +import TextField from '@mui/material/TextField'; +import Slider from '@mui/material/Slider'; import moment, {Moment} from 'moment'; +import {useTheme} from '@mui/material/styles'; +import {DatePicker} from '@mui/x-date-pickers/DatePicker'; +import {TimePicker} from '@mui/x-date-pickers/TimePicker'; import {useTranslation} from 'react-i18next'; import RemoveDialog from '../RemoveDialog'; import useActions from './useActions'; +import Box from '@mui/material/Box'; const HeaderEditing = ({travel, toggleEditing}) => { - const classes = useStyles(); const {t} = useTranslation(); + const theme = useTheme(); const actions = useActions({travel}); const [removing, toggleRemoving] = useReducer(i => !i, false); const dateMoment = useMemo(

@@ -66,37 +68,45 @@ toggleEditing();

}; return ( - <div className={classes.header}> + <Box sx={{padding: theme.spacing(2)}}> <form onSubmit={onSave}> <IconButton size="small" color="primary" type="submit" - className={classes.edit} + sx={{ + position: 'absolute', + top: 0, + right: 0, + margin: theme.spacing(1), + zIndex: theme.zIndex.speedDial, + }} > <Icon>done</Icon> </IconButton> <DatePicker + renderInput={props => ( + <TextField + {...props} + fullWidth + helperText=" " + sx={{marginTop: theme.spacing(3)}} + /> + )} 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=" " + renderInput={props => ( + <TextField {...props} fullWidth helperText=" " /> + )} value={time} onChange={setTime} - cancelLabel={t('generic.cancel')} ampm={false} minutesStep={5} - id="NewTravelTime" /> <TextField label={t('travel.creation.name')}

@@ -120,7 +130,7 @@ <TextField

label={t('travel.creation.meeting')} fullWidth multiline - rowsMax={4} + maxRows={4} inputProps={{maxLength: 250}} helperText={`${meeting.length}/250`} value={meeting}

@@ -132,7 +142,7 @@ <TextField

label={t('travel.creation.notes')} fullWidth multiline - rowsMax={4} + maxRows={4} inputProps={{maxLength: 250}} helperText={`${details.length}/250`} value={details}

@@ -140,7 +150,7 @@ onChange={e => setDetails(e.target.value)}

name="details" id="EditTravelDetails" /> - <div className={classes.slider}> + <Box sx={{marginTop: theme.spacing(2)}}> <Typography variant="caption"> {t('travel.creation.seats')} </Typography>

@@ -157,9 +167,19 @@ max={8}

valueLabelDisplay="auto" id="EditTravelSeats" /> - </div> + </Box> </form> - <div className={classes.actions}> + <Box + sx={{ + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + margin: theme.spacing(2, 0), + '& > *:first-child': { + marginBottom: theme.spacing(2), + }, + }} + > <Button variant="outlined" color="primary"

@@ -176,14 +196,14 @@ id="TravelRemove"

> {t('generic.remove')} </Button> - </div> + </Box> <RemoveDialog text={t('travel.actions.remove_alert')} open={removing} onClose={toggleRemoving} onRemove={onRemove} /> - </div> + </Box> ); };

@@ -193,33 +213,5 @@ `${moment(date).format('YYYY-MM-DD')} ${moment(time).format('HH:mm')}`,

'YYYY-MM-DD HH:mm' ).toISOString(); }; - -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;
M frontend/containers/Travel/index.tsxfrontend/containers/Travel/index.tsx

@@ -1,7 +1,7 @@

import {useMemo, 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 { styled } from '@mui/material/styles'; +import Divider from '@mui/material/Divider'; +import Paper from '@mui/material/Paper'; import {Travel as TravelType} from '../../generated/graphql'; import ClearButton from '../ClearButton'; import PassengersList from '../PassengersList';

@@ -11,6 +11,22 @@ import Header from './Header';

import useActions from './useActions'; import useProfile from '../../hooks/useProfile'; +const PREFIX = 'Travel'; + +const classes = { + root: `${PREFIX}-root` +}; + +const StyledPaper = styled(Paper)(( + { + theme + } +) => ({ + [`&.${classes.root}`]: { + position: 'relative', + } +})); + interface Props { travel: TravelType & {id: string}; getAddPassengerFunction: (addSelf: boolean) => () => void;

@@ -18,7 +34,7 @@ }

const Travel = (props: Props) => { const {travel} = props; - const classes = useStyles(); + const [isEditing, toggleEditing] = useReducer(i => !i, false); const actions = useActions({travel}); const {userId, connected} = useProfile();

@@ -36,7 +52,7 @@ return !isInTravel;

}, [travel, userId]); return ( - <Paper className={classes.root}> + <StyledPaper className={classes.root}> {isEditing ? ( <HeaderEditing travel={travel} toggleEditing={toggleEditing} /> ) : (

@@ -61,14 +77,8 @@ <ClearButton icon="close" onClick={onClick} tabIndex={-1} />

)} /> )} - </Paper> + </StyledPaper> ); }; - -const useStyles = makeStyles(theme => ({ - root: { - position: 'relative', - }, -})); export default Travel;
M frontend/containers/Travel/useActions.tsxfrontend/containers/Travel/useActions.tsx

@@ -10,7 +10,7 @@ Travel,

useUpdatePassengerMutation, TravelInput, } from '../../generated/graphql'; -import Button from '@material-ui/core/Button'; +import Button from '@mui/material/Button'; interface Props { travel: Travel & {id: string};
M frontend/containers/TravelColumns/AddTravel.tsxfrontend/containers/TravelColumns/AddTravel.tsx

@@ -1,32 +1,45 @@

import React from 'react'; -import Button from '@material-ui/core/Button'; -import Container from '@material-ui/core/Container'; -import useMediaQuery from '@material-ui/core/useMediaQuery'; -import {useTheme, makeStyles} from '@material-ui/core/styles'; +import {styled} from '@mui/material/styles'; +import Button from '@mui/material/Button'; +import Container from '@mui/material/Container'; +import useMediaQuery from '@mui/material/useMediaQuery'; +import {useTheme} from '@mui/material/styles'; import {useTranslation} from 'react-i18next'; import clsx from 'clsx'; +const PREFIX = 'AddTravel'; + +const classes = { + container: `${PREFIX}-container`, + button: `${PREFIX}-button`, +}; + +const StyledContainer = styled(Container)(({theme}) => ({ + [`& .${classes.container}`]: {}, + + [`& .${classes.button}`]: {}, +})); + interface Props { toggle: () => void; } const AddTravel = (props: Props) => { const {toggle} = props; - const classes = useStyles(); + const {t} = useTranslation(); const theme = useTheme(); - const matches = useMediaQuery(theme.breakpoints.up('sm')); - let containerClasses = [classes.container] - if (matches) { - containerClasses = [...containerClasses, 'tour_travel_add'] - } return ( - <Container + <StyledContainer maxWidth="sm" - className={clsx(containerClasses)} + sx={{display: 'flex', justifyContent: 'center', padding: 0}} > <Button - classes={{containedSecondary: classes.button}} + sx={{ + backgroundColor: theme.palette.background.paper, + color: theme.palette.text.primary, + '&:hover': {color: theme.palette.secondary.contrastText}, + }} fullWidth variant="contained" color="primary"

@@ -34,21 +47,8 @@ onClick={toggle}

> {t('travel.creation.title')} </Button> - </Container> + </StyledContainer> ); }; - -const useStyles = makeStyles(theme => ({ - container: { - display: 'flex', - justifyContent: 'center', - padding: 0, - }, - button: { - backgroundColor: theme.palette.background.paper, - color: theme.palette.text.primary, - '&:hover': {color: theme.palette.secondary.contrastText}, - }, -})); export default AddTravel;
D frontend/containers/TravelColumns/CustomArrow.tsx

@@ -1,55 +0,0 @@

-import {makeStyles} from '@material-ui/core/styles'; -import Box from '@material-ui/core/Box'; -import clsx from 'clsx'; -import {CSSProperties} from '@material-ui/styles'; - -interface Props { - className?: string; - style?: CSSProperties; - left?: number; - right?: number; - onClick?: () => {}; -} - -const CustomArrow = (props: Props) => { - const {className, style, onClick, left, right} = props; - const classes = useStyles(); - - return ( - <Box - className={clsx(className, classes.arrow)} - style={{ - ...style, - left, - right, - display: 'flex', - }} - onClick={onClick} - /> - ); -}; - -const useStyles = makeStyles(theme => ({ - arrow: { - position: 'fixed', - zIndex: 0, - width: 40, - minHeight: '100vh', - alignItems: 'center', - justifyContent: 'center', - transition: 'background-color 0.3s ease, box-shadow 0.3s ease', - '&:not(.slick-disabled)': { - backgroundColor: 'rgba(255,255,255,1)', - boxShadow: '0 0 6px rgb(1 1 1 / 20%)', - }, - '&:not(.slick-disabled):hover': { - boxShadow: '0 0 1px rgb(1 1 1 / 20%)', - }, - '&::before': { - fontSize: 23, - color: theme.palette.primary.main, - }, - }, -})); - -export default CustomArrow;
M frontend/containers/TravelColumns/Dots.tsxfrontend/containers/TravelColumns/Dots.tsx

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

import {createPortal} from 'react-dom'; -import Box from '@material-ui/core/Box'; +import Box from '@mui/material/Box'; const Dots = ({children}) => { const element = document.getElementById('slider-dots');
M frontend/containers/TravelColumns/NoCar.tsxfrontend/containers/TravelColumns/NoCar.tsx

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

-import Typography from '@material-ui/core/Typography'; -import Box from '@material-ui/core/Box'; -import {makeStyles} from '@material-ui/core/styles'; +import Typography from '@mui/material/Typography'; +import {useTheme} from '@mui/material/styles'; +import Box from '@mui/material/Box'; import {useTranslation} from 'react-i18next'; import ShareEvent from '../ShareEvent';

@@ -13,44 +13,41 @@

const url = typeof window !== 'undefined' ? window.location.href : ''; const NoCar = ({eventName, title, image}: Props) => { - const classes = useStyles({image}); const {t} = useTranslation(); + const theme = useTheme(); return ( - <Box className={classes.noTravel}> + <Box + sx={{ + margin: `${theme.spacing(4)} auto`, + marginTop: image ? 0 : theme.spacing(8), + width: '280px', + maxWidth: '100%', + paddingBottom: theme.spacing(16), + textAlign: 'center', + }} + > <Typography variant="h5">{title}</Typography> - <img className={classes.noTravelImage} src="/assets/car.png" /> + <Box + component="img" + sx={{ + width: image ? '100%' : 0, + height: image ? 'auto' : theme.spacing(6), + [theme.breakpoints.down('md')]: { + width: image ? '50%' : 0, + }, + }} + src="/assets/car.png" + /> <Typography>{t('event.no_travel.desc')}</Typography> <ShareEvent color="primary" - className={classes.share} + sx={{marginTop: theme.spacing(6), backgroundColor: '#fff'}} title={`Caroster ${eventName}`} url={`${url}`} /> </Box> ); }; - -const useStyles = makeStyles(theme => ({ - noTravel: ({image}) => ({ - margin: `${theme.spacing(4)}px auto`, - marginTop: image ? 0 : theme.spacing(8), - width: '280px', - maxWidth: '100%', - paddingBottom: theme.spacing(16), - textAlign: 'center', - }), - noTravelImage: ({image}) => ({ - width: image ? '100%' : 0, - height: image ? 'auto' : theme.spacing(6), - [theme.breakpoints.down('sm')]: { - width: image ? '50%' : 0, - }, - }), - share: { - marginTop: theme.spacing(6), - backgroundColor: '#fff', - }, -})); export default NoCar;
D frontend/containers/TravelColumns/_SliderSettings.tsx

@@ -1,55 +0,0 @@

-import CustomArrow from './CustomArrow'; -import Dots from './Dots'; - -const sliderSettings = { - accessibility: true, - dots: true, - appendDots: dots => <Dots>{dots}</Dots>, - nextArrow: <CustomArrow right={0} />, - prevArrow: <CustomArrow left={80} />, - arrows: true, - infinite: false, - speed: 500, - initialSlide: 0, - lazyLoad: true, - draggable: true, - swipeToSlide: false, - swipe: true, - slidesToScroll: 5, - slidesToShow: 5, - autoPlay: false, - responsive: [ - { - breakpoint: 720, - settings: { - slidesToScroll: 1, - slidesToShow: 1, - arrows: false, - }, - }, - { - breakpoint: 960, - settings: { - slidesToScroll: 2, - slidesToShow: 2, - arrows: false, - }, - }, - { - breakpoint: 1280, - settings: { - slidesToScroll: 2, - slidesToShow: 2, - }, - }, - { - breakpoint: 1920, - settings: { - slidesToScroll: 3, - slidesToShow: 3, - }, - }, - ], -}; - -export default sliderSettings;
M frontend/containers/TravelColumns/index.tsxfrontend/containers/TravelColumns/index.tsx

@@ -1,18 +1,18 @@

-import {useRef, useState} from 'react'; -import {makeStyles} from '@material-ui/core/styles'; -import Container from '@material-ui/core/Container'; -import Slider from 'react-slick'; +import {useState} from 'react'; import {useTranslation} from 'react-i18next'; -import {Travel as TravelData, TravelEntity} from '../../generated/graphql'; +import {useTheme} from '@mui/material/styles'; +import Container from '@mui/material/Container'; +import Masonry from '@mui/lab/Masonry'; +import Box from '@mui/material/Box'; import useEventStore from '../../stores/useEventStore'; import useToastStore from '../../stores/useToastStore'; import useProfile from '../../hooks/useProfile'; import useAddToEvents from '../../hooks/useAddToEvents'; -import {AddPassengerToTravel} from '../NewPassengerDialog'; -import Travel from '../Travel'; -import sliderSettings from './_SliderSettings'; import usePassengersActions from '../../hooks/usePassengersActions'; +import Travel from '../Travel'; import NoCar from './NoCar'; +import {Travel as TravelData, TravelEntity} from '../../generated/graphql'; +import {AddPassengerToTravel} from '../NewPassengerDialog'; type TravelType = TravelData & {id: string};

@@ -21,14 +21,14 @@ toggle: () => void;

} const TravelColumns = (props: Props) => { + const theme = useTheme(); const event = useEventStore(s => s.event); const travels = event?.travels?.data || []; - const slider = useRef(null); const {t} = useTranslation(); const addToast = useToastStore(s => s.addToast); const {addToEvent} = useAddToEvents(); const {profile, userId} = useProfile(); - const classes = useStyles(); + const [newPassengerTravelContext, toggleNewPassengerToTravel] = useState<{ travel: TravelType; } | null>(null);

@@ -52,8 +52,43 @@ }

}; return ( - <div className={classes.container}> - <div className={classes.dots} id="slider-dots" /> + <Box + sx={{ + paddingLeft: theme.spacing(2), + paddingRight: theme.spacing(2), + [theme.breakpoints.down('md')]: { + paddingLeft: theme.spacing(), + paddingRight: theme.spacing(), + }, + display: 'flex', + flexDirection: 'column', + }} + > + <Box + sx={{ + height: '56px', + overflow: 'auto', + '& overflow': '-moz-scrollbars-none', + '-ms-overflow-style': 'none', + '&::-webkit-scrollbar': { + height: '0 !important', + }, + '& .slick-dots': { + position: 'static', + '& li': { + display: 'block', + '& button:before': { + fontSize: '12px', + }, + }, + }, + '& .slick-dots li:first-child button:before, & .slick-dots li:last-child button:before': + { + color: theme.palette.primary.main, + }, + }} + id="slider-dots" + /> {(travels?.length === 0 && ( <NoCar image

@@ -61,14 +96,25 @@ eventName={event?.name}

title={t('event.no_travel.title')} /> )) || ( - <Slider ref={slider} {...sliderSettings}> + <Masonry columns={{xl: 4, lg: 3, md: 2, sm: 2, xs: 1}} spacing={1}> {sortedTravels?.map(({id, attributes}) => { const travel = {id, ...attributes}; return ( <Container key={travel.id} maxWidth="sm" - className={classes.slide} + sx={{ + padding: theme.spacing(1), + marginBottom: theme.spacing(10), + outline: 'none', + '& > *': { + cursor: 'default', + }, + + [theme.breakpoints.down('md')]: { + marginBottom: `calc(${theme.spacing(10)} + 56px)`, + }, + }} > <Travel travel={travel}

@@ -81,13 +127,27 @@ />

</Container> ); })} - <Container maxWidth="sm" className={classes.slide}> + <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)`, + }, + }} + > <NoCar eventName={event?.name} title={t('event.no_other_travel.title')} /> </Container> - </Slider> + </Masonry> )} {!!newPassengerTravelContext && ( <AddPassengerToTravel

@@ -96,7 +156,7 @@ toggle={() => toggleNewPassengerToTravel(null)}

travel={newPassengerTravelContext.travel} /> )} - </div> + </Box> ); };

@@ -111,52 +171,5 @@ if (dateA === dateB)

return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(); else return dateA - dateB; }; - -const useStyles = makeStyles(theme => ({ - container: { - paddingLeft: theme.spacing(6), - paddingRight: theme.spacing(6), - [theme.breakpoints.down('sm')]: { - paddingLeft: theme.spacing(), - paddingRight: theme.spacing(), - }, - display: 'flex', - flexDirection: 'column', - }, - dots: { - height: '56px', - overflow: 'auto', - '& overflow': '-moz-scrollbars-none', - '-ms-overflow-style': 'none', - '&::-webkit-scrollbar': { - height: '0 !important', - }, - '& .slick-dots': { - position: 'static', - '& li': { - display: 'block', - '& button:before': { - fontSize: '12px', - }, - }, - }, - '& .slick-dots li:first-child button:before, & .slick-dots li:last-child button:before': - { - color: theme.palette.primary.main, - }, - }, - slide: { - padding: theme.spacing(1), - marginBottom: theme.spacing(10), - outline: 'none', - '& > *': { - cursor: 'default', - }, - - [theme.breakpoints.down('sm')]: { - marginBottom: `${theme.spacing(10) + 56}px`, - }, - }, -})); export default TravelColumns;
M frontend/containers/VehicleChoiceDialog/VehicleItem.tsxfrontend/containers/VehicleChoiceDialog/VehicleItem.tsx

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

-import Typography from '@material-ui/core/Typography'; -import ListItem from '@material-ui/core/ListItem'; -import Box from '@material-ui/core/Box'; -import Button from '@material-ui/core/Button'; -import {makeStyles} from '@material-ui/core/styles'; +import Typography from '@mui/material/Typography'; +import {styled} from '@mui/material/styles'; +import ListItem from '@mui/material/ListItem'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; import {useTranslation} from 'react-i18next'; import { Vehicle,

@@ -10,6 +10,40 @@ FindUserVehiclesDocument,

useDeleteVehicleMutation, } from '../../generated/graphql'; import useProfile from '../../hooks/useProfile'; +import useTheme from '@mui/styles/useTheme'; + +const PREFIX = 'VehicleItem'; + +const classes = { + item: `${PREFIX}-item`, + section: `${PREFIX}-section`, + label: `${PREFIX}-label`, + select: `${PREFIX}-select`, +}; + +const StyledListItem = styled(ListItem)(({theme}) => ({ + [`&.${classes.item}`]: { + display: 'block', + padding: theme.spacing(2, 3), + }, + + [`& .${classes.section}`]: { + maxWidth: '75%', + marginBottom: theme.spacing(1), + }, + + [`& .${classes.label}`]: { + fontWeight: 'bold', + opacity: 0.6, + marginRight: theme.spacing(2), + }, + + [`& .${classes.select}`]: { + display: 'block', + maxWidth: '300px', + margin: `0 auto ${theme.spacing(1.5)} auto`, + }, +})); interface Props { vehicle: Vehicle & {id: string};

@@ -18,7 +52,8 @@ }

const VehicleItem = ({vehicle, select}: Props) => { const {t} = useTranslation(); - const classes = useStyles(); + const theme = useTheme(); + const {userId} = useProfile(); const [deleteVehicleMutation] = useDeleteVehicleMutation({ variables: {id: vehicle.id},

@@ -26,9 +61,12 @@ refetchQueries: [{query: FindUserVehiclesDocument, variables: {userId}}],

}); return ( - <ListItem className={classes.item}> + <StyledListItem sx={{display: 'block', padding: theme.spacing(2, 3)}}> <Box> - <Typography variant="overline" className={classes.label}> + <Typography + variant="overline" + sx={{fontWeight: 'bold', opacity: 0.6, marginRight: theme.spacing(2)}} + > {t('travel.vehicle.name')} </Typography> <Button

@@ -40,17 +78,30 @@ >

{t('generic.delete')} </Button> </Box> - <Typography variant="body1" className={classes.section}> + <Typography + variant="body1" + sx={{maxWidth: '75%', marginBottom: theme.spacing(1)}} + > {vehicle.name} </Typography> - <Typography variant="overline" className={classes.label}> + <Typography + variant="overline" + sx={{fontWeight: 'bold', opacity: 0.6, marginRight: theme.spacing(2)}} + > {t('travel.vehicle.seats_number')} </Typography> - <Typography variant="body1" className={classes.section}> + <Typography + variant="body1" + sx={{maxWidth: '75%', marginBottom: theme.spacing(1)}} + > {vehicle.seats} </Typography> <Button - className={classes.select} + sx={{ + display: 'block', + maxWidth: '300px', + margin: `0 auto ${theme.spacing(1.5)} auto`, + }} fullWidth color="primary" variant="contained"

@@ -58,29 +109,8 @@ onClick={select}

> {t('generic.select')} </Button> - </ListItem> + </StyledListItem> ); }; - -const useStyles = makeStyles(theme => ({ - item: { - display: 'block', - padding: theme.spacing(2, 3), - }, - section: { - maxWidth: '75%', - marginBottom: theme.spacing(1), - }, - label: { - fontWeight: 'bold', - opacity: 0.6, - marginRight: theme.spacing(2), - }, - select: { - display: 'block', - maxWidth: '300px', - margin: `0 auto ${theme.spacing(1.5)}px auto`, - }, -})); export default VehicleItem;
M frontend/containers/VehicleChoiceDialog/index.tsxfrontend/containers/VehicleChoiceDialog/index.tsx

@@ -1,19 +1,19 @@

import {forwardRef, Fragment} from 'react'; -import {makeStyles} from '@material-ui/core/styles'; -import Dialog from '@material-ui/core/Dialog'; -import DialogActions from '@material-ui/core/DialogActions'; -import DialogContent from '@material-ui/core/DialogContent'; -import DialogTitle from '@material-ui/core/DialogTitle'; -import Button from '@material-ui/core/Button'; -import List from '@material-ui/core/List'; -import Container from '@material-ui/core/Container'; -import Divider from '@material-ui/core/Divider'; -import Slide from '@material-ui/core/Slide'; +import {styled, useTheme} from '@mui/material/styles'; +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogTitle from '@mui/material/DialogTitle'; +import Button from '@mui/material/Button'; +import List from '@mui/material/List'; +import Container from '@mui/material/Container'; +import Divider from '@mui/material/Divider'; +import Slide from '@mui/material/Slide'; import {useTranslation} from 'react-i18next'; import VehicleItem from './VehicleItem'; -import Typography from '@material-ui/core/Typography'; +import Typography from '@mui/material/Typography'; import {Vehicle, VehicleEntity} from '../../generated/graphql'; -import Icon from '@material-ui/core/Icon'; +import Icon from '@mui/material/Icon'; interface Props { open: boolean;

@@ -34,8 +34,8 @@ toggle,

toggleNewTravel, vehicles, }: Props) => { + const theme = useTheme(); const {t} = useTranslation(); - const classes = useStyles(); return ( <Dialog

@@ -47,11 +47,23 @@ TransitionComponent={Transition}

> <DialogTitle> {t('travel.vehicle.title')} - <Icon className={classes.closeIcon} onClick={toggle} aria-label="close"> + <Icon + sx={{ + position: 'absolute', + top: theme.spacing(2), + right: theme.spacing(2), + cursor: 'pointer', + padding: theme.spacing(0.5), + width: theme.spacing(4), + height: theme.spacing(4), + }} + onClick={toggle} + aria-label="close" + > close </Icon> </DialogTitle> - <DialogContent dividers className={classes.content}> + <DialogContent dividers sx={{padding: 0}}> {(vehicles && vehicles.length != 0 && ( <List> {vehicles.map(({id, attributes}, index, {length}) => (

@@ -71,14 +83,14 @@ </Fragment>

))} </List> )) || ( - <Container className={classes.empty}> + <Container sx={{padding: theme.spacing(2, 3)}}> <Typography>{t('travel.vehicle.empty')}</Typography> </Container> )} </DialogContent> - <DialogActions className={classes.actions}> + <DialogActions sx={{justifyContent: 'center'}}> <Button - className={classes.new} + sx={{maxWidth: '300px'}} color="primary" fullWidth variant="outlined"

@@ -97,29 +109,5 @@

const Transition = forwardRef(function Transition(props, ref) { return <Slide direction="up" ref={ref} {...props} />; }); - -const useStyles = makeStyles(theme => ({ - actions: { - justifyContent: 'center', - }, - content: { - padding: 0, - }, - new: { - maxWidth: '300px', - }, - empty: { - padding: theme.spacing(2, 3), - }, - closeIcon: { - position: 'absolute', - top: theme.spacing(2), - right: theme.spacing(2), - cursor: 'pointer', - padding: theme.spacing(0.5), - width: theme.spacing(4), - height: theme.spacing(4), - }, -})); export default VehicleChoiceDialog;
M frontend/containers/WaitingList/AssignButton.tsxfrontend/containers/WaitingList/AssignButton.tsx

@@ -1,42 +1,46 @@

-import IconButton from '@material-ui/core/IconButton'; -import Icon from '@material-ui/core/Icon'; -import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction'; -import { makeStyles } from '@material-ui/core/styles'; +import IconButton from '@mui/material/IconButton'; +import Icon from '@mui/material/Icon'; +import {useTheme} from '@mui/material/styles'; import {useTranslation} from 'react-i18next'; +import ListItemSecondaryAction from '@mui/material/ListItemSecondaryAction'; interface Props { onClick: () => void; tabIndex?: number; - disabled: boolean + disabled: boolean; } const AssignButton = (props: Props) => { const {onClick, tabIndex} = props; - const classes = useStyles(); + const theme = useTheme(); + const {t} = useTranslation(); return ( - <ListItemSecondaryAction className={classes.action} onClick={onClick} tabIndex={tabIndex}> - <IconButton className={classes.button} disabled={props.disabled}> + <ListItemSecondaryAction + sx={{ + top: theme.spacing(3), + }} + onClick={onClick} + tabIndex={tabIndex} + > + <IconButton + sx={{ + margin: theme.spacing(1, 0, 0, 0), + borderRadius: 1, + fontSize: theme.typography.subtitle1, + padding: 0, + lineHeight: 1.5, + color: props.disabled ? 'black' : theme.palette.primary.main, + }} + disabled={props.disabled} + size="large" + > {t('passenger.actions.place')} <Icon>chevron_right</Icon> </IconButton> </ListItemSecondaryAction> ); }; - -const useStyles = makeStyles(theme => ({ - action: { - top: theme.spacing(3), - }, - 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

@@ -1,23 +1,106 @@

import moment from 'moment'; -import Link from '@material-ui/core/Link'; -import Typography from '@material-ui/core/Typography'; -import Button from '@material-ui/core/Button'; -import Slide from '@material-ui/core/Slide'; -import Dialog from '@material-ui/core/Dialog'; -import AppBar from '@material-ui/core/AppBar'; -import Toolbar from '@material-ui/core/Toolbar'; -import ListItem from '@material-ui/core/ListItem'; -import List from '@material-ui/core/List'; -import IconButton from '@material-ui/core/IconButton'; -import Icon from '@material-ui/core/Icon'; -import Box from '@material-ui/core/Box'; -import {makeStyles} from '@material-ui/core/styles'; +import { styled } from '@mui/material/styles'; +import Link from '@mui/material/Link'; +import Typography from '@mui/material/Typography'; +import Button from '@mui/material/Button'; +import Slide from '@mui/material/Slide'; +import Dialog from '@mui/material/Dialog'; +import AppBar from '@mui/material/AppBar'; +import Toolbar from '@mui/material/Toolbar'; +import ListItem from '@mui/material/ListItem'; +import List from '@mui/material/List'; +import IconButton from '@mui/material/IconButton'; +import Icon from '@mui/material/Icon'; +import Box from '@mui/material/Box'; import {useTranslation} from 'react-i18next'; import {forwardRef} from 'react'; import getMapsLink from '../../lib/getMapsLink'; import ShareEvent from '../ShareEvent'; import {Passenger, TravelEntity, Travel} from '../../generated/graphql'; +const PREFIX = 'TravelDialog'; + +const classes = { + offset: `${PREFIX}-offset`, + rtlBox: `${PREFIX}-rtlBox`, + info: `${PREFIX}-info`, + listItem: `${PREFIX}-listItem`, + date: `${PREFIX}-date`, + button: `${PREFIX}-button`, + noTravel: `${PREFIX}-noTravel`, + noTravelImage: `${PREFIX}-noTravelImage`, + share: `${PREFIX}-share` +}; + +const StyledSlide = styled(Slide)(( + { + theme + } +) => ({ + [`& .${classes.offset}`]: { + paddingTop: theme.spacing(7), + }, + + [`& .${classes.rtlBox}`]: { + display: 'flex', + padding: 0, + margin: 0, + direction: 'rtl', + [theme.breakpoints.down('md')]: { + display: 'block', + paddingBottom: theme.spacing(1), + }, + }, + + [`& .${classes.info}`]: { + padding: theme.spacing(0, 4, 0, 0), + width: '350px', + [theme.breakpoints.down('md')]: { + padding: theme.spacing(0.5, 1), + width: '100%', + textAlign: 'left', + }, + }, + + [`& .${classes.listItem}`]: { + display: 'flex', + justifyContent: 'left', + [theme.breakpoints.down('md')]: { + display: 'block', + textAlign: 'center', + }, + }, + + [`& .${classes.date}`]: { + textTransform: 'capitalize', + padding: theme.spacing(0, 0, 0.5, 0), + }, + + [`& .${classes.button}`]: { + padding: theme.spacing(1, 15), + margin: theme.spacing(1), + }, + + [`& .${classes.noTravel}`]: { + margin: '120px auto 0 auto', + width: '330px', + maxWidth: '100%', + textAlign: 'center', + }, + + [`& .${classes.noTravelImage}`]: { + width: 'calc(100% - 2px)', + [theme.breakpoints.down('md')]: { + width: 'calc(50% - 2px)', + }, + }, + + [`& .${classes.share}`]: { + marginTop: theme.spacing(2), + backgroundColor: '#fff', + } +})); + interface Props { eventName: string; travels: Array<TravelEntity>;

@@ -35,7 +118,7 @@ open,

onClose, onSelect, }: Props) => { - const classes = useStyles(); + const {t} = useTranslation(); const availableTravels = travels?.filter(

@@ -53,7 +136,7 @@ TransitionComponent={Transition}

> <AppBar> <Toolbar> - <IconButton onClick={onClose} color="inherit"> + <IconButton onClick={onClose} color="inherit" size="large"> <Icon>arrow_back_ios</Icon> </IconButton> <Typography variant="h5">

@@ -71,7 +154,6 @@ <Typography>

{t('passenger.creation.no_travel.desc', {name: passenger?.name})} </Typography> <ShareEvent - color="primary" className={classes.share} title={`Caroster ${eventName}`} url={`${typeof window !== 'undefined' ? window.location.href : ''}`}

@@ -127,64 +209,7 @@ );

}; const Transition = forwardRef(function Transition(props, ref) { - return <Slide direction="up" ref={ref} {...props} />; + return <StyledSlide direction="up" ref={ref} {...props} />; }); - -const useStyles = makeStyles(theme => ({ - offset: { - paddingTop: theme.spacing(7), - }, - rtlBox: { - display: 'flex', - padding: 0, - margin: 0, - direction: 'rtl', - [theme.breakpoints.down('sm')]: { - display: 'block', - paddingBottom: theme.spacing(1), - }, - }, - info: { - padding: theme.spacing(0, 4, 0, 0), - width: '350px', - [theme.breakpoints.down('sm')]: { - padding: theme.spacing(0.5, 1), - width: '100%', - textAlign: 'left', - }, - }, - listItem: { - display: 'flex', - justifyContent: 'left', - [theme.breakpoints.down('sm')]: { - display: 'block', - textAlign: 'center', - }, - }, - date: { - textTransform: 'capitalize', - padding: theme.spacing(0, 0, 0.5, 0), - }, - button: { - padding: theme.spacing(1, 15), - margin: theme.spacing(1), - }, - noTravel: { - margin: '120px auto 0 auto', - width: '330px', - maxWidth: '100%', - textAlign: 'center', - }, - noTravelImage: { - width: 'calc(100% - 2px)', - [theme.breakpoints.down('sm')]: { - width: 'calc(50% - 2px)', - }, - }, - share: { - marginTop: theme.spacing(2), - backgroundColor: '#fff', - }, -})); export default TravelDialog;
M frontend/containers/WaitingList/index.tsxfrontend/containers/WaitingList/index.tsx

@@ -1,10 +1,10 @@

import {useReducer, useState, useMemo, useCallback} from 'react'; -import Typography from '@material-ui/core/Typography'; -import IconButton from '@material-ui/core/IconButton'; -import Icon from '@material-ui/core/Icon'; -import Paper from '@material-ui/core/Paper'; -import Divider from '@material-ui/core/Divider'; -import {makeStyles} from '@material-ui/core/styles'; +import { styled } from '@mui/material/styles'; +import Typography from '@mui/material/Typography'; +import IconButton from '@mui/material/IconButton'; +import Icon from '@mui/material/Icon'; +import Paper from '@mui/material/Paper'; +import Divider from '@mui/material/Divider'; import {Trans, useTranslation} from 'react-i18next'; import useToastStore from '../../stores/useToastStore'; import useEventStore from '../../stores/useEventStore';

@@ -15,12 +15,53 @@ import AddPassengerButtons from '../AddPassengerButtons';

import ClearButton from '../ClearButton'; import AssignButton from './AssignButton'; import TravelDialog from './TravelDialog'; -import Button from '@material-ui/core/Button'; +import Button from '@mui/material/Button'; import router from 'next/dist/client/router'; -import Box from '@material-ui/core/Box'; -import Container from '@material-ui/core/Container'; +import Box from '@mui/material/Box'; +import Container from '@mui/material/Container'; import {PassengerEntity} from '../../generated/graphql'; +const PREFIX = 'WaitingList'; + +const classes = { + root: `${PREFIX}-root`, + card: `${PREFIX}-card`, + header: `${PREFIX}-header`, + editBtn: `${PREFIX}-editBtn` +}; + +const StyledBox = styled(Box)(( + { + theme + } +) => ({ + [`&.${classes.root}`]: { + position: 'relative', + paddingLeft: '80px', + + [theme.breakpoints.down('md')]: { + paddingLeft: 0, + }, + }, + + [`& .${classes.card}`]: { + marginTop: theme.spacing(6), + }, + + [`& .${classes.header}`]: { + position: 'relative', + padding: theme.spacing(2), + }, + + [`& .${classes.editBtn}`]: { + position: 'absolute', + top: 0, + right: 0, + margin: theme.spacing(1), + zIndex: theme.zIndex.speedDial, + } +})); + interface Props { getToggleNewPassengerDialogFunction: (addSelf: boolean) => () => void; canAddSelf: boolean;

@@ -30,7 +71,7 @@ const WaitingList = ({

getToggleNewPassengerDialogFunction, canAddSelf, }: Props) => { - const classes = useStyles(); + const {t} = useTranslation(); const clearToast = useToastStore(s => s.clearToast); const event = useEventStore(s => s.event);

@@ -116,7 +157,7 @@ <AssignButton onClick={onClick} tabIndex={-1} disabled={disabled} />

); return ( - <Box className={classes.root}> + <StyledBox className={classes.root}> <Container maxWidth="sm" className={classes.card}> <Paper> <div className={classes.header}>

@@ -170,7 +211,7 @@ open={!!addingPassenger}

onClose={() => setAddingPassenger(null)} onSelect={selectTravel} /> - </Box> + </StyledBox> ); };

@@ -181,30 +222,5 @@ if (dateA === dateB)

return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(); else return dateA - dateB; }; - -const useStyles = makeStyles(theme => ({ - root: { - position: 'relative', - paddingLeft: '80px', - - [theme.breakpoints.down('sm')]: { - paddingLeft: 0, - }, - }, - card: { - marginTop: theme.spacing(6), - }, - header: { - position: 'relative', - padding: theme.spacing(2), - }, - editBtn: { - position: 'absolute', - top: 0, - right: 0, - margin: theme.spacing(1), - zIndex: theme.zIndex.speedDial, - }, -})); export default WaitingList;
M frontend/containers/WelcomeDialog/index.tsxfrontend/containers/WelcomeDialog/index.tsx

@@ -1,20 +1,32 @@

import React from 'react'; -import {makeStyles} from '@material-ui/core/styles'; -import Dialog from '@material-ui/core/Dialog'; -import CardMedia from '@material-ui/core/CardMedia'; -import DialogActions from '@material-ui/core/DialogActions'; -import DialogContent from '@material-ui/core/DialogContent'; -import DialogContentText from '@material-ui/core/DialogContentText'; -import Typography from '@material-ui/core/Typography'; -import Button from '@material-ui/core/Button'; +import { styled } from '@mui/material/styles'; +import Dialog from '@mui/material/Dialog'; +import CardMedia from '@mui/material/CardMedia'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogContentText from '@mui/material/DialogContentText'; +import Typography from '@mui/material/Typography'; +import Button from '@mui/material/Button'; import {useTranslation} from 'react-i18next'; import useTourStore from '../../stores/useTourStore'; +const PREFIX = 'WelcomeDialog'; + +const classes = { + media: `${PREFIX}-media` +}; + +const StyledDialog = styled(Dialog)({ + [`& .${classes.media}`]: { + height: 240, + }, +}); + const WelcomeDialog = () => { const {t} = useTranslation(); const showWelcome = useTourStore(s => s.showWelcome); const setTour = useTourStore(s => s.setTour); - const classes = useStyles(); + const onStartTour = () => setTour({showWelcome: false, run: true, step: 0, prev: -1});

@@ -22,7 +34,7 @@

const onCancel = () => setTour({showWelcome: false}); return ( - <Dialog open={showWelcome} fullWidth maxWidth="xs"> + <StyledDialog open={showWelcome} fullWidth maxWidth="xs"> <CardMedia className={classes.media} image="/assets/Caroster_Octree_Social.jpg"

@@ -48,14 +60,8 @@ >

{t('tour.welcome.onboard')} </Button> </DialogActions> - </Dialog> + </StyledDialog> ); }; - -const useStyles = makeStyles({ - media: { - height: 240, - }, -}); export default WelcomeDialog;
A frontend/hooks/useEventListener.ts

@@ -0,0 +1,69 @@

+import { RefObject, useEffect, useRef } from 'react' +import useIsomorphicLayoutEffect from './useIsomorphicLayoutEffect' + +// Window Event based useEventListener interface +function useEventListener<K extends keyof WindowEventMap>( + eventName: K, + handler: (event: WindowEventMap[K]) => void, + element?: undefined, + options?: boolean | AddEventListenerOptions, +): void + +// Element Event based useEventListener interface +function useEventListener< + K extends keyof HTMLElementEventMap, + T extends HTMLElement = HTMLDivElement, +>( + eventName: K, + handler: (event: HTMLElementEventMap[K]) => void, + element: RefObject<T>, + options?: boolean | AddEventListenerOptions, +): void + +// Document Event based useEventListener interface +function useEventListener<K extends keyof DocumentEventMap>( + eventName: K, + handler: (event: DocumentEventMap[K]) => void, + element: RefObject<Document>, + options?: boolean | AddEventListenerOptions, +): void + +function useEventListener< + KW extends keyof WindowEventMap, + KH extends keyof HTMLElementEventMap, + T extends HTMLElement | void = void, +>( + eventName: KW | KH, + handler: ( + event: WindowEventMap[KW] | HTMLElementEventMap[KH] | Event, + ) => void, + element?: RefObject<T>, + options?: boolean | AddEventListenerOptions, +) { + // Create a ref that stores handler + const savedHandler = useRef(handler) + + useIsomorphicLayoutEffect(() => { + savedHandler.current = handler + }, [handler]) + + useEffect(() => { + // Define the listening target + const targetElement: T | Window = element?.current || window + if (!(targetElement && targetElement.addEventListener)) { + return + } + + // Create event listener that calls handler function stored in ref + const eventListener: typeof handler = event => savedHandler.current(event) + + targetElement.addEventListener(eventName, eventListener, options) + + // Remove event listener on cleanup + return () => { + targetElement.removeEventListener(eventName, eventListener) + } + }, [eventName, element, options]) +} + +export default useEventListener
A frontend/hooks/useIsomorphicLayoutEffect.ts

@@ -0,0 +1,6 @@

+import { useEffect, useLayoutEffect } from 'react' + +const useIsomorphicLayoutEffect = + typeof window !== 'undefined' ? useLayoutEffect : useEffect + +export default useIsomorphicLayoutEffect
A frontend/hooks/useMinimizedFab.ts

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

+import {useRef, useState} from 'react'; +import useEventListener from './useEventListener'; + +const useMinimizedFab = () => { + const [isFabMinimized, setIsFabMinimized] = useState(false); + const element = useRef( + typeof document !== 'undefined' + ? document.getElementById('event-content') + : null + ); + + useEventListener( + 'scroll', + event => { + if (typeof document != 'undefined') { + const positionFromTop = event.target.scrollTop; + const positionFromBottom = + event.target.scrollHeight - + event.target.offsetHeight - + event.target.scrollTop; + + if ( + (positionFromTop < 20 || positionFromBottom < 20) && + isFabMinimized + ) { + setIsFabMinimized(false); + } else if ( + !isFabMinimized && + positionFromTop > 20 && + positionFromBottom > 20 + ) { + setIsFabMinimized(true); + } + } + }, + element + ); + + return isFabMinimized; +}; + +export default useMinimizedFab;
M frontend/layouts/Centered.tsxfrontend/layouts/Centered.tsx

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

-import Container from '@material-ui/core/Container'; -import Box from '@material-ui/core/Box'; +import Container from '@mui/material/Container'; +import Box from '@mui/material/Box'; import DefaultLayout from './Default'; const CenteredLayout = ({children, ...props}) => {
M frontend/layouts/ConfirmLayout.tsxfrontend/layouts/ConfirmLayout.tsx

@@ -1,32 +1,42 @@

-import Card from '@material-ui/core/Card'; -import CardMedia from '@material-ui/core/CardMedia'; -import CardContent from '@material-ui/core/CardContent'; -import {makeStyles} from '@material-ui/core/styles'; +import Card from '@mui/material/Card'; +import { styled } from '@mui/material/styles'; +import CardMedia from '@mui/material/CardMedia'; +import CardContent from '@mui/material/CardContent'; import Layout from './Centered'; import Logo from '../components/Logo'; +const PREFIX = 'CommonConfirm'; + +const classes = { + wrapper: `${PREFIX}-wrapper` +}; + +const StyledLayout = styled(Layout)(( + { + theme + } +) => ({ + [`& .${classes.wrapper}`]: { + padding: theme.spacing(0, 8 ), + '&:last-child': { + paddingBottom: theme.spacing(12), + }, + } +})); + const CommonConfirm = ({children}) => { - const {wrapper} = useStyles(); + return ( - <Layout displayMenu={false}> + <StyledLayout displayMenu={false}> <Card> <CardMedia component={Logo} /> - <CardContent className={wrapper}> + <CardContent className={classes.wrapper}> {children} </CardContent> </Card> - </Layout> + </StyledLayout> ); }; - -const useStyles = makeStyles(theme => ({ - wrapper: { - padding: theme.spacing(0, 8 ), - '&:last-child': { - paddingBottom: theme.spacing(12), - }, - }, -})); export default CommonConfirm;
M frontend/layouts/Default.tsxfrontend/layouts/Default.tsx

@@ -3,7 +3,7 @@ import {Helmet} from 'react-helmet';

import useGTM from '../hooks/useGTM'; import GenericToolbar from '../containers/GenericToolbar'; import {ActionType} from '../containers/GenericMenu/Action'; -import Box from '@material-ui/core/Box'; +import Box from '@mui/material/Box'; import Banner from '../components/Banner'; import useMatomo from '../hooks/useMatomo';
M frontend/layouts/Event.tsxfrontend/layouts/Event.tsx

@@ -1,15 +1,35 @@

import {PropsWithChildren, useEffect, useState} from 'react'; -import useMediaQuery from '@material-ui/core/useMediaQuery'; -import {makeStyles, useTheme} from '@material-ui/core/styles'; +import {styled} from '@mui/material/styles'; +import useMediaQuery from '@mui/material/useMediaQuery'; +import Box from '@mui/material/Box'; +import {useTheme} from '@mui/material/styles'; import {useTranslation} from 'react-i18next'; import ErrorPage from '../pages/_error'; import useEventStore from '../stores/useEventStore'; import Layout from '../layouts/Default'; import EventBar from '../containers/EventBar'; -import {Event as EventType, useEventByUuidQuery} from '../generated/graphql'; import DrawerMenu from '../containers/DrawerMenu'; import AddToMyEventDialog from '../containers/AddToMyEventDialog'; -import Box from '@material-ui/core/Box'; +import {Event as EventType, useEventByUuidQuery} from '../generated/graphql'; + +const PREFIX = 'EventLayout'; + +const classes = { + content: `${PREFIX}-content`, +}; + +const StyledLayout = styled(Layout)(({theme}) => ({ + [`& .${classes.content}`]: { + flex: 1, + maxWidth: 'calc(100% - 85px)', + overflow: 'auto', + paddingBottom: theme.spacing(4), + + [theme.breakpoints.down('md')]: { + maxWidth: '100%', + }, + }, +})); const POLL_INTERVAL = 10000;

@@ -26,8 +46,8 @@ const EventLayout = (props: PropsWithChildren<Props>) => {

const {eventUUID, Tab, ...pageProps} = props; const {t} = useTranslation(); const theme = useTheme(); - const classes = useStyles(); - const isMobile = useMediaQuery(theme.breakpoints.down('sm')); + + const isMobile = useMediaQuery(theme.breakpoints.down('md')); const setEvent = useEventStore(s => s.setEvent); const [isAddToMyEvent, setIsAddToMyEvent] = useState(false); const {data: {eventByUUID: {data: {attributes, id} = {}} = {}} = {}} =

@@ -44,7 +64,7 @@

if (!event) return <ErrorPage statusCode={404} title={t`event.not_found`} />; return ( - <Layout + <StyledLayout pageTitle={t('event.title', {title: event.name})} menuTitle={t('event.title', {title: event.name})} displayMenu={false}

@@ -60,7 +80,7 @@ overflow="hidden"

flexDirection={isMobile ? 'column-reverse' : 'row'} > <DrawerMenu /> - <Box className={classes.content}> + <Box className={classes.content} id="event-content"> <Tab event={event} /> </Box> </Box>

@@ -69,21 +89,8 @@ event={event}

open={isAddToMyEvent} onClose={() => setIsAddToMyEvent(false)} /> - </Layout> + </StyledLayout> ); }; - -const useStyles = makeStyles(theme => ({ - content: { - flex: 1, - maxWidth: 'calc(100% - 85px)', - overflow: 'auto', - paddingBottom: theme.spacing(4), - - [theme.breakpoints.down('sm')]: { - maxWidth: '100%', - }, - }, -})); export default EventLayout;
M frontend/locales/en.jsonfrontend/locales/en.json

@@ -212,6 +212,7 @@ "signup.submit": "Create your account",

"signup.account_already": "Do you already have an account ?", "signup.login": "$t(menu.login)", "signup.errors.email_taken": "This email is already associated with an account", + "confirm.creating": "Creating the account", "confirm.title": "Confirm your email", "confirm.text": "You have received an email with a link. Please click on this link to confirm your account.", "confirm.login": "Return to the login screen",
M frontend/locales/fr.jsonfrontend/locales/fr.json

@@ -208,10 +208,11 @@ "signup.firstName": "Prénom",

"signup.lastName": "Nom", "signup.password": "Mot de passe", "signup.newsletter.consent": "Le covoiturage m'intéresse, je souhaite recevoir des nouvelles de Caroster", - "signup.submit": "Créer son compte", + "signup.submit": "Créer un compte", "signup.account_already": "Vous avez déjà un compte ?", "signup.login": "$t(menu.login)", "signup.errors.email_taken": "Cet email est déjà associé à un compte", + "confirm.creating": "Création de compte", "confirm.title": "Confirmez votre email", "confirm.text": "Vous avez reçu un email avec un lien. Merci de cliquer sur ce lien pour confirmer votre compte.", "confirm.login": "Retour à l'écran de connexion",
M frontend/package.jsonfrontend/package.json

@@ -14,8 +14,12 @@ "dependencies": {

"@apollo/client": "^3.6.9", "@date-io/dayjs": "1.x", "@date-io/moment": "1.x", - "@material-ui/core": "^4.12.4", - "@material-ui/pickers": "^3.3.10", + "@emotion/react": "^11.10.4", + "@emotion/styled": "^11.10.4", + "@mui/lab": "^5.0.0-alpha.102", + "@mui/material": "^5.10.8", + "@mui/styles": "^5.10.8", + "@mui/x-date-pickers": "^5.0.4", "crypto-js": "^4.1.1", "deepmerge": "^4.2.2", "graphql": "^16.6.0",

@@ -30,7 +34,6 @@ "react-dom": "^18.2.0",

"react-helmet": "^6.1.0", "react-i18next": "^11.18.6", "react-joyride": "^2.5.2", - "react-slick": "^0.29.0", "typescript": "^4.8.3", "zustand": "^4.1.1" },
M frontend/pages/_app.tsxfrontend/pages/_app.tsx

@@ -1,21 +1,25 @@

import {useEffect} from 'react'; import {AppProps} from 'next/app'; import {ApolloProvider} from '@apollo/client'; -import {ThemeProvider} from '@material-ui/core/styles'; -import CssBaseline from '@material-ui/core/CssBaseline'; -import {MuiPickersUtilsProvider} from '@material-ui/pickers'; +import {ThemeProvider, Theme, StyledEngineProvider} from '@mui/material/styles'; +import CssBaseline from '@mui/material/CssBaseline'; +import {LocalizationProvider} from '@mui/x-date-pickers/LocalizationProvider'; +import {AdapterMoment} from '@mui/x-date-pickers/AdapterMoment'; import {SessionProvider} from 'next-auth/react'; import moment from 'moment'; -import MomentUtils from '@date-io/moment'; import {useApollo} from '../lib/apolloClient'; import Metas from '../containers/Metas'; import Toasts from '../components/Toasts'; import theme from '../theme'; -import useProfile from '../hooks/useProfile'; import useLocale from '../hooks/useLocale'; import {I18nextProvider} from 'react-i18next'; import i18n, {initI18Next} from '../lib/i18n'; +declare module '@mui/styles/defaultTheme' { + // eslint-disable-next-line @typescript-eslint/no-empty-interface + interface DefaultTheme extends Theme {} +} + const App = function (props: AppProps) { const {Component, pageProps} = props; const apolloClient = useApollo(pageProps);

@@ -36,15 +40,15 @@ <I18nextProvider i18n={i18n}>

<ApolloProvider client={apolloClient}> <Metas metas={pageProps.metas} /> <ThemeProvider theme={theme}> - <MuiPickersUtilsProvider - libInstance={moment} - utils={MomentUtils} - locale={locale === 'fr' ? 'fr-ch' : 'en'} + <LocalizationProvider + dateAdapter={AdapterMoment} + dateLibInstance={moment} + adapterLocale={locale === 'fr' ? 'fr-ch' : 'en'} > <CssBaseline /> <Component {...pageProps} /> <Toasts /> - </MuiPickersUtilsProvider> + </LocalizationProvider> </ThemeProvider> </ApolloProvider> </I18nextProvider>
M frontend/pages/_document.tsxfrontend/pages/_document.tsx

@@ -1,7 +1,7 @@

import React from 'react'; import Document, {Html, Head, Main, NextScript} from 'next/document'; -import {ServerStyleSheets} from '@material-ui/core/styles'; import theme from '../theme'; +import { ServerStyleSheets } from '@mui/styles'; export default class MyDocument extends Document { render() {
M frontend/pages/_error.tsxfrontend/pages/_error.tsx

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

import {useTranslation} from 'react-i18next'; -import Box from '@material-ui/core/Box'; -import Typography from '@material-ui/core/Typography'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; interface Props { statusCode: number;
M frontend/pages/auth/confirm/google.tsxfrontend/pages/auth/confirm/google.tsx

@@ -1,10 +1,10 @@

-import Typography from '@material-ui/core/Typography'; -import Icon from '@material-ui/core/Icon'; -import FormControlLabel from '@material-ui/core/FormControlLabel'; -import Checkbox from '@material-ui/core/Checkbox'; -import Button from '@material-ui/core/Button'; -import Box from '@material-ui/core/Box'; -import {makeStyles} from '@material-ui/core/styles'; +import Typography from '@mui/material/Typography'; +import { styled } from '@mui/material/styles'; +import Icon from '@mui/material/Icon'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import Checkbox from '@mui/material/Checkbox'; +import Button from '@mui/material/Button'; +import Box from '@mui/material/Box'; import {useTranslation} from 'react-i18next'; import {useState} from 'react'; import pageUtils from '../../../lib/pageUtils';

@@ -13,9 +13,42 @@ import {useUpdateMeMutation} from '../../../generated/graphql';

import useRedirectUrlStore from '../../../stores/useRedirectUrl'; import router from 'next/router'; +const PREFIX = 'Confirm'; + +const classes = { + margins: `${PREFIX}-margins`, + newsletter: `${PREFIX}-newsletter`, + checkbox: `${PREFIX}-checkbox`, + center: `${PREFIX}-center` +}; + +const StyledCommonConfirm = styled(CommonConfirm)(( + { + theme + } +) => ({ + [`& .${classes.margins}`]: { + margin: theme.spacing(5, 0), + }, + + [`& .${classes.newsletter}`]: { + width: '100%', + margin: theme.spacing(2, 0), + }, + + [`& .${classes.checkbox}`]: { + padding: 0, + marginRight: theme.spacing(2), + }, + + [`& .${classes.center}`]: { + textAlign: 'center', + } +})); + const Confirm = () => { const {t} = useTranslation(); - const classes = useStyles(); + const [newsletterConsent, setNewsletterConsent] = useState(false); const [updateMe] = useUpdateMeMutation(); const getRedirectUrl = useRedirectUrlStore(s => s.getRedirectUrl);

@@ -26,7 +59,7 @@ router.push(callbackUrl);

}; return ( - <CommonConfirm> + <StyledCommonConfirm> <Typography variant="overline" component="h5" align="center"> {t('signup.create')} </Typography>

@@ -55,26 +88,9 @@ <Button variant="contained" color="secondary" onClick={onSubmit}>

{t('generic.confirm')} </Button> </Box> - </CommonConfirm> + </StyledCommonConfirm> ); }; - -const useStyles = makeStyles(theme => ({ - margins: { - margin: theme.spacing(5, 0), - }, - newsletter: { - width: '100%', - margin: theme.spacing(2, 0), - }, - checkbox: { - padding: 0, - marginRight: theme.spacing(2), - }, - center: { - textAlign: 'center', - }, -})); export default Confirm;
M frontend/pages/auth/confirm/index.tsxfrontend/pages/auth/confirm/index.tsx

@@ -1,47 +1,50 @@

-import Typography from '@material-ui/core/Typography'; -import Icon from '@material-ui/core/Icon'; -import {makeStyles} from '@material-ui/core/styles'; +import Typography from '@mui/material/Typography'; +import {styled, useTheme} from '@mui/material/styles'; +import Icon from '@mui/material/Icon'; import {useTranslation} from 'react-i18next'; import CommonConfirm from '../../../layouts/ConfirmLayout'; import pageUtils from '../../../lib/pageUtils'; +const PREFIX = 'Confirm'; + +const classes = { + margins: `${PREFIX}-margins`, +}; + +const StyledCommonConfirm = styled(CommonConfirm)(({theme}) => ({ + [`& .${classes.margins}`]: { + margin: theme.spacing(5, 0), + }, +})); + const Confirm = () => { const {t} = useTranslation(); - const {margins} = useStyles(); + const theme = useTheme(); return ( - <CommonConfirm> - <Typography variant="overline" component="h5" align="center"> + <StyledCommonConfirm> + <Typography variant="subtitle1" align="center"> + {t('confirm.creating')} + </Typography> + <Typography variant="h5" align="center"> {t('confirm.title')} </Typography> <Typography - variant="h5" - component="h2" align="center" + sx={{margin: theme.spacing(5, 0)}} > - {t('confirm.title')} - </Typography> - <Typography align="center" className={margins} component="div"> <Icon fontSize="large">mail</Icon> </Typography> <Typography - className={margins} + sx={{margin: theme.spacing(5, 0)}} variant="body2" - color="textSecondary" - component="p" align="center" > {t('confirm.text')} </Typography> - </CommonConfirm> + </StyledCommonConfirm> ); }; - -const useStyles = makeStyles(theme => ({ - margins: { - margin: theme.spacing(5, 0), - }, -})); export default Confirm;
M frontend/pages/auth/login.tsxfrontend/pages/auth/login.tsx

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

-import CardMedia from '@material-ui/core/CardMedia'; -import Card from '@material-ui/core/Card'; +import CardMedia from '@mui/material/CardMedia'; +import Card from '@mui/material/Card'; import {useTranslation} from 'react-i18next'; import Layout from '../../layouts/Centered'; import Logo from '../../components/Logo';

@@ -7,7 +7,8 @@ import SignInForm from '../../containers/SignInForm';

import LanguagesIcon from '../../containers/Languages/Icon'; import {getSession} from 'next-auth/react'; import pageUtils from '../../lib/pageUtils'; -import Typography from '@material-ui/core/Typography'; +import Typography from '@mui/material/Typography'; +import theme from '../../theme'; interface PageProps { error?: string;

@@ -24,10 +25,9 @@ <Card>

<CardMedia component={Logo} /> {emailConfirmation && ( <Typography - style={{marginBottom: '3rem'}} + sx={{marginBottom: theme.spacing(2)}} variant="body2" align="center" - color="textSecondary" >{t`signin.emailConfirmation`}</Typography> )} <SignInForm error={props?.error} />
M frontend/pages/auth/register/index.tsxfrontend/pages/auth/register/index.tsx

@@ -1,49 +1,77 @@

-import Card from '@material-ui/core/Card'; -import CardMedia from '@material-ui/core/CardMedia'; +import Card from '@mui/material/Card'; +import {useTheme} from '@mui/material/styles'; +import CardMedia from '@mui/material/CardMedia'; import {useTranslation} from 'react-i18next'; import Layout from '../../../layouts/Centered'; import Logo from '../../../components/Logo'; import LanguagesIcon from '../../../containers/Languages/Icon'; -import CardContent from '@material-ui/core/CardContent'; +import CardContent from '@mui/material/CardContent'; import SignUpActions from '../../../containers/MailSignUpForm/SignupActions'; -import Typography from '@material-ui/core/Typography'; -import {makeStyles} from '@material-ui/core/styles'; -import Box from '@material-ui/core/Box'; -import Divider from '@material-ui/core/Divider'; -import Button from '@material-ui/core/Button'; +import Typography from '@mui/material/Typography'; +import Box from '@mui/material/Box'; +import Divider from '@mui/material/Divider'; +import Button from '@mui/material/Button'; import LoginGoogle from '../../../containers/LoginGoogle'; import Link from 'next/link'; import Markdown from '../../../components/Markdown'; const MailSignup = () => { const {t} = useTranslation(); - const classes = useStyles(); + const theme = useTheme(); return ( <Layout menuTitle={t('signup.title')} displayMenu={false}> <Card> <CardMedia component={Logo} /> - <CardContent className={classes.content}> + <CardContent + sx={{ + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + width: '100%', + padding: theme.spacing(0, 6), + }} + > <Typography variant="overline" component="h5" align="center"> {t('signup.create')} </Typography> - <Box className={classes.content}> + <Box + sx={{ + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + width: '100%', + padding: {sm: theme.spacing(0, 6), xs: 0}, + }} + > <Link href="/auth/register/mail" passHref> <Button color="primary" variant="contained" fullWidth - className={classes.button} + sx={{ + margin: theme.spacing(8, 1, 4, 1), + }} > {t('signup.with_mail')} </Button> </Link> <LoginGoogle /> </Box> - <Box className={classes.divider}> + <Box + sx={{ + width: '100%', + textAlign: 'center', + margin: theme.spacing(10, 0, 2, 0), + }} + > <Markdown - className={classes.conditions} - variant="overline" + sx={{ + '& a': { + color: 'inherit', + }, + }} + variant="body1" align="center" > {t('signup.conditions')}

@@ -61,28 +89,5 @@ </Card>

</Layout> ); }; - -const useStyles = makeStyles(theme => ({ - content: { - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - width: '100%', - padding: theme.spacing(0, 6), - }, - button: { - margin: theme.spacing(8, 1, 4, 1), - }, - divider: { - width: '100%', - textAlign: 'center', - margin: theme.spacing(10, 0, 2, 0), - }, - conditions: { - '& a': { - color: 'inherit', - }, - }, -})); export default MailSignup;
M frontend/pages/auth/register/mail.tsxfrontend/pages/auth/register/mail.tsx

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

-import Card from '@material-ui/core/Card'; -import CardMedia from '@material-ui/core/CardMedia'; +import Card from '@mui/material/Card'; +import CardMedia from '@mui/material/CardMedia'; import {useTranslation} from 'react-i18next'; import Layout from '../../../layouts/Centered'; import MailSignUpForm from '../../../containers/MailSignUpForm';
M frontend/pages/e/[uuid]/details.tsxfrontend/pages/e/[uuid]/details.tsx

@@ -1,16 +1,17 @@

import moment from 'moment'; -import Button from '@material-ui/core/Button'; -import Box from '@material-ui/core/Box'; -import Link from '@material-ui/core/Link'; -import Paper from '@material-ui/core/Paper'; -import Divider from '@material-ui/core/Divider'; -import Container from '@material-ui/core/Container'; -import TextField from '@material-ui/core/TextField'; -import Typography from '@material-ui/core/Typography'; -import {makeStyles} from '@material-ui/core/styles'; -import {DatePicker} from '@material-ui/pickers'; +import Button from '@mui/material/Button'; +import Box from '@mui/material/Box'; +import Link from '@mui/material/Link'; +import Paper from '@mui/material/Paper'; +import Divider from '@mui/material/Divider'; +import Container from '@mui/material/Container'; +import TextField from '@mui/material/TextField'; +import Typography from '@mui/material/Typography'; +import {useTheme} from '@mui/material/styles'; +import {DatePicker} from '@mui/x-date-pickers/DatePicker'; import {PropsWithChildren, useState} from 'react'; import {useTranslation} from 'react-i18next'; +import pageUtils from '../../../lib/pageUtils'; import ShareEvent from '../../../containers/ShareEvent'; import useEventStore from '../../../stores/useEventStore'; import useToastStore from '../../../stores/useToastStore';

@@ -20,7 +21,6 @@ import {

EventByUuidDocument, useUpdateEventMutation, } from '../../../generated/graphql'; -import pageUtils from '../../../lib/pageUtils'; interface Props { eventUUID: string;

@@ -33,6 +33,7 @@ };

const DetailsTab: TabComponent = ({}) => { const {t} = useTranslation(); + const theme = useTheme(); const settings = useSettings(); const [updateEvent] = useUpdateEventMutation(); const addToast = useToastStore(s => s.addToast);

@@ -40,7 +41,6 @@ const setEventUpdate = useEventStore(s => s.setEventUpdate);

const event = useEventStore(s => s.event); const [isEditing, setIsEditing] = useState(false); const idPrefix = isEditing ? 'EditEvent' : 'Event'; - const classes = useStyles(); const onSave = async e => { try {

@@ -55,13 +55,19 @@ } catch (error) {

console.error(error); addToast(t('event.errors.cant_update')); } + }; + const sectionSx = { + marginBottom: theme.spacing(2), + width: '540px', + maxWidth: '100%', + paddingX: theme.spacing(2), }; const modifyButton = isEditing ? ( <Button variant="contained" color="primary" - className={classes.modify} + sx={{position: 'absolute', right: theme.spacing(2)}} onClick={onSave} > {t('event.details.save')}

@@ -70,7 +76,7 @@ ) : (

<Button variant="text" color="primary" - className={classes.modify} + sx={{position: 'absolute', right: theme.spacing(2)}} onClick={() => setIsEditing(true)} > {t('event.details.modify')}

@@ -80,11 +86,21 @@

if (!event) return null; return ( - <Box className={classes.root}> - <Container maxWidth="sm" className={classes.card}> - <Paper className={classes.paper}> + <Box + sx={{ + position: 'relative', + paddingLeft: '80px', + + [theme.breakpoints.down('md')]: { + paddingLeft: 0, + paddingBottom: '80px', + }, + }} + > + <Container maxWidth="sm" sx={{marginTop: theme.spacing(6)}}> + <Paper sx={{position: 'relative', padding: theme.spacing(2)}}> {modifyButton} - <div className={classes.section}> + <Box sx={sectionSx}> <Typography variant="h6">{t('event.fields.name')}</Typography> {isEditing ? ( <TextField

@@ -99,24 +115,25 @@ <Typography variant="body1" id={`${idPrefix}Name`}>

{event.name ?? t('event.fields.empty')} </Typography> )} - </div> - <div className={classes.section}> + </Box> + <Box sx={sectionSx}> <Typography variant="h6">{t('event.fields.date')}</Typography> {isEditing ? ( <DatePicker - fullWidth - placeholder={t('event.fields.date_placeholder')} + renderInput={props => ( + <TextField + {...props} + id={`${idPrefix}Date`} + fullWidth + placeholder={t('event.fields.date_placeholder')} + /> + )} value={event.date} onChange={date => setEventUpdate({ date: !date ? null : moment(date).format('YYYY-MM-DD'), }) } - format="DD/MM/YYYY" - cancelLabel={t('generic.cancel')} - clearable - clearLabel={t('generic.clear')} - id={`${idPrefix}Date`} /> ) : ( <Typography variant="body1" id={`${idPrefix}Date`}>

@@ -125,14 +142,14 @@ ? moment(event.date).format('DD/MM/YYYY')

: t('event.fields.empty')} </Typography> )} - </div> - <div className={classes.section}> + </Box> + <Box sx={sectionSx}> <Typography variant="h6">{t('event.fields.address')}</Typography> {isEditing ? ( <TextField fullWidth multiline - rowsMax={4} + maxRows={4} inputProps={{maxLength: 250}} helperText={`${event.address?.length ?? 0}/250`} defaultValue={event.address}

@@ -159,8 +176,8 @@ t('event.fields.empty')

)} </Typography> )} - </div> - <div className={classes.section}> + </Box> + <Box sx={sectionSx}> <Typography variant="h6"> {t('event.fields.description')} </Typography>

@@ -168,7 +185,7 @@ {isEditing ? (

<TextField fullWidth multiline - rowsMax={4} + maxRows={4} inputProps={{maxLength: 250}} helperText={`${event.description?.length || 0}/250`} defaultValue={event.description}

@@ -182,7 +199,7 @@ <Typography variant="body1" id={`${idPrefix}Description`}>

{event.description ?? t('event.fields.empty')} </Typography> )} - </div> + </Box> <Typography variant="h6">{t('event.fields.link')}</Typography> <Typography>{t('event.fields.link_desc')}</Typography> <Box py={4} justifyContent="center" display="flex">

@@ -202,41 +219,6 @@ </Container>

</Box> ); }; - -const useStyles = makeStyles(theme => ({ - root: { - position: 'relative', - paddingLeft: '80px', - - [theme.breakpoints.down('sm')]: { - paddingLeft: 0, - paddingBottom: '80px', - }, - }, - paper: { - position: 'relative', - padding: theme.spacing(2), - }, - card: { - marginTop: theme.spacing(6), - }, - modify: { - position: 'absolute', - right: theme.spacing(2), - }, - section: { - marginBottom: theme.spacing(2), - width: '540px', - maxWidth: '100%', - paddingX: theme.spacing(2), - }, - map: { - marginTop: theme.spacing(4), - }, - seeOnGMapButton: { - marginLeft: theme.spacing(2), - }, -})); export const getServerSideProps = pageUtils.getServerSideProps( async (context, apolloClient) => {
M frontend/pages/e/[uuid]/index.tsxfrontend/pages/e/[uuid]/index.tsx

@@ -1,18 +1,19 @@

import {useState, useReducer, PropsWithChildren} from 'react'; -import {makeStyles} from '@material-ui/core/styles'; +import Box from '@mui/material/Box'; +import {useTheme} from '@mui/material/styles'; import {useTranslation} from 'react-i18next'; -import EventLayout, {TabComponent} from '../../../layouts/Event'; +import {getSession, useSession} from 'next-auth/react'; import TravelColumns from '../../../containers/TravelColumns'; import NewTravelDialog from '../../../containers/NewTravelDialog'; import VehicleChoiceDialog from '../../../containers/VehicleChoiceDialog'; +import Fab from '../../../containers/Fab'; +import pageUtils from '../../../lib/pageUtils'; +import EventLayout, {TabComponent} from '../../../layouts/Event'; import { EventByUuidDocument, FindUserVehiclesDocument, useFindUserVehiclesQuery, } from '../../../generated/graphql'; -import Fab from '../../../containers/Fab'; -import pageUtils from '../../../lib/pageUtils'; -import {getSession, useSession} from 'next-auth/react'; interface Props { eventUUID: string;

@@ -23,17 +24,18 @@ const Page = (props: PropsWithChildren<Props>) => {

return <EventLayout {...props} Tab={TravelsTab} />; }; -const TravelsTab: TabComponent = (props: {event}) => { - const classes = useStyles(); +const TravelsTab: TabComponent = () => { const {t} = useTranslation(); + const theme = useTheme(); const session = useSession(); + const [openNewTravelContext, toggleNewTravel] = useState({opened: false}); + const [openVehicleChoice, toggleVehicleChoice] = useReducer(i => !i, false); + const isAuthenticated = session.status === 'authenticated'; const {data} = useFindUserVehiclesQuery({ skip: !isAuthenticated, }); const vehicles = data?.me?.profile?.vehicles?.data || []; - const [openNewTravelContext, toggleNewTravel] = useState({opened: false}); - const [openVehicleChoice, toggleVehicleChoice] = useReducer(i => !i, false); const addTravelClickHandler = isAuthenticated && vehicles?.length != 0

@@ -41,13 +43,21 @@ ? toggleVehicleChoice

: () => toggleNewTravel({opened: true}); return ( - <> + <Box> <TravelColumns toggle={addTravelClickHandler} /> <Fab onClick={addTravelClickHandler} aria-label="add-car" color="primary" - className={classes.bottomRight} + sx={{ + bottom: 0, + right: theme.spacing(6), + + [theme.breakpoints.down('md')]: { + right: theme.spacing(1), + bottom: 56, + }, + }} > {t('travel.creation.title')} </Fab>

@@ -61,21 +71,9 @@ toggle={toggleVehicleChoice}

toggleNewTravel={toggleNewTravel} vehicles={vehicles} /> - </> + </Box> ); }; - -const useStyles = makeStyles(theme => ({ - bottomRight: { - bottom: 0, - right: theme.spacing(6), - - [theme.breakpoints.down('sm')]: { - right: theme.spacing(1), - bottom: 56, - }, - }, -})); export const getServerSideProps = pageUtils.getServerSideProps( async (context, apolloClient) => {
M frontend/pages/index.tsxfrontend/pages/index.tsx

@@ -4,12 +4,13 @@ import moment from 'moment';

import Layout from '../layouts/Centered'; import CreateEvent from '../containers/CreateEvent'; import LanguagesIcon from '../containers/Languages/Icon'; -import Paper from '../components/Paper'; import Logo from '../components/Logo'; import {getSession, useSession} from 'next-auth/react'; import pageUtils from '../lib/pageUtils'; import {useEffect} from 'react'; import useRedirectUrlStore from '../stores/useRedirectUrl'; +import theme from '../theme'; +import Paper from '@mui/material/Paper'; interface PageProps { announcement?: string;

@@ -66,7 +67,7 @@ menuActions={menuActions}

displayMenu={isAuthenticated} {...props} > - <Paper> + <Paper sx={{padding: theme.spacing(2)}}> <Logo /> <CreateEvent /> </Paper>
M frontend/public/workbox-6a1bf588.jsfrontend/public/workbox-6a1bf588.js

@@ -1,1 +1,1 @@

-define(["exports"],(function(t){"use strict";try{self["workbox:core:6.5.3"]&&_()}catch(t){}const e=(t,...e)=>{let s=t;return e.length>0&&(s+=` :: ${JSON.stringify(e)}`),s};class s extends Error{constructor(t,s){super(e(t,s)),this.name=t,this.details=s}}try{self["workbox:routing:6.5.3"]&&_()}catch(t){}const n=t=>t&&"object"==typeof t?t:{handle:t};class r{constructor(t,e,s="GET"){this.handler=n(e),this.match=t,this.method=s}setCatchHandler(t){this.catchHandler=n(t)}}class i extends r{constructor(t,e,s){super((({url:e})=>{const s=t.exec(e.href);if(s&&(e.origin===location.origin||0===s.index))return s.slice(1)}),e,s)}}class a{constructor(){this.t=new Map,this.i=new Map}get routes(){return this.t}addFetchListener(){self.addEventListener("fetch",(t=>{const{request:e}=t,s=this.handleRequest({request:e,event:t});s&&t.respondWith(s)}))}addCacheListener(){self.addEventListener("message",(t=>{if(t.data&&"CACHE_URLS"===t.data.type){const{payload:e}=t.data,s=Promise.all(e.urlsToCache.map((e=>{"string"==typeof e&&(e=[e]);const s=new Request(...e);return this.handleRequest({request:s,event:t})})));t.waitUntil(s),t.ports&&t.ports[0]&&s.then((()=>t.ports[0].postMessage(!0)))}}))}handleRequest({request:t,event:e}){const s=new URL(t.url,location.href);if(!s.protocol.startsWith("http"))return;const n=s.origin===location.origin,{params:r,route:i}=this.findMatchingRoute({event:e,request:t,sameOrigin:n,url:s});let a=i&&i.handler;const o=t.method;if(!a&&this.i.has(o)&&(a=this.i.get(o)),!a)return;let c;try{c=a.handle({url:s,request:t,event:e,params:r})}catch(t){c=Promise.reject(t)}const h=i&&i.catchHandler;return c instanceof Promise&&(this.o||h)&&(c=c.catch((async n=>{if(h)try{return await h.handle({url:s,request:t,event:e,params:r})}catch(t){t instanceof Error&&(n=t)}if(this.o)return this.o.handle({url:s,request:t,event:e});throw n}))),c}findMatchingRoute({url:t,sameOrigin:e,request:s,event:n}){const r=this.t.get(s.method)||[];for(const i of r){let r;const a=i.match({url:t,sameOrigin:e,request:s,event:n});if(a)return r=a,(Array.isArray(r)&&0===r.length||a.constructor===Object&&0===Object.keys(a).length||"boolean"==typeof a)&&(r=void 0),{route:i,params:r}}return{}}setDefaultHandler(t,e="GET"){this.i.set(e,n(t))}setCatchHandler(t){this.o=n(t)}registerRoute(t){this.t.has(t.method)||this.t.set(t.method,[]),this.t.get(t.method).push(t)}unregisterRoute(t){if(!this.t.has(t.method))throw new s("unregister-route-but-not-found-with-method",{method:t.method});const e=this.t.get(t.method).indexOf(t);if(!(e>-1))throw new s("unregister-route-route-not-registered");this.t.get(t.method).splice(e,1)}}let o;const c=()=>(o||(o=new a,o.addFetchListener(),o.addCacheListener()),o);function h(t,e,n){let a;if("string"==typeof t){const s=new URL(t,location.href);a=new r((({url:t})=>t.href===s.href),e,n)}else if(t instanceof RegExp)a=new i(t,e,n);else if("function"==typeof t)a=new r(t,e,n);else{if(!(t instanceof r))throw new s("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});a=t}return c().registerRoute(a),a}try{self["workbox:strategies:6.5.3"]&&_()}catch(t){}const u={cacheWillUpdate:async({response:t})=>200===t.status||0===t.status?t:null},l={googleAnalytics:"googleAnalytics",precache:"precache-v2",prefix:"workbox",runtime:"runtime",suffix:"undefined"!=typeof registration?registration.scope:""},f=t=>[l.prefix,t,l.suffix].filter((t=>t&&t.length>0)).join("-"),w=t=>t||f(l.precache),d=t=>t||f(l.runtime);function p(t,e){const s=new URL(t);for(const t of e)s.searchParams.delete(t);return s.href}class y{constructor(){this.promise=new Promise(((t,e)=>{this.resolve=t,this.reject=e}))}}const g=new Set;function m(t){return"string"==typeof t?new Request(t):t}class v{constructor(t,e){this.h={},Object.assign(this,e),this.event=e.event,this.u=t,this.l=new y,this.p=[],this.g=[...t.plugins],this.m=new Map;for(const t of this.g)this.m.set(t,{});this.event.waitUntil(this.l.promise)}async fetch(t){const{event:e}=this;let n=m(t);if("navigate"===n.mode&&e instanceof FetchEvent&&e.preloadResponse){const t=await e.preloadResponse;if(t)return t}const r=this.hasCallback("fetchDidFail")?n.clone():null;try{for(const t of this.iterateCallbacks("requestWillFetch"))n=await t({request:n.clone(),event:e})}catch(t){if(t instanceof Error)throw new s("plugin-error-request-will-fetch",{thrownErrorMessage:t.message})}const i=n.clone();try{let t;t=await fetch(n,"navigate"===n.mode?void 0:this.u.fetchOptions);for(const s of this.iterateCallbacks("fetchDidSucceed"))t=await s({event:e,request:i,response:t});return t}catch(t){throw r&&await this.runCallbacks("fetchDidFail",{error:t,event:e,originalRequest:r.clone(),request:i.clone()}),t}}async fetchAndCachePut(t){const e=await this.fetch(t),s=e.clone();return this.waitUntil(this.cachePut(t,s)),e}async cacheMatch(t){const e=m(t);let s;const{cacheName:n,matchOptions:r}=this.u,i=await this.getCacheKey(e,"read"),a=Object.assign(Object.assign({},r),{cacheName:n});s=await caches.match(i,a);for(const t of this.iterateCallbacks("cachedResponseWillBeUsed"))s=await t({cacheName:n,matchOptions:r,cachedResponse:s,request:i,event:this.event})||void 0;return s}async cachePut(t,e){const n=m(t);var r;await(r=0,new Promise((t=>setTimeout(t,r))));const i=await this.getCacheKey(n,"write");if(!e)throw new s("cache-put-with-no-response",{url:(a=i.url,new URL(String(a),location.href).href.replace(new RegExp(`^${location.origin}`),""))});var a;const o=await this.v(e);if(!o)return!1;const{cacheName:c,matchOptions:h}=this.u,u=await self.caches.open(c),l=this.hasCallback("cacheDidUpdate"),f=l?await async function(t,e,s,n){const r=p(e.url,s);if(e.url===r)return t.match(e,n);const i=Object.assign(Object.assign({},n),{ignoreSearch:!0}),a=await t.keys(e,i);for(const e of a)if(r===p(e.url,s))return t.match(e,n)}(u,i.clone(),["__WB_REVISION__"],h):null;try{await u.put(i,l?o.clone():o)}catch(t){if(t instanceof Error)throw"QuotaExceededError"===t.name&&await async function(){for(const t of g)await t()}(),t}for(const t of this.iterateCallbacks("cacheDidUpdate"))await t({cacheName:c,oldResponse:f,newResponse:o.clone(),request:i,event:this.event});return!0}async getCacheKey(t,e){const s=`${t.url} | ${e}`;if(!this.h[s]){let n=t;for(const t of this.iterateCallbacks("cacheKeyWillBeUsed"))n=m(await t({mode:e,request:n,event:this.event,params:this.params}));this.h[s]=n}return this.h[s]}hasCallback(t){for(const e of this.u.plugins)if(t in e)return!0;return!1}async runCallbacks(t,e){for(const s of this.iterateCallbacks(t))await s(e)}*iterateCallbacks(t){for(const e of this.u.plugins)if("function"==typeof e[t]){const s=this.m.get(e),n=n=>{const r=Object.assign(Object.assign({},n),{state:s});return e[t](r)};yield n}}waitUntil(t){return this.p.push(t),t}async doneWaiting(){let t;for(;t=this.p.shift();)await t}destroy(){this.l.resolve(null)}async v(t){let e=t,s=!1;for(const t of this.iterateCallbacks("cacheWillUpdate"))if(e=await t({request:this.request,response:e,event:this.event})||void 0,s=!0,!e)break;return s||e&&200!==e.status&&(e=void 0),e}}class R{constructor(t={}){this.cacheName=d(t.cacheName),this.plugins=t.plugins||[],this.fetchOptions=t.fetchOptions,this.matchOptions=t.matchOptions}handle(t){const[e]=this.handleAll(t);return e}handleAll(t){t instanceof FetchEvent&&(t={event:t,request:t.request});const e=t.event,s="string"==typeof t.request?new Request(t.request):t.request,n="params"in t?t.params:void 0,r=new v(this,{event:e,request:s,params:n}),i=this.R(r,s,e);return[i,this.q(i,r,s,e)]}async R(t,e,n){let r;await t.runCallbacks("handlerWillStart",{event:n,request:e});try{if(r=await this.D(e,t),!r||"error"===r.type)throw new s("no-response",{url:e.url})}catch(s){if(s instanceof Error)for(const i of t.iterateCallbacks("handlerDidError"))if(r=await i({error:s,event:n,request:e}),r)break;if(!r)throw s}for(const s of t.iterateCallbacks("handlerWillRespond"))r=await s({event:n,request:e,response:r});return r}async q(t,e,s,n){let r,i;try{r=await t}catch(i){}try{await e.runCallbacks("handlerDidRespond",{event:n,request:s,response:r}),await e.doneWaiting()}catch(t){t instanceof Error&&(i=t)}if(await e.runCallbacks("handlerDidComplete",{event:n,request:s,response:r,error:i}),e.destroy(),i)throw i}}function b(t){t.then((()=>{}))}function q(){return q=Object.assign?Object.assign.bind():function(t){for(var e=1;e<arguments.length;e++){var s=arguments[e];for(var n in s)Object.prototype.hasOwnProperty.call(s,n)&&(t[n]=s[n])}return t},q.apply(this,arguments)}let D,U;const x=new WeakMap,L=new WeakMap,I=new WeakMap,C=new WeakMap,E=new WeakMap;let N={get(t,e,s){if(t instanceof IDBTransaction){if("done"===e)return L.get(t);if("objectStoreNames"===e)return t.objectStoreNames||I.get(t);if("store"===e)return s.objectStoreNames[1]?void 0:s.objectStore(s.objectStoreNames[0])}return k(t[e])},set:(t,e,s)=>(t[e]=s,!0),has:(t,e)=>t instanceof IDBTransaction&&("done"===e||"store"===e)||e in t};function O(t){return t!==IDBDatabase.prototype.transaction||"objectStoreNames"in IDBTransaction.prototype?(U||(U=[IDBCursor.prototype.advance,IDBCursor.prototype.continue,IDBCursor.prototype.continuePrimaryKey])).includes(t)?function(...e){return t.apply(B(this),e),k(x.get(this))}:function(...e){return k(t.apply(B(this),e))}:function(e,...s){const n=t.call(B(this),e,...s);return I.set(n,e.sort?e.sort():[e]),k(n)}}function T(t){return"function"==typeof t?O(t):(t instanceof IDBTransaction&&function(t){if(L.has(t))return;const e=new Promise(((e,s)=>{const n=()=>{t.removeEventListener("complete",r),t.removeEventListener("error",i),t.removeEventListener("abort",i)},r=()=>{e(),n()},i=()=>{s(t.error||new DOMException("AbortError","AbortError")),n()};t.addEventListener("complete",r),t.addEventListener("error",i),t.addEventListener("abort",i)}));L.set(t,e)}(t),e=t,(D||(D=[IDBDatabase,IDBObjectStore,IDBIndex,IDBCursor,IDBTransaction])).some((t=>e instanceof t))?new Proxy(t,N):t);var e}function k(t){if(t instanceof IDBRequest)return function(t){const e=new Promise(((e,s)=>{const n=()=>{t.removeEventListener("success",r),t.removeEventListener("error",i)},r=()=>{e(k(t.result)),n()},i=()=>{s(t.error),n()};t.addEventListener("success",r),t.addEventListener("error",i)}));return e.then((e=>{e instanceof IDBCursor&&x.set(e,t)})).catch((()=>{})),E.set(e,t),e}(t);if(C.has(t))return C.get(t);const e=T(t);return e!==t&&(C.set(t,e),E.set(e,t)),e}const B=t=>E.get(t);const P=["get","getKey","getAll","getAllKeys","count"],M=["put","add","delete","clear"],W=new Map;function j(t,e){if(!(t instanceof IDBDatabase)||e in t||"string"!=typeof e)return;if(W.get(e))return W.get(e);const s=e.replace(/FromIndex$/,""),n=e!==s,r=M.includes(s);if(!(s in(n?IDBIndex:IDBObjectStore).prototype)||!r&&!P.includes(s))return;const i=async function(t,...e){const i=this.transaction(t,r?"readwrite":"readonly");let a=i.store;return n&&(a=a.index(e.shift())),(await Promise.all([a[s](...e),r&&i.done]))[0]};return W.set(e,i),i}N=(t=>q({},t,{get:(e,s,n)=>j(e,s)||t.get(e,s,n),has:(e,s)=>!!j(e,s)||t.has(e,s)}))(N);try{self["workbox:expiration:6.5.3"]&&_()}catch(t){}const S="cache-entries",K=t=>{const e=new URL(t,location.href);return e.hash="",e.href};class A{constructor(t){this.U=null,this._=t}L(t){const e=t.createObjectStore(S,{keyPath:"id"});e.createIndex("cacheName","cacheName",{unique:!1}),e.createIndex("timestamp","timestamp",{unique:!1})}I(t){this.L(t),this._&&function(t,{blocked:e}={}){const s=indexedDB.deleteDatabase(t);e&&s.addEventListener("blocked",(()=>e())),k(s).then((()=>{}))}(this._)}async setTimestamp(t,e){const s={url:t=K(t),timestamp:e,cacheName:this._,id:this.C(t)},n=(await this.getDb()).transaction(S,"readwrite",{durability:"relaxed"});await n.store.put(s),await n.done}async getTimestamp(t){const e=await this.getDb(),s=await e.get(S,this.C(t));return null==s?void 0:s.timestamp}async expireEntries(t,e){const s=await this.getDb();let n=await s.transaction(S).store.index("timestamp").openCursor(null,"prev");const r=[];let i=0;for(;n;){const s=n.value;s.cacheName===this._&&(t&&s.timestamp<t||e&&i>=e?r.push(n.value):i++),n=await n.continue()}const a=[];for(const t of r)await s.delete(S,t.id),a.push(t.url);return a}C(t){return this._+"|"+K(t)}async getDb(){return this.U||(this.U=await function(t,e,{blocked:s,upgrade:n,blocking:r,terminated:i}={}){const a=indexedDB.open(t,e),o=k(a);return n&&a.addEventListener("upgradeneeded",(t=>{n(k(a.result),t.oldVersion,t.newVersion,k(a.transaction))})),s&&a.addEventListener("blocked",(()=>s())),o.then((t=>{i&&t.addEventListener("close",(()=>i())),r&&t.addEventListener("versionchange",(()=>r()))})).catch((()=>{})),o}("workbox-expiration",1,{upgrade:this.I.bind(this)})),this.U}}class F{constructor(t,e={}){this.N=!1,this.O=!1,this.T=e.maxEntries,this.k=e.maxAgeSeconds,this.B=e.matchOptions,this._=t,this.P=new A(t)}async expireEntries(){if(this.N)return void(this.O=!0);this.N=!0;const t=this.k?Date.now()-1e3*this.k:0,e=await this.P.expireEntries(t,this.T),s=await self.caches.open(this._);for(const t of e)await s.delete(t,this.B);this.N=!1,this.O&&(this.O=!1,b(this.expireEntries()))}async updateTimestamp(t){await this.P.setTimestamp(t,Date.now())}async isURLExpired(t){if(this.k){const e=await this.P.getTimestamp(t),s=Date.now()-1e3*this.k;return void 0===e||e<s}return!1}async delete(){this.O=!1,await this.P.expireEntries(1/0)}}try{self["workbox:range-requests:6.5.3"]&&_()}catch(t){}async function H(t,e){try{if(206===e.status)return e;const n=t.headers.get("range");if(!n)throw new s("no-range-header");const r=function(t){const e=t.trim().toLowerCase();if(!e.startsWith("bytes="))throw new s("unit-must-be-bytes",{normalizedRangeHeader:e});if(e.includes(","))throw new s("single-range-only",{normalizedRangeHeader:e});const n=/(\d*)-(\d*)/.exec(e);if(!n||!n[1]&&!n[2])throw new s("invalid-range-values",{normalizedRangeHeader:e});return{start:""===n[1]?void 0:Number(n[1]),end:""===n[2]?void 0:Number(n[2])}}(n),i=await e.blob(),a=function(t,e,n){const r=t.size;if(n&&n>r||e&&e<0)throw new s("range-not-satisfiable",{size:r,end:n,start:e});let i,a;return void 0!==e&&void 0!==n?(i=e,a=n+1):void 0!==e&&void 0===n?(i=e,a=r):void 0!==n&&void 0===e&&(i=r-n,a=r),{start:i,end:a}}(i,r.start,r.end),o=i.slice(a.start,a.end),c=o.size,h=new Response(o,{status:206,statusText:"Partial Content",headers:e.headers});return h.headers.set("Content-Length",String(c)),h.headers.set("Content-Range",`bytes ${a.start}-${a.end-1}/${i.size}`),h}catch(t){return new Response("",{status:416,statusText:"Range Not Satisfiable"})}}function $(t,e){const s=e();return t.waitUntil(s),s}try{self["workbox:precaching:6.5.3"]&&_()}catch(t){}function z(t){if(!t)throw new s("add-to-cache-list-unexpected-type",{entry:t});if("string"==typeof t){const e=new URL(t,location.href);return{cacheKey:e.href,url:e.href}}const{revision:e,url:n}=t;if(!n)throw new s("add-to-cache-list-unexpected-type",{entry:t});if(!e){const t=new URL(n,location.href);return{cacheKey:t.href,url:t.href}}const r=new URL(n,location.href),i=new URL(n,location.href);return r.searchParams.set("__WB_REVISION__",e),{cacheKey:r.href,url:i.href}}class G{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:t,state:e})=>{e&&(e.originalRequest=t)},this.cachedResponseWillBeUsed=async({event:t,state:e,cachedResponse:s})=>{if("install"===t.type&&e&&e.originalRequest&&e.originalRequest instanceof Request){const t=e.originalRequest.url;s?this.notUpdatedURLs.push(t):this.updatedURLs.push(t)}return s}}}class V{constructor({precacheController:t}){this.cacheKeyWillBeUsed=async({request:t,params:e})=>{const s=(null==e?void 0:e.cacheKey)||this.M.getCacheKeyForURL(t.url);return s?new Request(s,{headers:t.headers}):t},this.M=t}}let J,Q;async function X(t,e){let n=null;if(t.url){n=new URL(t.url).origin}if(n!==self.location.origin)throw new s("cross-origin-copy-response",{origin:n});const r=t.clone(),i={headers:new Headers(r.headers),status:r.status,statusText:r.statusText},a=e?e(i):i,o=function(){if(void 0===J){const t=new Response("");if("body"in t)try{new Response(t.body),J=!0}catch(t){J=!1}J=!1}return J}()?r.body:await r.blob();return new Response(o,a)}class Y extends R{constructor(t={}){t.cacheName=w(t.cacheName),super(t),this.W=!1!==t.fallbackToNetwork,this.plugins.push(Y.copyRedirectedCacheableResponsesPlugin)}async D(t,e){const s=await e.cacheMatch(t);return s||(e.event&&"install"===e.event.type?await this.j(t,e):await this.S(t,e))}async S(t,e){let n;const r=e.params||{};if(!this.W)throw new s("missing-precache-entry",{cacheName:this.cacheName,url:t.url});{const s=r.integrity,i=t.integrity,a=!i||i===s;n=await e.fetch(new Request(t,{integrity:"no-cors"!==t.mode?i||s:void 0})),s&&a&&"no-cors"!==t.mode&&(this.K(),await e.cachePut(t,n.clone()))}return n}async j(t,e){this.K();const n=await e.fetch(t);if(!await e.cachePut(t,n.clone()))throw new s("bad-precaching-response",{url:t.url,status:n.status});return n}K(){let t=null,e=0;for(const[s,n]of this.plugins.entries())n!==Y.copyRedirectedCacheableResponsesPlugin&&(n===Y.defaultPrecacheCacheabilityPlugin&&(t=s),n.cacheWillUpdate&&e++);0===e?this.plugins.push(Y.defaultPrecacheCacheabilityPlugin):e>1&&null!==t&&this.plugins.splice(t,1)}}Y.defaultPrecacheCacheabilityPlugin={cacheWillUpdate:async({response:t})=>!t||t.status>=400?null:t},Y.copyRedirectedCacheableResponsesPlugin={cacheWillUpdate:async({response:t})=>t.redirected?await X(t):t};class Z{constructor({cacheName:t,plugins:e=[],fallbackToNetwork:s=!0}={}){this.A=new Map,this.F=new Map,this.H=new Map,this.u=new Y({cacheName:w(t),plugins:[...e,new V({precacheController:this})],fallbackToNetwork:s}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this.u}precache(t){this.addToCacheList(t),this.$||(self.addEventListener("install",this.install),self.addEventListener("activate",this.activate),this.$=!0)}addToCacheList(t){const e=[];for(const n of t){"string"==typeof n?e.push(n):n&&void 0===n.revision&&e.push(n.url);const{cacheKey:t,url:r}=z(n),i="string"!=typeof n&&n.revision?"reload":"default";if(this.A.has(r)&&this.A.get(r)!==t)throw new s("add-to-cache-list-conflicting-entries",{firstEntry:this.A.get(r),secondEntry:t});if("string"!=typeof n&&n.integrity){if(this.H.has(t)&&this.H.get(t)!==n.integrity)throw new s("add-to-cache-list-conflicting-integrities",{url:r});this.H.set(t,n.integrity)}if(this.A.set(r,t),this.F.set(r,i),e.length>0){const t=`Workbox is precaching URLs without revision info: ${e.join(", ")}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(t)}}}install(t){return $(t,(async()=>{const e=new G;this.strategy.plugins.push(e);for(const[e,s]of this.A){const n=this.H.get(s),r=this.F.get(e),i=new Request(e,{integrity:n,cache:r,credentials:"same-origin"});await Promise.all(this.strategy.handleAll({params:{cacheKey:s},request:i,event:t}))}const{updatedURLs:s,notUpdatedURLs:n}=e;return{updatedURLs:s,notUpdatedURLs:n}}))}activate(t){return $(t,(async()=>{const t=await self.caches.open(this.strategy.cacheName),e=await t.keys(),s=new Set(this.A.values()),n=[];for(const r of e)s.has(r.url)||(await t.delete(r),n.push(r.url));return{deletedURLs:n}}))}getURLsToCacheKeys(){return this.A}getCachedURLs(){return[...this.A.keys()]}getCacheKeyForURL(t){const e=new URL(t,location.href);return this.A.get(e.href)}getIntegrityForCacheKey(t){return this.H.get(t)}async matchPrecache(t){const e=t instanceof Request?t.url:t,s=this.getCacheKeyForURL(e);if(s){return(await self.caches.open(this.strategy.cacheName)).match(s)}}createHandlerBoundToURL(t){const e=this.getCacheKeyForURL(t);if(!e)throw new s("non-precached-url",{url:t});return s=>(s.request=new Request(t),s.params=Object.assign({cacheKey:e},s.params),this.strategy.handle(s))}}const tt=()=>(Q||(Q=new Z),Q);class et extends r{constructor(t,e){super((({request:s})=>{const n=t.getURLsToCacheKeys();for(const r of function*(t,{ignoreURLParametersMatching:e=[/^utm_/,/^fbclid$/],directoryIndex:s="index.html",cleanURLs:n=!0,urlManipulation:r}={}){const i=new URL(t,location.href);i.hash="",yield i.href;const a=function(t,e=[]){for(const s of[...t.searchParams.keys()])e.some((t=>t.test(s)))&&t.searchParams.delete(s);return t}(i,e);if(yield a.href,s&&a.pathname.endsWith("/")){const t=new URL(a.href);t.pathname+=s,yield t.href}if(n){const t=new URL(a.href);t.pathname+=".html",yield t.href}if(r){const t=r({url:i});for(const e of t)yield e.href}}(s.url,e)){const e=n.get(r);if(e){return{cacheKey:e,integrity:t.getIntegrityForCacheKey(e)}}}}),t.strategy)}}t.CacheFirst=class extends R{async D(t,e){let n,r=await e.cacheMatch(t);if(!r)try{r=await e.fetchAndCachePut(t)}catch(t){t instanceof Error&&(n=t)}if(!r)throw new s("no-response",{url:t.url,error:n});return r}},t.ExpirationPlugin=class{constructor(t={}){this.cachedResponseWillBeUsed=async({event:t,request:e,cacheName:s,cachedResponse:n})=>{if(!n)return null;const r=this.G(n),i=this.V(s);b(i.expireEntries());const a=i.updateTimestamp(e.url);if(t)try{t.waitUntil(a)}catch(t){}return r?n:null},this.cacheDidUpdate=async({cacheName:t,request:e})=>{const s=this.V(t);await s.updateTimestamp(e.url),await s.expireEntries()},this.J=t,this.k=t.maxAgeSeconds,this.X=new Map,t.purgeOnQuotaError&&function(t){g.add(t)}((()=>this.deleteCacheAndMetadata()))}V(t){if(t===d())throw new s("expire-custom-caches-only");let e=this.X.get(t);return e||(e=new F(t,this.J),this.X.set(t,e)),e}G(t){if(!this.k)return!0;const e=this.Y(t);if(null===e)return!0;return e>=Date.now()-1e3*this.k}Y(t){if(!t.headers.has("date"))return null;const e=t.headers.get("date"),s=new Date(e).getTime();return isNaN(s)?null:s}async deleteCacheAndMetadata(){for(const[t,e]of this.X)await self.caches.delete(t),await e.delete();this.X=new Map}},t.NetworkFirst=class extends R{constructor(t={}){super(t),this.plugins.some((t=>"cacheWillUpdate"in t))||this.plugins.unshift(u),this.Z=t.networkTimeoutSeconds||0}async D(t,e){const n=[],r=[];let i;if(this.Z){const{id:s,promise:a}=this.tt({request:t,logs:n,handler:e});i=s,r.push(a)}const a=this.et({timeoutId:i,request:t,logs:n,handler:e});r.push(a);const o=await e.waitUntil((async()=>await e.waitUntil(Promise.race(r))||await a)());if(!o)throw new s("no-response",{url:t.url});return o}tt({request:t,logs:e,handler:s}){let n;return{promise:new Promise((e=>{n=setTimeout((async()=>{e(await s.cacheMatch(t))}),1e3*this.Z)})),id:n}}async et({timeoutId:t,request:e,logs:s,handler:n}){let r,i;try{i=await n.fetchAndCachePut(e)}catch(t){t instanceof Error&&(r=t)}return t&&clearTimeout(t),!r&&i||(i=await n.cacheMatch(e)),i}},t.RangeRequestsPlugin=class{constructor(){this.cachedResponseWillBeUsed=async({request:t,cachedResponse:e})=>e&&t.headers.has("range")?await H(t,e):e}},t.StaleWhileRevalidate=class extends R{constructor(t={}){super(t),this.plugins.some((t=>"cacheWillUpdate"in t))||this.plugins.unshift(u)}async D(t,e){const n=e.fetchAndCachePut(t).catch((()=>{}));e.waitUntil(n);let r,i=await e.cacheMatch(t);if(i);else try{i=await n}catch(t){t instanceof Error&&(r=t)}if(!i)throw new s("no-response",{url:t.url,error:r});return i}},t.cleanupOutdatedCaches=function(){self.addEventListener("activate",(t=>{const e=w();t.waitUntil((async(t,e="-precache-")=>{const s=(await self.caches.keys()).filter((s=>s.includes(e)&&s.includes(self.registration.scope)&&s!==t));return await Promise.all(s.map((t=>self.caches.delete(t)))),s})(e).then((t=>{})))}))},t.clientsClaim=function(){self.addEventListener("activate",(()=>self.clients.claim()))},t.precacheAndRoute=function(t,e){!function(t){tt().precache(t)}(t),function(t){const e=tt();h(new et(e,t))}(e)},t.registerRoute=h})); +define(["exports"],(function(t){"use strict";try{self["workbox:core:6.5.3"]&&_()}catch(t){}const e=(t,...e)=>{let s=t;return e.length>0&&(s+=` :: ${JSON.stringify(e)}`),s};class s extends Error{constructor(t,s){super(e(t,s)),this.name=t,this.details=s}}try{self["workbox:routing:6.5.3"]&&_()}catch(t){}const n=t=>t&&"object"==typeof t?t:{handle:t};class r{constructor(t,e,s="GET"){this.handler=n(e),this.match=t,this.method=s}setCatchHandler(t){this.catchHandler=n(t)}}class i extends r{constructor(t,e,s){super((({url:e})=>{const s=t.exec(e.href);if(s&&(e.origin===location.origin||0===s.index))return s.slice(1)}),e,s)}}class a{constructor(){this.t=new Map,this.i=new Map}get routes(){return this.t}addFetchListener(){self.addEventListener("fetch",(t=>{const{request:e}=t,s=this.handleRequest({request:e,event:t});s&&t.respondWith(s)}))}addCacheListener(){self.addEventListener("message",(t=>{if(t.data&&"CACHE_URLS"===t.data.type){const{payload:e}=t.data,s=Promise.all(e.urlsToCache.map((e=>{"string"==typeof e&&(e=[e]);const s=new Request(...e);return this.handleRequest({request:s,event:t})})));t.waitUntil(s),t.ports&&t.ports[0]&&s.then((()=>t.ports[0].postMessage(!0)))}}))}handleRequest({request:t,event:e}){const s=new URL(t.url,location.href);if(!s.protocol.startsWith("http"))return;const n=s.origin===location.origin,{params:r,route:i}=this.findMatchingRoute({event:e,request:t,sameOrigin:n,url:s});let a=i&&i.handler;const o=t.method;if(!a&&this.i.has(o)&&(a=this.i.get(o)),!a)return;let c;try{c=a.handle({url:s,request:t,event:e,params:r})}catch(t){c=Promise.reject(t)}const h=i&&i.catchHandler;return c instanceof Promise&&(this.o||h)&&(c=c.catch((async n=>{if(h)try{return await h.handle({url:s,request:t,event:e,params:r})}catch(t){t instanceof Error&&(n=t)}if(this.o)return this.o.handle({url:s,request:t,event:e});throw n}))),c}findMatchingRoute({url:t,sameOrigin:e,request:s,event:n}){const r=this.t.get(s.method)||[];for(const i of r){let r;const a=i.match({url:t,sameOrigin:e,request:s,event:n});if(a)return r=a,(Array.isArray(r)&&0===r.length||a.constructor===Object&&0===Object.keys(a).length||"boolean"==typeof a)&&(r=void 0),{route:i,params:r}}return{}}setDefaultHandler(t,e="GET"){this.i.set(e,n(t))}setCatchHandler(t){this.o=n(t)}registerRoute(t){this.t.has(t.method)||this.t.set(t.method,[]),this.t.get(t.method).push(t)}unregisterRoute(t){if(!this.t.has(t.method))throw new s("unregister-route-but-not-found-with-method",{method:t.method});const e=this.t.get(t.method).indexOf(t);if(!(e>-1))throw new s("unregister-route-route-not-registered");this.t.get(t.method).splice(e,1)}}let o;const c=()=>(o||(o=new a,o.addFetchListener(),o.addCacheListener()),o);function h(t,e,n){let a;if("string"==typeof t){const s=new URL(t,location.href);a=new r((({url:t})=>t.href===s.href),e,n)}else if(t instanceof RegExp)a=new i(t,e,n);else if("function"==typeof t)a=new r(t,e,n);else{if(!(t instanceof r))throw new s("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});a=t}return c().registerRoute(a),a}try{self["workbox:strategies:6.5.3"]&&_()}catch(t){}const u={cacheWillUpdate:async({response:t})=>200===t.status||0===t.status?t:null},l={googleAnalytics:"googleAnalytics",precache:"precache-v2",prefix:"workbox",runtime:"runtime",suffix:"undefined"!=typeof registration?registration.scope:""},f=t=>[l.prefix,t,l.suffix].filter((t=>t&&t.length>0)).join("-"),w=t=>t||f(l.precache),d=t=>t||f(l.runtime);function p(t,e){const s=new URL(t);for(const t of e)s.searchParams.delete(t);return s.href}class y{constructor(){this.promise=new Promise(((t,e)=>{this.resolve=t,this.reject=e}))}}const g=new Set;function m(t){return"string"==typeof t?new Request(t):t}class v{constructor(t,e){this.h={},Object.assign(this,e),this.event=e.event,this.u=t,this.l=new y,this.p=[],this.g=[...t.plugins],this.m=new Map;for(const t of this.g)this.m.set(t,{});this.event.waitUntil(this.l.promise)}async fetch(t){const{event:e}=this;let n=m(t);if("navigate"===n.mode&&e instanceof FetchEvent&&e.preloadResponse){const t=await e.preloadResponse;if(t)return t}const r=this.hasCallback("fetchDidFail")?n.clone():null;try{for(const t of this.iterateCallbacks("requestWillFetch"))n=await t({request:n.clone(),event:e})}catch(t){if(t instanceof Error)throw new s("plugin-error-request-will-fetch",{thrownErrorMessage:t.message})}const i=n.clone();try{let t;t=await fetch(n,"navigate"===n.mode?void 0:this.u.fetchOptions);for(const s of this.iterateCallbacks("fetchDidSucceed"))t=await s({event:e,request:i,response:t});return t}catch(t){throw (r&&(await this.runCallbacks("fetchDidFail",{error:t,event:e,originalRequest:r.clone(),request:i.clone()})), t)}}async fetchAndCachePut(t){const e=await this.fetch(t),s=e.clone();return this.waitUntil(this.cachePut(t,s)),e}async cacheMatch(t){const e=m(t);let s;const{cacheName:n,matchOptions:r}=this.u,i=await this.getCacheKey(e,"read"),a=Object.assign(Object.assign({},r),{cacheName:n});s=await caches.match(i,a);for(const t of this.iterateCallbacks("cachedResponseWillBeUsed"))s=(await t({cacheName:n,matchOptions:r,cachedResponse:s,request:i,event:this.event}))||void 0;return s}async cachePut(t,e){const n=m(t);var r;await(r=0,new Promise((t=>setTimeout(t,r))));const i=await this.getCacheKey(n,"write");if(!e)throw new s("cache-put-with-no-response",{url:(a=i.url,new URL(String(a),location.href).href.replace(new RegExp(`^${location.origin}`),""))});var a;const o=await this.v(e);if(!o)return!1;const{cacheName:c,matchOptions:h}=this.u,u=await self.caches.open(c),l=this.hasCallback("cacheDidUpdate"),f=l?await async function(t,e,s,n){const r=p(e.url,s);if(e.url===r)return t.match(e,n);const i=Object.assign(Object.assign({},n),{ignoreSearch:!0}),a=await t.keys(e,i);for(const e of a)if(r===p(e.url,s))return t.match(e,n)}(u,i.clone(),["__WB_REVISION__"],h):null;try{await u.put(i,l?o.clone():o)}catch(t){if(t instanceof Error)throw("QuotaExceededError"===t.name&&(await async function(){for(const t of g)await t()}()), t)}for(const t of this.iterateCallbacks("cacheDidUpdate"))await t({cacheName:c,oldResponse:f,newResponse:o.clone(),request:i,event:this.event});return!0}async getCacheKey(t,e){const s=`${t.url} | ${e}`;if(!this.h[s]){let n=t;for(const t of this.iterateCallbacks("cacheKeyWillBeUsed"))n=m(await t({mode:e,request:n,event:this.event,params:this.params}));this.h[s]=n}return this.h[s]}hasCallback(t){for(const e of this.u.plugins)if(t in e)return!0;return!1}async runCallbacks(t,e){for(const s of this.iterateCallbacks(t))await s(e)}*iterateCallbacks(t){for(const e of this.u.plugins)if("function"==typeof e[t]){const s=this.m.get(e),n=n=>{const r=Object.assign(Object.assign({},n),{state:s});return e[t](r)};yield n}}waitUntil(t){return this.p.push(t),t}async doneWaiting(){let t;for(;t=this.p.shift();)await t}destroy(){this.l.resolve(null)}async v(t){let e=t,s=!1;for(const t of this.iterateCallbacks("cacheWillUpdate"))if(e=(await t({request:this.request,response:e,event:this.event}))||void 0,s=!0,!e)break;return s||e&&200!==e.status&&(e=void 0),e}}class R{constructor(t={}){this.cacheName=d(t.cacheName),this.plugins=t.plugins||[],this.fetchOptions=t.fetchOptions,this.matchOptions=t.matchOptions}handle(t){const[e]=this.handleAll(t);return e}handleAll(t){t instanceof FetchEvent&&(t={event:t,request:t.request});const e=t.event,s="string"==typeof t.request?new Request(t.request):t.request,n="params"in t?t.params:void 0,r=new v(this,{event:e,request:s,params:n}),i=this.R(r,s,e);return[i,this.q(i,r,s,e)]}async R(t,e,n){let r;await t.runCallbacks("handlerWillStart",{event:n,request:e});try{if(r=await this.D(e,t),!r||"error"===r.type)throw new s("no-response",{url:e.url})}catch(s){if(s instanceof Error)for(const i of t.iterateCallbacks("handlerDidError"))if(r=await i({error:s,event:n,request:e}),r)break;if(!r)throw s}for(const s of t.iterateCallbacks("handlerWillRespond"))r=await s({event:n,request:e,response:r});return r}async q(t,e,s,n){let r,i;try{r=await t}catch(i){}try{await e.runCallbacks("handlerDidRespond",{event:n,request:s,response:r}),await e.doneWaiting()}catch(t){t instanceof Error&&(i=t)}if(await e.runCallbacks("handlerDidComplete",{event:n,request:s,response:r,error:i}),e.destroy(),i)throw i}}function b(t){t.then((()=>{}))}function q(){return q=Object.assign?Object.assign.bind():function(t){for(var e=1;e<arguments.length;e++){var s=arguments[e];for(var n in s)Object.prototype.hasOwnProperty.call(s,n)&&(t[n]=s[n])}return t},q.apply(this,arguments)}let D,U;const x=new WeakMap,L=new WeakMap,I=new WeakMap,C=new WeakMap,E=new WeakMap;let N={get(t,e,s){if(t instanceof IDBTransaction){if("done"===e)return L.get(t);if("objectStoreNames"===e)return t.objectStoreNames||I.get(t);if("store"===e)return s.objectStoreNames[1]?void 0:s.objectStore(s.objectStoreNames[0])}return k(t[e])},set:(t,e,s)=>(t[e]=s,!0),has:(t,e)=>t instanceof IDBTransaction&&("done"===e||"store"===e)||e in t};function O(t){return t!==IDBDatabase.prototype.transaction||"objectStoreNames"in IDBTransaction.prototype?(U||(U=[IDBCursor.prototype.advance,IDBCursor.prototype.continue,IDBCursor.prototype.continuePrimaryKey])).includes(t)?function(...e){return t.apply(B(this),e),k(x.get(this))}:function(...e){return k(t.apply(B(this),e))}:function(e,...s){const n=t.call(B(this),e,...s);return I.set(n,e.sort?e.sort():[e]),k(n)}}function T(t){return"function"==typeof t?O(t):(t instanceof IDBTransaction&&function(t){if(L.has(t))return;const e=new Promise(((e,s)=>{const n=()=>{t.removeEventListener("complete",r),t.removeEventListener("error",i),t.removeEventListener("abort",i)},r=()=>{e(),n()},i=()=>{s(t.error||new DOMException("AbortError","AbortError")),n()};t.addEventListener("complete",r),t.addEventListener("error",i),t.addEventListener("abort",i)}));L.set(t,e)}(t),e=t,(D||(D=[IDBDatabase,IDBObjectStore,IDBIndex,IDBCursor,IDBTransaction])).some((t=>e instanceof t))?new Proxy(t,N):t);var e}function k(t){if(t instanceof IDBRequest)return function(t){const e=new Promise(((e,s)=>{const n=()=>{t.removeEventListener("success",r),t.removeEventListener("error",i)},r=()=>{e(k(t.result)),n()},i=()=>{s(t.error),n()};t.addEventListener("success",r),t.addEventListener("error",i)}));return e.then((e=>{e instanceof IDBCursor&&x.set(e,t)})).catch((()=>{})),E.set(e,t),e}(t);if(C.has(t))return C.get(t);const e=T(t);return e!==t&&(C.set(t,e),E.set(e,t)),e}const B=t=>E.get(t);const P=["get","getKey","getAll","getAllKeys","count"],M=["put","add","delete","clear"],W=new Map;function j(t,e){if(!(t instanceof IDBDatabase)||e in t||"string"!=typeof e)return;if(W.get(e))return W.get(e);const s=e.replace(/FromIndex$/,""),n=e!==s,r=M.includes(s);if(!(s in(n?IDBIndex:IDBObjectStore).prototype)||!r&&!P.includes(s))return;const i=async function(t,...e){const i=this.transaction(t,r?"readwrite":"readonly");let a=i.store;return n&&(a=a.index(e.shift())),(await Promise.all([a[s](...e),r&&i.done]))[0]};return W.set(e,i),i}N=(t=>q({},t,{get:(e,s,n)=>j(e,s)||t.get(e,s,n),has:(e,s)=>!!j(e,s)||t.has(e,s)}))(N);try{self["workbox:expiration:6.5.3"]&&_()}catch(t){}const S="cache-entries",K=t=>{const e=new URL(t,location.href);return e.hash="",e.href};class A{constructor(t){this.U=null,this._=t}L(t){const e=t.createObjectStore(S,{keyPath:"id"});e.createIndex("cacheName","cacheName",{unique:!1}),e.createIndex("timestamp","timestamp",{unique:!1})}I(t){this.L(t),this._&&function(t,{blocked:e}={}){const s=indexedDB.deleteDatabase(t);e&&s.addEventListener("blocked",(()=>e())),k(s).then((()=>{}))}(this._)}async setTimestamp(t,e){const s={url:t=K(t),timestamp:e,cacheName:this._,id:this.C(t)},n=(await this.getDb()).transaction(S,"readwrite",{durability:"relaxed"});await n.store.put(s),await n.done}async getTimestamp(t){const e=await this.getDb(),s=await e.get(S,this.C(t));return null==s?void 0:s.timestamp}async expireEntries(t,e){const s=await this.getDb();let n=await s.transaction(S).store.index("timestamp").openCursor(null,"prev");const r=[];let i=0;for(;n;){const s=n.value;s.cacheName===this._&&(t&&s.timestamp<t||e&&i>=e?r.push(n.value):i++),n=await n.continue()}const a=[];for(const t of r)await s.delete(S,t.id),a.push(t.url);return a}C(t){return this._+"|"+K(t)}async getDb(){return this.U||(this.U=await function(t,e,{blocked:s,upgrade:n,blocking:r,terminated:i}={}){const a=indexedDB.open(t,e),o=k(a);return n&&a.addEventListener("upgradeneeded",(t=>{n(k(a.result),t.oldVersion,t.newVersion,k(a.transaction))})),s&&a.addEventListener("blocked",(()=>s())),o.then((t=>{i&&t.addEventListener("close",(()=>i())),r&&t.addEventListener("versionchange",(()=>r()))})).catch((()=>{})),o}("workbox-expiration",1,{upgrade:this.I.bind(this)})),this.U}}class F{constructor(t,e={}){this.N=!1,this.O=!1,this.T=e.maxEntries,this.k=e.maxAgeSeconds,this.B=e.matchOptions,this._=t,this.P=new A(t)}async expireEntries(){if(this.N)return void(this.O=!0);this.N=!0;const t=this.k?Date.now()-1e3*this.k:0,e=await this.P.expireEntries(t,this.T),s=await self.caches.open(this._);for(const t of e)await s.delete(t,this.B);this.N=!1,this.O&&(this.O=!1,b(this.expireEntries()))}async updateTimestamp(t){await this.P.setTimestamp(t,Date.now())}async isURLExpired(t){if(this.k){const e=await this.P.getTimestamp(t),s=Date.now()-1e3*this.k;return void 0===e||e<s}return!1}async delete(){this.O=!1,await this.P.expireEntries(1/0)}}try{self["workbox:range-requests:6.5.3"]&&_()}catch(t){}async function H(t,e){try{if(206===e.status)return e;const n=t.headers.get("range");if(!n)throw new s("no-range-header");const r=function(t){const e=t.trim().toLowerCase();if(!e.startsWith("bytes="))throw new s("unit-must-be-bytes",{normalizedRangeHeader:e});if(e.includes(","))throw new s("single-range-only",{normalizedRangeHeader:e});const n=/(\d*)-(\d*)/.exec(e);if(!n||!n[1]&&!n[2])throw new s("invalid-range-values",{normalizedRangeHeader:e});return{start:""===n[1]?void 0:Number(n[1]),end:""===n[2]?void 0:Number(n[2])}}(n),i=await e.blob(),a=function(t,e,n){const r=t.size;if(n&&n>r||e&&e<0)throw new s("range-not-satisfiable",{size:r,end:n,start:e});let i,a;return void 0!==e&&void 0!==n?(i=e,a=n+1):void 0!==e&&void 0===n?(i=e,a=r):void 0!==n&&void 0===e&&(i=r-n,a=r),{start:i,end:a}}(i,r.start,r.end),o=i.slice(a.start,a.end),c=o.size,h=new Response(o,{status:206,statusText:"Partial Content",headers:e.headers});return h.headers.set("Content-Length",String(c)),h.headers.set("Content-Range",`bytes ${a.start}-${a.end-1}/${i.size}`),h}catch(t){return new Response("",{status:416,statusText:"Range Not Satisfiable"})}}function $(t,e){const s=e();return t.waitUntil(s),s}try{self["workbox:precaching:6.5.3"]&&_()}catch(t){}function z(t){if(!t)throw new s("add-to-cache-list-unexpected-type",{entry:t});if("string"==typeof t){const e=new URL(t,location.href);return{cacheKey:e.href,url:e.href}}const{revision:e,url:n}=t;if(!n)throw new s("add-to-cache-list-unexpected-type",{entry:t});if(!e){const t=new URL(n,location.href);return{cacheKey:t.href,url:t.href}}const r=new URL(n,location.href),i=new URL(n,location.href);return r.searchParams.set("__WB_REVISION__",e),{cacheKey:r.href,url:i.href}}class G{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:t,state:e})=>{e&&(e.originalRequest=t)},this.cachedResponseWillBeUsed=async({event:t,state:e,cachedResponse:s})=>{if("install"===t.type&&e&&e.originalRequest&&e.originalRequest instanceof Request){const t=e.originalRequest.url;s?this.notUpdatedURLs.push(t):this.updatedURLs.push(t)}return s}}}class V{constructor({precacheController:t}){this.cacheKeyWillBeUsed=async({request:t,params:e})=>{const s=(null==e?void 0:e.cacheKey)||this.M.getCacheKeyForURL(t.url);return s?new Request(s,{headers:t.headers}):t},this.M=t}}let J,Q;async function X(t,e){let n=null;if(t.url){n=new URL(t.url).origin}if(n!==self.location.origin)throw new s("cross-origin-copy-response",{origin:n});const r=t.clone(),i={headers:new Headers(r.headers),status:r.status,statusText:r.statusText},a=e?e(i):i,o=function(){if(void 0===J){const t=new Response("");if("body"in t)try{new Response(t.body),J=!0}catch(t){J=!1}J=!1}return J}()?r.body:await r.blob();return new Response(o,a)}class Y extends R{constructor(t={}){t.cacheName=w(t.cacheName),super(t),this.W=!1!==t.fallbackToNetwork,this.plugins.push(Y.copyRedirectedCacheableResponsesPlugin)}async D(t,e){const s=await e.cacheMatch(t);return s||(e.event&&"install"===e.event.type?await this.j(t,e):await this.S(t,e))}async S(t,e){let n;const r=e.params||{};if(!this.W)throw new s("missing-precache-entry",{cacheName:this.cacheName,url:t.url});{const s=r.integrity,i=t.integrity,a=!i||i===s;n=await e.fetch(new Request(t,{integrity:"no-cors"!==t.mode?i||s:void 0})),s&&a&&"no-cors"!==t.mode&&(this.K(),await e.cachePut(t,n.clone()))}return n}async j(t,e){this.K();const n=await e.fetch(t);if(!(await e.cachePut(t,n.clone())))throw new s("bad-precaching-response",{url:t.url,status:n.status});return n}K(){let t=null,e=0;for(const[s,n]of this.plugins.entries())n!==Y.copyRedirectedCacheableResponsesPlugin&&(n===Y.defaultPrecacheCacheabilityPlugin&&(t=s),n.cacheWillUpdate&&e++);0===e?this.plugins.push(Y.defaultPrecacheCacheabilityPlugin):e>1&&null!==t&&this.plugins.splice(t,1)}}Y.defaultPrecacheCacheabilityPlugin={cacheWillUpdate:async({response:t})=>!t||t.status>=400?null:t},Y.copyRedirectedCacheableResponsesPlugin={cacheWillUpdate:async({response:t})=>t.redirected?await X(t):t};class Z{constructor({cacheName:t,plugins:e=[],fallbackToNetwork:s=!0}={}){this.A=new Map,this.F=new Map,this.H=new Map,this.u=new Y({cacheName:w(t),plugins:[...e,new V({precacheController:this})],fallbackToNetwork:s}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this.u}precache(t){this.addToCacheList(t),this.$||(self.addEventListener("install",this.install),self.addEventListener("activate",this.activate),this.$=!0)}addToCacheList(t){const e=[];for(const n of t){"string"==typeof n?e.push(n):n&&void 0===n.revision&&e.push(n.url);const{cacheKey:t,url:r}=z(n),i="string"!=typeof n&&n.revision?"reload":"default";if(this.A.has(r)&&this.A.get(r)!==t)throw new s("add-to-cache-list-conflicting-entries",{firstEntry:this.A.get(r),secondEntry:t});if("string"!=typeof n&&n.integrity){if(this.H.has(t)&&this.H.get(t)!==n.integrity)throw new s("add-to-cache-list-conflicting-integrities",{url:r});this.H.set(t,n.integrity)}if(this.A.set(r,t),this.F.set(r,i),e.length>0){const t=`Workbox is precaching URLs without revision info: ${e.join(", ")}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(t)}}}install(t){return $(t,(async()=>{const e=new G;this.strategy.plugins.push(e);for(const[e,s]of this.A){const n=this.H.get(s),r=this.F.get(e),i=new Request(e,{integrity:n,cache:r,credentials:"same-origin"});await Promise.all(this.strategy.handleAll({params:{cacheKey:s},request:i,event:t}))}const{updatedURLs:s,notUpdatedURLs:n}=e;return{updatedURLs:s,notUpdatedURLs:n}}))}activate(t){return $(t,(async()=>{const t=await self.caches.open(this.strategy.cacheName),e=await t.keys(),s=new Set(this.A.values()),n=[];for(const r of e)s.has(r.url)||(await t.delete(r),n.push(r.url));return{deletedURLs:n}}))}getURLsToCacheKeys(){return this.A}getCachedURLs(){return[...this.A.keys()]}getCacheKeyForURL(t){const e=new URL(t,location.href);return this.A.get(e.href)}getIntegrityForCacheKey(t){return this.H.get(t)}async matchPrecache(t){const e=t instanceof Request?t.url:t,s=this.getCacheKeyForURL(e);if(s){return(await self.caches.open(this.strategy.cacheName)).match(s)}}createHandlerBoundToURL(t){const e=this.getCacheKeyForURL(t);if(!e)throw new s("non-precached-url",{url:t});return s=>(s.request=new Request(t),s.params=Object.assign({cacheKey:e},s.params),this.strategy.handle(s))}}const tt=()=>(Q||(Q=new Z),Q);class et extends r{constructor(t,e){super((({request:s})=>{const n=t.getURLsToCacheKeys();for(const r of function*(t,{ignoreURLParametersMatching:e=[/^utm_/,/^fbclid$/],directoryIndex:s="index.html",cleanURLs:n=!0,urlManipulation:r}={}){const i=new URL(t,location.href);i.hash="",yield i.href;const a=function(t,e=[]){for(const s of[...t.searchParams.keys()])e.some((t=>t.test(s)))&&t.searchParams.delete(s);return t}(i,e);if(yield a.href,s&&a.pathname.endsWith("/")){const t=new URL(a.href);t.pathname+=s,yield t.href}if(n){const t=new URL(a.href);t.pathname+=".html",yield t.href}if(r){const t=r({url:i});for(const e of t)yield e.href}}(s.url,e)){const e=n.get(r);if(e){return{cacheKey:e,integrity:t.getIntegrityForCacheKey(e)}}}}),t.strategy)}}t.CacheFirst=class extends R{async D(t,e){let n,r=await e.cacheMatch(t);if(!r)try{r=await e.fetchAndCachePut(t)}catch(t){t instanceof Error&&(n=t)}if(!r)throw new s("no-response",{url:t.url,error:n});return r}},t.ExpirationPlugin=class{constructor(t={}){this.cachedResponseWillBeUsed=async({event:t,request:e,cacheName:s,cachedResponse:n})=>{if(!n)return null;const r=this.G(n),i=this.V(s);b(i.expireEntries());const a=i.updateTimestamp(e.url);if(t)try{t.waitUntil(a)}catch(t){}return r?n:null},this.cacheDidUpdate=async({cacheName:t,request:e})=>{const s=this.V(t);await s.updateTimestamp(e.url),await s.expireEntries()},this.J=t,this.k=t.maxAgeSeconds,this.X=new Map,t.purgeOnQuotaError&&function(t){g.add(t)}((()=>this.deleteCacheAndMetadata()))}V(t){if(t===d())throw new s("expire-custom-caches-only");let e=this.X.get(t);return e||(e=new F(t,this.J),this.X.set(t,e)),e}G(t){if(!this.k)return!0;const e=this.Y(t);if(null===e)return!0;return e>=Date.now()-1e3*this.k}Y(t){if(!t.headers.has("date"))return null;const e=t.headers.get("date"),s=new Date(e).getTime();return isNaN(s)?null:s}async deleteCacheAndMetadata(){for(const[t,e]of this.X)await self.caches.delete(t),await e.delete();this.X=new Map}},t.NetworkFirst=class extends R{constructor(t={}){super(t),this.plugins.some((t=>"cacheWillUpdate"in t))||this.plugins.unshift(u),this.Z=t.networkTimeoutSeconds||0}async D(t,e){const n=[],r=[];let i;if(this.Z){const{id:s,promise:a}=this.tt({request:t,logs:n,handler:e});i=s,r.push(a)}const a=this.et({timeoutId:i,request:t,logs:n,handler:e});r.push(a);const o=await e.waitUntil((async()=>(await e.waitUntil(Promise.race(r)))||(await a))());if(!o)throw new s("no-response",{url:t.url});return o}tt({request:t,logs:e,handler:s}){let n;return{promise:new Promise((e=>{n=setTimeout((async()=>{e(await s.cacheMatch(t))}),1e3*this.Z)})),id:n}}async et({timeoutId:t,request:e,logs:s,handler:n}){let r,i;try{i=await n.fetchAndCachePut(e)}catch(t){t instanceof Error&&(r=t)}return t&&clearTimeout(t),!r&&i||(i=await n.cacheMatch(e)),i}},t.RangeRequestsPlugin=class{constructor(){this.cachedResponseWillBeUsed=async({request:t,cachedResponse:e})=>e&&t.headers.has("range")?await H(t,e):e}},t.StaleWhileRevalidate=class extends R{constructor(t={}){super(t),this.plugins.some((t=>"cacheWillUpdate"in t))||this.plugins.unshift(u)}async D(t,e){const n=e.fetchAndCachePut(t).catch((()=>{}));e.waitUntil(n);let r,i=await e.cacheMatch(t);if(i);else try{i=await n}catch(t){t instanceof Error&&(r=t)}if(!i)throw new s("no-response",{url:t.url,error:r});return i}},t.cleanupOutdatedCaches=function(){self.addEventListener("activate",(t=>{const e=w();t.waitUntil((async(t,e="-precache-")=>{const s=(await self.caches.keys()).filter((s=>s.includes(e)&&s.includes(self.registration.scope)&&s!==t));return await Promise.all(s.map((t=>self.caches.delete(t)))),s})(e).then((t=>{})))}))},t.clientsClaim=function(){self.addEventListener("activate",(()=>self.clients.claim()))},t.precacheAndRoute=function(t,e){!function(t){tt().precache(t)}(t),function(t){const e=tt();h(new et(e,t))}(e)},t.registerRoute=h}));
M frontend/theme.tsfrontend/theme.ts

@@ -1,4 +1,4 @@

-import {createTheme} from '@material-ui/core/styles'; +import {createTheme} from '@mui/material/styles'; export const caroster = { palette: {

@@ -18,20 +18,21 @@ },

}, mixins: { toolbar: { - minHeight: 56, + minHeight: 64, }, }, - overrides: { - MuiAppBar: { - colorPrimary: { - backgroundColor: '#242424', - color: 'white', + components: { + MuiTextField: { + defaultProps: { + variant: 'standard', }, }, MuiInput: { - underline: { - '&&&&:hover:before': { - borderBottomColor: '#009688', + styleOverrides: { + underline: { + '&&&&:hover:before': { + borderBottomColor: '#009688', + }, }, }, },
M frontend/yarn.lockfrontend/yarn.lock

@@ -208,7 +208,7 @@ integrity sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==

dependencies: "@babel/types" "^7.18.9" -"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.18.6": +"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==

@@ -532,7 +532,7 @@ integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==

dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.18.6": +"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.17.12", "@babel/plugin-syntax-jsx@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0" integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==

@@ -974,7 +974,7 @@ dependencies:

core-js-pure "^3.20.2" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.14.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.17.2", "@babel/runtime@^7.18.9", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.17.2", "@babel/runtime@^7.18.3", "@babel/runtime@^7.18.9", "@babel/runtime@^7.19.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": version "7.19.0" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259" integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==

@@ -1022,11 +1022,23 @@ integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==

dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@date-io/core@1.x", "@date-io/core@^1.3.13": +"@date-io/core@^1.3.13": version "1.3.13" resolved "https://registry.yarnpkg.com/@date-io/core/-/core-1.3.13.tgz#90c71da493f20204b7a972929cc5c482d078b3fa" integrity sha512-AlEKV7TxjeK+jxWVKcCFrfYAk8spX9aCyiToFIiLPtfQbsjmRGLIhb5VZgptQcJdHtLXo7+m0DuurwFgUToQuA== +"@date-io/core@^2.15.0", "@date-io/core@^2.16.0": + version "2.16.0" + resolved "https://registry.yarnpkg.com/@date-io/core/-/core-2.16.0.tgz#7871bfc1d9bca9aa35ad444a239505589d0f22f6" + integrity sha512-DYmSzkr+jToahwWrsiRA2/pzMEtz9Bq1euJwoOuYwuwIYXnZFtHajY2E6a1VNVDc9jP8YUXK1BvnZH9mmT19Zg== + +"@date-io/date-fns@^2.15.0": + version "2.16.0" + resolved "https://registry.yarnpkg.com/@date-io/date-fns/-/date-fns-2.16.0.tgz#bd5e09b6ecb47ee55e593fc3a87e7b2caaa3da40" + integrity sha512-bfm5FJjucqlrnQcXDVU5RD+nlGmL3iWgkHTq3uAZWVIuBu6dDmGa3m8a6zo2VQQpu8ambq9H22UyUpn7590joA== + dependencies: + "@date-io/core" "^2.16.0" + "@date-io/dayjs@1.x": version "1.3.13" resolved "https://registry.yarnpkg.com/@date-io/dayjs/-/dayjs-1.3.13.tgz#3a9edf5a7227b31b0f00a4f640f8715626833a61"

@@ -1034,6 +1046,20 @@ integrity sha512-nD39xWYwQjDMIdpUzHIcADHxY9m1hm1DpOaRn3bc2rBdgmwQC0PfW0WYaHyGGP/6LEzEguINRbHuotMhf+T9Sg==

dependencies: "@date-io/core" "^1.3.13" +"@date-io/dayjs@^2.15.0": + version "2.16.0" + resolved "https://registry.yarnpkg.com/@date-io/dayjs/-/dayjs-2.16.0.tgz#0d2c254ad8db1306fdc4b8eda197cb53c9af89dc" + integrity sha512-y5qKyX2j/HG3zMvIxTobYZRGnd1FUW2olZLS0vTj7bEkBQkjd2RO7/FEwDY03Z1geVGlXKnzIATEVBVaGzV4Iw== + dependencies: + "@date-io/core" "^2.16.0" + +"@date-io/luxon@^2.15.0": + version "2.16.0" + resolved "https://registry.yarnpkg.com/@date-io/luxon/-/luxon-2.16.0.tgz#97773d56532706c1a68113a1de07eecd0d41bebb" + integrity sha512-L8UXHa/9VbfRqP4KB7JUZwFgOVxo22rONVod1o7GMN2Oku4PzJ0k1kXc+nLP9lRlF1UAA28oQsQqn85Y/PdBZw== + dependencies: + "@date-io/core" "^2.16.0" + "@date-io/moment@1.x": version "1.3.13" resolved "https://registry.yarnpkg.com/@date-io/moment/-/moment-1.3.13.tgz#56c2772bc4f6675fc6970257e6033e7a7c2960f0"

@@ -1041,10 +1067,120 @@ integrity sha512-3kJYusJtQuOIxq6byZlzAHoW/18iExJer9qfRF5DyyzdAk074seTuJfdofjz4RFfTd/Idk8WylOQpWtERqvFuQ==

dependencies: "@date-io/core" "^1.3.13" -"@emotion/hash@^0.8.0": +"@date-io/moment@^2.15.0": + version "2.16.0" + resolved "https://registry.yarnpkg.com/@date-io/moment/-/moment-2.16.0.tgz#a6658c05e14e7fcc57adeda675462e0b6750542d" + integrity sha512-wvu/40k128kF6P0jPbiyZcPR14VjJAgYEs+mYtsXz/AyWpC2DEJKly7ub+dpevUywbTzzpZysyCxCdzLzxD/uw== + dependencies: + "@date-io/core" "^2.16.0" + +"@emotion/babel-plugin@^11.10.0": + version "11.10.2" + resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.10.2.tgz#879db80ba622b3f6076917a1e6f648b1c7d008c7" + integrity sha512-xNQ57njWTFVfPAc3cjfuaPdsgLp5QOSuRsj9MA6ndEhH/AzuZM86qIQzt6rq+aGBwj3n5/TkLmU5lhAfdRmogA== + dependencies: + "@babel/helper-module-imports" "^7.16.7" + "@babel/plugin-syntax-jsx" "^7.17.12" + "@babel/runtime" "^7.18.3" + "@emotion/hash" "^0.9.0" + "@emotion/memoize" "^0.8.0" + "@emotion/serialize" "^1.1.0" + babel-plugin-macros "^3.1.0" + convert-source-map "^1.5.0" + escape-string-regexp "^4.0.0" + find-root "^1.1.0" + source-map "^0.5.7" + stylis "4.0.13" + +"@emotion/cache@^11.10.0", "@emotion/cache@^11.10.3": + version "11.10.3" + resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.10.3.tgz#c4f67904fad10c945fea5165c3a5a0583c164b87" + integrity sha512-Psmp/7ovAa8appWh3g51goxu/z3iVms7JXOreq136D8Bbn6dYraPnmL6mdM8GThEx9vwSn92Fz+mGSjBzN8UPQ== + dependencies: + "@emotion/memoize" "^0.8.0" + "@emotion/sheet" "^1.2.0" + "@emotion/utils" "^1.2.0" + "@emotion/weak-memoize" "^0.3.0" + stylis "4.0.13" + +"@emotion/hash@^0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.0.tgz#c5153d50401ee3c027a57a177bc269b16d889cb7" + integrity sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ== + +"@emotion/is-prop-valid@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz#7f2d35c97891669f7e276eb71c83376a5dc44c83" + integrity sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg== + dependencies: + "@emotion/memoize" "^0.8.0" + +"@emotion/memoize@^0.8.0": version "0.8.0" - resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" - integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== + resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.0.tgz#f580f9beb67176fa57aae70b08ed510e1b18980f" + integrity sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA== + +"@emotion/react@^11.10.4": + version "11.10.4" + resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.10.4.tgz#9dc6bccbda5d70ff68fdb204746c0e8b13a79199" + integrity sha512-j0AkMpr6BL8gldJZ6XQsQ8DnS9TxEQu1R+OGmDZiWjBAJtCcbt0tS3I/YffoqHXxH6MjgI7KdMbYKw3MEiU9eA== + dependencies: + "@babel/runtime" "^7.18.3" + "@emotion/babel-plugin" "^11.10.0" + "@emotion/cache" "^11.10.0" + "@emotion/serialize" "^1.1.0" + "@emotion/use-insertion-effect-with-fallbacks" "^1.0.0" + "@emotion/utils" "^1.2.0" + "@emotion/weak-memoize" "^0.3.0" + hoist-non-react-statics "^3.3.1" + +"@emotion/serialize@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.0.tgz#b1f97b1011b09346a40e9796c37a3397b4ea8ea8" + integrity sha512-F1ZZZW51T/fx+wKbVlwsfchr5q97iW8brAnXmsskz4d0hVB4O3M/SiA3SaeH06x02lSNzkkQv+n3AX3kCXKSFA== + dependencies: + "@emotion/hash" "^0.9.0" + "@emotion/memoize" "^0.8.0" + "@emotion/unitless" "^0.8.0" + "@emotion/utils" "^1.2.0" + csstype "^3.0.2" + +"@emotion/sheet@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.0.tgz#771b1987855839e214fc1741bde43089397f7be5" + integrity sha512-OiTkRgpxescko+M51tZsMq7Puu/KP55wMT8BgpcXVG2hqXc0Vo0mfymJ/Uj24Hp0i083ji/o0aLddh08UEjq8w== + +"@emotion/styled@^11.10.4": + version "11.10.4" + resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.10.4.tgz#e93f84a4d54003c2acbde178c3f97b421fce1cd4" + integrity sha512-pRl4R8Ez3UXvOPfc2bzIoV8u9P97UedgHS4FPX594ntwEuAMA114wlaHvOK24HB48uqfXiGlYIZYCxVJ1R1ttQ== + dependencies: + "@babel/runtime" "^7.18.3" + "@emotion/babel-plugin" "^11.10.0" + "@emotion/is-prop-valid" "^1.2.0" + "@emotion/serialize" "^1.1.0" + "@emotion/use-insertion-effect-with-fallbacks" "^1.0.0" + "@emotion/utils" "^1.2.0" + +"@emotion/unitless@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.0.tgz#a4a36e9cbdc6903737cd20d38033241e1b8833db" + integrity sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw== + +"@emotion/use-insertion-effect-with-fallbacks@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz#ffadaec35dbb7885bd54de3fa267ab2f860294df" + integrity sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A== + +"@emotion/utils@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.0.tgz#9716eaccbc6b5ded2ea5a90d65562609aab0f561" + integrity sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw== + +"@emotion/weak-memoize@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz#ea89004119dc42db2e1dba0f97d553f7372f6fcb" + integrity sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg== "@eslint/eslintrc@^0.4.3": version "0.4.3"

@@ -1479,81 +1615,146 @@ dependencies:

"@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@material-ui/core@^4.12.4": - version "4.12.4" - resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-4.12.4.tgz#4ac17488e8fcaf55eb6a7f5efb2a131e10138a73" - integrity sha512-tr7xekNlM9LjA6pagJmL8QCgZXaubWUwkJnoYcMKd4gw/t4XiyvnTkjdGrUVicyB2BsdaAv1tvow45bPM4sSwQ== +"@mui/base@5.0.0-alpha.100": + version "5.0.0-alpha.100" + resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-alpha.100.tgz#5587c98fb8c6fdf9138b92c519fe3fd79682f7ac" + integrity sha512-bSoJEKCENtmJrJDECHUe9PiqztIUACuSskyqw9ypqE7Dz3WxL3e8puFsWBkUsz+WOCjXh4B4Xljn88Ucxxv5HA== dependencies: - "@babel/runtime" "^7.4.4" - "@material-ui/styles" "^4.11.5" - "@material-ui/system" "^4.12.2" - "@material-ui/types" "5.1.0" - "@material-ui/utils" "^4.11.3" - "@types/react-transition-group" "^4.2.0" - clsx "^1.0.4" - hoist-non-react-statics "^3.3.2" - popper.js "1.16.1-lts" - prop-types "^15.7.2" - react-is "^16.8.0 || ^17.0.0" - react-transition-group "^4.4.0" + "@babel/runtime" "^7.19.0" + "@emotion/is-prop-valid" "^1.2.0" + "@mui/types" "^7.2.0" + "@mui/utils" "^5.10.6" + "@popperjs/core" "^2.11.6" + clsx "^1.2.1" + prop-types "^15.8.1" + react-is "^18.2.0" -"@material-ui/pickers@^3.3.10": - version "3.3.10" - resolved "https://registry.yarnpkg.com/@material-ui/pickers/-/pickers-3.3.10.tgz#f1b0f963348cc191645ef0bdeff7a67c6aa25485" - integrity sha512-hS4pxwn1ZGXVkmgD4tpFpaumUaAg2ZzbTrxltfC5yPw4BJV+mGkfnQOB4VpWEYZw2jv65Z0wLwDE/piQiPPZ3w== +"@mui/core-downloads-tracker@^5.10.8": + version "5.10.8" + resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.10.8.tgz#b00402dd0796e7e99a09cfc23cb0b0a6196c8e09" + integrity sha512-V5D7OInO4P9PdT/JACg7fwjbOORm3GklaMVgdGomjyxiyetgRND5CC9r35e1LK/DqHdoyDuhbFzdfrqWtpmEIw== + +"@mui/lab@^5.0.0-alpha.102": + version "5.0.0-alpha.102" + resolved "https://registry.yarnpkg.com/@mui/lab/-/lab-5.0.0-alpha.102.tgz#c6238777d7b1ae94a97d5d56cbbd95b9716a9b2b" + integrity sha512-hIJ82FGdll2NmEp6b2kBhIZ5j/yiOThObBoSYo7WhH+cQCLowbgdAMH45JP9/R8J2qe2lq1K/PI3bLgcGVZ+Rw== dependencies: - "@babel/runtime" "^7.6.0" - "@date-io/core" "1.x" - "@types/styled-jsx" "^2.2.8" - clsx "^1.0.2" - react-transition-group "^4.0.0" - rifm "^0.7.0" + "@babel/runtime" "^7.19.0" + "@mui/base" "5.0.0-alpha.100" + "@mui/system" "^5.10.8" + "@mui/types" "^7.2.0" + "@mui/utils" "^5.10.6" + clsx "^1.2.1" + prop-types "^15.8.1" + react-is "^18.2.0" + +"@mui/material@^5.10.8": + version "5.10.8" + resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.10.8.tgz#a223ec5f1128515107381e875f8d6257a00c3cd3" + integrity sha512-sF/Ka0IJjGXV52zoT4xAWEqXVRjNYbIjATo9L4Q5oQC5iJpGrKJFY16uNtWWB0+vp/nayAuPGZHrxtV+t3ecdQ== + dependencies: + "@babel/runtime" "^7.19.0" + "@mui/base" "5.0.0-alpha.100" + "@mui/core-downloads-tracker" "^5.10.8" + "@mui/system" "^5.10.8" + "@mui/types" "^7.2.0" + "@mui/utils" "^5.10.6" + "@types/react-transition-group" "^4.4.5" + clsx "^1.2.1" + csstype "^3.1.1" + prop-types "^15.8.1" + react-is "^18.2.0" + react-transition-group "^4.4.5" + +"@mui/private-theming@^5.10.6": + version "5.10.6" + resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.10.6.tgz#2c6bb2a4b7034cd402a099bd0349f217584e7b25" + integrity sha512-I/W0QyTLRdEx6py3lKAquKO/rNF/7j+nIOM/xCyI9kU0fcotVTcTY08mKMsS6vrzdWpi6pAkD0wP0KwWy5R5VA== + dependencies: + "@babel/runtime" "^7.19.0" + "@mui/utils" "^5.10.6" + prop-types "^15.8.1" + +"@mui/styled-engine@^5.10.8": + version "5.10.8" + resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.10.8.tgz#2db411e4278f06f70ccb6b5cd56ace67109513f6" + integrity sha512-w+y8WI18EJV6zM/q41ug19cE70JTeO6sWFsQ7tgePQFpy6ToCVPh0YLrtqxUZXSoMStW5FMw0t9fHTFAqPbngw== + dependencies: + "@babel/runtime" "^7.19.0" + "@emotion/cache" "^11.10.3" + csstype "^3.1.1" + prop-types "^15.8.1" -"@material-ui/styles@^4.11.5": - version "4.11.5" - resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.11.5.tgz#19f84457df3aafd956ac863dbe156b1d88e2bbfb" - integrity sha512-o/41ot5JJiUsIETME9wVLAJrmIWL3j0R0Bj2kCOLbSfqEkKf0fmaPt+5vtblUh5eXr2S+J/8J3DaCb10+CzPGA== +"@mui/styles@^5.10.8": + version "5.10.8" + resolved "https://registry.yarnpkg.com/@mui/styles/-/styles-5.10.8.tgz#19421237e1580326fe2025adb6edf84730fee77d" + integrity sha512-aQGfz4Av12Bf0C8sJC0a7UoTbusD0IUKo4zNjkB5ERmhIFrbgN1G8XDIVvTgrgwdKbstbFlHDFfXkDcYe9elSQ== dependencies: - "@babel/runtime" "^7.4.4" - "@emotion/hash" "^0.8.0" - "@material-ui/types" "5.1.0" - "@material-ui/utils" "^4.11.3" - clsx "^1.0.4" - csstype "^2.5.2" + "@babel/runtime" "^7.19.0" + "@emotion/hash" "^0.9.0" + "@mui/private-theming" "^5.10.6" + "@mui/types" "^7.2.0" + "@mui/utils" "^5.10.6" + clsx "^1.2.1" + csstype "^3.1.1" hoist-non-react-statics "^3.3.2" - jss "^10.5.1" - jss-plugin-camel-case "^10.5.1" - jss-plugin-default-unit "^10.5.1" - jss-plugin-global "^10.5.1" - jss-plugin-nested "^10.5.1" - jss-plugin-props-sort "^10.5.1" - jss-plugin-rule-value-function "^10.5.1" - jss-plugin-vendor-prefixer "^10.5.1" - prop-types "^15.7.2" + jss "^10.9.2" + jss-plugin-camel-case "^10.9.2" + jss-plugin-default-unit "^10.9.2" + jss-plugin-global "^10.9.2" + jss-plugin-nested "^10.9.2" + jss-plugin-props-sort "^10.9.2" + jss-plugin-rule-value-function "^10.9.2" + jss-plugin-vendor-prefixer "^10.9.2" + prop-types "^15.8.1" -"@material-ui/system@^4.12.2": - version "4.12.2" - resolved "https://registry.yarnpkg.com/@material-ui/system/-/system-4.12.2.tgz#f5c389adf3fce4146edd489bf4082d461d86aa8b" - integrity sha512-6CSKu2MtmiJgcCGf6nBQpM8fLkuB9F55EKfbdTC80NND5wpTmKzwdhLYLH3zL4cLlK0gVaaltW7/wMuyTnN0Lw== +"@mui/system@^5.10.8": + version "5.10.8" + resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.10.8.tgz#afea52aeed34bd2d98c993322b5b09585106953d" + integrity sha512-hRQ354zcrYP/KHqK8FheICSvE9raQaUgQaV+A3oD4JETaFUCVI9Ytt+RcQYgTqx02xlCXIjl8LK1rPjTneySqw== dependencies: - "@babel/runtime" "^7.4.4" - "@material-ui/utils" "^4.11.3" - csstype "^2.5.2" - prop-types "^15.7.2" + "@babel/runtime" "^7.19.0" + "@mui/private-theming" "^5.10.6" + "@mui/styled-engine" "^5.10.8" + "@mui/types" "^7.2.0" + "@mui/utils" "^5.10.6" + clsx "^1.2.1" + csstype "^3.1.1" + prop-types "^15.8.1" + +"@mui/types@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.0.tgz#91380c2d42420f51f404120f7a9270eadd6f5c23" + integrity sha512-lGXtFKe5lp3UxTBGqKI1l7G8sE2xBik8qCfrLHD5olwP/YU0/ReWoWT7Lp1//ri32dK39oPMrJN8TgbkCSbsNA== -"@material-ui/types@5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@material-ui/types/-/types-5.1.0.tgz#efa1c7a0b0eaa4c7c87ac0390445f0f88b0d88f2" - integrity sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A== +"@mui/utils@^5.10.3", "@mui/utils@^5.10.6": + version "5.10.6" + resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.10.6.tgz#98d432d2b05544c46efe356cf095cea3a37c2e59" + integrity sha512-g0Qs8xN/MW2M3fLL8197h5J2VB9U+49fLlnKKqC6zy/yus5cZwdT+Gwec+wUMxgwQoxMDn+J8oDWAn28kEOR/Q== + dependencies: + "@babel/runtime" "^7.19.0" + "@types/prop-types" "^15.7.5" + "@types/react-is" "^16.7.1 || ^17.0.0" + prop-types "^15.8.1" + react-is "^18.2.0" -"@material-ui/utils@^4.11.3": - version "4.11.3" - resolved "https://registry.yarnpkg.com/@material-ui/utils/-/utils-4.11.3.tgz#232bd86c4ea81dab714f21edad70b7fdf0253942" - integrity sha512-ZuQPV4rBK/V1j2dIkSSEcH5uT6AaHuKWFfotADHsC0wVL1NLd2WkFCm4ZZbX33iO4ydl6V0GPngKm8HZQ2oujg== +"@mui/x-date-pickers@^5.0.4": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@mui/x-date-pickers/-/x-date-pickers-5.0.4.tgz#79a509354eea4bedaa955ee52f37d80256d7e415" + integrity sha512-Co4tbwqXSdHfR8UoZSHQpDZqnFdikzQr0lQPG2AjGh9BdB4EdY3YE2+sZyAltjk/AXxp5JzIWDZ2Kj83ClzjwA== dependencies: - "@babel/runtime" "^7.4.4" + "@babel/runtime" "^7.18.9" + "@date-io/core" "^2.15.0" + "@date-io/date-fns" "^2.15.0" + "@date-io/dayjs" "^2.15.0" + "@date-io/luxon" "^2.15.0" + "@date-io/moment" "^2.15.0" + "@mui/utils" "^5.10.3" + "@types/react-transition-group" "^4.4.5" + clsx "^1.2.1" prop-types "^15.7.2" - react-is "^16.8.0 || ^17.0.0" + react-transition-group "^4.4.5" + rifm "^0.12.1" "@n1ru4l/graphql-live-query@^0.10.0": version "0.10.0"

@@ -1690,6 +1891,11 @@ pvtsutils "^1.3.2"

tslib "^2.4.0" webcrypto-core "^1.7.4" +"@popperjs/core@^2.11.6": + version "2.11.6" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45" + integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw== + "@rollup/plugin-babel@^5.2.0": version "5.3.1" resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz#04bc0608f4aa4b2e4b1aebf284344d0f68fda283"

@@ -1829,12 +2035,19 @@ version "4.0.0"

resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== -"@types/prop-types@*": +"@types/prop-types@*", "@types/prop-types@^15.7.5": version "15.7.5" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== -"@types/react-transition-group@^4.2.0": +"@types/react-is@^16.7.1 || ^17.0.0": + version "17.0.3" + resolved "https://registry.yarnpkg.com/@types/react-is/-/react-is-17.0.3.tgz#2d855ba575f2fc8d17ef9861f084acc4b90a137a" + integrity sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw== + dependencies: + "@types/react" "*" + +"@types/react-transition-group@^4.4.5": version "4.4.5" resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.5.tgz#aae20dcf773c5aa275d5b9f7cdbca638abc5e416" integrity sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==

@@ -1862,13 +2075,6 @@ version "0.16.2"

resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== -"@types/styled-jsx@^2.2.8": - version "2.2.9" - resolved "https://registry.yarnpkg.com/@types/styled-jsx/-/styled-jsx-2.2.9.tgz#e50b3f868c055bcbf9bc353eca6c10fdad32a53f" - integrity sha512-W/iTlIkGEyTBGTEvZCey8EgQlQ5l0DwMqi3iOXlLs2kyBwYTXHKEiU6IZ5EwoRwngL8/dGYuzezSup89ttVHLw== - dependencies: - "@types/react" "*" - "@types/trusted-types@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756"

@@ -2211,6 +2417,15 @@ integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==

dependencies: object.assign "^4.1.0" +babel-plugin-macros@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" + integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg== + dependencies: + "@babel/runtime" "^7.12.5" + cosmiconfig "^7.0.0" + resolve "^1.19.0" + babel-plugin-polyfill-corejs2@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz#e4c31d4c89b56f3cf85b92558954c66b54bd972d"

@@ -2481,11 +2696,6 @@ normalize-path "~3.0.0"

readdirp "~3.6.0" optionalDependencies: fsevents "~2.3.2" - -classnames@^2.2.5: - version "2.3.1" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" - integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== clean-stack@^2.0.0: version "2.2.0"

@@ -2547,7 +2757,7 @@ version "1.0.4"

resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== -clsx@^1.0.2, clsx@^1.0.4: +clsx@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==

@@ -2617,7 +2827,7 @@ no-case "^3.0.4"

tslib "^2.0.3" upper-case "^2.0.2" -convert-source-map@^1.7.0: +convert-source-map@^1.5.0, convert-source-map@^1.7.0: version "1.8.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==

@@ -2703,15 +2913,15 @@ dependencies:

"@babel/runtime" "^7.8.3" is-in-browser "^1.0.2" -csstype@^2.5.2: - version "2.6.20" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.20.tgz#9229c65ea0b260cf4d3d997cb06288e36a8d6dda" - integrity sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA== - csstype@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2" integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA== + +csstype@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" + integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== damerau-levenshtein@^1.0.8: version "1.0.8"

@@ -2893,11 +3103,6 @@ version "3.0.0"

resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== -enquire.js@^2.1.6: - version "2.1.6" - resolved "https://registry.yarnpkg.com/enquire.js/-/enquire.js-2.1.6.tgz#3e8780c9b8b835084c3f60e166dbc3c2a3c89814" - integrity sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw== - enquirer@^2.3.5: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d"

@@ -3329,6 +3534,11 @@ commondir "^1.0.1"

make-dir "^3.0.2" pkg-dir "^4.1.0" +find-root@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" + integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== + find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"

@@ -3614,7 +3824,7 @@ dependencies:

capital-case "^1.0.4" tslib "^2.0.3" -hoist-non-react-statics@^3.3.2: +hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==

@@ -4099,13 +4309,6 @@ dependencies:

remedial "^1.0.7" remove-trailing-spaces "^1.0.6" -json2mq@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/json2mq/-/json2mq-0.2.0.tgz#b637bd3ba9eabe122c83e9720483aeb10d2c904a" - integrity sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA== - dependencies: - string-convert "^0.2.0" - json5@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"

@@ -4153,7 +4356,7 @@ lodash.once "^4.0.0"

ms "^2.1.1" semver "^5.6.0" -jss-plugin-camel-case@^10.5.1: +jss-plugin-camel-case@^10.9.2: version "10.9.2" resolved "https://registry.yarnpkg.com/jss-plugin-camel-case/-/jss-plugin-camel-case-10.9.2.tgz#76dddfa32f9e62d17daa4e3504991fd0933b89e1" integrity sha512-wgBPlL3WS0WDJ1lPJcgjux/SHnDuu7opmgQKSraKs4z8dCCyYMx9IDPFKBXQ8Q5dVYij1FFV0WdxyhuOOAXuTg==

@@ -4162,7 +4365,7 @@ "@babel/runtime" "^7.3.1"

hyphenate-style-name "^1.0.3" jss "10.9.2" -jss-plugin-default-unit@^10.5.1: +jss-plugin-default-unit@^10.9.2: version "10.9.2" resolved "https://registry.yarnpkg.com/jss-plugin-default-unit/-/jss-plugin-default-unit-10.9.2.tgz#3e7f4a1506b18d8fe231554fd982439feb2a9c53" integrity sha512-pYg0QX3bBEFtTnmeSI3l7ad1vtHU42YEEpgW7pmIh+9pkWNWb5dwS/4onSfAaI0kq+dOZHzz4dWe+8vWnanoSg==

@@ -4170,7 +4373,7 @@ dependencies:

"@babel/runtime" "^7.3.1" jss "10.9.2" -jss-plugin-global@^10.5.1: +jss-plugin-global@^10.9.2: version "10.9.2" resolved "https://registry.yarnpkg.com/jss-plugin-global/-/jss-plugin-global-10.9.2.tgz#e7f2ad4a5e8e674fb703b04b57a570b8c3e5c2c2" integrity sha512-GcX0aE8Ef6AtlasVrafg1DItlL/tWHoC4cGir4r3gegbWwF5ZOBYhx04gurPvWHC8F873aEGqge7C17xpwmp2g==

@@ -4178,7 +4381,7 @@ dependencies:

"@babel/runtime" "^7.3.1" jss "10.9.2" -jss-plugin-nested@^10.5.1: +jss-plugin-nested@^10.9.2: version "10.9.2" resolved "https://registry.yarnpkg.com/jss-plugin-nested/-/jss-plugin-nested-10.9.2.tgz#3aa2502816089ecf3981e1a07c49b276d67dca63" integrity sha512-VgiOWIC6bvgDaAL97XCxGD0BxOKM0K0zeB/ECyNaVF6FqvdGB9KBBWRdy2STYAss4VVA7i5TbxFZN+WSX1kfQA==

@@ -4187,7 +4390,7 @@ "@babel/runtime" "^7.3.1"

jss "10.9.2" tiny-warning "^1.0.2" -jss-plugin-props-sort@^10.5.1: +jss-plugin-props-sort@^10.9.2: version "10.9.2" resolved "https://registry.yarnpkg.com/jss-plugin-props-sort/-/jss-plugin-props-sort-10.9.2.tgz#645f6c8f179309667b3e6212f66b59a32fb3f01f" integrity sha512-AP1AyUTbi2szylgr+O0OB7gkIxEGzySLITZ2GpsaoX72YMCGI2jYAc+WUhPfvUnZYiauF4zTnN4V4TGuvFjJlw==

@@ -4195,7 +4398,7 @@ dependencies:

"@babel/runtime" "^7.3.1" jss "10.9.2" -jss-plugin-rule-value-function@^10.5.1: +jss-plugin-rule-value-function@^10.9.2: version "10.9.2" resolved "https://registry.yarnpkg.com/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.9.2.tgz#9afe07596e477123cbf11120776be6a64494541f" integrity sha512-vf5ms8zvLFMub6swbNxvzsurHfUZ5Shy5aJB2gIpY6WNA3uLinEcxYyraQXItRHi5ivXGqYciFDRM2ZoVoRZ4Q==

@@ -4204,7 +4407,7 @@ "@babel/runtime" "^7.3.1"

jss "10.9.2" tiny-warning "^1.0.2" -jss-plugin-vendor-prefixer@^10.5.1: +jss-plugin-vendor-prefixer@^10.9.2: version "10.9.2" resolved "https://registry.yarnpkg.com/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.9.2.tgz#410a0f3b9f8dbbfba58f4d329134df4849aa1237" integrity sha512-SxcEoH+Rttf9fEv6KkiPzLdXRmI6waOTcMkbbEFgdZLDYNIP9UKNHFy6thhbRKqv0XMQZdrEsbDyV464zE/dUA==

@@ -4213,7 +4416,7 @@ "@babel/runtime" "^7.3.1"

css-vendor "^2.0.8" jss "10.9.2" -jss@10.9.2, jss@^10.5.1: +jss@10.9.2, jss@^10.9.2: version "10.9.2" resolved "https://registry.yarnpkg.com/jss/-/jss-10.9.2.tgz#9379be1f195ef98011dfd31f9448251bd61b95a9" integrity sha512-b8G6rWpYLR4teTUbGd4I4EsnWjg7MN0Q5bSsjKhVkJVjhQDy2KzkbD2AW3TuT0RYZVmZZHKIrXDn6kjU14qkUg==

@@ -4957,11 +5160,6 @@ integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==

dependencies: find-up "^4.0.0" -popper.js@1.16.1-lts: - version "1.16.1-lts" - resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1-lts.tgz#cf6847b807da3799d80ee3d6d2f90df8a3f50b05" - integrity sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA== - popper.js@^1.16.0: version "1.16.1" resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b"

@@ -5102,10 +5300,10 @@ version "16.13.1"

resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -"react-is@^16.8.0 || ^17.0.0": - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" - integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react-is@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== react-joyride@^2.5.2: version "2.5.2"

@@ -5132,18 +5330,7 @@ version "2.1.2"

resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.2.tgz#dc6345b9e8f9906dc2eeb68700b615e0b4fe752a" integrity sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw== -react-slick@^0.29.0: - version "0.29.0" - resolved "https://registry.yarnpkg.com/react-slick/-/react-slick-0.29.0.tgz#0bed5ea42bf75a23d40c0259b828ed27627b51bb" - integrity sha512-TGdOKE+ZkJHHeC4aaoH85m8RnFyWqdqRfAGkhd6dirmATXMZWAxOpTLmw2Ll/jPTQ3eEG7ercFr/sbzdeYCJXA== - dependencies: - classnames "^2.2.5" - enquire.js "^2.1.6" - json2mq "^0.2.0" - lodash.debounce "^4.0.8" - resize-observer-polyfill "^1.5.0" - -react-transition-group@^4.0.0, react-transition-group@^4.4.0: +react-transition-group@^4.4.5: version "4.4.5" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==

@@ -5277,11 +5464,6 @@ version "2.0.0"

resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== -resize-observer-polyfill@^1.5.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" - integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== - resolve-from@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"

@@ -5328,12 +5510,10 @@ version "1.3.0"

resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== -rifm@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/rifm/-/rifm-0.7.0.tgz#debe951a9c83549ca6b33e5919f716044c2230be" - integrity sha512-DSOJTWHD67860I5ojetXdEQRIBvF6YcpNe53j0vn1vp9EUb9N80EiZTxgP+FkDKorWC8PZw052kTF4C1GOivCQ== - dependencies: - "@babel/runtime" "^7.3.1" +rifm@^0.12.1: + version "0.12.1" + resolved "https://registry.yarnpkg.com/rifm/-/rifm-0.12.1.tgz#8fa77f45b7f1cda2a0068787ac821f0593967ac4" + integrity sha512-OGA1Bitg/dSJtI/c4dh90svzaUPt228kzFsUkJbtA2c964IqEAwWXeL9ZJi86xWv3j5SMqRvGULl7bA6cK0Bvg== rimraf@^2.6.3: version "2.7.1"

@@ -5570,6 +5750,11 @@ dependencies:

buffer-from "^1.0.0" source-map "^0.6.0" +source-map@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== + source-map@^0.6.0, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"

@@ -5603,11 +5788,6 @@ streamsearch@^1.1.0:

version "1.1.0" resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== - -string-convert@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/string-convert/-/string-convert-0.2.1.tgz#6982cc3049fbb4cd85f8b24568b9d9bf39eeff97" - integrity sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A== string-env-interpolation@1.0.1, string-env-interpolation@^1.0.1: version "1.0.1"

@@ -5697,6 +5877,11 @@ styled-jsx@5.0.6:

version "5.0.6" resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.6.tgz#fa684790a9cc3badded14badea163418fe568f77" integrity sha512-xOeROtkK5MGMDimBQ3J6iPId8q0t/BDoG5XN6oKkZClVz9ISF/hihN8OCn2LggMU6N32aXnrXBdn3auSqNS9fA== + +stylis@4.0.13: + version "4.0.13" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.13.tgz#f5db332e376d13cc84ecfe5dace9a2a51d954c91" + integrity sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag== supports-color@^5.3.0: version "5.5.0"