"use client"; import React, { useState, useEffect, useCallback } from "react"; import { useParams, useRouter } from "next/navigation"; import { motion, AnimatePresence } from "framer-motion"; import { ArrowLeft, AlertCircle, AlertTriangle, Beaker, Heart, Share2, } from "lucide-react"; import Image from "next/image"; import { searchByBarcode, Product } from "@/lib/openFoodFactsApi"; import { getEnergyKcal, getNutrientValue } from "@/lib/nutrition"; import { ProductSkeleton } from "@/app/components/ProductSkeleton"; /* ── Nutri-Score letter circle ─────────────────────────────────────────── */ const NUTRI_BG: Record = { a: "#038141", b: "#85bb2f", c: "#fecb02", d: "#ee8100", e: "#e63e11", }; const NUTRI_INK: Record = { a: "#fff", b: "#fff", c: "#3a2a00", d: "#fff", e: "#fff", }; const GRADE_DESC: Record = { a: "Excelente qualidade nutricional", b: "Boa qualidade nutricional", c: "Qualidade regular", d: "Qualidade ruim", e: "Qualidade muito ruim", }; const NOVA_DESC: Record = { 1: "Alimento in natura ou minimamente processado.", 2: "Ingrediente culinário processado.", 3: "Alimento processado com adição de sal, açúcar ou óleo.", 4: "Ultraprocessado — formulação industrial com aditivos.", }; const NOVA_BG: Record = { 1: "#038141", 2: "#fecb02", 3: "#ee8100", 4: "#e63e11", }; const NOVA_INK: Record = { 1: "#fff", 2: "#3a2a00", 3: "#fff", 4: "#fff", }; function NutriLetter({ grade, size = 92 }: { grade: string; size?: number }) { const g = grade?.toLowerCase(); if (!NUTRI_BG[g]) return null; return (
{g.toUpperCase()}
); } /* ── Nutri-Scale row (A–E with active highlight) ────────────────────────── */ function NutriScale({ active, size = "sm" }: { active: string; size?: "sm" | "md" }) { const px = size === "sm" ? 18 : 26; const fs = size === "sm" ? 9 : 12; return (
{["a", "b", "c", "d", "e"].map((l) => (
{l.toUpperCase()}
))}
); } /* ── NOVA pill ───────────────────────────────────────────────────────────── */ function NovaPill({ group }: { group: number }) { const labels: Record = { 1: "Não processado", 2: "Ingrediente culinário", 3: "Processado", 4: "Ultraprocessado", }; return (
{group} NOVA · {labels[group] || "Desconhecido"}
); } export default function ProdutoPage() { const params = useParams(); const router = useRouter(); const barcode = params.barcode as string; const [product, setProduct] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const searchProduct = useCallback(async () => { try { setLoading(true); setError(null); const result = await searchByBarcode(barcode); if (result) setProduct(result); else setError("Produto não encontrado na base de dados."); } catch { setError("Falha ao carregar os dados do produto."); } finally { setLoading(false); } }, [barcode]); useEffect(() => { if (barcode) searchProduct(); }, [barcode, searchProduct]); if (loading) return ; /* ── Error state ─────────────────────────────────────────────────────── */ if (error || !product) { return (

Produto não encontrado

{error}

); } const imageUrl = product.image_front_url || product.image_url || null; const grade = product.nutrition_grades?.toLowerCase(); const nova = product.nova_group; const gradeDesc = grade ? GRADE_DESC[grade] : null; const novaDesc = nova ? NOVA_DESC[nova] : null; /* ── Nutrition bars — % of daily reference ─────────────────────────── */ const dailyRef: Record = { "energy-kcal_100g": 2000, fat_100g: 70, saturated_fat_100g: 20, sugars_100g: 90, salt_100g: 6, fiber_100g: 30, proteins_100g: 50, }; const nutriments = product.nutriments || {}; const nutritionRows = [ { key: "energy-kcal_100g", label: "Energia", unit: "kcal" }, { key: "fat_100g", label: "Gorduras totais", unit: "g" }, { key: "saturated_fat_100g",label: "Gorduras saturadas", unit: "g" }, { key: "sugars_100g", label: "Açúcares", unit: "g" }, { key: "salt_100g", label: "Sal", unit: "g" }, { key: "fiber_100g", label: "Fibras", unit: "g" }, { key: "proteins_100g", label: "Proteínas", unit: "g" }, ]; const allergenTags = product.allergens_tags || []; return (
{/* ── Top bar ──────────────────────────────────────────────────────── */}
EAN · {product.code}
{/* ── Hero: image + headline + Nutri-Score ────────────────────────── */}
{/* Product image */} {imageUrl ? ( {product.product_name ) : (
Sem imagem
)}
{/* Right column — headline + score cards */} {/* Brand eyebrow */}
{product.brands || "Marca desconhecida"}
{/* Product name */}

{product.product_name || "Produto sem nome"}

{/* ── Nutri-Score hero metric ─────────────────────────────────── */} {grade && (
Nutri‑Score
{gradeDesc || `Nota ${grade.toUpperCase()}`}
)} {/* ── NOVA row ─────────────────────────────────────────────────── */} {nova && ( {novaDesc && (
{novaDesc}
)}
)} {/* ── Allergens ────────────────────────────────────────────────── */} {allergenTags.length > 0 && (
Contém alérgenos
{allergenTags.map((tag) => ( {tag.replace(/en:|pt:/g, "")} ))}
)}
{/* ── Nutrition composition section ───────────────────────────────── */}

Composição nutricional

por 100g · %VD de referência
{nutritionRows.map((row) => { const raw = nutriments[row.key as keyof typeof nutriments]; const v = row.key === "energy-kcal_100g" ? getEnergyKcal(nutriments) : getNutrientValue(raw); if (v == null) return null; const ref = dailyRef[row.key] || 100; const pct = Math.min(100, Math.round((v / ref) * 100)); const isHigh = pct > 40 && ["fat_100g", "saturated_fat_100g", "sugars_100g", "salt_100g"].includes(row.key); const isGood = ["fiber_100g", "proteins_100g"].includes(row.key); const barColor = isHigh ? "#e63e11" : isGood ? "var(--accent, oklch(52% 0.11 155))" : "var(--ink-500, #7a7269)"; return (
{row.label} {v} {row.unit}
{pct}% VD
); })}
{/* Ingredients */} {product.ingredients_text && (

Lista de Ingredientes

{product.ingredients_text}

)} {/* ── Footer attribution ─────────────────────────────────────────── */}
Dados fornecidos pela comunidade Open Food Facts sob licença ODbL. {product.countries && ` · Região: ${product.countries}`}
); }