From 34e915f9044569ea7ed567c114747c951cb73912 Mon Sep 17 00:00:00 2001 From: Sean Date: Mon, 22 Dec 2025 08:36:19 -0500 Subject: [PATCH] fixed layouts, routing and the platform switcher. --- app/{ => (app)}/(builder)/builder/layout.tsx | 0 app/{ => (app)}/(builder)/builder/page.tsx | 7 +- app/(app)/(builder)/parts/[partRole]/page.tsx | 16 + .../[productSlug]/page.tsx | 2 +- .../_old_prod_details/data.ts | 0 .../_old_prod_details/page.tsx | 2 +- .../parts/__DELETE[partRole]}/page.tsx | 0 .../parts/_components/buildDetailHref.ts | 0 .../[partRole]/[productSlug]/page.tsx | 0 .../parts/p/[platform]/[partRole]/page.tsx | 0 app/{(builder) => (app)}/layout.tsx | 0 app/{ => (app)}/vault/[uuid]/edit/page.tsx | 99 ++--- app/{ => (app)}/vault/page.tsx | 0 app/login/page.tsx | 2 +- components/Banner.tsx | 2 +- components/BuilderNav.tsx | 13 +- components/PricingHistoryGraph.tsx | 2 +- components/RetailersList.tsx | 2 +- components/parts/PartsBrowseClient.tsx | 338 ++++++++---------- components/parts/PlatformSwitcher.tsx | 32 +- components/parts/ProductDetailPageClient.tsx | 5 +- 21 files changed, 254 insertions(+), 268 deletions(-) rename app/{ => (app)}/(builder)/builder/layout.tsx (100%) rename app/{ => (app)}/(builder)/builder/page.tsx (99%) create mode 100644 app/(app)/(builder)/parts/[partRole]/page.tsx rename app/{(builder)/parts/[partRole] => (app)/(builder)/parts/__DELETE[partRole]}/[productSlug]/page.tsx (99%) rename app/{(builder)/parts/[partRole] => (app)/(builder)/parts/__DELETE[partRole]}/_old_prod_details/data.ts (100%) rename app/{(builder)/parts/[partRole] => (app)/(builder)/parts/__DELETE[partRole]}/_old_prod_details/page.tsx (99%) rename app/{(builder)/parts/[partRole] => (app)/(builder)/parts/__DELETE[partRole]}/page.tsx (100%) rename app/{ => (app)}/(builder)/parts/_components/buildDetailHref.ts (100%) rename app/{ => (app)}/(builder)/parts/p/[platform]/[partRole]/[productSlug]/page.tsx (100%) rename app/{ => (app)}/(builder)/parts/p/[platform]/[partRole]/page.tsx (100%) rename app/{(builder) => (app)}/layout.tsx (100%) rename app/{ => (app)}/vault/[uuid]/edit/page.tsx (87%) rename app/{ => (app)}/vault/page.tsx (100%) diff --git a/app/(builder)/builder/layout.tsx b/app/(app)/(builder)/builder/layout.tsx similarity index 100% rename from app/(builder)/builder/layout.tsx rename to app/(app)/(builder)/builder/layout.tsx diff --git a/app/(builder)/builder/page.tsx b/app/(app)/(builder)/builder/page.tsx similarity index 99% rename from app/(builder)/builder/page.tsx rename to app/(app)/(builder)/builder/page.tsx index c95cf75..84203cf 100644 --- a/app/(builder)/builder/page.tsx +++ b/app/(app)/(builder)/builder/page.tsx @@ -936,7 +936,7 @@ export default function GunbuilderPage() { BATTL BUILDERS

- BattlBuilder Early Access + Builder Early Access

Explore components from trusted brands, choose one part per @@ -1317,10 +1317,7 @@ export default function GunbuilderPage() { ) : hasParts ? ( ; +}) { + const { partRole } = await params; + + return ( + + ); +} diff --git a/app/(builder)/parts/[partRole]/[productSlug]/page.tsx b/app/(app)/(builder)/parts/__DELETE[partRole]/[productSlug]/page.tsx similarity index 99% rename from app/(builder)/parts/[partRole]/[productSlug]/page.tsx rename to app/(app)/(builder)/parts/__DELETE[partRole]/[productSlug]/page.tsx index ea5b164..cc359f1 100644 --- a/app/(builder)/parts/[partRole]/[productSlug]/page.tsx +++ b/app/(app)/(builder)/parts/__DELETE[partRole]/[productSlug]/page.tsx @@ -204,7 +204,7 @@ export default function ProductDetailsPage() { href={{ pathname: "/builder", query: platform ? { platform } : {} }} className="hover:text-zinc-300" > - The Armory + The Builder / - ← Back to The Armory + ← Back to the Builder

diff --git a/app/(builder)/parts/[partRole]/page.tsx b/app/(app)/(builder)/parts/__DELETE[partRole]/page.tsx similarity index 100% rename from app/(builder)/parts/[partRole]/page.tsx rename to app/(app)/(builder)/parts/__DELETE[partRole]/page.tsx diff --git a/app/(builder)/parts/_components/buildDetailHref.ts b/app/(app)/(builder)/parts/_components/buildDetailHref.ts similarity index 100% rename from app/(builder)/parts/_components/buildDetailHref.ts rename to app/(app)/(builder)/parts/_components/buildDetailHref.ts diff --git a/app/(builder)/parts/p/[platform]/[partRole]/[productSlug]/page.tsx b/app/(app)/(builder)/parts/p/[platform]/[partRole]/[productSlug]/page.tsx similarity index 100% rename from app/(builder)/parts/p/[platform]/[partRole]/[productSlug]/page.tsx rename to app/(app)/(builder)/parts/p/[platform]/[partRole]/[productSlug]/page.tsx diff --git a/app/(builder)/parts/p/[platform]/[partRole]/page.tsx b/app/(app)/(builder)/parts/p/[platform]/[partRole]/page.tsx similarity index 100% rename from app/(builder)/parts/p/[platform]/[partRole]/page.tsx rename to app/(app)/(builder)/parts/p/[platform]/[partRole]/page.tsx diff --git a/app/(builder)/layout.tsx b/app/(app)/layout.tsx similarity index 100% rename from app/(builder)/layout.tsx rename to app/(app)/layout.tsx diff --git a/app/vault/[uuid]/edit/page.tsx b/app/(app)/vault/[uuid]/edit/page.tsx similarity index 87% rename from app/vault/[uuid]/edit/page.tsx rename to app/(app)/vault/[uuid]/edit/page.tsx index d29c0df..57904c2 100644 --- a/app/vault/[uuid]/edit/page.tsx +++ b/app/(app)/vault/[uuid]/edit/page.tsx @@ -17,6 +17,7 @@ type BuildDto = { slot: string; productId: number; productName?: string | null; + productBrand?: string | null; bestPrice?: number | string | null; // backend may serialize BigDecimal as string }>; photos?: BuildPhotoDto[]; @@ -68,7 +69,7 @@ export default function VaultBuildEditPage() { return () => { previews.forEach((p) => URL.revokeObjectURL(p.url)); }; - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const authed = !!token; @@ -311,54 +312,62 @@ export default function VaultBuildEditPage() { {!build.items?.length ? (
No parts saved yet.
) : ( -
- {Object.entries( - build.items.reduce>( - (acc, it) => { - (acc[it.slot] ||= []).push(it); - return acc; - }, - {} - ) - ).map(([slot, items]) => ( -
-
- {slot} -
+
+ {/* header */} +
+
Product
+
Component (Role)
+
Brand
+
Price
+
-
- {items.map((it, idx) => ( -
-
-
- {it.productName ?? `Product #${it.productId}`} -
-
- ID:{" "} - {it.productId} -
+ {/* rows */} + {build.items + .slice() + .sort((a, b) => a.slot.localeCompare(b.slot)) + .map((it, idx) => { + const price = + typeof it.bestPrice === "number" + ? it.bestPrice + : it.bestPrice + ? Number(it.bestPrice) + : null; + + return ( +
+ {/* Product */} +
+
+ {it.productName ?? `Product #${it.productId}`}
- -
-
- {typeof it.bestPrice === "number" - ? `$${it.bestPrice.toFixed(2)}` - : it.bestPrice - ? `$${Number(it.bestPrice).toFixed(2)}` - : "—"} -
-
- Best price -
+
+ ID:{" "} + {it.productId}
- ))} -
-
- ))} + + {/* Component (Role) */} +
+ {it.slot} +
+ + {/* Brand */} +
+ {it.productBrand ?? "—"} +
+ + {/* Price */} +
+ {price != null && !Number.isNaN(price) + ? `$${price.toFixed(2)}` + : "—"} +
+
+ ); + })}
)}
diff --git a/app/vault/page.tsx b/app/(app)/vault/page.tsx similarity index 100% rename from app/vault/page.tsx rename to app/(app)/vault/page.tsx diff --git a/app/login/page.tsx b/app/login/page.tsx index 911b5fa..3ed3ba0 100644 --- a/app/login/page.tsx +++ b/app/login/page.tsx @@ -91,7 +91,7 @@ export default function LoginPage() {

- Log In to The Armory + Log In to The Builder

Use your beta credentials to get back to your saved builds. diff --git a/components/Banner.tsx b/components/Banner.tsx index 4bcc1b1..b1ddad5 100644 --- a/components/Banner.tsx +++ b/components/Banner.tsx @@ -11,7 +11,7 @@ export function Banner() { Early Access Beta - You're using an early-access prototype of The Armory. Data, + You're using an early-access prototype of the Builder. Data, pricing, and available parts are still evolving.

diff --git a/components/BuilderNav.tsx b/components/BuilderNav.tsx index 1d9bad1..d34b36f 100644 --- a/components/BuilderNav.tsx +++ b/components/BuilderNav.tsx @@ -57,6 +57,8 @@ export function BuilderNav({ activeCategoryId }: BuilderNavProps) { const renderDropdown = (label: string, items: Category[]) => { if (!items.length) return null; + const platform = searchParams.get("platform"); + return (
{/* Dropdown trigger */} @@ -80,7 +82,10 @@ export function BuilderNav({ activeCategoryId }: BuilderNavProps) { return (
  • - Viewing + + Viewing + {CATEGORIES.find((c) => c.id === currentCategory)?.name ?? @@ -188,4 +195,4 @@ export function BuilderNav({ activeCategoryId }: BuilderNavProps) { ); } -export default BuilderNav; \ No newline at end of file +export default BuilderNav; diff --git a/components/PricingHistoryGraph.tsx b/components/PricingHistoryGraph.tsx index 161e432..c4c29d7 100644 --- a/components/PricingHistoryGraph.tsx +++ b/components/PricingHistoryGraph.tsx @@ -1,6 +1,6 @@ "use client"; -import type { PriceHistoryPoint } from "@/app/(builder)/parts/[partRole]/_old_prod_details/data"; +import type { PriceHistoryPoint } from "@/app/(app)/(builder)/parts/__DELETE[partRole]/_old_prod_details/data"; interface PricingHistoryGraphProps { data: PriceHistoryPoint[]; diff --git a/components/RetailersList.tsx b/components/RetailersList.tsx index bec4b13..77df26a 100644 --- a/components/RetailersList.tsx +++ b/components/RetailersList.tsx @@ -1,6 +1,6 @@ "use client"; -import type { RetailerOffer } from "@/app/(builder)/parts/[partRole]/_old_prod_details/data"; +import type { RetailerOffer } from "@/app/(app)/(builder)/parts/__DELETE[partRole]/_old_prod_details/data"; interface RetailersListProps { retailers: RetailerOffer[]; diff --git a/components/parts/PartsBrowseClient.tsx b/components/parts/PartsBrowseClient.tsx index 1f0f948..2c412c6 100644 --- a/components/parts/PartsBrowseClient.tsx +++ b/components/parts/PartsBrowseClient.tsx @@ -1,8 +1,3 @@ -"use client"; - -import Link from "next/link"; -import { usePathname } from "next/navigation"; - /** * PartsBrowseClient * ----------------------------------------------------------------------------- @@ -13,8 +8,11 @@ import { usePathname } from "next/navigation"; * - layout + list/card views */ +"use client"; + +import Link from "next/link"; import { useEffect, useMemo, useState } from "react"; -import { useRouter, useSearchParams } from "next/navigation"; +import { usePathname, useRouter, useSearchParams } from "next/navigation"; import Filters from "@/components/parts/Filters"; import SortBar from "@/components/parts/SortBar"; @@ -23,10 +21,7 @@ import Pagination from "@/components/parts/Pagination"; import PlatformSwitcher from "@/components/parts/PlatformSwitcher"; import type { CategoryId } from "@/types/gunbuilder"; -import { - PART_ROLE_TO_CATEGORY, - normalizePartRole, -} from "@/lib/catalogMappings"; +import { PART_ROLE_TO_CATEGORY, normalizePartRole } from "@/lib/catalogMappings"; type ViewMode = "card" | "list"; type SortOption = "relevance" | "price-asc" | "price-desc" | "brand-asc"; @@ -49,7 +44,7 @@ type UiPart = { brand: string; platform: string; partRole: string; - price: number; // normalized + price: number; imageUrl?: string; buyUrl?: string; inStock?: boolean; @@ -57,6 +52,7 @@ type UiPart = { const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL ?? "http://localhost:8080"; + const PAGE_SIZE = 24; function normalizeId(id: string | number) { @@ -92,8 +88,13 @@ export default function PartsBrowseClient(props: { const searchParams = useSearchParams(); const pathname = usePathname(); + // ✅ Builder-mode = /parts/p/... (detail routes) + const isBuilderMode = pathname.startsWith("/parts/p/"); + + // ✅ Respect prop override first, else read query, else fallback const effectivePlatform = props.platform ?? searchParams.get("platform") ?? "AR-15"; + const partRole = props.partRole; const [viewMode, setViewMode] = useState("list"); @@ -103,7 +104,7 @@ export default function PartsBrowseClient(props: { const [brandFilter, setBrandFilter] = useState([]); const [sortBy, setSortBy] = useState("relevance"); - const [searchQuery, setSearchQuery] = useState(""); + const [searchQuery, setSearchQuery] = useState(""); const [priceRange, setPriceRange] = useState<{ min: number | null; max: number | null; @@ -114,6 +115,9 @@ export default function PartsBrowseClient(props: { const [inStockOnly, setInStockOnly] = useState(false); const [currentPage, setCurrentPage] = useState(1); + // ---------------------------- + // Fetch parts + // ---------------------------- useEffect(() => { if (!partRole) return; @@ -128,26 +132,28 @@ export default function PartsBrowseClient(props: { search.set("platform", effectivePlatform); search.append("partRoles", partRole); - const url = `${API_BASE_URL}/api/v1/products?${search.toString()}`; - const res = await fetch(url, { signal: controller.signal }); + const res = await fetch( + `${API_BASE_URL}/api/v1/products?${search.toString()}`, + { signal: controller.signal } + ); if (!res.ok) throw new Error(`Failed to load products (${res.status})`); const data: GunbuilderProductFromApi[] = await res.json(); - const normalized: UiPart[] = data.map((p) => ({ - id: normalizeId(p.id), - name: p.name, - brand: p.brand, - platform: p.platform, - partRole: p.partRole, - price: p.price ?? 0, - imageUrl: p.mainImageUrl ?? undefined, - buyUrl: p.buyUrl ?? undefined, - inStock: p.inStock ?? true, - })); - - setParts(normalized); + setParts( + data.map((p) => ({ + id: normalizeId(p.id), + name: p.name, + brand: p.brand, + platform: p.platform, + partRole: p.partRole, + price: p.price ?? 0, + imageUrl: p.mainImageUrl ?? undefined, + buyUrl: p.buyUrl ?? undefined, + inStock: p.inStock ?? true, + })) + ); } catch (err: any) { if (err?.name === "AbortError") return; setError(err?.message ?? "Failed to load products"); @@ -160,6 +166,7 @@ export default function PartsBrowseClient(props: { return () => controller.abort(); }, [partRole, effectivePlatform]); + // Reset pagination on filters useEffect(() => { setCurrentPage(1); }, [ @@ -172,11 +179,14 @@ export default function PartsBrowseClient(props: { inStockOnly, ]); + // Reset scroll on route change useEffect(() => { - // Reset scroll position whenever we land on or change parts routes - window.scrollTo({ top: 0, left: 0, behavior: "auto" }); + window.scrollTo({ top: 0, behavior: "auto" }); }, [pathname]); + // ---------------------------- + // Derived values + // ---------------------------- const availableBrands = useMemo( () => Array.from(new Set(parts.map((p) => p.brand))) @@ -186,34 +196,27 @@ export default function PartsBrowseClient(props: { ); const priceBounds = useMemo(() => { - if (parts.length === 0) - return { min: null as number | null, max: null as number | null }; - - let min = Number.POSITIVE_INFINITY; - let max = Number.NEGATIVE_INFINITY; - - for (const p of parts) { - const price = p.price ?? 0; - if (price < min) min = price; - if (price > max) max = price; - } - - if (!Number.isFinite(min) || !Number.isFinite(max)) { - return { min: null as number | null, max: null as number | null }; - } - - return { min, max }; + if (!parts.length) return { min: null as number | null, max: null as number | null }; + return { + min: Math.min(...parts.map((p) => p.price)), + max: Math.max(...parts.map((p) => p.price)), + }; }, [parts]); + // Keep priceRange clamped to bounds when bounds change useEffect(() => { - if (priceBounds.min == null || priceBounds.max == null) return; - + const minBound = priceBounds.min; + const maxBound = priceBounds.max; + if (minBound == null || maxBound == null) return; + setPriceRange((prev) => { - const min = prev.min ?? priceBounds.min!; - const max = prev.max ?? priceBounds.max!; + const nextMin = prev.min == null ? minBound : Math.max(minBound, prev.min); + const nextMax = prev.max == null ? maxBound : Math.min(maxBound, prev.max); + + // Ensure min never exceeds max (in case user typed weird values) return { - min: Math.max(priceBounds.min!, Math.min(min, priceBounds.max!)), - max: Math.min(priceBounds.max!, Math.max(max, priceBounds.min!)), + min: Math.min(nextMin, nextMax), + max: Math.max(nextMin, nextMax), }; }); }, [priceBounds.min, priceBounds.max]); @@ -221,30 +224,24 @@ export default function PartsBrowseClient(props: { const filteredParts = useMemo(() => { let result = [...parts]; - const effectiveMin = priceRange.min ?? priceBounds.min; - const effectiveMax = priceRange.max ?? priceBounds.max; - if (effectiveMin != null && effectiveMax != null) { - result = result.filter( - (p) => p.price >= effectiveMin && p.price <= effectiveMax - ); - } + if (priceRange.min != null) + result = result.filter((p) => p.price >= priceRange.min!); + if (priceRange.max != null) + result = result.filter((p) => p.price <= priceRange.max!); - if (brandFilter.length > 0) { + if (brandFilter.length) result = result.filter((p) => brandFilter.includes(p.brand)); - } - if (inStockOnly) { + if (inStockOnly) result = result.filter((p) => p.inStock ?? true); - } - const q = searchQuery.trim().toLowerCase(); - if (q) { - result = result.filter((p) => { - const name = (p.name ?? "").toLowerCase(); - const brand = (p.brand ?? "").toLowerCase(); - return name.includes(q) || brand.includes(q); - }); - } + const q = searchQuery.toLowerCase().trim(); + if (q) + result = result.filter( + (p) => + p.name.toLowerCase().includes(q) || + p.brand.toLowerCase().includes(q) + ); switch (sortBy) { case "price-asc": @@ -262,47 +259,25 @@ export default function PartsBrowseClient(props: { } return result; - }, [ - parts, - brandFilter, - sortBy, - searchQuery, - priceRange, - priceBounds.min, - priceBounds.max, - inStockOnly, - ]); + }, [parts, brandFilter, sortBy, searchQuery, priceRange, inStockOnly]); - const totalPages = useMemo( - () => - filteredParts.length === 0 - ? 1 - : Math.ceil(filteredParts.length / PAGE_SIZE), - [filteredParts.length] + const totalPages = Math.max(1, Math.ceil(filteredParts.length / PAGE_SIZE)); + const paginatedParts = filteredParts.slice( + (currentPage - 1) * PAGE_SIZE, + currentPage * PAGE_SIZE ); - const paginatedParts = useMemo(() => { - if (filteredParts.length === 0) return []; - const startIndex = (currentPage - 1) * PAGE_SIZE; - return filteredParts.slice(startIndex, startIndex + PAGE_SIZE); - }, [filteredParts, currentPage]); - - const visibleRange = useMemo(() => { - if (filteredParts.length === 0) return { start: 0, end: 0 }; - const start = (currentPage - 1) * PAGE_SIZE + 1; - const end = Math.min( - start + paginatedParts.length - 1, - filteredParts.length - ); - return { start, end }; - }, [filteredParts.length, currentPage, paginatedParts.length]); + const visibleRange = { + start: filteredParts.length ? (currentPage - 1) * PAGE_SIZE + 1 : 0, + end: Math.min(currentPage * PAGE_SIZE, filteredParts.length), + }; const headingTitle = props.title ?? `${partRole.replaceAll("-", " ")} parts`; - const headingSubtitle = - props.subtitle ?? - "Browse available parts pulled from your Ballistic backend."; + const headingSubtitle = props.subtitle ?? "Browse available parts."; - // ✅ Add-to-build handler: deep-links into builder’s existing ?select= logic + // ---------------------------- + // Add → Builder handoff + // ---------------------------- const handleAddToBuild = (p: UiPart) => { const normalizedRole = normalizePartRole(partRole); const categoryId: CategoryId | null = @@ -327,22 +302,22 @@ export default function PartsBrowseClient(props: { return (
    -
    -
    -
    -

    - Battl Builders -

    +
    +
    +

    + Battl Builders +

    -

    - {headingTitle}{" "} - {effectivePlatform} -

    +

    + {headingTitle}{" "} + {effectivePlatform} +

    -

    - {headingSubtitle} -

    +

    + {headingSubtitle} +

    + {!isBuilderMode && (
    -
    - - {!loading && !error && ( -
    - - ← Back to Build - - - {parts.length > 0 && ( -
    - - -
    - )} -
    )}
    + + {isBuilderMode && ( +
    + + ← Back to Build + + + {/* Optional: view toggle in builder-mode too */} + {parts.length > 0 && ( +
    + + +
    + )} +
    + )}
    -
    -
    - {!loading && !error && parts.length > 0 && ( -
    ); -} +} \ No newline at end of file diff --git a/components/parts/PlatformSwitcher.tsx b/components/parts/PlatformSwitcher.tsx index 27b0687..3cc2f1e 100644 --- a/components/parts/PlatformSwitcher.tsx +++ b/components/parts/PlatformSwitcher.tsx @@ -3,23 +3,10 @@ import { useMemo } from "react"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; -/** - * PlatformSwitcher - * - * Works on BOTH: - * - /parts/[partRole] (default browse route) - * - /parts/[platform]/[partRole] (canonical route) - * - * Behavior: - * - If you're on /parts/[partRole], switching platform navigates to /parts/[platform]/[partRole] - * (so your URL becomes explicit/shareable). - * - If you're already on /parts/[platform]/[partRole], it swaps the platform segment. - */ export default function PlatformSwitcher(props: { currentPlatform: string; partRole: string; - // If true, we keep query params when switching (nice for sort/paging) - preserveQuery?: boolean; + preserveQuery?: boolean; // keep sort/page/etc (but NOT platform) }) { const { currentPlatform, partRole, preserveQuery = true } = props; @@ -29,13 +16,19 @@ export default function PlatformSwitcher(props: { const queryString = useMemo(() => { if (!preserveQuery) return ""; - const q = searchParams?.toString(); + + // Copy params and DROP legacy platform param so it can’t conflict with the path + const sp = new URLSearchParams(searchParams?.toString()); + sp.delete("platform"); + + const q = sp.toString(); return q ? `?${q}` : ""; }, [searchParams, preserveQuery]); function go(platform: string) { - // We always navigate to canonical to keep it simple & consistent - router.push(`/parts/${encodeURIComponent(platform)}/${encodeURIComponent(partRole)}${queryString}`); + router.replace( + `/parts/p/${encodeURIComponent(platform)}/${encodeURIComponent(partRole)}${queryString}` + ); } return ( @@ -52,9 +45,8 @@ export default function PlatformSwitcher(props: { - - {pathname} - + {/* debug only */} + {pathname}
  • ); } \ No newline at end of file diff --git a/components/parts/ProductDetailPageClient.tsx b/components/parts/ProductDetailPageClient.tsx index 7c9beb2..ee995b8 100644 --- a/components/parts/ProductDetailPageClient.tsx +++ b/components/parts/ProductDetailPageClient.tsx @@ -122,11 +122,12 @@ export default function ProductDetailPageClient(props: {
    - + {/* temp disabling while I rework the routing. */} + {/* */}
    - + ← Back to list