"use client"; import { useEffect, useRef, useState } from "react"; import { useParams } from "next/navigation"; import { ShieldCheck, AlertTriangle, XCircle, Ban, Star, Globe, Building2, Recycle, Flag, Phone, Share2, Download, ShieldHalf,Info, } from "lucide-react"; import { T, LANGS, detectLang, type Lang } from "@/lib/i18n"; import { apiBase } from "@/lib/api"; import { Pill, Package } from "lucide-react"; import { Mail } from "lucide-react"; type State = "genuine" | "unverified" | "not_found" | "recalled"; const STYLES: Record = { genuine: { bg: "bg-emerald-500", ring: "ring-emerald-200", icon: ShieldCheck, color: "text-emerald-600" }, unverified: { bg: "bg-orange-500", ring: "ring-orange-200", icon: AlertTriangle, color: "text-orange-600" }, not_found: { bg: "bg-rose-500", ring: "ring-rose-200", icon: XCircle, color: "text-rose-600" }, recalled: { bg: "bg-rose-600", ring: "ring-rose-200", icon: Ban, color: "text-rose-700" }, }; export default function VerifyPage() { const { qr_code } = useParams<{ qr_code: string }>(); const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [lang, setLang] = useState("en"); const logged = useRef(false); const now = new Date(); const scanDate = now.toLocaleDateString(); const scanTime = now.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", }); useEffect(() => { setLang(detectLang()); }, []); useEffect(() => { fetch(`${apiBase}/verify/${qr_code}`) .then((r) => r.json()) .then((d) => { setData(d); // Log exactly one scan per real page view. The ref guard prevents // React Strict Mode's double-mount from logging twice in dev. if (!logged.current && d?.state && d.state !== "not_found") { logged.current = true; fetch(`${apiBase}/scans/log?qr_code=${encodeURIComponent(qr_code)}`, { method: "POST", }).catch(() => {}); } }) .catch(() => setData({ state: "not_found" })) .finally(() => setLoading(false)); }, [qr_code]); const t = T[lang]; const state: State = data?.state ?? "not_found"; const cfg = STYLES[state]; const Icon = cfg.icon; const product = data?.product ?? {}; const brand = data?.brand; const batch = data?.batch; const trust = data?.trust_score ?? product.trust_score ?? 0; const stars = Math.round((trust / 100) * 5); const compliance = (product.compliance ?? []).filter((c: any) => c?.type); const stateMsg: Record = { genuine: t.genuine_msg, unverified: t.unverified_msg, not_found: t.not_found_msg, recalled: t.recalled_msg, }; if (loading) return (
); return (
{/* Brand header + language selector */}
{brand?.logo_url ? :
} {brand?.name ?? "VerifyPack"}
{/* Status hero */}
{/* Icon */}
{/* Content */}

{t[state]}

{stateMsg[state]}

{/* Scan Date & Time */}
{t.scan_on}: {scanDate} • {scanTime}
{state !== "not_found" && ( <> {/* Product */}
{/* Product Image */}
{product.image_url ? ( {product.name} ) : (
)}
{/* Product Details */}
{/* Product Name */}

{product.name}

{/* Brand */}

{brand?.name ?? "VerifyPack"}

{/* Category */}
{product.category}
{/* Bottom Details */}
{product.details?.form_type || "-"}
{product.details?.net_weight || "-"}
{/* Trust score */} {trust > 0 && (
{/* Score Circle */}
{trust}
/100
{/* Score Details */}

{t.trust_score}

{trust >= 90 ? t.excellent : trust >= 75 ? t.good : trust >= 50 ? t.average : t.low}
{[...Array(5)].map((_, i) => ( ))}
)} {/* Batch info */} {batch && (

{t.sku}

{product.sku || "-"}

{t.batch_no}

{batch.number}

{t.mfg_date}

{fmt(batch.mfg_date)}

{t.expiry_date}

{fmt(batch.expiry_date)}

{t.net_weight}

{product.details?.net_weight || "-"}

{t.gtin}

{product.details?.gtin || "-"}

)} {/* Manufacturer */} {product.manufacturer?.company && (
{/* Icon */}
{/* Details */}

{t.manufactured_by}

{product.manufacturer.company}

{product.manufacturer.plant && (

{t.plant}{" "} {product.manufacturer.plant}

)} {product.manufacturer.country && (

{t.country_origin}{" "} {product.manufacturer.country}

)}
{/* Actions */}
)} {/* Compliance badges */} {compliance.length > 0 && (
{compliance.map((c: any, i: number) => ( {c.type}{c.number ? ` · ${c.number}` : ""} ))}
)} {/* Recycling */}

{t.recycling_info}

{product.recycling_info || t.recycling_info}

{/* Actions */}
} label="Report Issue" /> } label="Contact Brand" /> } label="Share Product" onClick={() => navigator.share?.({ title: product.name, url: location.href, }) } /> } label="Download Info" onClick={() => { document.title = `${product.name}-${batch?.number || "Product"}`; window.print(); }} />
)}
Powered by VerifyPack
| |

© {new Date().getFullYear()} VerifyPack. All rights reserved.

); } function fmt(d?: string) { if (!d || d === "None") return "—"; const dt = new Date(d); return isNaN(+dt) ? d : dt.toLocaleDateString(); } function Section({ title, icon, children }: any) { return (

{icon}{title}

{children}
); } function Row({ label, value }: { label: string; value?: string }) { if (!value || value === "None") return null; return (
{label} {value}
); } function Action({ icon, label, onClick }: any) { return ( ); }