From 5742e422f68ee757b68295ff408ce9a6e869c2bd Mon Sep 17 00:00:00 2001 From: Sumit Gupta Date: Thu, 3 May 2018 14:56:02 +0530 Subject: [PATCH 1/3] created first checkpoint for redux workshop implemented concept of transformers listening to all request start actions simulated a real time api call by adding set timeout to add item to cart api --- src/client/components/AddToCart.js | 14 ++++++-- src/client/components/Cart.js | 4 +-- src/client/components/CartBadge.js | 6 ++-- src/client/components/Product.js | 2 +- src/client/components/Products.js | 2 +- src/client/redux/actions/index.js | 9 +++-- src/client/redux/reducers/cartItems.js | 33 +++++++++++++++++-- src/client/redux/reducers/products.js | 31 +++++++++++++++-- .../transformers/transformGetCartItemsApi.js | 10 ++++++ .../transformers/transformProductsApi.js | 10 ++++++ src/server/connectors/index.js | 7 ++-- src/server/index.js | 3 +- 12 files changed, 109 insertions(+), 22 deletions(-) create mode 100644 src/client/redux/transformers/transformGetCartItemsApi.js create mode 100644 src/client/redux/transformers/transformProductsApi.js diff --git a/src/client/components/AddToCart.js b/src/client/components/AddToCart.js index c26d365..f26366c 100644 --- a/src/client/components/AddToCart.js +++ b/src/client/components/AddToCart.js @@ -6,21 +6,29 @@ import * as actionCreators from "../redux/actions"; class AddToCart extends React.Component { render() { - const { actions: { addItemsToCart }, product } = this.props; + const { + actions: { addItemsToCart }, + cartItems: { isLoading }, + product + } = this.props; return ( ); } } +const mapStateToProps = state => ({ + cartItems: state.cartItems +}); + const mapDispatchToProps = dispatch => ({ actions: bindActionCreators(actionCreators, dispatch) }); -export default connect(null, mapDispatchToProps)(AddToCart); +export default connect(mapStateToProps, mapDispatchToProps)(AddToCart); diff --git a/src/client/components/Cart.js b/src/client/components/Cart.js index f0e8923..82fe003 100644 --- a/src/client/components/Cart.js +++ b/src/client/components/Cart.js @@ -19,12 +19,12 @@ function CartItem({ cartItem }) { export class Cart extends React.Component { render() { const { cartItems } = this.props; - if (!cartItems.length) { + if (!cartItems.ids.length) { return Cart is empty; } return ( - {cartItems.map(cartItem => ( + {Object.values(cartItems.byId).map(cartItem => ( ))} diff --git a/src/client/components/CartBadge.js b/src/client/components/CartBadge.js index 479f999..3784d1e 100644 --- a/src/client/components/CartBadge.js +++ b/src/client/components/CartBadge.js @@ -12,11 +12,11 @@ class CartBadge extends React.Component { render() { const { cartItems } = this.props; - if (!cartItems) { + if (!cartItems.ids.length) { return null; } - return cartItems.length ? ( - {cartItems.length} + return cartItems.ids.length ? ( + {cartItems.ids.length} ) : null; } } diff --git a/src/client/components/Product.js b/src/client/components/Product.js index ad8b1fc..4166004 100644 --- a/src/client/components/Product.js +++ b/src/client/components/Product.js @@ -18,7 +18,7 @@ class Product extends React.Component { render() { const { products, match } = this.props; const productId = parseInt(match.params.id, 10); - const product = products.length && products.find(p => p.id === productId); + const product = products.ids.length && products.byId[productId]; if (!product) { return null; } diff --git a/src/client/components/Products.js b/src/client/components/Products.js index 6c2d5cc..33d5e46 100644 --- a/src/client/components/Products.js +++ b/src/client/components/Products.js @@ -34,7 +34,7 @@ class Products extends React.Component {
Products - {products.map(product => ( + {Object.values(products.byId).map(product => ( ))} diff --git a/src/client/redux/actions/index.js b/src/client/redux/actions/index.js index 771fd14..89bb2ad 100644 --- a/src/client/redux/actions/index.js +++ b/src/client/redux/actions/index.js @@ -1,16 +1,18 @@ import * as actionTypes from "../actionTypes"; +import { transformProductsApi } from "../transformers/transformProductsApi"; +import { transformGetCartItemsApi } from "../transformers/transformGetCartItemsApi"; export const getProducts = productId => { return dispatch => { dispatch({ type: actionTypes.GET_PRODUCTS_REQUEST }); const apiUrl = productId ? `/api/products/${productId}` : "/api/products"; - console.log(apiUrl); return fetch(apiUrl).then(async response => { const responseData = await response.json(); if (response.ok) { + const data = transformProductsApi(responseData); dispatch({ type: actionTypes.GET_PRODUCTS_SUCCESS, - payload: responseData + payload: data }); } else { dispatch({ @@ -28,9 +30,10 @@ export const getCartItems = () => { return fetch("/api/cart-items").then(async response => { const responseData = await response.json(); if (response.ok) { + const data = transformGetCartItemsApi(responseData); dispatch({ type: actionTypes.GET_CART_ITEMS_SUCCESS, - payload: responseData + payload: data }); } else { dispatch({ diff --git a/src/client/redux/reducers/cartItems.js b/src/client/redux/reducers/cartItems.js index 40cbb43..4f9dcf8 100644 --- a/src/client/redux/reducers/cartItems.js +++ b/src/client/redux/reducers/cartItems.js @@ -1,12 +1,39 @@ import * as actionTypes from "../actionTypes"; -export default function cartItemsReducer(state = [], action) { +const initialState = { + byId: {}, + ids: [], + isLoading: false, + isError: false, + errorMsg: "" +}; + +export default function cartItemsReducer(state = initialState, action) { switch (action.type) { + case actionTypes.ADD_ITEMS_TO_CART_REQUEST: + case actionTypes.GET_CART_ITEMS_REQUEST: + return { + ...state, + isLoading: true + }; + case actionTypes.GET_CART_ITEMS_SUCCESS: - return [...state, ...action.payload]; + return { + ...state, + ...action.payload, + isLoading: false + }; case actionTypes.ADD_ITEMS_TO_CART_SUCCESS: - return [...state, action.payload]; + return { + ...state, + byId: { + ...state.byId, + [action.payload.id]: action.payload + }, + ids: [...state.ids, action.payload.id], + isLoading: false + }; default: return state; diff --git a/src/client/redux/reducers/products.js b/src/client/redux/reducers/products.js index 61ce489..a6ada10 100644 --- a/src/client/redux/reducers/products.js +++ b/src/client/redux/reducers/products.js @@ -1,9 +1,36 @@ import * as actionTypes from "../actionTypes"; -export default function productsReducer(state = [], action) { +const initialState = { + byId: {}, + ids: [], + isLoading: false, + isError: false, + errorMsg: "" +}; + +export default function productsReducer(state = initialState, action) { switch (action.type) { + case actionTypes.GET_CART_ITEMS_REQUEST: + return { + ...state, + isLoading: true + }; + case actionTypes.GET_PRODUCTS_SUCCESS: - return action.payload; + return { + ...state, + ...action.payload, + isLoading: false + }; + + case actionTypes.GET_PRODUCTS_FAILURE: + return { + ...state, + isLoading: false, + isError: true, + errorMsg: action.payload + }; + default: return state; } diff --git a/src/client/redux/transformers/transformGetCartItemsApi.js b/src/client/redux/transformers/transformGetCartItemsApi.js new file mode 100644 index 0000000..03647f6 --- /dev/null +++ b/src/client/redux/transformers/transformGetCartItemsApi.js @@ -0,0 +1,10 @@ +export const transformGetCartItemsApi = data => ({ + byId: data.reduce( + (obj, cartItem) => ({ + ...obj, + [cartItem.id]: cartItem + }), + {} + ), + ids: data.map(cartItem => cartItem.id) +}); diff --git a/src/client/redux/transformers/transformProductsApi.js b/src/client/redux/transformers/transformProductsApi.js new file mode 100644 index 0000000..0e3980e --- /dev/null +++ b/src/client/redux/transformers/transformProductsApi.js @@ -0,0 +1,10 @@ +export const transformProductsApi = data => ({ + byId: data.reduce( + (obj, product) => ({ + ...obj, + [product.id]: product + }), + {} + ), + ids: data.map(product => product.id) +}); diff --git a/src/server/connectors/index.js b/src/server/connectors/index.js index 580c78a..79f3a0a 100644 --- a/src/server/connectors/index.js +++ b/src/server/connectors/index.js @@ -30,7 +30,6 @@ export function getProducts() { } export function getProduct(id) { - console.log("productId", id); return [products.find(product => product.id === id)]; } @@ -51,7 +50,11 @@ export function addToCart({ productId }) { product: products.find(p => p.id === productId) }; cartItems.push(newCartItem); - return newCartItem; + return new Promise((resolve, reject) => { + setTimeout(() => { + resolve(newCartItem); + }, 5000); + }); } export function deleteCartItem(args) { diff --git a/src/server/index.js b/src/server/index.js index 1b44b11..8db428c 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -45,7 +45,6 @@ app.get("/api/products", function(req, res) { app.get("/api/products/:id", function(req, res) { const id = parseInt(req.params.id, 10); - console.log("product id", id); res.json(getProduct(id)); }); @@ -59,7 +58,7 @@ app.get("/api/cart-items/:id", function(req, res) { }); app.post("/api/cart-items", function(req, res) { - res.json(addToCart(req.body)); + addToCart(req.body).then(res.json); }); app.post("/api/cart-items/:id", function(req, res) { From 7edf569128d76e35bc75d732bd0957423d0d7a29 Mon Sep 17 00:00:00 2001 From: Sumit Gupta Date: Thu, 3 May 2018 20:49:48 +0530 Subject: [PATCH 2/3] bug fix --- src/server/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/index.js b/src/server/index.js index 8db428c..b043fe5 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -58,7 +58,7 @@ app.get("/api/cart-items/:id", function(req, res) { }); app.post("/api/cart-items", function(req, res) { - addToCart(req.body).then(res.json); + addToCart(req.body).then(response => res.json(response)); }); app.post("/api/cart-items/:id", function(req, res) { From cdb0d4b256d969bf91ea535bc036411a210f6f40 Mon Sep 17 00:00:00 2001 From: Sumit Gupta Date: Fri, 4 May 2018 12:38:19 +0530 Subject: [PATCH 3/3] make boiler plate code same accross all solutions --- package.json | 1 + src/client/components/App.js | 3 ++- src/client/components/Header.js | 20 ++++++++++++++++++++ src/client/components/Products.js | 12 ++++++++++-- src/client/redux/reducers/products.js | 8 ++++++-- src/server/connectors/index.js | 18 +++++++++++++++++- src/server/index.js | 11 ++++++++--- yarn.lock | 4 ++++ 8 files changed, 68 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 1f211b3..6848d80 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "bootstrap": "^4.0.0", "classnames": "^2.2.5", "express": "^4.16.2", + "lodash": "4.17.10", "mongoose": "^4.13.6", "react": "^16.0.0", "react-dom": "^16.0.0", diff --git a/src/client/components/App.js b/src/client/components/App.js index 92fedc1..6fb283b 100644 --- a/src/client/components/App.js +++ b/src/client/components/App.js @@ -16,7 +16,8 @@ export default class App extends React.Component { - + + diff --git a/src/client/components/Header.js b/src/client/components/Header.js index f76544e..bb1fc5b 100644 --- a/src/client/components/Header.js +++ b/src/client/components/Header.js @@ -22,6 +22,26 @@ export default class Header extends React.Component { Products + + + Samsung + + + + + Apple + + product.id === id)]; } +export function getProductsByBrand(brand) { + return products.filter( + product => product.brand.toLowerCase() === brand.toLowerCase() + ); +} + export function getCartItem(id) { return cartItems.find(c => c.id === id); } diff --git a/src/server/index.js b/src/server/index.js index b043fe5..7c32686 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -4,7 +4,8 @@ import morgan from "morgan"; import { getUser, getProducts, - getProduct, + getProductById, + getProductsByBrand, getCartItems, getCartItem, addToCart, @@ -43,9 +44,13 @@ app.get("/api/products", function(req, res) { res.json(getProducts()); }); -app.get("/api/products/:id", function(req, res) { +app.get("/api/products/:id(\\d+)/", function(req, res) { const id = parseInt(req.params.id, 10); - res.json(getProduct(id)); + res.json(getProductById(id)); +}); + +app.get("/api/products/:brand(\\w+)/", function(req, res) { + res.json(getProductsByBrand(req.params.brand)); }); app.get("/api/cart-items", function(req, res) { diff --git a/yarn.lock b/yarn.lock index 78c06ea..efd3fa1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4969,6 +4969,10 @@ lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" +lodash@4.17.10: + version "4.17.10" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" + "lodash@>=3.5 <5", lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0: version "4.17.5" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511"