Compare commits

..

No commits in common. "7c870930cc968a7dd2ad6e86764552123db3ffe3" and "63f16bd0591c9e08e04d2a46f78371d962dd69c7" have entirely different histories.

38 changed files with 0 additions and 7681 deletions

24
.gitignore vendored
View file

@ -1,24 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View file

@ -1,38 +0,0 @@
import js from '@eslint/js'
import globals from 'globals'
import react from 'eslint-plugin-react'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
export default [
{ ignores: ['dist'] },
{
files: ['**/*.{js,jsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
parserOptions: {
ecmaVersion: 'latest',
ecmaFeatures: { jsx: true },
sourceType: 'module',
},
},
settings: { react: { version: '18.3' } },
plugins: {
react,
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...js.configs.recommended.rules,
...react.configs.recommended.rules,
...react.configs['jsx-runtime'].rules,
...reactHooks.configs.recommended.rules,
'react/jsx-no-target-blank': 'off',
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
]

View file

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- <title>e-shop</title> -->
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>

5600
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,36 +0,0 @@
{
"name": "ecommerce",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@reduxjs/toolkit": "^2.4.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-helmet-async": "^2.0.5",
"react-icons": "^5.4.0",
"react-redux": "^9.1.2",
"react-router": "^7.0.2"
},
"devDependencies": {
"@eslint/js": "^9.15.0",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@vitejs/plugin-react-swc": "^3.5.0",
"autoprefixer": "^10.4.20",
"eslint": "^9.15.0",
"eslint-plugin-react": "^7.37.2",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.14",
"globals": "^15.12.0",
"postcss": "^8.4.49",
"tailwindcss": "^3.4.16",
"vite": "^6.0.1"
}
}

View file

@ -1,6 +0,0 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View file

View file

@ -1,86 +0,0 @@
import { createBrowserRouter, RouterProvider } from "react-router";
import "./App.css";
import Layout from "./components/layout/Layout";
import Error from "./components/Error";
import Home from "./pages/Home";
import About from "./pages/About";
import Contact from "./pages/Contact";
import Shop from "./pages/Shop";
import Cart from "./pages/Cart";
import Checkout from "./pages/Checkout";
import { useState } from "react";
import Order from "./pages/Order";
import FilterData from "./components/FilterData";
import ProductDetail from "./pages/ProductDetail";
import { HelmetProvider } from "react-helmet-async";
function App() {
const [order, setOrder] = useState({
products: [],
orderNumber: "",
shippingInformation: { address: "", city: "", zip: "" },
totalPrice: 0,
});
const router = createBrowserRouter([
{
path: "/",
element: <Layout />,
errorElement: <Error />,
children: [
{
path: "/",
element: <Home />,
},
{
path: "/about",
element: <About />,
},
{
path: "/contact",
element: <Contact />,
},
{
path: "/shop",
element: <Shop />,
},
{
path: "/cart",
element: <Cart />,
},
{
path: "/product/:id",
element: <ProductDetail />,
},
{
path: "/checkout",
element: <Checkout setOrder={setOrder} />,
},
{
path: "/order-confirmation",
element: <Order order={order} />,
},
{
path: "/filter-data",
element: <FilterData />,
},
],
},
]);
return (
<>
<HelmetProvider>
<RouterProvider router={router} />
</HelmetProvider>
</>
);
}
export default App;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

View file

@ -1,72 +0,0 @@
export const categories = [
"Electronics",
"Fashion",
"Home & Kitchen",
"Beauty",
"Sports",
"Automotive",
];
export const mockData = [
{
id: 1,
image: "https://m.media-amazon.com/images/I/71IYWw-1KBL._SX569_.jpg",
name: "Men Upper",
price: 29.99,
},
{
id: 2,
image:
"https://m.media-amazon.com/images/I/51A2iKgaiML._SX300_SY300_QL70_FMwebp_.jpg",
name: "Motorcycle Helmet",
price: 39.99,
},
{
id: 3,
image:
"https://m.media-amazon.com/images/I/31Cdcr1VxEL._SX300_SY300_QL70_FMwebp_.jpg",
name: "Fry Pan",
price: 49.99,
},
{
id: 4,
image:
"https://m.media-amazon.com/images/I/514CfZOJ5rL._SX300_SY300_QL70_FMwebp_.jpg",
name: "Smartwatch",
price: 59.99,
},
{
id: 5,
image:
"https://m.media-amazon.com/images/I/31xXPjbn4jL._SX300_SY300_QL70_FMwebp_.jpg",
name: "Bluetooh Earphone",
price: 69.99,
},
{
id: 6,
image:
"https://m.media-amazon.com/images/I/41A7WrRCOpL._SY445_SX342_QL70_FMwebp_.jpg",
name: "Microwave Ovens",
price: 79.99,
},
{
id: 7,
image:
"https://m.media-amazon.com/images/I/41BMuuS76SL._SX300_SY300_QL70_FMwebp_.jpg",
name: "Wall Clock",
price: 89.99,
},
{
id: 8,
image: "https://m.media-amazon.com/images/I/61SFGUb9y8L._AC_UL320_.jpg",
name: "Chess",
price: 99.99,
},
{
id: 9,
image: "https://m.media-amazon.com/images/I/710SxepIfiL._SL1500_.jpg",
name: "dumbbell set",
price: 109.99,
},
];

View file

@ -1,68 +0,0 @@
import React, { useEffect, useState } from "react";
const category = [
{
title: "Men",
imageUrl:
"https://d2ki7eiqd260sq.cloudfront.net/MSLS00005GREENFIGS_2c9ab0616-5513-4e8d-8a78-6e294c31173a.webp",
},
{
title: "Women",
imageUrl:
"https://objectstorage.ap-mumbai-1.oraclecloud.com/n/softlogicbicloud/b/cdn/o/category-images/60ab26835e322.png",
},
{
title: "Kids",
imageUrl:
"https://objectstorage.ap-mumbai-1.oraclecloud.com/n/softlogicbicloud/b/cdn/o/category-images/60b88eb7c6211.png",
},
];
const CategorySection = () => {
const [loading, setLoading] = useState(true);
useEffect(() => {
const timer = setTimeout(() => {
setLoading(false);
}, 1500);
return () => clearTimeout(timer);
}, []);
if (loading) {
return (
<div className="container mx-auto flex justify-center items-center ">
<div className="grid grid-cols-1 sm:grid-cols-3 gap-6 md:space-x-14 animate-pulse ">
<div className="h-60 w-64 rounded-xl shadow bg-gray-300"></div>
<div className="h-60 w-64 rounded-xl shadow bg-gray-300"></div>
<div className="h-60 w-64 rounded-xl shadow bg-gray-300"></div>
</div>
</div>
);
}
return (
<div className=" container mx-auto flex justify-center items-center">
<div className=" grid grid-cols-1 sm:grid-cols-3 gap-6 md:space-x-14 ">
{category.map((elem, index) => (
<div
key={index}
className="relative h-64 transform transition-transform duration-300 hover:scale-105 cursor-pointer"
>
<img
src={elem.imageUrl}
alt=""
className="h-full object-cover rounded-lg shadow-md"
/>
<div className="absolute top-20 left-2">
<p className="text-md font-bold">{elem.title}</p>
<p className="text-gray-600">View All</p>
</div>
</div>
))}
</div>
</div>
);
};
export default CategorySection;

View file

@ -1,37 +0,0 @@
import React, { useState } from "react";
const ChangeAddress = ({ setAddress, setIsModelOpen }) => {
const onClose = () => {
setAddress(newAddress);
setIsModelOpen(false);
};
const [newAddress, setNewAddress] = useState("");
return (
<div>
<input
type="text"
placeholder="enter new address"
className="w-full p-2 border mb-4 outline-none"
onChange={(e) => setNewAddress(e.target.value)}
/>
<div className="flex justify-end">
<button
className="bg-gray-500 hover:bg-gray-700 py-2 px-4 text-white rounded mr-2"
onClick={() => setIsModelOpen(false)}
>
Cancel
</button>
<button
className="bg-blue-500 hover:bg-blue-700 py-2 px-4 rounded text-white"
onClick={onClose}
>
Save Address
</button>
</div>
</div>
);
};
export default ChangeAddress;

View file

@ -1,14 +0,0 @@
import React from "react";
import { Link, useRouteError } from "react-router";
const Error = () => {
const error = useRouteError();
return (
<div>
<h1>Oops! An error occured.</h1>
<p> {error.data}</p>
<Link to="/">Go Home</Link>
</div>
);
};
export default Error;

View file

@ -1,24 +0,0 @@
import React from "react";
import { useSelector } from "react-redux";
import ProductCard from "./ProductCard";
const FilterData = () => {
const filterProducts = useSelector((state) => state.products.filterData);
return (
<div className="mx-auto py-12 px-4 md:px-16 lg:px-24">
{filterProducts.length > 0 ? (
<>
<h2 className="text-2xl font-bold mb-6 text-center">Shop</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 lg:grid-cols-5 gap-6 cursor-pointer">
{filterProducts.map((product) => (
<ProductCard product={product} />
))}
</div>
</>
) : (
<h1 className="text-center">not found</h1>
)}
</div>
);
};
export default FilterData;

View file

@ -1,95 +0,0 @@
import React from "react";
import { BsTwitter } from "react-icons/bs";
import { FaFacebook, FaGithub, FaLinkedin } from "react-icons/fa";
import { Link } from "react-router";
const Footer = () => {
return (
<footer className="bg-gray-800 text-white py-10 px-4 md:px-16 lg:px-24 ">
<div className="container mx-auto grid grid-cols-1 md:grid-cols-3 gap-8">
<div>
<h3 className="text-xl font-semibold">e-shop</h3>
<p className="mt-4">
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Labore ex
animi accusamus consectetur harum. Rem mollitia provident
consequuntur ducimus exercitationem!
</p>
</div>
<div className="flex flex-col md:items-center">
<h4 className="text-lg font-semibold">Quick Links</h4>
<ul className="mt-4 space-y-2">
<li>
<Link to="/" className="hover:underline">
Home
</Link>
</li>
<li>
<Link to="/shop" className="hover:underline">
Shop
</Link>
</li>
<li>
<Link to="/about" className="hover:underline">
About
</Link>
</li>
<li>
<Link to="/contact" className="hover:underline">
Contact
</Link>
</li>
</ul>
</div>
<div>
<h4 className="text-lg font-semibold">Follow us</h4>
<div className="flex space-x-4 mt-4">
<a href="/" className="hover:text-gray-400">
<FaFacebook />
</a>
<a href="/" className="hover:text-gray-400">
<BsTwitter />
</a>
<a href="/" className="hover:text-gray-400">
<FaGithub />
</a>
<a href="/" className="hover:text-gray-400">
<FaLinkedin />
</a>
</div>
<form action="" className="flex items-center justify-center mt-8 ">
<input
type="email"
placeholder="enter email"
className="w-full p-2 rounded-lg bg-gray-800 border border-gray-600 outline-none"
/>
<button className="bg-red-500 px-4 py-2 text-white rounded-md ml-2 hover:bg-red-700">
Subscibe
</button>
</form>
</div>
</div>
<div className="mt-8 border-t border-gray-700 pt-4">
<div className="container mx-auto flex flex-col md:flex-row justify-between items-center">
<p>&copy; {new Date().getFullYear()} e-shop All rights reserved.</p>
<div className="flex space-x-4 mt-4 md:mt-0">
<a href="/" className="hover:underline">
Privacy Policy
</a>
<a href="/" className="hover:underline">
Terms & Conditions
</a>
</div>
</div>
</div>
</footer>
);
};
export default Footer;

View file

@ -1,87 +0,0 @@
import { useEffect, useState } from "react";
import { FaHeadset, FaLock, FaShoppingCart, FaTag } from "react-icons/fa";
import { FaMoneyBill1Wave } from "react-icons/fa6";
const InfoSection = () => {
const [loading, setLoading] = useState(true);
useEffect(() => {
const timer = setTimeout(() => {
setLoading(false);
}, 1500);
return () => {
clearTimeout(timer);
};
});
const infoItems = [
{
icon: <FaShoppingCart className="text-3xl text-red-600" />,
title: "Free Shipping",
description: "Get your orders delivered with no extra cost",
},
{
icon: <FaHeadset className="text-3xl text-red-600" />,
title: "Support 24/7",
description: "We are here to assist you anytime",
},
{
icon: <FaMoneyBill1Wave className="text-3xl text-red-600" />,
title: "100% Money Back",
description: "Full refund if you are not satisfied ",
},
{
icon: <FaLock className="text-3xl text-red-600" />,
title: "Payment Secure",
description: "your payment information is safe with us",
},
{
icon: <FaTag className="text-3xl text-red-600" />,
title: "Discount",
description: "Enjoy the best prices on your products",
},
];
if (loading) {
return (
<div className="bg-white shadow rounded mb-8 mt-12 ">
<div className="container mx-auto grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-6">
{infoItems.map((_, index) => (
<div
key={index}
className="flex flex-col items-center text-center p-4 border rounded-lg shadow-md animate-pulse h-48
"
>
<div className="bg-gray-300 w-12 h-12 mb-4 rounded-full"></div>
<h3 className="w-3/4 bg-gray-300 h-5 rounded mb-4"></h3>
<p className="bg-gray-300 rounded h-3 w-full mb-2"></p>
<p className="bg-gray-300 rounded h-3 w-full "></p>
</div>
))}
</div>
</div>
);
}
return (
<div className="bg-white mb-8 mt-12">
<div className="container mx-auto grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-6">
{infoItems.map((item, index) => (
<div
key={index}
className="flex flex-col items-center text-center p-4 border rounded-lg shadow-md
transform transition-transform duration-300 hover:scale-105 cursor-pointer "
>
{item.icon}
<h3 className="mt-4 text-lg font-serif font-semibold">
{item.title}
</h3>
<p className="mt-2 text-gray-600">{item.description}</p>
</div>
))}
</div>
</div>
);
};
export default InfoSection;

View file

@ -1,21 +0,0 @@
import React from "react";
const Modal = ({ isModelOpen, setIsModelOpen, children }) => {
if (!isModelOpen) return null;
return (
<div className="fixed inset-0 bg-gray-800 bg-opacity-75 flex justify-center items-center z-50">
<div className="bg-white shadow-lg rounded-lg p-6 w-full max-w-md">
<button
className="absolute top-4 right-4 text-gray-300 text-3xl"
onClick={() => setIsModelOpen(false)}
>
&times;
</button>
{children}
</div>
</div>
);
};
export default Modal;

View file

@ -1,107 +0,0 @@
import { useState } from "react";
import { FaSearch, FaShoppingCart, FaUser } from "react-icons/fa";
import { useDispatch, useSelector } from "react-redux";
import { Link, useNavigate } from "react-router";
import Login from "../pages/Login";
import Register from "../pages/Register";
import Modal from "./Model";
import { setSearchTerm } from "../redux/productSlice";
const Navbar = () => {
const products = useSelector((state) => state.cart.products);
const [isModelOpen, setIsModelOpen] = useState(false);
const [isLogin, setIsLogin] = useState(true);
const [search, setSearch] = useState();
const disptach = useDispatch();
const navigate = useNavigate("/");
const openSignUp = () => {
setIsLogin(false);
setIsModelOpen(true);
};
const openLogin = () => {
setIsLogin(true);
setIsModelOpen(true);
};
const handleSearch = (e) => {
e.preventDefault();
disptach(setSearchTerm(search));
navigate("/filter-data");
};
return (
<nav className="w-full bg-white shadow-md">
<div className="container mx-auto px-4 md:px-16 lg:px-24 py-4 flex justify-between items-center">
<div className="text-lg font-bold">
<Link to="/">
<span className="font-serif font-bold">E-Shop</span>
</Link>
</div>
<div className="relative flex-1 mx-4 ">
<form onSubmit={handleSearch}>
<input
type="text"
placeholder="search product"
onChange={(e) => setSearch(e.target.value)}
className="w-full border py-2 px-4 outline-none rounded-full"
/>
<FaSearch className="top-3 absolute right-3 text-red-500" />
</form>
</div>
<div className="flex item-center space-x-4">
<Link to="/cart" className="relative">
<FaShoppingCart className="text-xl" />
{products.length > 0 && (
<span className="absolute top-0 text-xs w-3 left-3 bg-red-600 rounded-full flex justify-center items-center text-white">
{products.length}
</span>
)}
</Link>
<div className="flex items-center gap-2">
<button
className="hidden md:block hover:underline"
onClick={openLogin}
>
Login
</button>
<span> | </span>
<button
className="hidden md:block hover:underline"
onClick={openSignUp}
>
Register
</button>
</div>
<button className="block md:hidden ">
<FaUser />
</button>
</div>
</div>
<div className="flex items-center justify-center space-x-10 py-4 text-sm font-bold">
<Link to="/" className="hover:underline">
Home
</Link>
<Link to="/shop" className="hover:underline">
Shop
</Link>
<Link to="/contact" className="hover:underline">
Contact
</Link>
<Link to="/about" className="hover:underline">
About
</Link>
</div>
<Modal setIsModelOpen={setIsModelOpen} isModelOpen={isModelOpen}>
{isLogin ? (
<Login openSignUp={openSignUp} />
) : (
<Register openLogin={openLogin} />
)}
</Modal>
</nav>
);
};
export default Navbar;

View file

@ -1,77 +0,0 @@
import React, { useState, useEffect } from "react";
import { FaStar } from "react-icons/fa";
import { addToCart } from "../redux/cartSlice";
import { useDispatch } from "react-redux";
import { Link } from "react-router";
const ProductCard = ({ product }) => {
const dispatch = useDispatch();
const [isLoading, setIsLoading] = useState(true);
const handleAddToCart = (e, product) => {
e.stopPropagation();
e.preventDefault();
dispatch(addToCart(product));
alert("Product Added Successfully!");
};
useEffect(() => {
const timer = setTimeout(() => {
setIsLoading(false);
}, 1500);
return () => clearTimeout(timer);
}, []);
if (isLoading) {
return (
<div className="bg-white p-4 shadow rounded relative border transform transition-transform cursor-pointer duration-300 hover:scale-105 animate-pulse">
<div className="w-full h-48 bg-gray-300 mb-4 rounded-lg"></div>
<div className="h-4 bg-gray-300 mb-2 rounded w-3/4"></div>
<div className="h-4 bg-gray-300 mb-2 rounded w-1/2"></div>
<div className="flex items-center mt-2">
{[...Array(5)].map((_, index) => (
<FaStar key={index} className="text-gray-300" />
))}
</div>
<div className="absolute bottom-4 right-2 flex items-center justify-center w-8 h-8 bg-gray-300 text-white text-sm rounded-full"></div>
</div>
);
}
return (
<Link to={`/product/${product.id}`}>
<div className="bg-white p-4 shadow rounded relative border transform transition-transform cursor-pointer duration-300 hover:scale-105">
<img
src={product.image}
alt={product.name}
className="w-full h-48 object-contain mb-4"
/>
<h3 className="text-lg font-semibold">{product.name}</h3>
<p className="text-gray-500">${product.price}</p>
<div className="flex items-center mt-2">
<FaStar className="text-yellow-500" />
<FaStar className="text-yellow-500" />
<FaStar className="text-yellow-500" />
<FaStar className="text-yellow-500" />
<FaStar className="text-yellow-500" />
</div>
<div
className="absolute bottom-4 right-2 flex items-center justify-center w-8 h-8 bg-red-600
group text-white text-sm rounded-full hover:w-32 hover:bg-red-700 duration-300 transition-all"
onClick={(e) => handleAddToCart(e, product)}
>
<span className="group-hover:hidden"> + </span>
<span className="hidden group-hover:block"> Add to cart </span>
</div>
</div>
</Link>
);
};
export default ProductCard;

View file

@ -1,15 +0,0 @@
import React from "react";
import Navbar from "../Navbar";
import Footer from "../Footer";
import { Outlet } from "react-router";
const Layout = () => {
return (
<>
<Navbar />
<Outlet />
<Footer />
</>
);
};
export default Layout;

View file

@ -1,3 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View file

@ -1,13 +0,0 @@
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App.jsx";
import { Provider } from "react-redux";
import { store } from "./redux/store.jsx";
import { HelmetProvider } from "react-helmet-async";
createRoot(document.getElementById("root")).render(
<HelmetProvider>
<Provider store={store}>
<App />
</Provider>
</HelmetProvider>
);

View file

@ -1,122 +0,0 @@
import React from "react";
import { FaCarSide } from "react-icons/fa";
import { IoIosPricetags } from "react-icons/io";
import { FaCartShopping } from "react-icons/fa6";
import { Helmet } from "react-helmet-async";
const About = () => {
return (
<div className="min-h-screen bg-gray-50 py-12 px-4 md:px-16 lg:px-24">
<Helmet>
<title>About Us - Esop</title>
<meta name="description" content="" />
</Helmet>
{/* Hero Section */}
<div className="relative bg-white rounded-lg shadow-lg overflow-hidden">
<div className=" bg-black bg-opacity-50 z-10"></div>
<div className="flex justify-center items-center">
<img
src="https://pujabhandar-1.web.app/assets/1-S31Ps3IF.jpg"
alt="About Us"
className="w-90 h-72 md:h-66 text-center object-cover"
/>
</div>
</div>
{/* Mission Section */}
<div className="mt-16 max-w-5xl mx-auto text-center">
<h2 className="text-2xl md:text-3xl font-bold mb-6 text-gray-800">
Our Mission
</h2>
<p className="text-gray-600 leading-relaxed text-lg">
At <span className="text-red-600 font-semibold">E-Shop</span>, our
mission is to provide an exceptional online shopping experience by
offering a wide range of high-quality products, seamless customer
service, and a commitment to your satisfaction.
</p>
</div>
{/* Company Highlights */}
<div className="mt-16 max-w-6xl mx-auto grid grid-cols-1 md:grid-cols-3 gap-8">
<div className="text-center">
<div className="bg-red-100 text-red-600 w-16 h-16 mx-auto flex items-center justify-center rounded-full mb-4">
<FaCartShopping className="w-8 h-8" />
</div>
<h3 className="text-xl font-semibold text-gray-800">
Quality Products
</h3>
<p className="text-gray-600 mt-2">
We offer a curated selection of top-quality products for all your
needs.
</p>
</div>
<div className="text-center">
<div className="bg-red-100 text-red-600 w-16 h-16 mx-auto flex items-center justify-center rounded-full mb-4">
<FaCarSide className="w-8 h-8" />
</div>
<h3 className="text-xl font-semibold text-gray-800">Fast Delivery</h3>
<p className="text-gray-600 mt-2">
We ensure quick and reliable shipping to your doorstep.
</p>
</div>
<div className="text-center">
<div className="bg-red-100 text-red-600 w-16 h-16 mx-auto flex items-center justify-center rounded-full mb-4">
<IoIosPricetags className="w-8 h-8" />
</div>
<h3 className="text-xl font-semibold text-gray-800">
Affordable Prices
</h3>
<p className="text-gray-600 mt-2">
Get the best value for your money with our competitive pricing.
</p>
</div>
</div>
{/* Meet the Team */}
<div className="mt-16 max-w-6xl mx-auto">
<h2 className="text-2xl md:text-3xl font-bold mb-8 text-gray-800 text-center">
Meet the Team
</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-8">
{[
{
name: "John Doe",
role: "Founder & CEO",
img: "https://static.vecteezy.com/system/resources/previews/024/183/538/non_2x/male-avatar-portrait-of-a-business-man-in-a-suit-illustration-of-male-character-in-modern-color-style-vector.jpg",
},
{
name: "Jane Smith",
role: "Chief Marketing Officer",
img: "https://static.vecteezy.com/system/resources/previews/024/183/538/non_2x/male-avatar-portrait-of-a-business-man-in-a-suit-illustration-of-male-character-in-modern-color-style-vector.jpg",
},
{
name: "Emily Johnson",
role: "Product Manager",
img: "https://static.vecteezy.com/system/resources/previews/024/183/538/non_2x/male-avatar-portrait-of-a-business-man-in-a-suit-illustration-of-male-character-in-modern-color-style-vector.jpg",
},
{
name: "Michael Brown",
role: "Lead Developer",
img: "https://static.vecteezy.com/system/resources/previews/024/183/538/non_2x/male-avatar-portrait-of-a-business-man-in-a-suit-illustration-of-male-character-in-modern-color-style-vector.jpg",
},
].map((member, index) => (
<div key={index} className="text-center">
<img
src={member.img}
alt={member.name}
className="w-32 h-32 mx-auto rounded-full mb-4"
/>
<h3 className="text-lg font-semibold text-gray-800">
{member.name}
</h3>
<p className="text-gray-600">{member.role}</p>
</div>
))}
</div>
</div>
</div>
);
};
export default About;

View file

@ -1,160 +0,0 @@
import React, { useState } from "react";
import { FaTrashAlt } from "react-icons/fa";
import { useDispatch, useSelector } from "react-redux";
import Modal from "../components/Model";
import ChangeAddress from "../components/ChangeAddress";
import {
decreaseQuantity,
increaseQuantity,
removeFromCart,
} from "../redux/cartSlice";
import { useNavigate } from "react-router";
const Cart = () => {
const cart = useSelector((state) => state.cart);
const [address, setAddress] = useState("main street, 123");
const [isModelOpen, setIsModelOpen] = useState(false);
const dispatch = useDispatch();
const navigate = useNavigate();
return (
<div className="container mx-auto py-8 min-h-96 px-4 md:px-16 lg:px-24">
{cart.products.length > 0 ? (
<div>
<h3 className="text-2xl font-semibold mb-4">SHOPPING CART</h3>
<div className="flex flex-col md:flex-row justify-between space-x-10 mt-8">
<div className="md:w-2/3">
<div className="hidden md:flex justify-between border-b items-center mb-4 text-xs font-bold">
<p>PRODUCTS</p>
<div className="flex space-x-8">
<p>PRICE</p>
<p>QUANTITY</p>
<p>SUBTOTAL</p>
<p>REMOVE</p>
</div>
</div>
<div>
{cart.products.map((product) => (
<div
key={product.id}
className="flex flex-col md:flex-row justify-between items-start md:items-center p-3 border-b space-y-4 md:space-y-0"
>
{/* Product Details */}
<div className="flex items-center space-x-4">
<img
src={product.image}
alt={product.name}
className="w-16 h-16 object-contain rounded"
/>
<div>
<h3 className="text-md font-semibold">
{product.name}
</h3>
</div>
</div>
{/* Responsive Details */}
<div className="flex flex-col md:flex-row md:space-x-12 items-start md:items-center w-full md:w-auto space-y-4 md:space-y-0">
{/* Price */}
<div className="md:hidden font-bold">Price:</div>
<p>${product.price}</p>
{/* Quantity */}
<div className="flex items-center">
<div className="md:hidden font-bold mr-2">
Quantity:
</div>
<div className="flex items-center border">
<button
className="text-xl font-bold px-1.5 border-r"
onClick={() =>
dispatch(decreaseQuantity(product.id))
}
>
-
</button>
<p className="text-sm px-2">{product.quantity}</p>
<button
className="text-xl px-1 border-1"
onClick={() =>
dispatch(increaseQuantity(product.id))
}
>
+
</button>
</div>
</div>
{/* Subtotal */}
<div>
<div className="md:hidden font-bold">Subtotal:</div>
<p>${(product.quantity * product.price).toFixed(2)}</p>
</div>
{/* Remove */}
<div>
<div className="md:hidden font-bold">Remove:</div>
<button
className="text-red-500 hover:text-red-700"
onClick={() => dispatch(removeFromCart(product.id))}
>
<FaTrashAlt />
</button>
</div>
</div>
</div>
))}
</div>
</div>
<div className="md:w-1/3 bg-white p-6 rounded-lg shadow-md border ">
<h3 className="text-sm font-semibold mb-5">CART TOTAL</h3>
<div className="flex justify-between mb-5 border-b pb-1">
<span className="text-sm">Total Items: </span>
<span>{cart.totalQuantity} </span>
</div>
<div className="mb-4 border-b pb-2">
{/* <p>Shipping:</p> */}
<p>Shipping to:</p>
<span className="text-xs font-bold">{address}</span>
<button
className="text-blue-500 hover:underline mt-1 ml-2"
onClick={() => setIsModelOpen(true)}
>
Change Address
</button>
</div>
<div className="flex justify-between mb-4">
<span>Total Price: </span>
<span>${cart.totalPrice.toFixed(2)}</span>
</div>
<button
className="w-full bg-red-600 text-white py-2 hover:bg-red-800"
onClick={() => navigate("/checkout")}
>
Proceed to Checkout
</button>
</div>
</div>
<Modal isModelOpen={isModelOpen} setIsModelOpen={setIsModelOpen}>
<ChangeAddress
setAddress={setAddress}
setIsModelOpen={setIsModelOpen}
/>
</Modal>
</div>
) : (
<div className="flex justify-center">
<img
src="https://static.vecteezy.com/system/resources/previews/016/462/240/non_2x/empty-shopping-cart-illustration-concept-on-white-background-vector.jpg"
alt=""
className="h-96 my-5"
/>
</div>
)}
</div>
);
};
export default Cart;

View file

@ -1,304 +0,0 @@
import React, { useState } from "react";
import { FaAngleUp, FaAngleDown } from "react-icons/fa";
import { useSelector } from "react-redux";
import { useNavigate } from "react-router";
const Checkout = ({ setOrder }) => {
const [billingToggle, setBillingToggle] = useState(true);
const [shippingToggle, setShippingToggle] = useState(false);
const [paymentToggle, setPaymentToggle] = useState(false);
const [paymentMethod, setPaymentMethod] = useState("cod");
const [shippingInfo, setShippingInfo] = useState({
address: " ",
city: "",
zip: "",
});
const cart = useSelector((state) => state.cart);
const navigate = useNavigate();
const handleOrder = () => {
const newOrder = {
products: cart.products,
orderNumber: "12344",
shippingInformation: shippingInfo,
totalPrice: cart.totalPrice,
};
setOrder(newOrder);
navigate("/order-confirmation");
console.log("button clicked");
};
return (
<div>
<div className="container mx-auto py-8 min-h-96 px-4 md:px-16 lg:px-24">
<h3 className="text-2xl font-semibold mb-4">CHECKOUT</h3>
<div className="flex flex-col md:flex-row justify-between space-x-10 mt-8">
<div className="border md:w-2/3 p-5 ">
{/* Billing Information */}
<div>
<div
className="flex items-center justify-between"
onClick={() => setBillingToggle(!billingToggle)}
>
<h3 className="text-lg font-semibold mb-2">
Billing Information
</h3>
{billingToggle ? <FaAngleDown /> : <FaAngleUp />}
</div>
<div className={`${billingToggle ? "" : "hidden"}`}>
<div>
<div>
<label className="block text-gray-700">Name</label>
<input
type="text"
placeholder="enter name"
className="w-full px-3 py-2 border mb-4 rounded outline-none"
/>
</div>
</div>
<div>
<div>
<label className="block text-gray-700" htmlFor="">
Email
</label>
<input
type="email"
placeholder="enter email"
className="w-full px-3 py-2 border mb-4 rounded outline-none"
/>
</div>
</div>
<div>
<div>
<label htmlFor="" className="block text-gray-700">
Phone
</label>
<input
type="text"
placeholder="enter phone"
className="w-full px-3 py-2 border mb-4 rounded outline-none"
/>
</div>
</div>
</div>
</div>
{/* shipping Information */}
<div className="mt-5">
<div
className="flex items-center justify-between"
onClick={() => setShippingToggle(!shippingToggle)}
>
<h3 className="text-lg font-semibold mb-2">
Shipping Information
</h3>
{shippingToggle ? <FaAngleDown /> : <FaAngleUp />}
</div>
<div className={`${shippingToggle ? "" : "hidden"}`}>
<div>
<div>
<label className="block text-gray-700">Address</label>
<input
type="text"
placeholder="enter address"
className="w-full px-3 py-2 border mb-4 rounded outline-none"
onChange={(e) =>
setShippingInfo({
...shippingInfo,
address: e.target.value,
})
}
/>
</div>
</div>
<div>
<div>
<label className="block text-gray-700" htmlFor="">
City
</label>
<input
type="text"
placeholder="enter city name"
className="w-full px-3 py-2 border mb-4 rounded outline-none"
onChange={(e) =>
setShippingInfo({
...shippingInfo,
city: e.target.value,
})
}
/>
</div>
</div>
<div>
<div>
<label htmlFor="" className="block text-gray-700">
Zipcode
</label>
<input
type="text"
placeholder="enter zip code"
className="w-full px-3 py-2 border mb-4 rounded outline-none"
onChange={(e) =>
setShippingInfo({
...shippingInfo,
zip: e.target.value,
})
}
/>
</div>
</div>
</div>
</div>
{/* payment Information */}
<div className="mt-5">
<div
className="flex items-center justify-between"
onClick={() => setPaymentToggle(!paymentToggle)}
>
<h3 className="text-lg font-semibold mb-2">Payment Method</h3>
{paymentToggle ? <FaAngleDown /> : <FaAngleUp />}
</div>
<div className={`${paymentToggle ? "" : "hidden"}`}>
<div className="flex items-center mb-2">
<input
type="radio"
name="payment"
checked={paymentMethod === "cod"}
onChange={() => setPaymentMethod("cod")}
/>
<label className="block text-gray-700 ml-2">
Cash on Delivery
</label>
</div>
<div className="flex items-center mb-2">
<input
type="radio"
name="payment"
checked={paymentMethod === "dc"}
onChange={() => setPaymentMethod("dc")}
/>
<label className="block text-gray-700 ml-2">Debit Card</label>
</div>
{paymentMethod === "dc" && (
<div className="bg-gray-100 p-4 rounded-lg mb-4">
<h3 className="text-xl font-semibold mb-4">
Debit Card Information
</h3>
<div className="mb-4">
<label
htmlFor=""
className="block text-gray-700 font-semibold mb-2"
>
Card Number
</label>
<input
type="text"
placeholder="enter card number"
className="border p-2 w-full rounded outline-none"
required
/>
</div>
<div className="mb-4">
<label className="block text-gray-700 font-semibold mb-2">
Card Holder Name
</label>
<input
type="text"
placeholder="enter card holder name"
className="border p-2 w-full rounded outline-none"
required
/>
</div>
<div className="flex justify-between mb-4">
<div className="w-1/2 mr-2">
<label
htmlFor=""
className="block text-gray-700 font-semibold mb-2"
>
Expire Date
</label>
<input
type="text"
placeholder="MM/YY"
className="p-2 border w-full rounded outline-none"
/>
</div>
<div className="w-1/2 ml-2">
<label
htmlFor=""
className="block text-gray-700 font-semibold mb-2"
>
CVV
</label>
<input
type="text"
placeholder="CVV"
className="border p-2 w-full rounded outline-none"
required
/>
</div>
</div>
</div>
)}
</div>
</div>
</div>
<div className="md:w-1/3 bg-white p-6 rounded-lg shadow-md border mt-8 md:mt-0">
<h3 className="text-lg font-semibold mb-4">Order Summary</h3>
<div className="space-y-4">
{cart.products.map((product) => (
<div key={product.id} className="flex justify-between">
<div className="flex items-center">
<img
src={product.image}
alt={product.name}
className="w-16 h-16 object-contain rounded"
/>
<div className="ml-4">
<h4 className="text-md font-semibold">{product.name}</h4>
<p className="text-gray-600">
${product.price} X {product.quantity}
</p>
</div>
</div>
<div className="text-gray-800">
${product.price}
{/* * {product.quantity} */}
</div>
</div>
))}
</div>
<div className="mt-4 border-t pt-4">
<div className="flex justify-between">
<span>Total Price: </span>
<span className="font-semibold">
${cart.totalPrice.toFixed(2)}
</span>
</div>
</div>
<button
className="w-full bg-red-600 text-white py-2 mt-6 hover:bg-red-800"
onClick={handleOrder}
>
Place Order
</button>
</div>
</div>
</div>
</div>
);
};
export default Checkout;

View file

@ -1,119 +0,0 @@
import React from "react";
import { IoLocationSharp } from "react-icons/io5";
import { IoMdMail } from "react-icons/io";
import { FaPhone } from "react-icons/fa6";
import { Helmet } from "react-helmet-async";
const Contact = () => {
return (
<div className="min-h-screen bg-gray-100 py-12 px-4 md:px-16 lg:px-24">
<Helmet>
<title>Contact Us - E-Shop</title>
<meta name="description" content="" />
</Helmet>
<div className="max-w-5xl mx-auto bg-white shadow-lg rounded-lg overflow-hidden">
<div className="flex flex-col md:flex-row">
<div className="bg-red-600 text-white p-8 md:w-1/2 flex flex-col justify-center">
<h2 className="text-3xl font-bold mb-4">Contact Us</h2>
<p className="mb-6">
Have any questions? We'd love to hear from you. Feel free to reach
out to us!
</p>
<div className="space-y-4">
<div className="flex items-center">
<IoLocationSharp className="text-xl mr-2" />
<p>123 Street Name, City, State, ZIP</p>
</div>
<div className="flex items-center">
<IoMdMail className="text-xl mr-2" />
<p>contact@example.com</p>
</div>
<div className="flex items-center">
<FaPhone className="text-xl mr-2" />
<p>+1 (555) 123-4567</p>
</div>
</div>
</div>
<div className="p-8 md:w-1/2">
<h2 className="text-3xl uppercase font-bold mb-6 text-gray-800">
Get in Touch
</h2>
<form action="#" method="POST" className="space-y-6">
<div>
<label
htmlFor="name"
className="block text-sm font-medium text-gray-700"
>
Name
</label>
<input
type="text"
name="name"
id="name"
className="mt-1 block w-full px-4 py-2 outline-none border border-gray-300 rounded-md shadow-sm"
placeholder="your name"
required
/>
</div>
<div>
<label
htmlFor="email"
className="block text-sm font-medium text-gray-700"
>
Email
</label>
<input
type="email"
name="email"
id="email"
className="mt-1 block w-full px-4 py-2 outline-none border border-gray-300 rounded-md shadow-sm"
placeholder="enter your email"
required
/>
</div>
<div>
<label
htmlFor="message"
className="block text-sm font-medium text-gray-700"
>
Message
</label>
<textarea
name="message"
id="message"
rows="4"
className="mt-1 block w-full px-4 py-2 outline-none border border-gray-300 rounded-md shadow-sm"
placeholder="enter your message"
required
></textarea>
<button
type="submit"
className="w-full py-2 px-4 bg-red-600 text-white font-bold
rounded-md hover:bg-red-700 mt-3"
>
Send Message
</button>
</div>
</form>
</div>
</div>
</div>
</div>
);
};
export default Contact;

View file

@ -1,105 +0,0 @@
import React, { useEffect, useState } from "react";
import Hero from "../assets/images/hero-banner.jpg";
import InfoSection from "../components/InfoSection";
import CategorySection from "../components/CategorySection";
import { setProducts } from "../redux/productSlice";
import { useDispatch, useSelector } from "react-redux";
import { categories, mockData } from "../assets/mockData";
import ProductCard from "../components/ProductCard";
import { Link } from "react-router";
import Shop from "./Shop";
import { Helmet } from "react-helmet-async";
const Home = () => {
const [loading, setLoading] = useState(true);
const dispatch = useDispatch();
const products = useSelector((state) => state.products);
useEffect(() => {
dispatch(setProducts(mockData));
}, []);
useEffect(() => {
const timer = setTimeout(() => {
setLoading(false);
}, 1500);
return () => clearTimeout(timer);
}, []);
return (
<>
<Helmet>
<title>Home - E-Shop</title>
<meta name="description" content="" />
</Helmet>
<div className="bg-white mt-2 px-4 md:px-16 lg:px-24">
<div className="container mx-auto py-4 flex flex-col md:flex-row md:space-x-4">
{/* Categories Section */}
<div className="w-full md:w-3/12">
<div className="bg-red-600 text-white text-sm font-bold px-2 py-2.5">
Shop By Categories
</div>
<ul className="space-y-4 bg-gray-100 p-3 border">
{categories.map((category, index) => (
<li
key={index}
className="flex items-center text-sm font-medium"
>
<div className="w-2 h-2 border border-red-500 rounded-full mr-2"></div>
<div>{category}</div>
</li>
))}
</ul>
</div>
{/* Hero Section */}
<div className="w-full md:w-9/12 relative">
<div className="w-full">
<img
src={Hero}
alt="Hero Banner"
className="w-full h-96 object-cover rounded-lg"
/>
</div>
<div className="absolute inset-0 flex flex-col justify-center items-center text-center bg-black bg-opacity-50 text-white">
<h2 className="text-2xl md:text-4xl font-bold">
WELCOME TO E-SHOP
</h2>
<p className="text-base md:text-lg">MILLIONS + PRODUCTS</p>
<Link to="/shop">
<button
className="bg-red-600 px-8 py-1.5 text-white mt-4 hover:bg-red-700 transform
transition-transform duration-300 hover:scale-105"
>
SHOP NOW
</button>
</Link>
</div>
</div>
</div>
<InfoSection />
<CategorySection />
<div className="container mx-auto py-12">
{loading ? (
<div className="h-8 w-32 mx-auto bg-gray-300 rounded-xl mb-6 animate-pulse"></div>
) : (
<h2 className="text-2xl font-bold mb-6 text-center">
Top Products
</h2>
)}
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 lg:grid-cols-5 gap-6 cursor-pointer">
{products?.products?.slice(0, 5).map((product) => (
<ProductCard product={product} />
))}
</div>
</div>
</div>
<Shop />
</>
);
};
export default Home;

View file

@ -1,57 +0,0 @@
import React from "react";
const Login = ({ openSignUp }) => {
return (
<div className="max-w-md mx-auto p-6 bg-white rounded-lg">
<h2 className="text-2xl font-bold mb-4">Login</h2>
<form action="">
<div className="mb-4">
<label htmlFor="block text-gray-700 mb-1">Email</label>
<input
type="email"
className="w-full px-4 py-2 border border-gray-300 outline-none rounded-lg"
placeholder="enter an email"
/>
</div>
<div className="mb-4">
<label className="block text-gray-700 mb-1">Password</label>
<input
type="password"
className="w-full px-4 py-2 border border-gray-300 rounded-lg outline-none"
placeholder="enter password"
/>
</div>
<div className="mb-4 flex items-center justify-between">
<label htmlFor="inline-flex items-center">
<input type="checkbox" className="form-checkbox" />
<span className="ml-2 text-gray-700 ">Remember Me</span>
</label>
<a href="/" className="text-gray-700">
Forgot Password?
</a>
</div>
<div className="mb-4 ">
<button
type="submit"
className="w-full bg-red-600 text-white py-2 rounded-full hover:bg-red-700"
>
Login
</button>
</div>
</form>
<div className="text-center">
<span className="text-gray-700">Don't have an account ? </span>
<button
className="text-red-600 hover:text-red-700 hover:underline"
onClick={openSignUp}
>
Sign Up
</button>
</div>
</div>
);
};
export default Login;

View file

@ -1,57 +0,0 @@
import React from "react";
import { useNavigate } from "react-router";
const Order = ({ order }) => {
const navigate = useNavigate("/");
return (
<div className="container mx-auto py-8 px-4 md:px-16 lg:px-24">
<h2 className="text-2xl font-semibold mb-4">Thank you for your order</h2>
<p>
Your order has been placed successfully you will recieve email shortly.
</p>
<div className="mt-6 p-4 border rounded-lg bg-gray-100">
<h3 className="text-lg font-semibold mb-2">Order Summary</h3>
<p>Order Number : {order.orderNumber}</p>
<div className="mt-4">
<h4 className="text-md font-semibold mb-2">Shipping Information</h4>
<p>{order.shippingInformation.address}</p>
<p>{order.shippingInformation.city}</p>
<p>{order.shippingInformation.zip}</p>
</div>
<div className="mt-4">
<h3 className="text-md font-semibold mb-2">Items Ordered</h3>
{order.products.map((product) => (
<div key={product.id} className="flex justify-between mt-2">
<p>
{product.name} (X{product.quantity})
</p>
<p>{product.price * product.quantity}</p>
</div>
))}
</div>
<div className="mt-4 flex justify-between">
<span>Total Price:</span>
<span className="font-semibold">{order.totalPrice}</span>
</div>
<div className="mt-6">
<button className="bg-green-500 text-white py-2 px-4 hover:bg-green-600">
Order Tracking
</button>
<button
className="ml-4 bg-red-600 text-white py-2 px-4 hover:bg-red-800
"
onClick={() => navigate("/")}
>
Continue Shopping
</button>
</div>
</div>
</div>
);
};
export default Order;

View file

@ -1,94 +0,0 @@
import React, { useEffect, useState } from "react";
import { FaCarSide } from "react-icons/fa";
import { useSelector } from "react-redux";
import { useParams } from "react-router";
import { FaQuestion } from "react-icons/fa";
import { Helmet } from "react-helmet-async";
const ProductDetail = () => {
const { id } = useParams();
const products = useSelector((state) => state.products.products);
const [product, setProduct] = useState();
const [quantity, setQuantity] = useState(1);
useEffect(() => {
const newProduct = products.find((product) => product.id === parseInt(id));
setProduct(newProduct);
}, [id, products]);
if (!product) {
return <div>Loading...</div>;
}
return (
<div className="container mx-auto py-8 px-4 md:px-16 lg:px-24">
<Helmet>
<title>{product.name} - Product Description</title>
<meta
name="description"
content={`Details about ${product.name}`}
></meta>
</Helmet>
<div className="flex flex-col md:flex-row gap-x-16">
{/* product image */}
<div className="md:w-1/2 py-4 shadow-md md:px-8 h-96 flex justify-center">
<img src={product.image} alt={product.name} className="h-full" />
</div>
{/* product information */}
<div className="md:w-1/2 p-4 shadow-md md:p-16 flex flex-col items-center gap-y-2">
<h2 className="text-3xl font-semibold mb-2"> {product.name} </h2>
<p className="text-xl font-semibold text-gray-800 mb-4">
${product.price}
</p>
<div className="flex flex-wrap items-center justify-center mb-4 gap-4">
<button
className="bg-red-600 hover:bg-red-700 text-xl font-bold text-white w-10 h-10
rounded-full"
onClick={() => setQuantity(quantity > 1 ? quantity - 1 : 1)}
>
-
</button>
<input
type="text"
id="quantity"
value={quantity}
className="border border-gray-300 text-center text-lg font-semibold rounded-md p-2 w-14"
/>
<button
className="bg-red-600 hover:bg-red-700 text-xl font-bold text-white w-10 h-10
rounded-full"
onClick={() => setQuantity(quantity + 1)}
>
+
</button>
<button className="bg-red-600 text-white py-1.5 px-4 hover:bg-red-800">
Add to Cart
</button>
</div>
<div className="flex flex-col gap-y-4 mt-4">
<p className="flex items-center">
<FaCarSide className="mr-1" />
Delivery & Return
</p>
<p className="flex items-center">
<FaQuestion className="mr-1" />
Ask a Question
</p>
</div>
</div>
</div>
<div className="mt-8">
<h3 className="text-xl font-bold mb-2">Product Description</h3>
<p>Product Description will goes here.</p>
</div>
</div>
);
};
export default ProductDetail;

View file

@ -1,62 +0,0 @@
import React from "react";
const Register = ({ openLogin }) => {
return (
<>
<div className="max-w-md mx-auto p-6 bg-white rounded-lg">
<h2 className="text-2xl font-bold mb-4 text-gray-800">Register</h2>
<form action="">
<div className="mb-4">
<label htmlFor="block text-gray-700 mb-1">Name</label>
<input
type="name"
className="w-full px-4 py-2 border border=gray-300 rounded-lg outline-none"
placeholder="enter your name"
/>
</div>
<div className="mb-4">
<label className="block text-gray-700 mb-1 ">Email</label>
<input
type="email"
className="w-full px-4 py-2 border border-gray-300 rounded-lg
outline-none"
placeholder="enter an email"
/>
</div>
<div className="mb-4">
<label className="block text-gray-700 mb-1">Password</label>
<input
type="password"
className="w-full px-4 py-2 border border-gray-300 rounded-lg
outline-none"
placeholder="enter a password"
/>
</div>
<div className="mb-4 ">
<button
type="submit"
className="w-full bg-red-600 text-white py-2 rounded-full hover:bg-red-700
transition duration-300"
>
Sign Up
</button>
</div>
</form>
<div className="text-center">
<span className="text-gray-700">Already have an account ? </span>
<button
className="text-red-600 hover:underline focus:outline-none"
onClick={openLogin}
>
Login
</button>
</div>
</div>
</>
);
};
export default Register;

View file

@ -1,37 +0,0 @@
import React, { useEffect, useState } from "react";
import { useSelector } from "react-redux";
import ProductCard from "../components/ProductCard";
const Shop = () => {
const [loading, setLoading] = useState(true);
const products = useSelector((state) => state.products);
useEffect(() => {
const timer = setTimeout(() => {
setLoading(false);
}, 1500);
return () => {
clearTimeout(timer);
};
}, []);
return (
<>
<div className="mx-auto py-12 px-4 md:px-16 lg:px-24">
{loading ? (
<div className="h-8 w-32 mx-auto mb-6 bg-gray-300 rounded-xl animate-pulse"></div>
) : (
<h2 className="text-2xl font-bold mb-6 text-center">Shop</h2>
)}
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 lg:grid-cols-5 gap-6 cursor-pointer">
{products?.products?.map((product) => (
<ProductCard product={product} />
))}
</div>
</div>
</>
);
};
export default Shop;

View file

@ -1,75 +0,0 @@
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
products: [],
totalQuantity: 0,
totalPrice: 0,
};
export const cartSlice = createSlice({
name: "cart",
initialState,
reducers: {
addToCart(state, action) {
const newItem = action.payload;
const itemIndex = state.products.find((item) => item.id === newItem.id);
if (itemIndex) {
itemIndex.quantity++;
itemIndex.totalPrice += newItem.price;
} else {
state.products.push({
id: newItem.id,
name: newItem.name,
price: newItem.price,
quantity: 1,
totalPrice: newItem.price,
image: newItem.image,
});
}
state.totalPrice += newItem.price;
state.totalQuantity++;
},
removeFromCart(state, action) {
const id = action.payload;
const findItem = state.products.find((item) => item.id === id);
if (findItem) {
state.totalPrice -= findItem.totalPrice;
state.totalQuantity -= findItem.quantity;
state.products = state.products.filter((item) => item.id !== id);
}
},
increaseQuantity(state, action) {
const id = action.payload;
const findItem = state.products.find((item) => item.id === id);
if (findItem) {
findItem.quantity++;
findItem.totalPrice += findItem.price;
state.totalQuantity++;
state.totalPrice += findItem.price;
}
},
decreaseQuantity(state, action) {
const id = action.payload;
const findItem = state.products.find((item) => item.id === id);
if (findItem.quantity > 1) {
if (findItem) {
findItem.quantity--;
findItem.totalPrice -= findItem.price;
state.totalQuantity--;
state.totalPrice -= findItem.price;
}
}
},
},
});
export const { addToCart, removeFromCart, increaseQuantity, decreaseQuantity } =
cartSlice.actions;
export default cartSlice.reducer;

View file

@ -1,28 +0,0 @@
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
products: [],
searchTerm: "",
filterData: [],
};
export const productSlice = createSlice({
name: "products",
initialState,
reducers: {
setProducts(state, action) {
state.products = action.payload;
},
setSearchTerm(state, action) {
state.searchTerm = action.payload;
state.filterData = state.products.filter((product) =>
product.name.toLowerCase().includes(state.searchTerm.toLowerCase())
);
},
},
});
export const { setSearchTerm, setProducts } = productSlice.actions;
export default productSlice.reducer;

View file

@ -1,9 +0,0 @@
import { configureStore } from "@reduxjs/toolkit";
import cartReducer from "./cartSlice";
import productReducer from "./productSlice";
export const store = configureStore({
reducer: {
cart: cartReducer,
products: productReducer,
},
});

View file

@ -1,8 +0,0 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {},
},
plugins: [],
};

View file

@ -1,7 +0,0 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
})