Skip to content

Commit a5ace7d

Browse files
committed
Merge pull request #24 from Justkant/finish-cart-features
Finish cart features
2 parents a2bde61 + 50fd03d commit a5ace7d

File tree

18 files changed

+175
-74
lines changed

18 files changed

+175
-74
lines changed

Diff for: api/api.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
require('../server.babel'); // babel registration (runtime transpilation for node)
2-
31
import express from 'express';
42
import bodyParser from 'body-parser';
53
import cookieParser from 'cookie-parser';
@@ -53,7 +51,7 @@ app.route('/users/:id/cart/:cartId')
5351

5452
app.route('/users/:id/orders')
5553
.get(users.auth, users.isOwner, users.getUserOrders)
56-
.post(users.auth, users.isOwner, users.validateCart);
54+
.post(users.auth, users.isOwner, users.validateCart, users.load);
5755

5856
app.route('/users/:id/orders/:orderId')
5957
.get(users.auth, users.isOwner, users.getUserOrder)

Diff for: api/functions/users.js

+22-19
Original file line numberDiff line numberDiff line change
@@ -322,23 +322,26 @@ function getUserCartItem(req, res) {
322322
}
323323

324324
function updateCartItem(req, res) {
325-
Cart.get(req.params.cartId).getJoin().run().then((cartItem) => {
326-
req.user.cartTotal -= cartItem.product.price * cartItem.nbItem;
327-
cartItem.nbItem = req.body.nbItem;
328-
cartItem.save().then(() => {
329-
req.user.cartTotal += cartItem.product.price * cartItem.nbItem;
330-
req.user.save().then(() => {
331-
res.json(req.user.getPublic());
332-
}, (error) => {
333-
console.error(error.message);
334-
res.status(400).json({msg: 'Something went wrong', err: error.message});
335-
});
336-
}, (error) => {
337-
});
338-
}, (error) => {
339-
console.error(error.message);
340-
res.status(404).json({msg: 'Cart Item not found', err: error.message});
341-
});
325+
for (const cartPos in req.user.cart) {
326+
if (req.user.cart[cartPos].id === req.params.cartId) {
327+
req.user.cartTotal -= req.user.cart[cartPos].product.price * req.user.cart[cartPos].nbItem;
328+
if (req.body.nbItem <= 0) {
329+
res.status(400).json({msg: 'nbItem has to be a positive number'});
330+
} else {
331+
req.user.cart[cartPos].nbItem = req.body.nbItem;
332+
req.user.cartTotal += req.user.cart[cartPos].product.price * req.user.cart[cartPos].nbItem;
333+
req.user.saveAll().then(() => {
334+
res.json(req.user.getPublic());
335+
}, (error) => {
336+
console.error(error.message);
337+
res.status(400).json({msg: 'Something went wrong', err: error.message});
338+
});
339+
}
340+
return;
341+
}
342+
}
343+
console.error(error.message);
344+
res.status(404).json({msg: 'Cart Item not found', err: error.message});
342345
}
343346

