Skip to content

Commit 66e621b

Browse files
authored
Merge pull request alim1496#5 from alim1496/feature/introduce-categories-tab
add categories feature
2 parents 50e7944 + 8465991 commit 66e621b

5 files changed

Lines changed: 105 additions & 50 deletions

File tree

src/App.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import Profile from "./pages/Profile";
1515
import AllProducts from "./pages/AllProducts";
1616
import ScrollToTopButton from "./components/ScrollToTopButton";
1717
import BannerPopup from "./components/BannerPopup";
18+
import AllCategories from "./pages/AllCategories";
19+
import SingleCategory from "./pages/SingleCategory";
1820

1921
function App() {
2022
return (
@@ -23,7 +25,9 @@ function App() {
2325
<Routes>
2426
<Route path="/" element={<Home />} />
2527
<Route path="/products" element={<AllProducts />} />
28+
<Route path="/categories" element={<AllCategories />} />
2629
<Route path="/product/:productID" element={<SingleProduct />} />
30+
<Route path="/category/:slug" element={<SingleCategory />} />
2731
<Route path="/wishlist" element={<ProtectedRoute />}>
2832
<Route path="/wishlist" element={<Wishlist />} />
2933
</Route>

src/components/Navbar.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ const Navbar: FC = () => {
5353
>
5454
Products
5555
</Link>
56+
<Link
57+
to="/categories"
58+
className="text-xl font-bold"
59+
data-test="main-categories"
60+
>
61+
Categories
62+
</Link>
5663
<div className="flex items-center gap-2">
5764
{username !== "" ? (
5865
<img

src/pages/AllCategories.tsx

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { FC, useEffect } from "react";
2+
import { useAppDispatch, useAppSelector } from "../redux/hooks";
3+
import { addCategories } from "../redux/features/productSlice";
4+
import { Link } from "react-router-dom";
5+
6+
const AllCategories: FC = () => {
7+
const dispatch = useAppDispatch();
8+
9+
const allCategories = useAppSelector(
10+
(state) => state.productReducer.categories
11+
);
12+
13+
useEffect(() => {
14+
const fetchCategories = () => {
15+
fetch("https://dummyjson.com/products/categories")
16+
.then((res) => res.json())
17+
.then((data) => {
18+
dispatch(addCategories(data));
19+
});
20+
};
21+
if (allCategories.length === 0) fetchCategories();
22+
}, [allCategories, dispatch]);
23+
24+
return (
25+
<div className="container mx-auto min-h-[83vh] p-4 font-karla">
26+
<span className="text-lg dark:text-white">Categories</span>
27+
<div className="grid xl:grid-cols-6 lg:grid-cols-4 md:grid-cols-3 sm:grid-cols-2 gap-2 my-2">
28+
{allCategories &&
29+
allCategories.map((category) => (
30+
<div
31+
key={category.slug}
32+
className="bg-gray-100 dark:bg-slate-600 dark:text-white px-4 py-4 font-karla mr-2 mb-2"
33+
>
34+
<div className="text-lg">{category.name}</div>
35+
<Link
36+
to={{ pathname: `/category/${category.slug}` }}
37+
className="hover:underline text-blue-500"
38+
>
39+
View products
40+
</Link>
41+
</div>
42+
))}
43+
</div>
44+
</div>
45+
);
46+
};
47+
48+
export default AllCategories;

src/pages/AllProducts.tsx

Lines changed: 7 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,33 @@
11
import { FC, useEffect, useRef, useState } from "react";
22
import { useAppSelector, useAppDispatch } from "../redux/hooks";
3-
import { addCategories, addProducts } from "../redux/features/productSlice";
3+
import { addProducts } from "../redux/features/productSlice";
44
import ProductCard from "../components/ProductCard";
55
import { Product } from "../models/Product";
66

77
const AllProducts: FC = () => {
88
const dispatch = useAppDispatch();
9-
const [category, setCategory] = useState("all");
109
const sortRef = useRef<HTMLSelectElement>(null);
1110
const [currentProducts, setCurrentProducts] = useState<Product[]>([]);
1211
const allProducts = useAppSelector(
1312
(state) => state.productReducer.allProducts
1413
);
15-
const allCategories = useAppSelector(
16-
(state) => state.productReducer.categories
17-
);
1814

1915
useEffect(() => {
2016
const fetchProducts = () => {
21-
fetch("https://dummyjson.com/products?limit=100")
17+
fetch("https://dummyjson.com/products?limit=500")
2218
.then((res) => res.json())
2319
.then(({ products }) => {
2420
dispatch(addProducts(products));
2521
});
2622
};
27-
const fetchCategories = () => {
28-
fetch("https://dummyjson.com/products/categories")
29-
.then((res) => res.json())
30-
.then((data) => {
31-
dispatch(addCategories(data));
32-
});
33-
};
23+
3424
if (allProducts.length === 0) fetchProducts();
35-
if (allCategories.length === 0) fetchCategories();
36-
}, [allProducts, allCategories, dispatch]);
25+
}, [allProducts, dispatch]);
3726

3827
useEffect(() => {
3928
setCurrentProducts(allProducts);
4029
}, [allProducts]);
4130

42-
useEffect(() => {
43-
if (category !== "all") {
44-
const updated = allProducts.filter((pro) => pro.category === category);
45-
setCurrentProducts(updated);
46-
}
47-
}, [category, allProducts]);
48-
4931
const sortProducts = (sortValue: string) => {
5032
if (sortValue === "asc") {
5133
setCurrentProducts(
@@ -74,35 +56,10 @@ const AllProducts: FC = () => {
7456

7557
return (
7658
<div className="container mx-auto min-h-[83vh] p-4 font-karla">
77-
<div className="grid grid-cols-5 gap-1">
78-
<div className="col-span-1">
79-
<h1 className="font-bold mb-2 dark:text-white">Categories</h1>
80-
<div className="space-y-1">
81-
{allCategories.map((_category) => (
82-
<div
83-
key={_category.slug}
84-
className={`cursor-pointer dark:text-white hover:text-blue-500 ${
85-
_category.slug === category ? "text-blue-500" : ""
86-
}`}
87-
onClick={() => {
88-
setCategory(_category.slug);
89-
if (sortRef && sortRef.current)
90-
sortRef.current.value = "default";
91-
sortProducts("default");
92-
}}
93-
>
94-
{_category.name}
95-
</div>
96-
))}
97-
</div>
98-
</div>
59+
<div className="grid grid-cols-4 gap-1">
9960
<div className="col-span-4 space-y-4">
10061
<div className="flex items-center justify-between">
101-
<div className="flex items-center space-x-2 text-lg dark:text-white">
102-
<span>Products</span>
103-
<span> {">"} </span>
104-
<span className="font-bold">{category}</span>
105-
</div>
62+
<span className="text-lg dark:text-white">Products</span>
10663
<select
10764
ref={sortRef}
10865
className="border border-black dark:border-white rounded p-1 dark:text-white dark:bg-slate-600"
@@ -113,7 +70,7 @@ const AllProducts: FC = () => {
11370
<option value="desc">Price (high to low)</option>
11471
</select>
11572
</div>
116-
<div className="grid gap-4 xl:grid-cols-3 lg:grid-cols-2 md:grid-cols-1">
73+
<div className="grid gap-4 xl:grid-cols-4 lg:grid-cols-3 md:grid-cols-2 sm:grid-cols-1">
11774
{currentProducts.map((product) => (
11875
<ProductCard key={product.id} {...product} />
11976
))}

src/pages/SingleCategory.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { FC, useEffect, useState } from "react";
2+
import { useParams } from "react-router-dom";
3+
import { Product } from "../models/Product";
4+
import ProductCard from "../components/ProductCard";
5+
6+
const SingleCategory: FC = () => {
7+
const { slug } = useParams();
8+
const [productList, setProductList] = useState<Product[]>([]);
9+
10+
useEffect(() => {
11+
const fetchProducts = () => {
12+
fetch(`https://dummyjson.com/products/category/${slug}`)
13+
.then((res) => res.json())
14+
.then((data) => {
15+
const { products } = data;
16+
setProductList(products);
17+
});
18+
};
19+
20+
fetchProducts();
21+
}, [slug]);
22+
23+
return (
24+
<div className="container mx-auto min-h-[83vh] p-4 font-karla">
25+
<div className="flex items-center space-x-2 text-lg dark:text-white">
26+
<span>Categories</span>
27+
<span> {">"} </span>
28+
<span className="font-bold">{slug}</span>
29+
</div>
30+
<div className="grid gap-4 xl:grid-cols-4 lg:grid-cols-3 md:grid-cols-2 sm:grid-cols-1 my-2">
31+
{productList?.map((product) => (
32+
<ProductCard key={product.id} {...product} />
33+
))}
34+
</div>
35+
</div>
36+
);
37+
};
38+
39+
export default SingleCategory;

0 commit comments

Comments
 (0)