-
-
{category}
-
+
+
+
{name}
+
)
}
diff --git a/src/containers/herosection/HeroPage.tsx b/src/containers/herosection/HeroPage.tsx
index 0a9ff0e..54d8d5d 100644
--- a/src/containers/herosection/HeroPage.tsx
+++ b/src/containers/herosection/HeroPage.tsx
@@ -7,65 +7,28 @@ import { IoShieldCheckmarkOutline } from "react-icons/io5";
import Category from '../categories/Category';
import NewArrivals from '../Arrivals/NewArrivals';
import FeaturedProduct from '../FeaturedProducts/FeaturedProducts';
+import { useGetAllCategoriesQuery } from '../../services/productApi';
+import { useDispatch, useSelector } from 'react-redux';
+import { RootState } from '../../redux/store';
+import { setIsCategoriesFetched, setallCategories } from '../../redux/slices/categorySlice';
+import CategorySkeleton from '../../components/categories/CategoriesSkeleton';
-const categories = [
- {
- image: "https://images.unsplash.com/photo-1498049794561-7780e7231661?q=80&w=1740&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
- category: "Electronics"
- },
- {
- image: "https://images.unsplash.com/photo-1539109136881-3be0616acf4b?q=80&w=1587&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
- category: "Fashion"
- },
- {
- image: "https://plus.unsplash.com/premium_photo-1669652639337-c513cc42ead6?q=80&w=1587&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
- category: "Books"
- },
- {
- image: "https://images.unsplash.com/photo-1523575708161-ad0fc2a9b951?q=80&w=1740&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
- category: "Home & Garden"
- },
- {
- image: "https://images.unsplash.com/photo-1576438162986-c685b1cfed7a?q=80&w=1576&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
- category: "Sports"
- },
- {
- image: "https://plus.unsplash.com/premium_photo-1684795780266-ecd819f04f96?q=80&w=1632&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
- category: "Toys"
- },
- {
- image: "https://images.unsplash.com/photo-1556982962-dc0ee0f77f47?q=80&w=1587&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
- category: "Automotive"
- },
- {
- image: "https://images.unsplash.com/photo-1487412912498-0447578fcca8?q=80&w=1740&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
- category: "Beauty"
- },
- {
- image: "https://plus.unsplash.com/premium_photo-1673953509975-576678fa6710?q=80&w=1740&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
- category: "Health"
- },
- {
- image: "https://images.unsplash.com/photo-1458560871784-56d23406c091?q=80&w=1674&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
- category: "Music"
- },
- {
- image: "https://images.unsplash.com/photo-1488646953014-85cb44e25828?q=80&w=1635&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
- category: "Travel"
- },
- {
- image: "https://images.unsplash.com/photo-1458560871784-56d23406c091?q=80&w=1674&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
- category: "Music"
- },
- {
- image: "https://images.unsplash.com/photo-1488646953014-85cb44e25828?q=80&w=1635&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
- category: "Travel"
- },
-
-];
-
function HeroPage() {
+ const dispatch = useDispatch();
+ const { allCategories, isCategoriesFetched } :any = useSelector((state: RootState) => state.category);
+ const { data: categoriess } = useGetAllCategoriesQuery();
+
+ useEffect(() => {
+ if (!isCategoriesFetched && categoriess) {
+ dispatch(setallCategories(categoriess));
+ dispatch(setIsCategoriesFetched(true));
+ }
+ }, [categoriess, isCategoriesFetched, dispatch]);
+
+ const categoriesToDisplay = allCategories.length ? allCategories.data : categoriess;
+ const list = categoriesToDisplay?.data
+
const images = [
"https://images.unsplash.com/photo-1491553895911-0055eca6402d?q=80&w=1760&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
"https://noticiasconcursos.com.br/wp-content/uploads/2023/08/noticiasconcursos.com.br-novo-iphone-da-apple-ja-tem-data-prevista-para-lancamento-veja-quando-09-iphone-15-750x430.jpg",
@@ -82,7 +45,7 @@ function HeroPage() {
}, []);
return (
-
+
@@ -118,11 +81,19 @@ function HeroPage() {
Shop By Category Here:
- {categories.map((cat, index) => (
-
-
-
- ))}
+ {list && list.length ? (
+
+ {list.map((cat:{image:string, name: string}, index:number) => (
+
+
+
+ ))}
+
+ ) : (
+
+
+
+ )}
SMARTER VALUES, GREAT DEALS
diff --git a/src/containers/nav/NavbarComponents.tsx b/src/containers/nav/NavbarComponents.tsx
index a26deb0..b3d2480 100644
--- a/src/containers/nav/NavbarComponents.tsx
+++ b/src/containers/nav/NavbarComponents.tsx
@@ -4,9 +4,10 @@ import education from '../../assets/education.png';
import suppliment from '../../assets/supplement.png';
import shoes from '../../assets/shoes.png';
import electronics from '../../assets/electronics.png';
-import { Link, NavLink } from 'react-router-dom';
+import { useNavigate, NavLink } from 'react-router-dom';
-export function PopularCategory({ title }: { title: string }) {
+
+export function PopularCategory({ title }: { title: string }) {
return (
<>
@@ -35,28 +36,35 @@ export function SidebarLink({ icon, name, to }: { icon: string; name: string; to
);
}
export function DesktopNav() {
+
+ const navigate = useNavigate();
+
+ const handleClick = (name: string) => {
+ navigate(`/categories/${name}`);
+ };
+
return (
-
-
-
-
-
-
-
+ handleClick('Plus')} />
+ handleClick('Flash Sales')} />
+ handleClick('Babies')} />
+ handleClick('Fathers')} />
+ handleClick('Electronics')} />
+ handleClick('Beauty')} />
+ handleClick('Sports')} />
);
}
-function DeskNavLink({ to, name }: { to: string; name: string }) {
+function DeskNavLink({ onClick, name }: { onClick: () => void, name: string }) {
return (
-
{name}
-
+
);
}
diff --git a/src/pages/CategoriesPage.tsx b/src/pages/CategoriesPage.tsx
new file mode 100644
index 0000000..7af218a
--- /dev/null
+++ b/src/pages/CategoriesPage.tsx
@@ -0,0 +1,234 @@
+import React, { useEffect, useState } from 'react';
+import { useParams } from 'react-router-dom';
+import Footer from '../components/footer/Footer';
+import Navbar from '../components/navbar/Navbar';
+import { TiShoppingCart } from "react-icons/ti";
+import { FaHeart } from "react-icons/fa";
+import Hero from '../assets/Hero.png'
+import StarRating from '../components/common/Ratings';
+import { useSelector } from 'react-redux';
+import { Category } from '../types/Types';
+import { useDispatch } from 'react-redux';
+import { useGetAllCategoriesQuery } from '../services/productApi';
+import { setIsCategoriesFetched, setallCategories } from '../redux/slices/categorySlice';
+import { FaLongArrowAltRight, FaLongArrowAltLeft } from "react-icons/fa";
+
+
+
+const CategoriesPage: React.FC = () => {
+ const { categoryId } = useParams<{ categoryId: string }>();
+ const [selectedCategory, setSelectedCategory] = useState
('');
+ const [cateId, setCategoryId] = useState('');
+
+ // categories
+ const dispatch = useDispatch();
+ const { allCategories, isCategoriesFetched } :any = useSelector((state: any) => state.category);
+ const { data: categoriess, isLoading } = useGetAllCategoriesQuery();
+
+ // categories on page load
+ useEffect(() => {
+ if (!isCategoriesFetched && categoriess) {
+ dispatch(setallCategories(categoriess));
+ dispatch(setIsCategoriesFetched(true));
+ }
+ }, [categoriess, isCategoriesFetched, dispatch]);
+
+ useEffect(() => {
+ if (!isLoading && allCategories) {
+ const category = allCategories?.data?.find((cat: Category) => cat.name === categoryId);
+ if (category) {
+ setCategoryId(category.id);
+ } else {
+ setCategoryId("");
+ }
+ }
+ }, [categoryId, allCategories, isLoading]);
+
+ const categoriesToDisplay = allCategories.length ? allCategories.data : categoriess;
+ const { productsDataList: productsList } = useSelector((state: any) => state.products);
+
+ const [currentPage, setCurrentPage] = useState(0);
+ const [nameFilter] = useState('');
+ const [minPrice, setMinPrice] = useState('');
+ const [maxPrice, setMaxPrice] = useState('');
+ const [sortOption, setSortOption] = useState('');
+
+ const productsPerPage = 12;
+
+ const handleNext = () => {
+ if ((currentPage + 1) * productsPerPage < productsList.length) {
+ setCurrentPage(currentPage + 1);
+ }
+ };
+
+ const handlePrev = () => {
+ if (currentPage > 0) {
+ setCurrentPage(currentPage - 1);
+ }
+ };
+
+ const handleCategoryChange = (event: React.ChangeEvent) => {
+ setSelectedCategory(event.target.value);
+ setCategoryId(event.target.value)
+ };
+
+ const filteredProducts = [...productsList]
+ .filter(product => {
+ const matchesName = product.name.toLowerCase().includes(nameFilter.toLowerCase());
+ const matchesMinPrice = minPrice === '' || product?.sizes?.[0]?.price >= parseFloat(minPrice);
+ const matchesMaxPrice = maxPrice === '' || product?.sizes?.[0]?.price <= parseFloat(maxPrice);
+ const matchesCategory = selectedCategory === '' || product.categoryId === selectedCategory;
+ const matchesCategoryId = cateId === '' || product.categoryId === cateId;
+ return matchesName && matchesMinPrice && matchesMaxPrice && matchesCategory && matchesCategoryId;
+ })
+ .sort((a, b) => {
+ if (sortOption === 'name') {
+ return a.name.localeCompare(b.name);
+ } else if (sortOption === 'date') {
+ return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
+ }
+ else if (sortOption === 'rating') {
+ return b.ratings - a.ratings;
+ }
+ return 0;
+ });
+
+
+ const startIndex = currentPage * productsPerPage;
+ const endIndex = startIndex + productsPerPage;
+ const currentProducts = filteredProducts.slice(startIndex, endIndex);
+
+ return (
+
+
+
+
+
+
+
+
+
+ Grab up to 50% off on Selected Products
+
+
+
+
+
+
+
+
+
+
+
+
+
+ setMinPrice(e.target.value)}
+ />
+
+
+ setMaxPrice(e.target.value)}
+ />
+
+
+
+
+
+
+ {/* Products */}
+
+
+
+ {currentProducts.length ===0 ? (
+
No products found.
+ ):(currentProducts.map((product, index) => (
+
+
+
+
+
+
+
+
{product.name}
+
${product?.sizes?.[0]?.price}
+
+
{product.manufacturer}
+
+
+
+
+ ))
+ )}
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default CategoriesPage;
diff --git a/src/redux/slices/categorySlice.ts b/src/redux/slices/categorySlice.ts
index cba23d1..fd415e2 100644
--- a/src/redux/slices/categorySlice.ts
+++ b/src/redux/slices/categorySlice.ts
@@ -1,11 +1,17 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
+import { Category } from '../../types/Types';
+
interface CategoryState {
isCreated: boolean;
+ isCategoriesFetched: boolean;
+ allCategories: Category[];
}
const initialState: CategoryState = {
isCreated: false,
+ isCategoriesFetched: false,
+ allCategories: [],
};
const categorySlice = createSlice({
@@ -15,9 +21,15 @@ const categorySlice = createSlice({
setIsCreated: (state, action: PayloadAction) => {
state.isCreated = action.payload;
},
+ setIsCategoriesFetched: (state, action: PayloadAction) => {
+ state.isCategoriesFetched = action.payload;
+ },
+ setallCategories: (state, action: PayloadAction) => {
+ state.allCategories = action.payload;
+ },
},
});
-export const { setIsCreated } = categorySlice.actions;
+export const { setIsCreated , setIsCategoriesFetched, setallCategories} = categorySlice.actions;
export default categorySlice.reducer;
diff --git a/src/redux/store.ts b/src/redux/store.ts
index a3e96ee..f650c7c 100644
--- a/src/redux/store.ts
+++ b/src/redux/store.ts
@@ -1,25 +1,24 @@
// src/store.js
-
import { configureStore } from '@reduxjs/toolkit';
import { mavericksApi } from '../services';
import { setupListeners } from '@reduxjs/toolkit/query';
import registerReducer from './slices/registerSlice';
import userReducer from './slices/userSlice';
import productReducer from './slices/productsSlice';
-import categorySlice from './slices/categorySlice';
+import categoriesReducer from './slices/categorySlice';
export const store = configureStore({
reducer: {
products: productReducer,
user: userReducer,
register: registerReducer,
- category: categorySlice,
+ category: categoriesReducer,
[mavericksApi.reducerPath]: mavericksApi.reducer,
},
middleware: getDefaultMiddleware => getDefaultMiddleware().concat(mavericksApi.middleware),
});
-// Setup listeners for RTK Query features
+// Setup listeners for RTK Query feature
setupListeners(store.dispatch);
export type RootState = ReturnType;
diff --git a/src/services/productApi.ts b/src/services/productApi.ts
index c434056..e611265 100644
--- a/src/services/productApi.ts
+++ b/src/services/productApi.ts
@@ -1,4 +1,4 @@
-import type { Product } from '../types/Types';
+import type { Category, Product } from '../types/Types';
import { mavericksApi } from '.';
export const productsApi = mavericksApi.injectEndpoints({
@@ -6,8 +6,11 @@ export const productsApi = mavericksApi.injectEndpoints({
getProducts: builder.query({
query: () => 'products',
}),
+ getAllCategories: builder.query({
+ query: () => 'category',
+ }),
}),
overrideExisting: false,
});
-export const { useGetProductsQuery } = productsApi;
+export const { useGetProductsQuery, useGetAllCategoriesQuery } = productsApi;
diff --git a/src/types/Types.ts b/src/types/Types.ts
index 0d62272..5f412df 100644
--- a/src/types/Types.ts
+++ b/src/types/Types.ts
@@ -2,6 +2,7 @@ import { JwtPayload } from 'jwt-decode';
// types/Types.ts
export type Product = {
+ reviews: Review[];
id: string;
name: string;
description: string;
@@ -22,7 +23,27 @@ export type ProductResponse = {
data: Product[];
};
+export interface Review {
+ createdAt: string;
+ feedback: string;
+ feedbackImage: string;
+ id: string;
+ productId: string;
+ rating: number;
+}
export interface CustomJwtPayload extends JwtPayload {
id: string;
role: string;
}
+
+export type Category = {
+ id: string;
+ name: string;
+ image?:string;
+};
+
+export type CategoryResponse = {
+ ok: boolean;
+ message: string;
+ data: Category[];
+};
\ No newline at end of file