The product detail page atDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/dlampatricio/florale/llms.txt
Use this file to discover all available pages before exploring further.
/producto/[id] is a dynamic Next.js server component that renders a full-page view of a single artisanal product. It resolves the product ID from the URL, fetches the product alongside all other products and categories in parallel, computes a related-products list from the same category, and delegates rendering to the ProductDetailClient component. If no product matches the requested ID, the page calls notFound() to show Next.js’s built-in 404 screen.
Route structure
The file lives atapp/(app)/producto/[id]/page.tsx. The [id] segment is a UUID that maps directly to the id column in the Supabase products table. Because the params object is a Promise in Next.js 15’s async params API, the page awaits it before using the value.
Parallel data fetching
Three Supabase requests fire concurrently so none of them blocks the others:| Call | Purpose |
|---|---|
getProductById(id) | Primary product data for the detail view |
getProducts() | Full product list used to derive related products |
getCategories() | Category list used to find the current product’s category label |
generateMetadata for SEO
generateMetadata is an exported async function co-located with the page. Next.js calls it before rendering to inject <title>, <meta description>, and social-graph tags into the document <head>. It also awaits params and calls getProductById. Because all fetches in lib/products.ts use cache: 'no-store', each call goes directly to the Supabase REST API without caching.
Metadata fields produced
<title>
{product.name} — Florale. Falls back to "Producto no encontrado — Florale" if the ID is invalid.<meta description>
product.description if present; otherwise the default string "Producto artesanal único en Florale.".Open Graph image
The product’s Supabase Storage URL, declared at 1200 × 630 px. Omitted if
product.image is empty.Twitter card
summary_large_image card type, same title, description, and image as Open Graph.Handling missing products
WhengetProductById returns undefined (the product doesn’t exist, or its ID is malformed), the page calls Next.js’s notFound() helper, which immediately stops rendering and surfaces the nearest not-found.tsx boundary — or the framework’s default 404 page if none is defined.
generateMetadata also short-circuits gracefully: it returns only { title: 'Producto no encontrado — Florale' } so search engines never index a broken metadata frame.
Related products
Related products are products that share the samecategoryId as the current product but have a different id. The filtering is done in JavaScript after getProducts() returns, so no extra database query is needed.
ProductDetailClient receives this array and renders up to six items in a 2-column (mobile) / 3-column (desktop) grid beneath the main product detail — labeled “También te puede gustar” (“You might also like”). Each related card is a plain <Link> to /producto/{rp.id} with the same square image crop used in the catalog.
ProductDetailClient component
ProductDetailClient is the 'use client' rendering boundary. It handles all interactive state that cannot live in a server component:
Add to cart
The Añadir al carrito button calls
addItem(product.id) from useCartStore and fires a toast notification. The button switches to a green Agregado confirmation state after the first click (tracked by local useState).Go to cart shortcut
If
itemCount > 0, a secondary Ir al carrito button appears below the primary CTA, linking directly to /carrito.Share product
The share button calls
navigator.share() on supported devices. On desktop it falls back to copying the URL to the clipboard and shows a brief ✓ Copied state.Complete data flow
Next.js resolves the dynamic segment
The URL
/producto/abc-123 makes params resolve to { id: 'abc-123' }.generateMetadata runs first
Fetches
getProductById('abc-123') and returns the <head> metadata before rendering.Page component fetches in parallel
getProductById, getProducts, and getCategories all fire concurrently via Promise.all.notFound() or render
If the product is missing,
notFound() halts rendering. Otherwise, related products are filtered and passed to ProductDetailClient.