Skip to content

Commit 3065f07

Browse files
committed
47
1 parent c3bbebf commit 3065f07

File tree

10 files changed

+1509
-46
lines changed

10 files changed

+1509
-46
lines changed

finished-application/backend/prisma/datamodel.graphql

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ type User {
2424
type CartItem {
2525
id: ID! @unique
2626
quantity: Int! @default(value: 1)
27-
item: Item # Relationship to an Item
27+
item: Item! # Relationship to an Item
2828
user: User! # Relationship to a user
2929
createdAt: DateTime!
3030
updatedAt: DateTime!

finished-application/frontend/components/Search.js

+1-45
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import Downshift from 'downshift';
33
import Router from 'next/router';
44
import { ApolloConsumer } from 'react-apollo';
55
import gql from 'graphql-tag';
6-
import styled, { keyframes } from 'styled-components';
76
import debounce from 'lodash.debounce';
7+
import { DropDown, DropDownItem, SearchStyles } from './styles/DropDown';
88

99
const SEARCH_ITEMS_QUERY = gql`
1010
query SEARCH_ITEMS_QUERY($searchTerm: String!) {
@@ -24,50 +24,6 @@ function routeToItem(item) {
2424
});
2525
}
2626

27-
const DropDown = styled.div`
28-
position: absolute;
29-
width: 100%;
30-
z-index: 2;
31-
border: 1px solid ${props => props.theme.lightgrey};
32-
`;
33-
34-
const DropDownItem = styled.div`
35-
border-bottom: 1px solid ${props => props.theme.lightgrey};
36-
background: ${props => (props.highlighted ? '#f7f7f7' : 'white')};
37-
padding: 1rem;
38-
transition: all 0.2s;
39-
${props => (props.highlighted ? 'padding-left: 2rem;' : null)};
40-
display: flex;
41-
align-items: center;
42-
border-left: 10px solid ${props => (props.highlighted ? props.theme.lightgrey : 'white')};
43-
img {
44-
margin-right: 10px;
45-
}
46-
`;
47-
48-
const glow = keyframes`
49-
from {
50-
box-shadow: 0 0 0px yellow;
51-
}
52-
53-
to {
54-
box-shadow: 0 0 10px 1px yellow;
55-
}
56-
`;
57-
58-
const SearchStyles = styled.div`
59-
position: relative;
60-
input {
61-
width: 100%;
62-
padding: 10px;
63-
border: 0;
64-
font-size: 2rem;
65-
&.loading {
66-
animation: ${glow} 0.5s ease-in-out infinite alternate;
67-
}
68-
}
69-
`;
70-
7127
class AutoComplete extends React.Component {
7228
state = {
7329
items: [],
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import styled, { keyframes } from 'styled-components';
2+
3+
const DropDown = styled.div`
4+
position: absolute;
5+
width: 100%;
6+
z-index: 2;
7+
border: 1px solid ${props => props.theme.lightgrey};
8+
`;
9+
10+
const DropDownItem = styled.div`
11+
border-bottom: 1px solid ${props => props.theme.lightgrey};
12+
background: ${props => (props.highlighted ? '#f7f7f7' : 'white')};
13+
padding: 1rem;
14+
transition: all 0.2s;
15+
${props => (props.highlighted ? 'padding-left: 2rem;' : null)};
16+
display: flex;
17+
align-items: center;
18+
border-left: 10px solid ${props => (props.highlighted ? props.theme.lightgrey : 'white')};
19+
img {
20+
margin-right: 10px;
21+
}
22+
`;
23+
24+
const glow = keyframes`
25+
from {
26+
box-shadow: 0 0 0px yellow;
27+
}
28+
29+
to {
30+
box-shadow: 0 0 10px 1px yellow;
31+
}
32+
`;
33+
34+
const SearchStyles = styled.div`
35+
position: relative;
36+
input {
37+
width: 100%;
38+
padding: 10px;
39+
border: 0;
40+
font-size: 2rem;
41+
&.loading {
42+
animation: ${glow} 0.5s ease-in-out infinite alternate;
43+
}
44+
}
45+
`;
46+
47+
export { DropDown, DropDownItem, SearchStyles };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import styled, { keyframes } from 'styled-components';
2+
3+
const DropDown = styled.div`
4+
position: absolute;
5+
width: 100%;
6+
z-index: 2;
7+
border: 1px solid ${props => props.theme.lightgrey};
8+
`;
9+
10+
const DropDownItem = styled.div`
11+
border-bottom: 1px solid ${props => props.theme.lightgrey};
12+
background: ${props => (props.highlighted ? '#f7f7f7' : 'white')};
13+
padding: 1rem;
14+
transition: all 0.2s;
15+
${props => (props.highlighted ? 'padding-left: 2rem;' : null)};
16+
display: flex;
17+
align-items: center;
18+
border-left: 10px solid ${props => (props.highlighted ? props.theme.lightgrey : 'white')};
19+
img {
20+
margin-right: 10px;
21+
}
22+
`;
23+
24+
const glow = keyframes`
25+
from {
26+
box-shadow: 0 0 0px yellow;
27+
}
28+
29+
to {
30+
box-shadow: 0 0 10px 1px yellow;
31+
}
32+
`;
33+
34+
const SearchStyles = styled.div`
35+
position: relative;
36+
input {
37+
width: 100%;
38+
padding: 10px;
39+
border: 0;
40+
font-size: 2rem;
41+
&.loading {
42+
animation: ${glow} 0.5s ease-in-out infinite alternate;
43+
}
44+
}
45+
`;
46+
47+
export { DropDown, DropDownItem, SearchStyles };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import React from 'react';
2+
import { Mutation } from 'react-apollo';
3+
import styled from 'styled-components';
4+
import PropTypes from 'prop-types';
5+
import gql from 'graphql-tag';
6+
import { CURRENT_USER_QUERY } from './User';
7+
8+
const REMOVE_FROM_CART_MUTATION = gql`
9+
mutation removeFromCart($id: ID!) {
10+
removeFromCart(id: $id) {
11+
id
12+
}
13+
}
14+
`;
15+
16+
const BigButton = styled.button`
17+
font-size: 3rem;
18+
background: none;
19+
border: 0;
20+
&:hover {
21+
color: ${props => props.theme.red};
22+
cursor: pointer;
23+
}
24+
`;
25+
26+
class RemoveFromCart extends React.Component {
27+
static propTypes = {
28+
id: PropTypes.string.isRequired,
29+
};
30+
// This gets called as soon as we get a response back from the server after a mutation has been performed
31+
update = (cache, payload) => {
32+
console.log('Running remove from cart update fn');
33+
// 1. first read the cache
34+
const data = cache.readQuery({ query: CURRENT_USER_QUERY });
35+
console.log(data);
36+
// 2. remove that item from the cart
37+
const cartItemId = payload.data.removeFromCart.id;
38+
data.me.cart = data.me.cart.filter(cartItem => cartItem.id !== cartItemId);
39+
// 3. write it back to the cache
40+
cache.writeQuery({ query: CURRENT_USER_QUERY, data });
41+
};
42+
render() {
43+
return (
44+
<Mutation
45+
mutation={REMOVE_FROM_CART_MUTATION}
46+
variables={{ id: this.props.id }}
47+
update={this.update}
48+
optimisticResponse={{
49+
__typename: 'Mutation',
50+
removeFromCart: {
51+
__typename: 'CartItem',
52+
id: this.props.id,
53+
},
54+
}}
55+
>
56+
{(removeFromCart, { loading, error }) => (
57+
<BigButton
58+
disabled={loading}
59+
onClick={() => {
60+
removeFromCart().catch(err => alert(err.message));
61+
}}
62+
title="Delete Item"
63+
>
64+
&times;
65+
</BigButton>
66+
)}
67+
</Mutation>
68+
);
69+
}
70+
}
71+
72+
export default RemoveFromCart;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import { TransitionGroup, CSSTransition } from 'react-transition-group';
4+
import styled from 'styled-components';
5+
6+
const AnimationStyles = styled.span`
7+
position: relative;
8+
.count {
9+
display: block;
10+
position: relative;
11+
transition: all 0.4s;
12+
backface-visibility: hidden;
13+
}
14+
/* Intial State of the entered Dot */
15+
.count-enter {
16+
transform: scale(4) rotateX(0.5turn);
17+
}
18+
.count-enter-active {
19+
transform: rotateX(0);
20+
}
21+
.count-exit {
22+
top: 0;
23+
position: absolute;
24+
transform: rotateX(0);
25+
}
26+
.count-exit-active {
27+
transform: scale(4) rotateX(0.5turn);
28+
}
29+
`;
30+
31+
const Dot = styled.div`
32+
background: ${props => props.theme.red};
33+
color: white;
34+
border-radius: 50%;
35+
padding: 0.5rem;
36+
line-height: 2rem;
37+
min-width: 3rem;
38+
margin-left: 1rem;
39+
font-weight: 100;
40+
font-feature-settings: 'tnum';
41+
font-variant-numeric: tabular-nums;
42+
`;
43+
44+
const CartCount = ({ count }) => (
45+
<AnimationStyles>
46+
<TransitionGroup>
47+
<CSSTransition
48+
unmountOnExit
49+
className="count"
50+
classNames="count"
51+
key={count}
52+
timeout={{ enter: 400, exit: 400 }}
53+
>
54+
<Dot>{count}</Dot>
55+
</CSSTransition>
56+
</TransitionGroup>
57+
</AnimationStyles>
58+
);
59+
60+
export default CartCount;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import Link from 'next/link';
2+
import { Mutation } from 'react-apollo';
3+
import { TOGGLE_CART_MUTATION } from './Cart';
4+
import NavStyles from './styles/NavStyles';
5+
import User from './User';
6+
import CartCount from './CartCount';
7+
import Signout from './Signout';
8+
9+
const Nav = () => (
10+
<User>
11+
{({ data: { me } }) => (
12+
<NavStyles>
13+
<Link href="/items">
14+
<a>Shop</a>
15+
</Link>
16+
{me && (
17+
<>
18+
<Link href="/sell">
19+
<a>Sell</a>
20+
</Link>
21+
<Link href="/orders">
22+
<a>Orders</a>
23+
</Link>
24+
<Link href="/me">
25+
<a>Account</a>
26+
</Link>
27+
<Signout />
28+
<Mutation mutation={TOGGLE_CART_MUTATION}>
29+
{(toggleCart) => (
30+
<button onClick={toggleCart}>
31+
My Cart
32+
<CartCount count={me.cart.reduce((tally, cartItem) => tally + cartItem.quantity, 0)}></CartCount>
33+
</button>
34+
)}
35+
</Mutation>
36+
</>
37+
)}
38+
{!me && (
39+
<Link href="/signup">
40+
<a>Sign In</a>
41+
</Link>
42+
43+
)}
44+
</NavStyles>
45+
)}
46+
</User>
47+
);
48+
49+
export default Nav;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
enum Permission {
2+
ADMIN
3+
USER
4+
ITEMCREATE
5+
ITEMUPDATE
6+
ITEMDELETE
7+
PERMISSIONUPDATE
8+
}
9+
10+
type User {
11+
id: ID! @unique
12+
name: String!
13+
email: String! @unique
14+
password: String!
15+
resetToken: String
16+
resetTokenExpiry: String
17+
permissions: [Permission]
18+
cart: [CartItem!]!
19+
}
20+
21+
type Item {
22+
id: ID! @unique
23+
title: String!
24+
description: String!
25+
image: String
26+
largeImage: String
27+
price: Int!
28+
user: User!
29+
}
30+
31+
32+
type CartItem {
33+
id: ID! @unique
34+
quantity: Int! @default(value: 1)
35+
item: Item # relationship to Item
36+
user: User! # relationship to User
37+
}

0 commit comments

Comments
 (0)