Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add categories feature #5

Merged
merged 1 commit into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
add categories feature
  • Loading branch information
alim1496 committed May 27, 2024
commit 8465991d150ba9e69b148206375cb93479a208f8
4 changes: 4 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import Profile from "./pages/Profile";
import AllProducts from "./pages/AllProducts";
import ScrollToTopButton from "./components/ScrollToTopButton";
import BannerPopup from "./components/BannerPopup";
import AllCategories from "./pages/AllCategories";
import SingleCategory from "./pages/SingleCategory";

function App() {
return (
Expand All @@ -23,7 +25,9 @@ function App() {
<Routes>
<Route path="/" element={<Home />} />
<Route path="/products" element={<AllProducts />} />
<Route path="/categories" element={<AllCategories />} />
<Route path="/product/:productID" element={<SingleProduct />} />
<Route path="/category/:slug" element={<SingleCategory />} />
<Route path="/wishlist" element={<ProtectedRoute />}>
<Route path="/wishlist" element={<Wishlist />} />
</Route>
Expand Down
7 changes: 7 additions & 0 deletions src/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ const Navbar: FC = () => {
>
Products
</Link>
<Link
to="/categories"
className="text-xl font-bold"
data-test="main-categories"
>
Categories
</Link>
<div className="flex items-center gap-2">
{username !== "" ? (
<img
Expand Down
48 changes: 48 additions & 0 deletions src/pages/AllCategories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { FC, useEffect } from "react";
import { useAppDispatch, useAppSelector } from "../redux/hooks";
import { addCategories } from "../redux/features/productSlice";
import { Link } from "react-router-dom";

const AllCategories: FC = () => {
const dispatch = useAppDispatch();

const allCategories = useAppSelector(
(state) => state.productReducer.categories
);

useEffect(() => {
const fetchCategories = () => {
fetch("https://dummyjson.com/products/categories")
.then((res) => res.json())
.then((data) => {
dispatch(addCategories(data));
});
};
if (allCategories.length === 0) fetchCategories();
}, [allCategories, dispatch]);

return (
<div className="container mx-auto min-h-[83vh] p-4 font-karla">
<span className="text-lg dark:text-white">Categories</span>
<div className="grid xl:grid-cols-6 lg:grid-cols-4 md:grid-cols-3 sm:grid-cols-2 gap-2 my-2">
{allCategories &&
allCategories.map((category) => (
<div
key={category.slug}
className="bg-gray-100 dark:bg-slate-600 dark:text-white px-4 py-4 font-karla mr-2 mb-2"
>
<div className="text-lg">{category.name}</div>
<Link
to={{ pathname: `/category/${category.slug}` }}
className="hover:underline text-blue-500"
>
View products
</Link>
</div>
))}
</div>
</div>
);
};

export default AllCategories;
57 changes: 7 additions & 50 deletions src/pages/AllProducts.tsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,33 @@
import { FC, useEffect, useRef, useState } from "react";
import { useAppSelector, useAppDispatch } from "../redux/hooks";
import { addCategories, addProducts } from "../redux/features/productSlice";
import { addProducts } from "../redux/features/productSlice";
import ProductCard from "../components/ProductCard";
import { Product } from "../models/Product";

const AllProducts: FC = () => {
const dispatch = useAppDispatch();
const [category, setCategory] = useState("all");
const sortRef = useRef<HTMLSelectElement>(null);
const [currentProducts, setCurrentProducts] = useState<Product[]>([]);
const allProducts = useAppSelector(
(state) => state.productReducer.allProducts
);
const allCategories = useAppSelector(
(state) => state.productReducer.categories
);

useEffect(() => {
const fetchProducts = () => {
fetch("https://dummyjson.com/products?limit=100")
fetch("https://dummyjson.com/products?limit=500")
.then((res) => res.json())
.then(({ products }) => {
dispatch(addProducts(products));
});
};
const fetchCategories = () => {
fetch("https://dummyjson.com/products/categories")
.then((res) => res.json())
.then((data) => {
dispatch(addCategories(data));
});
};

if (allProducts.length === 0) fetchProducts();
if (allCategories.length === 0) fetchCategories();
}, [allProducts, allCategories, dispatch]);
}, [allProducts, dispatch]);

useEffect(() => {
setCurrentProducts(allProducts);
}, [allProducts]);

useEffect(() => {
if (category !== "all") {
const updated = allProducts.filter((pro) => pro.category === category);
setCurrentProducts(updated);
}
}, [category, allProducts]);

const sortProducts = (sortValue: string) => {
if (sortValue === "asc") {
setCurrentProducts(
Expand Down Expand Up @@ -74,35 +56,10 @@ const AllProducts: FC = () => {

return (
<div className="container mx-auto min-h-[83vh] p-4 font-karla">
<div className="grid grid-cols-5 gap-1">
<div className="col-span-1">
<h1 className="font-bold mb-2 dark:text-white">Categories</h1>
<div className="space-y-1">
{allCategories.map((_category) => (
<div
key={_category.slug}
className={`cursor-pointer dark:text-white hover:text-blue-500 ${
_category.slug === category ? "text-blue-500" : ""
}`}
onClick={() => {
setCategory(_category.slug);
if (sortRef && sortRef.current)
sortRef.current.value = "default";
sortProducts("default");
}}
>
{_category.name}
</div>
))}
</div>
</div>
<div className="grid grid-cols-4 gap-1">
<div className="col-span-4 space-y-4">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2 text-lg dark:text-white">
<span>Products</span>
<span> {">"} </span>
<span className="font-bold">{category}</span>
</div>
<span className="text-lg dark:text-white">Products</span>
<select
ref={sortRef}
className="border border-black dark:border-white rounded p-1 dark:text-white dark:bg-slate-600"
Expand All @@ -113,7 +70,7 @@ const AllProducts: FC = () => {
<option value="desc">Price (high to low)</option>
</select>
</div>
<div className="grid gap-4 xl:grid-cols-3 lg:grid-cols-2 md:grid-cols-1">
<div className="grid gap-4 xl:grid-cols-4 lg:grid-cols-3 md:grid-cols-2 sm:grid-cols-1">
{currentProducts.map((product) => (
<ProductCard key={product.id} {...product} />
))}
Expand Down
39 changes: 39 additions & 0 deletions src/pages/SingleCategory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { FC, useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import { Product } from "../models/Product";
import ProductCard from "../components/ProductCard";

const SingleCategory: FC = () => {
const { slug } = useParams();
const [productList, setProductList] = useState<Product[]>([]);

useEffect(() => {
const fetchProducts = () => {
fetch(`https://dummyjson.com/products/category/${slug}`)
.then((res) => res.json())
.then((data) => {
const { products } = data;
setProductList(products);
});
};

fetchProducts();
}, [slug]);

return (
<div className="container mx-auto min-h-[83vh] p-4 font-karla">
<div className="flex items-center space-x-2 text-lg dark:text-white">
<span>Categories</span>
<span> {">"} </span>
<span className="font-bold">{slug}</span>
</div>
<div className="grid gap-4 xl:grid-cols-4 lg:grid-cols-3 md:grid-cols-2 sm:grid-cols-1 my-2">
{productList?.map((product) => (
<ProductCard key={product.id} {...product} />
))}
</div>
</div>
);
};

export default SingleCategory;