From 3eeca771d1aa4272cef64547f72a79b5e779dcb1 Mon Sep 17 00:00:00 2001 From: Getabalewkemaw Date: Wed, 12 Nov 2025 13:29:13 +0300 Subject: [PATCH] Adding products details page --- app/admin/stock/page.tsx | 45 ++ app/products/[id]/page.tsx | 162 ++++++++ app/products/page.tsx | 238 +++++++++++ .../products/StockManagementDashboard.tsx | 392 ++++++++++++++++++ lib/api/products.ts | 45 ++ lib/hooks/useProducts.ts | 1 + types/product.ts | 6 + 7 files changed, 889 insertions(+) create mode 100644 app/admin/stock/page.tsx create mode 100644 app/products/[id]/page.tsx create mode 100644 app/products/page.tsx create mode 100644 components/products/StockManagementDashboard.tsx diff --git a/app/admin/stock/page.tsx b/app/admin/stock/page.tsx new file mode 100644 index 0000000..e9721a8 --- /dev/null +++ b/app/admin/stock/page.tsx @@ -0,0 +1,45 @@ +/** + * Stock Management Page + * Full page for managing inventory and stock levels + */ + +"use client"; + +import { useAuth } from "@/lib/hooks/useAuth"; +import { useRouter } from "next/navigation"; +import { useEffect } from "react"; +import Navbar from "@/components/NavBar"; +import StockManagementDashboard from "@/components/products/StockManagementDashboard"; + +export default function StockManagementPage() { + const { isAdmin, isLoading } = useAuth(); + const router = useRouter(); + + useEffect(() => { + if (!isLoading && !isAdmin) { + router.push("/dashboard"); + } + }, [isAdmin, isLoading, router]); + + if (isLoading) { + return ( +
+
+
+ ); + } + + if (!isAdmin) { + return null; + } + + return ( +
+ + +
+ +
+
+ ); +} diff --git a/app/products/[id]/page.tsx b/app/products/[id]/page.tsx new file mode 100644 index 0000000..90151a6 --- /dev/null +++ b/app/products/[id]/page.tsx @@ -0,0 +1,162 @@ +"use client"; + +import { useEffect, useMemo, useState } from "react"; +import { useParams } from "next/navigation"; +import Navbar from "@/components/NavBar"; +import { getPublicProductById, recordProductView } from "@/lib/api/products"; +import type { ProductWithDetails, ProductMedia } from "@/types/product"; + +export default function ProductDetailPage() { + const params = useParams<{ id: string }>(); + const id = params?.id; + const [product, setProduct] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [activeMediaIndex, setActiveMediaIndex] = useState(0); + + useEffect(() => { + const run = async () => { + if (!id) return; + setLoading(true); + setError(null); + const res = await getPublicProductById(id); + if (res.success && res.data) { + setProduct(res.data); + setActiveMediaIndex(0); + // fire-and-forget view record + recordProductView(id).catch(() => {}); + } else { + setError(res.error || "Failed to load product"); + } + setLoading(false); + }; + run(); + }, [id]); + + const renderMedia = (media: ProductMedia) => { + if (media.mediaType === "video") { + return