Skip to content
Open
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
4 changes: 2 additions & 2 deletions client/src/app/(auth)/sign-up/[[...sign-up]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import Image from 'next/image';

export default function Page() {
return (
<section>
<div>
<section className="bg-white">
<div className="lg:grid lg:min-h-screen lg:grid-cols-12">
<aside className="relative block h-16 lg:order-last lg:col-span-5 lg:h-full xl:col-span-6">
<Image
height={1000}
Expand Down
1 change: 1 addition & 0 deletions client/src/app/_Components/ProductCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export default function ProductCard({ product }: ProductCardProps) {
return (
<Link
href={`/product-details/${product?.id}`}
prefetch
className="flex flex-col hover:border hover:shadow-md rounded-lg border-blue-200 bg-white"
>
<Image
Expand Down
47 changes: 9 additions & 38 deletions client/src/app/_Components/ProductSection.tsx
Original file line number Diff line number Diff line change
@@ -1,53 +1,24 @@
'use client';
import React, { useEffect, useState } from 'react';
import ProductList from './ProductList';
import { getLatestProducts, StrapiResponse } from '../_utils/productsAPI';
import React from 'react';
import { AxiosResponse } from 'axios';
import { Product } from '../_utils/productsAPI';

export default function ProductSection() {
const [productList, setProductList] = useState<Product[]>([]);

//a state for featured products
const [featuredProducts, setFeaturedProducts] = useState<Product[]>([]);

useEffect(() => {
fetchLatestProducts();
}, []);

const fetchLatestProducts = async () => {
try {
const res: AxiosResponse<StrapiResponse<Product[]>> =
await getLatestProducts();
// console.log(res.data.data);
// setProductList(res.data.data);
const allProducts = res.data.data; //get all products
//select 8 random
const randomProducts = [...allProducts]
.sort(() => Math.random() - 0.5) //shuffle
.slice(0, 4);
import ProductList from './ProductList';
import { getLatestProducts, Product, StrapiResponse } from '../_utils/productsAPI';

setFeaturedProducts(randomProducts);
setProductList(allProducts); //store all products
} catch (err) {
console.error(err);
}
};
export default async function ProductSection() {
const res: AxiosResponse<StrapiResponse<Product[]>> = await getLatestProducts();
const allProducts = res.data.data;
const featuredProducts = [...allProducts]
.sort(() => Math.random() - 0.5)
.slice(0, 4);

return (
<div className="px-6 sm:px-10 md:px-20 py-10">
{/*Featured products section*/}
<h1
id="featured-products"
className="text-2xl md:text-3xl font-bold text-black my-4"
>
Featured Products
</h1>
<ProductList productList={featuredProducts} />

{/*All products section*/}
{/* <h1 className="text-3xl font-bold text-black my-4">All Products</h1>
<ProductList productList={productList} /> */}
</div>
);
}
49 changes: 30 additions & 19 deletions client/src/app/_utils/productsAPI.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import axiosClient from './axiosInstance';
import { AxiosResponse } from 'axios';
import { unstable_cache } from 'next/cache';

export interface Product {
id: number;
Expand All @@ -26,26 +27,36 @@ export interface StrapiResponse<T> {
meta: any;
}

const getLatestProducts = (): Promise<
AxiosResponse<StrapiResponse<Product[]>>
> => {
return axiosClient.get(
'/products?fields[0]=title&fields[1]=price&fields[2]=category&populate[image][fields][0]=url',
);
};
const getLatestProducts = unstable_cache(
async (): Promise<AxiosResponse<StrapiResponse<Product[]>>> => {
return axiosClient.get(
'/products?fields[0]=title&fields[1]=price&fields[2]=category&populate[image][fields][0]=url',
);
},
['latest-products'],
{ revalidate: 60 },
);

const getProductById = (id: number) => {
return axiosClient.get(`/products/${id}?populate=*`);
// return axiosClient.get(
// `/products/${id}?fields[0]=title&fields[1]=price&fields[2]=category&populate[image][fields][0]=url`,
// );
};
const getProductById = unstable_cache(
async (
id: number,
): Promise<AxiosResponse<StrapiResponse<Product>>> => {
return axiosClient.get(`/products/${id}?populate=*`);
},
(id: number) => ['product-by-id', id],
{ revalidate: 60 },
);

const getProductByCategory = (category: string) => {
return axiosClient.get(
// `/products?filters[category][$eq]=${category}&fields[0]=title&fields[1]=price&fields[2]=category&populate[image][fields][0]=url`,
`/products?filters[category][$eq]=${category}&populate=*`,
);
};
const getProductByCategory = unstable_cache(
async (
category: string,
): Promise<AxiosResponse<StrapiResponse<Product[]>>> => {
return axiosClient.get(
`/products?filters[category][$eq]=${category}&populate=*`,
);
},
(category: string) => ['products-category', category],
{ revalidate: 60 },
);

export { getLatestProducts, getProductById, getProductByCategory };
49 changes: 49 additions & 0 deletions client/src/app/all-products/_components/AllProductsClient.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
'use client';
import React, { useState } from 'react';
import ProductList from '../../_Components/ProductList';
import SearchBar from './SearchBar';
import { Product } from '../../_utils/productsAPI';

interface AllProductsClientProps {
products: Product[];
categories: string[];
}

export default function AllProductsClient({ products, categories }: AllProductsClientProps) {
const [loadCount, setLoadCount] = useState(2);
const [searchQuery, setSearchQuery] = useState('');
const [selectedCategory, setSelectedCategory] = useState('');

const filteredProducts = products.filter((product) => {
return (
product.attributes.title.toLowerCase().includes(searchQuery.toLowerCase()) &&
(selectedCategory === '' || product.attributes.category === selectedCategory)
);
});

const handleLoadMore = () => {
setLoadCount(loadCount + 2);
};

return (
<div className="px-10 md:px-20 pt-10">
<SearchBar
searchQuery={searchQuery}
selectedCategory={selectedCategory}
setSearchQuery={setSearchQuery}
setSelectedCategory={setSelectedCategory}
categories={categories}
/>
<h1 className="text-3xl font-bold text-black my-4">All Products</h1>
<ProductList productList={filteredProducts.slice(0, loadCount)} />
{loadCount < filteredProducts.length && (
<button
onClick={handleLoadMore}
className="w-full p-3 text-gray-500 rounded-lg transition duration-200 bg-transparent hover:shadow-md"
>
Load More
</button>
)}
</div>
);
}
93 changes: 11 additions & 82 deletions client/src/app/all-products/page.tsx
Original file line number Diff line number Diff line change
@@ -1,87 +1,16 @@
'use client';
import React, { useCallback, useEffect, useState } from 'react';
import ProductList from '../_Components/ProductList'; // Assuming ProductList is a shared component
import { getLatestProducts, StrapiResponse } from '../_utils/productsAPI';
import React from 'react';
import { AxiosResponse } from 'axios';
import { Product } from '../_utils/productsAPI';
import SearchBar from './_components/SearchBar';

export default function AllProductsPage() {
const [productList, setProductList] = useState<Product[]>([]);
const [visibleProducts, setVisibleProducts] = useState<Product[]>([]);
const [loadCount, setLoadCount] = useState(2); // Start by loading 10 products at a time
const [searchQuery, setSearchQuery] = useState(''); //search input
const [selectedCategory, setSelectedCategory] = useState(''); //category
const [categories, setCategories] = useState<string[]>([]); //categories I render in dropdown

useEffect(() => {
fetchAllProducts();
}, []);

useEffect(() => {
loadMoreProducts();
}, [loadCount]);

const fetchAllProducts = async () => {
try {
const res: AxiosResponse<StrapiResponse<Product[]>> =
await getLatestProducts();
setProductList(res.data.data); // Store all products
setVisibleProducts(res.data.data.slice(0, loadCount)); // Initially load 10 products

// get unique categories from products
const uniqueCategories = Array.from(
new Set(res.data.data.map((product) => product.attributes.category)),
);
setCategories(uniqueCategories);
} catch (err) {
console.error(err);
}
};

//Filter products based on SearchBar
const filteredProducts = productList.filter((product) => {
return (
product.attributes.title
.toLowerCase()
.includes(searchQuery.toLowerCase()) &&
(selectedCategory === '' ||
product.attributes.category === selectedCategory)
);
});

const loadMoreProducts = /*useCallback(*/ () => {
const newVisibleCount = visibleProducts.length + 2;
setVisibleProducts(productList.slice(0, newVisibleCount));
}; /*, [visibleProducts, productList])*/

const handleLoadMore = () => {
setLoadCount(loadCount + 2); // Load 10 more products
};
import { getLatestProducts, Product, StrapiResponse } from '../_utils/productsAPI';
import AllProductsClient from './_components/AllProductsClient';

export default async function AllProductsPage() {
const res: AxiosResponse<StrapiResponse<Product[]>> = await getLatestProducts();
const productList = res.data.data;
const categories = Array.from(
new Set(productList.map((product) => product.attributes.category)),
);

return (
<div className="px-10 md:px-20 pt-10">
{/* Search Bar */}
<SearchBar
searchQuery={searchQuery}
selectedCategory={selectedCategory}
setSearchQuery={setSearchQuery}
setSelectedCategory={setSelectedCategory}
categories={categories}
/>

{/* All products section */}
<h1 className="text-3xl font-bold text-black my-4">All Products</h1>
<ProductList productList={filteredProducts.slice(0, loadCount)} />

{visibleProducts.length < productList.length && (
<button
onClick={handleLoadMore}
className="w-full p-3 text-gray-500 rounded-lg transition duration-200 bg-transparent hover:shadow-md"
>
Load More
</button>
)}
</div>
<AllProductsClient products={productList} categories={categories} />
);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
`use client`;
'use client';
import React, { useContext } from 'react';
import { Product } from '../../../_utils/productsAPI';
import { HiOutlineShoppingCart } from 'react-icons/hi';
Expand Down
12 changes: 12 additions & 0 deletions client/src/app/product-details/[productid]/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import SkeletonProductInfo from './_components/SkeletonProductInfo';

export default function Loading() {
return (
<div className="text-black px-10 py-8 md:px-28">
<div className="grid grid-cols-1 sm:grid-cols-2 mt-10 gap-5">
<div className="w-[400px] h-[225px] bg-slate-200 rounded-lg animate-pulse" />
<SkeletonProductInfo />
</div>
</div>
);
}
Loading