summaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorWes Bos <wesbos@gmail.com>2017-09-21 15:39:12 -0400
committerWes Bos <wesbos@gmail.com>2017-09-21 15:39:12 -0400
commit3b45dd7b593825721ec9ee9f6a49c732601b1ae0 (patch)
treea2e8e73d6b6b6bc32eda2bb79af817397e4304f5 /components
parent1450e579c67e3d969cb099dfe6c1cefd1e313fb5 (diff)
Cha Ching
Diffstat (limited to 'components')
-rw-r--r--components/AddToCart.js84
-rw-r--r--components/Cart.js35
-rw-r--r--components/CartList.js54
-rw-r--r--components/ChaChing.js56
-rw-r--r--components/CreateItem.js113
-rw-r--r--components/ErrorMessage.js32
-rw-r--r--components/Item.js84
-rw-r--r--components/Items.js119
-rw-r--r--components/LoginAuth0.js58
-rw-r--r--components/Meta.js7
-rw-r--r--components/Nav.js54
-rw-r--r--components/Page.js10
-rw-r--r--components/Pagination.js70
-rw-r--r--components/Search.js103
-rw-r--r--components/SingleItem.js36
15 files changed, 556 insertions, 359 deletions
diff --git a/components/AddToCart.js b/components/AddToCart.js
index 0c40efe..9bcdb7f 100644
--- a/components/AddToCart.js
+++ b/components/AddToCart.js
@@ -1,6 +1,37 @@
import { Component } from 'react';
-import { ADD_TO_CART_MUTATION, CURRENT_USER_QUERY } from '../queries';
+import { ADD_TO_CART_MUTATION, CURRENT_USER_QUERY, SINGLE_ITEM_QUERY } from '../queries';
+import { removeFromCartEnhancer, userEnhancer } from '../enhancers';
import { graphql, compose } from 'react-apollo';
+import Transition from 'react-transition-group/Transition';
+import makeImage from '../lib/image';
+import styled from 'styled-components';
+
+const JumpImg = styled.img`
+ border: 0 solid black;
+ max-width: 100px;
+ transition: all 0.5s;
+ position: fixed;
+ left: ${props => props.x}px;
+ top: -100%;
+ &.jump-entered {
+ border-color: green;
+ transform-origin: 0 0;
+ transform: scale(0);
+ top: ${props => props.y}px;
+ left: ${props => props.x}px;
+ }
+ &.jump-entering {
+ border-color: yellow;
+ top: ${props => props.y}px;
+ left: ${props => props.x}px;
+ }
+ &.jump-exited {
+ border-color: red;
+ }
+ &.jump-exiting {
+ border-color: yellow;
+ }
+`;
class AddToCart extends Component {
componentDidMount() {
@@ -12,28 +43,55 @@ class AddToCart extends Component {
variables: {
userId: this.props.currentUserQuery.user.id,
itemId: this.props.id,
- }
+ },
});
this.props.currentUserQuery.refetch();
- console.log(res)
- }
+ console.log(res);
+ };
+
+ removeFromCart = async () => {
+ const res = await this.props.removeFromCart({
+ 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);
+ const image = this.props.singleItemQuery.Item.image;
+ const isInCart = cartIds.includes(this.props.id);
+ const { x, y } = document.querySelector('.cart').getBoundingClientRect();
+ console.log({ x, y });
+
return (
<div>
-
- {
- cartIds.includes(this.props.id)
- ? <button>Added to cart βœ…</button>
- : <button onClick={this.addToCart}>Add To Cart πŸ‘œ</button>
- }
+ {isInCart ? (
+ <button onClick={this.removeFromCart}>❌ Remove From Cart</button>
+ ) : (
+ <button onClick={this.addToCart}>Add To Cart πŸ‘œ</button>
+ )}
+ <Transition in={isInCart} timeout={1000}>
+ {status => <JumpImg x={x} y={y} src={makeImage(image)} className={`jump-${status}`} />}
+ </Transition>
</div>
- )
+ );
}
}
-const userEnhancer = graphql(CURRENT_USER_QUERY, { name: 'currentUserQuery' });
const createOrderEnhancer = graphql(ADD_TO_CART_MUTATION, { name: 'addToCart' });
-export default compose(userEnhancer, createOrderEnhancer)(AddToCart);
+const singleItemEnhancer = graphql(SINGLE_ITEM_QUERY, {
+ name: 'singleItemQuery',
+ options: ({ id }) => ({
+ variables: {
+ id,
+ },
+ }),
+});
+export default compose(userEnhancer, createOrderEnhancer, removeFromCartEnhancer, singleItemEnhancer)(AddToCart);
diff --git a/components/Cart.js b/components/Cart.js
index 57a00fc..88b072c 100644
--- a/components/Cart.js
+++ b/components/Cart.js
@@ -1,12 +1,17 @@
-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";
+import { Component } from 'react';
+import { graphql, compose } from 'react-apollo';
+import styled from 'styled-components';
+import { CURRENT_USER_QUERY } from '../queries';
+import formatMoney from '../lib/formatMoney.js';
+import ChaChing from './ChaChing';
-const cartStyles = styled.div`
+const CartStyles = styled.div`
background: white;
+ border-radius: 10px;
padding: 20px;
+ overflow: hidden;
+ display: flex;
+ align-items: center;
`;
class Cart extends Component {
@@ -14,7 +19,6 @@ class Cart extends Component {
// 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);
}
@@ -23,19 +27,20 @@ class Cart extends Component {
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>
+ <CartStyles className="cart">
+ There
+ {user.cart.length === 1 ? ' is ' : 'are '}
+ <ChaChing amount={user.cart.length} />
+ {user.cart.length === 1 ? ' item ' : ' items '}
+ in your cart totaling
+ <ChaChing amount={formatMoney(total)} />
+ </CartStyles>
);
}
}
-const userEnhancer = graphql(CURRENT_USER_QUERY, { name: "currentUserQuery" });
+const userEnhancer = graphql(CURRENT_USER_QUERY, { name: 'currentUserQuery' });
export default compose(userEnhancer)(Cart);
diff --git a/components/CartList.js b/components/CartList.js
index d0456aa..d485907 100644
--- a/components/CartList.js
+++ b/components/CartList.js
@@ -5,31 +5,30 @@ 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 { 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';
+import TakeMyMoney from './TakeMyMoney';
+import { removeFromCartEnhancer } from '../enhancers';
+import { CURRENT_USER_QUERY } 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.loading) {
+ return <p>Loading...</p>;
}
- if(this.props.error) {
- return <p>Error...</p>
+ if (this.props.error) {
+ return <p>Error...</p>;
}
- if(!has(this.props, 'currentUserQuery.user.cart')) {
- return <p>Don't have it yet!</p>
+ if (!has(this.props, 'currentUserQuery.user.cart')) {
+ return <p>Don't have it yet!</p>;
}
const cart = this.props.currentUserQuery.user.cart;
@@ -43,32 +42,27 @@ class CartList extends Component {
{cart.map(item => (
<li key={item.id}>
{item.title}
- <button onClick={() => this.props.removeFromCart({ variables: {
- userId,
- itemId: item.id
- }})}>&times; Delete</button>
+ <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"
- >
+ <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)
+export default compose(userEnhancer, removeFromCartEnhancer)(CartList);
diff --git a/components/ChaChing.js b/components/ChaChing.js
new file mode 100644
index 0000000..5874c27
--- /dev/null
+++ b/components/ChaChing.js
@@ -0,0 +1,56 @@
+import TransitionGroup from 'react-transition-group/TransitionGroup';
+import Transition from 'react-transition-group/Transition';
+import styled from 'styled-components';
+
+const CartCount = styled.div`
+ padding: 10px;
+ transition: all 0.5s;
+ display: inline-block;
+ background: white;
+ background: linear-gradient(to bottom, #eeeeee 0%, #eeeeee 100%);
+ transform: translateY(100%);
+ z-index: 1;
+ position: relative;
+ &.cart-entering {
+ background: green;
+ transform: translateY(100%);
+ }
+ &.cart-entered {
+ transform: translateY(0);
+ }
+ &.cart-exiting {
+ position: absolute;
+ z-index: 0;
+ left: 0;
+ transform: translateY(-100%);
+ }
+`;
+
+const ChaChingStyles = styled.div`
+ position: relative;
+ display: inline-block;
+ overflow: hidden;
+`;
+
+const ChaChing = ({ amount }) => (
+ <ChaChingStyles>
+ <TransitionGroup>
+ <Transition
+ in
+ timeout={{
+ enter: 0,
+ exit: 500,
+ }}
+ key={amount}
+ >
+ {status => (
+ <CartCount className={`cart-${status}`} key={`count-${amount}`}>
+ {amount}
+ </CartCount>
+ )}
+ </Transition>
+ </TransitionGroup>
+ </ChaChingStyles>
+);
+
+export default ChaChing;
diff --git a/components/CreateItem.js b/components/CreateItem.js
index 3b86f2c..d9dc97b 100644
--- a/components/CreateItem.js
+++ b/components/CreateItem.js
@@ -1,99 +1,124 @@
-import React, { Component } from 'react'
-import { graphql, gql } from 'react-apollo'
+import React, { Component } from 'react';
+import { graphql, gql } from 'react-apollo';
import { ALL_ITEMS_QUERY, CREATE_LINK_MUTATION } from '../queries';
+import ErrorMessage from './ErrorMessage';
class CreateLink extends Component {
-
state = {
description: '',
title: '',
image: '',
price: 0,
fullPrice: 0,
- loading: false
- }
+ loading: false,
+ error: {
+ message: 'shit!',
+ },
+ };
componentWillReceiveProps(nextProps) {
console.log(nextProps);
}
- uploadFile = async (e) => {
+ uploadFile = async e => {
const files = e.currentTarget.files;
- let data = new FormData();
+ const data = new FormData();
data.append('data', files[0]);
// use the file endpoint
const res = await fetch('https://api.graph.cool/file/v1/cj5xz8szs28930145gct82bdj', {
method: 'POST',
- body: data
+ body: data,
});
const file = await res.json();
console.log(file);
this.setState({ image: file.id });
- }
+ };
+
+ _createLink = async e => {
+ e.preventDefault();
+ // pull the values from state
+ const { description, title, image, price, fullPrice } = this.state;
+ // create a mutation
+ // TODO: handle any errors
+ // turn loading on
+ this.setState({ loading: true });
+ try {
+ const res = await this.props.createLinkMutation({
+ // pass in those variables from state
+ variables: {
+ description,
+ title,
+ price: parseInt(price),
+ fullPrice,
+ imageId: image,
+ },
+ });
+ } catch (error) {
+ this.setState({ error });
+ console.log(error);
+ }
+ this.setState({ loading: false });
+ };
render() {
return (
<div>
- { this.state.loading ? 'LOADING...' : 'Ready!' }
+ {this.state.loading ? 'LOADING...' : 'Ready!'}
+
+ <ErrorMessage error={this.state.error} onButtonClick={() => this.setState({ error: {} })} />
<form onSubmit={this._createLink}>
<p>
Image
- <input onChange={this.uploadFile} type='file'/>
+ <input onChange={this.uploadFile} type="file" />
</p>
- <p>Title
- <input value={this.state.title} onChange={(e) => this.setState({ title: e.target.value })} type='text' placeholder='A description for the link'/>
+ <p>
+ Title
+ <input
+ value={this.state.title}
+ onChange={e => this.setState({ title: e.target.value })}
+ type="text"
+ placeholder="A description for the link"
+ />
</p>
- <label>Price<input type="number" min="0" value={this.state.price} onChange={(e) => this.setState({ price: e.target.value })} /></label>
+ <label>
+ Price<input
+ type="number"
+ min="0"
+ value={this.state.price}
+ onChange={e => this.setState({ price: e.target.value })}
+ />
+ </label>
<textarea
value={this.state.description}
- onChange={(e) => this.setState({ description: e.target.value })}
- type='text'
- placeholder='The desc for this item'
- ></textarea>
+ onChange={e => this.setState({ description: e.target.value })}
+ type="text"
+ placeholder="The desc for this item"
+ />
<button type="submit">Submit</button>
</form>
</div>
- )
- }
-
- _createLink = async (e) => {
- e.preventDefault();
- // pull the values from state
- const { description, title, image, price, fullPrice } = this.state;
- // create a mutation
- // TODO: handle any errors
- // turn loading on
- this.setState({ loading: true });
- const res = await this.props.createLinkMutation({
- // pass in those variables from state
- variables: {
- description,
- title,
- price: parseInt(price),
- fullPrice,
- imageId: image
- }
- });
- this.setState({ loading: false });
+ );
}
}
// 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_LINK_MUTATION, { name: 'createLinkMutation', options: {
+export default graphql(CREATE_LINK_MUTATION, {
+ name: 'createLinkMutation',
+ options: {
// Easy, but slow
// refetchQueries: ['AllLinksQuery']
// This is much Better / efficient
// Notice how the variable is called createItem - that is because createItem is the name of the query!
update: (proxy, { data: { createItem } }) => {
- console.log({createItem, ALL_ITEMS_QUERY});
+ console.log({ createItem, ALL_ITEMS_QUERY });
const data = proxy.readQuery({ query: ALL_ITEMS_QUERY });
// data is our store, allItems is our sub-"state", it's just an array. We can just add it to
data.allItems.unshift(createItem);
// and then "set state", so it will update on the page. This will update the cache for us!
proxy.writeQuery({ query: ALL_ITEMS_QUERY, data });
},
- } })(CreateLink)
+ },
+})(CreateLink);
diff --git a/components/ErrorMessage.js b/components/ErrorMessage.js
new file mode 100644
index 0000000..12de51d
--- /dev/null
+++ b/components/ErrorMessage.js
@@ -0,0 +1,32 @@
+import styled from 'styled-components';
+import React from 'react';
+
+import PropTypes from 'prop-types';
+
+const StyledError = styled.div`
+ background: pink;
+ border: 2px solid red;
+ padding: 20px;
+ display: flex;
+ p {
+ margin: 0;
+ flex: 1 0 auto;
+ }
+`;
+
+const DisplayError = props => {
+ if (!props.error || !props.error.message) return null;
+ return (
+ <StyledError>
+ <p>{props.error.message}</p>
+ <button onClick={props.onButtonClick}>&times;</button>
+ </StyledError>
+ );
+};
+
+DisplayError.propTypes = {
+ error: PropTypes.object.isRequired,
+ onButtonClick: PropTypes.func.isRequired,
+};
+
+export default DisplayError;
diff --git a/components/Item.js b/components/Item.js
index 66ca66f..1b47fcf 100644
--- a/components/Item.js
+++ b/components/Item.js
@@ -1,33 +1,61 @@
-import React, { Component } from 'react'
-import { graphql, gql, compose } from 'react-apollo'
-import { SINGLE_ITEM_QUERY } from '../queries';
-import withData from '../lib/withData';
+import styled from 'styled-components';
+import slugify from 'slugify';
+import { Link } from '../routes';
+import AddToCart from './AddToCart';
+import makeImage from '../lib/image';
+import TakeMyMoney from './TakeMyMoney';
+import formatMoney from '../lib/formatMoney';
-class Item extends Component {
- render() {
- if(!this.props.findItem.Item) return <p>Not ready</p>
- console.log(this.props.findItem.Item);
+const Item = styled.div`
+ background: #f3f3f3;
+ padding: 5px;
+ img {
+ width: 100%;
+ }
+`;
- if(this.props.loading) return <p>Loading...</p>
- if(this.props.error) return <p>Error...</p>
+const ItemComponent = ({ item }) => (
+ <Item key={item.id}>
+ {item.image ? <img key={item.image.secret} src={makeImage(item.image)} alt={item.title} /> : null}
+ <h3>
+ <Link
+ route="item"
+ params={{
+ slug: slugify(item.title),
+ itemId: item.id,
+ }}
+ >
+ <a>{item.title}</a>
+ </Link>
+ </h3>
- const item = this.props.findItem.Item;
- return (
- <div>
- <h2>Viewing {item.title}</h2>
- </div>
- )
- }
-}
+ <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>
-const ComponentWithMutations = compose(
- graphql(SINGLE_ITEM_QUERY, {
- name: 'findItem',
- // This comes from Props
- options: ({ id }) => ({
- variables: { id }
- })
- })
-)(Item);
+ <AddToCart id={item.id} />
+ <button onClick={() => this.props.removeItemMutation({ variables: { id: item.id } })}>&times; Delete item</button>
+ </Item>
+);
-export default ComponentWithMutations;
+export default ItemComponent;
diff --git a/components/Items.js b/components/Items.js
index d212ead..053c9af 100644
--- a/components/Items.js
+++ b/components/Items.js
@@ -1,20 +1,12 @@
-import { Component } from 'react'
-import { withApollo, graphql, compose } from 'react-apollo'
-import UpdateItem from './UpdateItem';
-import Link from 'next/link';
+import { Component } from 'react';
+import { withApollo, graphql, compose } from 'react-apollo';
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 Item from './Item';
import { ALL_ITEMS_QUERY, DELETE_ITEM_MUTATION } from '../queries';
-const Title = styled.h1`
- font-size: 10px;
-`;
+const Title = styled.h1`font-size: 10px;`;
const Items = styled.div`
display: grid;
@@ -22,50 +14,39 @@ const Items = styled.div`
grid-gap: 20px;
`;
-const Item = styled.div`
- background: #f3f3f3;
- padding: 5px;
- img {
- width: 100%;
- }
-`;
-
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) {
+ if (this.props.page !== nextProps.page) {
this.prefetchNextItems(nextProps.page);
}
}
- prefetchNextItems = (currentPage) => {
+ 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
- }
- })
- }
+ skip: page * 3 - 3,
+ },
+ });
+ };
render() {
- console.log("CLIENTTT!!", this.props.client);
-
// 1
if (this.props.allItemsQuery && this.props.allItemsQuery.loading) {
- return <div>Loading</div>
+ return <div>Loading</div>;
}
// 2
if (this.props.allItemsQuery && this.props.allItemsQuery.error) {
- console.log(this.props.allItemsQuery.error)
- return <div>Error</div>
+ console.log(this.props.allItemsQuery.error);
+ return <div>Error</div>;
}
// 3
@@ -73,53 +54,12 @@ class ItemList extends Component {
return (
<div>
- <Pagination page={this.props.page}></Pagination>
+ <Pagination page={this.props.page} />
<Title>Items For Sale</Title>
- <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>
-
- </Item>
- ))}
- </Items>
-
+ <Items key={this.props.page}>{itemsToRender.map((item, i) => <Item key={item.id} item={item} />)}</Items>
</div>
- )
+ );
}
-
}
// 1
@@ -127,30 +67,31 @@ 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: 'allItemsQuery', options({ page }) {
- return {
- variables: {
- skip: (page * 3) - 3
- },
- }
-}});
+const itemEnhancer = graphql(ALL_ITEMS_QUERY, {
+ name: 'allItemsQuery',
+ options({ page }) {
+ return {
+ variables: {
+ skip: page * 3 - 3,
+ },
+ };
+ },
+});
const deleteItemEnhancer = graphql(DELETE_ITEM_MUTATION, {
name: 'removeItemMutation',
options: {
update: (proxy, { data: { deleteItem } }) => {
-
// grab the data from our cache
const data = proxy.readQuery({ query: ALL_ITEMS_QUERY });
- // filter out the delted item
+ // filter out the deleted item
data.allItems = data.allItems.filter(item => item.id !== deleteItem.id);
- // and then "set state" (update cache), so it will update whereever we have used this data on the page
+ // and then "set state" (update cache), so it will update wherever we have used this data on the page
proxy.writeQuery({ query: ALL_ITEMS_QUERY, data });
-
},
- }
+ },
});
-export default withApollo(compose(itemsEnahncer, deleteItemEnhancer)(ItemList));
+export default withApollo(compose(itemEnhancer, deleteItemEnhancer)(ItemList));
diff --git a/components/LoginAuth0.js b/components/LoginAuth0.js
index 262d8bd..8691e82 100644
--- a/components/LoginAuth0.js
+++ b/components/LoginAuth0.js
@@ -1,70 +1,70 @@
-import { Component, PropTypes } from 'react'
-import Auth0Lock from 'auth0-lock'
-import { graphql, compose } from 'react-apollo'
+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 {
-
- constructor (props) {
- super(props)
- if(typeof window === 'undefined') return;
- const redirectUrl = `http://localhost:3000/signup`;
- console.log(redirectUrl);
+ constructor(props) {
+ super(props);
+ if (typeof window === 'undefined') return;
+ const redirectUrl = `http://localhost:6969/signup`;
this._lock = new Auth0Lock('l851ev2q8X48wf56eGLjIWFbMwwbvWPE', 'wesbos.auth0.com', {
auth: {
redirect: false,
- }
+ },
});
}
+ componentDidMount() {
+ console.log('MOUNT');
+ this._lock.on('authenticated', authResult => {
+ window.localStorage.setItem('auth0IdToken', authResult.idToken);
+ this.props.currentUserQuery.refetch();
+ });
+ // refech on page load because of the server render
+ this.props.currentUserQuery.refetch();
+ }
+
logout = () => {
window.localStorage.removeItem('auth0IdToken');
this.props.currentUserQuery.refetch();
- }
+ };
- createUser = () => {
+ createUser = async () => {
const variables = {
idToken: window.localStorage.getItem('auth0IdToken'),
emailAddress: 'wesbos@gmail.com',
- name: 'Hardcoded Wes'
+ name: 'Hardcoded Wes',
};
// TODO - make a createUser function
this.props
.createUser({ variables })
.then(response => {
// this.props.currentUserQuery.refetch();
- this.props.history.replace("/");
+ this.props.history.replace('/');
})
.catch(e => {
console.error(e);
- this.props.history.replace("/");
+ this.props.history.replace('/');
});
};
- componentDidMount() {
- console.log('MOUNT');
- this._lock.on('authenticated', (authResult) => {
- window.localStorage.setItem('auth0IdToken', authResult.idToken)
- this.props.currentUserQuery.refetch();
- })
- }
-
_showLogin = () => {
- this._lock.show()
- }
+ this._lock.show();
+ };
render() {
const { user } = this.props.currentUserQuery;
- if ( user ) {
- return <button onClick={this.logout}>Log out πŸ‘‹</button>
+ if (user) {
+ return <button onClick={this.logout}>Log out πŸ‘‹</button>;
}
return (
<div>
<button onClick={this._showLogin}>Log in with Auth0 </button>
</div>
- )
+ );
}
}
const userEnhancer = graphql(CURRENT_USER_QUERY, { name: 'currentUserQuery' });
-export default compose(userEnhancer)(LoginAuth0)
+export default compose(userEnhancer)(LoginAuth0);
diff --git a/components/Meta.js b/components/Meta.js
index 5ecea01..2655a21 100644
--- a/components/Meta.js
+++ b/components/Meta.js
@@ -1,17 +1,16 @@
import React from 'react';
-
import Head from 'next/head';
-import Nav from './Nav';
-export default () => (
+const Meta = () => (
<div>
<Head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta charSet="utf-8" />
- <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" />
<title>Sick Fits</title>
</Head>
</div>
);
+
+export default Meta;
diff --git a/components/Nav.js b/components/Nav.js
index ad0b05a..d1f341d 100644
--- a/components/Nav.js
+++ b/components/Nav.js
@@ -1,10 +1,46 @@
-import Link from 'next/link'
+import Link from 'next/link';
+import styled from 'styled-components';
-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="/cart"><a>My Cart</a></Link>
- </ul>
-)
+const StyledUl = styled.ul`
+ margin: 0;
+ padding: 0;
+ display: flex;
+ li {
+ display: flex;
+ flex: 1;
+ }
+ a {
+ padding: 10px;
+ flex: 1;
+ text-decoration: none;
+ text-align: center;
+ background: rgba(0, 0, 0, 0.2);
+ color: white;
+ margin-right: 20px;
+ &:hover {
+ background: rgba(0, 0, 0, 0.3);
+ }
+ }
+`;
+
+const Nav = () => (
+ <StyledUl>
+ <Link prefetch href="/">
+ <a>Home</a>
+ </Link>
+ <Link prefetch href="/signup">
+ <a>Sign Up</a>
+ </Link>
+ <Link prefetch href="/add">
+ <a>Add an Item</a>
+ </Link>
+ <Link prefetch href="/orders">
+ <a>Orders</a>
+ </Link>
+ <Link prefetch href="/cart">
+ <a>My Cart</a>
+ </Link>
+ </StyledUl>
+);
+
+export default Nav;
diff --git a/components/Page.js b/components/Page.js
index c05eeb7..50a6135 100644
--- a/components/Page.js
+++ b/components/Page.js
@@ -1,5 +1,5 @@
-import Header from './Header'
-import Meta from './Meta'
+import Header from './Header';
+import Meta from './Meta';
import Nav from './Nav';
import styled from 'styled-components';
@@ -13,10 +13,10 @@ const StyledPage = styled.div`
const Page = ({ children }) => (
<StyledPage className="main">
<Meta />
- <Nav></Nav>
+ <Nav />
<Header />
- { children }
+ {children}
</StyledPage>
-)
+);
export default Page;
diff --git a/components/Pagination.js b/components/Pagination.js
index 1b75ed8..fa42e35 100644
--- a/components/Pagination.js
+++ b/components/Pagination.js
@@ -1,47 +1,43 @@
-import React, { Component } from 'react'
-import { graphql, gql, compose } from 'react-apollo'
+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'
+import { Link } from '../routes';
-class Pagination extends Component {
- render() {
- const { loading, error } = this.props.allItemsQuery;
+const Pagination = props => {
+ const { loading, error } = props.allItemsQuery;
- if(loading) return <p>Loading Item...</p>
+ 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>
+ const meta = props.allItemsQuery._allItemsMeta;
+ const { page } = 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 > 1 ? (
+ <Link prefetch route="items" params={{ page: page - 1 }}>
+ <a>←Prev</a>
+ </Link>
+ ) : null}
- {
- page <= pages
- ? <Link prefetch href={{ pathname: 'items', query: { page: page + 1 }}}><a>Next β†’</a></Link>
- : null
- }
+ {page <= pages ? (
+ <Link prefetch route="items" params={{ page: page + 1 }}>
+ <a>Next β†’</a>
+ </Link>
+ ) : null}
+ </div>
+ );
+};
- </div>
- )
- }
-}
-
-const ComponentWithMutations = compose(
- graphql(ALL_ITEMS_QUERY, { name: 'allItemsQuery' })
-)(Pagination);
+const ComponentWithMutations = compose(graphql(ALL_ITEMS_QUERY, { name: 'allItemsQuery' }))(Pagination);
export default ComponentWithMutations;
diff --git a/components/Search.js b/components/Search.js
index afab3e8..63c467e 100644
--- a/components/Search.js
+++ b/components/Search.js
@@ -1,78 +1,69 @@
-import Downshift from 'downshift'
+import Downshift from 'downshift';
import { SEARCH_ITEMS_QUERY } from '../queries';
-import { graphql, compose } from 'react-apollo'
+import { graphql, compose } from 'react-apollo';
import makeImage from '../lib/image';
-import Router from 'next/router'
+import { Router } from '../routes';
import slugify from 'slugify';
function routeToItem(item) {
- Router.push({
- pathname: '/item',
- query: {
- slug: slugify(item.title),
- itemId: item.id
- }
- })
+ Router.pushRoute('item', {
+ 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
- }) => (
+ <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%' }
- })} />
+ <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>
- ))}
+ {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 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)
+const searchEnhancer = graphql(SEARCH_ITEMS_QUERY, {
+ name: 'searchItems',
+ options: {
+ variables: { searchTerm: 'camo' },
+ },
+});
+export default compose(searchEnhancer)(Search);
diff --git a/components/SingleItem.js b/components/SingleItem.js
new file mode 100644
index 0000000..297c33d
--- /dev/null
+++ b/components/SingleItem.js
@@ -0,0 +1,36 @@
+import { graphql, compose } from 'react-apollo';
+import { Motion, spring } from 'react-motion';
+import { SINGLE_ITEM_QUERY } from '../queries';
+import makeImage from '../lib/image';
+
+const SingleItem = props => {
+ if (!props.findItem.Item) return <p>Not ready</p>;
+ console.log(props.findItem.Item);
+
+ if (props.loading) return <p>Loading...</p>;
+ if (props.error) return <p>Error...</p>;
+
+ const item = props.findItem.Item;
+ return (
+ <div>
+ <img src={makeImage(item.image)} alt={item.title} />
+ <h2>Viewing {item.title}</h2>
+
+ <Motion defaultStyle={{ x: 0 }} style={{ x: spring(100) }}>
+ {value => <div>{value.x}</div>}
+ </Motion>
+ </div>
+ );
+};
+
+const ComponentWithMutations = compose(
+ graphql(SINGLE_ITEM_QUERY, {
+ name: 'findItem',
+ // This comes from Props
+ options: ({ id }) => ({
+ variables: { id },
+ }),
+ })
+)(SingleItem);
+
+export default ComponentWithMutations;