summaryrefslogtreecommitdiffstats
path: root/frontend
diff options
context:
space:
mode:
authorWes Bos <wesbos@gmail.com>2018-02-15 16:59:58 -0500
committerWes Bos <wesbos@gmail.com>2018-02-15 16:59:58 -0500
commit8f2c595eca30e2d7138c8e8d6685774a8f44e55d (patch)
tree3408b9c8eb3b0ea43cb72e13e0bd6393ab5bac61 /frontend
parenta09b01abf7c03e6b7e43f2175e5a34501808821b (diff)
omg
Diffstat (limited to 'frontend')
-rw-r--r--frontend/.vscode/settings.json2
-rw-r--r--frontend/components/AddToCart.js1
-rw-r--r--frontend/components/Cart.js136
-rw-r--r--frontend/components/CartItem.js39
-rw-r--r--frontend/components/CartList.js32
-rw-r--r--frontend/components/CreateItem.js3
-rw-r--r--frontend/components/Header.js70
-rw-r--r--frontend/components/Item.js79
-rw-r--r--frontend/components/Items.js6
-rw-r--r--frontend/components/LoginAuth0.js76
-rw-r--r--frontend/components/Meta.js3
-rw-r--r--frontend/components/Nav.js98
-rw-r--r--frontend/components/OrderList.js3
-rw-r--r--frontend/components/Page.js62
-rw-r--r--frontend/components/Pagination.js11
-rw-r--r--frontend/components/RemoveFromCart.js16
-rw-r--r--frontend/components/Search.js81
-rw-r--r--frontend/components/SingleItem.js4
-rw-r--r--frontend/components/TakeMyMoney.js37
-rw-r--r--frontend/components/ToggleCart.js22
-rw-r--r--frontend/components/styles/Title.js20
-rw-r--r--frontend/enhancers/enhancers.js22
-rw-r--r--frontend/lib/image.js6
-rw-r--r--frontend/lib/initApollo.js15
-rw-r--r--frontend/pages/_document.js26
-rw-r--r--frontend/pages/cart.js21
-rw-r--r--frontend/pages/index.js13
-rw-r--r--frontend/pages/orders.js8
-rw-r--r--frontend/queries/index.js29
-rw-r--r--frontend/static/favicon.pngbin0 -> 5122 bytes
-rw-r--r--frontend/static/nprogress.css8
31 files changed, 552 insertions, 397 deletions
diff --git a/frontend/.vscode/settings.json b/frontend/.vscode/settings.json
index a92f73f..4718ffd 100644
--- a/frontend/.vscode/settings.json
+++ b/frontend/.vscode/settings.json
@@ -1,3 +1,3 @@
{
- "eslint.enable": false
+ "eslint.enable": true
} \ No newline at end of file
diff --git a/frontend/components/AddToCart.js b/frontend/components/AddToCart.js
index 5a11513..6cab321 100644
--- a/frontend/components/AddToCart.js
+++ b/frontend/components/AddToCart.js
@@ -7,7 +7,6 @@ import { CURRENT_USER_QUERY, SINGLE_ITEM_QUERY } from '../queries';
import { removeFromCartEnhancer, userEnhancer, addtoCartEnhancer } from '../enhancers/enhancers';
console.log(addtoCartEnhancer);
-import makeImage from '../lib/image';
const JumpImg = styled.img`
border: 0 solid black;
diff --git a/frontend/components/Cart.js b/frontend/components/Cart.js
index 72c34ae..78e0d88 100644
--- a/frontend/components/Cart.js
+++ b/frontend/components/Cart.js
@@ -1,9 +1,14 @@
import { Component } from 'react';
import { graphql, compose } from 'react-apollo';
import styled from 'styled-components';
-import { networkEnhancer, userEnhancer, uiEnhancer } from '../enhancers/enhancers';
+import Title from './styles/Title';
+import { updateUIEnhancer, userEnhancer, uiEnhancer } from '../enhancers/enhancers';
import RemoveFromCart from './RemoveFromCart';
import TakeMyMoney from './TakeMyMoney';
+import ToggleCart from './ToggleCart';
+import formatMoney from '../lib/formatMoney';
+
+import CartItem from './CartItem';
const CartStyles = styled.div`
padding: 20px;
@@ -13,75 +18,104 @@ const CartStyles = styled.div`
height: 100%;
top: 0;
right: 0;
+ width: 40%;
+ min-width: 500px;
bottom: 0;
transform: translateX(100%);
transition: all 0.3s;
box-shadow: 0 0 10px 3px rgba(0, 0, 0, 0.2);
z-index: 5;
+ display: grid;
+ grid-template-rows: auto 1fr auto;
${props => props.open && `transform: translateX(0);`};
- .toggleOpen {
- position: absolute;
- top: 0;
- left: 0;
- transform: translateX(-100%);
+ header {
+ border-bottom: 5px solid ${props => props.theme.black};
+ margin-bottom: 2rem;
+ padding-bottom: 2rem;
+ }
+ footer {
+ border-top: 10px double ${props => props.theme.black};
+ margin-top: 2rem;
+ padding-top: 2rem;
+ display: grid;
+ grid-template-columns: auto auto;
+ align-items: center;
+ font-size: 3rem;
+ font-weight: 900;
+ p {
+ margin: 0;
+ }
}
`;
+const CartUl = styled.ul`
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ overflow: scroll;
+`;
+
+const Supreme = styled.h3`
+ background: ${props => props.theme.red};
+ color: white;
+ display: inline-block;
+ padding: 4px 5px;
+ transform: skew(-3deg);
+ margin: 0;
+ font-size: 4rem;
+`;
+
+const CloseButton = styled.button`
+ background: black;
+ color: white;
+ font-size: 3rem;
+ border: 0;
+ position: absolute;
+ z-index: 2;
+ right: 0;
+`;
+
class Cart extends Component {
- state = {
- open: true,
- };
componentDidMount() {
- console.log('refetching..');
- setTimeout(this.props.currentUser.refetch, 10);
+ // TODO listen for escape
+ }
+
+ componentWillUnmount() {
+ // TODO unlisten for escape
}
- toggleOpen = () => {
- this.setState({ open: !this.state.open });
- };
- handleClick = () => {
- console.log('CLICK');
- this.props.updateNetworkStatus(!this.props.ui.networkStatus.isConnected);
- };
render() {
- if (!this.props.currentUser.me) {
- return null;
- }
const { me } = this.props.currentUser;
+ if (!me) return null;
+ const itemCount = me.cart.length;
+ const quantityCount = me.cart.reduce((tally, cartItem) => tally + cartItem.quantity, 0);
+ const totalPrice = me.cart.reduce((tally, cartItem) => tally + cartItem.quantity * cartItem.item.price, 0);
return (
- <CartStyles className="cart" open={this.state.open}>
- <button onClick={this.handleClick}>
- Toggle Network Status:: {this.props.ui.networkStatus.isConnected.toString()}
- </button>
- <button className="toggleOpen" onClick={this.toggleOpen}>
- Open Cart
- </button>
-
- <p>🛒: {me.cart.length} Items</p>
- <ul>
- {me.cart.map(cartItem => (
- <li key={cartItem.id}>
- <strong>
- {cartItem.quantity} of {cartItem.item.title}
- </strong>
- <RemoveFromCart id={cartItem.id} />
- </li>
- ))}
- </ul>
+ <CartStyles open={this.props.ui.uiData.isCartOpen}>
+ <header>
+ <ToggleCart
+ render={toggle => (
+ <CloseButton title="close" onClick={toggle}>
+ &times;
+ </CloseButton>
+ )}
+ />
+ <Supreme>Your Cart.</Supreme>
+ <p>
+ You have {itemCount} item{itemCount === 1 ? '' : 's'} in your cart. S{'I'.repeat(quantityCount)}CK
+ </p>
+ </header>
- <TakeMyMoney>
- <hr />
- <button>Checkout!!!</button>
- </TakeMyMoney>
- {/* Theren
- {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)} /> */}
+ <CartUl>{me.cart.map(cartItem => <CartItem key={cartItem.id} cartItem={cartItem} />)}</CartUl>
+ <footer>
+ <p>{formatMoney(totalPrice)}</p>
+ <TakeMyMoney>
+ <button>Checkout</button>
+ </TakeMyMoney>
+ </footer>
</CartStyles>
);
}
}
-export default compose(userEnhancer, networkEnhancer, uiEnhancer)(Cart);
+export default compose(userEnhancer, uiEnhancer)(Cart);
diff --git a/frontend/components/CartItem.js b/frontend/components/CartItem.js
new file mode 100644
index 0000000..83c948e
--- /dev/null
+++ b/frontend/components/CartItem.js
@@ -0,0 +1,39 @@
+import formatMoney from '../lib/formatMoney';
+import RemoveFromCart from './RemoveFromCart';
+import styled from 'styled-components';
+
+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 }) => (
+ <CartItemStyles key={cartItem.id}>
+ <img width="100" src={cartItem.item.image} alt={cartItem.item.title} />
+ <div className="cart-item-details">
+ <h3>{cartItem.item.title}</h3>
+ <p>
+ {formatMoney(cartItem.quantity * cartItem.item.price)}
+ {' — '}
+ <em>
+ {cartItem.quantity} &times; {formatMoney(cartItem.item.price)} each
+ </em>
+ </p>
+ </div>
+ <RemoveFromCart id={cartItem.id} />
+ </CartItemStyles>
+);
+
+export default CartItem;
diff --git a/frontend/components/CartList.js b/frontend/components/CartList.js
index bf08f87..1a44012 100644
--- a/frontend/components/CartList.js
+++ b/frontend/components/CartList.js
@@ -1,21 +1,11 @@
// I THINK THIS CAN BE DELETED
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, GET_CART_STATE } from '../queries';
-import { graphql, compose } from 'react-apollo';
-import has from 'lodash.has';
-import get from 'lodash.get';
+import { compose } from 'react-apollo';
import styled from 'styled-components';
+import PropTypes from 'prop-types';
import formatMoney from '../lib/formatMoney';
-import makeImage from '../lib/image';
import TakeMyMoney from './TakeMyMoney';
-import { removeFromCartEnhancer } from '../enhancers/enhancers';
-import { CURRENT_USER_QUERY } from '../queries';
-import PropTypes from 'prop-types';
+import { removeFromCartEnhancer, userEnhancer, uiEnhancer } from '../enhancers/enhancers';
const CartStyles = styled.div`
padding: 20px;
@@ -44,15 +34,6 @@ class CartList extends Component {
client: PropTypes.object,
};
- componentDidMount() {
- setTimeout(this.props.currentUserQuery.refetch, 1);
- // Set the cart state to be closed
- this.context.client.writeQuery({
- query: GET_CART_STATE,
- data: { ui: { isCartOpen: true, __typename: 'Network' } },
- });
- }
-
toggleOpen = () => {
this.props.uiQuery.updateQuery(() => ({ ui: { isCartOpen: false, __typename: 'Network' } }));
};
@@ -66,15 +47,13 @@ class CartList extends Component {
return <p>Error....</p>;
}
- // if (!has(this.props, 'currentUserQuery.user.cart')) {
// return <p>Don't have it yet!</p>;
// }
// OMG Clean this up
- let { user = {} } = this.props.currentUserQuery || {};
+
if (!user) user = {};
const cart = user.cart || [];
const userId = user.id;
- // const userId = this.props.currentUserQuery.user.id;
const total = cart.reduce((a, b) => a + b.price, 0);
return (
@@ -83,7 +62,6 @@ class CartList extends Component {
Open Cart
</button>
<h1>{cart.length} Items</h1>
-
<ul>
{cart.map(item => (
<li key={item.id}>
@@ -110,6 +88,4 @@ class CartList extends Component {
}
}
-const userEnhancer = graphql(CURRENT_USER_QUERY, { name: 'currentUserQuery' });
-const uiEnhancer = graphql(GET_CART_STATE, { name: 'uiQuery' });
export default compose(userEnhancer, removeFromCartEnhancer, uiEnhancer)(CartList);
diff --git a/frontend/components/CreateItem.js b/frontend/components/CreateItem.js
index 47e98ec..0d43c68 100644
--- a/frontend/components/CreateItem.js
+++ b/frontend/components/CreateItem.js
@@ -2,7 +2,6 @@ import React, { Component } from 'react';
import { graphql, gql } from 'react-apollo';
import { ALL_ITEMS_QUERY, CREATE_ITEM_MUTATION } from '../queries';
import ErrorMessage from './ErrorMessage';
-import makeImage from '../lib/image';
import { fileEndpoint } from '../config';
class CreateLink extends Component {
@@ -102,7 +101,7 @@ class CreateLink extends Component {
type="text"
placeholder="The desc for this item"
/>
- <button disabledx={!this.state.loading} type="submit">
+ <button disabled={!this.state.loading} type="submit">
Submit
</button>
</form>
diff --git a/frontend/components/Header.js b/frontend/components/Header.js
index aa3c3d8..0183e4a 100644
--- a/frontend/components/Header.js
+++ b/frontend/components/Header.js
@@ -1,40 +1,66 @@
import { Component } from 'react';
-import { graphql, compose } from 'react-apollo';
+import { compose } from 'react-apollo';
import { userEnhancer } from '../enhancers/enhancers';
-import Cart from './Cart';
-import Login from './LoginAuth0';
-import Search from './Search';
import NProgress from 'nprogress';
import Router from 'next/router';
+import styled from 'styled-components';
+import Link from 'next/link';
import Signout from './Signout';
+import Cart from './Cart';
+import Search from './Search';
+import Nav from './Nav';
Router.onRouteChangeStart = url => {
- console.log(`Loading: ${url}`);
NProgress.start();
};
Router.onRouteChangeComplete = () => NProgress.done();
Router.onRouteChangeError = () => NProgress.done();
-class Header 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
- // setTimeout(this.props.currentUserQuery.refetch, 1);
- console.log(this.props.currentUser);
+const StyledHeader = styled.header`
+ .bar {
+ border-bottom: 10px solid ${props => props.theme.black};
+ display: grid;
+ grid-template-columns: auto 1fr;
+ justify-content: space-between;
+ align-items: stretch;
+ }
+ .sub-bar {
+ display: grid;
+ grid-template-columns: 1fr auto;
+ border-bottom: 1px solid ${props => props.theme.lightgrey};
}
+`;
- render() {
- return (
- <div>
- {this.props.currentUser.me ? this.props.currentUser.me.email : 'Not Signed in'}
- <Signout />
- <Cart />
- {/* <Search /> */}
- </div>
- );
+const Logo = styled.h1`
+ font-size: 4rem;
+ margin-left: 2rem;
+ transform: skew(-5deg);
+ a {
+ padding: 1rem 1.5rem;
+ background: ${props => props.theme.red};
+ color: white;
+ letter-spacing: -2px;
+ text-transform: uppercase;
}
-}
+`;
+
+const Header = props => (
+ <StyledHeader>
+ <div className="bar">
+ <Logo>
+ <Link href="/">
+ <a>Sick Fits!</a>
+ </Link>
+ </Logo>
+ <Nav />
+ </div>
+ <div className="sub-bar">
+ <Search />
+ <Signout />
+ </div>
+ <Cart />
+ </StyledHeader>
+);
export default compose(userEnhancer)(Header);
// export default Header;
diff --git a/frontend/components/Item.js b/frontend/components/Item.js
index 42a6c87..87fb0b9 100644
--- a/frontend/components/Item.js
+++ b/frontend/components/Item.js
@@ -1,31 +1,74 @@
import React from 'react';
+import Title from './styles/Title';
import styled from 'styled-components';
import slugify from 'slugify';
import { compose } from 'react-apollo';
import { Link } from '../routes';
import AddToCart from './AddToCart';
-import makeImage from '../lib/image';
import formatMoney from '../lib/formatMoney';
import { removeItemMutation } from '../enhancers/enhancers';
const Item = styled.div`
- background: #f3f3f3;
- padding: 5px;
+ background: white;
+ border: 1px solid ${props => props.theme.offWhite};
+ box-shadow: 0 12px 24px 0 rgba(0, 0, 0, 0.09);
+ position: relative;
+ display: grid;
+ align-content: start;
+ /* grid-template-rows: repeat(auto-fit, minmax(100px,)); */
+ grid-auto-rows: fit-content;
img {
width: 100%;
}
+ p {
+ font-size: 14px;
+ line-height: 2;
+ font-weight: 600;
+ padding: 0 3rem;
+ font-size: 1.5rem;
+ }
+ .buttonList {
+ display: grid;
+ border-top: 1px solid ${props => props.theme.lightgrey};
+ grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
+ grid-gap: 1px;
+ background: ${props => props.theme.lightgrey};
+ align-self: end;
+ & > * {
+ background: white;
+ border: 0;
+ text-align: center;
+ font-size: 1rem;
+ padding: 1rem;
+ }
+ }
+`;
+
+const PriceTag = styled.span`
+ background: ${props => props.theme.red};
+ transform: rotate(2deg);
+ color: white;
+ font-weight: 600;
+ padding: 5px;
+ font-size: 3rem;
+ display: inline-block;
+ position: absolute;
+ top: -3px;
+ right: -3px;
`;
class ItemComponent extends React.Component {
removeItem = () => {
- this.props.removeItem({ variables: { id: this.props.item.id } });
+ if (confirm('Are you sure you want to delete this item?')) {
+ this.props.removeItem({ variables: { id: this.props.item.id } });
+ }
};
render() {
const item = this.props.item;
return (
<Item key={item.id}>
{item.image ? <img src={item.image} alt={item.title} /> : null}
- <h3>
+ <Title>
<Link
route="item"
params={{
@@ -35,20 +78,24 @@ class ItemComponent extends React.Component {
>
<a>{item.title}</a>
</Link>
- </h3>
+ </Title>
+
+ <PriceTag>{formatMoney(item.price)}</PriceTag>
<p>{item.description}</p>
- <Link
- href={{
- pathname: '/admin/update',
- query: { id: item.id },
- }}
- >
- <a>Edit ✏️</a>
- </Link>
- <AddToCart id={item.id} />
- <button onClick={this.removeItem}>&times; Delete item</button>
+ <div className="buttonList">
+ <Link
+ href={{
+ pathname: '/admin/update',
+ query: { id: item.id },
+ }}
+ >
+ <a>Edit ✏️</a>
+ </Link>
+ <AddToCart id={item.id} />
+ <button onClick={this.removeItem}>&times; Delete item</button>
+ </div>
</Item>
);
}
diff --git a/frontend/components/Items.js b/frontend/components/Items.js
index a3f98c9..6206592 100644
--- a/frontend/components/Items.js
+++ b/frontend/components/Items.js
@@ -13,8 +13,10 @@ const Title = styled.h1`
const Items = styled.div`
display: grid;
- grid-template-columns: repeat(4, 1fr);
- grid-gap: 20px;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ grid-gap: 60px;
+ max-width: ${props => props.theme.maxWidth};
+ margin: 0 auto;
`;
class ItemList extends Component {
diff --git a/frontend/components/LoginAuth0.js b/frontend/components/LoginAuth0.js
deleted file mode 100644
index b55ae4a..0000000
--- a/frontend/components/LoginAuth0.js
+++ /dev/null
@@ -1,76 +0,0 @@
-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`;
- this._lock = new Auth0Lock('r6D64Waoq7roAnv8GT04dnn1tpq9PAqu', 'wesbos.auth0.com', {
- auth: {
- redirect: false,
- },
- });
- }
-
- componentDidMount() {
- this._lock.on('authenticated', authResult => {
- console.log(authResult);
- window.localStorage.setItem('auth0IdToken', authResult.idToken);
- console.log('Im baaaack!');
- this.props.currentUserQuery.refetch();
- });
-
- this._lock.on('authorization_error', err => {
- console.error(err);
- });
-
- // refech on page load because of the server render
- this.props.currentUserQuery.refetch();
- }
-
- logout = () => {
- window.localStorage.removeItem('auth0IdToken');
- this.props.currentUserQuery.refetch();
- };
-
- createUser = async () => {
- const variables = {
- idToken: window.localStorage.getItem('auth0IdToken'),
- emailAddress: 'wesbos@gmail.com',
- name: 'Hardcoded Wes',
- };
- // TODO - make a createUser function
- this.props
- .createUser({ variables })
- .then(response => {
- // this.props.currentUserQuery.refetch();
- this.props.history.replace('/');
- })
- .catch(e => {
- console.error(e);
- this.props.history.replace('/');
- });
- };
-
- _showLogin = () => {
- this._lock.show();
- };
-
- 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>
- </div>
- );
- }
-}
-
-const userEnhancer = graphql(CURRENT_USER_QUERY, { name: 'currentUserQuery' });
-export default compose(userEnhancer)(LoginAuth0);
diff --git a/frontend/components/Meta.js b/frontend/components/Meta.js
index d7a2e17..bee5a69 100644
--- a/frontend/components/Meta.js
+++ b/frontend/components/Meta.js
@@ -12,8 +12,7 @@ const Meta = () => (
}
`}
<meta charSet="utf-8" />
- <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="shortcut icon" href="/static/favicon.png" />
<link rel="stylesheet" type="text/css" href="/static/nprogress.css" />
<title>Sick Fits</title>
diff --git a/frontend/components/Nav.js b/frontend/components/Nav.js
index 25965c2..9793087 100644
--- a/frontend/components/Nav.js
+++ b/frontend/components/Nav.js
@@ -1,49 +1,95 @@
-import Link from "next/link";
-import styled from "styled-components";
+import Link from 'next/link';
+import styled from 'styled-components';
+import { userEnhancer } from '../enhancers/enhancers';
+import { compose } from 'react-apollo';
+import ToggleCart from './ToggleCart';
const StyledUl = styled.ul`
margin: 0;
padding: 0;
display: flex;
- li {
+ justify-self: end;
+ a,
+ button {
+ padding: 0 30px;
display: flex;
- flex: 1;
+ align-items: center;
+ position: relative;
+ text-transform: uppercase;
+ font-weight: 900;
+ font-size: 2rem;
+ background: none;
+ border: 0;
+ &:before {
+ content: '';
+ width: 1px;
+ background: ${props => props.theme.lightgrey};
+ height: 100%;
+ left: 0;
+ position: absolute;
+ transform: rotate(20deg);
+ top: 0;
+ bottom: 0;
+ }
+ &:after {
+ height: 2px;
+ background: red;
+ content: '';
+ width: 0;
+ position: absolute;
+ transform: translateX(-50%);
+ transition: width 0.6s;
+ transition-timing-function: cubic-bezier(1, -0.65, 0, 2.31);
+ left: 50%;
+ margin-top: 2rem;
+ }
+ &:hover,
+ &:focus {
+ outline: none;
+ &:after {
+ width: calc(100% - 60px);
+ }
+ }
}
- a {
- padding: 10px;
- flex: 1;
- text-decoration: none;
- text-align: center;
- background: rgba(0, 0, 0, 0.2);
+ .cart-count {
+ background: ${props => props.theme.red};
color: white;
- margin-right: 20px;
- &:hover {
- background: rgba(0, 0, 0, 0.3);
- }
+ border-radius: 50%;
+ padding: 5px;
+ margin-left: 1rem;
+ font-weight: 100;
+ font-feature-settings: 'tnum';
+ font-variant-numeric: tabular-nums;
}
`;
-const Nav = () => (
+const Nav = ({ currentUser }) => (
<StyledUl>
- <Link prefetch href="/">
- <a>Home</a>
- </Link>
<Link prefetch href="/items">
- <a>Just Items</a>
+ <a>Shop</a>
+ </Link>
+ <Link prefetch href="/add">
+ <a>Sell</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>
+ {currentUser.me ? (
+ <ToggleCart
+ render={toggle => (
+ <button onClick={toggle}>
+ My Cart
+ <span className="cart-count">
+ {currentUser.me.cart.reduce((tally, cartItem) => tally + cartItem.quantity, 0)}
+ </span>
+ </button>
+ )}
+ />
+ ) : null}
</StyledUl>
);
-export default Nav;
+export default compose(userEnhancer)(Nav);
diff --git a/frontend/components/OrderList.js b/frontend/components/OrderList.js
index e478984..5e32ba2 100644
--- a/frontend/components/OrderList.js
+++ b/frontend/components/OrderList.js
@@ -3,14 +3,12 @@ import { USER_ORDERS_QUERY } from '../queries';
import { graphql, compose } from 'react-apollo';
import { format, formatDistance } from 'date-fns';
import formatMoney from '../lib/formatMoney';
-import makeImage from '../lib/image';
class OrderList extends Component {
componentDidMount() {
this.props.userOrdersQuery.refetch();
}
render() {
- console.log(this.props.userOrdersQuery);
if (this.props.loading) {
return <p>Loading...</p>;
}
@@ -23,7 +21,6 @@ class OrderList extends Component {
if (!orders || !orders.length) return <p>No Orders Yet!</p>;
- console.log(orders);
return (
<div>
<h2>You have {orders.length} Orders</h2>
diff --git a/frontend/components/Page.js b/frontend/components/Page.js
index 93cd7c9..66c65e6 100644
--- a/frontend/components/Page.js
+++ b/frontend/components/Page.js
@@ -1,44 +1,56 @@
-import styled from 'styled-components';
+import styled, { ThemeProvider, injectGlobal } from 'styled-components';
import Header from './Header';
import Meta from './Meta';
import Nav from './Nav';
import CartList from './CartList';
import { Component } from 'react';
-class ErrorBoundary extends Component {
- state = {
- error: null,
- errorInfo: null,
- };
- componentDidCatch(error, errorInfo) {
- console.log('Caught an erorrror!');
- this.setState({ error, errorInfo });
+// Global Style
+
+const theme = {
+ red: '#FF0000',
+ black: '#393939',
+ grey: '#3A3A3A',
+ lightgrey: '#E1E1E1',
+ offWhite: '#EDEDED',
+ maxWidth: '1300px',
+};
+
+const globals = injectGlobal`
+ html {
+ box-sizing: border-box;
+ font-size: 10px;
+ }
+ body {
+ font-family: 'radnika next', sans-serif;
+ padding: 0;
+ background-color: #ffffff;
+ background: blue;
+ margin: 0;
+ font-size: 1.5rem;
}
- render() {
- if (this.state.error) {
- return <p>Shit! AN error! {this.state.error.message}</p>;
- }
- return this.props.children;
+ *, *:before, *:after {
+ box-sizing: inherit;
}
-}
+ a {
+ color: ${theme.black};
+ text-decoration: none;
+ }
+`;
const StyledPage = styled.div`
- font-family: sans-serif;
- color: #303030;
- background: #efc600;
- padding: 100px;
+ color: ${props => props.theme.black};
+ background: white;
`;
const Page = ({ children }) => (
- <StyledPage className="main">
- <ErrorBoundary>
+ <ThemeProvider theme={theme}>
+ <StyledPage className="main">
<Meta />
- <Nav />
<Header />
- {/* <CartList /> */}
{children}
- </ErrorBoundary>
- </StyledPage>
+ </StyledPage>
+ </ThemeProvider>
);
export default Page;
diff --git a/frontend/components/Pagination.js b/frontend/components/Pagination.js
index 2cb5080..d00778b 100644
--- a/frontend/components/Pagination.js
+++ b/frontend/components/Pagination.js
@@ -1,15 +1,10 @@
-import React, { Component } from 'react';
-import { graphql, gql, compose } from 'react-apollo';
-import { ALL_ITEMS_QUERY } from '../queries';
-import withData from '../lib/withData';
+import React from 'react';
+import { compose } from 'react-apollo';
import { Link } from '../routes';
import { itemEnhancer } from '../enhancers/enhancers';
const Pagination = props => {
- const { loading, error } = props.itemsQuery;
-
- if (loading) return <p>Loading Item...</p>;
-
+ if (props.loading) return <p>Loading Item...</p>;
const { aggregate, pageInfo } = props.itemsQuery.itemsConnection;
const { page } = props;
diff --git a/frontend/components/RemoveFromCart.js b/frontend/components/RemoveFromCart.js
index e3eb919..8d0d214 100644
--- a/frontend/components/RemoveFromCart.js
+++ b/frontend/components/RemoveFromCart.js
@@ -5,6 +5,16 @@ import styled from 'styled-components';
import PropTypes from 'prop-types';
import { removeFromCartEnhancer } from '../enhancers/enhancers';
+const BigButton = styled.button`
+ font-size: 3rem;
+ background: none;
+ border: 0;
+ &:hover {
+ color: ${props => props.theme.red};
+ cursor: pointer;
+ }
+`;
+
class RemoveFromCart extends Component {
handleRemoveFromCart = async () => {
console.log(`gonna remove item from cart`);
@@ -18,7 +28,11 @@ class RemoveFromCart extends Component {
};
render() {
- return <button onClick={this.handleRemoveFromCart}>&times; Remove</button>;
+ return (
+ <BigButton title="Remove From Cart" onClick={this.handleRemoveFromCart}>
+ &times;
+ </BigButton>
+ );
}
}
diff --git a/frontend/components/Search.js b/frontend/components/Search.js
index 0ce136e..33017dd 100644
--- a/frontend/components/Search.js
+++ b/frontend/components/Search.js
@@ -1,9 +1,9 @@
import Downshift from 'downshift';
-import { SEARCH_ITEMS_QUERY } from '../queries';
import { graphql, compose } from 'react-apollo';
-import makeImage from '../lib/image';
-import { Router } from '../routes';
import slugify from 'slugify';
+import styled from 'styled-components';
+import { SEARCH_ITEMS_QUERY } from '../queries';
+import { Router } from '../routes';
function routeToItem(item) {
Router.pushRoute('item', {
@@ -12,38 +12,62 @@ function routeToItem(item) {
});
}
-function BasicAutocomplete(props) {
+const DropDown = styled.div`
+ position: absolute;
+ width: 100%;
+ z-index: 2;
+ border: 1px solid ${props => props.theme.lightgrey};
+ input {
+ border: 0;
+ }
+`;
+
+const DropDownItem = styled.div`
+ border-bottom: 1px solid ${props => props.theme.lightgrey};
+ background: ${props => (props.highlighted ? '#f7f7f7' : 'white')};
+ padding: 1rem;
+ transition: all 0.2s;
+ ${props => (props.highlighted ? 'padding-left: 2rem;' : null)};
+ display: flex;
+ align-items: center;
+ border-left: 10px solid ${props => (props.highlighted ? props.theme.lightgrey : 'white')};
+ img {
+ margin-right: 10px;
+ }
+`;
+
+const SearchStyles = styled.div`
+ position: relative;
+`;
+
+function AutoComplete(props) {
const { items, onChange } = props;
return (
- <Downshift onChange={routeToItem} itemToString={item => item.title}>
+ <Downshift onChange={routeToItem} itemToString={i => (i === null ? '' : i.title)}>
{({ getInputProps, getItemProps, isOpen, inputValue, selectedItem, highlightedIndex }) => (
<div>
+ {/* This is the searchInput */}
<input
{...getInputProps({
placeholder: 'Search For Item',
- onChange: e => props.refetch({ searchTerm: e.target.value }),
- style: { fontSize: '20px', padding: '20px', display: 'block', width: '100%' },
+ id: 'search',
+ onChange: e => {
+ console.log('Change happened');
+ props.refetch({ searchTerm: e.target.value });
+ },
+ style: { fontSize: '20px', padding: '10px', display: 'block', width: '100%' },
})}
/>
{isOpen ? (
- <div>
+ <DropDown>
+ {/* This is the Dropdown */}
{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} />
+ <DropDownItem {...getItemProps({ item })} key={item.id} highlighted={highlightedIndex === index}>
+ <img width="50" src={item.image} alt={item.title} />
{item.title}
- </div>
+ </DropDownItem>
))}
- </div>
+ </DropDown>
) : null}
</div>
)}
@@ -52,16 +76,19 @@ function BasicAutocomplete(props) {
}
const Search = props => (
- <BasicAutocomplete
- items={props.searchItems.allItems}
- onChange={selectedItem => console.log(selectedItem)}
- refetch={props.searchItems.refetch}
- />
+ <SearchStyles>
+ <AutoComplete
+ items={props.searchItems.items}
+ onChange={selectedItem => console.log(selectedItem)}
+ refetch={props.searchItems.refetch}
+ />
+ </SearchStyles>
);
const searchEnhancer = graphql(SEARCH_ITEMS_QUERY, {
name: 'searchItems',
options: {
+ // TODO: Why is this camo
variables: { searchTerm: 'camo' },
},
});
diff --git a/frontend/components/SingleItem.js b/frontend/components/SingleItem.js
index 47dbd0e..58da71c 100644
--- a/frontend/components/SingleItem.js
+++ b/frontend/components/SingleItem.js
@@ -3,9 +3,9 @@ import { SINGLE_ITEM_QUERY } from '../queries';
import { singleItemEnhancer } from '../enhancers/enhancers';
const SingleItem = props => {
- if (props.loading || !props.item) return <p>Loading...</p>;
- if (props.error) return <p>Error...</p>;
console.log(props);
+ if (props.loading) return <p>Loading...</p>;
+ if (props.error) return <p>Error...</p>;
const item = props.findItem.items[0];
return (
<div>
diff --git a/frontend/components/TakeMyMoney.js b/frontend/components/TakeMyMoney.js
index 30a19dc..5bdc409 100644
--- a/frontend/components/TakeMyMoney.js
+++ b/frontend/components/TakeMyMoney.js
@@ -7,22 +7,12 @@ import formatMoney from '../lib/formatMoney';
class TakeMyMoney extends Component {
onToken = async res => {
console.log('We got a toooken!');
- 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() {
@@ -31,21 +21,18 @@ class TakeMyMoney extends Component {
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={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={me.email}
- >
- {formatMoney(total)}
- {this.props.children}
- </StripeCheckout>
- </div>
+ <StripeCheckout
+ 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={me.email}
+ >
+ {this.props.children}
+ </StripeCheckout>
);
}
}
diff --git a/frontend/components/ToggleCart.js b/frontend/components/ToggleCart.js
new file mode 100644
index 0000000..c80baa6
--- /dev/null
+++ b/frontend/components/ToggleCart.js
@@ -0,0 +1,22 @@
+import { Component } from 'react';
+import { compose } from 'react-apollo';
+import { uiEnhancer, updateUIEnhancer } from '../enhancers/enhancers';
+import PropTypes from 'prop-types';
+
+class ToggleCart extends Component {
+ static propTypes = {
+ render: PropTypes.func.isRequired,
+ toggleCart: PropTypes.func.isRequired,
+ ui: PropTypes.object.isRequired,
+ };
+ toggle = e => {
+ // if this is a keyPress event, make sure it's the enter key
+ if (e.keyCode && e.keyCode !== 16) return;
+ this.props.toggleCart(!this.props.ui.uiData.isCartOpen);
+ };
+ render() {
+ return this.props.render(this.toggle);
+ }
+}
+
+export default compose(uiEnhancer, updateUIEnhancer)(ToggleCart);
diff --git a/frontend/components/styles/Title.js b/frontend/components/styles/Title.js
new file mode 100644
index 0000000..e1901ec
--- /dev/null
+++ b/frontend/components/styles/Title.js
@@ -0,0 +1,20 @@
+import styled from 'styled-components';
+
+const Title = styled.h3`
+ margin: 0 1rem;
+ text-align: center;
+ transform: skew(-5deg) rotate(-1deg);
+ margin-top: -3rem;
+ text-shadow: 2px 2px 0 rgba(0, 0, 0, 0.1);
+ a {
+ background: ${props => props.theme.red};
+ display: inline;
+ line-height: 1.3;
+ font-size: 4rem;
+ text-align: center;
+ color: white;
+ padding: 0 1rem;
+ }
+`;
+
+export default Title;
diff --git a/frontend/enhancers/enhancers.js b/frontend/enhancers/enhancers.js
index 88bebe4..ded59b9 100644
--- a/frontend/enhancers/enhancers.js
+++ b/frontend/enhancers/enhancers.js
@@ -6,8 +6,9 @@ import {
SINGLE_ITEM_QUERY,
CURRENT_USER_QUERY,
ADD_TO_CART_MUTATION,
- UPDATE_NETWORK_STATUS,
+ UPDATE_UI,
GET_UI_STATE,
+ TOGGLE_CART,
} from '../queries/index';
export const itemEnhancer = graphql(ALL_ITEMS_QUERY, {
@@ -79,8 +80,21 @@ export const userEnhancer = graphql(CURRENT_USER_QUERY, { name: 'currentUser' })
export const uiEnhancer = graphql(GET_UI_STATE, { name: 'ui' });
-export const networkEnhancer = graphql(UPDATE_NETWORK_STATUS, {
- props: ({ mutate }) => ({
- updateNetworkStatus: isConnected => mutate({ variables: { isConnected } }),
+export const updateUIEnhancer = graphql(UPDATE_UI, {
+ props: data => ({
+ updateNetworkStatus: isConnected =>
+ data.mutate({
+ variables: {
+ ...data.ownProps.ui.uiData,
+ isConnected,
+ },
+ }),
+ toggleCart: isCartOpen =>
+ data.mutate({
+ variables: {
+ ...data.ownProps.ui.uiData,
+ isCartOpen,
+ },
+ }),
}),
});
diff --git a/frontend/lib/image.js b/frontend/lib/image.js
deleted file mode 100644
index 5b6efb9..0000000
--- a/frontend/lib/image.js
+++ /dev/null
@@ -1,6 +0,0 @@
-import { imageEndpoint } from '../config';
-
-export default function(image, width = 300, height = 300) {
- if (!image || !image.secret) return '';
- return `${imageEndpoint}/${image.secret}/${width}x${height}`;
-}
diff --git a/frontend/lib/initApollo.js b/frontend/lib/initApollo.js
index 45f0fec..e86c7e1 100644
--- a/frontend/lib/initApollo.js
+++ b/frontend/lib/initApollo.js
@@ -20,20 +20,19 @@ function create(initialState) {
const stateLink = withClientState({
cache,
defaults: {
- networkStatus: {
- __typename: 'NetworkStatus',
+ uiData: {
+ __typename: 'UIData',
isConnected: true,
- isCartOpen: true,
+ isCartOpen: false,
},
},
resolvers: {
Mutation: {
- updateNetworkStatus: (_, { isConnected }, { cache }) => {
- console.log(isConnected);
+ setLocalData: (_, values, { cache }) => {
const data = {
- networkStatus: {
- __typename: 'NetworkStatus',
- isConnected,
+ uiData: {
+ __typename: 'UIData',
+ ...values,
},
};
cache.writeData({ data });
diff --git a/frontend/pages/_document.js b/frontend/pages/_document.js
new file mode 100644
index 0000000..e9a6570
--- /dev/null
+++ b/frontend/pages/_document.js
@@ -0,0 +1,26 @@
+import Document, { Head, Main, NextScript } from 'next/document';
+import { ServerStyleSheet } from 'styled-components';
+
+export default class MyDocument extends Document {
+ static getInitialProps({ renderPage }) {
+ const sheet = new ServerStyleSheet();
+ const page = renderPage(App => props => sheet.collectStyles(<App {...props} />));
+ const styleTags = sheet.getStyleElement();
+ return { ...page, styleTags };
+ }
+
+ render() {
+ return (
+ <html>
+ <Head>
+ <title>My page</title>
+ {this.props.styleTags}
+ </Head>
+ <body>
+ <Main />
+ <NextScript />
+ </body>
+ </html>
+ );
+ }
+}
diff --git a/frontend/pages/cart.js b/frontend/pages/cart.js
deleted file mode 100644
index afdacf7..0000000
--- a/frontend/pages/cart.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import { Component } from 'react';
-import withData from '../lib/withData';
-import Items from '../components/Items';
-import CreateItem from '../components/CreateItem';
-import Signup from '../components/Signup';
-import LoginAuth0 from '../components/LoginAuth0';
-import Page from '../components/Page';
-import CartList from '../components/CartList';
-
-class Orders extends Component {
- render() {
- return (
- <Page>
- <h2>My Cart</h2>
- <CartList />
- </Page>
- );
- }
-}
-
-export default withData(Orders);
diff --git a/frontend/pages/index.js b/frontend/pages/index.js
index 128ccf1..d4625c5 100644
--- a/frontend/pages/index.js
+++ b/frontend/pages/index.js
@@ -1,22 +1,19 @@
-import { Component } from "react";
-import PropTypes from "prop-types";
-import Items from "../components/Items";
-import Header from "../components/Header";
-import Page from "../components/Page";
-import withData from "../lib/withData";
+import PropTypes from 'prop-types';
+import Items from '../components/Items';
+import Page from '../components/Page';
+import withData from '../lib/withData';
const Home = props => {
const page = parseInt(props.url.query.page) || 1;
return (
<Page>
- <p>Hello</p>
<Items page={page} />
</Page>
);
};
Home.propTypes = {
- url: PropTypes.object
+ url: PropTypes.object.isRequired,
};
export default withData(Home);
diff --git a/frontend/pages/orders.js b/frontend/pages/orders.js
index 4025868..15004a9 100644
--- a/frontend/pages/orders.js
+++ b/frontend/pages/orders.js
@@ -1,9 +1,5 @@
import { Component } from 'react';
import withData from '../lib/withData';
-import Items from '../components/Items';
-import CreateItem from '../components/CreateItem';
-import Signup from '../components/Signup';
-import LoginAuth0 from '../components/LoginAuth0';
import Page from '../components/Page';
import OrderList from '../components/OrderList';
@@ -12,9 +8,9 @@ class Orders extends Component {
return (
<Page>
<h2>My Orders</h2>
- <OrderList/>
+ <OrderList />
</Page>
- )
+ );
}
}
diff --git a/frontend/queries/index.js b/frontend/queries/index.js
index 2464246..beaec05 100644
--- a/frontend/queries/index.js
+++ b/frontend/queries/index.js
@@ -102,8 +102,8 @@ export const SINGLE_ITEM_QUERY = gql`
export const SEARCH_ITEMS_QUERY = gql`
${itemDetails}
query SearchItems($searchTerm: String!) {
- allItems(
- filter: {
+ items(
+ where: {
OR: [
{ title_contains: $searchTerm }
{ description_contains: $searchTerm }
@@ -238,32 +238,17 @@ export const GET_CART_STATE = gql`
}
`;
-export const UPDATE_CART_STATE = gql`
- mutation updateCartState($isCartOpen: Boolean!) {
- updateCartState(isCartOpen: $isCartOpen) {
- ui {
- isCartOpen
- }
- }
- }
-`;
-
-export const UI_STATE = gql`
- query {
- isCartOpen @client
- }
-`;
-
-export const UPDATE_NETWORK_STATUS = gql`
- mutation updateNetworkStatus($isConnected: Boolean) {
- updateNetworkStatus(isConnected: $isConnected) @client
+export const UPDATE_UI = gql`
+ mutation setLocalData($isConnected: Boolean, $isCartOpen: Boolean) {
+ setLocalData(isConnected: $isConnected, isCartOpen: $isCartOpen) @client
}
`;
export const GET_UI_STATE = gql`
query cartState {
- networkStatus @client {
+ uiData @client {
isConnected
+ isCartOpen
}
}
`;
diff --git a/frontend/static/favicon.png b/frontend/static/favicon.png
new file mode 100644
index 0000000..0601ad6
--- /dev/null
+++ b/frontend/static/favicon.png
Binary files differ
diff --git a/frontend/static/nprogress.css b/frontend/static/nprogress.css
index e18709f..544ce01 100644
--- a/frontend/static/nprogress.css
+++ b/frontend/static/nprogress.css
@@ -4,7 +4,7 @@
}
#nprogress .bar {
- background: #29d;
+ background: red;
position: fixed;
z-index: 1031;
top: 0;
@@ -21,7 +21,7 @@
right: 0px;
width: 100px;
height: 100%;
- box-shadow: 0 0 10px #29d, 0 0 5px #29d;
+ box-shadow: 0 0 10px red, 0 0 5px red;
opacity: 1.0;
-webkit-transform: rotate(3deg) translate(0px, -4px);
@@ -44,8 +44,8 @@
box-sizing: border-box;
border: solid 2px transparent;
- border-top-color: #29d;
- border-left-color: #29d;
+ border-top-color: red;
+ border-left-color: red;
border-radius: 50%;
-webkit-animation: nprogress-spinner 400ms linear infinite;