Skip to content

Commit

Permalink
[#187862829] ft-create-landing-page
Browse files Browse the repository at this point in the history
  • Loading branch information
SaddockAime committed Jul 6, 2024
1 parent ff1539f commit d11f5dc
Show file tree
Hide file tree
Showing 30 changed files with 729 additions and 31 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
.pnp.js

# testing
/coverage
/dist

# production
/build
Expand Down
19 changes: 19 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
"mini-css-extract-plugin": "^2.9.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-icons": "^5.2.1",
"react-redux": "^9.1.2",
"react-router-dom": "^6.24.0",
"react-spinners": "^0.14.1",
"sass": "^1.77.6",
"save-dev": "0.0.1-security"
},
Expand Down
Binary file added public/assets/left-bottom.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/left-top.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/middle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/right-bottom.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/right-top.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/shoe1.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/shoe2.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/shoe3.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/shoe4.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ const App: React.FC = () => (
</Router>
);

export default App;
export default App;
2 changes: 1 addition & 1 deletion src/components/layout/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ const Footer: React.FC = () => (
</footer>
);

export default Footer;
export default Footer;
2 changes: 1 addition & 1 deletion src/components/layout/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ const Header: React.FC = () => (
</header>
);

export default Header;
export default Header;
63 changes: 63 additions & 0 deletions src/components/product/Product.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/* eslint-disable */
import React, { useState, useRef } from 'react';
import { CiHeart } from "react-icons/ci";
import { PiShoppingCartThin } from "react-icons/pi";
import '../../styles/index.scss';

interface ProductProps {
images: string[];
name: string;
price: string;
stock: number;
description: string;
discount: number;
}

const Product: React.FC<ProductProps> = ({ images, name, price, stock, description, discount }) => {
const [currentImageIndex, setCurrentImageIndex] = useState(0);
const intervalRef = useRef<NodeJS.Timeout | null>(null);

const handleMouseEnter = () => {
intervalRef.current = setInterval(() => {
setCurrentImageIndex((prevIndex) => (prevIndex + 1) % images.length);
}, 2000);
};

const handleMouseLeave = () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
setCurrentImageIndex(0);
};

const truncateDescription = (desc: string, length: number) => {
return desc.length > length ? `${desc.substring(0, length)}...` : desc;
};

return (
<div className="product">
<div
className="product-image-container"
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<span className="discount-badge">{discount}%</span>
<img src={images[currentImageIndex]} alt="Product" className="product-image" />
</div>
<div className="product-info">
<div className="product-add">
<div className="icon-container"><CiHeart className="icon" /></div>
<div className="icon-container"><PiShoppingCartThin className="icon" /></div>
</div>
<div className="product-name">{name}</div>
<div className="product-details">
<p className="product-price">{price}</p>
<p className="product-stock"><span className="stock">Stock:</span>{stock}</p>
</div>
<p className="product-description">{truncateDescription(description, 20)}</p>
</div>
</div>
);
};

export default Product;
134 changes: 134 additions & 0 deletions src/components/sample/Sample.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/* eslint-disable */
import React, { useState, useEffect, useRef } from 'react';
import { TfiHeadphoneAlt } from "react-icons/tfi";
import { VscWorkspaceTrusted } from "react-icons/vsc";
import { LiaShippingFastSolid } from "react-icons/lia";
import { FaHandHoldingUsd } from "react-icons/fa";
import { GoDotFill } from "react-icons/go";
import { FaChevronCircleLeft, FaChevronCircleRight } from "react-icons/fa";
import '../../styles/index.scss';

const images = [
'/assets/middle.png',
'/assets/shoe2.jpeg',
'/assets/shoe3.jpeg'
];

const Sample: React.FC = () => {
const [currentIndex, setCurrentIndex] = useState(0);
const intervalRef = useRef<NodeJS.Timeout | null>(null);

const handleLeftClick = () => {
setCurrentIndex((prevIndex) => (prevIndex === 0 ? images.length - 1 : prevIndex - 1));
};

const handleRightClick = () => {
setCurrentIndex((prevIndex) => (prevIndex === images.length - 1 ? 0 : prevIndex + 1));
};

const startImageChangeInterval = () => {
intervalRef.current = setInterval(() => {
setCurrentIndex((prevIndex) => (prevIndex + 1) % images.length);
}, 5000);
};

useEffect(() => {
startImageChangeInterval();
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
};
}, []);

useEffect(() => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
startImageChangeInterval();
}
}, [currentIndex]);

