summaryrefslogtreecommitdiffstats
path: root/stepped-solutions/53/frontend/components
diff options
context:
space:
mode:
Diffstat (limited to 'stepped-solutions/53/frontend/components')
-rwxr-xr-xstepped-solutions/53/frontend/components/Cart.js66
-rwxr-xr-xstepped-solutions/53/frontend/components/Order.js91
-rwxr-xr-xstepped-solutions/53/frontend/components/Search.js94
-rwxr-xr-xstepped-solutions/53/frontend/components/TakeMyMoney.js77
-rwxr-xr-xstepped-solutions/53/frontend/components/styles/OrderStyles.js37
5 files changed, 365 insertions, 0 deletions
diff --git a/stepped-solutions/53/frontend/components/Cart.js b/stepped-solutions/53/frontend/components/Cart.js
new file mode 100755
index 0000000..2a72b38
--- /dev/null
+++ b/stepped-solutions/53/frontend/components/Cart.js
@@ -0,0 +1,66 @@
+import React from 'react';
+import { Query, Mutation } from 'react-apollo';
+import gql from 'graphql-tag';
+import { adopt } from 'react-adopt';
+import User from './User';
+import CartStyles from './styles/CartStyles';
+import Supreme from './styles/Supreme';
+import CloseButton from './styles/CloseButton';
+import SickButton from './styles/SickButton';
+import CartItem from './CartItem';
+import calcTotalPrice from '../lib/calcTotalPrice';
+import formatMoney from '../lib/formatMoney';
+import TakeMyMoney from './TakeMyMoney';
+
+const LOCAL_STATE_QUERY = gql`
+ query {
+ cartOpen @client
+ }
+`;
+
+const TOGGLE_CART_MUTATION = gql`
+ mutation {
+ toggleCart @client
+ }
+`;
+/* eslint-disable */
+const Composed = adopt({
+ user: ({ render }) => <User>{render}</User>,
+ toggleCart: ({ render }) => <Mutation mutation={TOGGLE_CART_MUTATION}>{render}</Mutation>,
+ localState: ({ render }) => <Query query={LOCAL_STATE_QUERY}>{render}</Query>,
+});
+/* eslint-enable */
+
+const Cart = () => (
+ <Composed>
+ {({ user, toggleCart, localState }) => {
+ const me = user.data.me;
+ if (!me) return null;
+ return (
+ <CartStyles open={localState.data.cartOpen}>
+ <header>
+ <CloseButton onClick={toggleCart} title="close">
+ &times;
+ </CloseButton>
+ <Supreme>{me.name}'s Cart</Supreme>
+ <p>
+ You Have {me.cart.length} Item{me.cart.length === 1 ? '' : 's'} in your cart.
+ </p>
+ </header>
+ <ul>{me.cart.map(cartItem => <CartItem key={cartItem.id} cartItem={cartItem} />)}</ul>
+ <footer>
+ <p>{formatMoney(calcTotalPrice(me.cart))}</p>
+ {me.cart.length && (
+ <TakeMyMoney>
+ <SickButton>Checkout</SickButton>
+ </TakeMyMoney>
+ )}
+ </footer>
+ </CartStyles>
+ );
+ }}
+ </Composed>
+);
+
+export default Cart;
+export { LOCAL_STATE_QUERY, TOGGLE_CART_MUTATION };
diff --git a/stepped-solutions/53/frontend/components/Order.js b/stepped-solutions/53/frontend/components/Order.js
new file mode 100755
index 0000000..f2a2caf
--- /dev/null
+++ b/stepped-solutions/53/frontend/components/Order.js
@@ -0,0 +1,91 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Query } from 'react-apollo';
+import { format } from 'date-fns';
+import Head from 'next/head';
+import gql from 'graphql-tag';
+import formatMoney from '../lib/formatMoney';
+import Error from './ErrorMessage';
+import OrderStyles from './styles/OrderStyles';
+
+const SINGLE_ORDER_QUERY = gql`
+ query SINGLE_ORDER_QUERY($id: ID!) {
+ order(id: $id) {
+ id
+ charge
+ total
+ createdAt
+ user {
+ id
+ }
+ items {
+ id
+ title
+ description
+ price
+ image
+ quantity
+ }
+ }
+ }
+`;
+
+class Order extends React.Component {
+ static propTypes = {
+ id: PropTypes.string.isRequired,
+ };
+ render() {
+ return (
+ <Query query={SINGLE_ORDER_QUERY} variables={{ id: this.props.id }}>
+ {({ data, error, loading }) => {
+ if (error) return <Error error={error} />;
+ if (loading) return <p>Loading...</p>;
+ const order = data.order;
+ return (
+ <OrderStyles>
+ <Head>
+ <title>Sick Fits - Order {order.id}</title>
+ </Head>
+ <p>
+ <span>Order ID:</span>
+ <span>{this.props.id}</span>
+ </p>
+ <p>
+ <span>Charge</span>
+ <span>{order.charge}</span>
+ </p>
+ <p>
+ <span>Date</span>
+ <span>{format(order.createdAt, 'MMMM d, YYYY h:mm a')}</span>
+ </p>
+ <p>
+ <span>Order Total</span>
+ <span>{formatMoney(order.total)}</span>
+ </p>
+ <p>
+ <span>Item Count</span>
+ <span>{order.items.length}</span>
+ </p>
+ <div className="items">
+ {order.items.map(item => (
+ <div className="order-item" key={item.id}>
+ <img src={item.image} alt={item.title} />
+ <div className="item-details">
+ <h2>{item.title}</h2>
+ <p>Qty: {item.quantity}</p>
+ <p>Each: {formatMoney(item.price)}</p>
+ <p>SubTotal: {formatMoney(item.price * item.quantity)}</p>
+ <p>{item.description}</p>
+ </div>
+ </div>
+ ))}
+ </div>
+ </OrderStyles>
+ );
+ }}
+ </Query>
+ );
+ }
+}
+
+export default Order;
diff --git a/stepped-solutions/53/frontend/components/Search.js b/stepped-solutions/53/frontend/components/Search.js
new file mode 100755
index 0000000..c29f93e
--- /dev/null
+++ b/stepped-solutions/53/frontend/components/Search.js
@@ -0,0 +1,94 @@
+import React from 'react';
+import Downshift, { resetIdCounter } from 'downshift';
+import Router from 'next/router';
+import { ApolloConsumer } from 'react-apollo';
+import gql from 'graphql-tag';
+import debounce from 'lodash.debounce';
+import { DropDown, DropDownItem, SearchStyles } from './styles/DropDown';
+
+const SEARCH_ITEMS_QUERY = gql`
+ query SEARCH_ITEMS_QUERY($searchTerm: String!) {
+ items(where: { OR: [{ title_contains: $searchTerm }, { description_contains: $searchTerm }] }) {
+ id
+ image
+ title
+ }
+ }
+`;
+
+function routeToItem(item) {
+ Router.push({
+ pathname: '/item',
+ query: {
+ id: item.id,
+ },
+ });
+}
+
+class AutoComplete extends React.Component {
+ state = {
+ items: [],
+ loading: false,
+ };
+ onChange = debounce(async (e, client) => {
+ console.log('Searching...');
+ // turn loading on
+ this.setState({ loading: true });
+ // Manually query apollo client
+ const res = await client.query({
+ query: SEARCH_ITEMS_QUERY,
+ variables: { searchTerm: e.target.value },
+ });
+ this.setState({
+ items: res.data.items,
+ loading: false,
+ });
+ }, 350);
+ render() {
+ resetIdCounter();
+ return (
+ <SearchStyles>
+ <Downshift onChange={routeToItem} itemToString={item => (item === null ? '' : item.title)}>
+ {({ getInputProps, getItemProps, isOpen, inputValue, highlightedIndex }) => (
+ <div>
+ <ApolloConsumer>
+ {client => (
+ <input
+ {...getInputProps({
+ type: 'search',
+ placeholder: 'Search For An Item',
+ id: 'search',
+ className: this.state.loading ? 'loading' : '',
+ onChange: e => {
+ e.persist();
+ this.onChange(e, client);
+ },
+ })}
+ />
+ )}
+ </ApolloConsumer>
+ {isOpen && (
+ <DropDown>
+ {this.state.items.map((item, index) => (
+ <DropDownItem
+ {...getItemProps({ item })}
+ key={item.id}
+ highlighted={index === highlightedIndex}
+ >
+ <img width="50" src={item.image} alt={item.title} />
+ {item.title}
+ </DropDownItem>
+ ))}
+ {!this.state.items.length &&
+ !this.state.loading && <DropDownItem> Nothing Found {inputValue}</DropDownItem>}
+ </DropDown>
+ )}
+ </div>
+ )}
+ </Downshift>
+ </SearchStyles>
+ );
+ }
+}
+
+export default AutoComplete;
diff --git a/stepped-solutions/53/frontend/components/TakeMyMoney.js b/stepped-solutions/53/frontend/components/TakeMyMoney.js
new file mode 100755
index 0000000..9d6614e
--- /dev/null
+++ b/stepped-solutions/53/frontend/components/TakeMyMoney.js
@@ -0,0 +1,77 @@
+import React from 'react';
+import StripeCheckout from 'react-stripe-checkout';
+import { Mutation } from 'react-apollo';
+import Router from 'next/router';
+import NProgress from 'nprogress';
+import PropTypes from 'prop-types';
+import gql from 'graphql-tag';
+import calcTotalPrice from '../lib/calcTotalPrice';
+import Error from './ErrorMessage';
+import User, { CURRENT_USER_QUERY } from './User';
+
+const CREATE_ORDER_MUTATION = gql`
+ mutation createOrder($token: String!) {
+ createOrder(token: $token) {
+ id
+ charge
+ total
+ items {
+ id
+ title
+ }
+ }
+ }
+`;
+
+function totalItems(cart) {
+ return cart.reduce((tally, cartItem) => tally + cartItem.quantity, 0);
+}
+
+class TakeMyMoney extends React.Component {
+ onToken = async (res, createOrder) => {
+ NProgress.start();
+ console.log('On Token Called!');
+ console.log(res.id);
+ // manually call the mutation once we have the stripe token
+ const order = await createOrder({
+ variables: {
+ token: res.id,
+ },
+ }).catch(err => {
+ alert(err.message);
+ });
+ Router.push({
+ pathname: '/order',
+ query: { id: order.data.createOrder.id },
+ });
+ };
+ render() {
+ return (
+ <User>
+ {({ data: { me } }) => (
+ <Mutation
+ mutation={CREATE_ORDER_MUTATION}
+ refetchQueries={[{ query: CURRENT_USER_QUERY }]}
+ >
+ {createOrder => (
+ <StripeCheckout
+ amount={calcTotalPrice(me.cart)}
+ name="Sick Fits"
+ description={`Order of ${totalItems(me.cart)} items!`}
+ image={me.cart.length && me.cart[0].item && me.cart[0].item.image}
+ stripeKey="pk_test_Vtknn6vSdcZWSG2JWvEiWSqC"
+ currency="USD"
+ email={me.email}
+ token={res => this.onToken(res, createOrder)}
+ >
+ {this.props.children}
+ </StripeCheckout>
+ )}
+ </Mutation>
+ )}
+ </User>
+ );
+ }
+}
+
+export default TakeMyMoney;
diff --git a/stepped-solutions/53/frontend/components/styles/OrderStyles.js b/stepped-solutions/53/frontend/components/styles/OrderStyles.js
new file mode 100755
index 0000000..f461b70
--- /dev/null
+++ b/stepped-solutions/53/frontend/components/styles/OrderStyles.js
@@ -0,0 +1,37 @@
+import styled from 'styled-components';
+
+const OrderStyles = styled.div`
+ max-width: 1000px;
+ margin: 0 auto;
+ border: 1px solid ${props => props.theme.offWhite};
+ box-shadow: ${props => props.theme.bs};
+ padding: 2rem;
+ border-top: 10px solid red;
+ & > p {
+ display: grid;
+ grid-template-columns: 1fr 5fr;
+ margin: 0;
+ border-bottom: 1px solid ${props => props.theme.offWhite};
+ span {
+ padding: 1rem;
+ &:first-child {
+ font-weight: 900;
+ text-align: right;
+ }
+ }
+ }
+ .order-item {
+ border-bottom: 1px solid ${props => props.theme.offWhite};
+ display: grid;
+ grid-template-columns: 300px 1fr;
+ align-items: center;
+ grid-gap: 2rem;
+ margin: 2rem 0;
+ padding-bottom: 2rem;
+ img {
+ width: 100%;
+ object-fit: cover;
+ }
+ }
+`;
+export default OrderStyles;