From ff7fc88e36bb66a47327fa2c7e8513b222e539ef Mon Sep 17 00:00:00 2001 From: Wes Bos Date: Wed, 27 Jun 2018 13:02:25 -0400 Subject: Video 21 --- .../05/frontend/components/styles/CartStyles.js | 47 -- .../05/frontend/components/styles/CloseButton.js | 13 - .../05/frontend/components/styles/Form.js | 71 --- .../05/frontend/components/styles/ItemStyles.js | 39 -- .../05/frontend/components/styles/NavStyles.js | 64 -- .../frontend/components/styles/OrderItemStyles.js | 44 -- .../05/frontend/components/styles/OrderStyles.js | 38 -- .../05/frontend/components/styles/PriceTag.js | 17 - .../05/frontend/components/styles/SickButton.js | 20 - .../05/frontend/components/styles/Supreme.js | 13 - .../05/frontend/components/styles/Table.js | 31 - .../05/frontend/components/styles/Title.js | 20 - stepped-solutions/13/backend/src/createServer.js | 22 + stepped-solutions/13/backend/src/db.js | 11 + stepped-solutions/13/backend/src/index.js | 20 + .../13/backend/src/resolvers/Mutation.js | 3 + .../13/backend/src/resolvers/Query.js | 3 + stepped-solutions/13/backend/src/schema.graphql | 7 + stepped-solutions/15/datamodel.graphql | 17 + stepped-solutions/15/src/generated/prisma.graphql | 659 +++++++++++++++++++++ stepped-solutions/15/src/index.js | 20 + stepped-solutions/15/src/resolvers/Mutation.js | 27 + stepped-solutions/15/src/resolvers/Query.js | 12 + stepped-solutions/15/src/schema.graphql | 9 + stepped-solutions/16/_app.js | 31 + stepped-solutions/17/frontend/components/Item.js | 48 ++ stepped-solutions/17/frontend/components/Items.js | 50 ++ stepped-solutions/17/frontend/components/Nav.js | 24 + stepped-solutions/17/frontend/pages/index.js | 9 + stepped-solutions/17/frontend/pages/items.js | 3 + .../18/frontend/components/CreateItem.js | 109 ++++ stepped-solutions/18/frontend/components/Items.js | 51 ++ stepped-solutions/18/frontend/pages/sell.js | 9 + .../19/frontend/components/CreateItem.js | 143 +++++ .../20/backend/src/resolvers/Mutation.js | 36 ++ .../20/backend/src/resolvers/Query.js | 13 + stepped-solutions/20/backend/src/schema.graphql | 11 + .../20/frontend/components/UpdateItem.js | 117 ++++ stepped-solutions/20/frontend/pages/update.js | 9 + .../21/backend/src/resolvers/Mutation.js | 45 ++ stepped-solutions/21/backend/src/schema.graphql | 12 + .../21/frontend/components/DeleteItem.js | 48 ++ stepped-solutions/21/frontend/components/Item.js | 49 ++ 43 files changed, 1627 insertions(+), 417 deletions(-) delete mode 100644 stepped-solutions/05/frontend/components/styles/CartStyles.js delete mode 100644 stepped-solutions/05/frontend/components/styles/CloseButton.js delete mode 100644 stepped-solutions/05/frontend/components/styles/Form.js delete mode 100644 stepped-solutions/05/frontend/components/styles/ItemStyles.js delete mode 100644 stepped-solutions/05/frontend/components/styles/NavStyles.js delete mode 100644 stepped-solutions/05/frontend/components/styles/OrderItemStyles.js delete mode 100644 stepped-solutions/05/frontend/components/styles/OrderStyles.js delete mode 100644 stepped-solutions/05/frontend/components/styles/PriceTag.js delete mode 100644 stepped-solutions/05/frontend/components/styles/SickButton.js delete mode 100644 stepped-solutions/05/frontend/components/styles/Supreme.js delete mode 100644 stepped-solutions/05/frontend/components/styles/Table.js delete mode 100644 stepped-solutions/05/frontend/components/styles/Title.js create mode 100644 stepped-solutions/13/backend/src/createServer.js create mode 100644 stepped-solutions/13/backend/src/db.js create mode 100644 stepped-solutions/13/backend/src/index.js create mode 100644 stepped-solutions/13/backend/src/resolvers/Mutation.js create mode 100644 stepped-solutions/13/backend/src/resolvers/Query.js create mode 100644 stepped-solutions/13/backend/src/schema.graphql create mode 100644 stepped-solutions/15/datamodel.graphql create mode 100644 stepped-solutions/15/src/generated/prisma.graphql create mode 100644 stepped-solutions/15/src/index.js create mode 100644 stepped-solutions/15/src/resolvers/Mutation.js create mode 100644 stepped-solutions/15/src/resolvers/Query.js create mode 100644 stepped-solutions/15/src/schema.graphql create mode 100644 stepped-solutions/16/_app.js create mode 100755 stepped-solutions/17/frontend/components/Item.js create mode 100755 stepped-solutions/17/frontend/components/Items.js create mode 100755 stepped-solutions/17/frontend/components/Nav.js create mode 100755 stepped-solutions/17/frontend/pages/index.js create mode 100755 stepped-solutions/17/frontend/pages/items.js create mode 100755 stepped-solutions/18/frontend/components/CreateItem.js create mode 100755 stepped-solutions/18/frontend/components/Items.js create mode 100755 stepped-solutions/18/frontend/pages/sell.js create mode 100755 stepped-solutions/19/frontend/components/CreateItem.js create mode 100755 stepped-solutions/20/backend/src/resolvers/Mutation.js create mode 100755 stepped-solutions/20/backend/src/resolvers/Query.js create mode 100755 stepped-solutions/20/backend/src/schema.graphql create mode 100755 stepped-solutions/20/frontend/components/UpdateItem.js create mode 100755 stepped-solutions/20/frontend/pages/update.js create mode 100755 stepped-solutions/21/backend/src/resolvers/Mutation.js create mode 100755 stepped-solutions/21/backend/src/schema.graphql create mode 100755 stepped-solutions/21/frontend/components/DeleteItem.js create mode 100755 stepped-solutions/21/frontend/components/Item.js (limited to 'stepped-solutions') diff --git a/stepped-solutions/05/frontend/components/styles/CartStyles.js b/stepped-solutions/05/frontend/components/styles/CartStyles.js deleted file mode 100644 index d5ee93a..0000000 --- a/stepped-solutions/05/frontend/components/styles/CartStyles.js +++ /dev/null @@ -1,47 +0,0 @@ -import styled from 'styled-components'; - -const CartStyles = styled.div` - padding: 20px; - position: relative; - background: white; - position: fixed; - height: 100%; - top: 0; - right: 0; - width: 40%; - min-width: 500px; - bottom: 0; - transform: translateX(100%); - transition: all 0.3s; - box-shadow: 0 0 10px 3px rgba(0, 0, 0, 0.2); - z-index: 5; - display: grid; - grid-template-rows: auto 1fr auto; - ${props => props.open && `transform: translateX(0);`}; - header { - border-bottom: 5px solid ${props => props.theme.black}; - margin-bottom: 2rem; - padding-bottom: 2rem; - } - footer { - border-top: 10px double ${props => props.theme.black}; - margin-top: 2rem; - padding-top: 2rem; - display: grid; - grid-template-columns: auto auto; - align-items: center; - font-size: 3rem; - font-weight: 900; - p { - margin: 0; - } - } - ul { - margin: 0; - padding: 0; - list-style: none; - overflow: scroll; - } -`; - -export default CartStyles; diff --git a/stepped-solutions/05/frontend/components/styles/CloseButton.js b/stepped-solutions/05/frontend/components/styles/CloseButton.js deleted file mode 100644 index 69fd55c..0000000 --- a/stepped-solutions/05/frontend/components/styles/CloseButton.js +++ /dev/null @@ -1,13 +0,0 @@ -import styled from 'styled-components'; - -const CloseButton = styled.button` - background: black; - color: white; - font-size: 3rem; - border: 0; - position: absolute; - z-index: 2; - right: 0; -`; - -export default CloseButton; diff --git a/stepped-solutions/05/frontend/components/styles/Form.js b/stepped-solutions/05/frontend/components/styles/Form.js deleted file mode 100644 index 5717130..0000000 --- a/stepped-solutions/05/frontend/components/styles/Form.js +++ /dev/null @@ -1,71 +0,0 @@ -import styled, { keyframes } from 'styled-components'; - -const loading = keyframes` - from { - background-position: 0 0; - /* rotate: 0; */ - } - - to { - background-position: 100% 100%; - /* rotate: 360deg; */ - } -`; - -const Form = styled.form` - box-shadow: 0 0 5px 3px rgba(0, 0, 0, 0.05); - background: rgba(0, 0, 0, 0.02); - border: 5px solid white; - padding: 20px; - font-size: 1.5rem; - line-height: 1.5; - font-weight: 600; - label { - display: block; - margin-bottom: 1rem; - } - input, - textarea, - select { - width: 100%; - padding: 0.5rem; - font-size: 1rem; - border: 1px solid black; - &:focus { - outline: 0; - border-color: ${props => props.theme.red}; - } - } - button, - input[type='submit'] { - width: auto; - background: red; - color: white; - border: 0; - font-size: 2rem; - font-weight: 600; - padding: 0.5rem 1.2rem; - } - fieldset { - border: 0; - padding: 0; - - &[disabled] { - opacity: 0.5; - } - &::before { - height: 10px; - content: ''; - display: block; - background-image: linear-gradient(to right, #ff3019 0%, #e2b04a 50%, #ff3019 100%); - } - &[aria-busy='true']::before { - background-size: 50% auto; - animation: ${loading} 0.5s linear infinite; - } - } -`; - -Form.displayName = 'Form'; - -export default Form; diff --git a/stepped-solutions/05/frontend/components/styles/ItemStyles.js b/stepped-solutions/05/frontend/components/styles/ItemStyles.js deleted file mode 100644 index b117d5f..0000000 --- a/stepped-solutions/05/frontend/components/styles/ItemStyles.js +++ /dev/null @@ -1,39 +0,0 @@ -import styled from 'styled-components'; - -const Item = styled.div` - background: white; - border: 1px solid ${props => props.theme.offWhite}; - box-shadow: ${props => props.theme.bs}; - position: relative; - display: flex; - flex-direction: column; - img { - width: 100%; - height: 400px; - object-fit: cover; - } - p { - font-size: 12px; - line-height: 2; - font-weight: 300; - flex-grow: 1; - padding: 0 3rem; - font-size: 1.5rem; - } - .buttonList { - display: grid; - width: 100%; - border-top: 1px solid ${props => props.theme.lightgrey}; - grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); - grid-gap: 1px; - background: ${props => props.theme.lightgrey}; - & > * { - background: white; - border: 0; - font-size: 1rem; - padding: 1rem; - } - } -`; - -export default Item; diff --git a/stepped-solutions/05/frontend/components/styles/NavStyles.js b/stepped-solutions/05/frontend/components/styles/NavStyles.js deleted file mode 100644 index 41523df..0000000 --- a/stepped-solutions/05/frontend/components/styles/NavStyles.js +++ /dev/null @@ -1,64 +0,0 @@ -import styled from 'styled-components'; - -const NavStyles = styled.ul` - margin: 0; - padding: 0; - display: flex; - justify-self: end; - font-size: 2rem; - a, - button { - padding: 1rem 3rem; - display: flex; - align-items: center; - position: relative; - text-transform: uppercase; - font-weight: 900; - font-size: 1em; - background: none; - border: 0; - cursor: pointer; - @media (max-width: 700px) { - font-size: 10px; - padding: 0 10px; - } - &:before { - content: ''; - width: 2px; - background: ${props => props.theme.lightgrey}; - height: 100%; - left: 0; - position: absolute; - transform: skew(-20deg); - top: 0; - bottom: 0; - } - &:after { - height: 2px; - background: red; - content: ''; - width: 0; - position: absolute; - transform: translateX(-50%); - transition: width 0.4s; - transition-timing-function: cubic-bezier(1, -0.65, 0, 2.31); - left: 50%; - margin-top: 2rem; - } - &:hover, - &:focus { - outline: none; - &:after { - width: calc(100% - 60px); - } - } - } - @media (max-width: 1300px) { - border-top: 1px solid ${props => props.theme.lightgrey}; - width: 100%; - justify-content: center; - font-size: 1.5rem; - } -`; - -export default NavStyles; diff --git a/stepped-solutions/05/frontend/components/styles/OrderItemStyles.js b/stepped-solutions/05/frontend/components/styles/OrderItemStyles.js deleted file mode 100644 index 292c75d..0000000 --- a/stepped-solutions/05/frontend/components/styles/OrderItemStyles.js +++ /dev/null @@ -1,44 +0,0 @@ -import styled from 'styled-components'; - -const OrderItemStyles = styled.li` - box-shadow: ${props => props.theme.bs}; - list-style: none; - padding: 2rem; - border: 1px solid ${props => props.theme.offWhite}; - h2 { - border-bottom: 2px solid red; - margin-top: 0; - margin-bottom: 2rem; - padding-bottom: 2rem; - } - - .images { - display: grid; - grid-gap: 10px; - grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); - margin-top: 1rem; - img { - height: 200px; - object-fit: cover; - width: 100%; - } - } - .order-meta { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(20px, 1fr)); - display: grid; - grid-gap: 1rem; - text-align: center; - & > * { - margin: 0; - background: rgba(0, 0, 0, 0.03); - padding: 1rem 0; - } - strong { - display: block; - margin-bottom: 1rem; - } - } -`; - -export default OrderItemStyles; diff --git a/stepped-solutions/05/frontend/components/styles/OrderStyles.js b/stepped-solutions/05/frontend/components/styles/OrderStyles.js deleted file mode 100644 index 4a2d804..0000000 --- a/stepped-solutions/05/frontend/components/styles/OrderStyles.js +++ /dev/null @@ -1,38 +0,0 @@ -import styled from 'styled-components'; - -const OrderStyles = styled.div` - max-width: 1000px; - margin: 0 auto; - border: 1px solid ${props => props.theme.offWhite}; - box-shadow: ${props => props.theme.bs}; - padding: 2rem; - border-top: 10px solid red; - & > p { - display: grid; - grid-template-columns: 1fr 5fr; - margin: 0; - border-bottom: 1px solid ${props => props.theme.offWhite}; - span { - padding: 1rem; - &:first-child { - font-weight: 900; - text-align: right; - } - } - } - .order-item { - border-bottom: 1px solid ${props => props.theme.offWhite}; - display: grid; - grid-template-columns: 300px 1fr; - align-items: center; - grid-gap: 2rem; - margin: 2rem 0; - padding-bottom: 2rem; - img { - width: 100%; - height: 100%; - object-fit: cover; - } - } -`; -export default OrderStyles; diff --git a/stepped-solutions/05/frontend/components/styles/PriceTag.js b/stepped-solutions/05/frontend/components/styles/PriceTag.js deleted file mode 100644 index 9116681..0000000 --- a/stepped-solutions/05/frontend/components/styles/PriceTag.js +++ /dev/null @@ -1,17 +0,0 @@ -import styled from 'styled-components'; - -const PriceTag = styled.span` - background: ${props => props.theme.red}; - transform: rotate(3deg); - color: white; - font-weight: 600; - padding: 5px; - line-height: 1; - font-size: 3rem; - display: inline-block; - position: absolute; - top: -3px; - right: -3px; -`; - -export default PriceTag; diff --git a/stepped-solutions/05/frontend/components/styles/SickButton.js b/stepped-solutions/05/frontend/components/styles/SickButton.js deleted file mode 100644 index 5b5352e..0000000 --- a/stepped-solutions/05/frontend/components/styles/SickButton.js +++ /dev/null @@ -1,20 +0,0 @@ -import styled from 'styled-components'; - -const SickButton = styled.button` - background: red; - color: white; - font-weight: 500; - border: 0; - border-radius: 0; - text-transform: uppercase; - font-size: 2rem; - padding: 0.8rem 1.5rem; - transform: skew(-2deg); - display: inline-block; - transition: all 0.5s; - &[disabled] { - opacity: 0.5; - } -`; - -export default SickButton; diff --git a/stepped-solutions/05/frontend/components/styles/Supreme.js b/stepped-solutions/05/frontend/components/styles/Supreme.js deleted file mode 100644 index d946ccd..0000000 --- a/stepped-solutions/05/frontend/components/styles/Supreme.js +++ /dev/null @@ -1,13 +0,0 @@ -import styled from 'styled-components'; - -const Supreme = styled.h3` - background: ${props => props.theme.red}; - color: white; - display: inline-block; - padding: 4px 5px; - transform: skew(-3deg); - margin: 0; - font-size: 4rem; -`; - -export default Supreme; diff --git a/stepped-solutions/05/frontend/components/styles/Table.js b/stepped-solutions/05/frontend/components/styles/Table.js deleted file mode 100644 index e9d0673..0000000 --- a/stepped-solutions/05/frontend/components/styles/Table.js +++ /dev/null @@ -1,31 +0,0 @@ -import styled from 'styled-components'; - -const Table = styled.table` - border-spacing: 0; - width: 100%; - border: 1px solid ${props => props.theme.offWhite}; - thead { - font-size: 10px; - } - td, - th { - border-bottom: 1px solid ${props => props.theme.offWhite}; - border-right: 1px solid ${props => props.theme.offWhite}; - padding: 10px 5px; - position: relative; - &:last-child { - border-right: none; - width: 150px; - button { - width: 100%; - } - } - } - tr { - &:hover { - background: ${props => props.theme.offWhite}; - } - } -`; - -export default Table; diff --git a/stepped-solutions/05/frontend/components/styles/Title.js b/stepped-solutions/05/frontend/components/styles/Title.js deleted file mode 100644 index e1901ec..0000000 --- a/stepped-solutions/05/frontend/components/styles/Title.js +++ /dev/null @@ -1,20 +0,0 @@ -import styled from 'styled-components'; - -const Title = styled.h3` - margin: 0 1rem; - text-align: center; - transform: skew(-5deg) rotate(-1deg); - margin-top: -3rem; - text-shadow: 2px 2px 0 rgba(0, 0, 0, 0.1); - a { - background: ${props => props.theme.red}; - display: inline; - line-height: 1.3; - font-size: 4rem; - text-align: center; - color: white; - padding: 0 1rem; - } -`; - -export default Title; diff --git a/stepped-solutions/13/backend/src/createServer.js b/stepped-solutions/13/backend/src/createServer.js new file mode 100644 index 0000000..c552b02 --- /dev/null +++ b/stepped-solutions/13/backend/src/createServer.js @@ -0,0 +1,22 @@ +const { GraphQLServer } = require('graphql-yoga'); +const Mutation = require('./resolvers/Mutation'); +const Query = require('./resolvers/Query'); +const db = require('./db'); + +// Create the GraphQL Yoga Server + +function createServer() { + return new GraphQLServer({ + typeDefs: 'src/schema.graphql', + resolvers: { + Mutation, + Query, + }, + resolverValidationOptions: { + requireResolversForResolveType: false, + }, + context: req => ({ ...req, db }), + }); +} + +module.exports = createServer; diff --git a/stepped-solutions/13/backend/src/db.js b/stepped-solutions/13/backend/src/db.js new file mode 100644 index 0000000..8f66240 --- /dev/null +++ b/stepped-solutions/13/backend/src/db.js @@ -0,0 +1,11 @@ +// This file connects to the remote prisma DB and gives us the ability to query it with JS +const { Prisma } = require('prisma-binding'); + +const db = new Prisma({ + typeDefs: 'src/generated/prisma.graphql', + endpoint: process.env.PRISMA_ENDPOINT, + secret: process.env.PRISMA_SECRET, + debug: false, +}); + +module.exports = db; diff --git a/stepped-solutions/13/backend/src/index.js b/stepped-solutions/13/backend/src/index.js new file mode 100644 index 0000000..e2dfa96 --- /dev/null +++ b/stepped-solutions/13/backend/src/index.js @@ -0,0 +1,20 @@ +require('dotenv').config({ path: 'variables.env' }); +const createServer = require('./createServer'); +const db = require('./db'); + +const server = createServer(); + +// TODO Use express middlware to handle cookies (JWT) +// TODO Use express middlware to populate current user + +server.start( + { + cors: { + credentials: true, + origin: process.env.FRONTEND_URL, + }, + }, + deets => { + console.log(`Server is now running on port http:/localhost:${deets.port}`); + } +); diff --git a/stepped-solutions/13/backend/src/resolvers/Mutation.js b/stepped-solutions/13/backend/src/resolvers/Mutation.js new file mode 100644 index 0000000..f9bedf6 --- /dev/null +++ b/stepped-solutions/13/backend/src/resolvers/Mutation.js @@ -0,0 +1,3 @@ +const Mutations = {}; + +module.exports = Mutations; diff --git a/stepped-solutions/13/backend/src/resolvers/Query.js b/stepped-solutions/13/backend/src/resolvers/Query.js new file mode 100644 index 0000000..eebe788 --- /dev/null +++ b/stepped-solutions/13/backend/src/resolvers/Query.js @@ -0,0 +1,3 @@ +const Query = {}; + +module.exports = Query; diff --git a/stepped-solutions/13/backend/src/schema.graphql b/stepped-solutions/13/backend/src/schema.graphql new file mode 100644 index 0000000..5a1f430 --- /dev/null +++ b/stepped-solutions/13/backend/src/schema.graphql @@ -0,0 +1,7 @@ +type Mutation { + hi: String +} + +type Query { + hi: String +} diff --git a/stepped-solutions/15/datamodel.graphql b/stepped-solutions/15/datamodel.graphql new file mode 100644 index 0000000..600ec3c --- /dev/null +++ b/stepped-solutions/15/datamodel.graphql @@ -0,0 +1,17 @@ +type User { + id: ID! @unique + name: String! + email: String! +} + +type Item { + id: ID! @unique + title: String! + description: String! + image: String + largeImage: String + price: Int! + # createAt: DateTime! + # updatedAt: DateTime! + # user: User! +} diff --git a/stepped-solutions/15/src/generated/prisma.graphql b/stepped-solutions/15/src/generated/prisma.graphql new file mode 100644 index 0000000..19ea80b --- /dev/null +++ b/stepped-solutions/15/src/generated/prisma.graphql @@ -0,0 +1,659 @@ +# source: https://us1.prisma.sh/wesbos/siccccccccck-fits/dev +# timestamp: Mon Jun 25 2018 12:45:45 GMT-0400 (EDT) + +type AggregateItem { + count: Int! +} + +type AggregateUser { + count: Int! +} + +type BatchPayload { + """The number of nodes that have been affected by the Batch operation.""" + count: Long! +} + +type Item implements Node { + id: ID! + title: String! + description: String! + image: String + largeImage: String + price: Int! +} + +"""A connection to a list of items.""" +type ItemConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [ItemEdge]! + aggregate: AggregateItem! +} + +input ItemCreateInput { + title: String! + description: String! + image: String + largeImage: String + price: Int! +} + +"""An edge in a connection.""" +type ItemEdge { + """The item at the end of the edge.""" + node: Item! + + """A cursor for use in pagination.""" + cursor: String! +} + +enum ItemOrderByInput { + id_ASC + id_DESC + title_ASC + title_DESC + description_ASC + description_DESC + image_ASC + image_DESC + largeImage_ASC + largeImage_DESC + price_ASC + price_DESC + updatedAt_ASC + updatedAt_DESC + createdAt_ASC + createdAt_DESC +} + +type ItemPreviousValues { + id: ID! + title: String! + description: String! + image: String + largeImage: String + price: Int! +} + +type ItemSubscriptionPayload { + mutation: MutationType! + node: Item + updatedFields: [String!] + previousValues: ItemPreviousValues +} + +input ItemSubscriptionWhereInput { + """Logical AND on all given filters.""" + AND: [ItemSubscriptionWhereInput!] + + """Logical OR on all given filters.""" + OR: [ItemSubscriptionWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [ItemSubscriptionWhereInput!] + + """ + The subscription event gets dispatched when it's listed in mutation_in + """ + mutation_in: [MutationType!] + + """ + The subscription event gets only dispatched when one of the updated fields names is included in this list + """ + updatedFields_contains: String + + """ + The subscription event gets only dispatched when all of the field names included in this list have been updated + """ + updatedFields_contains_every: [String!] + + """ + The subscription event gets only dispatched when some of the field names included in this list have been updated + """ + updatedFields_contains_some: [String!] + node: ItemWhereInput +} + +input ItemUpdateInput { + title: String + description: String + image: String + largeImage: String + price: Int +} + +input ItemWhereInput { + """Logical AND on all given filters.""" + AND: [ItemWhereInput!] + + """Logical OR on all given filters.""" + OR: [ItemWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [ItemWhereInput!] + id: ID + + """All values that are not equal to given value.""" + id_not: ID + + """All values that are contained in given list.""" + id_in: [ID!] + + """All values that are not contained in given list.""" + id_not_in: [ID!] + + """All values less than the given value.""" + id_lt: ID + + """All values less than or equal the given value.""" + id_lte: ID + + """All values greater than the given value.""" + id_gt: ID + + """All values greater than or equal the given value.""" + id_gte: ID + + """All values containing the given string.""" + id_contains: ID + + """All values not containing the given string.""" + id_not_contains: ID + + """All values starting with the given string.""" + id_starts_with: ID + + """All values not starting with the given string.""" + id_not_starts_with: ID + + """All values ending with the given string.""" + id_ends_with: ID + + """All values not ending with the given string.""" + id_not_ends_with: ID + title: String + + """All values that are not equal to given value.""" + title_not: String + + """All values that are contained in given list.""" + title_in: [String!] + + """All values that are not contained in given list.""" + title_not_in: [String!] + + """All values less than the given value.""" + title_lt: String + + """All values less than or equal the given value.""" + title_lte: String + + """All values greater than the given value.""" + title_gt: String + + """All values greater than or equal the given value.""" + title_gte: String + + """All values containing the given string.""" + title_contains: String + + """All values not containing the given string.""" + title_not_contains: String + + """All values starting with the given string.""" + title_starts_with: String + + """All values not starting with the given string.""" + title_not_starts_with: String + + """All values ending with the given string.""" + title_ends_with: String + + """All values not ending with the given string.""" + title_not_ends_with: String + description: String + + """All values that are not equal to given value.""" + description_not: String + + """All values that are contained in given list.""" + description_in: [String!] + + """All values that are not contained in given list.""" + description_not_in: [String!] + + """All values less than the given value.""" + description_lt: String + + """All values less than or equal the given value.""" + description_lte: String + + """All values greater than the given value.""" + description_gt: String + + """All values greater than or equal the given value.""" + description_gte: String + + """All values containing the given string.""" + description_contains: String + + """All values not containing the given string.""" + description_not_contains: String + + """All values starting with the given string.""" + description_starts_with: String + + """All values not starting with the given string.""" + description_not_starts_with: String + + """All values ending with the given string.""" + description_ends_with: String + + """All values not ending with the given string.""" + description_not_ends_with: String + image: String + + """All values that are not equal to given value.""" + image_not: String + + """All values that are contained in given list.""" + image_in: [String!] + + """All values that are not contained in given list.""" + image_not_in: [String!] + + """All values less than the given value.""" + image_lt: String + + """All values less than or equal the given value.""" + image_lte: String + + """All values greater than the given value.""" + image_gt: String + + """All values greater than or equal the given value.""" + image_gte: String + + """All values containing the given string.""" + image_contains: String + + """All values not containing the given string.""" + image_not_contains: String + + """All values starting with the given string.""" + image_starts_with: String + + """All values not starting with the given string.""" + image_not_starts_with: String + + """All values ending with the given string.""" + image_ends_with: String + + """All values not ending with the given string.""" + image_not_ends_with: String + largeImage: String + + """All values that are not equal to given value.""" + largeImage_not: String + + """All values that are contained in given list.""" + largeImage_in: [String!] + + """All values that are not contained in given list.""" + largeImage_not_in: [String!] + + """All values less than the given value.""" + largeImage_lt: String + + """All values less than or equal the given value.""" + largeImage_lte: String + + """All values greater than the given value.""" + largeImage_gt: String + + """All values greater than or equal the given value.""" + largeImage_gte: String + + """All values containing the given string.""" + largeImage_contains: String + + """All values not containing the given string.""" + largeImage_not_contains: String + + """All values starting with the given string.""" + largeImage_starts_with: String + + """All values not starting with the given string.""" + largeImage_not_starts_with: String + + """All values ending with the given string.""" + largeImage_ends_with: String + + """All values not ending with the given string.""" + largeImage_not_ends_with: String + price: Int + + """All values that are not equal to given value.""" + price_not: Int + + """All values that are contained in given list.""" + price_in: [Int!] + + """All values that are not contained in given list.""" + price_not_in: [Int!] + + """All values less than the given value.""" + price_lt: Int + + """All values less than or equal the given value.""" + price_lte: Int + + """All values greater than the given value.""" + price_gt: Int + + """All values greater than or equal the given value.""" + price_gte: Int +} + +input ItemWhereUniqueInput { + id: ID +} + +""" +The `Long` scalar type represents non-fractional signed whole numeric values. +Long can represent values between -(2^63) and 2^63 - 1. +""" +scalar Long + +type Mutation { + createUser(data: UserCreateInput!): User! + createItem(data: ItemCreateInput!): Item! + updateUser(data: UserUpdateInput!, where: UserWhereUniqueInput!): User + updateItem(data: ItemUpdateInput!, where: ItemWhereUniqueInput!): Item + deleteUser(where: UserWhereUniqueInput!): User + deleteItem(where: ItemWhereUniqueInput!): Item + upsertUser(where: UserWhereUniqueInput!, create: UserCreateInput!, update: UserUpdateInput!): User! + upsertItem(where: ItemWhereUniqueInput!, create: ItemCreateInput!, update: ItemUpdateInput!): Item! + updateManyUsers(data: UserUpdateInput!, where: UserWhereInput): BatchPayload! + updateManyItems(data: ItemUpdateInput!, where: ItemWhereInput): BatchPayload! + deleteManyUsers(where: UserWhereInput): BatchPayload! + deleteManyItems(where: ItemWhereInput): BatchPayload! +} + +enum MutationType { + CREATED + UPDATED + DELETED +} + +"""An object with an ID""" +interface Node { + """The id of the object.""" + id: ID! +} + +"""Information about pagination in a connection.""" +type PageInfo { + """When paginating forwards, are there more items?""" + hasNextPage: Boolean! + + """When paginating backwards, are there more items?""" + hasPreviousPage: Boolean! + + """When paginating backwards, the cursor to continue.""" + startCursor: String + + """When paginating forwards, the cursor to continue.""" + endCursor: String +} + +type Query { + users(where: UserWhereInput, orderBy: UserOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [User]! + items(where: ItemWhereInput, orderBy: ItemOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [Item]! + user(where: UserWhereUniqueInput!): User + item(where: ItemWhereUniqueInput!): Item + usersConnection(where: UserWhereInput, orderBy: UserOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): UserConnection! + itemsConnection(where: ItemWhereInput, orderBy: ItemOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): ItemConnection! + + """Fetches an object given its ID""" + node( + """The ID of an object""" + id: ID! + ): Node +} + +type Subscription { + user(where: UserSubscriptionWhereInput): UserSubscriptionPayload + item(where: ItemSubscriptionWhereInput): ItemSubscriptionPayload +} + +type User implements Node { + id: ID! + name: String! + email: String! +} + +"""A connection to a list of items.""" +type UserConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [UserEdge]! + aggregate: AggregateUser! +} + +input UserCreateInput { + name: String! + email: String! +} + +"""An edge in a connection.""" +type UserEdge { + """The item at the end of the edge.""" + node: User! + + """A cursor for use in pagination.""" + cursor: String! +} + +enum UserOrderByInput { + id_ASC + id_DESC + name_ASC + name_DESC + email_ASC + email_DESC + updatedAt_ASC + updatedAt_DESC + createdAt_ASC + createdAt_DESC +} + +type UserPreviousValues { + id: ID! + name: String! + email: String! +} + +type UserSubscriptionPayload { + mutation: MutationType! + node: User + updatedFields: [String!] + previousValues: UserPreviousValues +} + +input UserSubscriptionWhereInput { + """Logical AND on all given filters.""" + AND: [UserSubscriptionWhereInput!] + + """Logical OR on all given filters.""" + OR: [UserSubscriptionWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [UserSubscriptionWhereInput!] + + """ + The subscription event gets dispatched when it's listed in mutation_in + """ + mutation_in: [MutationType!] + + """ + The subscription event gets only dispatched when one of the updated fields names is included in this list + """ + updatedFields_contains: String + + """ + The subscription event gets only dispatched when all of the field names included in this list have been updated + """ + updatedFields_contains_every: [String!] + + """ + The subscription event gets only dispatched when some of the field names included in this list have been updated + """ + updatedFields_contains_some: [String!] + node: UserWhereInput +} + +input UserUpdateInput { + name: String + email: String +} + +input UserWhereInput { + """Logical AND on all given filters.""" + AND: [UserWhereInput!] + + """Logical OR on all given filters.""" + OR: [UserWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [UserWhereInput!] + id: ID + + """All values that are not equal to given value.""" + id_not: ID + + """All values that are contained in given list.""" + id_in: [ID!] + + """All values that are not contained in given list.""" + id_not_in: [ID!] + + """All values less than the given value.""" + id_lt: ID + + """All values less than or equal the given value.""" + id_lte: ID + + """All values greater than the given value.""" + id_gt: ID + + """All values greater than or equal the given value.""" + id_gte: ID + + """All values containing the given string.""" + id_contains: ID + + """All values not containing the given string.""" + id_not_contains: ID + + """All values starting with the given string.""" + id_starts_with: ID + + """All values not starting with the given string.""" + id_not_starts_with: ID + + """All values ending with the given string.""" + id_ends_with: ID + + """All values not ending with the given string.""" + id_not_ends_with: ID + name: String + + """All values that are not equal to given value.""" + name_not: String + + """All values that are contained in given list.""" + name_in: [String!] + + """All values that are not contained in given list.""" + name_not_in: [String!] + + """All values less than the given value.""" + name_lt: String + + """All values less than or equal the given value.""" + name_lte: String + + """All values greater than the given value.""" + name_gt: String + + """All values greater than or equal the given value.""" + name_gte: String + + """All values containing the given string.""" + name_contains: String + + """All values not containing the given string.""" + name_not_contains: String + + """All values starting with the given string.""" + name_starts_with: String + + """All values not starting with the given string.""" + name_not_starts_with: String + + """All values ending with the given string.""" + name_ends_with: String + + """All values not ending with the given string.""" + name_not_ends_with: String + email: String + + """All values that are not equal to given value.""" + email_not: String + + """All values that are contained in given list.""" + email_in: [String!] + + """All values that are not contained in given list.""" + email_not_in: [String!] + + """All values less than the given value.""" + email_lt: String + + """All values less than or equal the given value.""" + email_lte: String + + """All values greater than the given value.""" + email_gt: String + + """All values greater than or equal the given value.""" + email_gte: String + + """All values containing the given string.""" + email_contains: String + + """All values not containing the given string.""" + email_not_contains: String + + """All values starting with the given string.""" + email_starts_with: String + + """All values not starting with the given string.""" + email_not_starts_with: String + + """All values ending with the given string.""" + email_ends_with: String + + """All values not ending with the given string.""" + email_not_ends_with: String +} + +input UserWhereUniqueInput { + id: ID +} diff --git a/stepped-solutions/15/src/index.js b/stepped-solutions/15/src/index.js new file mode 100644 index 0000000..ac75c05 --- /dev/null +++ b/stepped-solutions/15/src/index.js @@ -0,0 +1,20 @@ +require('dotenv').config({ path: 'variables.env' }); +const createServer = require('./createServer'); +const db = require('./db'); + +const server = createServer(); + +// TODO Use express middlware to handle cookies (JWT) +// TODO Use express middlware to populate current user + +server.start( + { + cors: { + credentials: true, + origin: process.env.FRONTEND_URL, + }, + }, + deets => { + console.log(`Server is now running on port http://localhost:${deets.port}`); + } +); diff --git a/stepped-solutions/15/src/resolvers/Mutation.js b/stepped-solutions/15/src/resolvers/Mutation.js new file mode 100644 index 0000000..5a90e54 --- /dev/null +++ b/stepped-solutions/15/src/resolvers/Mutation.js @@ -0,0 +1,27 @@ +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; + }, + // createDog(parent, args, ctx, info) { + // global.dogs = global.dogs || []; + // // create a dog + // const newDog = { name: args.name }; + // global.dogs.push(newDog); + // return newDog; + // }, +}; + +module.exports = Mutations; diff --git a/stepped-solutions/15/src/resolvers/Query.js b/stepped-solutions/15/src/resolvers/Query.js new file mode 100644 index 0000000..b02cf71 --- /dev/null +++ b/stepped-solutions/15/src/resolvers/Query.js @@ -0,0 +1,12 @@ +const { forwardTo } = require('prisma-binding'); + +const Query = { + items: forwardTo('db'), + // async items(parent, args, ctx, info) { + // console.log('Getting Items!!'); + // const items = await ctx.db.query.items(); + // return items; + // }, +}; + +module.exports = Query; diff --git a/stepped-solutions/15/src/schema.graphql b/stepped-solutions/15/src/schema.graphql new file mode 100644 index 0000000..ddab542 --- /dev/null +++ b/stepped-solutions/15/src/schema.graphql @@ -0,0 +1,9 @@ +# import * from './generated/prisma.graphql' + +type Mutation { + createItem(title: String, description: String, price: Int, image: String, largeImage: String): Item! +} + +type Query { + items: [Item]! +} diff --git a/stepped-solutions/16/_app.js b/stepped-solutions/16/_app.js new file mode 100644 index 0000000..fc03876 --- /dev/null +++ b/stepped-solutions/16/_app.js @@ -0,0 +1,31 @@ +import App, { Container } from 'next/app'; +import Page from '../components/Page'; +import { ApolloProvider } from 'react-apollo'; +import withData from '../lib/withData'; + +class MyApp extends App { + static async getInitialProps({ Component, ctx }) { + let pageProps = {}; + if (Component.getInitialProps) { + pageProps = await Component.getInitialProps(ctx); + } + // this exposes the query to the user + pageProps.query = ctx.query; + return { pageProps }; + } + render() { + const { Component, apollo, pageProps } = this.props; + + return ( + + + + + + + + ); + } +} + +export default withData(MyApp); diff --git a/stepped-solutions/17/frontend/components/Item.js b/stepped-solutions/17/frontend/components/Item.js new file mode 100755 index 0000000..092b443 --- /dev/null +++ b/stepped-solutions/17/frontend/components/Item.js @@ -0,0 +1,48 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import Link from 'next/link'; +import Title from './styles/Title'; +import ItemStyles from './styles/ItemStyles'; +import PriceTag from './styles/PriceTag'; +import formatMoney from '../lib/formatMoney'; + +export default class Item extends Component { + static propTypes = { + item: PropTypes.object.isRequired, + }; + + render() { + const { item } = this.props; + return ( + + {item.image && {item.title}} + + + <Link + href={{ + pathname: '/item', + query: { id: item.id }, + }} + > + <a>{item.title}</a> + </Link> + + {formatMoney(item.price)} +

