From 1450e579c67e3d969cb099dfe6c1cefd1e313fb5 Mon Sep 17 00:00:00 2001 From: Wes Bos Date: Fri, 15 Sep 2017 14:26:13 -0400 Subject: yep --- components/AddToCart.js | 39 +++++++++++++++ components/Cart.js | 41 +++++++++++++++ components/CartList.js | 74 ++++++++++++++++++++++++++++ components/Header.js | 10 ++-- components/Item.js | 33 +++++++++++++ components/Items.js | 126 ++++++++++++++++++++++++++++++++--------------- components/LoginAuth0.js | 20 ++++++-- components/Meta.js | 11 +++-- components/Nav.js | 5 +- components/Page.js | 9 ++-- components/Pagination.js | 47 ++++++++++++++++++ components/Search.js | 78 +++++++++++++++++++++++++++++ components/UpdateItem.js | 5 +- 13 files changed, 436 insertions(+), 62 deletions(-) create mode 100644 components/AddToCart.js create mode 100644 components/Cart.js create mode 100644 components/CartList.js create mode 100644 components/Item.js create mode 100644 components/Pagination.js create mode 100644 components/Search.js (limited to 'components') diff --git a/components/AddToCart.js b/components/AddToCart.js new file mode 100644 index 0000000..0c40efe --- /dev/null +++ b/components/AddToCart.js @@ -0,0 +1,39 @@ +import { Component } from 'react'; +import { ADD_TO_CART_MUTATION, CURRENT_USER_QUERY } from '../queries'; +import { graphql, compose } from 'react-apollo'; + +class AddToCart extends Component { + componentDidMount() { + this.props.currentUserQuery.refetch(); + } + + addToCart = async () => { + const res = await this.props.addToCart({ + variables: { + userId: this.props.currentUserQuery.user.id, + itemId: this.props.id, + } + }); + this.props.currentUserQuery.refetch(); + console.log(res) + } + render() { + const user = this.props.currentUserQuery.user; + if (!user) return

Loading...

; + const cartIds = user.cart.map(item => item.id); + return ( +
+ + { + cartIds.includes(this.props.id) + ? + : + } +
+ ) + } +} + +const userEnhancer = graphql(CURRENT_USER_QUERY, { name: 'currentUserQuery' }); +const createOrderEnhancer = graphql(ADD_TO_CART_MUTATION, { name: 'addToCart' }); +export default compose(userEnhancer, createOrderEnhancer)(AddToCart); diff --git a/components/Cart.js b/components/Cart.js new file mode 100644 index 0000000..57a00fc --- /dev/null +++ b/components/Cart.js @@ -0,0 +1,41 @@ +import { Component } from "react"; +import { graphql, compose } from "react-apollo"; +import { CURRENT_USER_QUERY } from "../queries"; +import styled from "styled-components"; +import formatMoney from "../lib/formatMoney.js"; + +const cartStyles = styled.div` + background: white; + padding: 20px; +`; + +class Cart extends Component { + componentDidMount() { + // This fetches the new data, but doesn't populate the user via props + // this.props.currentUserQuery.refetch(); + // This fetches the new data, and populates the user via props + console.log("refetching!"); + setTimeout(this.props.currentUserQuery.refetch, 1); + } + + render() { + // Check for loading state.. + const { loading, error } = this.props.currentUserQuery; + const { user } = this.props.currentUserQuery; + if (loading || error || !user) return

Cart Loading...

; + const { email = "" } = user; + const total = user.cart.reduce((a, b) => a + b.price, 0); + + return ( +
+

+ πŸ’° There are {user.cart.length} Items in your cart + totaling {formatMoney(total)} +

+
+ ); + } +} + +const userEnhancer = graphql(CURRENT_USER_QUERY, { name: "currentUserQuery" }); +export default compose(userEnhancer)(Cart); diff --git a/components/CartList.js b/components/CartList.js new file mode 100644 index 0000000..d0456aa --- /dev/null +++ b/components/CartList.js @@ -0,0 +1,74 @@ +import { Component } from 'react'; +import withData from '../lib/withData'; +import Items from '../components/Items'; +import Signup from '../components/Signup'; +import LoginAuth0 from '../components/LoginAuth0'; +import Page from '../components/Page'; +import { USER_ORDERS_QUERY } from '../queries'; +import { graphql, compose } from 'react-apollo' +import has from 'lodash.has'; +import get from 'lodash.get'; +import formatMoney from '../lib/formatMoney'; +import makeImage from '../lib/image'; +import TakeMyMoney from './TakeMyMoney' + +import { CURRENT_USER_QUERY, REMOVE_FROM_CART_MUTATION } from '../queries'; + +class CartList extends Component { + componentDidMount() { + setTimeout(this.props.currentUserQuery.refetch, 1); + } + render() { + + if(this.props.loading) { + return

Loading...

+ } + + if(this.props.error) { + return

Error...

+ } + + if(!has(this.props, 'currentUserQuery.user.cart')) { + return

Don't have it yet!

+ } + + const cart = this.props.currentUserQuery.user.cart; + const userId = this.props.currentUserQuery.user.id; + + const total = cart.reduce((a, b) => a + b.price, 0); + return ( +
+

{cart.length} Items

+ + + + +
+ ) + } +} + +const userEnhancer = graphql(CURRENT_USER_QUERY, { name: 'currentUserQuery' }); +const removeItemEnhancer = graphql(REMOVE_FROM_CART_MUTATION, { name: 'removeFromCart', options: { + update: (proxy, payload) => { + const data = proxy.readQuery({ query: CURRENT_USER_QUERY }); + const cartItemId = payload.data.removeFromCartItems.cartItem.id; + data.user.cart = data.user.cart.filter(item => item.id !== cartItemId); + proxy.writeQuery({ query: CURRENT_USER_QUERY, data }); + }, + } }); +export default compose(userEnhancer, removeItemEnhancer)(CartList) diff --git a/components/Header.js b/components/Header.js index 52aeef9..60d0621 100644 --- a/components/Header.js +++ b/components/Header.js @@ -1,6 +1,9 @@ import { Component } from 'react' import { graphql, compose } from 'react-apollo' import { CURRENT_USER_QUERY } from '../queries'; +import Cart from './Cart'; +import Login from './LoginAuth0'; +import Search from './Search'; class Header extends Component { @@ -12,13 +15,14 @@ class Header extends Component { } render() { - console.log(this.props.currentUserQuery.user); const user = this.props.currentUserQuery.user || {}; const { email = '' } = user; - return (
-

{email} I'm the header!

+

Signed in as {email}

+ + +
) } diff --git a/components/Item.js b/components/Item.js new file mode 100644 index 0000000..66ca66f --- /dev/null +++ b/components/Item.js @@ -0,0 +1,33 @@ +import React, { Component } from 'react' +import { graphql, gql, compose } from 'react-apollo' +import { SINGLE_ITEM_QUERY } from '../queries'; +import withData from '../lib/withData'; + +class Item extends Component { + render() { + if(!this.props.findItem.Item) return

Not ready

+ console.log(this.props.findItem.Item); + + if(this.props.loading) return

Loading...

+ if(this.props.error) return

Error...

+ + const item = this.props.findItem.Item; + return ( +
+

Viewing {item.title}

+
+ ) + } +} + +const ComponentWithMutations = compose( + graphql(SINGLE_ITEM_QUERY, { + name: 'findItem', + // This comes from Props + options: ({ id }) => ({ + variables: { id } + }) + }) +)(Item); + +export default ComponentWithMutations; diff --git a/components/Items.js b/components/Items.js index 7d07643..d212ead 100644 --- a/components/Items.js +++ b/components/Items.js @@ -1,29 +1,30 @@ import { Component } from 'react' -import { graphql, compose } from 'react-apollo' +import { withApollo, graphql, compose } from 'react-apollo' import UpdateItem from './UpdateItem'; import Link from 'next/link'; import styled from 'styled-components'; import TakeMyMoney from './TakeMyMoney'; +import AddToCart from './AddToCart'; +import Pagination from './Pagination'; import formatMoney from '../lib/formatMoney'; import makeImage from '../lib/image'; +import slugify from 'slugify'; import { ALL_ITEMS_QUERY, DELETE_ITEM_MUTATION } from '../queries'; const Title = styled.h1` - font-size: 50px; + font-size: 10px; `; - - const Items = styled.div` display: grid; - grid-template-columns: repeat(4, calc(25% - 20px)); + grid-template-columns: repeat(4, calc(33% - 20px)); grid-gap: 20px; `; const Item = styled.div` background: #f3f3f3; - padding: 20px; + padding: 5px; img { width: 100%; } @@ -31,51 +32,91 @@ const Item = styled.div` class ItemList extends Component { + componentDidMount() { + this.prefetchNextItems(this.props.page); + } + + componentWillReceiveProps(nextProps) { + // update the next items if the page prop changed + if(this.props.page !== nextProps.page) { + this.prefetchNextItems(nextProps.page); + } + } + + prefetchNextItems = (currentPage) => { + const page = currentPage + 1; + console.log(`Prefetching Next items! Page ${page}`); + this.props.client.query({ + query: ALL_ITEMS_QUERY, + variables: { + skip: (page * 3) - 3 + } + }) + } + render() { + console.log("CLIENTTT!!", this.props.client); // 1 - if (this.props.allLinksQuery && this.props.allLinksQuery.loading) { + if (this.props.allItemsQuery && this.props.allItemsQuery.loading) { return
Loading
} // 2 - if (this.props.allLinksQuery && this.props.allLinksQuery.error) { - console.log(this.props.allLinksQuery.error) + if (this.props.allItemsQuery && this.props.allItemsQuery.error) { + console.log(this.props.allItemsQuery.error) return
Error
} // 3 - const itemsToRender = this.props.allLinksQuery.allItems + const itemsToRender = this.props.allItemsQuery.allItems; return ( - +
+ Items For Sale - {itemsToRender.map((item,i) => ( - - { item.image ? : null } -

{item.title}

-

{item.description}

- - Edit {item.id} - - - - - - - -
- ))} - + + {itemsToRender.map((item,i) => ( + + { item.image ? : null } +

+ + {item.title} + +

+ +

{item.description}

+ + Edit ✏️ + + + + + + + + + +
+ ))} +
+ +
) } @@ -86,7 +127,14 @@ class ItemList extends Component { // We export the graphQL HOC - this will fetch the data and inject it into the ItemList compeont via props // Create some Enhancers -const itemsEnahncer = graphql(ALL_ITEMS_QUERY, { name: 'allLinksQuery' }); +const itemsEnahncer = graphql(ALL_ITEMS_QUERY, { name: 'allItemsQuery', options({ page }) { + return { + variables: { + skip: (page * 3) - 3 + }, + } +}}); + const deleteItemEnhancer = graphql(DELETE_ITEM_MUTATION, { name: 'removeItemMutation', options: { @@ -105,4 +153,4 @@ const deleteItemEnhancer = graphql(DELETE_ITEM_MUTATION, { } }); -export default compose(itemsEnahncer, deleteItemEnhancer)(ItemList) +export default withApollo(compose(itemsEnahncer, deleteItemEnhancer)(ItemList)); diff --git a/components/LoginAuth0.js b/components/LoginAuth0.js index d5293b5..262d8bd 100644 --- a/components/LoginAuth0.js +++ b/components/LoginAuth0.js @@ -1,5 +1,7 @@ import { Component, PropTypes } from 'react' import Auth0Lock from 'auth0-lock' +import { graphql, compose } from 'react-apollo' +import { CURRENT_USER_QUERY } from '../queries/index'; class LoginAuth0 extends Component { @@ -15,9 +17,14 @@ class LoginAuth0 extends Component { }); } + logout = () => { + window.localStorage.removeItem('auth0IdToken'); + this.props.currentUserQuery.refetch(); + } + createUser = () => { const variables = { - idToken: window.localStorage.getItem("auth0IdToken"), + idToken: window.localStorage.getItem('auth0IdToken'), emailAddress: 'wesbos@gmail.com', name: 'Hardcoded Wes' }; @@ -25,6 +32,7 @@ class LoginAuth0 extends Component { this.props .createUser({ variables }) .then(response => { + // this.props.currentUserQuery.refetch(); this.props.history.replace("/"); }) .catch(e => { @@ -36,9 +44,8 @@ class LoginAuth0 extends Component { componentDidMount() { console.log('MOUNT'); this._lock.on('authenticated', (authResult) => { - console.log('HIIIIIIII') window.localStorage.setItem('auth0IdToken', authResult.idToken) - console.log('Done!', authResult); + this.props.currentUserQuery.refetch(); }) } @@ -47,6 +54,10 @@ class LoginAuth0 extends Component { } render() { + const { user } = this.props.currentUserQuery; + if ( user ) { + return + } return (
@@ -55,4 +66,5 @@ class LoginAuth0 extends Component { } } -export default LoginAuth0; +const userEnhancer = graphql(CURRENT_USER_QUERY, { name: 'currentUserQuery' }); +export default compose(userEnhancer)(LoginAuth0) diff --git a/components/Meta.js b/components/Meta.js index d851946..5ecea01 100644 --- a/components/Meta.js +++ b/components/Meta.js @@ -1,5 +1,6 @@ -import Head from 'next/head' -import Router from 'next/router' +import React from 'react'; + +import Head from 'next/head'; import Nav from './Nav'; export default () => ( @@ -7,10 +8,10 @@ export default () => ( -
-) +); diff --git a/components/Nav.js b/components/Nav.js index 2e32a1c..ad0b05a 100644 --- a/components/Nav.js +++ b/components/Nav.js @@ -3,7 +3,8 @@ import Link from 'next/link' export default () => ( ) diff --git a/components/Page.js b/components/Page.js index 1b07fe6..c05eeb7 100644 --- a/components/Page.js +++ b/components/Page.js @@ -1,6 +1,5 @@ import Header from './Header' import Meta from './Meta' -import withData from '../lib/withData'; import Nav from './Nav'; import styled from 'styled-components'; @@ -14,12 +13,10 @@ const StyledPage = styled.div` const Page = ({ children }) => ( -
-
- { children } -
+
+ { children } ) -export default withData(Page); +export default Page; diff --git a/components/Pagination.js b/components/Pagination.js new file mode 100644 index 0000000..1b75ed8 --- /dev/null +++ b/components/Pagination.js @@ -0,0 +1,47 @@ +import React, { Component } from 'react' +import { graphql, gql, compose } from 'react-apollo' +import { ALL_ITEMS_QUERY } from '../queries'; +import withData from '../lib/withData'; +import Link from 'next/link' + +class Pagination extends Component { + render() { + const { loading, error } = this.props.allItemsQuery; + + if(loading) return

Loading Item...

+ + const meta = this.props.allItemsQuery._allItemsMeta; + const { page } = this.props; + const pages = Math.floor(meta.count / 3); + return ( +
+

+ Page {page} + of + {pages} + - + {meta.count} + total +

+ + { page > 1 + ? ←Prev + : null + } + + { + page <= pages + ? Next β†’ + : null + } + +
+ ) + } +} + +const ComponentWithMutations = compose( + graphql(ALL_ITEMS_QUERY, { name: 'allItemsQuery' }) +)(Pagination); + +export default ComponentWithMutations; diff --git a/components/Search.js b/components/Search.js new file mode 100644 index 0000000..afab3e8 --- /dev/null +++ b/components/Search.js @@ -0,0 +1,78 @@ +import Downshift from 'downshift' +import { SEARCH_ITEMS_QUERY } from '../queries'; +import { graphql, compose } from 'react-apollo' +import makeImage from '../lib/image'; +import Router from 'next/router' +import slugify from 'slugify'; + +function routeToItem(item) { + Router.push({ + pathname: '/item', + query: { + slug: slugify(item.title), + itemId: item.id + } + }) +} + +function BasicAutocomplete(props) { + const { items, onChange } = props; + console.log(props); + return ( + item.title} + > + {({ + getInputProps, + getItemProps, + isOpen, + inputValue, + selectedItem, + highlightedIndex + }) => ( +
+ props.refetch({ searchTerm: e.target.value }), + style: { fontSize: '20px', padding: '20px', display: 'block', width: '100%' } + })} /> + {isOpen ? ( +
+ {items + .map((item, index) => ( +
+ {item.title}/ + {item.title} +
+ ))} +
+ ) : null} +
+ )} +
+ ) +} + +const Search = (props) => ( + console.log(selectedItem)} + refetch={props.searchItems.refetch} + /> +) + +const searchEnhancer = graphql(SEARCH_ITEMS_QUERY, { name: 'searchItems', options: { + variables: { searchTerm: 'camo' } +} }); +export default compose(searchEnhancer)(Search) diff --git a/components/UpdateItem.js b/components/UpdateItem.js index 0edad06..a3b45a8 100644 --- a/components/UpdateItem.js +++ b/components/UpdateItem.js @@ -1,6 +1,6 @@ import React, { Component } from 'react' import { graphql, gql, compose } from 'react-apollo' -import { SINGLE_LINK_QUERY, UPDATE_LINK_MUTATION } from '../queries'; +import { SINGLE_ITEM_QUERY, UPDATE_LINK_MUTATION } from '../queries'; class UpdateLink extends Component { @@ -66,7 +66,7 @@ class UpdateLink extends Component { const ComponentWithMutations = compose( // First, query for getting the link - graphql(SINGLE_LINK_QUERY, { + graphql(SINGLE_ITEM_QUERY, { name: 'findItem', options: ({ id }) => ({ variables: { id } @@ -76,5 +76,4 @@ const ComponentWithMutations = compose( graphql(UPDATE_LINK_MUTATION, { name: 'updateItem' }) )(UpdateLink); - export default ComponentWithMutations; -- cgit v1.3