summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWes Bos <wesbos@gmail.com>2018-02-13 13:19:23 -0500
committerWes Bos <wesbos@gmail.com>2018-02-13 13:19:23 -0500
commitfa3ec3299b3fa33e5a699683beb96cfa4e6dd51d (patch)
tree601b4ff9650cd17a6d4181fc5982b401411d2a6a
parente0d0baa75ec0bf617f91b6cd779305d4009cae12 (diff)
getting there
-rw-r--r--backend/README.md11
-rw-r--r--backend/database/datamodel.graphql13
-rw-r--r--backend/database/prisma.yml3
-rw-r--r--backend/package.json12
-rw-r--r--backend/src/generated/prisma.graphql350
-rw-r--r--backend/src/index.js2
-rw-r--r--backend/src/resolvers/Mutation.js121
-rw-r--r--backend/src/resolvers/Query.js7
-rw-r--r--backend/src/schema.graphql6
-rw-r--r--backend/src/stripe.js1
-rw-r--r--frontend/components/AddToCart.js5
-rw-r--r--frontend/components/Cart.js7
-rw-r--r--frontend/components/CreateItem.js17
-rw-r--r--frontend/components/Header.js4
-rw-r--r--frontend/components/Item.js14
-rw-r--r--frontend/components/Page.js2
-rw-r--r--frontend/components/SingleItem.js3
-rw-r--r--frontend/components/TakeMyMoney.js46
-rw-r--r--frontend/enhancers/enhancers.js11
-rw-r--r--frontend/queries/fragments.js1
-rw-r--r--frontend/queries/index.js33
21 files changed, 560 insertions, 109 deletions
diff --git a/backend/README.md b/backend/README.md
index e1c8e10..765d19e 100644
--- a/backend/README.md
+++ b/backend/README.md
@@ -18,4 +18,15 @@ When you want to add a new function:
### Installing Docker
+1. download docker
+1. run `prisma local start`
+1. Run `prisma deploy`
+if in trouble, run `prisma local nuke`
+
+
+
+### Cloudinary
+
+1. go to your settings and turn on unsigned uploading
+2. \ No newline at end of file
diff --git a/backend/database/datamodel.graphql b/backend/database/datamodel.graphql
index 4620206..ed70892 100644
--- a/backend/database/datamodel.graphql
+++ b/backend/database/datamodel.graphql
@@ -14,6 +14,7 @@ type User {
password: String!
name: String!
posts: [Post!]!
+ orders: [Order!]!
website: String
age: Int
resetToken: String
@@ -24,12 +25,22 @@ type User {
type CartItem {
id: ID! @unique
quantity: Int! @default(value: 1)
- item: Item!
+ item: Item! # Relationship to an Item
+ user: User! # Relationship to a user
}
type Item {
id: ID! @unique
title: String!
description: String!
+ image: String
price: Int!
}
+
+type Order {
+ id: ID! @unique
+ items: [Item!]!
+ total: Int!
+ user: User!
+ charge: String!
+} \ No newline at end of file
diff --git a/backend/database/prisma.yml b/backend/database/prisma.yml
index ce652ef..b01378b 100644
--- a/backend/database/prisma.yml
+++ b/backend/database/prisma.yml
@@ -15,5 +15,6 @@ datamodel: datamodel.graphql
# seed:
# import: seed.graphql
-cluster: ${env:PRISMA_CLUSTER}
+# cluster: ${env:PRISMA_CLUSTER}
+cluster: local
diff --git a/backend/package.json b/backend/package.json
index fda13b0..10b57aa 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -9,19 +9,25 @@
},
"dependencies": {
"bcryptjs": "2.4.3",
+ "graphql": "^0.12.3",
"graphql-yoga": "1.2.4",
"jsonwebtoken": "8.1.1",
"nodemailer": "^4.4.2",
- "prisma-binding": "1.5.7"
+ "prisma-binding": "1.5.7",
+ "stripe": "^5.4.0"
},
"devDependencies": {
"chalk": "^2.3.0",
"dotenv": "5.0.0",
- "graphql-cli": "2.13.1",
+ "graphql-cli": "2.14.0",
"graphql-request": "^1.4.1",
"jest-cli": "^22.1.4",
"nodemon": "1.14.12",
"npm-run-all": "4.1.2",
- "prisma": "1.1.3"
+ "prisma": "1.2.0"
+ },
+ "license": "MIT",
+ "repository": {
+ "url": "wesbos.com"
}
}
diff --git a/backend/src/generated/prisma.graphql b/backend/src/generated/prisma.graphql
index 6132328..402e097 100644
--- a/backend/src/generated/prisma.graphql
+++ b/backend/src/generated/prisma.graphql
@@ -9,15 +9,25 @@ type CartItem implements Node {
id: ID!
quantity: Int!
item(where: ItemWhereInput): Item!
+ user(where: UserWhereInput): User!
}
type Item implements Node {
id: ID!
title: String!
description: String!
+ image: String
price: Int!
}
+type Order implements Node {
+ id: ID!
+ items(where: ItemWhereInput, orderBy: ItemOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [Item!]
+ total: Int!
+ user(where: UserWhereInput): User!
+ charge: String!
+}
+
type Post implements Node {
id: ID!
createdAt: DateTime!
@@ -34,6 +44,7 @@ type User implements Node {
password: String!
name: String!
posts(where: PostWhereInput, orderBy: PostOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [Post!]
+ orders(where: OrderWhereInput, orderBy: OrderOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [Order!]
website: String
age: Int
resetToken: String
@@ -54,6 +65,10 @@ type AggregateItem {
count: Int!
}
+type AggregateOrder {
+ count: Int!
+}
+
type AggregatePost {
count: Int!
}
@@ -75,13 +90,19 @@ type CartItemConnection {
input CartItemCreateInput {
quantity: Int
item: ItemCreateOneInput!
+ user: UserCreateOneWithoutCartInput!
}
-input CartItemCreateManyInput {
- create: [CartItemCreateInput!]
+input CartItemCreateManyWithoutUserInput {
+ create: [CartItemCreateWithoutUserInput!]
connect: [CartItemWhereUniqueInput!]
}
+input CartItemCreateWithoutUserInput {
+ quantity: Int
+ item: ItemCreateOneInput!
+}
+
type CartItemEdge {
node: CartItem!
cursor: String!
@@ -123,13 +144,32 @@ input CartItemSubscriptionWhereInput {
input CartItemUpdateInput {
quantity: Int
item: ItemUpdateOneInput
+ user: UserUpdateOneWithoutCartInput
}
-input CartItemUpdateManyInput {
- create: [CartItemCreateInput!]
+input CartItemUpdateManyWithoutUserInput {
+ create: [CartItemCreateWithoutUserInput!]
connect: [CartItemWhereUniqueInput!]
disconnect: [CartItemWhereUniqueInput!]
delete: [CartItemWhereUniqueInput!]
+ update: [CartItemUpdateWithoutUserInput!]
+ upsert: [CartItemUpsertWithoutUserInput!]
+}
+
+input CartItemUpdateWithoutUserDataInput {
+ quantity: Int
+ item: ItemUpdateOneInput
+}
+
+input CartItemUpdateWithoutUserInput {
+ where: CartItemWhereUniqueInput!
+ data: CartItemUpdateWithoutUserDataInput!
+}
+
+input CartItemUpsertWithoutUserInput {
+ where: CartItemWhereUniqueInput!
+ update: CartItemUpdateWithoutUserDataInput!
+ create: CartItemCreateWithoutUserInput!
}
input CartItemWhereInput {
@@ -158,6 +198,7 @@ input CartItemWhereInput {
quantity_gt: Int
quantity_gte: Int
item: ItemWhereInput
+ user: UserWhereInput
}
input CartItemWhereUniqueInput {
@@ -175,9 +216,15 @@ type ItemConnection {
input ItemCreateInput {
title: String!
description: String!
+ image: String
price: Int!
}
+input ItemCreateManyInput {
+ create: [ItemCreateInput!]
+ connect: [ItemWhereUniqueInput!]
+}
+
input ItemCreateOneInput {
create: ItemCreateInput
connect: ItemWhereUniqueInput
@@ -195,6 +242,8 @@ enum ItemOrderByInput {
title_DESC
description_ASC
description_DESC
+ image_ASC
+ image_DESC
price_ASC
price_DESC
updatedAt_ASC
@@ -207,6 +256,7 @@ type ItemPreviousValues {
id: ID!
title: String!
description: String!
+ image: String
price: Int!
}
@@ -230,9 +280,17 @@ input ItemSubscriptionWhereInput {
input ItemUpdateInput {
title: String
description: String
+ image: String
price: Int
}
+input ItemUpdateManyInput {
+ create: [ItemCreateInput!]
+ connect: [ItemWhereUniqueInput!]
+ disconnect: [ItemWhereUniqueInput!]
+ delete: [ItemWhereUniqueInput!]
+}
+
input ItemUpdateOneInput {
create: ItemCreateInput
connect: ItemWhereUniqueInput
@@ -285,6 +343,20 @@ input ItemWhereInput {
description_not_starts_with: String
description_ends_with: String
description_not_ends_with: String
+ image: String
+ image_not: String
+ image_in: [String!]
+ image_not_in: [String!]
+ image_lt: String
+ image_lte: String
+ image_gt: String
+ image_gte: String
+ image_contains: String
+ image_not_contains: String
+ image_starts_with: String
+ image_not_starts_with: String
+ image_ends_with: String
+ image_not_ends_with: String
price: Int
price_not: Int
price_in: [Int!]
@@ -306,26 +378,32 @@ type Mutation {
createUser(data: UserCreateInput!): User!
createCartItem(data: CartItemCreateInput!): CartItem!
createItem(data: ItemCreateInput!): Item!
+ createOrder(data: OrderCreateInput!): Order!
updatePost(data: PostUpdateInput!, where: PostWhereUniqueInput!): Post
updateUser(data: UserUpdateInput!, where: UserWhereUniqueInput!): User
updateCartItem(data: CartItemUpdateInput!, where: CartItemWhereUniqueInput!): CartItem
updateItem(data: ItemUpdateInput!, where: ItemWhereUniqueInput!): Item
+ updateOrder(data: OrderUpdateInput!, where: OrderWhereUniqueInput!): Order
deletePost(where: PostWhereUniqueInput!): Post
deleteUser(where: UserWhereUniqueInput!): User
deleteCartItem(where: CartItemWhereUniqueInput!): CartItem
deleteItem(where: ItemWhereUniqueInput!): Item
+ deleteOrder(where: OrderWhereUniqueInput!): Order
upsertPost(where: PostWhereUniqueInput!, create: PostCreateInput!, update: PostUpdateInput!): Post!
upsertUser(where: UserWhereUniqueInput!, create: UserCreateInput!, update: UserUpdateInput!): User!
upsertCartItem(where: CartItemWhereUniqueInput!, create: CartItemCreateInput!, update: CartItemUpdateInput!): CartItem!
upsertItem(where: ItemWhereUniqueInput!, create: ItemCreateInput!, update: ItemUpdateInput!): Item!
+ upsertOrder(where: OrderWhereUniqueInput!, create: OrderCreateInput!, update: OrderUpdateInput!): Order!
updateManyPosts(data: PostUpdateInput!, where: PostWhereInput!): BatchPayload!
updateManyUsers(data: UserUpdateInput!, where: UserWhereInput!): BatchPayload!
updateManyCartItems(data: CartItemUpdateInput!, where: CartItemWhereInput!): BatchPayload!
updateManyItems(data: ItemUpdateInput!, where: ItemWhereInput!): BatchPayload!
+ updateManyOrders(data: OrderUpdateInput!, where: OrderWhereInput!): BatchPayload!
deleteManyPosts(where: PostWhereInput!): BatchPayload!
deleteManyUsers(where: UserWhereInput!): BatchPayload!
deleteManyCartItems(where: CartItemWhereInput!): BatchPayload!
deleteManyItems(where: ItemWhereInput!): BatchPayload!
+ deleteManyOrders(where: OrderWhereInput!): BatchPayload!
}
enum MutationType {
@@ -338,6 +416,153 @@ interface Node {
id: ID!
}
+type OrderConnection {
+ pageInfo: PageInfo!
+ edges: [OrderEdge]!
+ aggregate: AggregateOrder!
+}
+
+input OrderCreateInput {
+ total: Int!
+ charge: String!
+ items: ItemCreateManyInput
+ user: UserCreateOneWithoutOrdersInput!
+}
+
+input OrderCreateManyWithoutUserInput {
+ create: [OrderCreateWithoutUserInput!]
+ connect: [OrderWhereUniqueInput!]
+}
+
+input OrderCreateWithoutUserInput {
+ total: Int!
+ charge: String!
+ items: ItemCreateManyInput
+}
+
+type OrderEdge {
+ node: Order!
+ cursor: String!
+}
+
+enum OrderOrderByInput {
+ id_ASC
+ id_DESC
+ total_ASC
+ total_DESC
+ charge_ASC
+ charge_DESC
+ updatedAt_ASC
+ updatedAt_DESC
+ createdAt_ASC
+ createdAt_DESC
+}
+
+type OrderPreviousValues {
+ id: ID!
+ total: Int!
+ charge: String!
+}
+
+type OrderSubscriptionPayload {
+ mutation: MutationType!
+ node: Order
+ updatedFields: [String!]
+ previousValues: OrderPreviousValues
+}
+
+input OrderSubscriptionWhereInput {
+ AND: [OrderSubscriptionWhereInput!]
+ OR: [OrderSubscriptionWhereInput!]
+ mutation_in: [MutationType!]
+ updatedFields_contains: String
+ updatedFields_contains_every: [String!]
+ updatedFields_contains_some: [String!]
+ node: OrderWhereInput
+}
+
+input OrderUpdateInput {
+ total: Int
+ charge: String
+ items: ItemUpdateManyInput
+ user: UserUpdateOneWithoutOrdersInput
+}
+
+input OrderUpdateManyWithoutUserInput {
+ create: [OrderCreateWithoutUserInput!]
+ connect: [OrderWhereUniqueInput!]
+ disconnect: [OrderWhereUniqueInput!]
+ delete: [OrderWhereUniqueInput!]
+ update: [OrderUpdateWithoutUserInput!]
+ upsert: [OrderUpsertWithoutUserInput!]
+}
+
+input OrderUpdateWithoutUserDataInput {
+ total: Int
+ charge: String
+ items: ItemUpdateManyInput
+}
+
+input OrderUpdateWithoutUserInput {
+ where: OrderWhereUniqueInput!
+ data: OrderUpdateWithoutUserDataInput!
+}
+
+input OrderUpsertWithoutUserInput {
+ where: OrderWhereUniqueInput!
+ update: OrderUpdateWithoutUserDataInput!
+ create: OrderCreateWithoutUserInput!
+}
+
+input OrderWhereInput {
+ AND: [OrderWhereInput!]
+ OR: [OrderWhereInput!]
+ id: ID
+ id_not: ID
+ id_in: [ID!]
+ id_not_in: [ID!]
+ id_lt: ID
+ id_lte: ID
+ id_gt: ID
+ id_gte: ID
+ id_contains: ID
+ id_not_contains: ID
+ id_starts_with: ID
+ id_not_starts_with: ID
+ id_ends_with: ID
+ id_not_ends_with: ID
+ total: Int
+ total_not: Int
+ total_in: [Int!]
+ total_not_in: [Int!]
+ total_lt: Int
+ total_lte: Int
+ total_gt: Int
+ total_gte: Int
+ charge: String
+ charge_not: String
+ charge_in: [String!]
+ charge_not_in: [String!]
+ charge_lt: String
+ charge_lte: String
+ charge_gt: String
+ charge_gte: String
+ charge_contains: String
+ charge_not_contains: String
+ charge_starts_with: String
+ charge_not_starts_with: String
+ charge_ends_with: String
+ charge_not_ends_with: String
+ items_every: ItemWhereInput
+ items_some: ItemWhereInput
+ items_none: ItemWhereInput
+ user: UserWhereInput
+}
+
+input OrderWhereUniqueInput {
+ id: ID
+}
+
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
@@ -523,14 +748,17 @@ type Query {
users(where: UserWhereInput, orderBy: UserOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [User]!
cartItems(where: CartItemWhereInput, orderBy: CartItemOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [CartItem]!
items(where: ItemWhereInput, orderBy: ItemOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [Item]!
+ orders(where: OrderWhereInput, orderBy: OrderOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [Order]!
post(where: PostWhereUniqueInput!): Post
user(where: UserWhereUniqueInput!): User
cartItem(where: CartItemWhereUniqueInput!): CartItem
item(where: ItemWhereUniqueInput!): Item
+ order(where: OrderWhereUniqueInput!): Order
postsConnection(where: PostWhereInput, orderBy: PostOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): PostConnection!
usersConnection(where: UserWhereInput, orderBy: UserOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): UserConnection!
cartItemsConnection(where: CartItemWhereInput, orderBy: CartItemOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): CartItemConnection!
itemsConnection(where: ItemWhereInput, orderBy: ItemOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): ItemConnection!
+ ordersConnection(where: OrderWhereInput, orderBy: OrderOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): OrderConnection!
node(id: ID!): Node
}
@@ -539,6 +767,7 @@ type Subscription {
user(where: UserSubscriptionWhereInput): UserSubscriptionPayload
cartItem(where: CartItemSubscriptionWhereInput): CartItemSubscriptionPayload
item(where: ItemSubscriptionWhereInput): ItemSubscriptionPayload
+ order(where: OrderSubscriptionWhereInput): OrderSubscriptionPayload
}
type UserConnection {
@@ -556,7 +785,18 @@ input UserCreateInput {
resetToken: String
resetTokenExpiry: String
posts: PostCreateManyWithoutAuthorInput
- cart: CartItemCreateManyInput
+ orders: OrderCreateManyWithoutUserInput
+ cart: CartItemCreateManyWithoutUserInput
+}
+
+input UserCreateOneWithoutCartInput {
+ create: UserCreateWithoutCartInput
+ connect: UserWhereUniqueInput
+}
+
+input UserCreateOneWithoutOrdersInput {
+ create: UserCreateWithoutOrdersInput
+ connect: UserWhereUniqueInput
}
input UserCreateOneWithoutPostsInput {
@@ -564,6 +804,30 @@ input UserCreateOneWithoutPostsInput {
connect: UserWhereUniqueInput
}
+input UserCreateWithoutCartInput {
+ email: String!
+ password: String!
+ name: String!
+ website: String
+ age: Int
+ resetToken: String
+ resetTokenExpiry: String
+ posts: PostCreateManyWithoutAuthorInput
+ orders: OrderCreateManyWithoutUserInput
+}
+
+input UserCreateWithoutOrdersInput {
+ email: String!
+ password: String!
+ name: String!
+ website: String
+ age: Int
+ resetToken: String
+ resetTokenExpiry: String
+ posts: PostCreateManyWithoutAuthorInput
+ cart: CartItemCreateManyWithoutUserInput
+}
+
input UserCreateWithoutPostsInput {
email: String!
password: String!
@@ -572,7 +836,8 @@ input UserCreateWithoutPostsInput {
age: Int
resetToken: String
resetTokenExpiry: String
- cart: CartItemCreateManyInput
+ orders: OrderCreateManyWithoutUserInput
+ cart: CartItemCreateManyWithoutUserInput
}
type UserEdge {
@@ -640,7 +905,26 @@ input UserUpdateInput {
resetToken: String
resetTokenExpiry: String
posts: PostUpdateManyWithoutAuthorInput
- cart: CartItemUpdateManyInput
+ orders: OrderUpdateManyWithoutUserInput
+ cart: CartItemUpdateManyWithoutUserInput
+}
+
+input UserUpdateOneWithoutCartInput {
+ create: UserCreateWithoutCartInput
+ connect: UserWhereUniqueInput
+ disconnect: UserWhereUniqueInput
+ delete: UserWhereUniqueInput
+ update: UserUpdateWithoutCartInput
+ upsert: UserUpsertWithoutCartInput
+}
+
+input UserUpdateOneWithoutOrdersInput {
+ create: UserCreateWithoutOrdersInput
+ connect: UserWhereUniqueInput
+ disconnect: UserWhereUniqueInput
+ delete: UserWhereUniqueInput
+ update: UserUpdateWithoutOrdersInput
+ upsert: UserUpsertWithoutOrdersInput
}
input UserUpdateOneWithoutPostsInput {
@@ -652,6 +936,40 @@ input UserUpdateOneWithoutPostsInput {
upsert: UserUpsertWithoutPostsInput
}
+input UserUpdateWithoutCartDataInput {
+ email: String
+ password: String
+ name: String
+ website: String
+ age: Int
+ resetToken: String
+ resetTokenExpiry: String
+ posts: PostUpdateManyWithoutAuthorInput
+ orders: OrderUpdateManyWithoutUserInput
+}
+
+input UserUpdateWithoutCartInput {
+ where: UserWhereUniqueInput!
+ data: UserUpdateWithoutCartDataInput!
+}
+
+input UserUpdateWithoutOrdersDataInput {
+ email: String
+ password: String
+ name: String
+ website: String
+ age: Int
+ resetToken: String
+ resetTokenExpiry: String
+ posts: PostUpdateManyWithoutAuthorInput
+ cart: CartItemUpdateManyWithoutUserInput
+}
+
+input UserUpdateWithoutOrdersInput {
+ where: UserWhereUniqueInput!
+ data: UserUpdateWithoutOrdersDataInput!
+}
+
input UserUpdateWithoutPostsDataInput {
email: String
password: String
@@ -660,7 +978,8 @@ input UserUpdateWithoutPostsDataInput {
age: Int
resetToken: String
resetTokenExpiry: String
- cart: CartItemUpdateManyInput
+ orders: OrderUpdateManyWithoutUserInput
+ cart: CartItemUpdateManyWithoutUserInput
}
input UserUpdateWithoutPostsInput {
@@ -668,6 +987,18 @@ input UserUpdateWithoutPostsInput {
data: UserUpdateWithoutPostsDataInput!
}
+input UserUpsertWithoutCartInput {
+ where: UserWhereUniqueInput!
+ update: UserUpdateWithoutCartDataInput!
+ create: UserCreateWithoutCartInput!
+}
+
+input UserUpsertWithoutOrdersInput {
+ where: UserWhereUniqueInput!
+ update: UserUpdateWithoutOrdersDataInput!
+ create: UserCreateWithoutOrdersInput!
+}
+
input UserUpsertWithoutPostsInput {
where: UserWhereUniqueInput!
update: UserUpdateWithoutPostsDataInput!
@@ -786,6 +1117,9 @@ input UserWhereInput {
posts_every: PostWhereInput
posts_some: PostWhereInput
posts_none: PostWhereInput
+ orders_every: OrderWhereInput
+ orders_some: OrderWhereInput
+ orders_none: OrderWhereInput
cart_every: CartItemWhereInput
cart_some: CartItemWhereInput
cart_none: CartItemWhereInput
diff --git a/backend/src/index.js b/backend/src/index.js
index 359456f..22cc9ef 100644
--- a/backend/src/index.js
+++ b/backend/src/index.js
@@ -19,7 +19,7 @@ const server = new GraphQLServer({
typeDefs: 'src/generated/prisma.graphql',
endpoint: process.env.PRISMA_ENDPOINT, // the endpoint of the Prisma DB service (value is set in .env)
secret: process.env.PRISMA_SECRET, // taken from database/prisma.yml (value is set in .env)
- debug: true, // log all GraphQL queries & mutations
+ debug: false, // log all GraphQL queries & mutations
}),
}),
});
diff --git a/backend/src/resolvers/Mutation.js b/backend/src/resolvers/Mutation.js
index 003a563..1c071fe 100644
--- a/backend/src/resolvers/Mutation.js
+++ b/backend/src/resolvers/Mutation.js
@@ -5,6 +5,7 @@ const { getUserId, Context } = require('../utils');
const { randomBytes } = require('crypto');
const { promisify } = require('util');
const mail = require('../mail');
+const stripe = require('../stripe');
const mutations = {
// Signup Mutations
@@ -39,6 +40,8 @@ const mutations = {
// Creation of Post Mutations
async createItem(parent, args, ctx, info) {
+ // TODO - they should be signed in when creating an item for sale
+ // TODO: The user should be saved to the item so they can manage it
return ctx.db.mutation.createItem({ data: { ...args } }, info);
},
@@ -202,52 +205,49 @@ const mutations = {
Add to cart
*/
async addToCart(parent, args, ctx, info) {
- console.l('Add to cart calle');
+ console.l('Add to cart called');
const userId = getUserId(ctx);
- // get the current user
- const currentUser = await ctx.db.query.user(
- {
- where: { id: userId },
- },
- '{ cart { id, quantity item { id } }}'
- );
+ if (!userId) {
+ throw new Error('You must be signed in to add to cart!');
+ }
- // find out if the user currently has this item in their cart
- const existingCartItemIndex = currentUser.cart.findIndex(cartItem => cartItem.item.id === args.id);
+ // 1. Check if there is a CartItem for this user and item already
+ const [existingCartItem] = await ctx.db.query.cartItems({
+ where: {
+ user: { id: userId },
+ item: { id: args.id },
+ },
+ });
- if (existingCartItemIndex >= 0) {
- console.l('======This item already exists in the cart============');
- const cartItem = currentUser.cart[existingCartItemIndex];
- return ctx.db.mutation.updateCartItem({
- where: { id: cartItem.id },
- data: {
- quantity: cartItem.quantity + 1,
+ if (existingCartItem) {
+ return ctx.db.mutation.updateCartItem(
+ {
+ where: { id: existingCartItem.id },
+ data: { quantity: existingCartItem.quantity + 1 },
},
- });
+ info
+ );
}
- const res = await ctx.db.mutation.updateUser({
- where: { id: userId },
- data: {
- cart: {
- create: [
- {
- item: {
- connect: {
- id: args.id,
- },
- },
- quantity: 1,
+ // Otherwise create a new cartItem
+ return ctx.db.mutation.createCartItem(
+ {
+ data: {
+ user: {
+ connect: {
+ id: userId,
},
- ],
+ },
+ item: {
+ connect: { id: args.id },
+ },
},
},
- });
- console.log(res);
- // return res;
+ info
+ );
},
+ // delete that cart item
async removeFromCart(parent, args, ctx, info) {
- // delete that cart item
return ctx.db.mutation.deleteCartItem({
where: { id: args.id },
});
@@ -260,6 +260,57 @@ const mutations = {
// console.log(cartItem);
// return cartItem;
},
+ async createOrder(parent, args, ctx, info) {
+ const userId = getUserId(ctx);
+ const user = await ctx.db.query.user(
+ { where: { id: userId } },
+ '{ id, name, email, cart { id, quantity, item { price, id } }}'
+ );
+ console.log('-------USER-----');
+ console.log(user);
+ console.log('-------USER-----');
+ // 1. Recalculate the total for the price
+ const amount = user.cart.reduce((tally, cartItem) => tally + cartItem.item.price * cartItem.quantity, 0);
+ // TODO Error Handling
+ // 2.1 Create a Stripe Customer
+ const customer = await stripe.customers.create({
+ email: user.email,
+ });
+ console.log(customer);
+ // 2.3 Charge the stripe token
+ const charge = await stripe.charges.create({
+ amount,
+ currency: 'usd',
+ source: args.token,
+ });
+
+ // Save this order to the database
+ const items = user.cart.map(cartItem => ({ id: cartItem.item.id }));
+ console.l('---Gonna connect these items: ----');
+ console.log(items);
+ console.l('---Gonna connect these items: ----');
+ const order = await ctx.db.mutation.createOrder({
+ data: {
+ total: charge.amount,
+ charge: charge.id,
+ items: {
+ connect: items,
+ },
+ user: {
+ connect: {
+ id: user.id,
+ },
+ },
+ },
+ });
+ console.l('--------ORDER-------------');
+ console.log(order);
+ console.l('--------ORDER-------------');
+ return order;
+ // 4. Send an email with their order
+ // 5. Send the order back
+ // 6. Clean up, clear the users cart adn send back { user, order }
+ },
};
module.exports = mutations;
diff --git a/backend/src/resolvers/Query.js b/backend/src/resolvers/Query.js
index e6ea299..8649e43 100644
--- a/backend/src/resolvers/Query.js
+++ b/backend/src/resolvers/Query.js
@@ -31,6 +31,7 @@ const Query = {
},
me(parent, args, ctx, info) {
+ console.l('me!');
const Authorization = ctx.request.get('Authorization');
if (!Authorization || Authorization === 'null') {
console.log('Authorization is null');
@@ -39,6 +40,12 @@ const Query = {
const id = getUserId(ctx);
return ctx.db.query.user({ where: { id } }, info);
},
+ async orders(parent, args, ctx, info) {
+ console.l('--------------------FETCHING ORDERS---------------------');
+ console.log(args);
+ return ctx.db.query.orders({}, info);
+ console.l('--------------------FETCHING ORDERS---------------------');
+ },
};
module.exports = Query;
diff --git a/backend/src/schema.graphql b/backend/src/schema.graphql
index b30b096..84844c6 100644
--- a/backend/src/schema.graphql
+++ b/backend/src/schema.graphql
@@ -1,4 +1,4 @@
-# import CartItem, ItemWhereInput, ItemOrderByInput, allItems, Post, Item, ItemCreateInput, ItemOrderByInput, ItemWhereInput from './generated/prisma.graphql'
+# import Order, CartItem, ItemWhereInput, ItemOrderByInput, allItems, Post, Item, ItemCreateInput, ItemOrderByInput, ItemWhereInput, Query.order, Query.orders from './generated/prisma.graphql'
type Query {
feed: [Post!]!
@@ -33,11 +33,12 @@ type Mutation {
publish(id: ID!): Post!
deletePost(id: ID!): Post!
updatePost(id: ID!, title: String, text: String): Post!
- createItem(title: String, description: String, price: Int): Item!
+ createItem(title: String, description: String, price: Int, image: String): Item!
deleteItem(id: ID!): Item!
updateItem(id: ID!, title: String, description: String, price: Int): Item!
addToCart(id: ID!): CartItem
removeFromCart(id: ID!): CartItem
+ createOrder(token: String!): Order!
}
type AuthPayload {
@@ -52,5 +53,6 @@ type User {
name: String!
posts: [Post!]!
age: Int
+ orders: [Order!]!
cart: [CartItem!]!
}
diff --git a/backend/src/stripe.js b/backend/src/stripe.js
new file mode 100644
index 0000000..c6be8ca
--- /dev/null
+++ b/backend/src/stripe.js
@@ -0,0 +1 @@
+module.exports = require('stripe')(process.env.STRIPE_SECRET);
diff --git a/frontend/components/AddToCart.js b/frontend/components/AddToCart.js
index 4522137..5a11513 100644
--- a/frontend/components/AddToCart.js
+++ b/frontend/components/AddToCart.js
@@ -38,7 +38,7 @@ const JumpImg = styled.img`
class AddToCart extends Component {
static propTypes = {
- currentUser: PropTypes.object,
+ currentUser: PropTypes.object.isRequired,
};
componentDidMount() {
@@ -52,8 +52,7 @@ class AddToCart extends Component {
id: this.props.id,
},
});
- console.log({ realResponse: res });
- this.props.currentUser.refetch();
+ // this.props.currentUser.refetch();
};
render() {
diff --git a/frontend/components/Cart.js b/frontend/components/Cart.js
index 4153600..46a8941 100644
--- a/frontend/components/Cart.js
+++ b/frontend/components/Cart.js
@@ -3,6 +3,8 @@ import { graphql, compose } from 'react-apollo';
import styled from 'styled-components';
import { CURRENT_USER_QUERY } from '../queries';
import RemoveFromCart from './RemoveFromCart';
+import TakeMyMoney from './TakeMyMoney';
+import { formatMoney } from '../lib/formatMoney';
const CartStyles = styled.div`
background: white;
@@ -43,6 +45,11 @@ class Cart extends Component {
</li>
))}
</ul>
+
+ <TakeMyMoney>
+ <hr />
+ <button>Checkout!!!</button>
+ </TakeMyMoney>
{/* There
{user.cart.length === 1 ? ' is ' : 'are '}
<ChaChing amount={user.cart.length} />
diff --git a/frontend/components/CreateItem.js b/frontend/components/CreateItem.js
index e4aa77c..47e98ec 100644
--- a/frontend/components/CreateItem.js
+++ b/frontend/components/CreateItem.js
@@ -24,24 +24,27 @@ class CreateLink extends Component {
uploadFile = async e => {
this.setState({ loading: true });
+
const files = e.currentTarget.files;
const data = new FormData();
- data.append('data', files[0]);
+ data.append('file', files[0]);
+ data.append('upload_preset', 'sickfits');
// use the file endpoint
- const res = await fetch(fileEndpoint, {
+ const res = await fetch('https://api.cloudinary.com/v1_1/wesbos/image/upload', {
method: 'POST',
body: data,
});
const file = await res.json();
- this.setState({ image: file.id, loading: false });
+ console.log(file);
+ this.setState({ image: file.secure_url, loading: false });
};
_createLink = async e => {
e.preventDefault();
// pull the values from state
- const { description, title, price } = this.state;
+ const { description, title, price, image } = this.state;
// create a mutation
// TODO: handle any errors
// turn loading on
@@ -53,6 +56,7 @@ class CreateLink extends Component {
variables: {
description,
title,
+ image,
price: parseInt(price),
},
});
@@ -73,6 +77,7 @@ class CreateLink extends Component {
<p>
Image
<input onChange={this.uploadFile} type="file" accept=".png, .jpg, .jpeg" />
+ {this.state.image ? <img src={this.state.image} width="100" alt={this.state.title} /> : null}
</p>
<p>
Title
@@ -105,9 +110,11 @@ class CreateLink extends Component {
);
}
}
+
+// TODO: The create item should only be allowed
+
// When we submit this mutation, we need to update our store - we have a few ways to do that:
// One - we can go nucular and run refetchQueries() which will just go get everything - this is easy, but at the cost of efficiency.
-
export default graphql(CREATE_ITEM_MUTATION, {
name: 'createItemMutation',
options: {
diff --git a/frontend/components/Header.js b/frontend/components/Header.js
index 774bd0b..aa3c3d8 100644
--- a/frontend/components/Header.js
+++ b/frontend/components/Header.js
@@ -1,6 +1,6 @@
import { Component } from 'react';
import { graphql, compose } from 'react-apollo';
-import { CURRENT_USER_QUERY } from '../queries';
+import { userEnhancer } from '../enhancers/enhancers';
import Cart from './Cart';
import Login from './LoginAuth0';
import Search from './Search';
@@ -21,6 +21,7 @@ class Header extends Component {
// this.props.currentUserQuery.refetch();
// This fetches the new data, and populates the user via props
// setTimeout(this.props.currentUserQuery.refetch, 1);
+ console.log(this.props.currentUser);
}
render() {
@@ -35,6 +36,5 @@ class Header extends Component {
}
}
-const userEnhancer = graphql(CURRENT_USER_QUERY, { name: 'currentUser' });
export default compose(userEnhancer)(Header);
// export default Header;
diff --git a/frontend/components/Item.js b/frontend/components/Item.js
index 2a0fc69..42a6c87 100644
--- a/frontend/components/Item.js
+++ b/frontend/components/Item.js
@@ -5,7 +5,6 @@ import { compose } from 'react-apollo';
import { Link } from '../routes';
import AddToCart from './AddToCart';
import makeImage from '../lib/image';
-import TakeMyMoney from './TakeMyMoney';
import formatMoney from '../lib/formatMoney';
import { removeItemMutation } from '../enhancers/enhancers';
@@ -25,7 +24,7 @@ class ItemComponent extends React.Component {
const item = this.props.item;
return (
<Item key={item.id}>
- {item.image ? <img key={item.image.secret} src={makeImage(item.image)} alt={item.title} /> : null}
+ {item.image ? <img src={item.image} alt={item.title} /> : null}
<h3>
<Link
route="item"
@@ -48,17 +47,6 @@ class ItemComponent extends React.Component {
>
<a>Edit ✏️</a>
</Link>
-
- <TakeMyMoney
- id={item.id}
- amount={item.price}
- name={item.title} // the pop-in header title
- description={item.description} // the pop-in header subtitle
- image={makeImage(item.image)}
- >
- <button>Buy for {formatMoney(item.price)}</button>
- </TakeMyMoney>
-
<AddToCart id={item.id} />
<button onClick={this.removeItem}>&times; Delete item</button>
</Item>
diff --git a/frontend/components/Page.js b/frontend/components/Page.js
index 291bdcd..93cd7c9 100644
--- a/frontend/components/Page.js
+++ b/frontend/components/Page.js
@@ -16,7 +16,7 @@ class ErrorBoundary extends Component {
}
render() {
if (this.state.error) {
- return <p>Shit! AN error!</p>;
+ return <p>Shit! AN error! {this.state.error.message}</p>;
}
return this.props.children;
}
diff --git a/frontend/components/SingleItem.js b/frontend/components/SingleItem.js
index 9d18074..47dbd0e 100644
--- a/frontend/components/SingleItem.js
+++ b/frontend/components/SingleItem.js
@@ -1,7 +1,6 @@
import { graphql, compose } from 'react-apollo';
import { SINGLE_ITEM_QUERY } from '../queries';
import { singleItemEnhancer } from '../enhancers/enhancers';
-import makeImage from '../lib/image';
const SingleItem = props => {
if (props.loading || !props.item) return <p>Loading...</p>;
@@ -10,7 +9,7 @@ const SingleItem = props => {
const item = props.findItem.items[0];
return (
<div>
- <img src={makeImage(item.image)} alt={item.title} />
+ <img src={item.image} alt={item.title} />
<h2>Viewing {item.title}</h2>
<p>{item.description}</p>
</div>
diff --git a/frontend/components/TakeMyMoney.js b/frontend/components/TakeMyMoney.js
index 042e9c8..30a19dc 100644
--- a/frontend/components/TakeMyMoney.js
+++ b/frontend/components/TakeMyMoney.js
@@ -2,35 +2,47 @@ import { Component } from 'react';
import StripeCheckout from 'react-stripe-checkout';
import { graphql, compose } from 'react-apollo';
import { CREATE_ORDER_MUTATION, CURRENT_USER_QUERY } from '../queries';
+import formatMoney from '../lib/formatMoney';
class TakeMyMoney extends Component {
onToken = async res => {
console.log('We got a toooken!');
- const token = res.id;
- const userId = this.props.currentUserQuery.user.id;
- const itemId = this.props.id;
- console.log(`Going to make a purchase with ${token}`);
- console.log(`THe person that bought this was ${userId}`);
- console.log(`The item id is ${itemId}`);
- const charge = await this.props.createOrder({ variables: { token, userId, itemId } });
- alert(`Back from the charge! ${charge.id}`);
- console.log(charge);
+ console.log(res);
+ const order = await this.props.createOrder({
+ variables: {
+ token: res.id,
+ },
+ });
+ console.log(order);
+ // const token = res.id;
+ // const userId = this.props.currentUserQuery.user.id;
+ // const itemId = this.props.id;
+ // console.log(`Going to make a purchase with ${token}`);
+ // console.log(`THe person that bought this was ${userId}`);
+ // console.log(`The item id is ${itemId}`);
+ // const charge = await this.props.createOrder({ variables: { token, userId, itemId } });
+ // alert(`Back from the charge! ${charge.id}`);
+ // console.log(charge);
};
+
render() {
- // const user = this.props.currentUserQuery.user || {};
- // const email = user.email || '';
- const email = 'wesbos@gmail.com';
+ const { me } = this.props.currentUserQuery;
+ if (!me.cart.length) return null;
+ const total = me.cart.reduce((tally, cartItem) => tally + cartItem.item.price * cartItem.quantity, 0);
+ const totalItems = me.cart.reduce((tally, cartItem) => tally + cartItem.quantity, 0);
return (
<div>
<StripeCheckout
- amount={this.props.amount}
- name={this.props.name}
- description={this.props.description}
+ amount={total}
+ name="Sick Fits Haul"
+ description={`Order of ${totalItems} Items From Sick Fits`}
+ image={me.cart[0].item.image}
token={this.onToken}
stripeKey="pk_lclTtThFp8CnO3QtEZSd8HA9mFUps"
currency="USD"
- email={email}
+ email={me.email}
>
+ {formatMoney(total)}
{this.props.children}
</StripeCheckout>
</div>
@@ -40,4 +52,4 @@ class TakeMyMoney extends Component {
const userEnhancer = graphql(CURRENT_USER_QUERY, { name: 'currentUserQuery' });
const createOrderEnhancer = graphql(CREATE_ORDER_MUTATION, { name: 'createOrder' });
-export default compose(createOrderEnhancer)(TakeMyMoney);
+export default compose(userEnhancer, createOrderEnhancer)(TakeMyMoney);
diff --git a/frontend/enhancers/enhancers.js b/frontend/enhancers/enhancers.js
index f7021c3..1abdd78 100644
--- a/frontend/enhancers/enhancers.js
+++ b/frontend/enhancers/enhancers.js
@@ -59,7 +59,16 @@ export const addtoCartEnhancer = graphql(ADD_TO_CART_MUTATION, {
name: 'addToCart',
options: {
update: (proxy, payload) => {
- console.log('=----asdf-asd-fas-df-asdf-sa-df');
+ const newCartItem = payload.data.addToCart;
+ const data = proxy.readQuery({ query: CURRENT_USER_QUERY });
+ const existingIndex = data.me.cart.findIndex(cartItem => cartItem.id === newCartItem.id);
+ if (existingIndex >= 0) {
+ // already in cache, just replace it
+ data.me.cart = [...data.me.cart.slice(0, existingIndex), newCartItem, ...data.me.cart.slice(existingIndex + 1)];
+ } else {
+ data.me.cart = [...data.me.cart, newCartItem];
+ }
+ proxy.writeQuery({ query: CURRENT_USER_QUERY, data });
},
},
});
diff --git a/frontend/queries/fragments.js b/frontend/queries/fragments.js
index 1708d78..9a2d641 100644
--- a/frontend/queries/fragments.js
+++ b/frontend/queries/fragments.js
@@ -6,5 +6,6 @@ export const itemDetails = gql`
title
price
description
+ image
}
`;
diff --git a/frontend/queries/index.js b/frontend/queries/index.js
index e61a9d5..365c6f4 100644
--- a/frontend/queries/index.js
+++ b/frontend/queries/index.js
@@ -3,8 +3,8 @@ import { itemDetails } from './fragments';
export const CREATE_ITEM_MUTATION = gql`
${itemDetails}
- mutation createItem($description: String!, $title: String!, $price: Int!) {
- createItem(description: $description, title: $title, price: $price) {
+ mutation createItem($description: String!, $title: String!, $price: Int!, $image: String) {
+ createItem(description: $description, title: $title, price: $price, image: $image) {
...itemDetails
}
}
@@ -58,20 +58,14 @@ export const RESET_MUTATION = gql`
`;
export const CREATE_ORDER_MUTATION = gql`
- mutation CreateOrderMutation($token: String!, $userId: ID!, $itemId: ID!) {
- createOrder(token: $token, userId: $userId, itemId: $itemId) {
+ mutation CreateOrderMutation($token: String!) {
+ createOrder(token: $token) {
id
- token
charge
- amount
- item {
+ total
+ items {
id
title
- description
- }
- user {
- id
- email
}
}
}
@@ -85,8 +79,6 @@ export const ALL_ITEMS_QUERY = gql`
pageInfo {
hasNextPage
hasPreviousPage
- startCursor
- endCursor
}
aggregate {
count
@@ -161,6 +153,11 @@ export const CURRENT_USER_QUERY = gql`
id
email
name
+ orders {
+ charge
+ id
+ total
+ }
cart {
id
quantity
@@ -200,6 +197,14 @@ export const ADD_TO_CART_MUTATION = gql`
mutation addToCart($id: ID!) {
addToCart(id: $id) {
id
+ quantity
+ item {
+ id
+ price
+ description
+ image
+ title
+ }
}
}
`;