"use client"; import { useState, useEffect, useCallback, useMemo, Suspense } from "react"; import { useSearchParams, useRouter } from "next/navigation"; import { Search, ArrowLeft, Loader2, SlidersHorizontal, Database, ArrowUpDown, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Sheet, SheetContent, SheetTrigger, SheetHeader, SheetTitle, } from "@/components/ui/sheet"; import { ProductCard } from "@/app/components/ProductCard"; import { ProductCardSkeleton } from "@/app/components/ProductCardSkeleton"; import { DonateCard } from "@/app/components/DonateCard"; import { FilterSidebar } from "@/app/components/FilterSidebar"; import { SearchBar } from "@/app/components/SearchBar"; import { Pagination } from "@/app/components/Pagination"; import { useProductFilters } from "@/hooks/useProductFilters"; import { useSearchCache } from "@/hooks/useSearchCache"; import { useStorageMonitor } from "@/hooks/useStorageMonitor"; import { useCacheOnlyMode } from "@/hooks/useCacheOnlyMode"; import { CacheOnlySwitch } from "@/app/components/CacheOnlySwitch"; import { detectUserCountry } from "@/lib/geolocation"; import { FilterRecord } from "@/lib/cacheManager"; import { ProductFilters } from "@/lib/types"; import Link from "next/link"; import { Product, sortProducts } from "@/lib/openFoodFactsApi"; // Função para converter ProductFilters para FilterRecord function convertFiltersToRecord(filters: ProductFilters): FilterRecord { const record: FilterRecord = {}; Object.entries(filters).forEach(([key, value]) => { if (value !== undefined && value !== null) { record[key] = value as FilterRecord[string]; } }); return record; } function ResultadosContent() { const searchParams = useSearchParams(); const router = useRouter(); const query = searchParams.get("q") || ""; const countryParam = searchParams.get("country") || ""; // Estados principais const [allProducts, setAllProducts] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [detectedCountry, setDetectedCountry] = useState(""); const [viewMode, setViewMode] = useState<"grid" | "list">("grid"); const [forceRefresh, setForceRefresh] = useState(false); // Estados de paginação const [currentPage, setCurrentPage] = useState(1); const [itemsPerPage, setItemsPerPage] = useState(20); // Estados para modais mobile const [showFilters, setShowFilters] = useState(false); const [showSort, setShowSort] = useState(false); const [showCacheInfo, setShowCacheInfo] = useState(false); // Hook de cache const { state: cacheState, getCachedSearch, saveSearch, clearCache, setError: setCacheError, setLoading: setCacheLoading, } = useSearchCache(); // Hook de filtros const { filters, updateFilters, clearFilters, hasActiveFilters, setTotalResults, setIsLoading, } = useProductFilters(); // Hook de monitoramento de storage const { stats: storageStats, refreshStats: refreshStorageStats } = useStorageMonitor(); // Hook de modo cache-only const { cacheOnly } = useCacheOnlyMode(); // Detectar país do usuário useEffect(() => { const detectCountry = async () => { try { const country = await detectUserCountry(true); setDetectedCountry(country.code); } catch { setDetectedCountry("br"); } }; detectCountry(); }, []); const loadProducts = useCallback( async (forceRefresh: boolean = false) => { try { setLoading(true); setError(null); setIsLoading(true); setCacheLoading(true); const countryToUse = detectedCountry || countryParam || "br"; // Verificar cache primeiro (se não for refresh forçado) if (!forceRefresh) { const cached = getCachedSearch( query, countryToUse, convertFiltersToRecord(filters) ); if (cached) { console.log("Usando dados do cache"); setAllProducts(cached.products); setTotalResults(cached.totalCount); setLoading(false); setIsLoading(false); setCacheLoading(false); return; } } console.log("Fazendo busca completa na API"); // Construir parâmetros da API const params = new URLSearchParams({ q: query, country: countryToUse, page: "1", page_size: "1000", // Buscar até 1000 produtos por página }); if (cacheOnly) { params.append("cache_only", "true"); } // Fazer busca na API const response = await fetch(`/api/search?${params.toString()}`); const data = await response.json(); if (!response.ok) { throw new Error(data.error || "Erro ao buscar produtos"); } const result = { products: data.products || [], totalCount: data.count || 0, actualPages: 1, }; // Salvar no cache const searchId = saveSearch( query, countryToUse, result.products, result.totalCount, convertFiltersToRecord(filters) ); console.log(`Busca salva no cache com ID: ${searchId}`); setAllProducts(result.products); setTotalResults(result.totalCount); setCurrentPage(1); // Voltar para primeira página } catch (err) { const errorMessage = err instanceof Error ? err.message : "Erro ao buscar produtos"; setError(errorMessage); setCacheError(errorMessage); } finally { setLoading(false); setIsLoading(false); setCacheLoading(false); } }, [ query, detectedCountry, countryParam, filters, cacheOnly, getCachedSearch, saveSearch, setIsLoading, setTotalResults, setCacheLoading, setCacheError, ] ); useEffect(() => { if (query) { loadProducts(forceRefresh); setForceRefresh(false); } }, [query, countryParam, detectedCountry, loadProducts, forceRefresh]); // Sincronizar produtos com cache do servidor useEffect(() => { if (allProducts.length > 0) { // Enviar produtos para cache em background fetch("/api/cache/sync", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ products: allProducts }), }).catch((err) => console.error("Erro ao sincronizar cache:", err)); } }, [allProducts]); // Produtos filtrados e ordenados para exibição const filteredAndSortedProducts = useMemo(() => { let filtered = [...allProducts]; // Aplicar filtros aqui (implementar conforme necessário) // Por enquanto, apenas ordenação if (filters.sortBy) { filtered = sortProducts( filtered, filters.sortBy, filters.sortOrder || "asc" ); } return filtered; }, [allProducts, filters.sortBy, filters.sortOrder]); // Produtos da página atual const currentPageProducts = useMemo(() => { const startIndex = (currentPage - 1) * itemsPerPage; const endIndex = startIndex + itemsPerPage; return filteredAndSortedProducts.slice(startIndex, endIndex); }, [filteredAndSortedProducts, currentPage, itemsPerPage]); // Cálculos de paginação const totalPages = Math.ceil(filteredAndSortedProducts.length / itemsPerPage); const totalItems = filteredAndSortedProducts.length; const handleRefresh = useCallback(() => { setForceRefresh(true); }, []); const handleClearCache = useCallback(() => { clearCache( query, detectedCountry || countryParam || "br", convertFiltersToRecord(filters) ); setForceRefresh(true); }, [clearCache, query, detectedCountry, countryParam, filters]); const handleRefreshStorageStats = useCallback(() => { refreshStorageStats(); }, [refreshStorageStats]); const handleSortChange = useCallback( (sortBy: string) => { updateFilters({ sortBy: sortBy as | "relevance" | "nutrition_grade" | "name" | "brand" | "energy" | "fat" | "sugars" | "completeness" | "with_images" | "with_nutrition" | "eco_score" | "nova_group", }); }, [updateFilters] ); if (!query) { return (

