Commit ff68d617 authored by Christopher Cottier's avatar Christopher Cottier

Merge branch 'AFP45-Display-User-Cart-And-Update-Items-Within' into 'Dev'

Afp45 display user cart and update items within

See merge request !22
parents 16fff575 abb83311
......@@ -14,13 +14,18 @@ spec:
spec:
containers:
- name: afp-ecom-api-container
image: nexus.mynisum.com/afp-ecom-api:11
image: nexus.mynisum.com/afp-ecom-api:13
imagePullPolicy: Always
ports:
- containerPort: 8084
env:
- name: PORT
value: "8080"
- name: MONGOCONNECTION
valueFrom:
secretKeyRef:
name: mongodb-secret
key: spring.data.mongodb.uri
imagePullSecrets:
- name: registry-creds
......
spring.data.mongodb.uri=mongodb+srv://ecom:ecom@e-commerce-db-cluster.va815.mongodb.net/e-commerce-db?retryWrites=true&w=majority
spring.data.mongodb.database=e-commerce-db
security.enable-csrf=false
server.port=8080
apiVersion: apps/v1
kind: Deployment
metadata:
name: afp-ecom-ui-deployment
spec:
replicas: 2
selector:
matchLabels:
app: afp-ecom-ui
template:
metadata:
labels:
app: afp-ecom-ui
spec:
containers:
- name: afp-ecom-ui-container
image: nexus.mynisum.com/afp-ecom-ui:2
imagePullPolicy: Always
ports:
- containerPort: 8085
env:
- name: PORT
value: "8080"
imagePullSecrets:
- name: registry-creds
---
apiVersion: v1
kind: Service
metadata:
name: afp-ecom-ui-service
spec:
type: LoadBalancer
ports:
- port: 8085
targetPort: 8080
selector:
app: afp-ecom-ui
\ No newline at end of file
import React, { Component } from 'react'
import './header.css';
import { NavLink} from "react-router-dom"
import Nav from "react-bootstrap/Nav"
import {LinkContainer} from 'react-router-bootstrap'
export default class HeaderCart extends Component {
......@@ -12,7 +12,9 @@ export default class HeaderCart extends Component {
render() {
return (
<div>
<Nav.Link href="/cart">Cart</Nav.Link>
<LinkContainer to="/cart" >
<Nav.Link id="nav-Cart-link">Cart</Nav.Link>
</LinkContainer>
</div>
)
}
......
......@@ -18,7 +18,7 @@ export default function SubmitOrder(props) {
<div id="SubmitButtonContainer">
<p className="errorMessage">{(props.errorWhileValidating === 1 && props.allFieldsValidated[0] === 0)? 'One or more fields is missing a value' : ''}</p>
<button id="SubmitButtonInput" onClick={handleSubmitClick} >Submit Order</button>
<button id="SubmitButtonInput" onClick={() => {handleSubmitClick()}} >Submit Order</button>
</div>
</div>
......
......@@ -17,6 +17,13 @@ export default function Checkout() {
///////////////////////
// const {currentUser} = useSelector(state => state)
// const {cart} = useSelector(state => state)
///////////////////////
// Submit Action State
///////////////////////
const dispatch = useDispatch()
///////////////////////
// Shipping Info State
......@@ -56,15 +63,6 @@ export default function Checkout() {
const [errorWhileValidating, setErrorWhileValidating] = useState([0])
///////////////////////
// Submit Action State
///////////////////////
const dispatch = useDispatch()
// This effect listens for the submit button to be clicked
// It then checks each of the required fields to make sure they are non null
useEffect( () => {
......@@ -79,11 +77,7 @@ export default function Checkout() {
shippingAddress,
city,
state,
zipCode,
// cardNumber,
// cardHolderName,
// expirationDate,
// cvv
zipCode
]
// initially validated, gets flipped if required field is empty
......@@ -128,19 +122,6 @@ export default function Checkout() {
let handleSubmit = () => {
console.log("Submitting Order!")
// let order = {}
// order["firstName"] = firstName
// order["lastName"] = lastName
// order["shippingAddress"] = shippingAddress
// order["aptSuitNo"] = aptSuiteNo
// order["city"] = city
// order["state"] = state
// order["zipCode"] = zipCode
// order["cardNumber"] = cardNumber
// order["cardHolderName"] = cardHolderName
// order["expirationDate"] = expirationDate
// order["cvv"] = cvv
// console.log(order)
let chrisSpec = {
"user": {
......@@ -175,32 +156,6 @@ export default function Checkout() {
}
// This watches for changes on any shipping input fields and prints the current states on any change
useEffect( () => {
console.log("======================================")
console.log("First Name: " + firstName)
console.log("Last Name: " + lastName)
console.log("Shipping Address: " + shippingAddress)
console.log("Apt Suite No: " + aptSuiteNo)
console.log("City: " + city)
console.log("State: " + state)
console.log("Zip: " + zipCode)
}, [firstName, lastName, shippingAddress, aptSuiteNo, city, state, zipCode])
// This watches for changes on any billing input fields and prints the current states on any change
useEffect( () => {
console.log("======================================")
console.log("Card Number: " + cardNumber)
console.log("Card Holder Name: " + cardHolderName)
console.log("Expiration Date: " + expirationDate)
console.log("CVV: " + cvv)
}, [cardNumber, cardHolderName, expirationDate, cvv])
return (
<div id="checkout-container">
......
......@@ -2,36 +2,43 @@ import React from 'react'
import './shopping-cart.css'
export default function CartItem(props) {
return (
<div className="shoppingCartItem" >
{console.log(props.productInfo)}
{/* This is based on amazon's style */}
{/* Flex-Direction: Row */}
{/* Left image div */}
<div id="productImageContainer">
<img className="productImage" alt="" src={props.productInfo.productImageUrl} />
<div className="productImageContainer">
<img className="productImage" alt="Product" src={props.productInfo.productImageUrl} />
</div>
{/* Name and Product details */}
<div id="nameAndDetails">
<div id="productName">{props.productInfo.productName}</div>
<div id="productStock">{props.productInfo.availableStock} left in stock</div>
<div className="nameAndDetails">
<div className="productName">{props.productInfo.productName}</div>
<div className="productStock">{props.productInfo.availableStock} left in stock</div>
<div id="quantityControls">
<select id="productQuantitySelect"><option>{props.productInfo.quantity}</option></select>
<button>Delete</button>
<div className="quantityControls">
<input
className="productQuantitySelect"
type="number"
value={props.quantity}
min="1"
max="99"
onChange={(e) => {props.handleQuantityUpdate(props.productInfo.sku, e.target.value)}}
/>
<button onClick={() => { props.handleDelete(props.productInfo.sku) }}>Delete</button>
</div>
</div>
{/* Price and promotions */}
<div id="priceAndPromotions">
<div id="productPrice">${props.productInfo.price.toFixed(2)}</div>
<div className="priceAndPromotions">
<div className="productPrice">${props.productInfo.price.toFixed(2)}</div>
</div>
</div>
......@@ -42,17 +49,3 @@ export default function CartItem(props) {
/*
brand
name
price
*/
\ No newline at end of file
......@@ -12,10 +12,19 @@
width: 100%;
}
#cartItemList {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
margin-bottom: 100px;
}
.shoppingCartItem {
width: 80%;
min-height: 250px;
min-height: 200px;
border-top: 2px solid black;
display: flex;
background-color: white;
......@@ -25,7 +34,13 @@
justify-content: space-between;
}
#productImageContainer {
.productName {
padding-top: 25px;
margin-bottom: 15px;
font-size: 125%;
}
.productImageContainer {
display: flex;
width: 30%;
margin-right: 20px;
......@@ -36,11 +51,12 @@
.productImage {
display: flex;
width: 100%;
padding: 15px;
/* height: auto; */
/* max-height: 100%; */
}
#nameAndDetails {
.nameAndDetails {
display: flex;
width: 45%;
height: 200px;
......@@ -48,7 +64,7 @@
text-align: left;
}
#priceAndPromotions {
.priceAndPromotions {
display: flex;
width: 10%;
height: 200px;
......@@ -57,10 +73,37 @@
align-items: flex-end;
}
#productQuantitySelect {
width: 40px;
.productPrice {
margin-top: 40px;
font-size: 125%;
font-weight: bold;
}
.quantityControls{
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-end;
}
.productQuantitySelect {
width: 40px;
}
#checkoutButtonContainer {
width: 50%;
min-height: 100px;
display: flex;
justify-content: center;
align-items: center;
}
#checkoutButton {
width: 100%;
height: 100%;
background-color: black;
color: white;
font-size: 150%;
font-weight: bold;
margin-bottom: 100px;
}
\ No newline at end of file
import React, { useState, useEffect } from 'react'
import CartItem from './CartItem.js'
import './shopping-cart.css'
import { useSelector, useDispatch } from 'react-redux'
import { Link, Redirect } from 'react-router-dom'
import { updateUserCart } from './../../actions/cart_actions'
export default function ShoppingCart() {
const dispatch = useDispatch()
const userSession = useSelector(state => state.user.currentUser)
const allProducts = useSelector(state => state.market.products)
const [cartRefs, setCartRefs] = useState(useSelector(state => state.cart))
const [cartItems, setCartItems] = useState([])
////////////////////////////////////////////////////////////////
// Map Product Refs to Products that exist in redux global state
////////////////////////////////////////////////////////////////
useEffect(() => {
const productsFromRefs = []
cartRefs.map((cartRef) => {
productsFromRefs.push((
{
product: allProducts.filter((currProduct) => (currProduct.sku === cartRef.productRef.sku))[0],
quantity: cartRef.quantity
}))
})
console.log("NEW PRODUCTS FROM REFS YO")
console.log(productsFromRefs)
setCartItems(productsFromRefs)
}, [cartRefs])
////////////////////////////////
// Delete Item from Cart
////////////////////////////////
let handleDelete = (skuToBeDeleted) => {
const newCartRefs = cartRefs.filter((cartRef) => !(cartRef.productRef.sku === skuToBeDeleted))
setCartRefs(newCartRefs)
}
////////////////////////////////
// Update Quantity of an item
////////////////////////////////
let handleQuantityUpdate = (skuToBeUpdated, newQuantity) => {
const newCartRefs = []
cartRefs.map( (cartRef) => {
if (cartRef.productRef.sku === skuToBeUpdated) {
let temp = cartRef
temp.quantity = newQuantity
newCartRefs.push(temp)
}
else {
newCartRefs.push(cartRef)
}
})
setCartRefs(newCartRefs)
}
////////////////////////////////////////////////////////////////
// Sync Cart with backend and redux global state
////////////////////////////////////////////////////////////////
useEffect(() => {
if (!userSession) return;
const cartUpdateObj = {
userId: userSession.email,
cartItems: cartRefs
}
dispatch(updateUserCart(cartUpdateObj, cartUpdateObj.userId))
}, [cartItems])
if (!userSession) return <Redirect to="/product-market" />
return (
<div id="shoppingCartContainer">
{/* map each cart item into CartItem component */}
<div id="cartItemList">
{cartItems.map((currItem) => {
return (
<CartItem
productInfo={currItem.product}
quantity={currItem.quantity}
handleDelete={handleDelete}
handleQuantityUpdate={handleQuantityUpdate}
key={currItem.product.sku}
/>
)
})}
</div>
<Link id="checkoutButtonContainer" to="/checkout">
<button id="checkoutButton">Proceed To Checkout</button>
</Link>
</div>
)
}
import React, {useState, useEffect} from 'react'
import CartItem from './CartItem.js'
import './shopping-cart.css'
export default function ShoppingCart() {
const [cartItems, setCartItems] = useState([])
// load the json containing user's cart items
useEffect( () => {
setCartItems(
[
{
"id": 1,
"sku": 1,
"upc": 1,
"productName": "UMYOGO Mens Athletic Walking Blade Running Tennis Shoes",
"productDescription": "Rubber sole Rubber material of sole possesses high durability for prolonging the wearing time of our shoes.\nThe elastic blade soles have high flexibility which allows the shoes to bend strongly while doing sports.\nKnit upper material make it possible that your feet free breath when you run or walk.\nIt's soft and protective to cushion your every step",
"price": 45.00,
"availableStock": 100,
"blockedStock": 20,
"productImageUrl": "https://images-na.ssl-images-amazon.com/images/I/71djekdy4eL._AC_UY695_.jpg",
"brand": "UMYOGO",
"category": "shoe",
"quantity": 2
},
{
"id": 2,
"sku": 2,
"upc": 2,
"productName": "adidas Women's Cloudfoam Pure Running Shoe",
"productDescription": "100% Synthetic\nImported\nRubber sole\nPlatform measures approximately 0-3 inches\nBoot opening measures approximately 6-12 inches around\nWomen's specific fit\nCloudfoam memory sockliner and textile lining",
"price": 50.00,
"availableStock": 100,
"blockedStock": 20,
"productImageUrl": "https://images-na.ssl-images-amazon.com/images/I/71G0sB8FnFL._AC_UX695_.jpg",
"brand": "Adidas",
"category": "shoe"
},
{
"id": 3,
"sku": 3,
"upc": 3,
"productName": "Merrell Men's Moab 2 Vent Hiking Shoe",
"productDescription": "100% suede leather, mesh\nImported\nSynthetic sole\nPerformance suede leather and mesh upper\nBellows, closed-cell foam tongue keeps moisture and debris out",
"price": 100.00,
"availableStock": 100,
"blockedStock": 20,
"productImageUrl": "https://images-na.ssl-images-amazon.com/images/I/81m9GyEmoRL._AC_UX695_.jpg",
"brand": "Merrell",
"category": "shoe"
},
{
"id": 4,
"sku": 4,
"upc": 4,
"productName": "Nike Women's Training Basketball Shoe",
"productDescription": "Rubber\nBrand New\n100% Authentic\nOriginal Packaging",
"price": 85.00,
"availableStock": 100,
"blockedStock": 20,
"productImageUrl": "https://images-na.ssl-images-amazon.com/images/I/71zKmTHcHUL._AC_UX695_.jpg",
"brand": "Nike",
"category": "shoe"
},
{
"id": 5,
"sku": 5,
"upc": 5,
"productName": "adidas Men's Daily 3.0 Skate Shoe",
"productDescription": "100% Leather\nRubber sole\nadidas Mens Skate Shoe\nThe adidas brand has a long history and deep-rooted connection with sport. Everything we do is rooted in sport",
"price": 85.00,
"availableStock": 100,
"blockedStock": 20,
"productImageUrl": "https://images-na.ssl-images-amazon.com/images/I/71NVcBO0LEL._AC_UX695_.jpg",
"brand": "Adidas",
"category": "shoe"
},
{
"id": 6,
"sku": 6,
"upc": 6,
"productName": "Amazon Basics Lightweight Soft Easy Care Microfiber Pillowcases - 2-Pack, Black",
"productDescription": "100% Polyester\nImported\nIncludes two 20 x 30 inch standard-size pillowcases\nPolyester microfiber offers strength and exceptional softness",
"price": 8.99,
"availableStock": 100,
"blockedStock": 20,
"productImageUrl": "https://images-na.ssl-images-amazon.com/images/I/818vTEo5TPL._AC_SX679_.jpg",
"brand": "Amazon Basics",
"category": "pillow case"
},
{
"id": 7,
"sku": 7,
"upc": 7,
"productName": "Pillowcase Set of 2 Durable Chair Cushion, Chair Pad Filled Cotton",
"productDescription": "Comfortable Thickness: Seat pad with a thickness of 10 cm is sufficient to relieve stress, so if you sit for a long time, you will feel its warmth and softness.",
"price": 42.99,
"availableStock": 100,
"blockedStock": 20,
"productImageUrl": "https://images-na.ssl-images-amazon.com/images/I/61Txdww5uXS._AC_SX679_.jpg",
"brand": "PillowCase",
"category": "pillow case"
},
{
"id": 8,
"sku": 8,
"upc": 8,
"productName": "Amazon Basics Lightweight Soft Easy Care Microfiber Pillowcases - 2-Pack, Navy Blue",
"productDescription": "100% Polyester\nImported\nIncludes two 20 x 30 inch standard-size pillowcases\nPolyester microfiber offers strength and exceptional softness",
"price": 8.99,
"availableStock": 100,
"blockedStock": 20,
"productImageUrl": "https://images-na.ssl-images-amazon.com/images/I/81jZRRyZCfL._AC_SX679_.jpg",
"brand": "Amazon Basics",
"category": "pillow case"
},
{
"id": 9,
"sku": 9,
"upc": 9,
"productName": "Bedsure Bamboo Pillow Cases Queen Set of 2 - Cooling Ultra Soft Pillowcases",
"productDescription": "Bamboo-derived Rayon\nExceptionally Bamboo Texture: Bedsure queen Pillowcases layer your sleep space with an irresistibly sleek sateen finish to transform your bed into a luxurious retreat - Slip in feathery soft pillowcases to have a dreamy rest as if you are sleeping on a cloud and also wake up with cool feelings - Silky smooth to the touch, bamboo pillowcase queen glides over your hair and face to create a beauty sleep.",
"price": 11.04,
"availableStock": 100,
"blockedStock": 20,
"productImageUrl": "https://images-na.ssl-images-amazon.com/images/I/61IdiIz3qML._AC_SX679_.jpg",
"brand": "BedSure",
"category": "pillow case"
},
{
"id": 10,
"sku": 10,
"upc": 10,
"productName": "Amazon Basics Lightweight Soft Easy Care Microfiber Pillowcases - 2-Pack, Taupe",
"productDescription": "100% Polyester\nImported\nIncludes two 20 x 30 inch standard-size pillowcases\nPolyester microfiber offers strength and exceptional softness",
"price": 8.99,
"availableStock": 100,
"blockedStock": 20,
"productImageUrl": "https://images-na.ssl-images-amazon.com/images/I/91GB7gHxSuL._AC_SX679_.jpg",
"brand": "Amazon Basics",
"category": "pillow case"
}
]
)
}, [] )
return (
<div id="shoppingCartContainer">
{/* map each cart item into CartItem component */}
{cartItems.map( (currItem) => {
return (
<CartItem productInfo={currItem} />
)
})}
</div>
)
}
......@@ -12,7 +12,7 @@ const userReducer = (state = initialState, action) => {
newState.currentUser = action.user.data;
return newState;
case LOGOUT_USER:
return newState.currentUser = null;
return newState.currentUser = ({currentUser: null});
default:
return state;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment