"use client"; import { useEffect, useState } from "react"; import { useParams } from "next/navigation"; import Link from "next/link"; import { getSpaceIdFromCookie, getCartKey } from "@/lib/spaces"; const API_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000/api"; interface ProductVariant { name: string; sku: string; provider: string; price: number; } interface Product { slug: string; name: string; description: string; category: string; product_type: string; tags: string[]; image_url: string; base_price: number; variants: ProductVariant[]; is_active: boolean; } const MOCKUP_TYPES = [ { type: "shirt", label: "T-Shirt", icon: "👕" }, { type: "sticker", label: "Sticker", icon: "🏷️" }, { type: "print", label: "Art Print", icon: "🖼️" }, ]; function getMockupType(productType: string): string { if (productType.includes("shirt") || productType.includes("tee") || productType.includes("hoodie")) return "shirt"; if (productType.includes("sticker")) return "sticker"; if (productType.includes("print")) return "print"; return "shirt"; } export default function ProductPage() { const params = useParams(); const slug = params.slug as string; const [product, setProduct] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [selectedVariant, setSelectedVariant] = useState(null); const [selectedMockup, setSelectedMockup] = useState("shirt"); const [quantity, setQuantity] = useState(1); const [addingToCart, setAddingToCart] = useState(false); const [addedToCart, setAddedToCart] = useState(false); const [imageLoading, setImageLoading] = useState(true); useEffect(() => { async function fetchProduct() { try { const res = await fetch(`${API_URL}/products/${slug}`); if (!res.ok) { setError(res.status === 404 ? "Product not found" : "Failed to load product"); return; } const data = await res.json(); setProduct(data); if (data.variants?.length > 0) { setSelectedVariant(data.variants[0]); } setSelectedMockup(getMockupType(data.product_type)); } catch { setError("Failed to load product"); } finally { setLoading(false); } } if (slug) fetchProduct(); }, [slug]); const getOrCreateCart = async (): Promise => { let cartId = localStorage.getItem(getCartKey(getSpaceIdFromCookie())); if (cartId) { try { const res = await fetch(`${API_URL}/cart/${cartId}`); if (res.ok) return cartId; } catch { /* cart expired */ } } try { const res = await fetch(`${API_URL}/cart`, { method: "POST", headers: { "Content-Type": "application/json" }, }); if (res.ok) { const data = await res.json(); cartId = data.id; localStorage.setItem(getCartKey(getSpaceIdFromCookie()), cartId!); return cartId; } } catch { return null; } return null; }; const handleAddToCart = async () => { if (!product || !selectedVariant) return; setAddingToCart(true); try { const cartId = await getOrCreateCart(); if (!cartId) { alert("Failed to create cart"); return; } const res = await fetch(`${API_URL}/cart/${cartId}/items`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ product_slug: product.slug, variant_sku: selectedVariant.sku, quantity, }), }); if (res.ok) { setAddedToCart(true); setTimeout(() => setAddedToCart(false), 3000); } else { const data = await res.json(); alert(data.detail || "Failed to add to cart"); } } catch { alert("Failed to add to cart"); } finally { setAddingToCart(false); } }; if (loading) { return (
{/* Image skeleton */}
{/* Content skeleton */}
); } if (error || !product) { return (

{error || "Product not found"}

Back to Products
); } return (
{/* Breadcrumb */}
{/* Product Image Section */}
{/* Main mockup image */}
{imageLoading && (
Loading mockup...
)} {`${product.name} setImageLoading(false)} onError={() => setImageLoading(false)} />
{/* Mockup type switcher */}
{MOCKUP_TYPES.map((mt) => ( ))}
{/* Raw design preview */}

Original Design

{`${product.name}
{/* Product Details Section */}
{product.category} — {product.product_type}

{product.name}

{product.description}

${selectedVariant?.price.toFixed(2) || product.base_price.toFixed(2)} + shipping
{/* Variant Selection */} {product.variants && product.variants.length > 1 && (
{product.variants.map((variant) => ( ))}
)} {/* Quantity */}
{quantity}
{/* Add to Cart */} {addedToCart && ( View Cart → )} {/* Product info */}
Printed and shipped by Printful. Fulfilled on demand — no waste.
Standard shipping: 5–12 business days. Express available at checkout.
Bella + Canvas 3001 — premium 100% combed cotton, retail fit.
{/* Tags */} {product.tags?.length > 0 && (
{product.tags.map((tag) => ( #{tag} ))}
)}
); }