Skip to content

Commit 44977b9

Browse files
committed
fix: renamed items to products, added skeleton to product tiles, grid now pulls data from backend
Renamed "Items" to "Products" but the component remain the same. Added skeleton to show that products are loading. The grid of tiles now pulls data from backend server. If no backend server is running. It will use dummy data.
1 parent 6edba1b commit 44977b9

File tree

8 files changed

+391
-62
lines changed

8 files changed

+391
-62
lines changed

Diff for: .env.example

+4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ GITHUB_ID = ""
1717
GITHUB_SECRET = ""
1818

1919

20+
# URL for the backend API
21+
22+
NEXT_PUBLIC_API_URL="http://localhost:3030"
23+
2024
# Firebase Config Keys
2125

2226
NEXT_PUBLIC_FIREBASE_API_KEY=

Diff for: components/CategorySection/CategorySection.jsx

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
"use client";
22

3-
import GridItemComp from "./GridItemComp.jsx";
3+
import ProductTileGrid from "./ProductTileGrid.jsx";
44

55
export default function CategorySection({ sectionHeader }) {
66
return (
7-
<section className="flex flex-col justify-container items-center">
8-
<div className="p-4 mx-2 md:mx-6 xl:mx-16 container">
9-
<h1 className="py-5 text-2xl font-semibold tracking-tight leading-relaxed">
7+
<section className="justify-container flex flex-col items-center">
8+
<div className="container mx-2 p-4 md:mx-6 xl:mx-16">
9+
<h1 className="py-5 text-2xl font-semibold leading-relaxed tracking-tight">
1010
{sectionHeader}
1111
</h1>
12-
<GridItemComp />
12+
<ProductTileGrid />
1313
</div>
1414
</section>
1515
);

Diff for: components/CategorySection/GridItemComp.jsx

-23
This file was deleted.

Diff for: components/CategorySection/ItemComp.jsx

-34
This file was deleted.

Diff for: components/CategorySection/ProductTile.jsx

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"use client";
2+
3+
import Skeleton from "@/components/ui/skeleton";
4+
5+
function ProductTile({ productName, price, imageURL, id }) {
6+
const cost = price?.toFixed(2);
7+
8+
return (
9+
<div className="group relative">
10+
<a href={"/product/" + id}>
11+
<div className="aspect-h-1 aspect-w-1 lg:aspect-none w-full overflow-hidden rounded-md group-hover:opacity-75 lg:h-56">
12+
<img
13+
src={
14+
imageURL
15+
? imageURL
16+
: "https://img.freepik.com/premium-vector/default-image-icon-vector-missing-picture-page-website-design-mobile-app-no-photo-available_87543-11093.jpg"
17+
}
18+
className="h-full w-full object-cover object-center lg:h-full lg:w-full"
19+
/>
20+
</div>
21+
<div className="mt-4 flex justify-between gap-4">
22+
<div className="w-full text-sm text-neutral-700">{productName}</div>
23+
<div className="text-sm font-medium text-neutral-900">${cost}</div>
24+
</div>
25+
</a>
26+
</div>
27+
);
28+
}
29+
30+
// Skeleton for loading state
31+
function ProductTileSkeleton() {
32+
return (
33+
<div className="group relative">
34+
<div>
35+
<Skeleton className="aspect-h-1 aspect-w-1 lg:aspect-none w-full overflow-hidden rounded-md group-hover:opacity-75 lg:h-56" />
36+
</div>
37+
<div className="mt-4 flex justify-between">
38+
<div className="w-full">
39+
<Skeleton className="h-4 w-full" />
40+
</div>
41+
</div>
42+
</div>
43+
);
44+
}
45+
46+
export { ProductTile, ProductTileSkeleton };

Diff for: components/CategorySection/ProductTileGrid.jsx

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { useEffect, useState } from "react";
2+
import { ProductTile, ProductTileSkeleton } from "./ProductTile.jsx";
3+
4+
export default function ProductTileGrid() {
5+
const [products, setProducts] = useState([]);
6+
const [isLoading, setIsLoading] = useState(true);
7+
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
8+
useEffect(() => {
9+
const fetchData = async () => {
10+
try {
11+
// Fetch data from the API. Need to use the apiUrl variable from the env and have the backend server running.
12+
const response = await fetch({ apiUrl } + "/products");
13+
if (!response.ok) throw new Error("Network response was not ok");
14+
const data = await response.json();
15+
setProducts(data);
16+
} catch (error) {
17+
console.error(
18+
"Fetching data failed, falling back to backup data:",
19+
error,
20+
);
21+
22+
// Fetch backup dummy data from GitHub
23+
const backupData = await fetch(
24+
"https://raw.githubusercontent.com/4human-org/murphy-ecommerce-backend/main/util/products.json",
25+
).then((res) => res.json());
26+
setProducts(backupData);
27+
} finally {
28+
setIsLoading(false); // Set loading to false when data is fetched
29+
}
30+
};
31+
fetchData();
32+
}, []);
33+
34+
return (
35+
<div className="grid grid-cols-1 gap-4 pb-10 sm:grid-cols-2 sm:gap-3 md:grid-cols-3 lg:grid-cols-6 xl:gap-5">
36+
{isLoading
37+
? Array.from({ length: 12 }).map((_, index) => (
38+
<ProductTileSkeleton key={index} />
39+
))
40+
: products.map(({ name, price, imagesUrl, id }) => (
41+
<ProductTile
42+
key={name}
43+
productName={name}
44+
price={price}
45+
imageURL={imagesUrl[0]}
46+
id={id}
47+
48+
/>
49+
))}
50+
</div>
51+
);
52+
}

Diff for: components/ui/skeleton.jsx

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { cn } from "@/lib/utils";
2+
3+
function Skeleton({ className, ...props }) {
4+
return (
5+
<div
6+
className={cn("animate-pulse rounded-md bg-muted", className)}
7+
{...props}
8+
/>
9+
);
10+
}
11+
12+
export default Skeleton;

0 commit comments

Comments
 (0)