diff --git a/package.json b/package.json
index 583c793..232e0dc 100644
--- a/package.json
+++ b/package.json
@@ -22,7 +22,6 @@
"axios": "^0.19.2",
"bootstrap": "^4.4.1",
"classnames": "^2.2.6",
- "node-sass": "^4.14.1",
"prop-types": "^15.7.2",
"react": "^16.13.1",
"react-bootstrap": "^1.0.0-beta.17",
@@ -33,10 +32,11 @@
"react-router-dom": "^5.1.2",
"react-scripts": "3.4.0",
"redux": "^4.0.5",
- "redux-thunk": "^2.3.0"
+ "redux-thunk": "^2.3.0",
+ "sass": "^1.86.3"
},
"scripts": {
- "start": "react-scripts start",
+ "start": "set NODE_OPTIONS=--openssl-legacy-provider && react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
diff --git a/src/components/Products/FavouriteIcon.js b/src/components/Products/FavouriteIcon.js
new file mode 100644
index 0000000..2e64ed0
--- /dev/null
+++ b/src/components/Products/FavouriteIcon.js
@@ -0,0 +1,16 @@
+import React, { useState } from 'react';
+import styles from './SingleProduct.module.css'
+
+const FavouriteIcon = ({isActive=false}) => {
+ const[hovered,setHovered]=useState(false)
+ const iconClass=hovered ||isActive ?'fas fa-heart' :'far fa-heart'
+ return (
+
setHovered(true)}
+ onMouseLeave={()=>setHovered(false)}>
+
+
+ )
+}
+
+export default FavouriteIcon
\ No newline at end of file
diff --git a/src/components/Products/NewArrivals.js b/src/components/Products/NewArrivals.js
index 9c8fd6e..89e9ff9 100644
--- a/src/components/Products/NewArrivals.js
+++ b/src/components/Products/NewArrivals.js
@@ -4,105 +4,114 @@
** Github URL: https://github.com/quintuslabs/fashion-cube
*/
-import React, { Component } from "react";
-import SingleProduct from "./SingleProduct";
-import Heading from "../Heading";
-import PropTypes from "prop-types";
-class NewArrivals extends Component {
- constructor(props) {
- super(props);
- this.state = {
- products: this.props.products,
- productsBAK: this.props.products,
- departments: this.props.departments
- };
- }
-
- optionClicked(option) {
- let FilterList = this.state.productsBAK.filter(
- item => item.department === option
- );
- if (FilterList.length > 0) {
- this.setState({ products: FilterList });
- } else {
- this.setState({ products: this.state.productsBAK });
- }
- this.setState({ selectedOption: option });
- }
-
- render() {
- const { products, departments } = this.state;
-
- return (
-
-
-
-
-
-
-
-
-
- - this.optionClicked("All")}
- className={`grid_sorting_button button d-flex flex-column justify-content-center align-items-center ${
- this.state.selectedOption === "All"
- ? "active is-checked"
- : null
- }`}
- >
- all
-
- - this.optionClicked("Women")}
- >
- women's
-
-
- - this.optionClicked("Men")}
- >
- men's
-
-
-
-
-
-
- {products &&
- products.slice(0, 8).map((item, index) => {
- return (
-
-
-
- );
- })}
-
-
-
- );
- }
-}
-
-NewArrivals.propTypes = {
- addToCart: PropTypes.func
-};
-
-export default NewArrivals;
+ import React, { Component } from "react";
+ import SingleProduct from "./SingleProduct";
+ import Heading from "../Heading";
+ import PropTypes from "prop-types";
+ import jumpTo from "../../modules/Navigation";
+ class NewArrivals extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ products: this.props.products,
+ productsBAK: this.props.products,
+ departments: this.props.departments
+ };
+ }
+
+ optionClicked(option) {
+ let FilterList = this.state.productsBAK.filter(
+ item => item.department === option
+ );
+ if (FilterList.length > 0) {
+ this.setState({ products: FilterList });
+ } else {
+ this.setState({ products: this.state.productsBAK });
+ }
+ this.setState({ selectedOption: option });
+ }
+ handleNavigate = (productId) => {
+ jumpTo(`/fashion-cube/single-product/${productId}`);
+ };
+ handleAddToCart = (productId) => {
+ this.props.addToBag(productId)
+ };
+ render() {
+ const { products, departments } = this.state;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ - this.optionClicked("All")}
+ className={`grid_sorting_button button d-flex flex-column justify-content-center align-items-center ${
+ this.state.selectedOption === "All"
+ ? "active is-checked"
+ : null
+ }`}
+ >
+ all
+
+ - this.optionClicked("Women")}
+ >
+ women's
+
+
+ - this.optionClicked("Men")}
+ >
+ men's
+
+
+
+
+
+
+ {products &&
+ products.slice(0, 8).map((item, index) => {
+ return (
+
+
+
+ );
+ })}
+
+
+
+ );
+ }
+ }
+
+ NewArrivals.propTypes = {
+ addToCart: PropTypes.func
+ };
+
+ export default NewArrivals;
+
\ No newline at end of file
diff --git a/src/components/Products/ProductPrice.js b/src/components/Products/ProductPrice.js
new file mode 100644
index 0000000..44a7f9b
--- /dev/null
+++ b/src/components/Products/ProductPrice.js
@@ -0,0 +1,22 @@
+import React from 'react'
+import styles from './SingleProduct.module.css';
+import SkeletonProduct from './SkeltonProduct';
+const ProductPrice = ({productItem}) => {
+ const newprice=productItem?.price
+
+ if(!newprice){
+ return(
+
+ )
+ }
+ const oldPrice=(parseFloat(newprice) + 30).toFixed(2)
+ return (
+
+ ₹ {oldPrice}
+ ₹ {newprice}
+
+
+ )
+}
+
+export default ProductPrice
\ No newline at end of file
diff --git a/src/components/Products/SingleProduct.js b/src/components/Products/SingleProduct.js
index 5f932c4..db062d7 100644
--- a/src/components/Products/SingleProduct.js
+++ b/src/components/Products/SingleProduct.js
@@ -4,46 +4,67 @@
** Github URL: https://github.com/quintuslabs/fashion-cube
*/
-import React from "react";
-import jumpTo from "../../modules/Navigation";
-
-function SingleProduct(props) {
- const { productItem } = props;
- return (
-
-
- jumpTo(`/fashion-cube/single-product/${productItem._id}`)
- }
- >
-
-

-
-
-
-
- {/*
- -$20
-
*/}
-
-
-
{productItem.title}
-
-
- ₹ {productItem.price}
- ₹ {(parseFloat(productItem.price) + 30).toFixed(2)}
-
-
-
-
props.addToBag(productItem._id)}
- >
-
add to cart
-
-
- );
-}
-
-export default SingleProduct;
+ import React, { useState } from "react";
+ import SkeletonProduct from "./SkeltonProduct";
+ import FavouriteIcon from "./FavouriteIcon";
+ import ProductPrice from "./ProductPrice";
+ import styles from "./SingleProduct.module.css";
+ const SingleProduct = ({ productItem, onNavigate, onAddToCart }) => {
+ const[imageError,setImageError]=useState(false)
+ if (!productItem) {
+ return ;
+ }
+ const HandleNavigate = () => {
+ onNavigate(productItem._id);
+ };
+ const HandleAddToCart = (e) => {
+ e.stopPropagation();
+ onAddToCart(productItem._id);
+ };
+ return (
+
+
+
+ {!imageError ?

setImageError(true)}
+ />:
}
+
+
+
+
+
+ {productItem.discount && (
+
+ -${productItem.discount}
+
+ )}
+
+
+
+
{productItem.title}
+
+
+
+
+
+
+ );
+ };
+
+ export default React.memo(SingleProduct);
+
\ No newline at end of file
diff --git a/src/components/Products/SingleProduct.module.css b/src/components/Products/SingleProduct.module.css
new file mode 100644
index 0000000..25ab66b
--- /dev/null
+++ b/src/components/Products/SingleProduct.module.css
@@ -0,0 +1,209 @@
+.card {
+ height: 380px;
+ cursor: pointer;
+ margin-bottom: 30px;
+ position: relative;
+ border-radius: 5px;
+ overflow: hidden;
+ transition: box-shadow 0.3s ease;
+
+
+}
+
+.card::after {
+ position: absolute;
+ top: 0;
+ left: -1px;
+ width: calc(100% + 1px);
+ height: 100%;
+ pointer-events: none;
+ content: "";
+ border: 2px solid rgba(235, 235, 235, 0);
+ border-radius: 5px;
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
+ transition: all 0.3s ease;
+ z-index: -1;
+}
+
+.card:hover::after {
+ box-shadow: 0 25px 29px rgba(0, 0, 0, 0.2);
+ border-color: #f8e3e3;
+}
+
+.product {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ height: 100%;
+ background-color: #f2f2f261;
+}
+
+.image {
+ width: 100%;
+ height: 240px;
+ display: flex;
+ justify-content: center;
+ overflow: hidden;
+}
+
+.imageTag {
+ max-width: 100%;
+ max-height: 100%;
+ transition: transform 0.5s ease;
+ cursor: zoom-in;
+ object-fit: contain;
+}
+
+.product:hover .imageTag {
+ transform: scale(1.2);
+}
+
+.info {
+ text-align: center;
+ padding: 0 10px;
+ flex-grow: 1;
+ margin-bottom: 12px;
+}
+
+.name {
+ margin-top: 20px;
+ font-size: 14px;
+ font-weight: 600;
+ color: #1e1e27;
+ line-height: 20px;
+}
+
+.name:hover {
+ color: #da8692;
+}
+
+.price {
+ position: relative;
+ height: 22px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-top: 15px;
+
+
+}
+
+.newprice {
+ font-size: 17px;
+ color: #fe4c50;
+ font-weight: 600;
+ animation-name: showNewPrice;
+}
+
+.oldprice {
+ font-size: 14px;
+ margin-left: 10px;
+ color: #b5aec4;
+ text-decoration: line-through;
+ animation-name: showOldPrice;
+}
+.oldprice,
+.newprice {
+ position: absolute;
+ animation-duration: 3s;
+ animation-iteration-count: infinite;
+ animation-timing-function: ease-in-out;
+ opacity: 0;
+}
+
+@keyframes showOldPrice {
+ 0% { opacity: 0; transform: translateY(10px); }
+ 10% { opacity: 1; transform: translateY(0); }
+ 40% { opacity: 1; transform: translateY(0); }
+ 50% { opacity: 0; transform: translateY(-10px); }
+ 100% { opacity: 0; }
+}
+
+
+@keyframes showNewPrice {
+ 0%, 49% { opacity: 0; transform: translateY(10px); }
+ 60% { opacity: 1; transform: translateY(0); }
+ 90% { opacity: 1; }
+ 100% { opacity: 0; transform: translateY(-10px); }
+}
+
+.productBubble {
+ position: absolute;
+ top: 12px;
+ right: 12px;
+ background: #fe4c50;
+ border-radius: 50px;
+ text-transform: uppercase;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
+ padding: 6px 10px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 15px;
+ font-weight: 600;
+ color: #fff;
+ animation: pulse 3s ease-in-out infinite;
+}
+
+.productBubble span {
+ font-size: 12px;
+ font-weight: 600;
+ color: #ffffff;
+}
+
+.addToCart {
+ width: 100%;
+ background-color: #fe4c50;
+ border-color: #fe4c50;
+ color: white;
+ padding: 8px 0;
+ font-size: 13px;
+ font-weight: 500;
+ border-radius: 5px;
+ text-align: center;
+ transition: background-color 0.3s ease;
+ cursor: pointer;
+ margin-top: auto;
+}
+
+.addToCart:hover {
+ background-color: #fe7c7f;
+ border-color: #fe7c7f;
+}
+
+.favorite {
+ position: absolute;
+ top: 20px;
+ left: 15px;
+ color: #b9b4c7;
+ width: 20px;
+ height: 20px;
+ z-index: 2;
+ transition: color 0.3s ease;
+
+}
+
+.favorite:hover {
+ color: #fe4c50;
+ animation: pulse 0.6s ease-in-out infinite;
+}
+
+@keyframes pulse {
+ 0% {
+ opacity: 0.2;
+ }
+
+ 50% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0.2;
+ }
+}
+
+.skeleton {
+ animation: pulse 1.9s ease-in-out infinite;
+ border-radius: 6px;
+
+}
\ No newline at end of file
diff --git a/src/components/Products/SkeltonProduct.js b/src/components/Products/SkeltonProduct.js
new file mode 100644
index 0000000..6a182b0
--- /dev/null
+++ b/src/components/Products/SkeltonProduct.js
@@ -0,0 +1,18 @@
+import React from "react";
+import styles from './SingleProduct.module.css'
+const SkeletonProduct = ({type}) => {
+ const base=styles.skeleton
+ if(type==='image')return
+ if(type==='text') return (
)
+ if(type==='product')
+ {return(
+
)
+
+}};
+export default SkeletonProduct;