all repos — caroster @ 71f58f6653388edb7455a935a96e099959eb5edd

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

✨ Save events on login & signup #122
Tim Izzo tim@octree.ch
Mon, 21 Jun 2021 10:59:22 +0200
commit

71f58f6653388edb7455a935a96e099959eb5edd

parent

88d8a2b5aa242ff2964f361e868e820c23153d86

M backend/extensions/users-permissions/models/User.jsbackend/extensions/users-permissions/models/User.js

@@ -3,7 +3,7 @@ lifecycles: {

async afterCreate(user) { if (user.email) { try { - strapi.plugins['email'].services.contact.subscribe({ + await strapi.plugins['email'].services.contact.subscribe({ email: user.email, }); strapi.log.info(
M frontend/containers/SignInForm/index.jsfrontend/containers/SignInForm/index.js

@@ -13,7 +13,6 @@ import CardActions from '@material-ui/core/CardActions';

import {makeStyles} from '@material-ui/core/styles'; import useLoginForm from '../../hooks/useLoginForm'; import useToastsStore from '../../stores/useToastStore'; -import useAuthStore from '../../stores/useAuthStore'; import useLoginWithProvider from '../../hooks/useLoginWithProvider'; import useAddToEvents from '../../hooks/useAddToEvents';

@@ -21,7 +20,6 @@ const SignIn = () => {

const {t} = useTranslation(); const classes = useStyles(); const router = useRouter(); - const token = useAuthStore(s => s.token); const {loginWithProvider} = useLoginWithProvider(); const [error, setError] = useState(''); const [email, setEmail] = useState('');

@@ -30,10 +28,6 @@ const addToast = useToastsStore(s => s.addToast);

const {login, loading} = useLoginForm(email, password); const {saveStoredEvents} = useAddToEvents(); - useEffect(() => { - if (token) router.replace('/dashboard'); - }, [token]); - const canSubmit = useMemo( () => [email, password].filter(s => s.length < 4).length === 0, [email, password]

@@ -52,7 +46,6 @@

return false; }; - // If an access token is given in URL params, login with auth provider useEffect(() => { const authWithGoogle = async search => { try {

@@ -108,7 +101,6 @@ id="SignInEmail"

name="password" type="password" error={!!error} - gutterBottom /> <RouterLink href="/auth/lost-password"> <Link>
D frontend/containers/SignUpForm/__snapshots__/SignUp.test.js.snap

@@ -1,168 +0,0 @@

-// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`SignUp match snapshot without props 1`] = ` -<form> - <div - className="MuiFormControl-root MuiTextField-root MuiFormControl-marginDense MuiFormControl-fullWidth" - > - <label - className="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-marginDense" - data-shrink={false} - htmlFor="SignUpFirstName" - id="SignUpFirstName-label" - > - Prénom - </label> - <div - className="MuiInputBase-root MuiInput-root MuiInput-underline MuiInputBase-fullWidth MuiInput-fullWidth MuiInputBase-formControl MuiInput-formControl MuiInputBase-marginDense MuiInput-marginDense" - onClick={[Function]} - > - <input - aria-invalid={false} - autoFocus={true} - className="MuiInputBase-input MuiInput-input MuiInputBase-inputMarginDense MuiInput-inputMarginDense" - disabled={false} - id="SignUpFirstName" - name="firstName" - onAnimationStart={[Function]} - onBlur={[Function]} - onChange={[Function]} - onFocus={[Function]} - required={false} - type="text" - value="" - /> - </div> - </div> - <div - className="MuiFormControl-root MuiTextField-root MuiFormControl-marginDense MuiFormControl-fullWidth" - > - <label - className="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-marginDense" - data-shrink={false} - htmlFor="SignUpLastName" - id="SignUpLastName-label" - > - Nom - </label> - <div - className="MuiInputBase-root MuiInput-root MuiInput-underline MuiInputBase-fullWidth MuiInput-fullWidth MuiInputBase-formControl MuiInput-formControl MuiInputBase-marginDense MuiInput-marginDense" - onClick={[Function]} - > - <input - aria-invalid={false} - autoFocus={true} - className="MuiInputBase-input MuiInput-input MuiInputBase-inputMarginDense MuiInput-inputMarginDense" - disabled={false} - id="SignUpLastName" - name="lastName" - onAnimationStart={[Function]} - onBlur={[Function]} - onChange={[Function]} - onFocus={[Function]} - required={false} - type="text" - value="" - /> - </div> - </div> - <div - className="MuiFormControl-root MuiTextField-root MuiFormControl-marginDense MuiFormControl-fullWidth" - > - <label - className="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-marginDense" - data-shrink={false} - htmlFor="SignUpEmail" - id="SignUpEmail-label" - > - Email - </label> - <div - className="MuiInputBase-root MuiInput-root MuiInput-underline MuiInputBase-fullWidth MuiInput-fullWidth MuiInputBase-formControl MuiInput-formControl MuiInputBase-marginDense MuiInput-marginDense" - onClick={[Function]} - > - <input - aria-invalid={false} - autoFocus={true} - className="MuiInputBase-input MuiInput-input MuiInputBase-inputMarginDense MuiInput-inputMarginDense" - disabled={false} - id="SignUpEmail" - name="email" - onAnimationStart={[Function]} - onBlur={[Function]} - onChange={[Function]} - onFocus={[Function]} - required={false} - type="email" - value="" - /> - </div> - </div> - <div - className="MuiFormControl-root MuiTextField-root MuiFormControl-marginDense MuiFormControl-fullWidth" - > - <label - className="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-marginDense" - data-shrink={false} - htmlFor="SignUpEmail" - id="SignUpEmail-label" - > - Mot de passe - </label> - <div - className="MuiInputBase-root MuiInput-root MuiInput-underline MuiInputBase-fullWidth MuiInput-fullWidth MuiInputBase-formControl MuiInput-formControl MuiInputBase-marginDense MuiInput-marginDense" - onClick={[Function]} - > - <input - aria-invalid={false} - autoFocus={true} - className="MuiInputBase-input MuiInput-input MuiInputBase-inputMarginDense MuiInput-inputMarginDense" - disabled={false} - id="SignUpEmail" - name="password" - onAnimationStart={[Function]} - onBlur={[Function]} - onChange={[Function]} - onFocus={[Function]} - required={false} - type="password" - value="" - /> - </div> - </div> - <button - aria-disabled={true} - className="MuiButtonBase-root MuiButton-root MuiButton-contained MuiButton-containedPrimary Mui-disabled Mui-disabled" - disabled={true} - id="SignUpSubmit" - onBlur={[Function]} - onDragLeave={[Function]} - onFocus={[Function]} - onKeyDown={[Function]} - onKeyUp={[Function]} - onMouseDown={[Function]} - onMouseLeave={[Function]} - onMouseUp={[Function]} - onTouchEnd={[Function]} - onTouchMove={[Function]} - onTouchStart={[Function]} - tabIndex={-1} - type="submit" - > - <span - className="MuiButton-label" - > - Créer son compte - </span> - </button> - <a - className="MuiTypography-root MuiLink-root MuiLink-underlineHover MuiTypography-colorPrimary" - href="/login" - id="SignUpLogin" - onBlur={[Function]} - onFocus={[Function]} - > - Déjà un compte ? Se connecter - </a> -</form> -`;
M frontend/containers/SignUpForm/index.jsfrontend/containers/SignUpForm/index.js

@@ -10,16 +10,12 @@ import CircularProgress from '@material-ui/core/CircularProgress';

import {makeStyles} from '@material-ui/core/styles'; import useToastsStore from '../../stores/useToastStore'; import {useRegisterMutation} from '../../generated/graphql'; -import useAuthStore from '../../stores/useAuthStore'; -import useAddToEvents from '../../hooks/useAddToEvents'; const SignUp = () => { const {t} = useTranslation(); const classes = useStyles(); const addToast = useToastsStore(s => s.addToast); const router = useRouter(); - const setToken = useAuthStore(s => s.setToken); - const setUser = useAuthStore(s => s.setUser); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(''); const [firstName, setFirstName] = useState('');

@@ -27,7 +23,6 @@ const [lastName, setLastName] = useState('');

const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [register] = useRegisterMutation(); - const {saveStoredEvents} = useAddToEvents(); const canSubmit = useMemo( () =>

@@ -41,17 +36,14 @@ e.preventDefault?.();

if (isLoading) return; setIsLoading(true); try { - const {data} = await register({ + await register({ variables: { email, password, username: `${firstName} ${lastName}`, }, }); - setToken(data.register.jwt); - setUser(data.register.user); - saveStoredEvents(); - router.push('/dashboard'); + router.push('/auth/confirm'); } catch (error) { const strapiError = getStrapiError(error); console.error({strapiError});
M frontend/hooks/useAddToEvents.tsfrontend/hooks/useAddToEvents.ts

@@ -2,6 +2,7 @@ import {useCallback} from 'react';

import {useUpdateMeMutation} from '../generated/graphql'; import useAuthStore from '../stores/useAuthStore'; import create from 'zustand'; +import {persist} from 'zustand/middleware'; type Store = { eventsToBeAdded: string[];

@@ -9,12 +10,19 @@ addEvent: (eventId: string) => void;

clear: () => void; }; -const store = create<Store>((set, get) => ({ - eventsToBeAdded: [], - addEvent: eventId => - set({eventsToBeAdded: [...get().eventsToBeAdded, eventId]}), - clear: () => set({eventsToBeAdded: []}), -})); +const store = create<Store>( + persist( + (set, get) => ({ + eventsToBeAdded: [], + addEvent: eventId => + set({eventsToBeAdded: [...get().eventsToBeAdded, eventId]}), + clear: () => set({eventsToBeAdded: []}), + }), + { + name: 'add-events', + } + ) +); const useAddToEvents = () => { const [updateProfile] = useUpdateMeMutation();

@@ -24,7 +32,6 @@ const addEvent = store(s => s.addEvent);

const clearStore = store(s => s.clear); const saveStoredEvents = useCallback(() => { - console.log('SAVE STORED EVENTS', {eventsToBeAdded}); if (eventsToBeAdded.length > 0) { updateProfile({ variables: {
M frontend/locales/fr.jsonfrontend/locales/fr.json

@@ -29,6 +29,7 @@ "timeout": "Connexion instable, re-essayé plus tard"

} }, "event": { + "title": "{{title}} - Caroster", "fields": { "starts_on": "Commence le", "address": "Adresse",

@@ -187,6 +188,11 @@ "text_html": "Lorem Ipsum dolor sit amet, consectetur <strong>adipiscing elit</strong>",

"dashboard": "Aller sur son tableau de bord", "create_event": "Créer un évènement" } + }, + "confirm": { + "title": "Confirmez votre compte", + "text": "Vous avez reçu un email avec un lien. Merci de cliquer sur ce lien pour confirmer votre compte.", + "login": "Retour à l'écran de connexion" }, "signin": { "title": "Connexion",
M frontend/pages/_app.tsxfrontend/pages/_app.tsx

@@ -10,7 +10,7 @@ import Toasts from '../components/Toasts';

import 'moment/locale/fr-ch'; import '../i18n'; -export default function App(props: AppProps) { +const App = function (props: AppProps) { const {Component, pageProps} = props; const apolloClient = useApollo(pageProps);

@@ -38,4 +38,6 @@ </ThemeProvider>

</Fragment> </ApolloProvider> ); -} +}; + +export default App;
A frontend/pages/auth/confirm.tsx

@@ -0,0 +1,44 @@

+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 {useTranslation} from 'react-i18next'; +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'; + +const Confirm = () => { + const {t} = useTranslation(); + + return ( + <Layout> + <Card> + <CardMedia component={Logo} /> + <CardContent> + <Typography gutterBottom variant="h5" component="h2"> + {t('confirm.title')} + </Typography> + <Typography variant="body2" color="textSecondary" component="p"> + {t('confirm.text')} + </Typography> + </CardContent> + <CardActionArea> + <CardActions> + <Button + color="primary" + variant="contained" + href={'/auth/login'} + id="SignUpSuccessLogin" + > + {t('confirm.login')} + </Button> + </CardActions> + </CardActionArea> + </Card> + </Layout> + ); +}; + +export default Confirm;
M frontend/pages/auth/login.tsxfrontend/pages/auth/login.tsx

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

+import {useEffect} from 'react'; import {useTranslation} from 'react-i18next'; +import {useRouter} from 'next/router'; import CardMedia from '@material-ui/core/CardMedia'; import Divider from '@material-ui/core/Divider'; import Card from '@material-ui/core/Card';

@@ -6,9 +8,17 @@ import Layout from '../../layouts/Centered';

import Logo from '../../components/Logo'; import SignInForm from '../../containers/SignInForm'; import LoginGoogle from '../../containers/LoginGoogle'; +import useAuthStore from '../../stores/useAuthStore'; const login = () => { const {t} = useTranslation(); + const router = useRouter(); + const token = useAuthStore(s => s.token); + + useEffect(() => { + if (token) router.replace('/dashboard'); + }, [token]); + return ( <Layout menuTitle={t('signin.title')} displayMenu={false}> <Card>
M frontend/pages/e/[eventId].tsxfrontend/pages/e/[eventId].tsx

@@ -70,8 +70,8 @@ if (loading) return <Loading />;

return ( <Layout - pageTitle={t('event.title')} - menuTitle={t('meta.title', {title: event.name})} + pageTitle={t('event.title', {title: event.name})} + menuTitle={t('event.title', {title: event.name})} displayMenu={false} > <EventBar