Skip to content

Commit a06e910

Browse files
calling categories and brand through API
1 parent 40a7fce commit a06e910

File tree

4 files changed

+178
-36
lines changed

4 files changed

+178
-36
lines changed

data.json

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1719,5 +1719,99 @@
17191719
],
17201720
"thumbnail": "https://cdn.dummyjson.com/products/images/groceries/Kiwi/thumbnail.png"
17211721
}
1722+
],
1723+
"brands": [
1724+
{
1725+
"value": "Essence",
1726+
"label": "Essence",
1727+
"checked": false
1728+
},
1729+
{
1730+
"value": "Glamour Beauty",
1731+
"label": "Glamour Beauty",
1732+
"checked": false
1733+
},
1734+
{
1735+
"value": "Velvet Touch",
1736+
"label": "Velvet Touch",
1737+
"checked": false
1738+
},
1739+
{
1740+
"value": "Chic Cosmetics",
1741+
"label": "Chic Cosmetics",
1742+
"checked": false
1743+
},
1744+
{
1745+
"value": "Nail Couture",
1746+
"label": "Nail Couture",
1747+
"checked": false
1748+
},
1749+
{
1750+
"value": "Calvin Klein",
1751+
"label": "Calvin Klein",
1752+
"checked": false
1753+
},
1754+
{
1755+
"value": "Chanel",
1756+
"label": "Chanel",
1757+
"checked": false
1758+
},
1759+
{
1760+
"value": "Dior",
1761+
"label": "Dior",
1762+
"checked": false
1763+
},
1764+
{
1765+
"value": "Dolce & Gabbana",
1766+
"label": "Dolce & Gabbana",
1767+
"checked": false
1768+
},
1769+
{
1770+
"value": "Gucci",
1771+
"label": "Gucci",
1772+
"checked": false
1773+
},
1774+
{
1775+
"value": "Annibale Colombo",
1776+
"label": "Annibale Colombo",
1777+
"checked": false
1778+
},
1779+
{
1780+
"value": "Furniture Co.",
1781+
"label": "Furniture Co.",
1782+
"checked": false
1783+
},
1784+
{
1785+
"value": "Knoll",
1786+
"label": "Knoll",
1787+
"checked": false
1788+
},
1789+
{
1790+
"value": "Bath Trends",
1791+
"label": "Bath Trends",
1792+
"checked": false
1793+
}
1794+
],
1795+
"categories": [
1796+
{
1797+
"value": "beauty",
1798+
"label": "Beauty",
1799+
"checked": false
1800+
},
1801+
{
1802+
"value": "fragrances",
1803+
"label": "Fragrances",
1804+
"checked": false
1805+
},
1806+
{
1807+
"value": "furniture",
1808+
"label": "Furniture",
1809+
"checked": false
1810+
},
1811+
{
1812+
"value": "groceries",
1813+
"label": "Groceries",
1814+
"checked": false
1815+
}
17221816
]
17231817
}

src/features/product/components/ProductList.js

Lines changed: 28 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@ import React, { useState, Fragment, useEffect } from "react";
22
import { useSelector, useDispatch } from "react-redux";
33
import {
44
fetchAllProductsAsync,
5+
fetchBrandsAsync,
6+
fetchCategoriesAsync,
57
fetchProductsByFiltersAsync,
68
selectAllProducts,
9+
selectBrands,
10+
selectCategories,
711
selectTotalItems,
812
} from "../productSlice";
913
import { Dialog, Disclosure, Menu, Transition } from "@headlessui/react";
@@ -25,47 +29,29 @@ const sortOptions = [
2529
{ name: "Price: High to Low", sort: "price", order: "desc", current: false },
2630
];
2731

28-
const filters = [
29-
{
30-
id: "category",
31-
name: "Category",
32-
options: [
33-
{ value: "beauty", label: "Beauty", checked: false },
34-
{ value: "fragrances", label: "Fragrances", checked: false },
35-
{ value: "furniture", label: "Furniture", checked: false },
36-
{ value: "groceries", label: "Groceries", checked: false },
37-
],
38-
},
39-
{
40-
id: "brand",
41-
name: "Brands",
42-
options: [
43-
{ value: "Essence", label: "Essence", checked: false },
44-
{ value: "Glamour Beauty", label: "Glamour Beauty", checked: false },
45-
{ value: "Velvet Touch", label: "Velvet Touch", checked: false },
46-
{ value: "Chic Cosmetics", label: "Chic Cosmetics", checked: false },
47-
{ value: "Nail Couture", label: "Nail Couture", checked: false },
48-
{ value: "Calvin Klein", label: "Calvin Klein", checked: false },
49-
{ value: "Chanel", label: "Chanel", checked: false },
50-
{ value: "Dior", label: "Dior", checked: false },
51-
{ value: "Dolce & Gabbana", label: "Dolce & Gabbana", checked: false },
52-
{ value: "Gucci", label: "Gucci", checked: false },
53-
{ value: "Annibale Colombo", label: "Annibale Colombo", checked: false },
54-
{ value: "Furniture Co.", label: "Furniture Co.", checked: false },
55-
{ value: "Knoll", label: "Knoll", checked: false },
56-
{ value: "Bath Trends", label: "Bath Trends", checked: false },
57-
],
58-
},
59-
];
60-
6132
function classNames(...classes) {
6233
return classes.filter(Boolean).join(" ");
6334
}
6435