{item.description}

+ +
+ + Edit ✏️ + + + +
+
+ ); + } +} diff --git a/stepped-solutions/17/frontend/components/Items.js b/stepped-solutions/17/frontend/components/Items.js new file mode 100755 index 0000000..1a30dd5 --- /dev/null +++ b/stepped-solutions/17/frontend/components/Items.js @@ -0,0 +1,50 @@ +import React, { Component } from 'react'; +import { Query } from 'react-apollo'; +import gql from 'graphql-tag'; +import styled from 'styled-components'; +import Item from './Item'; + +const ALL_ITEMS_QUERY = gql` + query ALL_ITEMS_QUERY { + items { + id + title + price + description + image + largeImage + } + } +`; + +const Center = styled.div` + text-align: center; +`; + +const ItemsList = styled.div` + display: grid; + grid-template-columns: 1fr 1fr; + grid-gap: 60px; + max-width: ${props => props.theme.maxWidth}; + margin: 0 auto; +`; + +class Items extends Component { + render() { + return ( +
+ + {({ data, error, loading }) => { + if (loading) return

Loading...

; + if (error) return

Error: {error.message}

; + return ( + {data.items.map(item => )} + ); + }} +
+
+ ); + } +} + +export default Items; diff --git a/stepped-solutions/17/frontend/components/Nav.js b/stepped-solutions/17/frontend/components/Nav.js new file mode 100755 index 0000000..a5da5de --- /dev/null +++ b/stepped-solutions/17/frontend/components/Nav.js @@ -0,0 +1,24 @@ +import Link from 'next/link'; +import NavStyles from './styles/NavStyles'; + +const Nav = () => ( + + + Shop + + + Sell + + + Signup + + + Orders + + + Account + + +); + +export default Nav; diff --git a/stepped-solutions/17/frontend/pages/index.js b/stepped-solutions/17/frontend/pages/index.js new file mode 100755 index 0000000..7a29ae0 --- /dev/null +++ b/stepped-solutions/17/frontend/pages/index.js @@ -0,0 +1,9 @@ +import Items from '../components/Items'; + +const Home = props => ( +
+ +
+); + +export default Home; diff --git a/stepped-solutions/17/frontend/pages/items.js b/stepped-solutions/17/frontend/pages/items.js new file mode 100755 index 0000000..d9fdebb --- /dev/null +++ b/stepped-solutions/17/frontend/pages/items.js @@ -0,0 +1,3 @@ +import Home from './index'; + +export default Home; diff --git a/stepped-solutions/18/frontend/components/CreateItem.js b/stepped-solutions/18/frontend/components/CreateItem.js new file mode 100755 index 0000000..cf389da --- /dev/null +++ b/stepped-solutions/18/frontend/components/CreateItem.js @@ -0,0 +1,109 @@ +import React, { Component } from 'react'; +import { Mutation } from 'react-apollo'; +import gql from 'graphql-tag'; +import Router from 'next/router'; +import Form from './styles/Form'; +import formatMoney from '../lib/formatMoney'; +import Error from './ErrorMessage'; + +const CREATE_ITEM_MUTATION = gql` + mutation CREATE_ITEM_MUTATION( + $title: String! + $description: String! + $price: Int! + $image: String + $largeImage: String + ) { + createItem( + title: $title + description: $description + price: $price + image: $image + largeImage: $largeImage + ) { + id + } + } +`; + +class CreateItem extends Component { + state = { + title: 'Cool Shoes', + description: 'I love those shoes', + image: 'dog.jpg', + largeImage: 'large-dog.jpg', + price: 1000, + }; + handleChange = e => { + const { name, type, value } = e.target; + const val = type === 'number' ? parseFloat(value) : value; + this.setState({ [name]: val }); + }; + render() { + return ( + + {(createItem, { loading, error }) => ( +
{ + // Stop the form from submitting + e.preventDefault(); + // call the mutation + const res = await createItem(); + // change them to the single item page + console.log(res); + Router.push({ + pathname: '/item', + query: { id: res.data.createItem.id }, + }); + }} + > + +
+ + + + +