344347
function deleteCartItem(req, res) {
@@ -370,7 +373,7 @@ function getUserOrders(req, res) {
370373
res.json(req.user.orders);
371374
}
372375

373-
function validateCart(req, res) {
376+
function validateCart(req, res, next) {
374377
(new Order({
375378
cartTotal: req.user.cartTotal,
376379
userId: req.user.id
@@ -386,7 +389,7 @@ function validateCart(req, res) {
386389
deleteCart(req.user.cart).then(() => {
387390
req.user.cartTotal = 0;
388391
req.user.save().then(() => {
389-
res.json(result);
392+
next();
390393
}, (error) => {
391394
console.error(error.message);
392395
res.status(400).json({msg: 'Something went wrong', err: error.message});

Diff for: package.json

+13-13
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"author": "Quentin 'Kant' Jaccarino",
33
"name": "whatashop",
4-
"version": "0.1.1",
4+
"version": "0.1.2",
55
"description": "Online Shopping Application",
66
"homepage": "https://github.com/Justkant/WhatAShop#readme",
77
"license": "MIT",
@@ -102,7 +102,7 @@
102102
"cookie-parser": "1.4.0",
103103
"express": "4.13.3",
104104
"file-loader": "0.8.5",
105-
"history": "1.13.1",
105+
"history": "1.15.0",
106106
"hoist-non-react-statics": "1.0.3",
107107
"http-proxy": "1.12.0",
108108
"jsonwebtoken": "5.4.1",
@@ -119,16 +119,16 @@
119119
"react-document-meta": "2.0.0",
120120
"react-dom": "0.14.3",
121121
"react-redux": "4.0.0",
122-
"react-router": "1.0.0",
122+
"react-router": "1.0.1",
123123
"redux": "3.0.4",
124-
"redux-router": "1.0.0-beta4",
124+
"redux-router": "1.0.0-beta5",
125125
"scroll-behavior": "0.3.0",
126126
"serialize-javascript": "1.1.2",
127127
"serve-favicon": "2.3.0",
128-
"superagent": "1.4.0",
129-
"thinky": "2.2.0",
128+
"superagent": "1.5.0",
129+
"thinky": "2.2.2",
130130
"url-loader": "0.5.7",
131-
"webpack-isomorphic-tools": "2.2.18"
131+
"webpack-isomorphic-tools": "2.2.21"
132132
},
133133
"devDependencies": {
134134
"apidoc": "0.13.1",
@@ -140,18 +140,18 @@
140140
"babel-runtime": "5.8.29",
141141
"better-npm-run": "0.0.4",
142142
"chai": "3.4.1",
143-
"clean-webpack-plugin": "0.1.4",
143+
"clean-webpack-plugin": "0.1.5",
144144
"concurrently": "1.0.0",
145145
"css-loader": "0.23.0",
146-
"eslint": "1.10.1",
146+
"eslint": "1.10.3",
147147
"eslint-config-airbnb": "0.1.0",
148148
"eslint-loader": "1.1.1",
149-
"eslint-plugin-import": "0.10.1",
150-
"eslint-plugin-react": "3.10.0",
149+
"eslint-plugin-import": "0.11.0",
150+
"eslint-plugin-react": "3.11.3",
151151
"extract-text-webpack-plugin": "0.9.1",
152152
"json-loader": "0.5.4",
153153
"karma": "0.13.15",
154-
"karma-chrome-launcher": "0.2.1",
154+
"karma-chrome-launcher": "0.2.2",
155155
"karma-cli": "0.1.1",
156156
"karma-firefox-launcher": "0.1.7",
157157
"karma-mocha": "0.2.1",
@@ -170,7 +170,7 @@
170170
"stylus-loader": "1.4.2",
171171
"webpack": "1.12.9",
172172
"webpack-dev-middleware": "1.4.0",
173-
"webpack-hot-middleware": "2.5.0"
173+
"webpack-hot-middleware": "2.6.0"
174174
},
175175
"engines": {
176176
"node": "4.x"

Diff for: src/client.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import createHistory from 'history/lib/createBrowserHistory';
88
import useScroll from 'scroll-behavior/lib/useStandardScroll';
99
import createStore from './redux/create';
1010
import ApiClient from './helpers/ApiClient';
11-
import {Provider} from 'react-redux';
12-
import {reduxReactRouter, ReduxRouter} from 'redux-router';
11+
import { Provider } from 'react-redux';
12+
import { reduxReactRouter, ReduxRouter } from 'redux-router';
1313

1414
import getRoutes from './routes';
1515
import makeRouteHooksSafe from './helpers/makeRouteHooksSafe';
@@ -18,10 +18,10 @@ const client = new ApiClient();
1818

1919
// Three different types of scroll behavior available.
2020
// Documented here: https://github.com/rackt/scroll-behavior
21-
const scrollablehistory = useScroll(createHistory);
21+
const scrollableHistory = useScroll(createHistory);
2222

2323
const dest = document.getElementById('content');
24-
const store = createStore(reduxReactRouter, makeRouteHooksSafe(getRoutes), scrollablehistory, client, window.__data);
24+
const store = createStore(reduxReactRouter, makeRouteHooksSafe(getRoutes), scrollableHistory, client, window.__data);
2525

2626
const component = (
2727
<Provider store={store} key="provider">

Diff for: src/components/Navbar/Navbar.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export default class Navbar extends React.Component {
4747
<div className={styles.productList}>
4848
{user.cart.map(({product}) => {
4949
return (
50-
<Link to={'/product/' + product.id} activeClassName="active">
50+
<Link to={'/product/' + product.id} activeClassName="active" key={product.id}>
5151
<p>{product.title}</p>
5252
</Link>
5353
);

Diff for: src/components/Title/Title.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,22 @@ import React, { Component, PropTypes } from 'react';
22

33
export default class Title extends Component {
44
static propTypes = {
5-
title: PropTypes.string
5+
title: PropTypes.string,
6+
showButton: PropTypes.bool,
7+
button: PropTypes.string,
8+
func: PropTypes.func
69
};
710

811
render() {
9-
const {title} = this.props;
12+
const {title, showButton, button, func} = this.props;
1013
const styles = require('./Title.styl');
1114

1215
return (
1316
<div className={styles.container}>
1417
<div className={styles.tab}>
1518
<span>{title}</span>
1619
</div>
20+
{showButton && <button onClick={func}><i className="material-icons">{button}</i></button>}
1721
</div>
1822
);
1923
}

Diff for: src/components/Title/Title.styl

+17
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,25 @@
1111
align-items: center;
1212
margin-right: 30px;
1313
color: #999;
14+
flex-grow: 1;
15+
justify-content: center;
1416

1517
span {
1618
font-weight: 300;
1719
}
1820
}
21+
22+
button {
23+
color: #999;
24+
background: transparent;
25+
border: 0;
26+
cursor: pointer;
27+
28+
&:hover {
29+
color: #44c63d;
30+
}
31+
32+
&:focus {
33+
outline: 0;
34+
}
35+
}

Diff for: src/components/__tests__/Search-test.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
/* eslint no-unused-expressions: 0*/
22
import React from 'react';
3-
import {renderIntoDocument} from 'react-addons-test-utils';
4-
import { expect} from 'chai';
3+
import { renderIntoDocument } from 'react-addons-test-utils';
4+
import { expect } from 'chai';
55
import { Search } from 'components';
66
import { Provider } from 'react-redux';
7-
import {reduxReactRouter} from 'redux-router';
7+
import { reduxReactRouter } from 'redux-router';
88
import createHistory from 'history/lib/createMemoryHistory';
99
import createStore from 'redux/create';
1010
import ApiClient from 'helpers/ApiClient';
@@ -32,5 +32,4 @@ describe('Search', () => {
3232
it('should render correctly', () => {
3333
expect(renderer).to.be.ok;
3434
});
35-
3635
});

Diff for: src/containers/Cart/Cart.js

+35-8
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,55 @@
11
import React, { Component, PropTypes } from 'react';
22
import { connect } from 'react-redux';
33
import { Title } from 'components';
4-
import { deleteCartItem } from 'redux/modules/auth';
4+
import { deleteCartItem, updateCartItem, validateCart } from 'redux/modules/auth';
5+
import { pushState } from 'redux-router';
56

6-
@connect(state => ({user: state.auth.user}), { deleteCartItem })
7+
@connect(state => ({user: state.auth.user}), { deleteCartItem, updateCartItem, validateCart, pushState })
78
export default class Cart extends Component {
89
static propTypes = {
910
user: PropTypes.object,
10-
deleteCartItem: PropTypes.func
11+
deleteCartItem: PropTypes.func,
12+
updateCartItem: PropTypes.func,
13+
validateCart: PropTypes.func,
14+
pushState: PropTypes.func
1115
};
1216

17+
handleFocus(ev) {
18+
ev.target.select();
19+
}
20+
21+
inputHandler(productId, currentNbItem) {
22+
const nb = parseInt(this.refs.inputNbItem.value, 10);
23+
24+
if (nb && nb !== currentNbItem) {
25+
this.updateItem.bind(this, productId, nb)();
26+
}
27+
}
28+
29+
updateItem(productId, nbItem) {
30+
if (nbItem > 0) {
31+
this.props.updateCartItem(this.props.user.id, productId, nbItem);
32+
}
33+
}
34+
1335
deleteItem(productId) {
1436
this.props.deleteCartItem(this.props.user.id, productId);
1537
}
1638

39+
validateCart() {
40+
this.props.validateCart(this.props.user.id);
41+
this.props.pushState(null, '/profile/orders');
42+
}
43+
1744
render() {
1845
const styles = require('./Cart.styl');
1946
const {user} = this.props;
2047

2148
return (
2249
<div className={styles.container}>
23-
<Title title="Cart"/>
50+
<Title title="Cart" showButton button="done" func={this.validateCart.bind(this)}/>
2451
<div className={styles.productContainer}>
25-
{user && user.cart.map(({id, product, nbItem}) => {
52+
{user && user.cart && user.cart.map(({id, product, nbItem}) => {
2653
return (
2754
<div className={styles.element} key={product.id}>
2855
<div className={styles.imageContainer}>
@@ -33,9 +60,9 @@ export default class Cart extends Component {
3360
<p>{product.price} $</p>
3461
<div className={styles.buttonContainer}>
3562
<div className={styles.inputNumber}>
36-
<button><i className="material-icons">remove</i></button>
37-
<input className={styles.number} defaultValue={nbItem}/>
38-
<button><i className="material-icons">add</i></button>
63+
<button onClick={this.updateItem.bind(this, id, nbItem - 1)}><i className="material-icons">remove</i></button>
64+
<input className={styles.number} ref="inputNbItem" value={nbItem} onChange={this.inputHandler.bind(this, id, nbItem)} onFocus={this.handleFocus}/>
65+
<button onClick={this.updateItem.bind(this, id, nbItem + 1)}><i className="material-icons">add</i></button>
3966
</div>
4067
<button className={styles.deleteButton} onClick={this.deleteItem.bind(this, id)}>
4168
<i className="material-icons">delete</i>

Diff for: src/containers/Cart/Cart.styl

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
.productContainer {
99
background-color: #f5f5f5;
10-
padding: 20px;
10+
padding: 20px 20px 0 20px;
1111
display: flex;
1212
flex-direction: column;
1313
overflow: auto;

Diff for: src/containers/Orders/Orders.js

+10-7
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1-
import React, { Component } from 'react';
1+
import React, { Component, PropTypes } from 'react';
2+
import { connect } from 'react-redux';
23

4+
@connect(state => ({user: state.auth.user}))
35
export default class Orders extends Component {
6+
static propTypes = {
7+
user: PropTypes.object
8+
};
9+
410
render() {
11+
const { user } = this.props;
512
const styles = require('./Orders.styl');
6-
const array = [];
7-
for (let index = 0; index < 50; index++) {
8-
array.push('Orders of kant');
9-
}
1013

1114
return (
1215
<div className={styles.container}>
13-
{array.map((value, index) => {
14-
return (<div className={styles.element} key={value + index}><h4>{value}</h4></div>);
16+
{user && user.orders && user.orders.map((value, index) => {
17+
return (<div className={styles.element} key={value.id + index}><h4>{value.cartTotal}</h4></div>);
1518
})}
1619
</div>
1720
);

Diff for: src/helpers/__tests__/getStatusFromRoutes-test.js

-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { expect } from 'chai';
22
import getStatusFromRoutes from '../getStatusFromRoutes';
33

44
describe('getStatusFromRoutes', () => {
5-
65
it('should return null when no routes have status code', () => {
76
const status = getStatusFromRoutes([
87
{}, {}

Diff for: src/helpers/__tests__/makeRouteHooksSafe-test.js

-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import makeRouteHooksSafe from '../makeRouteHooksSafe';
55

66

77
describe('makeRouteHooksSafe', () => {
8-
98
it('should work with JSX routes', () => {
109
const onEnter = () => {
1110
throw new Error('Shouldn\'t call onEnter');

Diff for: src/helpers/connectData.js

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import hoistStatics from 'hoist-non-react-statics';
88
*/
99

1010
export default function connectData(fetchData, fetchDataDeferred) {
11-
1211
return function wrapWithFetchData(WrappedComponent) {
1312
class ConnectData extends Component {
1413
render() {

Diff for: src/redux/middleware/transitionMiddleware.js

-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ export default ({getState, dispatch}) => next => action => {
1111

1212
const {components, location, params} = action.payload;
1313
const promise = new Promise((resolve) => {
14-
1514
const doTransition = () => {
1615
next(action);
1716
Promise.all(getDataDependencies(components, getState, dispatch, location, params, true))

0 commit comments

Comments
 (0)