6536
export default function ProductList() {
6637
const [mobileFiltersOpen, setMobileFiltersOpen] = useState(false);
6738
const products = useSelector(selectAllProducts);
39+
const categories = useSelector(selectCategories);
40+
const brands = useSelector(selectBrands);
6841
const totalItems = useSelector(selectTotalItems);
42+
const filters = [
43+
{
44+
id: "category",
45+
name: "Category",
46+
options: categories,
47+
},
48+
{
49+
id: "brand",
50+
name: "Brands",
51+
options: brands,
52+
},
53+
];
54+
6955
const dispatch = useDispatch();
7056
const [filter, setFilter] = useState({});
7157
const [sort, setSort] = useState({});
@@ -114,6 +100,11 @@ export default function ProductList() {
114100
setPage(1);
115101
}, [totalItems, sort]);
116102

103+
useEffect(() => {
104+
dispatch(fetchBrandsAsync());
105+
dispatch(fetchCategoriesAsync());
106+
}, []);
107+
117108
return (
118109
<div className="bg-white">
119110
<div>
@@ -122,6 +113,7 @@ export default function ProductList() {
122113
handleFilter={handleFilter}
123114
mobileFiltersOpen={mobileFiltersOpen}
124115
setMobileFiltersOpen={setMobileFiltersOpen}
116+
filters={filters}
125117
/>
126118

127119
<main className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
@@ -202,7 +194,7 @@ export default function ProductList() {
202194
</h2>
203195

204196
<div className="grid grid-cols-1 gap-x-8 gap-y-10 lg:grid-cols-4">
205-
<DesktopFilter handleFilter={handleFilter} />
197+
<DesktopFilter handleFilter={handleFilter} filters={filters} />
206198

207199
{/* Product grid */}
208200
<div className="lg:col-span-3">
@@ -230,6 +222,7 @@ function MobileFilter({
230222
mobileFiltersOpen,
231223
setMobileFiltersOpen,
232224
handleFilter,
225+
filters,
233226
}) {
234227
return (
235228
<Transition.Root show={mobileFiltersOpen} as={Fragment}>
@@ -343,7 +336,7 @@ function MobileFilter({
343336
</Transition.Root>
344337
);
345338
}
346-
function DesktopFilter({ handleFilter }) {
339+
function DesktopFilter({ handleFilter, filters }) {
347340
return (
348341
<form className="hidden lg:block">
349342
{filters.map((section) => (

src/features/product/productAPI.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,17 @@ export function fetchProductsByFilters(filter, sort, pagination) {
5454
resolve({ data: { products: data.data, totalItems: data.items } });
5555
});
5656
}
57+
export function fetchCategories() {
58+
return new Promise(async (resolve) => {
59+
const response = await fetch("http://localhost:8080/categories");
60+
const data = await response.json();
61+
resolve({ data });
62+
});
63+
}
64+
export function fetchBrands() {
65+
return new Promise(async (resolve) => {
66+
const response = await fetch("http://localhost:8080/brands");
67+
const data = await response.json();
68+
resolve({ data });
69+
});
70+
}

src/features/product/productSlice.js

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
2-
import { fetchAllProducts, fetchProductsByFilters } from "./productAPI";
2+
import {
3+
fetchAllProducts,
4+
fetchProductsByFilters,
5+
fetchBrands,
6+
fetchCategories,
7+
} from "./productAPI";
38

49
const initialState = {
510
products: [],
11+
brands: [],
12+
categories: [],
613
status: "idle",
714
totalItems: 0,
815
};
@@ -25,6 +32,24 @@ export const fetchProductsByFiltersAsync = createAsyncThunk(
2532
}
2633
);
2734

35+
export const fetchBrandsAsync = createAsyncThunk(
36+
"product/fetchBrands",
37+
async () => {
38+
const response = await fetchBrands();
39+
// The value we return becomes the `fulfilled` action payload
40+
return response.data;
41+
}
42+
);
43+
44+
export const fetchCategoriesAsync = createAsyncThunk(
45+
"product/fetchCategories",
46+
async () => {
47+
const response = await fetchCategories();
48+
// The value we return becomes the `fulfilled` action payload
49+
return response.data;
50+
}
51+
);
52+
2853
export const productSlice = createSlice({
2954
name: "product",
3055
initialState,
@@ -49,13 +74,29 @@ export const productSlice = createSlice({
4974
state.status = "idle";
5075
state.products = action.payload.products;
5176
state.totalItems = action.payload.totalItems;
77+
})
78+
.addCase(fetchBrandsAsync.pending, (state) => {
79+
state.status = "loading";
80+
})
81+
.addCase(fetchBrandsAsync.fulfilled, (state, action) => {
82+
state.status = "idle";
83+
state.brands = action.payload;
84+
})
85+
.addCase(fetchCategoriesAsync.pending, (state) => {
86+
state.status = "loading";
87+
})
88+
.addCase(fetchCategoriesAsync.fulfilled, (state, action) => {
89+
state.status = "idle";
90+
state.categories = action.payload;
5291
});
5392
},
5493
});
5594

5695
export const { increment } = productSlice.actions;
5796

5897
export const selectAllProducts = (state) => state.product.products;
98+
export const selectBrands = (state) => state.product.brands;
99+
export const selectCategories = (state) => state.product.categories;
59100
export const selectTotalItems = (state) => state.product.totalItems;
60101

61102
export default productSlice.reducer;

0 commit comments

Comments
 (0)