summaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorWes Bos <wesbos@gmail.com>2017-09-15 14:26:13 -0400
committerWes Bos <wesbos@gmail.com>2017-09-15 14:26:13 -0400
commit1450e579c67e3d969cb099dfe6c1cefd1e313fb5 (patch)
tree5c255968082c16452f1dc8245dbbb890f91dce87 /components
parent7af74ff8988961c988f03c95961b70c2918521ae (diff)
yep
Diffstat (limited to 'components')
-rw-r--r--components/AddToCart.js39
-rw-r--r--components/Cart.js41
-rw-r--r--components/CartList.js74
-rw-r--r--components/Header.js10
-rw-r--r--components/Item.js33
-rw-r--r--components/Items.js122
-rw-r--r--components/LoginAuth0.js20
-rw-r--r--components/Meta.js11
-rw-r--r--components/Nav.js5
-rw-r--r--components/Page.js9
-rw-r--r--components/Pagination.js47
-rw-r--r--components/Search.js78
-rw-r--r--components/UpdateItem.js5
13 files changed, 434 insertions, 60 deletions
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 <p>Loading...</p>;
+ const cartIds = user.cart.map(item => item.id);
+ return (
+ <div>
+
+ {
+ cartIds.includes(this.props.id)
+ ? <button>Added to cart βœ…</button>
+ : <button onClick={this.addToCart}>Add To Cart πŸ‘œ</button>
+ }
+ </div>
+ )
+ }
+}
+
+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 <p>Cart Loading...</p>;
+ const { email = "" } = user;
+ const total = user.cart.reduce((a, b) => a + b.price, 0);
+
+ return (
+ <div>
+ <p>
+ πŸ’° There are <strong>{user.cart.length}</strong> Items in your cart
+ totaling <strong>{formatMoney(total)}</strong>
+ </p>
+ </div>
+ );
+ }
+}
+
+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 <p>Loading...</p>
+ }
+
+ if(this.props.error) {
+ return <p>Error...</p>
+ }
+
+ if(!has(this.props, 'currentUserQuery.user.cart')) {
+ return <p>Don't have it yet!</p>
+ }
+
+ 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 (
+ <div>
+ <h1>{cart.length} Items</h1>
+ <ul>
+ {cart.map(item => (
+ <li key={item.id}>
+ {item.title}
+ <button onClick={() => this.props.removeFromCart({ variables: {
+ userId,
+ itemId: item.id
+ }})}>&times; Delete</button>
+ </li>
+ ))}
+ </ul>
+ <TakeMyMoney
+ amount={total}
+ name="Testing 123"
+ description="Test test 123"
+ >
+ <button>Buy for {formatMoney(total)}</button>
+ </TakeMyMoney>
+ </div>
+ )
+ }
+}
+
+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 (
<div>
- <p>{email} I'm the header!</p>
+ <p>Signed in as <strong>{email}</strong></p>
+ <Login></Login>
+ <Cart></Cart>
+ <Search></Search>
</div>
)
}
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 <p>Not ready</p>
+ console.log(this.props.findItem.Item);
+
+ if(this.props.loading) return <p>Loading...</p>
+ if(this.props.error) return <p>Error...</p>
+
+ const item = this.props.findItem.Item;
+ return (
+ <div>
+ <h2>Viewing {item.title}</h2>
+ </div>
+ )
+ }
+}
+
+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 <div>Loading</div>
}
// 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 <div>Error</div>
}
// 3
- const itemsToRender = this.props.allLinksQuery.allItems
+ const itemsToRender = this.props.allItemsQuery.allItems;
return (
- <Items>
+ <div>
+ <Pagination page={this.props.page}></Pagination>
<Title>Items For Sale</Title>
- {itemsToRender.map((item,i) => (
- <Item className="item" key={i}>
- { item.image ? <img src={makeImage(item.image)} /> : null }
- <h3>{item.title}</h3>
- <p>{item.description}</p>
- <Link href={{
- pathname: '/admin/update',
- query: { id: item.id }
- }}>
- <a>Edit {item.id}</a>
- </Link>
+ <Items>
+ {itemsToRender.map((item,i) => (
+ <Item className="item" key={i}>
+ { item.image ? <img key={item.image.secret} src={makeImage(item.image)} /> : null }
+ <h3>
+ <Link href={{
+ pathname: 'item',
+ query: {
+ slug: slugify(item.title),
+ itemId: item.id
+ },
+ }}>
+ <a>{item.title}</a>
+ </Link>
+ </h3>
+
+ <p>{item.description}</p>
+ <Link href={{
+ pathname: '/admin/update',
+ query: { id: item.id }
+ }}>
+ <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}></AddToCart>
+ <button onClick={() => this.props.removeItemMutation({ variables: { id: item.id }})}>&times; Delete item</button>
- <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>
- <button onClick={() => this.props.removeItemMutation({ variables: { id: item.id }})}>&times; Delete item</button>
+ </Item>
+ ))}
+ </Items>
- </Item>
- ))}
- </Items>
+ </div>
)
}
@@ -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 <button onClick={this.logout}>Log out πŸ‘‹</button>
+ }
return (
<div>
<button onClick={this._showLogin}>Log in with Auth0 </button>
@@ -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 () => (
<Head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta charSet="utf-8" />
- <Nav/>
+ <Nav />
<link rel="shortcut icon" href="https://wesbos.com/wp-content/themes/wb2014/i/crown-yellow-small.png" />
- <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"/>
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" />
<title>Sick Fits</title>
</Head>
</div>
-)
+);
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 () => (
<ul>
<Link href="/"><a>Home</a></Link>
- <Link href="/signup/"><a>Signup</a></Link>
- <Link href="/orders/"><a>Orders</a></Link>
+ <Link href="/signup"><a>Signup</a></Link>
+ <Link href="/orders"><a>Orders</a></Link>
+ <Link href="/cart"><a>My Cart</a></Link>
</ul>
)
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 }) => (
<StyledPage className="main">
<Meta />
- <Header />
<Nav></Nav>
- <div>
- { children }
- </div>
+ <Header />
+ { children }
</StyledPage>
)
-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 <p>Loading Item...</p>
+
+ const meta = this.props.allItemsQuery._allItemsMeta;
+ const { page } = this.props;
+ const pages = Math.floor(meta.count / 3);
+ return (
+ <div>
+ <p>
+ Page <strong>{page} </strong>
+ of
+ <strong>{pages} </strong>
+ -
+ <strong>{meta.count} </strong>
+ total
+ </p>
+
+ { page > 1
+ ? <Link prefetch href={{ pathname: 'items', query: { page: page - 1 }}}><a>←Prev</a></Link>
+ : null
+ }
+
+ {
+ page <= pages
+ ? <Link prefetch href={{ pathname: 'items', query: { page: page + 1 }}}><a>Next β†’</a></Link>
+ : null
+ }
+
+ </div>
+ )
+ }
+}
+
+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 (
+ <Downshift
+ onChange={routeToItem}
+ itemToString={(item) => item.title}
+ >
+ {({
+ getInputProps,
+ getItemProps,
+ isOpen,
+ inputValue,
+ selectedItem,
+ highlightedIndex
+ }) => (
+ <div>
+ <input {...getInputProps({
+ placeholder: 'Search For Item',
+ onChange: (e) => props.refetch({ searchTerm: e.target.value }),
+ style: { fontSize: '20px', padding: '20px', display: 'block', width: '100%' }
+ })} />
+ {isOpen ? (
+ <div>
+ {items
+ .map((item, index) => (
+ <div
+ {...getItemProps({item})}
+ key={item.id}
+ style={{
+ backgroundColor: highlightedIndex === index ? '#e8e8e8' : 'white',
+ borderLeft: highlightedIndex === index ? '10px solid #ffc600' : '10px solid white',
+ padding: '10px',
+ display: 'flex',
+ alignItems: 'center'
+ }}
+ >
+ <img width="50" style={{ 'margin-right': '10px'}} src={makeImage(item.image)} alt={item.title}/>
+ {item.title}
+ </div>
+ ))}
+ </div>
+ ) : null}
+ </div>
+ )}
+ </Downshift>
+ )
+}
+
+const Search = (props) => (
+ <BasicAutocomplete
+ items={props.searchItems.allItems}
+ onChange={selectedItem => 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;