productSlug fixes
CI / test (push) Successful in 7s

This commit is contained in:
2026-01-26 07:10:37 -05:00
parent b09ccc542e
commit fb2effca47
2 changed files with 44 additions and 11 deletions
+9 -3
View File
@@ -295,7 +295,7 @@ function buildAssemblyRows(params: {
rows.push({ rows.push({
key: `${groupLabel}-${paths.complete}`, key: `${groupLabel}-${paths.complete}`,
kind: "category", kind: "category",
label: "Complete Assembly (Fastest)", label: "Completed Lower (Fastest)",
categoryId: paths.complete, categoryId: paths.complete,
indent: 0, indent: 0,
pill: mode === "complete" ? "Selected" : undefined, pill: mode === "complete" ? "Selected" : undefined,
@@ -1625,9 +1625,15 @@ function GunbuilderPageContent() {
</div> </div>
{selectedPart ? ( {selectedPart ? (
<div className="text-[0.7rem] text-zinc-500 line-clamp-1"> <Link
href={`/parts/p/${canonicalPlatform}/${category.id}/${selectedPart.id}-${selectedPart.name
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/(^-|-$)/g, "")}`}
className="text-[0.7rem] text-zinc-500 hover:text-amber-300 hover:underline line-clamp-1 transition-colors"
>
{selectedPart.name} {selectedPart.name}
</div> </Link>
) : ( ) : (
<div className="text-[0.7rem] text-zinc-600"> <div className="text-[0.7rem] text-zinc-600">
No part selected No part selected
@@ -18,6 +18,7 @@ type OfferFromApi = {
price?: number | null; price?: number | null;
originalPrice?: number | null; originalPrice?: number | null;
buyUrl?: string | null; buyUrl?: string | null;
buyShortUrl?: string | null;
inStock?: boolean | null; inStock?: boolean | null;
lastUpdated?: string | null; lastUpdated?: string | null;
}; };
@@ -29,6 +30,7 @@ type GunbuilderProductFromApi = {
platform: string; platform: string;
partRole: string; partRole: string;
price: number | null; price: number | null;
caliber?: string | null;
// Optional (legacy fallback label if product.offers is missing) // Optional (legacy fallback label if product.offers is missing)
merchantName?: string | null; merchantName?: string | null;
@@ -37,6 +39,7 @@ type GunbuilderProductFromApi = {
mainImageUrl?: string | null; mainImageUrl?: string | null;
buyUrl?: string | null; buyUrl?: string | null;
buyShortUrl?: string | null;
inStock?: boolean | null; inStock?: boolean | null;
shortDescription?: string | null; shortDescription?: string | null;
@@ -249,6 +252,12 @@ export default function ProductDetailsPage() {
} }
const data: GunbuilderProductFromApi = await res.json(); const data: GunbuilderProductFromApi = await res.json();
console.log('[DEBUG] Product from API:', {
id: data.id,
name: data.name,
buyUrl: data.buyUrl,
buyShortUrl: data.buyShortUrl
});
setProduct(data); setProduct(data);
} catch (err: any) { } catch (err: any) {
if (err?.name === "AbortError") return; if (err?.name === "AbortError") return;
@@ -286,6 +295,7 @@ export default function ProductDetailsPage() {
} }
const data: OfferFromApi[] = await res.json(); const data: OfferFromApi[] = await res.json();
console.log('[DEBUG] Offers from API:', data);
setOffersFromApi(Array.isArray(data) ? data : []); setOffersFromApi(Array.isArray(data) ? data : []);
} catch (err: any) { } catch (err: any) {
if (err?.name === "AbortError") return; if (err?.name === "AbortError") return;
@@ -336,14 +346,23 @@ export default function ProductDetailsPage() {
* - Fallback to legacy single-offer derived from product if needed * - Fallback to legacy single-offer derived from product if needed
*/ */
const offers = useMemo(() => { const offers = useMemo(() => {
if (offersFromApi.length > 0) return sortOffers(offersFromApi); if (offersFromApi.length > 0) {
console.log('[DEBUG] Using offers from API, first offer:', offersFromApi[0]);
const fallbackShortUrl = product?.buyShortUrl ?? null;
const normalized = offersFromApi.map((offer) => ({
...offer,
buyShortUrl: offer.buyShortUrl || fallbackShortUrl,
}));
return sortOffers(normalized);
}
if (product?.buyUrl) { if (product?.buyUrl || product?.buyShortUrl) {
console.log('[DEBUG] Using fallback from product, buyShortUrl:', product.buyShortUrl, 'buyUrl:', product.buyUrl);
return sortOffers([ return sortOffers([
{ {
merchantName: product.merchantName ?? "Retailer", merchantName: product.merchantName ?? "Retailer",
price: product.price, price: product.price,
buyUrl: product.buyUrl, buyUrl: product.buyShortUrl || product.buyUrl,
inStock: product.inStock ?? true, inStock: product.inStock ?? true,
}, },
]); ]);
@@ -565,6 +584,14 @@ export default function ProductDetailsPage() {
{product.platform || platform} {product.platform || platform}
</span> </span>
</span> </span>
{product.caliber && (
<span className="rounded border border-zinc-800 bg-zinc-900/60 px-2 py-1 text-zinc-300">
Caliber:{" "}
<span className="font-semibold text-zinc-100">
{product.caliber}
</span>
</span>
)}
<span className="rounded border border-zinc-800 bg-zinc-900/60 px-2 py-1 text-zinc-300"> <span className="rounded border border-zinc-800 bg-zinc-900/60 px-2 py-1 text-zinc-300">
Role:{" "} Role:{" "}
<span className="font-semibold text-zinc-100"> <span className="font-semibold text-zinc-100">
@@ -666,9 +693,9 @@ export default function ProductDetailsPage() {
{o.price != null ? `$${o.price.toFixed(2)}` : "—"} {o.price != null ? `$${o.price.toFixed(2)}` : "—"}
</td> </td>
<td className="px-3 py-2 text-right"> <td className="px-3 py-2 text-right">
{o.buyUrl ? ( {(o.buyShortUrl || o.buyUrl) ? (
<a <a
href={o.buyUrl} href={o.buyShortUrl || o.buyUrl}
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
className="inline-flex items-center justify-center rounded-md bg-amber-400 px-3 py-1.5 text-xs font-semibold text-black hover:bg-amber-300" className="inline-flex items-center justify-center rounded-md bg-amber-400 px-3 py-1.5 text-xs font-semibold text-black hover:bg-amber-300"
@@ -692,10 +719,10 @@ export default function ProductDetailsPage() {
)} )}
{/* Best Offer CTA */} {/* Best Offer CTA */}
{bestOffer?.buyUrl ? ( {(bestOffer?.buyShortUrl || bestOffer?.buyUrl) ? (
<div className="mt-3 flex justify-end"> <div className="mt-3 flex justify-end">
<a <a
href={bestOffer.buyUrl} href={bestOffer.buyShortUrl || bestOffer.buyUrl}
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
className="rounded-md border border-zinc-700 bg-zinc-900/70 px-4 py-2 text-sm font-semibold text-zinc-200 hover:bg-zinc-800" className="rounded-md border border-zinc-700 bg-zinc-900/70 px-4 py-2 text-sm font-semibold text-zinc-200 hover:bg-zinc-800"
@@ -753,4 +780,4 @@ export default function ProductDetailsPage() {
</div> </div>
</main> </main>
); );
} }