Skip to content

Commit

Permalink
Merge pull request #24 from Justkant/finish-cart-features
Browse files Browse the repository at this point in the history
Finish cart features
  • Loading branch information
Justkant committed Dec 8, 2015
2 parents a2bde61 + 50fd03d commit a5ace7d
Show file tree
Hide file tree
Showing 18 changed files with 175 additions and 74 deletions.
4 changes: 1 addition & 3 deletions api/api.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
require('../server.babel'); // babel registration (runtime transpilation for node)

import express from 'express';
import bodyParser from 'body-parser';
import cookieParser from 'cookie-parser';
Expand Down Expand Up @@ -53,7 +51,7 @@ app.route('/users/:id/cart/:cartId')

app.route('/users/:id/orders')
.get(users.auth, users.isOwner, users.getUserOrders)
.post(users.auth, users.isOwner, users.validateCart);
.post(users.auth, users.isOwner, users.validateCart, users.load);

app.route('/users/:id/orders/:orderId')
.get(users.auth, users.isOwner, users.getUserOrder)
Expand Down
41 changes: 22 additions & 19 deletions api/functions/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -322,23 +322,26 @@ function getUserCartItem(req, res) {
}

function updateCartItem(req, res) {
Cart.get(req.params.cartId).getJoin().run().then((cartItem) => {
req.user.cartTotal -= cartItem.product.price * cartItem.nbItem;
cartItem.nbItem = req.body.nbItem;
cartItem.save().then(() => {
req.user.cartTotal += cartItem.product.price * cartItem.nbItem;
req.user.save().then(() => {
res.json(req.user.getPublic());
}, (error) => {
console.error(error.message);
res.status(400).json({msg: 'Something went wrong', err: error.message});
});
}, (error) => {
});
}, (error) => {
console.error(error.message);
res.status(404).json({msg: 'Cart Item not found', err: error.message});
});
for (const cartPos in req.user.cart) {
if (req.user.cart[cartPos].id === req.params.cartId) {
req.user.cartTotal -= req.user.cart[cartPos].product.price * req.user.cart[cartPos].nbItem;
if (req.body.nbItem <= 0) {
res.status(400).json({msg: 'nbItem has to be a positive number'});
} else {
req.user.cart[cartPos].nbItem = req.body.nbItem;
req.user.cartTotal += req.user.cart[cartPos].product.price * req.user.cart[cartPos].nbItem;
req.user.saveAll().then(() => {
res.json(req.user.getPublic());
}, (error) => {
console.error(error.message);
res.status(400).json({msg: 'Something went wrong', err: error.message});
});
}
return;
}
}
console.error(error.message);
res.status(404).json({msg: 'Cart Item not found', err: error.message});
}

