From b1600adec47a04f60e1da07d94a3ed3906ff5aee Mon Sep 17 00:00:00 2001 From: Wes Bos Date: Thu, 14 Jun 2018 16:44:21 -0400 Subject: starter files --- .../frontend/components/AddToCart.js | 68 ++++++++++ finished-application/frontend/components/Cart.js | 74 +++++++++++ .../frontend/components/CartCount.js | 64 +++++++++ .../frontend/components/CartItem.js | 53 ++++++++ .../frontend/components/CreateItem.js | 148 +++++++++++++++++++++ .../frontend/components/DeleteItem.js | 71 ++++++++++ finished-application/frontend/components/Dump.js | 19 +++ .../frontend/components/EditUser.js | 81 +++++++++++ .../frontend/components/ErrorMessage.js | 51 +++++++ finished-application/frontend/components/Header.js | 71 ++++++++++ finished-application/frontend/components/Item.js | 53 ++++++++ finished-application/frontend/components/Items.js | 74 +++++++++++ .../frontend/components/LoadingItem.js | 22 +++ finished-application/frontend/components/Meta.js | 16 +++ finished-application/frontend/components/Nav.js | 59 ++++++++ finished-application/frontend/components/Order.js | 98 ++++++++++++++ .../frontend/components/OrderList.js | 93 +++++++++++++ finished-application/frontend/components/Page.js | 67 ++++++++++ .../frontend/components/Pagination.js | 97 ++++++++++++++ .../frontend/components/Permissions.js | 133 ++++++++++++++++++ .../frontend/components/PleaseSignIn.js | 46 +++++++ .../frontend/components/RemoveFromCart.js | 57 ++++++++ finished-application/frontend/components/Reset.js | 85 ++++++++++++ .../frontend/components/ResetRequest.js | 55 ++++++++ finished-application/frontend/components/Search.js | 143 ++++++++++++++++++++ finished-application/frontend/components/Signin.js | 79 +++++++++++ .../frontend/components/Signout.js | 25 ++++ finished-application/frontend/components/Signup.js | 93 +++++++++++++ .../frontend/components/SingleItem.js | 77 +++++++++++ .../frontend/components/TakeMyMoney.js | 83 ++++++++++++ .../frontend/components/UpdateItem.js | 105 +++++++++++++++ finished-application/frontend/components/User.js | 45 +++++++ .../frontend/components/styles/CartStyles.js | 47 +++++++ .../frontend/components/styles/CloseButton.js | 13 ++ .../frontend/components/styles/Form.js | 71 ++++++++++ .../frontend/components/styles/ItemStyles.js | 39 ++++++ .../frontend/components/styles/NavStyles.js | 64 +++++++++ .../frontend/components/styles/OrderItemStyles.js | 44 ++++++ .../frontend/components/styles/OrderStyles.js | 38 ++++++ .../frontend/components/styles/PriceTag.js | 17 +++ .../frontend/components/styles/SickButton.js | 20 +++ .../frontend/components/styles/Supreme.js | 13 ++ .../frontend/components/styles/Table.js | 31 +++++ .../frontend/components/styles/Title.js | 20 +++ 44 files changed, 2722 insertions(+) create mode 100644 finished-application/frontend/components/AddToCart.js create mode 100644 finished-application/frontend/components/Cart.js create mode 100644 finished-application/frontend/components/CartCount.js create mode 100644 finished-application/frontend/components/CartItem.js create mode 100644 finished-application/frontend/components/CreateItem.js create mode 100644 finished-application/frontend/components/DeleteItem.js create mode 100644 finished-application/frontend/components/Dump.js create mode 100644 finished-application/frontend/components/EditUser.js create mode 100644 finished-application/frontend/components/ErrorMessage.js create mode 100644 finished-application/frontend/components/Header.js create mode 100644 finished-application/frontend/components/Item.js create mode 100644 finished-application/frontend/components/Items.js create mode 100644 finished-application/frontend/components/LoadingItem.js create mode 100644 finished-application/frontend/components/Meta.js create mode 100644 finished-application/frontend/components/Nav.js create mode 100644 finished-application/frontend/components/Order.js create mode 100644 finished-application/frontend/components/OrderList.js create mode 100644 finished-application/frontend/components/Page.js create mode 100644 finished-application/frontend/components/Pagination.js create mode 100644 finished-application/frontend/components/Permissions.js create mode 100644 finished-application/frontend/components/PleaseSignIn.js create mode 100644 finished-application/frontend/components/RemoveFromCart.js create mode 100644 finished-application/frontend/components/Reset.js create mode 100644 finished-application/frontend/components/ResetRequest.js create mode 100644 finished-application/frontend/components/Search.js create mode 100644 finished-application/frontend/components/Signin.js create mode 100644 finished-application/frontend/components/Signout.js create mode 100644 finished-application/frontend/components/Signup.js create mode 100644 finished-application/frontend/components/SingleItem.js create mode 100644 finished-application/frontend/components/TakeMyMoney.js create mode 100644 finished-application/frontend/components/UpdateItem.js create mode 100644 finished-application/frontend/components/User.js create mode 100644 finished-application/frontend/components/styles/CartStyles.js create mode 100644 finished-application/frontend/components/styles/CloseButton.js create mode 100644 finished-application/frontend/components/styles/Form.js create mode 100644 finished-application/frontend/components/styles/ItemStyles.js create mode 100644 finished-application/frontend/components/styles/NavStyles.js create mode 100644 finished-application/frontend/components/styles/OrderItemStyles.js create mode 100644 finished-application/frontend/components/styles/OrderStyles.js create mode 100644 finished-application/frontend/components/styles/PriceTag.js create mode 100644 finished-application/frontend/components/styles/SickButton.js create mode 100644 finished-application/frontend/components/styles/Supreme.js create mode 100644 finished-application/frontend/components/styles/Table.js create mode 100644 finished-application/frontend/components/styles/Title.js (limited to 'finished-application/frontend/components') diff --git a/finished-application/frontend/components/AddToCart.js b/finished-application/frontend/components/AddToCart.js new file mode 100644 index 0000000..03242a4 --- /dev/null +++ b/finished-application/frontend/components/AddToCart.js @@ -0,0 +1,68 @@ +import { Component } from 'react'; +import { Mutation } from 'react-apollo'; +import PropTypes from 'prop-types'; +import gql from 'graphql-tag'; +import User, { CURRENT_USER_QUERY } from './User'; + +const ADD_TO_CART_MUTATION = gql` + mutation addToCart($id: ID!) { + addToCart(id: $id) { + id + quantity + item { + id + price + description + image + title + } + } + } +`; + +class AddToCart extends Component { + static propTypes = { + id: PropTypes.string.isRequired, + }; + + update = (cache, payload) => { + const newCartItem = payload.data.addToCart; + const data = cache.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]; + } + cache.writeQuery({ query: CURRENT_USER_QUERY, data }); + }; + + render() { + const { id } = this.props; + return ( + + {({ data: { me }, loading }) => { + if (!me || loading) return null; + return ( + + {(addToCart, { loading }) => ( + + )} + + ); + }} + + ); + } +} + +export default AddToCart; +export { ADD_TO_CART_MUTATION }; diff --git a/finished-application/frontend/components/Cart.js b/finished-application/frontend/components/Cart.js new file mode 100644 index 0000000..7e8a9c5 --- /dev/null +++ b/finished-application/frontend/components/Cart.js @@ -0,0 +1,74 @@ +import React from 'react'; +import { Mutation, Query } from 'react-apollo'; +import { adopt } from 'react-adopt'; +import gql from 'graphql-tag'; +import TakeMyMoney from './TakeMyMoney'; +import formatMoney from '../lib/formatMoney'; +import CartItem from './CartItem'; +import { CURRENT_USER_QUERY } from './User'; +import calcTotalPrice from '../lib/calcTotalPrice'; +import Error from './ErrorMessage'; +import CartStyles from './styles/CartStyles'; +import Supreme from './styles/Supreme'; +import CloseButton from './styles/CloseButton'; +import SickButton from './styles/SickButton'; + +const LOCAL_STATE_QUERY = gql` + query { + cartOpen @client + } +`; + +const TOGGLE_CART_MUTATION = gql` + mutation { + toggleCart @client + } +`; + +const Composed = adopt({ + toggleCart: ({ render }) => ( + + {(mutate, result) => render({ mutate, result })} + + ), + localState: ({ render }) => , + currentUser: ({ render }) => ( + + ), +}); + +const Cart = () => ( + + {({ toggleCart, localState, currentUser }) => { + const { data: { me }, error, loading } = currentUser; + if (loading) return

Loading...

; + if (error) return ; + if (!me) return null; + return ( + +
+ + × + + + {me.name}'s Cart. +

+ You have {me.cart.length} item{me.cart.length === 1 ? '' : 's'} in your cart. +

+
+ +
    {me.cart.map(cartItem => )}
+
+

{formatMoney(calcTotalPrice(me.cart))}

+ + Checkout + +
+
+ ); + }} +
+); + +export default Cart; +export { LOCAL_STATE_QUERY, TOGGLE_CART_MUTATION }; diff --git a/finished-application/frontend/components/CartCount.js b/finished-application/frontend/components/CartCount.js new file mode 100644 index 0000000..ae51f20 --- /dev/null +++ b/finished-application/frontend/components/CartCount.js @@ -0,0 +1,64 @@ +import styled from 'styled-components'; +import { TransitionGroup, CSSTransition } from 'react-transition-group'; +import PropTypes from 'prop-types'; + +const Dot = styled.div` + background: ${props => props.theme.red}; + color: white; + border-radius: 50%; + padding: 0.5rem; + line-height: 2rem; + min-width: 3rem; + margin-left: 1rem; + font-weight: 100; + font-feature-settings: 'tnum'; + font-variant-numeric: tabular-nums; +`; + +const AnimationStyles = styled.span` + position: relative; + .count { + display: block; + position: relative; + transition: all 0.4s; + backface-visibility: hidden; + } + .count-enter { + transform: rotateX(0.5turn); + } + + .count-enter-active { + transform: rotateX(0); + } + + .count-exit { + top: 0; + position: absolute; + transform: rotateX(0); + } + + .count-exit-active { + transform: rotateX(0.5turn); + } +`; + +const CartCount = ({ count }) => ( + + + + {count} + + + +); + +CartCount.propTypes = { + count: PropTypes.number.isRequired, +}; +export default CartCount; diff --git a/finished-application/frontend/components/CartItem.js b/finished-application/frontend/components/CartItem.js new file mode 100644 index 0000000..6c6d575 --- /dev/null +++ b/finished-application/frontend/components/CartItem.js @@ -0,0 +1,53 @@ +import styled from 'styled-components'; +import PropTypes from 'prop-types'; +import formatMoney from '../lib/formatMoney'; +import RemoveFromCart from './RemoveFromCart'; + +const CartItemStyles = styled.li` + padding: 1rem 0; + border-bottom: 1px solid ${props => props.theme.lightgrey}; + display: grid; + align-items: center; + grid-template-columns: auto 1fr auto; + img { + margin-right: 10px; + } + h3 { + margin: 0; + } + p { + margin: 0; + } +`; + +const CartItem = ({ cartItem }) => { + if (!cartItem.item) + return ( + +

Ack! That Item is gone!

+ +
+ ); + return ( + + {cartItem.item.title} +
+

{cartItem.item.title}

+

+ {formatMoney(cartItem.quantity * cartItem.item.price)} + {' β€” '} + + {cartItem.quantity} × {formatMoney(cartItem.item.price)} each + +

+
+ +
+ ); +}; + +CartItem.propTypes = { + cartItem: PropTypes.object.isRequired, +}; + +export default CartItem; diff --git a/finished-application/frontend/components/CreateItem.js b/finished-application/frontend/components/CreateItem.js new file mode 100644 index 0000000..503b3cb --- /dev/null +++ b/finished-application/frontend/components/CreateItem.js @@ -0,0 +1,148 @@ +import React, { Component } from 'react'; +import { Mutation } from 'react-apollo'; +import Router from 'next/router'; +import wait from 'waait'; +import gql from 'graphql-tag'; +import Error from './ErrorMessage'; +import Form from './styles/Form'; +import formatMoney from '../lib/formatMoney'; +import { ALL_ITEMS_QUERY } from './Items'; +import { PAGINATION_QUERY } from './Pagination'; + +const CREATE_ITEM_MUTATION = gql` + mutation CREATE_ITEM_MUTATION( + $description: String! + $title: String! + $price: Int! + $image: String + $largeImage: String + ) { + createItem( + description: $description + title: $title + price: $price + image: $image + largeImage: $largeImage + ) { + id + } + } +`; + +class CreateItem extends Component { + state = { + title: '', + description: '', + image: '', + largeImage: '', + price: 0, + }; + + handleChange = e => { + const { name, value, type } = e.target; + const val = type === 'number' ? parseFloat(value) : value; + this.setState({ [name]: val }); + }; + + uploadFile = async e => { + this.setState({ loading: true }); + const files = e.currentTarget.files; + const data = new FormData(); + data.append('file', files[0]); + data.append('upload_preset', 'sickfits'); + + // use the file endpoint + 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.secure_url, + largeImage: file.eager[0].secure_url, + loading: false, + }); + }; + + render() { + return ( + + {(createItem, { loading, error }) => ( +
{ + e.preventDefault(); + const { data: { createItem: item } } = await createItem(); + // we wait 0 ms so it puts the router push at the end of the call stack. This ensures that refetchQueries runs before we unmount the component :) + await wait(); + Router.push({ + pathname: `/item`, + query: { id: item.id }, + }); + }} + > +

Sell an Item.

+ +
+ + + +