Nenhuma busca realizada

Digite um termo de busca para encontrar produtos

); } return (
{/* Header Mobile Otimizado */}
{/* Botões de ação - apenas filtros e ordenar */}
{/* Header Desktop (mantido) */}

Resultados para "{query}"

{!loading && totalItems > 0 && (

{totalItems} produto{totalItems !== 1 ? "s" : ""} encontrado {totalItems !== 1 ? "s" : ""} {detectedCountry && ` em ${detectedCountry}`} {cacheState.isCached && ( Cache Local )} {storageStats && ( • Storage: {storageStats?.storageSizeMB || 0} MB ( {storageStats?.totalProducts || 0} produtos) )}

)}
{/* Switch Cache-Only em produção */} {/* Barra de Pesquisa - Desktop */}
{/* Mobile Controls */}
{ if (value) { const currentArray = (filters[key as keyof typeof filters] as string[]) || []; updateFilters({ [key]: currentArray.filter((item) => item !== value), }); } else { updateFilters({ [key]: undefined }); } }} />
{/* Layout principal */}
{/* Sidebar de filtros - Desktop */}
{ if (value) { const currentArray = (filters[key as keyof typeof filters] as string[]) || []; updateFilters({ [key]: currentArray.filter((item) => item !== value), }); } else { updateFilters({ [key]: undefined }); } }} />
{/* Conteúdo principal */}
{/* Loading Inicial */} {loading && (
{/* Indicador de progresso do cache */} {cacheState.progress && (

Carregando todos os resultados...

Página {cacheState.progress?.currentPage || 0} de{" "} {cacheState.progress?.totalPages || 0} • {cacheState.progress?.loadedProducts || 0} produtos carregados

)} {/* Skeleton dos produtos */}
{[...Array(8)].map((_, i) => ( ))}
)} {/* Erro */} {error && !loading && (

Erro ao buscar produtos

{error}

)} {/* Sem Resultados */} {!loading && !error && totalItems === 0 && (

Nenhum produto encontrado

Tente ajustar os filtros ou buscar com outros termos

{hasActiveFilters && ( )}
)} {/* Grid de Produtos */} {!loading && totalItems > 0 && ( <>
{/* Card de Doação - sempre primeiro */}
{currentPageProducts.map((product, index) => (
))}
{/* Paginação */} {totalPages > 1 && (
)} {/* Informações adicionais */}

{cacheState.isCached ? ( <> Dados carregados do cache local • ) : ( <> Dados carregados da API externa • )} {storageStats && ( <> {" • "} )}

)}
{/* Modais Mobile */} {/* Modal de Filtros */}

Filtros

{/* Modal de Ordenação */}

Ordenar por

{[ { value: "relevance", label: "Relevância" }, { value: "nutrition_grade", label: "Classificação Nutricional", }, { value: "name", label: "Nome" }, { value: "energy", label: "Energia" }, { value: "with_images", label: "Com Imagens" }, ].map((option) => ( ))}
{/* Modal de Informações de Cache */} Informações do Cache

Produtos em cache:

{storageStats?.totalProducts ?? 0}

Tamanho do storage:

{storageStats?.storageSizeMB ? `${storageStats?.storageSizeMB} MB` : "0 KB"}

Última atualização:

{storageStats?.lastUpdated ? new Date(storageStats?.lastUpdated).toLocaleDateString( "pt-BR" ) : "Nunca"}

{/* Botão de limpar cache */}
); } export default function ResultadosPage() { return (
} > ); }