PLP complete - store updated, Cart partial complete and gh pages backup

This commit is contained in:
2020-03-27 13:31:24 +05:30
parent f8d0a04e54
commit c4a85afca3
102 changed files with 1008 additions and 318 deletions

View File

@@ -1,11 +1,21 @@
import React from 'react';
import PropTypes from 'prop-types';
import styles from './Button.component.scss'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import * as fwIcons from '@fortawesome/free-solid-svg-icons'
import './Button.component.scss'
const Button = props => {
return (
<div className='c-Button'>
</div>
<button
className={`c-Button ${props.classes}`}
type={props.buttonType}
name={props.buttonName}
value={props.buttonValue}
disabled={props.buttonDisabled}
autoFocus={props.buttonAutoFocus}
onClick={props.onClickHandler}>
{props.buttonIcon && <FontAwesomeIcon icon={fwIcons[props.buttonIcon]} />}
{props.buttonText}
</button>
);
};

View File

@@ -1,4 +1,6 @@
/* PLOP_INJECT_IMPORT */
import ItemPrice from './molecules/ItemPrice';
import SectionLoader from './molecules/SectionLoader';
import CartSummary from './molecules/CartSummary';
import CartItem from './molecules/CartItem';
import CartList from './molecules/CartList';
@@ -23,6 +25,8 @@ import SelectOption from './atoms/SelectOption';
export {
/* PLOP_INJECT_EXPORT */
ItemPrice,
SectionLoader,
CartSummary,
CartItem,
CartList,

View File

@@ -1,3 +1,17 @@
.c-CartIcon {
@import './../../../styles/variables';
.c-Plp__c-SortAndFilterPanel__c-CartIcon {
margin-left: 1rem;
position: relative;
.c-Plp__c-SortAndFilterPanel__c-CartIcon__badge {
position: absolute;
top: -3px;
right: -9px;
padding: 0px 5px;
border-radius: 50%;
background-color: $prominent-color;
color: $neutral-00;
font-size: $smaller-font-size;
}
}

View File

@@ -1,19 +1,34 @@
import React from 'react';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import './CartIcon.component.scss';
import {withRouter} from 'react-router-dom'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faShoppingCart } from '@fortawesome/free-solid-svg-icons'
import { createPropsSelector } from 'reselect-immutable-helpers';
import {getCartTotalCount} from './../../../pages/Cart/selectors'
const CartIcon = props => {
const navigateToCart = () => {
props.history.push('/view/cart')
}
return (
<div className='c-CartIcon'>
<div className='c-Plp__c-SortAndFilterPanel__c-CartIcon header-icon' onClick={navigateToCart}>
<FontAwesomeIcon icon={faShoppingCart} />
<span className="c-Plp__c-SortAndFilterPanel__c-CartIcon__badge">{props.cartTotalCount}</span>
</div>
);
};
CartIcon.defaultProps = {
};
CartIcon.propTypes = {
cartTotalCount: PropTypes.number
};
export default CartIcon;
const mapStateToProps = createPropsSelector({
cartTotalCount: getCartTotalCount
})
export default connect(mapStateToProps)(withRouter(CartIcon));

View File

@@ -1,3 +1,22 @@
.c-CartItem {
@import './../../../styles//variables';
.c-Cart__c-CartList__c-CartItem {
padding: 15px 0;
margin-bottom: 13px;
background: $neutral-00;
border: 1px solid $neutral-40;
line-height: 1;
[class*="col-"] {
padding-left: 10px;
padding-right: 10px;
}
.c-Cart__c-CartList__c-CartItem__image {
margin-bottom: 0.7rem;
}
.c-Cart__c-CartList__c-CartItem__name {
font-size: $small-font-size;
}
}

View File

@@ -1,12 +1,25 @@
import React from 'react';
import PropTypes from 'prop-types';
import styles from './CartItem.component.scss';
import './CartItem.component.scss';
import SectionLoader from '../SectionLoader/SectionLoader';
import ItemPrice from '../ItemPrice/ItemPrice';
const CartItem = props => {
return (
<div className='c-CartItem'>
</div>
);
const CartItem = ({product}) => {
return product ?
<article className='c-Cart__c-CartList__c-CartItem'>
<div className="container">
<div className="row">
<div className="c-Cart__c-CartList__c-CartItem__inner col-4 col-md-3 col-lg-3">
<img className="c-Cart__c-CartList__c-CartItem__image" src={product.img_url} alt={product.name} style={{width: "100%"}} />
</div>
<div className="c-Cart__c-CartList__c-CartItem__inner col-8 col-md-9 col-lg-9">
{product.name && <p className="c-Cart__c-CartList__c-CartItem__name">{product.name}</p>}
<ItemPrice product={product} />
</div>
</div>
</div>
</article> : <SectionLoader />
};
CartItem.defaultProps = {
@@ -14,7 +27,7 @@ CartItem.defaultProps = {
};
CartItem.propTypes = {
product: PropTypes.object
};
export default CartItem;

View File

@@ -1,3 +1,16 @@
@import './../../../styles/variables';
.c-CartList {
padding: 0.8em 0;
.c-CartList__emptyCart {
background: $neutral-00;
margin-top: 25%;
border: 1px solid black;
text-align: center;
padding: 1em;
.c-CartList__emptyCart__information {
color: $neutral-40;
}
}
}

View File

@@ -1,11 +1,34 @@
import React from 'react';
import PropTypes from 'prop-types';
import styles from './CartList.component.scss';
import {connect} from 'react-redux';
import {createPropsSelector} from 'reselect-immutable-helpers';
import {getProducts} from './../../../pages/PLP/selectors'
import {getCartItems} from './../../../pages/Cart/selectors'
import CartItem from './../CartItem';
import './CartList.component.scss';
const CartList = props => {
const products = props.products
const isCartEmpty = !props.cartItems || Object.keys(props.cartItems).length === 0
const cartItemTiles = !isCartEmpty && Object.keys(props.cartItems).map((itemId, key) => {
const product = products && products.length > 0 && products.filter(product => product.id === parseInt(itemId))[0]
return <CartItem product={product} key={key} />
})
const getEmptyCartBlock = () => {
return (
<div className="c-CartList__emptyCart">
<h3>Your cart is Empty!</h3>
<p className="c-CartList__emptyCart__information">Please add some items from Available Products (use start button on top left)</p>
</div>
)
}
return (
<div className='c-CartList'>
</div>
<section className='c-CartList'>
{isCartEmpty ? getEmptyCartBlock() : cartItemTiles}
</section>
);
};
@@ -14,7 +37,13 @@ CartList.defaultProps = {
};
CartList.propTypes = {
cartItems: PropTypes.object,
products: PropTypes.array
};
export default CartList;
const mapStateToProps = createPropsSelector({
cartItems: getCartItems,
products: getProducts
})
export default connect(mapStateToProps)(CartList);

View File

@@ -1,9 +1,19 @@
@import './../../../styles/variables';
.c-Footer {
display: flex;
align-items: center;
justify-content: center;
position: fixed;
left: 0;
bottom: 0;
width: 100%;
background-color: #222;
color: white;
text-align: center;
background-color: $brand-color;
color: $font-color-light;
padding: 0.7em;
p {
margin-bottom: 0;
font-weight: bold;
}
}

View File

@@ -5,6 +5,7 @@ import styles from './Footer.component.scss';
const Footer = props => {
return (
<footer className='c-Footer'>
<p>@Copyright</p>
</footer>
);
};

View File

@@ -1,3 +1,35 @@
@import './../../../styles/variables';
.c-Header {
display: flex;
// align-items: center;
// justify-content: center;
position: fixed;
height: $header-height;
left: 0;
top: 0;
right: 0;
width: 100%;
background-color: $brand-color;
color: $neutral-00;
padding: 1em 1.5em;
z-index: 2;
transition: transform 0.4s;
.c-Header__logo-main {
cursor: pointer;
}
.c-Header__iconClass {
transform: rotateX(18deg) rotateZ(10deg) scale(2);
color: yellow;
margin: 6px 0px 3px 3px;
}
.header-icon {
font-size: $bigger-font-size;
&:hover {
color: $light-accent-color;
}
}
}

View File

@@ -1,14 +1,44 @@
import React from 'react';
import PropTypes from 'prop-types';
import React, {useEffect} from 'react';
import { Link } from 'react-router-dom';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faStar } from '@fortawesome/free-solid-svg-icons'
import Search from './../Search'
import CartIcon from './../CartIcon'
const Header = props => {
useEffect(() => {
const body = document.body;
const scrollUp = "scroll-up";
const scrollDown = "scroll-down";
let lastScroll = 0;
window.addEventListener("scroll", () => {
const currentScroll = window.pageYOffset;
if (currentScroll === 0) {
body.classList.remove(scrollUp);
return;
}
if (currentScroll > lastScroll && !body.classList.contains(scrollDown)) {
// down
body.classList.remove(scrollUp);
body.classList.add(scrollDown);
} else if (currentScroll < lastScroll && body.classList.contains(scrollDown)) {
// up
body.classList.remove(scrollDown);
body.classList.add(scrollUp);
}
lastScroll = currentScroll;
});
})
return (
<header className='c-Header'>
<Link to="/view/plp" className="c-Header__logo-main"><FontAwesomeIcon className="c-Header__iconClass" icon={faStar} /></Link>
<Search />
<CartIcon />
{!props.inCart && <CartIcon />}
</header>
);
};

View File

@@ -0,0 +1,27 @@
@import './../../../styles/variables';
.c-ItemPrice {
line-height: $smaller-font-size;
margin-bottom: 0.9rem;
display: flex;
.c-ItemPrice__price {
font-size: $smaller-font-size;
font-weight: $bold-font-weight;
padding-right: 0.5rem;
}
.c-ItemPrice__price--strikethrough {
color: $neutral-40;
font-weight: $bold-font-weight;
font-size: $tiny-font-size;
text-decoration: line-through;
margin-left: $unit * 0.8;
}
.c-ItemPrice__discount {
color: #4aa219;
font-weight: $bold-font-weight;
font-size: $tiny-font-size;
margin-left: auto;
}
}

View File

@@ -0,0 +1,33 @@
import React from 'react';
import PropTypes from 'prop-types';
import './ItemPrice.component.scss';
const ItemPrice = ({product}) => {
const hasDiscount = !!product.discount && product.discount !== 0
return (
<div className='c-ItemPrice'>
{product.discountedPrice && <span className="c-Plp__c-ProductContainer__c-ProductTile__price">&#x20B9;{product.discountedPrice}</span>}
{
hasDiscount &&
(
<React.Fragment>
<span className="c-ItemPrice__price--strikethrough"> {product.price}</span>
<span className="c-ItemPrice__discount">{product.discount}% off</span>
</React.Fragment>
)
}
</div>
);
};
ItemPrice.defaultProps = {
product: PropTypes.object
};
ItemPrice.propTypes = {
};
export default ItemPrice;

View File

@@ -0,0 +1,8 @@
import React from 'react';
import ItemPrice from './ItemPrice';
describe('ItemPrice', () => {
it('renders without error', () => {
});
});

View File

@@ -0,0 +1,3 @@
import ItemPrice from './ItemPrice.jsx';
export default ItemPrice;

View File

@@ -1,82 +1,125 @@
@import url('https://fonts.googleapis.com/css?family=Indie+Flower');
.c-PageLoader {
margin-top: 50%;
// margin-top: 50%;
display: flex;
align-items: center;
justify-content: center;
.is-animate {
background: #ffb200;
box-sizing: border-box;
font-size: 66px;
display: -webkit-inline-box;
padding: 14px;
border-radius: 7px;
}
.is-animate > div {
animation-name: style;
display: -webkit-inline-box;
color: #fff;
padding: 9px;
background: #ffb200;
font-family: 'Indie Flower', cursive;
box-shadow: 2px 2px 9px 2px;
}
.l{
animation: letterspacing 1s infinite alternate cubic-bezier(.2, 0, 0, 1);
}
background: rgba(0,0,0,0.3);
width: 100%;
height: 100%;
position: fixed;
top:0px;
left:0px;
z-index: 20;
// .is-animate {
// background: #ffb200;
// box-sizing: border-box;
// font-size: 66px;
// display: -webkit-inline-box;
// padding: 14px;
// border-radius: 7px;
// }
// .is-animate > div {
// animation-name: style;
// display: -webkit-inline-box;
// color: #fff;
// padding: 9px;
// background: #ffb200;
// font-family: 'Indie Flower', cursive;
// box-shadow: 2px 2px 9px 2px;
// }
// .l{
// animation: letterspacing 1s infinite alternate cubic-bezier(.2, 0, 0, 1);
// }
.is-animate > div {
animation-duration: 1s;
animation-fill-mode: both;
animation-iteration-count: infinite;
}
// .is-animate > div {
// animation-duration: 1s;
// animation-fill-mode: both;
// animation-iteration-count: infinite;
// }
.is-animate > div:nth-child(1) { animation-delay: 0.0s }
.is-animate > div:nth-child(2) { animation-delay: 0.1s }
.is-animate > div:nth-child(3) { animation-delay: 0.2s }
.is-animate > div:nth-child(4) { animation-delay: 0.3s }
.is-animate > div:nth-child(5) { animation-delay: 0.4s }
.is-animate > div:nth-child(6) { animation-delay: 0.5s }
.is-animate > div:nth-child(7) { animation-delay: 0.6s }
// .is-animate > div:nth-child(1) { animation-delay: 0.0s }
// .is-animate > div:nth-child(2) { animation-delay: 0.1s }
// .is-animate > div:nth-child(3) { animation-delay: 0.2s }
// .is-animate > div:nth-child(4) { animation-delay: 0.3s }
// .is-animate > div:nth-child(5) { animation-delay: 0.4s }
// .is-animate > div:nth-child(6) { animation-delay: 0.5s }
// .is-animate > div:nth-child(7) { animation-delay: 0.6s }
@keyframes style {
from {
transform: scale3d(1, 1, 1);
}
30% {
box-shadow: 0px 0px 0px 0px;
transform: scale3d(1.25, 0.75, 1);
}
40% {
transform: scale3d(0.75, 1.25, 1);
}
50% {
transform: scale3d(1.15, 0.85, 1);
}
65% {
transform: scale3d(.95, 1.05, 1);
}
75% {
transform: scale3d(1.05, .95, 1);
}
to {
transform: scale3d(1, 1, 1);
// @keyframes style {
// from {
// transform: scale3d(1, 1, 1);
// }
// 30% {
// box-shadow: 0px 0px 0px 0px;
// transform: scale3d(1.25, 0.75, 1);
// }
// 40% {
// transform: scale3d(0.75, 1.25, 1);
// }
// 50% {
// transform: scale3d(1.15, 0.85, 1);
// }
// 65% {
// transform: scale3d(.95, 1.05, 1);
// }
// 75% {
// transform: scale3d(1.05, .95, 1);
// }
// to {
// transform: scale3d(1, 1, 1);
// }
// }
// @keyframes letterspacing {
// 0% {
// filter: blur(0.1rem);
// }
// 100% {
// filter: blur(0.5rem);
// }
// to {
// letter-spacing: none;
// filter: blur(0rem);
// }
// }
.c-PageLoader__lds-ripple {
display: inline-block;
position: relative;
width: 80px;
height: 80px;
div {
position: absolute;
border: 4px solid #fff;
opacity: 1;
border-radius: 50%;
animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite;
&:nth-child(2) {
animation-delay: -0.5s;
}
}
}
@keyframes letterspacing {
@keyframes lds-ripple {
0% {
filter: blur(0.1rem);
}
100% {
filter: blur(0.5rem);
top: 36px;
left: 36px;
width: 0;
height: 0;
opacity: 1;
}
to {
letter-spacing: none;
filter: blur(0rem);
100% {
top: 0px;
left: 0px;
width: 72px;
height: 72px;
opacity: 0;
}
}
}

View File

@@ -2,18 +2,24 @@ import React from 'react';
const PageLoader = props => {
return (
<div className='c-PageLoader'>
<div className='is-animate'>
<div className='l'>l</div>
<div className='l'>o</div>
<div className='l'>a</div>
<div className='l'>d</div>
<div className='l'>i</div>
<div className='l'>n</div>
<div className='l'>g</div>
</div>
<div className='c-PageLoader'>
<div className="c-PageLoader__lds-ripple">
<div></div>
<div></div>
</div>
</div>
)
// <div className='c-PageLoader'>
// <div className='is-animate'>
// <div className='l'>l</div>
// <div className='l'>o</div>
// <div className='l'>a</div>
// <div className='l'>d</div>
// <div className='l'>i</div>
// <div className='l'>n</div>
// <div className='l'>g</div>
// </div>
// </div>
};
PageLoader.defaultProps = {

View File

@@ -1,3 +1,3 @@
.c-ProductContainer {
.c-Plp__c-ProductContainer {
}

View File

@@ -1,20 +1,48 @@
import React from 'react';
import PropTypes from 'prop-types';
import styles from './ProductContainer.component.scss';
import {connect} from 'react-redux';
import {dispatchProducts} from './../../../pages/PLP/actions'
import ProductTile from './../ProductTile'
import PageLoader from '../PageLoader/PageLoader';
const ProductContainer = props => {
return (
<div className='c-ProductContainer'>
</div>
);
const productsWithDiscountedPrice = props.products
&& props.products.length !== 0
&& props.products.map(product => {
const discount = product.discount && product.price * (product.discount/100)
product.discountedPrice = Math.ceil(product.price - discount)
return product
})
const productTiles = productsWithDiscountedPrice && productsWithDiscountedPrice.map((product, key) =>{
return <ProductTile product={product} key={key} />
})
props.dispatchProducts(productsWithDiscountedPrice)
return props.products && props.products.length !== 0 ?
<main className='c-Plp__c-ProductContainer'>
<div className="container">
<div className="row">
{productTiles}
</div>
</div>
</main> : <PageLoader />
};
ProductContainer.defaultProps = {
products: []
};
ProductContainer.propTypes = {
products: PropTypes.array
};
export default ProductContainer;
const mapDispatchToProps = ({
dispatchProducts
})
export default connect(
null,
mapDispatchToProps
)(ProductContainer);

View File

@@ -1,3 +1,36 @@
.c-ProductTile {
@import './../../../styles/variables';
.c-Plp__c-ProductContainer__c-ProductTile {
// box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
max-width: 300px;
// margin-bottom: 2rem;
padding: 10px 15px;
border: 1px solid $neutral-17;
.c-Plp__c-ProductContainer__c-ProductTile__image {
margin-bottom: 0.7rem;
}
.c-Plp__c-ProductContainer__c-ProductTile__name {
font-size: $small-font-size;
}
button {
display: block;
margin: 0 auto;
border-radius: 20px;
padding: 7px;
background-color: $add-to-cart-button-background-color;
cursor: pointer;
font-size: $smaller-font-size;
font-weight: $bold-font-weight;
}
button:hover {
opacity: 0.7;
}
@media only screen and (min-width: 768px) {
}
}

View File

@@ -1,20 +1,76 @@
import React from 'react';
import PropTypes from 'prop-types';
import styles from './ProductTile.component.scss';
import {connect} from 'react-redux';
import {createPropsSelector} from 'reselect-immutable-helpers';
import {addToCart} from './../../../pages/Cart/actions'
import {getCartTotalCount, getCartItems} from './../../../pages/Cart/selectors'
import Button from './../../atoms/Button'
import ItemPrice from '../ItemPrice/ItemPrice';
import './ProductTile.component.scss';
const ProductTile = props => {
const product = props.product
const addToCartButtonClass = "btn btn__addToCart"
const addToCart = (productId) => {
let cartTotalCount = props.cartTotalCount
const cartItems = props.cartItems
let count = cartItems && cartItems[productId] ? cartItems[productId] : 0
cartItems[productId] = ++count
props.addToCart(++cartTotalCount, cartItems)
}
return (
<div className='c-ProductTile'>
</div>
<article
className='c-Plp__c-ProductContainer__c-ProductTile col-6 col-md-4 col-lg-2'
id={`product_${product.id}`}>
<figure>
<img className="c-Plp__c-ProductContainer__c-ProductTile__image" src={product.img_url} alt={product.name} style={{width: "100%"}} />
{product.name && <figcaption className="c-Plp__c-ProductContainer__c-ProductTile__name">{product.name}</figcaption>}
</figure>
{/* <div className="c-Plp__c-ProductContainer__c-ProductTile__price__container">
{product.discountedPrice && <span className="c-Plp__c-ProductContainer__c-ProductTile__price">&#x20B9;{product.discountedPrice}</span>}
{
hasDiscount &&
(
<React.Fragment>
<span className="c-Plp__c-ProductContainer__c-ProductTile__price--strikethrough">{product.price}</span>
<span className="c-Plp__c-ProductContainer__c-ProductTile__discount">{product.discount}% off</span>
</React.Fragment>
)
}
</div> */}
<ItemPrice product={product} />
<Button
classes={addToCartButtonClass}
buttonValue="add-to-cart"
buttonType="button"
buttonName="addToCartButton"
buttonText="Add To Cart"
onClickHandler={() => addToCart(product.id)} />
</article>
);
};
ProductTile.defaultProps = {
};
ProductTile.propTypes = {
addToCart: PropTypes.func,
cartTotalCount: PropTypes.number,
cartItems: PropTypes.object
};
export default ProductTile;
const mapStateToProps = createPropsSelector({
cartTotalCount: getCartTotalCount,
cartItems: getCartItems
})
const mapDispatchToProps = ({
addToCart
})
export default connect(
mapStateToProps,
mapDispatchToProps
)(ProductTile);

View File

@@ -1,3 +1,3 @@
.c-Search {
.c-Plp__c-SortAndFilterPanel__c-Search {
margin-left: auto;
}

View File

@@ -1,9 +1,15 @@
import React from 'react';
import PropTypes from 'prop-types';
import React, {useState} from 'react';
import './Search.component.scss';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faSearch } from '@fortawesome/free-solid-svg-icons'
import InputField from './../../atoms/InputField'
const Search = props => {
const [iconClicked, setIconClicked] = useState(false)
return (
<div className='c-Search'>
<div className='c-Plp__c-SortAndFilterPanel__c-Search header-icon' onClick={() => setIconClicked(!iconClicked)}>
{iconClicked && <InputField /> }
<FontAwesomeIcon icon={faSearch} />
</div>
);
};

View File

@@ -0,0 +1,47 @@
.c-SectionLoader {
display: flex;
align-items: center;
justify-content: center;
background: rgba(0,0,0,0.3);
width: 100%;
height: 100%;
position: relative;
top:0px;
left:0px;
z-index: 10;
.c-SectionLoader__lds-ripple {
display: inline-block;
position: absolute;
width: 80px;
height: 80px;
div {
position: absolute;
border: 4px solid #fff;
opacity: 1;
border-radius: 50%;
animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite;
&:nth-child(2) {
animation-delay: -0.5s;
}
}
}
@keyframes lds-ripple {
0% {
top: 36px;
left: 36px;
width: 0;
height: 0;
opacity: 1;
}
100% {
top: 0px;
left: 0px;
width: 72px;
height: 72px;
opacity: 0;
}
}
}

View File

@@ -0,0 +1,24 @@
import React from 'react';
import PropTypes from 'prop-types';
import './SectionLoader.component.scss';
const SectionLoader = props => {
return (
<div className='c-SectionLoader'>
<div className="c-SectionLoader__lds-ripple">
<div></div>
<div></div>
</div>
</div>
);
};
SectionLoader.defaultProps = {
};
SectionLoader.propTypes = {
};
export default SectionLoader;

View File

@@ -0,0 +1,8 @@
import React from 'react';
import SectionLoader from './SectionLoader';
describe('SectionLoader', () => {
it('renders without error', () => {
});
});

View File

@@ -0,0 +1,3 @@
import SectionLoader from './SectionLoader.jsx';
export default SectionLoader;

View File

@@ -1,3 +1,31 @@
.c-SortAndFilterPanel {
@import './../../../styles/variables';
.c-Plp__c-SortAndFilterPanel {
text-align: center;
.c-Plp__c-SortAndFilterPanel__tool {
padding: 0.8rem;
font-size: $font-size;
font-weight: $bold-font-weight;
border: 1px solid $neutral-17;
.c-Plp__c-SortAndFilterPanel__toolContent {
margin-bottom: 0;
}
}
// .ripple {
// background-color: $shocking-pink;
// width: 1rem;
// height: 1rem;
// position: absolute;
// border-radius: 50%;
// transform: translateX(-100%) translateY(-100%);
// mix-blend-mode: screen;
// animation: ripple 1000ms ease-out forwards;
// }
// @keyframes ripple {
// 0% { transform: translate(-100%, -100%); }
// 80% { transform: translate(-100%, -100%) scale(50); }
// 100% { transform: translate(-100%, -100%) scale(50); opacity: 0; }
// }
}

View File

@@ -1,9 +1,22 @@
import React from 'react';
import PropTypes from 'prop-types';
import './SortAndFilterPanel.component.scss'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faSort } from '@fortawesome/free-solid-svg-icons'
import { faFilter } from '@fortawesome/free-solid-svg-icons'
const SortAndFilterPanel = props => {
return (
<div className='c-SortAndFilterPanel'>
<div className='c-Plp__c-SortAndFilterPanel'>
<div className="container">
<div className="row">
<div className="c-Plp__c-SortAndFilterPanel__tool sort col-6">
<p className="c-Plp__c-SortAndFilterPanel__toolContent"><FontAwesomeIcon icon={faSort} /> Sort</p>
</div>
<div className="c-Plp__c-SortAndFilterPanel__tool filter col-6">
<p className="c-Plp__c-SortAndFilterPanel__toolContent"><FontAwesomeIcon icon={faFilter} /> Filter</p>
</div>
</div>
</div>
</div>
);
};

View File

@@ -1,20 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import styles from './Cart.module.scss';
const Cart = props => {
return (
<div className={styles.root}>
</div>
);
};
Cart.defaultProps = {
};
Cart.propTypes = {
};
export default Cart;

View File

@@ -0,0 +1,36 @@
import React from 'react';
import './Cart.module.scss';
import Header from './../../components/molecules/Header'
import CartList from './../../components/molecules/CartList'
import CartSummary from './../../components/molecules/CartSummary'
import Footer from './../../components/molecules/Footer'
const Cart = props => {
return (
<div className="c-Cart">
<Header inCart={true} />
<main className="container c-Cart__mainContent">
<div className="row">
<div className="col-12 col-md-8 col-lg-8">
<CartList />
</div>
<div className="col-12 col-md-4 col-lg-4">
<CartSummary />
</div>
</div>
</main>
<Footer />
</div>
);
};
Cart.defaultProps = {
};
Cart.propTypes = {
};
export default Cart;

View File

@@ -1,3 +1,13 @@
@import './../../styles/variables';
.c-Cart {
background: $neutral-12;
font-family: $font-family;
margin-top: $header-height;
margin-bottom: 46px;
// padding: 15px 10px;
.c-Cart__mainContent {
}
}

View File

@@ -1,3 +1,4 @@
export const ADD_TO_CART = 'ADD_TO_CART'
export const UPDATE_FORM_VALUES = 'UPDATE_BILLING_FORM_VALUES'
export const UPDATE_FORM_ERRORS = 'UPDATE_BILLING_FORM_ERRORS'
@@ -9,6 +10,13 @@ export const initializeLogin = () => (dispatch) => {
// .catch((err) => ({statusCode: err.statusCode || 500}))
}
export const addToCart = (cartTotalCount, cartItems) => {
return {
type: ADD_TO_CART,
payload: {cartTotalCount, cartItems}
}
}
export const updateFormValues = (formValues) => {
return {
type: UPDATE_FORM_VALUES,

View File

@@ -1,3 +1,3 @@
import Cart from './Cart';
import Cart from './Cart.jsx';
export default Cart;

View File

@@ -1,11 +1,18 @@
import Immutable from 'immutable'
import {UPDATE_FORM_ERRORS, UPDATE_FORM_VALUES} from './actions'
import {
ADD_TO_CART,
UPDATE_FORM_ERRORS,
UPDATE_FORM_VALUES
} from './actions'
const initialState = Immutable.Map()
const initialState = Immutable.Map({
cartItems: {}
})
const reducer = (state = initialState, action) => {
switch (action.type) {
case ADD_TO_CART:
case UPDATE_FORM_ERRORS:
case UPDATE_FORM_VALUES:
return state.mergeDeep(action.payload)

View File

@@ -3,12 +3,14 @@ import {createGetSelector} from 'reselect-immutable-helpers'
const getData = ({data}) => data
export const getLogin = createSelector(
export const getCart = createSelector(
getData,
(dataState) => {
return dataState.pages.cart
}
)
export const getFormValues = createGetSelector(getLogin, 'formValues')
export const getFormErrors = createGetSelector(getLogin, 'formErrors')
export const getFormValues = createGetSelector(getCart, 'formValues')
export const getFormErrors = createGetSelector(getCart, 'formErrors')
export const getCartTotalCount = createGetSelector(getCart, 'cartTotalCount')
export const getCartItems = createGetSelector(getCart, 'cartItems')

View File

@@ -1,29 +1,46 @@
import React from 'react';
import React, {useState, useEffect} from 'react';
import PropTypes from 'prop-types';
import styles from './Plp.module.scss';
import {connect} from 'react-redux';
import {dispatchProducts} from './actions';
import Header from './../../components/molecules/Header'
import SortAndFilterPanel from './../../components/molecules/SortAndFilterPanel'
import ProductContainer from './../../components/molecules/ProductContainer'
import Footer from './../../components/molecules/Footer'
const Plp = props => {
const Plp = ({dispatchProducts}) => {
const [products, setProducts] = useState([])
useEffect(() => {
fetch('https://api.myjson.com/bins/qzuzi')
.then(res => res.json())
.then(products => {
// console.log(products)
setProducts(products)
dispatchProducts(products)
})
}, [])
return (
<div className={styles.root}>
<Header />
<SortAndFilterPanel />
<ProductContainer />
<Footer />
</div>
);
};
Plp.defaultProps = {
<div className="c-Plp">
<Header />
<SortAndFilterPanel />
<ProductContainer products={products} />
<Footer />
</div>
)
};
Plp.propTypes = {
dispatchProducts: PropTypes.func
};
export default Plp;
const mapDispatchToProps = ({
dispatchProducts
})
export default connect(
null,
mapDispatchToProps
)(Plp);

View File

@@ -1,3 +1,7 @@
@import './../../styles/variables';
.c-Plp {
background: $neutral-12;
font-family: $font-family;
margin-top: $header-height;
}

View File

@@ -1,3 +1,4 @@
export const DISPATCH_PRODUCTS = 'DISPATCH_PRODUCTS'
export const UPDATE_FORM_VALUES = 'UPDATE_BILLING_FORM_VALUES'
export const UPDATE_FORM_ERRORS = 'UPDATE_BILLING_FORM_ERRORS'
@@ -9,6 +10,13 @@ export const initializeLogin = () => (dispatch) => {
// .catch((err) => ({statusCode: err.statusCode || 500}))
}
export const dispatchProducts = (products) => {
return {
type: DISPATCH_PRODUCTS,
payload: {products}
}
}
export const updateFormValues = (formValues) => {
return {
type: UPDATE_FORM_VALUES,

View File

@@ -1,11 +1,18 @@
import Immutable from 'immutable'
import {UPDATE_FORM_ERRORS, UPDATE_FORM_VALUES} from './actions'
import {
DISPATCH_PRODUCTS,
UPDATE_FORM_ERRORS,
UPDATE_FORM_VALUES
} from './actions'
const initialState = Immutable.Map()
const initialState = Immutable.Map({
products: []
})
const reducer = (state = initialState, action) => {
switch (action.type) {
case DISPATCH_PRODUCTS:
case UPDATE_FORM_ERRORS:
case UPDATE_FORM_VALUES:
return state.mergeDeep(action.payload)

View File

@@ -3,12 +3,13 @@ import {createGetSelector} from 'reselect-immutable-helpers'
const getData = ({data}) => data
export const getLogin = createSelector(
export const getPlp = createSelector(
getData,
(dataState) => {
return dataState.pages.plp
}
)
export const getFormValues = createGetSelector(getLogin, 'formValues')
export const getFormErrors = createGetSelector(getLogin, 'formErrors')
export const getFormValues = createGetSelector(getPlp, 'formValues')
export const getFormErrors = createGetSelector(getPlp, 'formErrors')
export const getProducts = createGetSelector(getPlp, 'products')

View File

@@ -27,7 +27,7 @@ class Router extends React.Component {
return (
<Provider store={store}>
<BrowserRouter basename="/adobeassignment">
<Route exact path="/" component={LoadableLogin} />
<Route exact path="/" component={LoadablePLP} />
<Route path="/login" component={LoadableLogin} />
<Route path="/view/plp" component={LoadablePLP} />
<Route path="/view/cart" component={LoadableCart} />

View File

@@ -10,4 +10,5 @@
@import '../components/molecules/LoginForm/LoginForm.component';
@import '../components/molecules/PageLoader/PageLoader.component';
@import '../components/molecules/SocialLogin/SocialLogin.component';
// @import '../components/molecules/SortAndFilterPanel/SortAndFilterPanel.component';
// @import '../preloader/preload';

View File

@@ -98,7 +98,8 @@ $big-font-size: 20px;
$font-size: 16px;
$small-font-size: 14px;
$smaller-font-size: 12px;
$tiny-font-size: 8px;
$tiny-font-size: 10px;
$smallest-font-size: 8px;
// Font weight
$thin-font-weight: 100;
@@ -117,6 +118,7 @@ $neutral-00: #fff;
$neutral-10: #f7f7f7;
$neutral-12: #f1f3f6;
$neutral-15: #eee;
$neutral-17: #e1e1e1;
$neutral-20: #d5d5d5;
$neutral-30: #bfbfbf;
$neutral-40: #999;
@@ -163,6 +165,7 @@ $dark-error-color: darken($error-color, 15%);
// Sale color
$sale-color: $error-color;
$prominent-color: $error-color;
// Social colors
$facebook-color: #3a5a93;
@@ -178,7 +181,7 @@ $yelp-color: #af0606;
// ---
$font-color: $neutral-60;
$font-color-light: #b8b8b8;
$font-color-light: $neutral-12;
$link-color: $ui-brand-color;
$active-link-color: $dark-accent-color;
@@ -194,6 +197,7 @@ $focused-input-border-color: $secondary-brand-color;
$disabled-input-color: $neutral-40;
$disabled-input-background-color: $neutral-15;
$disabled-button-background-color: $neutral-15;
$add-to-cart-button-background-color: #f7ae3a;
$horizontal-input-padding: $unit;
$vertical-input-padding: $unit;

View File

@@ -13,3 +13,11 @@
background-position: -150vw 0;
}
}
.scroll-down .c-Header {
transform: translate3d(0, -100%, 0);
}
.scroll-up .c-Header {
transform: none;
}

View File

@@ -22,6 +22,7 @@
html {
color: $font-color;
background-color: $neutral-12;
font-family: $font-family;
font-size: $font-size;
line-height: $line-height;
@@ -33,7 +34,6 @@ html {
body {
margin: 0;
background-color: #222!important;
}
// Grouping content