all repos — caroster @ 4522d225277f111c36c362fe6fc6276a7f722926

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

setup provider email, signin and setup screen
Hadrien Froger hadrien@octree.ch
Fri, 17 Jul 2020 08:54:50 +0100
commit

4522d225277f111c36c362fe6fc6276a7f722926

parent

550370ebaa750b5433b92c35d407de6f7f363911

M app/package-lock.jsonapp/package-lock.json

@@ -856,13 +856,12 @@ "@babel/helper-plugin-utils": "^7.10.4"

} }, "@babel/plugin-transform-runtime": { - "version": "7.10.5", - "resolved": "https://npm-8ee.hidora.com/@babel%2fplugin-transform-runtime/-/plugin-transform-runtime-7.10.5.tgz", - "integrity": "sha512-tV4V/FjElJ9lQtyjr5xD2IFFbgY46r7EeVu5a8CpEKT5laheHKSlFeHjpkPppW3PqzGLAuv5k2qZX5LgVZIX5w==", - "dev": true, + "version": "7.9.0", + "resolved": "https://npm-8ee.hidora.com/@babel%2fplugin-transform-runtime/-/plugin-transform-runtime-7.9.0.tgz", + "integrity": "sha512-pUu9VSf3kI1OqbWINQ7MaugnitRss1z533436waNXp+0N3ur3zfut37sXiQMxkuCF4VUjwZucen/quskCh7NHw==", "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", "resolve": "^1.8.1", "semver": "^5.5.1" },

@@ -870,8 +869,7 @@ "dependencies": {

"semver": { "version": "5.7.1", "resolved": "https://npm-8ee.hidora.com/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" } } },

@@ -2863,17 +2861,6 @@ "requires": {

"@babel/helper-plugin-utils": "^7.8.3" } }, - "@babel/plugin-transform-runtime": { - "version": "7.9.0", - "resolved": "https://npm-8ee.hidora.com/@babel%2fplugin-transform-runtime/-/plugin-transform-runtime-7.9.0.tgz", - "integrity": "sha512-pUu9VSf3kI1OqbWINQ7MaugnitRss1z533436waNXp+0N3ur3zfut37sXiQMxkuCF4VUjwZucen/quskCh7NHw==", - "requires": { - "@babel/helper-module-imports": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3", - "resolve": "^1.8.1", - "semver": "^5.5.1" - } - }, "@babel/preset-env": { "version": "7.9.0", "resolved": "https://npm-8ee.hidora.com/@babel%2fpreset-env/-/preset-env-7.9.0.tgz",

@@ -3468,9 +3455,9 @@ "lodash.uniq": "^4.5.0"

} }, "caniuse-lite": { - "version": "1.0.30001100", - "resolved": "https://npm-8ee.hidora.com/caniuse-lite/-/caniuse-lite-1.0.30001100.tgz", - "integrity": "sha512-0eYdp1+wFCnMlCj2oudciuQn2B9xAFq3WpgpcBIZTxk/1HNA/O2YA7rpeYhnOqsqAJq1AHUgx6i1jtafg7m2zA==" + "version": "1.0.30001102", + "resolved": "https://npm-8ee.hidora.com/caniuse-lite/-/caniuse-lite-1.0.30001102.tgz", + "integrity": "sha512-fOjqRmHjRXv1H1YD6QVLb96iKqnu17TjcLSaX64TwhGYed0P1E1CCWZ9OujbbK4Z/7zax7zAzvQidzdtjx8RcA==" }, "capture-exit": { "version": "2.0.0",

@@ -3506,9 +3493,9 @@ "resolved": "https://npm-8ee.hidora.com/chardet/-/chardet-0.7.0.tgz",

"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" }, "chokidar": { - "version": "3.4.0", - "resolved": "https://npm-8ee.hidora.com/chokidar/-/chokidar-3.4.0.tgz", - "integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==", + "version": "3.4.1", + "resolved": "https://npm-8ee.hidora.com/chokidar/-/chokidar-3.4.1.tgz", + "integrity": "sha512-TQTJyr2stihpC4Sya9hs2Xh+O2wf+igjL36Y75xx2WdHuiICcn/XJza46Jwt0eT5hVpQOzo3FpY3cj3RVYLX0g==", "requires": { "anymatch": "~3.1.1", "braces": "~3.0.2",

@@ -4798,9 +4785,9 @@ "resolved": "https://npm-8ee.hidora.com/ee-first/-/ee-first-1.1.1.tgz",

"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "electron-to-chromium": { - "version": "1.3.498", - "resolved": "https://npm-8ee.hidora.com/electron-to-chromium/-/electron-to-chromium-1.3.498.tgz", - "integrity": "sha512-W1hGwaQEU8j9su2jeAr3aabkPuuXw+j8t73eajGAkEJWbfWiwbxBwQN/8Qmv2qCy3uCDm2rOAaZneYQM8VGC4w==" + "version": "1.3.499", + "resolved": "https://npm-8ee.hidora.com/electron-to-chromium/-/electron-to-chromium-1.3.499.tgz", + "integrity": "sha512-y7FwtQm/8xuLMnYQfBQDYzCpNn+VkSnf4c3Km5TWMNXg7JA5RQBuxmcLaKdDVcIK0K5xGIa7TlxpRt4BdNxNoA==" }, "elliptic": { "version": "6.5.3",

@@ -6739,9 +6726,9 @@ "resolved": "https://npm-8ee.hidora.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz",

"integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==" }, "i18next": { - "version": "19.6.1", - "resolved": "https://npm-8ee.hidora.com/i18next/-/i18next-19.6.1.tgz", - "integrity": "sha512-/pp1yfIkHcg3ZtbA7/lL80VQhC+VdpRs39RcztTosdVXpGqN7cQZfg04htfrDpHYGnUx5iTNuo9xXwZuO7FkbA==", + "version": "19.6.2", + "resolved": "https://npm-8ee.hidora.com/i18next/-/i18next-19.6.2.tgz", + "integrity": "sha512-Zyd/Z32FY+sD+Eg6sLj5DeDSlrIN3WZ4onuOBRGcjDx/rvodsyUZ9TJ2Y+3aD9Vu8MPbiMU2WesIER/rs1ioyw==", "requires": { "@babel/runtime": "^7.10.1" }

@@ -11113,18 +11100,6 @@ "lodash.debounce": "^4.0.8",

"resize-observer-polyfill": "^1.5.0" } }, - "react-test-renderer": { - "version": "16.13.1", - "resolved": "https://npm-8ee.hidora.com/react-test-renderer/-/react-test-renderer-16.13.1.tgz", - "integrity": "sha512-Sn2VRyOK2YJJldOqoh8Tn/lWQ+ZiKhyZTPtaO0Q6yNj+QDbmRkVFap6pZPy3YQk8DScRDfyqm/KxKYP9gCMRiQ==", - "dev": true, - "requires": { - "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "react-is": "^16.8.6", - "scheduler": "^0.19.1" - } - }, "react-transition-group": { "version": "4.4.1", "resolved": "https://npm-8ee.hidora.com/react-transition-group/-/react-transition-group-4.4.1.tgz",

@@ -12328,12 +12303,11 @@ "resolved": "https://npm-8ee.hidora.com/stealthy-require/-/stealthy-require-1.1.1.tgz",

"integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" }, "strapi-react-context": { - "version": "0.2.4", - "resolved": "https://npm-8ee.hidora.com/strapi-react-context/-/strapi-react-context-0.2.4.tgz", - "integrity": "sha512-r4WWqP0paCI4pmhJQpmdOWigworIaqt+1VSoN53/llCq9w7azPEE3fhrIlFj4gbqyVXXGyViqNodX+3JLlJ+XA==", + "version": "0.2.6", + "resolved": "https://npm-8ee.hidora.com/strapi-react-context/-/strapi-react-context-0.2.6.tgz", + "integrity": "sha512-l8iQzVVSfZW8Gxj0ORGv6PskUDeOT3Cb46CCCkLcE7oXRihNCjWuVJAkELQlkZpcvIx2aYGA1cOv0KDAu4e2fQ==", "requires": { - "@testing-library/react-hooks": "^3.3.0", - "react": "^16.13.1" + "@testing-library/react-hooks": "^3.3.0" } }, "stream-browserify": {
M app/package.jsonapp/package.json

@@ -4,7 +4,6 @@ "version": "0.1.0",

"private": true, "proxy": "http://localhost:1337", "dependencies": { - "@babel/runtime": "^7.10.5", "@date-io/moment": "^1.3.13", "@material-ui/core": "^4.10.2", "@material-ui/pickers": "^3.2.10",

@@ -12,7 +11,7 @@ "@testing-library/jest-dom": "^4.2.4",

"@testing-library/react": "^9.5.0", "@testing-library/user-event": "^7.2.1", "fontsource-roboto": "^2.1.4", - "i18next": "^19.6.1", + "i18next": "^19.5.1", "leaflet": "^1.6.0", "marked": "^1.1.0", "moment": "^2.27.0",

@@ -24,7 +23,7 @@ "react-leaflet": "^2.7.0",

"react-router-dom": "^5.2.0", "react-scripts": "3.4.1", "react-slick": "^0.26.1", - "strapi-react-context": "^0.2.4" + "strapi-react-context": "^0.2.6" }, "scripts": { "start": "react-scripts start",

@@ -49,8 +48,6 @@ "last 1 safari version"

] }, "devDependencies": { - "@babel/plugin-transform-runtime": "^7.10.5", - "eslint-config-google": "^0.14.0", - "react-test-renderer": "^16.13.1" + "eslint-config-google": "^0.14.0" } }
M app/src/App.jsapp/src/App.js