return (
<div>
<div className="sampleImages1">
<div className="sample1">
<div className="menShoe">
<div className="text-container">
<p>Men's Shoes</p>
<button>View &gt;</button>
</div>
</div>
<div className="phones">
<div className="text-container">
<p>Phones</p>
<button>View &gt;</button>
</div>
</div>
</div>
<div className="sample2" style={{ backgroundImage: `url(${images[currentIndex]})` }}>
<div className="arrow left" onClick={handleLeftClick}>
<FaChevronCircleLeft className="icon-arrow" />
</div>
<div className="arrow right" onClick={handleRightClick}>
<FaChevronCircleRight className="icon-arrow" />
</div>
<div className="dots">
{images.map((_, index) => (
<GoDotFill
key={index}
className="icon-dots"
style={{ color: currentIndex === index ? '#ff6d18' : '#fff' }}
/>
))}
</div>
</div>
<div className="sample3">
<div className="womenShoe">
<div className="text-container">
<p>Women's Shoes</p>
<button>View &gt;</button>
</div>
</div>
<div className="accessories">
<div className="text-container">
<p>Accessories</p>
<button>View &gt;</button>
</div>
</div>
</div>
</div>
<div className="trust-container">
<div className="trust">
<TfiHeadphoneAlt className="icon" />
<div className="name">
<h2>Responsive</h2>
<p>Customer service available 24/7</p>
</div>
</div>
<div className="trust">
<VscWorkspaceTrusted className="icon" />
<div className="name">
<h2>Secure</h2>
<p>Certified marketplace since 2024</p>
</div>
</div>
<div className="trust">
<LiaShippingFastSolid className="icon" />
<div className="name">
<h2>Shipping</h2>
<p>Free, fast, and reliable worldwide</p>
</div>
</div>
<div className="trust">
<FaHandHoldingUsd className="icon" />
<div className="name">
<h2>Transparent</h2>
<p>Free return policy</p>
</div>
</div>
</div>
</div>
);
};

export default Sample;
2 changes: 1 addition & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ root.render(
<Provider store={store}>
<App />
</Provider>
);
);
55 changes: 39 additions & 16 deletions src/pages/LandingPage.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,52 @@
/* eslint-disable */
import React, { useEffect } from 'react';
import { useAppDispatch, useAppSelector } from '../store/store';
import { loadWelcomeMessage } from '../store/features/welcomeSlice';
import { IWelcomeMessage } from '../utils/types/store';
import Header from '../components/layout/Header';
import Footer from '../components/layout/Footer';
import '../styles/LandingPage.scss';
import { useAppDispatch, useAppSelector } from'../store/store';
import { fetchProducts } from '../store/features/product/productSlice';
import Product from '../components/product/Product';
import Sample from '../components/sample/Sample';
import { LiaLongArrowAltRightSolid } from "react-icons/lia";
import { PuffLoader } from 'react-spinners';
import '../styles/index.scss';

const LandingPage: React.FC = () => {
const dispatch = useAppDispatch();
const welcomeMessage: IWelcomeMessage = useAppSelector((state) => state.initialMessage.welcomeMessage);
const { products, isError, isSuccess, isLoading, message } = useAppSelector((state: any) => state.products);

useEffect(() => {
dispatch(loadWelcomeMessage());
dispatch(fetchProducts());
}, [dispatch]);

return (
<>
<Header />
<div className="landingPage">
<h1>
{welcomeMessage.message}
</h1>
<div className="landing-page">
<Sample />
<div className="landing-container">
{
isLoading ? <div className="loader">
<PuffLoader color="#ff6d18" size={300} loading = {isLoading} />
</div> : (
<div>
<div className="head">
<h1>Today's Deal</h1>
<p>View all <LiaLongArrowAltRightSolid className="icon" /></p>
</div>
<div className="product-list">
{isSuccess ?( products.map((product: any) => (
<Product
key={product.id}
images={product.images}
name={product.name}
price={`$${product.price}`}
stock={Number(product.quantity)}
description={product.description}
discount={Number(product.discount.replace('%', ''))}
/>
))): null}
</div>
</div>
)
}
</div>
<Footer />
</>
</div>
);
};

Expand Down
2 changes: 1 addition & 1 deletion src/pages/NotFound.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ const NotFound: React.FC = () => (
</main>
);

export default NotFound;
export default NotFound;
2 changes: 1 addition & 1 deletion src/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ const AppRouter: React.FC = () => {
);
};

export default AppRouter;
export default AppRouter;
16 changes: 16 additions & 0 deletions src/store/features/product/productService.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/* eslint-disable */
import axiosInstance from "../../../utils/axios/axiosInstance";

const fetchProducts = async () => {
try {
const response = await axiosInstance.get(`/api/shop/user-get-products`);
return response.data;
} catch (error) {
throw new Error('Failed to fetch products.');
}
};

const productService = {
fetchProducts,
}
export default productService;
Loading

0 comments on commit d11f5dc

Please sign in to comment.