Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NextJS Example using cookieStorage [WIP] #804

Closed
wants to merge 15 commits into from
Closed
5,275 changes: 5,275 additions & 0 deletions examples/graphql-server-typeorm-postgres/package-lock.json

Large diffs are not rendered by default.

Binary file removed examples/graphql-server-typescript/.DS_Store
Binary file not shown.
27 changes: 27 additions & 0 deletions examples/next-graphql-typescript/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/
next-env.d.ts

# production
/build

# misc
.DS_Store
.env*

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
package-lock.json
18 changes: 18 additions & 0 deletions examples/next-graphql-typescript/components/FormError.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';
import { Typography, withStyles, WithStyles } from '@material-ui/core';

const styles = () => ({
formError: {
color: 'red',
},
});

interface Props {
error: string;
}

const FormError = ({ classes, error }: WithStyles<'formError'> & Props) => {
return <Typography className={classes.formError}>{error}</Typography>;
};

export default withStyles(styles)(FormError);
33 changes: 33 additions & 0 deletions examples/next-graphql-typescript/components/Layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import Head from 'next/head';
import { makeStyles } from '@material-ui/styles';
import Navigation from '../components/Navigation';
const useStyles = makeStyles({
root: {
margin: 'auto',
maxWidth: 500,
marginTop: 50,
},
container: {
padding: 16,
},
});

interface Props {
titleKey: string;
}

const Layout: React.FC<Props> = ({ titleKey, children }) => {
const classes = useStyles({});
return (
<div className={classes.root}>
<Head>
<title>Title</title>
</Head>
<Navigation />
<div className={classes.container}>{children}</div>
</div>
);
};

export default Layout;
115 changes: 115 additions & 0 deletions examples/next-graphql-typescript/components/Login.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import React from 'react';
import {
Button,
FormControl,
Input,
InputLabel,
Typography,
withStyles,
WithStyles,
} from '@material-ui/core';
import Link from 'next/link';
import FormError from './FormError';
import { accountsPassword } from '../utils/accounts';
import Router from 'next/router';
const styles = () => ({
formContainer: {
display: 'flex',
flexDirection: 'column' as 'column',
},
});

interface State {
email: string;
password: string;
code: string;
error: string | null;
}

class Login extends React.Component<WithStyles<'formContainer'>, State> {
public state = {
code: '',
email: '',
error: null,
password: '',
};

public onChangeEmail = ({ target }: React.ChangeEvent<HTMLInputElement>) => {
this.setState({ email: target.value });
};

public onChangePassword = ({ target }: React.ChangeEvent<HTMLInputElement>) => {
this.setState({ password: target.value });
};

public onChangeCode = ({ target }: React.ChangeEvent<HTMLInputElement>) => {
this.setState({ code: target.value });
};

public onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
this.setState({ error: null });
try {
await accountsPassword.login({
code: this.state.code,
password: this.state.password,
user: {
email: this.state.email,
},
});
Router.push({
pathname: '/',
// query: { name: "Zeit" }
});
} catch (err) {
this.setState({ error: err.message });
}
};

public render() {
const { classes } = this.props;
const { email, password, code, error } = this.state;
return (
<form onSubmit={this.onSubmit} className={classes.formContainer}>
<Typography variant="h3" gutterBottom={true}>
Login
</Typography>
<FormControl margin="normal">
<InputLabel htmlFor="email">Email</InputLabel>
<Input id="email" value={email} autoComplete="email" onChange={this.onChangeEmail} />
</FormControl>
<FormControl margin="normal">
<InputLabel htmlFor="password">Password</InputLabel>
<Input
id="password"
type="password"
value={password}
autoComplete="current-password"
onChange={this.onChangePassword}
/>
</FormControl>
<FormControl margin="normal">
<InputLabel htmlFor="password">2fa code if enabled</InputLabel>
<Input id="code" value={code} onChange={this.onChangeCode} />
</FormControl>
<Button variant="outlined" color="primary" type="submit">
Login
</Button>
{/* eslint-disable-next-line */}
{error && <FormError error={error!} />}
<Button>
<Link href="/signup" as={`/signup`}>
<a>Sign Up</a>
</Link>
</Button>
<Button>
<Link href="/reset-password" as={`/reset-password`}>
<a>Reset Password</a>
</Link>
</Button>
</form>
);
}
}