@@ -12,6 +12,7 @@ const models = [

{name: 'events'}, {name: 'cars'}, {name: 'pages'}, + {name: 'users'}, {name: 'settings', singleType: true, init: true}, ];
M app/src/Router.jsapp/src/Router.js

@@ -8,6 +8,10 @@ import Event from './pages/Event';

import NotFound from './pages/NotFound'; import Dashboard from './pages/Dashboard'; import SignUp from './pages/SignUp'; +import SignUpSuccess from './pages/SignUpSuccess'; +import NotConfirmed from './pages/SignUpSuccess'; +import SignIn from './pages/SignIn'; + const Router = () => { useGTM(); return (

@@ -15,8 +19,11 @@ <BrowserRouter>

<Switch> <Route path="/e/:eventId" component={Event} /> <Route path="/" exact component={Home} /> + <Route path="/register/success" exact component={SignUpSuccess} /> <Route path="/register" exact component={SignUp} /> + <Route path="/login" exact component={SignIn} /> <Route path="/dashboard" exact component={Dashboard} /> + <Route path="/confirm" exact component={NotConfirmed} /> <Route component={NotFound} /> </Switch> </BrowserRouter>
A app/src/containers/AddToMyEventDialog/AddToMyEventDialog.js

@@ -0,0 +1,46 @@

+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 DialogTitle from '@material-ui/core/DialogTitle'; +import Slide from '@material-ui/core/Slide'; +import Button from '@material-ui/core/Button'; +import {useTranslation} from 'react-i18next'; + +const Transition = React.forwardRef(function Transition(props, ref) { + return <Slide direction="up" ref={ref} {...props} />; +}); + +const AddToMyEventDialog = ({event, open, onClose}) => { + const {t} = useTranslation(); + return ( + <Dialog open={open} TransitionComponent={Transition} onClose={onClose}> + <DialogContent> + <DialogTitle> + {t('event.add_to_my_events.title', {eventName: event.name})} + </DialogTitle> + <DialogContentText + dangerouslySetInnerHTML={{ + __html: t('event.add_to_my_events.text_html', { + eventName: event.name, + }), + }} + /> + </DialogContent> + <DialogActions> + <Button onClick={onClose} id="AddToMyEventCancel"> + {t('event.add_to_my_events.cancel')} + </Button> + <Button id="AddToMyEventLogin" href="/login"> + {t('event.add_to_my_events.login')} + </Button> + <Button id="AddToMyEventRegister" href="/register" color="primary"> + {t('event.add_to_my_events.register')} + </Button> + </DialogActions> + </Dialog> + ); +}; + +export default AddToMyEventDialog;
A app/src/containers/AddToMyEventDialog/index.js

@@ -0,0 +1,2 @@

+import AddToMyEventDialog from './AddToMyEventDialog'; +export default AddToMyEventDialog;
A app/src/containers/SignIn/SignIn.js

@@ -0,0 +1,100 @@

+import React, {useCallback, useState, useMemo} from 'react'; +import {Redirect} from 'react-router-dom'; +import {useTranslation} from 'react-i18next'; +import {useAuth} from 'strapi-react-context'; +import TextField from '@material-ui/core/TextField'; +import Button from '@material-ui/core/Button'; +import CardContent from '@material-ui/core/CardContent'; +import {CircularProgress} from '@material-ui/core'; +import CardActions from '@material-ui/core/CardActions'; +import {useToast} from '../../contexts/Toast'; + +export default () => { + const {t} = useTranslation(); + const {login, token, authState} = useAuth(); + const [isLoading, setIsLoading] = useState(false); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const {addToast} = useToast(); + + const canSubmit = useMemo( + () => [email, password].filter(s => s.length < 4).length === 0, + [email, password] + ); + + const onSubmit = useCallback( + async evt => { + if (evt.preventDefault) evt.preventDefault(); + if (isLoading) { + return; + } + setIsLoading(true); + try { + const error = await login(email, password); + if (error) { + addToast(t('signin.errors')); + } + + // TODO add to my event if saved in local storage + // TODO remove from local storage. + } catch (error) { + console.log('ERROR', {error}); + } + + setIsLoading(false); + return false; + }, + [email, password, login, isLoading, addToast, t] + ); + + if (token) { + return <Redirect to="/dashboard" />; + } + if (authState && authState.user && !authState.user.confirmed) { + return <Redirect to="/confirm" />; + } + return ( + <form onSubmit={onSubmit}> + <CardContent> + <TextField + label={t('signin.email')} + fullWidth + required={true} + margin="dense" + value={email} + onChange={({target: {value = ''}}) => setEmail(value)} + id="SignInEmail" + name="email" + type="email" + /> + <TextField + label={t('signin.password')} + fullWidth + required={true} + margin="dense" + value={password} + onChange={({target: {value = ''}}) => setPassword(value)} + id="SignInEmail" + name="password" + type="password" + /> + </CardContent> + <CardActions> + <Button + color="primary" + variant="contained" + type="submit" + disabled={!canSubmit} + aria-disabled={!canSubmit} + id="SignInSubmit" + > + {t('signin.login')} + {isLoading && <CircularProgress size={20} />} + </Button> + <Button id="SignInRegister" href="/register"> + {t('signin.register')} + </Button> + </CardActions> + </form> + ); +};
A app/src/containers/SignIn/SignIn.test.js

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

+import React from 'react'; +import renderer from 'react-test-renderer'; +import SignIn from './SignIn'; +describe('SignIn', () => { + const signIn = renderer.create(<SignIn />); + it('match snapshot without props', () => { + expect(signIn.toJSON()).toMatchSnapshot(); + }); + + it('can not submit the form until email and password is set', () => {}); +});
A app/src/containers/SignIn/index.js

@@ -0,0 +1,3 @@

+import SignIn from './SignIn'; + +export default SignIn;
M app/src/containers/SignUp/SignUp.jsapp/src/containers/SignUp/SignUp.js

@@ -4,7 +4,6 @@ import {useAuth} from 'strapi-react-context';

import TextField from '@material-ui/core/TextField'; import Button from '@material-ui/core/Button'; import CardContent from '@material-ui/core/CardContent'; -import CardActionArea from '@material-ui/core/CardActions'; import CardActions from '@material-ui/core/CardActions'; import {useToast} from '../../contexts/Toast'; import {Redirect} from 'react-router-dom';

@@ -12,7 +11,7 @@ import {CircularProgress} from '@material-ui/core';

export default () => { const {t} = useTranslation(); - const {signUp, token} = useAuth(); + const {signUp, authState = {}} = useAuth(); const [isLoading, setIsLoading] = useState(false); const [firstName, setFirstName] = useState(''); const [lastName, setLastName] = useState('');

@@ -30,42 +29,38 @@

const onSubmit = useCallback( async evt => { if (evt.preventDefault) evt.preventDefault(); + if (isLoading) return; setIsLoading(true); try { - const error = await signUp(email.replace(/\.@/, '_'), email, password, { + await signUp(email.replace(/\.@/, '_'), email, password, { firstName, lastName, }); - if (error) { + } catch (error) { + if (error.kind && error.kind === 'bad_data') addToast(t('signup.errors.email_taken')); + else if (error.kind) { + addToast(t(`generic.errors.${error.kind}`)); + } else { + addToast(t(`generic.errors.unknown`)); } - } catch (error) { - console.log('ERROR', {error}); - // if (error.statusCode && error.statusCode === 400) { - // const [message] = error.message.messages; - // console.log('add toast', message); - // addToast(message.message); - // } } console.log('SIGN UP'); setIsLoading(false); return false; }, - [firstName, lastName, email, password, addToast, signUp] + [firstName, lastName, email, password, addToast, signUp, t, isLoading] ); - if (isLoading) { - return ( - <CardContent> - <CircularProgress /> - </CardContent> + + if (authState.user) { + return authState.user.confirmed ? ( + <Redirect to="/dashboard" /> + ) : ( + <Redirect to="/register/success" /> ); } - if (token) { - return <Redirect to="/dashboard" />; - } - return ( <form onSubmit={onSubmit}> <CardContent>

@@ -113,24 +108,22 @@ name="password"

type="password" /> </CardContent> - <CardActionArea> - <CardActions> - <Button - color="primary" - variant="contained" - type="submit" - disabled={!canSubmit} - aria-disabled={!canSubmit} - id="SignUpSubmit" - s - > - {t('signup.submit')} - </Button> - <Button id="SignUpLogin" href="/login"> - {t('signup.login')} - </Button> - </CardActions> - </CardActionArea> + <CardActions> + <Button + color="primary" + variant="contained" + type="submit" + disabled={!canSubmit} + aria-disabled={!canSubmit} + id="SignUpSubmit" + > + {t('signup.submit')} + {isLoading && <CircularProgress />} + </Button> + <Button id="SignUpLogin" href="/login"> + {t('signup.login')} + </Button> + </CardActions> </form> ); };
M app/src/locales/fr.jsonapp/src/locales/fr.json

@@ -9,7 +9,17 @@ "create": "Créer",

"cancel": "Annuler", "remove": "Supprimer", "save": "Enregistrer", - "confirm": "Confirmer" + "confirm": "Confirmer", + "errors":{ + "unknown": "Une erreur inconnue c'est produite", + "rejected": "Quelque chose c'est mal passé", + "bad_data": "Il manque quelque chose", + "unauthorized": "Problème d'authentification", + "forbidden": "Vous n'avez pas le droit de faire cette action", + "not_found": "Resource introuvable", + "server": "Problème sur nos serveurs", + "timeout": "Connexion instable, re-essayé plus tard" + } }, "event": { "fields": {

@@ -32,11 +42,21 @@ "hide_details": "Cacher les détails",

"add_car": "Ajouter une voiture", "invite": "Inviter", "find_car": "Trouver une voiture", - "copied": "Le lien a été copié dans votre presse-papier" + "copied": "Le lien a été copié dans votre presse-papier", + "add_to_my_events": "Ajouter à mes évènements", + "my_events": "Mes évènements", + "logout": "Déconnecter" }, "errors": { "cant_create": "Impossible de créer l'événement", "cant_update": "Impossible de modifier l'événement" + }, + "add_to_my_events": { + "cancel": "Annuler", + "login": "Se connecter", + "register" : "S'inscrire", + "title": "Ajouter {{eventName}} à sa liste d'évènement", + "text_html": "Pour ajouter <strong>{{eventName}}</strong>, il est nécessaire de se connecter ou de créer un compte." } }, "car": {

@@ -99,7 +119,18 @@ "submit": "Créer son compte",

"login": "Déjà un compte ? Se connecter", "errors":{ "email_taken": "Email déjà pris" + }, + "success": { + "title": "Bienvenue !", + "text": "Nous avons besoin de confirmer votre email avant de pouvoir vous connecter", + "login": "Se connecter" } - + }, + "signin": { + "email": "Email", + "password": "Mot de passe", + "login": "Se connecter", + "register": "Pas encore de compte ? S'inscrire", + "errors": "Vérifier votre email et mot de passe" } }
M app/src/pages/Event.jsapp/src/pages/Event.js

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

-import React, {useState, useReducer, useEffect} from 'react'; +import React, {useState, useReducer, useEffect, useCallback} from 'react'; import {Helmet} from 'react-helmet'; import {useTranslation} from 'react-i18next'; +import {useAuth} from 'strapi-react-context'; import AppBar from '@material-ui/core/AppBar'; import Toolbar from '@material-ui/core/Toolbar'; import Container from '@material-ui/core/Container';

@@ -17,16 +18,18 @@ import EventDetails from '../containers/EventDetails';

import EventFab from '../containers/EventFab'; import CarColumns from '../containers/CarColumns'; import NewCarDialog from '../containers/NewCarDialog'; +import AddToMyEventDialog from '../containers/AddToMyEventDialog'; const Event = () => { const {t} = useTranslation(); const {addToast} = useToast(); const [anchorEl, setAnchorEl] = useState(null); + const [isAddToMyEvent, setIsAddToMyEvent] = useState(false); const [detailsOpen, toggleDetails] = useReducer(i => !i, false); const classes = useStyles({detailsOpen}); const [openNewCar, toggleNewCar] = useReducer(i => !i, false); const {event, isEditing, setIsEditing, updateEvent} = useEvent(); - + const {token, logout} = useAuth(); useEffect(() => { window.scrollTo(0, 0); }, []);

@@ -61,6 +64,16 @@ return true;

} }; + const addToMyEvents = useCallback(async () => { + if (!event) return; + localStorage.setItem('addToMyEvents', event.id); + setIsAddToMyEvent(true); + }, [event]); + + const goToDashboard = useCallback( + () => (window.location.href = '/dashboard'), + [] + ); if (!event) return <Loading />; return (

@@ -145,6 +158,16 @@ : t('event.actions.show_details'),

onClick: toggleDetails, id: 'DetailsTab', }, + !token && { + label: t('event.actions.add_to_my_events'), + onClick: addToMyEvents, + id: 'AddToMyEventsTab', + }, + !!token && { + label: t('event.actions.my_events'), + onClick: goToDashboard, + id: 'GoToDashboardTab', + }, { label: t('event.actions.add_car'), onClick: toggleNewCar,

@@ -155,7 +178,12 @@ label: t('event.actions.invite'),

onClick: () => {}, id: 'InviteTab', }, - ]} + !!token && { + label: t('event.actions.logout'), + onClick: logout, + id: 'LogoutTab', + }, + ].filter(Boolean)} /> </Toolbar> <Container className={classes.container} maxWidth="sm">

@@ -165,6 +193,11 @@ </AppBar>

<CarColumns toggleNewCar={toggleNewCar} /> <EventFab toggleNewCar={toggleNewCar} open={openNewCar} /> <NewCarDialog open={openNewCar} toggle={toggleNewCar} /> + <AddToMyEventDialog + open={isAddToMyEvent} + onClose={() => setIsAddToMyEvent(false)} + event={event} + /> </Layout> ); };
A app/src/pages/NotConfirmed.js

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

+import React from 'react'; +import Layout from '../layouts/Centered'; +import Card from '@material-ui/core/Card'; +import CardMedia from '@material-ui/core/CardMedia'; +import Logo from '../components/Logo'; +import {Redirect} from 'react-router-dom'; +import {useTranslation} from 'react-i18next'; +import {useAuth} from 'strapi-react-context'; +import Button from '@material-ui/core/Button'; +import CardContent from '@material-ui/core/CardContent'; +import CardActionArea from '@material-ui/core/CardActions'; +import CardActions from '@material-ui/core/CardActions'; +import Typography from '@material-ui/core/Typography'; + +export default () => { + const {t} = useTranslation(); + const {token} = useAuth(); + if (token) { + return <Redirect to="/dashboard" />; + } + return ( + <Layout> + <Card> + <CardMedia component={Logo} /> + <CardContent> + <Typography gutterBottom variant="h5" component="h2"> + {t('signup.notConfirmed.title')} + </Typography> + <Typography variant="body2" color="textSecondary" component="p"> + {t('signup.notConfirmed.text')} + </Typography> + </CardContent> + <CardActionArea> + <CardActions> + <Button + color="primary" + variant="contained" + href={'/login'} + id="SignUpSuccessLogin" + > + {t('signup.notConfirmed.login')} + </Button> + </CardActions> + </CardActionArea> + </Card> + </Layout> + ); +};
A app/src/pages/SignIn.js

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

+import React from 'react'; +import Layout from '../layouts/Centered'; +import Card from '@material-ui/core/Card'; +import CardMedia from '@material-ui/core/CardMedia'; +import Logo from '../components/Logo'; +import SignIn from '../containers/SignIn'; + +export default () => { + return ( + <Layout> + <Card> + <CardMedia component={Logo} /> + <SignIn /> + </Card> + </Layout> + ); +};
A app/src/pages/SignIn.test.js

@@ -0,0 +1,13 @@

+import React from 'react'; +import renderer from 'react-test-renderer'; +jest.mock('../containers/SignIn/index', () => ({ + __esModule: true, + default: () => <span>/containers/SignIn component</span>, +})); +import SignIn from './SignIn'; +describe('SignIn page', () => { + const SignInPage = renderer.create(<SignIn />); + it('match snapshot', () => { + expect(SignInPage.toJSON()).toMatchSnapshot(); + }); +});
A app/src/pages/SignUpSuccess.js

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

+import React from 'react'; +import Layout from '../layouts/Centered'; +import Card from '@material-ui/core/Card'; +import CardMedia from '@material-ui/core/CardMedia'; +import Logo from '../components/Logo'; +import {Redirect} from 'react-router-dom'; +import {useTranslation} from 'react-i18next'; +import {useAuth} from 'strapi-react-context'; +import Button from '@material-ui/core/Button'; +import CardContent from '@material-ui/core/CardContent'; +import CardActions from '@material-ui/core/CardActions'; +import Typography from '@material-ui/core/Typography'; + +export default () => { + const {t} = useTranslation(); + const {token} = useAuth(); + if (token) { + return <Redirect to="/dashboard" />; + } + return ( + <Layout> + <Card> + <CardMedia component={Logo} /> + <CardContent> + <Typography gutterBottom variant="h5" component="h2"> + {t('signup.success.title')} + </Typography> + <Typography variant="body2" color="textSecondary" component="p"> + {t('signup.success.text')} + </Typography> + </CardContent> + <CardActions> + <Button + color="primary" + variant="contained" + href={'/login'} + id="SignUpSuccessLogin" + > + {t('signup.success.login')} + </Button> + </CardActions> + </Card> + </Layout> + ); +};
A app/src/pages/__snapshots__/SignIn.test.js.snap

@@ -0,0 +1,35 @@

+// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SignIn page match snapshot 1`] = ` +<div> + <div + className="makeStyles-layout-1" + > + <div + className="MuiContainer-root MuiContainer-maxWidthSm" + > + <div + className="MuiPaper-root MuiCard-root MuiPaper-elevation1 MuiPaper-rounded" + > + <div + className="makeStyles-layout-2" + > + <a + className="makeStyles-link-3" + href="https://caroster.io" + > + <img + alt="Caroster" + className="makeStyles-logo-4" + src="logo.png" + /> + </a> + </div> + <span> + /containers/SignIn component + </span> + </div> + </div> + </div> +</div> +`;
A config/plugins.js

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

+module.exports = ({env}) => ({ + email: { + provider: 'sendgrid-template', + providerOptions: { + templateId: 'd-cbd6d791d40e4490ac373e1cf2c782f6', + }, + settings: { + defaultFrom: 'hello@caroster.io', + defaultReplyTo: 'hello@caroster.io', + }, + }, +});
M config/server.jsconfig/server.js

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

-module.exports = ({ env }) => ({ +module.exports = ({env}) => ({ host: env('HOST', '0.0.0.0'), port: env.int('PORT', 1337), + url: env('STRAPI_URL', ''), });