From c99c82c8379749f244deab4fe44bafea2272a152 Mon Sep 17 00:00:00 2001 From: Evandro Fonseca Junior Date: Tue, 20 Jan 2026 12:10:56 -0300 Subject: [PATCH] refactor(product page, api): improve error handling and enhance type safety - Refactored error handling in the product page to simplify catch block. - Updated type definitions in openFoodFactsApi for better type safety. - Introduced utility functions for search type detection and barcode validation. --- app/produto/[barcode]/page.tsx | 22 +++++++++++----------- lib/openFoodFactsApi.ts | 22 +++++++++++++++++++--- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/app/produto/[barcode]/page.tsx b/app/produto/[barcode]/page.tsx index 0c868fd..9dd51e2 100644 --- a/app/produto/[barcode]/page.tsx +++ b/app/produto/[barcode]/page.tsx @@ -3,22 +3,22 @@ import React, { useState, useEffect, useCallback } from "react"; import { useParams, useRouter } from "next/navigation"; import { motion, AnimatePresence } from "framer-motion"; -import { - ArrowLeft, AlertCircle, AlertTriangle, - Database, Globe, ShieldCheck, Beaker, ClipboardList +import { + ArrowLeft, + AlertCircle, + AlertTriangle, + Database, + Globe, + ShieldCheck, + Beaker, + ClipboardList, } from "lucide-react"; import { Button } from "@/components/ui/button"; import Image from "next/image"; -import { - searchByBarcode, - Product, - formatNutritionData, -} from "@/lib/openFoodFactsApi"; -import Link from "next/link"; +import { searchByBarcode, Product, formatNutritionData } from "@/lib/openFoodFactsApi"; import { NutriScoreBadge, NovaBadge } from "@/app/components/NutriScoreBadge"; import { NutritionGrid } from "@/app/components/NutritionBar"; import { ProductSkeleton } from "@/app/components/ProductSkeleton"; -import { cn } from "@/lib/utils"; export default function ProdutoPage() { const params = useParams(); @@ -36,7 +36,7 @@ export default function ProdutoPage() { const result = await searchByBarcode(barcode); if (result) setProduct(result); else setError("Produto não catalogado na base ODbL."); - } catch (err) { + } catch { setError("Falha crítica na sincronização de dados."); } finally { setLoading(false); diff --git a/lib/openFoodFactsApi.ts b/lib/openFoodFactsApi.ts index 98c09fa..195a0e0 100644 --- a/lib/openFoodFactsApi.ts +++ b/lib/openFoodFactsApi.ts @@ -17,7 +17,7 @@ export interface Product { sugars_100g?: number; fiber_100g?: number; sodium_100g?: number; - [key: string]: any; // Flexibilidade para outros nutrientes + [key: string]: number | undefined; // Flexibilidade para outros nutrientes mantendo tipagem segura }; nutrition_grades?: string; nova_group?: number; @@ -240,8 +240,11 @@ export function formatNutritionData(p: Product) { const n = p.nutriments; if (!n) return null; - const fmt = (v: any, d: number = 1) => { - const num = parseFloat(v); + const fmt = (v: unknown, d: number = 1) => { + const num = + typeof v === "number" + ? v + : parseFloat(typeof v === "string" ? v : String(v)); return isNaN(num) ? "N/A" : num.toFixed(d); }; @@ -253,4 +256,17 @@ export function formatNutritionData(p: Product) { sugars: n.sugars_100g ? `${fmt(n.sugars_100g)}g` : "N/A", sodium: n.sodium_100g ? `${fmt(n.sodium_100g, 2)}g` : "N/A", }; +} + +/** + * Utilitários de detecção e validação de busca + */ +export function detectSearchType(input: string): "barcode" | "name" { + const cleaned = input.replace(/\D/g, ""); + return cleaned.length >= 8 && cleaned.length <= 13 ? "barcode" : "name"; +} + +export function isValidBarcode(barcode: string): boolean { + const cleaned = barcode.replace(/\D/g, ""); + return cleaned.length >= 8 && cleaned.length <= 13 && /^\d+$/.test(cleaned); } \ No newline at end of file