export default withStyles(styles)(Login);
22 changes: 22 additions & 0 deletions examples/next-graphql-typescript/components/MuiTheme.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { createMuiTheme } from '@material-ui/core/styles';
import { red } from '@material-ui/core/colors';

// Create a theme instance.
const MuiTheme = createMuiTheme({
palette: {
primary: {
main: '#556cd6',
},
secondary: {
main: '#19857b',
},
error: {
main: red.A400,
},
background: {
default: '#fff',
},
},
});

export default MuiTheme;
41 changes: 41 additions & 0 deletions examples/next-graphql-typescript/components/Navigation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react';
import Link from 'next/link';

const Navigation: React.FC = () => {
return (
<ul className="root">
<li>
<Link href="" as={`/`}>
<a>Home</a>
</Link>
</li>
<li>
<Link href="/ssr" as={`/ssr`}>
<a>ssr</a>
</Link>
</li>
<style jsx>{`
.root {
margin: 1rem 0 1rem 0;
padding: 0;
display: flex;
list-style: none;
}
.root > li:not(:last-child) {
margin-right: 1rem;
}
a:link,
a:visited {
text-decoration: none;
color: navy;
text-transform: uppercase;
}
a:hover {
text-decoration: underline;
}
`}</style>
</ul>
);
};

export default Navigation;
91 changes: 91 additions & 0 deletions examples/next-graphql-typescript/components/ResetPassword.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import React, { useState } from 'react';
import { RouteComponentProps, Link } from 'react-router-dom';
import { FormControl, InputLabel, Input, Button, Typography, Snackbar } from '@material-ui/core';
import { makeStyles } from '@material-ui/styles';

import { accountsGraphQL } from '../utils/accounts';
import FormError from '../components/FormError';

// const useStyles = makeStyles({
// formContainer: {
// display: 'flex',
// flexDirection: 'column',
// },
// });

// const LogInLink = (props: any) => <Link to="/login" {...props} />;

interface Props {
token: string;
}

const ResetPassword = Props => {
// const classes = useStyles;
const [snackbarMessage, setSnackbarMessage] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null);
const [email, setEmail] = useState('');
const [newPassword, setNewPassword] = useState('');
const token = this.props.token;

const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setError(null);
setSnackbarMessage(null);
try {
// If no tokens send email to user
if (!token) {
await accountsGraphQL.sendResetPasswordEmail(email);
setSnackbarMessage('Email sent');
} else {
// If token try to change user password
await accountsGraphQL.resetPassword(token, newPassword);
setSnackbarMessage('Your password has been reset successfully');
}
} catch (err) {
setError(err.message);
setSnackbarMessage(null);
}
};

return (
<form onSubmit={onSubmit}>
<Typography variant="h4" gutterBottom>
Reset Password
</Typography>
{!token && (
<FormControl margin="normal">
<InputLabel htmlFor="email">Email</InputLabel>
<Input id="email" value={email} onChange={e => setEmail(e.target.value)} />
</FormControl>
)}
{token && (
<FormControl margin="normal">
<InputLabel htmlFor="new-password">New Password</InputLabel>
<Input
id="new-password"
type="password"
value={newPassword}
onChange={e => setNewPassword(e.target.value)}
/>
</FormControl>
)}
<Button variant="contained" color="primary" type="submit">
Reset Password
</Button>
{error && <FormError error={error!} />}
<Button>Log In</Button>
<Snackbar
anchorOrigin={{
vertical: 'top',
horizontal: 'right',
}}
open={!!snackbarMessage}
autoHideDuration={4000}
onClose={() => setSnackbarMessage(null)}
message={<span>{snackbarMessage}</span>}
/>
</form>
);
};

export default ResetPassword;
Loading