summaryrefslogtreecommitdiffstats
path: root/stepped-solutions/28
diff options
context:
space:
mode:
Diffstat (limited to 'stepped-solutions/28')
-rwxr-xr-xstepped-solutions/28/backend/src/resolvers/Mutation.js95
-rwxr-xr-xstepped-solutions/28/backend/src/schema.graphql17
-rwxr-xr-xstepped-solutions/28/frontend/components/Nav.js36
-rwxr-xr-xstepped-solutions/28/frontend/components/Signin.js76
-rwxr-xr-xstepped-solutions/28/frontend/components/Signup.js86
-rwxr-xr-xstepped-solutions/28/frontend/components/User.js27
-rwxr-xr-xstepped-solutions/28/frontend/pages/signup.js18
7 files changed, 355 insertions, 0 deletions
diff --git a/stepped-solutions/28/backend/src/resolvers/Mutation.js b/stepped-solutions/28/backend/src/resolvers/Mutation.js
new file mode 100755
index 0000000..7bcbb9c
--- /dev/null
+++ b/stepped-solutions/28/backend/src/resolvers/Mutation.js
@@ -0,0 +1,95 @@
+const bcrypt = require('bcryptjs');
+const jwt = require('jsonwebtoken');
+
+const Mutations = {
+ async createItem(parent, args, ctx, info) {
+ // TODO: Check if they are logged in
+
+ const item = await ctx.db.mutation.createItem(
+ {
+ data: {
+ ...args,
+ },
+ },
+ info
+ );
+
+ console.log(item);
+
+ return item;
+ },
+ updateItem(parent, args, ctx, info) {
+ // first take a copy of the updates
+ const updates = { ...args };
+ // remove the ID from the updates
+ delete updates.id;
+ // run the update method
+ return ctx.db.mutation.updateItem(
+ {
+ data: updates,
+ where: {
+ id: args.id,
+ },
+ },
+ info
+ );
+ },
+ async deleteItem(parent, args, ctx, info) {
+ const where = { id: args.id };
+ // 1. find the item
+ const item = await ctx.db.query.item({ where }, `{ id title}`);
+ // 2. Check if they own that item, or have the permissions
+ // TODO
+ // 3. Delete it!
+ return ctx.db.mutation.deleteItem({ where }, info);
+ },
+ async signup(parent, args, ctx, info) {
+ // lowercase their email
+ args.email = args.email.toLowerCase();
+ // hash their password
+ const password = await bcrypt.hash(args.password, 10);
+ // create the user in the database
+ const user = await ctx.db.mutation.createUser(
+ {
+ data: {
+ ...args,
+ password,
+ permissions: { set: ['USER'] },
+ },
+ },
+ info
+ );
+ // create the JWT token for them
+ const token = jwt.sign({ userId: user.id }, process.env.APP_SECRET);
+ // We set the jwt as a cookie on the response
+ ctx.response.cookie('token', token, {
+ httpOnly: true,
+ maxAge: 1000 * 60 * 60 * 24 * 365, // 1 year cookie
+ });
+ // Finalllllly we return the user to the browser
+ return user;
+ },
+ async signin(parent, { email, password }, ctx, info) {
+ // 1. check if there is a user with that email
+ const user = await ctx.db.query.user({ where: { email } });
+ if (!user) {
+ throw new Error(`No such user found for email ${email}`);
+ }
+ // 2. Check if their password is correct
+ const valid = await bcrypt.compare(password, user.password);
+ if (!valid) {
+ throw new Error('Invalid Password!');
+ }
+ // 3. generate the JWT Token
+ const token = jwt.sign({ userId: user.id }, process.env.APP_SECRET);
+ // 4. Set the cookie with the token
+ ctx.response.cookie('token', token, {
+ httpOnly: true,
+ maxAge: 1000 * 60 * 60 * 24 * 365,
+ });
+ // 5. Return the user
+ return user;
+ },
+};
+
+module.exports = Mutations;
diff --git a/stepped-solutions/28/backend/src/schema.graphql b/stepped-solutions/28/backend/src/schema.graphql
new file mode 100755
index 0000000..a8ad765
--- /dev/null
+++ b/stepped-solutions/28/backend/src/schema.graphql
@@ -0,0 +1,17 @@
+# import * from './generated/prisma.graphql'
+
+type Mutation {
+ createItem(title: String, description: String, price: Int, image: String, largeImage: String): Item!
+ updateItem(id: ID!, title: String, description: String, price: Int): Item!
+ deleteItem(id: ID!): Item
+ signup(email: String!, password: String!, name: String!): User!
+ signin(email: String!, password: String!): User!
+}
+
+type Query {
+ items(where: ItemWhereInput, orderBy: ItemOrderByInput, skip: Int, first: Int): [Item]!
+ item(where: ItemWhereUniqueInput!): Item
+ itemsConnection(where: ItemWhereInput): ItemConnection!
+ me: User
+
+}
diff --git a/stepped-solutions/28/frontend/components/Nav.js b/stepped-solutions/28/frontend/components/Nav.js
new file mode 100755
index 0000000..28e94d3
--- /dev/null
+++ b/stepped-solutions/28/frontend/components/Nav.js
@@ -0,0 +1,36 @@
+import Link from 'next/link';
+import NavStyles from './styles/NavStyles';
+import User from './User';
+
+const Nav = () => (
+ <User>
+ {({ data: { me } }) => (
+ <NavStyles>
+ <Link href="/items">
+ <a>Shop</a>
+ </Link>
+ {me && (
+ <>
+ <Link href="/sell">
+ <a>Sell</a>
+ </Link>
+ <Link href="/orders">
+ <a>Orders</a>
+ </Link>
+ <Link href="/me">
+ <a>Account</a>
+ </Link>
+ </>
+ )}
+ {!me && (
+ <Link href="/signup">
+ <a>Sign In</a>
+ </Link>
+
+ )}
+ </NavStyles>
+ )}
+ </User>
+);
+
+export default Nav;
diff --git a/stepped-solutions/28/frontend/components/Signin.js b/stepped-solutions/28/frontend/components/Signin.js
new file mode 100755
index 0000000..4fd3013
--- /dev/null
+++ b/stepped-solutions/28/frontend/components/Signin.js
@@ -0,0 +1,76 @@
+import React, { Component } from 'react';
+import { Mutation } from 'react-apollo';
+import gql from 'graphql-tag';
+import Form from './styles/Form';
+import Error from './ErrorMessage';
+import { CURRENT_USER_QUERY } from './User';
+
+const SIGNIN_MUTATION = gql`
+ mutation SIGNIN_MUTATION($email: String!, $password: String!) {
+ signin(email: $email, password: $password) {
+ id
+ email
+ name
+ }
+ }
+`;
+
+class Signin extends Component {
+ state = {
+ name: '',
+ password: '',
+ email: '',
+ };
+ saveToState = e => {
+ this.setState({ [e.target.name]: e.target.value });
+ };
+ render() {
+ return (
+ <Mutation
+ mutation={SIGNIN_MUTATION}
+ variables={this.state}
+ refetchQueries={[{ query: CURRENT_USER_QUERY }]}
+ >
+ {(signup, { error, loading }) => (
+ <Form
+ method="post"
+ onSubmit={async e => {
+ e.preventDefault();
+ await signup();
+ this.setState({ name: '', email: '', password: '' });
+ }}
+ >
+ <fieldset disabled={loading} aria-busy={loading}>
+ <h2>Sign into your account</h2>
+ <Error error={error} />
+ <label htmlFor="email">
+ Email
+ <input
+ type="email"
+ name="email"
+ placeholder="email"
+ value={this.state.email}
+ onChange={this.saveToState}
+ />
+ </label>
+ <label htmlFor="password">
+ Password
+ <input
+ type="password"
+ name="password"
+ placeholder="password"
+ value={this.state.password}
+ onChange={this.saveToState}
+ />
+ </label>
+
+ <button type="submit">Sign In!</button>
+ </fieldset>
+ </Form>
+ )}
+ </Mutation>
+ );
+ }
+}
+
+export default Signin;
diff --git a/stepped-solutions/28/frontend/components/Signup.js b/stepped-solutions/28/frontend/components/Signup.js
new file mode 100755
index 0000000..3e59a2b
--- /dev/null
+++ b/stepped-solutions/28/frontend/components/Signup.js
@@ -0,0 +1,86 @@
+import React, { Component } from 'react';
+import { Mutation } from 'react-apollo';
+import gql from 'graphql-tag';
+import Form from './styles/Form';
+import Error from './ErrorMessage';
+import { CURRENT_USER_QUERY } from './User';
+
+const SIGNUP_MUTATION = gql`
+ mutation SIGNUP_MUTATION($email: String!, $name: String!, $password: String!) {
+ signup(email: $email, name: $name, password: $password) {
+ id
+ email
+ name
+ }
+ }
+`;
+
+class Signup extends Component {
+ state = {
+ name: '',
+ password: '',
+ email: '',
+ };
+ saveToState = e => {
+ this.setState({ [e.target.name]: e.target.value });
+ };
+ render() {
+ return (
+ <Mutation
+ mutation={SIGNUP_MUTATION}
+ variables={this.state}
+ refetchQueries={[{ query: CURRENT_USER_QUERY }]}
+ >
+ {(signup, { error, loading }) => (
+ <Form
+ method="post"
+ onSubmit={async e => {
+ e.preventDefault();
+ await signup();
+ this.setState({ name: '', email: '', password: '' });
+ }}
+ >
+ <fieldset disabled={loading} aria-busy={loading}>
+ <h2>Sign Up for An Account</h2>
+ <Error error={error} />
+ <label htmlFor="email">
+ Email
+ <input
+ type="email"
+ name="email"
+ placeholder="email"
+ value={this.state.email}
+ onChange={this.saveToState}
+ />
+ </label>
+ <label htmlFor="name">
+ Name
+ <input
+ type="text"
+ name="name"
+ placeholder="name"
+ value={this.state.name}
+ onChange={this.saveToState}
+ />
+ </label>
+ <label htmlFor="password">
+ Password
+ <input
+ type="password"
+ name="password"
+ placeholder="password"
+ value={this.state.password}
+ onChange={this.saveToState}
+ />
+ </label>
+
+ <button type="submit">Sign Up!</button>
+ </fieldset>
+ </Form>
+ )}
+ </Mutation>
+ );
+ }
+}
+
+export default Signup;
diff --git a/stepped-solutions/28/frontend/components/User.js b/stepped-solutions/28/frontend/components/User.js
new file mode 100755
index 0000000..af074c0
--- /dev/null
+++ b/stepped-solutions/28/frontend/components/User.js
@@ -0,0 +1,27 @@
+import { Query } from 'react-apollo';
+import gql from 'graphql-tag';
+import PropTypes from 'prop-types';
+
+const CURRENT_USER_QUERY = gql`
+ query {
+ me {
+ id
+ email
+ name
+ permissions
+ }
+ }
+`;
+
+const User = props => (
+ <Query {...props} query={CURRENT_USER_QUERY}>
+ {payload => console.log(payload) || props.children(payload)}
+ </Query>
+);
+
+User.PropTypes = {
+ children: PropTypes.func.isRequired,
+};
+
+export default User;
+export { CURRENT_USER_QUERY };
diff --git a/stepped-solutions/28/frontend/pages/signup.js b/stepped-solutions/28/frontend/pages/signup.js
new file mode 100755
index 0000000..28ed2f4
--- /dev/null
+++ b/stepped-solutions/28/frontend/pages/signup.js
@@ -0,0 +1,18 @@
+import Signup from '../components/Signup';
+import Signin from '../components/Signin';
+import styled from 'styled-components';
+
+const Columns = styled.div`
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ grid-gap: 20px;
+`;
+
+const SignupPage = props => (
+ <Columns>
+ <Signup />
+ <Signin />
+ </Columns>
+);
+
+export default SignupPage;