function deleteCartItem(req, res) {
Expand Down Expand Up @@ -370,7 +373,7 @@ function getUserOrders(req, res) {
res.json(req.user.orders);
}

function validateCart(req, res) {
function validateCart(req, res, next) {
(new Order({
cartTotal: req.user.cartTotal,
userId: req.user.id
Expand All @@ -386,7 +389,7 @@ function validateCart(req, res) {
deleteCart(req.user.cart).then(() => {
req.user.cartTotal = 0;
req.user.save().then(() => {
res.json(result);
next();
}, (error) => {
console.error(error.message);
res.status(400).json({msg: 'Something went wrong', err: error.message});
Expand Down
26 changes: 13 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"author": "Quentin 'Kant' Jaccarino",
"name": "whatashop",
"version": "0.1.1",
"version": "0.1.2",
"description": "Online Shopping Application",
"homepage": "https://github.com/Justkant/WhatAShop#readme",
"license": "MIT",
Expand Down Expand Up @@ -102,7 +102,7 @@
"cookie-parser": "1.4.0",
"express": "4.13.3",
"file-loader": "0.8.5",
"history": "1.13.1",
"history": "1.15.0",
"hoist-non-react-statics": "1.0.3",
"http-proxy": "1.12.0",
"jsonwebtoken": "5.4.1",
Expand All @@ -119,16 +119,16 @@
"react-document-meta": "2.0.0",
"react-dom": "0.14.3",
"react-redux": "4.0.0",
"react-router": "1.0.0",
"react-router": "1.0.1",
"redux": "3.0.4",
"redux-router": "1.0.0-beta4",
"redux-router": "1.0.0-beta5",
"scroll-behavior": "0.3.0",
"serialize-javascript": "1.1.2",
"serve-favicon": "2.3.0",
"superagent": "1.4.0",
"thinky": "2.2.0",
"superagent": "1.5.0",
"thinky": "2.2.2",
"url-loader": "0.5.7",
"webpack-isomorphic-tools": "2.2.18"
"webpack-isomorphic-tools": "2.2.21"
},
"devDependencies": {
"apidoc": "0.13.1",
Expand All @@ -140,18 +140,18 @@
"babel-runtime": "5.8.29",
"better-npm-run": "0.0.4",
"chai": "3.4.1",
"clean-webpack-plugin": "0.1.4",
"clean-webpack-plugin": "0.1.5",
"concurrently": "1.0.0",
"css-loader": "0.23.0",
"eslint": "1.10.1",
"eslint": "1.10.3",
"eslint-config-airbnb": "0.1.0",
"eslint-loader": "1.1.1",
"eslint-plugin-import": "0.10.1",
"eslint-plugin-react": "3.10.0",
"eslint-plugin-import": "0.11.0",
"eslint-plugin-react": "3.11.3",
"extract-text-webpack-plugin": "0.9.1",
"json-loader": "0.5.4",
"karma": "0.13.15",
"karma-chrome-launcher": "0.2.1",
"karma-chrome-launcher": "0.2.2",
"karma-cli": "0.1.1",
"karma-firefox-launcher": "0.1.7",
"karma-mocha": "0.2.1",
Expand All @@ -170,7 +170,7 @@
"stylus-loader": "1.4.2",
"webpack": "1.12.9",
"webpack-dev-middleware": "1.4.0",
"webpack-hot-middleware": "2.5.0"
"webpack-hot-middleware": "2.6.0"
},
"engines": {
"node": "4.x"
Expand Down
8 changes: 4 additions & 4 deletions src/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import createHistory from 'history/lib/createBrowserHistory';
import useScroll from 'scroll-behavior/lib/useStandardScroll';
import createStore from './redux/create';
import ApiClient from './helpers/ApiClient';
import {Provider} from 'react-redux';
import {reduxReactRouter, ReduxRouter} from 'redux-router';
import { Provider } from 'react-redux';
import { reduxReactRouter, ReduxRouter } from 'redux-router';

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

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

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

const component = (
<Provider store={store} key="provider">
Expand Down
2 changes: 1 addition & 1 deletion src/components/Navbar/Navbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export default class Navbar extends React.Component {
<div className={styles.productList}>
{user.cart.map(({product}) => {
return (
<Link to={'/product/' + product.id} activeClassName="active">
<Link to={'/product/' + product.id} activeClassName="active" key={product.id}>
<p>{product.title}</p>
</Link>
);
Expand Down
8 changes: 6 additions & 2 deletions src/components/Title/Title.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,22 @@ import React, { Component, PropTypes } from 'react';

export default class Title extends Component {
static propTypes = {
title: PropTypes.string
title: PropTypes.string,
showButton: PropTypes.bool,
button: PropTypes.string,
func: PropTypes.func
};

render() {
const {title} = this.props;
const {title, showButton, button, func} = this.props;
const styles = require('./Title.styl');

return (
<div className={styles.container}>
<div className={styles.tab}>
<span>{title}</span>
</div>
{showButton && <button onClick={func}><i className="material-icons">{button}</i></button>}
</div>
);
}
Expand Down
17 changes: 17 additions & 0 deletions src/components/Title/Title.styl
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,25 @@
align-items: center;
margin-right: 30px;
color: #999;
flex-grow: 1;
justify-content: center;

span {
font-weight: 300;
}
}

button {
color: #999;
background: transparent;
border: 0;
cursor: pointer;

&:hover {
color: #44c63d;
}

&:focus {
outline: 0;
}
}
7 changes: 3 additions & 4 deletions src/components/__tests__/Search-test.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/* eslint no-unused-expressions: 0*/
import React from 'react';
import {renderIntoDocument} from 'react-addons-test-utils';
import { expect} from 'chai';
import { renderIntoDocument } from 'react-addons-test-utils';
import { expect } from 'chai';
import { Search } from 'components';
import { Provider } from 'react-redux';
import {reduxReactRouter} from 'redux-router';
import { reduxReactRouter } from 'redux-router';
import createHistory from 'history/lib/createMemoryHistory';
import createStore from 'redux/create';
import ApiClient from 'helpers/ApiClient';
Expand Down Expand Up @@ -32,5 +32,4 @@ describe('Search', () => {
it('should render correctly', () => {
expect(renderer).to.be.ok;
});

});
43 changes: 35 additions & 8 deletions src/containers/Cart/Cart.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,55 @@
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { Title } from 'components';
import { deleteCartItem } from 'redux/modules/auth';
import { deleteCartItem, updateCartItem, validateCart } from 'redux/modules/auth';
import { pushState } from 'redux-router';

@connect(state => ({user: state.auth.user}), { deleteCartItem })
@connect(state => ({user: state.auth.user}), { deleteCartItem, updateCartItem, validateCart, pushState })
export default class Cart extends Component {
static propTypes = {
user: PropTypes.object,
deleteCartItem: PropTypes.func
deleteCartItem: PropTypes.func,
updateCartItem: PropTypes.func,
validateCart: PropTypes.func,
pushState: PropTypes.func
};

handleFocus(ev) {
ev.target.select();
}

inputHandler(productId, currentNbItem) {
const nb = parseInt(this.refs.inputNbItem.value, 10);

if (nb && nb !== currentNbItem) {
this.updateItem.bind(this, productId, nb)();
}
}

updateItem(productId, nbItem) {
if (nbItem > 0) {
this.props.updateCartItem(this.props.user.id, productId, nbItem);
}
}

deleteItem(productId) {
this.props.deleteCartItem(this.props.user.id, productId);
}

validateCart() {
this.props.validateCart(this.props.user.id);
this.props.pushState(null, '/profile/orders');
}

render() {
const styles = require('./Cart.styl');
const {user} = this.props;

return (
<div className={styles.container}>
<Title title="Cart"/>
<Title title="Cart" showButton button="done" func={this.validateCart.bind(this)}/>
<div className={styles.productContainer}>
{user && user.cart.map(({id, product, nbItem}) => {
{user && user.cart && user.cart.map(({id, product, nbItem}) => {
return (
<div className={styles.element} key={product.id}>
<div className={styles.imageContainer}>
Expand All @@ -33,9 +60,9 @@ export default class Cart extends Component {
<p>{product.price} $</p>
<div className={styles.buttonContainer}>
<div className={styles.inputNumber}>
<button><i className="material-icons">remove</i></button>
<input className={styles.number} defaultValue={nbItem}/>
<button><i className="material-icons">add</i></button>
<button onClick={this.updateItem.bind(this, id, nbItem - 1)}><i className="material-icons">remove</i></button>
<input className={styles.number} ref="inputNbItem" value={nbItem} onChange={this.inputHandler.bind(this, id, nbItem)} onFocus={this.handleFocus}/>
<button onClick={this.updateItem.bind(this, id, nbItem + 1)}><i className="material-icons">add</i></button>
</div>
<button className={styles.deleteButton} onClick={this.deleteItem.bind(this, id)}>
<i className="material-icons">delete</i>
Expand Down
2 changes: 1 addition & 1 deletion src/containers/Cart/Cart.styl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

.productContainer {
background-color: #f5f5f5;
padding: 20px;
padding: 20px 20px 0 20px;
display: flex;
flex-direction: column;
overflow: auto;
Expand Down
17 changes: 10 additions & 7 deletions src/containers/Orders/Orders.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import React, { Component } from 'react';
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';

@connect(state => ({user: state.auth.user}))
export default class Orders extends Component {
static propTypes = {
user: PropTypes.object
};

render() {
const { user } = this.props;
const styles = require('./Orders.styl');
const array = [];
for (let index = 0; index < 50; index++) {
array.push('Orders of kant');
}

return (
<div className={styles.container}>
{array.map((value, index) => {
return (<div className={styles.element} key={value + index}><h4>{value}</h4></div>);
{user && user.orders && user.orders.map((value, index) => {
return (<div className={styles.element} key={value.id + index}><h4>{value.cartTotal}</h4></div>);
})}
</div>
);
Expand Down
1 change: 0 additions & 1 deletion src/helpers/__tests__/getStatusFromRoutes-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { expect } from 'chai';
import getStatusFromRoutes from '../getStatusFromRoutes';

describe('getStatusFromRoutes', () => {

it('should return null when no routes have status code', () => {
const status = getStatusFromRoutes([
{}, {}
Expand Down
1 change: 0 additions & 1 deletion src/helpers/__tests__/makeRouteHooksSafe-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import makeRouteHooksSafe from '../makeRouteHooksSafe';


describe('makeRouteHooksSafe', () => {

it('should work with JSX routes', () => {
const onEnter = () => {
throw new Error('Shouldn\'t call onEnter');
Expand Down
1 change: 0 additions & 1 deletion src/helpers/connectData.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import hoistStatics from 'hoist-non-react-statics';
*/

export default function connectData(fetchData, fetchDataDeferred) {

return function wrapWithFetchData(WrappedComponent) {
class ConnectData extends Component {
render() {
Expand Down
1 change: 0 additions & 1 deletion src/redux/middleware/transitionMiddleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ export default ({getState, dispatch}) => next => action => {

const {components, location, params} = action.payload;
const promise = new Promise((resolve) => {

const doTransition = () => {
next(action);
Promise.all(getDataDependencies(components, getState, dispatch, location, params, true))
Expand Down
Loading

0 comments on commit a5ace7d

Please sign in to comment.