Implementing PDP page

parent d0865944
.appFooter {
position: fixed;
// position: fixed;
bottom: 0;
left: 0;
}
\ No newline at end of file
......@@ -8,6 +8,7 @@
left: 0;
width: 100%;
box-shadow: 0 0 2px 0 rgba-background($primary-black, .24), 0 2px 2px 0 rgba-background($primary-black, .12);
background: $primary-white;
@include flex-box();
h1 {
......
......@@ -6,7 +6,7 @@
height: 100%;
.loader {
border: 16px solid $gray;
border: 16px solid $gray1;
border-radius: 50%;
border-top: 16px solid $primary;
width: 120px;
......
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import api from 'services/api';
import App from 'App';
import configureStore from 'redux/configureStore';
......@@ -19,6 +20,8 @@ export function main(args = {}) {
if (renderNow) {
api.init(store);
reactDOM.render(
<Provider store={store}>
<App />
......
class Image {
constructor(data) {
this.data = data || {};
}
get width() {
const { width } = this.data;
return width;
}
get height() {
const { height } = this.data;
return height;
}
get alt() {
const { alt } = this.data;
return alt || 'product image';
}
get url() {
const { href: url } = this.data;
return url;
}
}
export default Image;
\ No newline at end of file
import Product from 'models/product';
import Image from 'models/image';
import ROUTES from 'constants/routes';
class ListingProduct extends Product {
constructor(data) {
super(data);
this._image = new Image(this.data.thumbnail);
}
get image() {
return this._image;
}
get sellingPrice() {
const { high, low } = this.data.priceRange.selling;
return `$${low} - $${high}`;
}
get detailsPageUrl() {
const { id } = this.data;
return ROUTES.details.getUrl(id);
}
}
export default ListingProduct;
\ No newline at end of file
import Image from 'models/image';
class Product {
constructor(data) {
this.data = data || {};
this._image = new Image(this.data.hero);
}
get name() {
const { name = '' } = this.data;
return name.replace(/&amp;/, '&');
}
get image() {
return this._image;
}
get sellingPrice() {
const { priceRange: { selling: { high, low } = {} } = {} } = this.data;
return `$${low} - $${high}`;
}
}
export default Product;
\ No newline at end of file
import Product from 'models/product';
import Image from 'models/image';
class ProductDetails extends Product {
constructor(data) {
super(data);
const { images = [] } = this.data;
this._images = images.map(item => new Image(item));
}
get images() {
return this._images;
}
}
export default ProductDetails;
\ No newline at end of file
import React, { Component } from 'react';
import styles from './PDP.module.scss';
import style from './PDP.module.scss';
import Product from 'models/product';
class PDPPage extends Component {
componentDidMount() {
const { match: { params: { productId } } } = this.props;
this.props.getProductDetails(productId);
}
render() {
const image = { url: 'https://www.westelm.com/weimgs/rk/images/wcm/products/202003/0005/belgian-linen-ladder-stripe-embroidery-duvet-cover-shams-s-m.jpg' };
const { product } = this.props;
const { name, } = new Product(product);
return (
<section className={styles.PDPPage}>
PDP Page!
<section className={style.PDPPage}>
<div className={style.content}>
<div className={style.images}>
<img
src={image.url}
alt={image.alt}
className={style.productImage}
/>
</div>
<div className={style.details}>
<h1>{name}</h1>
<div>Price: 44 - 329</div>
</div>
</div>
</section>
);
}
}
export default PDPPage;
\ No newline at end of file
......@@ -2,4 +2,24 @@
.PDPPage {
margin-top: $large-footer-height;
h1 {
color: $gray2;
}
.content {
display: flex;
align-items: flex-start;
justify-content: space-evenly;
box-sizing: border-box;
padding: 15px;
flex-wrap: wrap;
.images {}
.details {
// width: 50%;
}
}
}
\ No newline at end of file
import { connect } from 'react-redux';
import { getProductDetails } from 'redux/actions/product';
import PDPPage from './PDP';
export default PDPPage;
\ No newline at end of file
const mapStateToProps = (state) => {
const { product, loading, error } = state.product;
return {
product,
loading,
error
}
};
const mapDispatchToProps = {
getProductDetails
};
export default connect(mapStateToProps, mapDispatchToProps)(PDPPage);
\ No newline at end of file
import React, { Component } from 'react';
import styles from './PLP.module.scss';
import style from './PLP.module.scss';
import ProductItem from './ProductItem';
import ListingProduct from 'models/listingProduct';
class PLPPage extends Component {
render() {
const { name, items } = this.props;
return (
<section className={styles.PLPPage}>
PLP Page!
<section className={style.PLPPage}>
<h1>{name}</h1>
<ul className={style.products}>
{
items.map(item => {
return (
<ProductItem
key={item.id}
data={new ListingProduct(item)}
/>
)
})
}
</ul>
</section>
);
}
......
@import '~styles/variables';
@import '~styles/mixins';
.PLPPage {
margin-top: $large-footer-height;
padding: 10px;
h1 {
margin: 0;
margin-left: 10px;
color: $gray2;
}
.products {
margin: 0;
padding: 0;
display: flex;
flex-wrap: wrap;
}
}
\ No newline at end of file
import React, { Component } from 'react';
import style from './ProductItem.module.scss';
class ProductItem extends Component {
render() {
const { data = {} } = this.props;
const { name, image, sellingPrice, detailsPageUrl } = data;
return (
<li className={style.productItem}>
<a href={detailsPageUrl}>
<img
src={image.url}
alt={image.alt}
className={style.productImage}
/>
</a>
<div className={style.details}>
<a className={style.title} href={detailsPageUrl}>{name}</a>
<div className={style.sellingPrice}>
{sellingPrice}
</div>
</div>
</li>
);
}
}
export default ProductItem;
@import '~styles/variables';
.productItem {
list-style: none;
max-height: 420px;
max-width: 350px;
box-sizing: border-box;
padding: 10px;
.productImage {
width: 100%;
}
.details {
padding: 5px;
}
.sellingPrice {
color: $primary;
font-weight: bold;
margin-top: 5px;
}
}
\ No newline at end of file
import ProductItem from './ProductItem';
export default ProductItem;
\ No newline at end of file
import { connect } from 'react-redux';
import PLPPage from './PLP';
export default PLPPage;
\ No newline at end of file
const mapStateToProps = ({ category }) => {
const { items, name } = category;
return {
items,
name
}
};
export default connect(mapStateToProps, null)(PLPPage);
import API from 'services/api';
import { PRODUCT_ACTION_TYPE } from 'redux/constants/actionTypes';
export function getProductDetails(id) {
const { PRODUCT_INIT_BEGIN, PRODUCT_INIT_SUCCESS, PRODUCT_INIT_FAILED } = PRODUCT_ACTION_TYPE;
return async dispatch => {
dispatch({ type: PRODUCT_INIT_BEGIN });
try {
const product = await API.getProductDetails(id);
dispatch({ type: PRODUCT_INIT_SUCCESS, payload: product });
}
catch (err) {
console.log(err);
dispatch({ type: PRODUCT_INIT_FAILED, payload: err });
}
};
}
\ No newline at end of file
......@@ -7,4 +7,14 @@ export const CATEGORY_ACTION_TYPE = Object.freeze({
CATEGORY_INIT_BEGIN,
CATEGORY_INIT_SUCCESS,
CATEGORY_INIT_FAILED,
});
const PRODUCT_INIT_BEGIN = 'PRODUCT_INIT_BEGIN';
const PRODUCT_INIT_SUCCESS = 'PRODUCT_INIT_SUCCESS';
const PRODUCT_INIT_FAILED = 'PRODUCT_INIT_FAILED';
export const PRODUCT_ACTION_TYPE = Object.freeze({
PRODUCT_INIT_BEGIN,
PRODUCT_INIT_SUCCESS,
PRODUCT_INIT_FAILED,
});
\ No newline at end of file
......@@ -21,7 +21,8 @@ export default function categoryReducer(state = initialState, action) {
case CATEGORY_INIT_SUCCESS: {
return {
...state,
loading: false
loading: false,
...action.payload
}
}
case CATEGORY_INIT_FAILURE: {
......
import { combineReducers } from 'redux';
import categoryReducer from './category';
import productReducer from './product';
const rootReducer = combineReducers({
category: categoryReducer,
product: productReducer
});
export default rootReducer;
import { PRODUCT_ACTION_TYPE } from 'redux/constants/actionTypes';
const { PRODUCT_INIT_BEGIN, PRODUCT_INIT_SUCCESS, PRODUCT_INIT_FAILURE } = PRODUCT_ACTION_TYPE;
const initialState = {
loading: true,
error: null,
product: null
};
export default function productReducer(state = initialState, action) {
switch (action.type) {
case PRODUCT_INIT_BEGIN: {
return {
...state,
loading: true
}
}
case PRODUCT_INIT_SUCCESS: {
return {
...state,
loading: false,
product: action.payload
}
}
case PRODUCT_INIT_FAILURE: {
return {
...state,
loading: false,
error: action.payload,
}
}
default:
return state;
}
}
\ No newline at end of file
......@@ -16,15 +16,23 @@ async function get(url) {
StatusText: ${response.statusText}`;
throw Error(message);
}
}
class ApiClient {
init(store) {
this.store = store;
}
async getCategoryData() {
return get(URL.CATEGORY);
}
async getProductDetails(id) {
const { category: { items = [] } = {} } = this.store.getState();
const product = items.find(item => item.id === id);
return Promise.resolve(product)
}
}
export default new ApiClient();
\ No newline at end of file
// Primary
$primary : #866347;
$primary-black : #333333;
$gray : #F5F6F8;
$primary-white : white;
$gray1 : #F5F6F8;
$gray2 : gray;
$large-footer-height: 60px;
\ No newline at end of file
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