feat(search): implement paginated API fetching for large page sizes

- Removed upper limit on page_size validation to support requests >100
- Added automatic pagination logic to fetch from Open Food Facts API in chunks of 1000
- Accumulates products across multiple API pages until desired count is reached
- Updated error messages and product data fields for consistency
This commit is contained in:
Evandro Fonseca Junior committed 2025-10-09 07:16:26 -03:00
1 parent c173fec668
commit bfb58ef16e
34 files changed
+18630 -7815

No files matched your search

@@ -0,0 +1,96 @@
import { Heart, Check, Code } from "lucide-react";
import { Button } from "@/components/ui/button";
export function DonationSection() {
return (
<section className="py-20 bg-white dark:bg-gray-900">
<div className="max-w-4xl mx-auto px-4">
<div className="text-center mb-12">
<h2 className="text-3xl md:text-4xl font-bold mb-4">
Apoie a Transparência Alimentar
</h2>
<p className="text-xl text-gray-600 dark:text-gray-400">
Este projeto é mantido por desenvolvedores independentes que
acreditam que informação sobre saúde deve ser acessível a todos, sem
custo.
</p>
</div>
{/* Benefícios da Doação */}
<div className="bg-gradient-to-br from-blue-50 to-green-50 dark:from-gray-800 dark:to-gray-700 rounded-2xl p-8 mb-8">
<h3 className="text-2xl font-bold mb-6">Sua doação garante:</h3>
<div className="grid md:grid-cols-2 gap-4">
<div className="flex items-start gap-3">
<div className="w-6 h-6 rounded-full bg-blue-500 flex items-center justify-center flex-shrink-0 mt-1">
<Check className="w-4 h-4 text-white" />
</div>
<p>Servidores sempre disponíveis para consultas instantâneas</p>
</div>
<div className="flex items-start gap-3">
<div className="w-6 h-6 rounded-full bg-green-500 flex items-center justify-center flex-shrink-0 mt-1">
<Check className="w-4 h-4 text-white" />
</div>
<p>Novas funcionalidades baseadas em feedback da comunidade</p>
</div>
<div className="flex items-start gap-3">
<div className="w-6 h-6 rounded-full bg-purple-500 flex items-center justify-center flex-shrink-0 mt-1">
<Check className="w-4 h-4 text-white" />
</div>
<p>Manutenção contínua e atualizações de segurança</p>
</div>
<div className="flex items-start gap-3">
<div className="w-6 h-6 rounded-full bg-orange-500 flex items-center justify-center flex-shrink-0 mt-1">
<Check className="w-4 h-4 text-white" />
</div>
<p>Expansão do acesso a informações para mais pessoas</p>
</div>
</div>
</div>
{/* Humanização - História do Projeto */}
<div className="bg-gray-50 dark:bg-gray-800 rounded-2xl p-8 mb-8">
<div className="flex items-center gap-4 mb-4">
<div className="w-16 h-16 rounded-full bg-gradient-to-br from-blue-500 to-green-500 flex items-center justify-center">
<Code className="w-8 h-8 text-white" />
</div>
<div>
<h4 className="font-bold text-lg">
Ideia nascida de um estudante
</h4>
<p className="text-sm text-gray-600 dark:text-gray-400">
Mantido por NutriScan
</p>
</div>
</div>
<p className="text-gray-600 dark:text-gray-400">
O que começou como uma ideia de um estudante de Engenharia da
Computação se tornou uma ferramenta que democratiza o acesso à
informação nutricional. Cada doação ajuda a manter este projeto vivo
e acessível para todos.
</p>
</div>
{/* CTA de Doação */}
<div className="text-center">
<a
href="https://buymeacoffee.com/evandrotruuta"
target="_blank"
rel="noopener noreferrer"
className="inline-block"
>
<Button
size="lg"
className="bg-gradient-to-r from-blue-600 to-green-600 text-white px-8 py-6 text-lg hover:shadow-2xl transition-all"
>
<Heart className="w-5 h-5 mr-2" />
Doe/Donate
</Button>
</a>
<p className="mt-4 text-sm text-gray-500">
Qualquer valor faz diferença 💛
</p>
</div>
</div>
</section>
);
}
+46 -128
View File
@@ -1,174 +1,92 @@
import {
BarChart3,
Shield,
List,
Globe,
Zap,
Heart,
AlertTriangle,
CheckCircle,
} from "lucide-react";
import { Shield, Zap, Sparkles, Heart, CheckCircle } from "lucide-react";
const features = [
{
icon: BarChart3,
title: "Informações Nutricionais Completas",
icon: Shield,
title: "Dados da Comunidade Open Food Facts",
description:
"Calorias, proteínas, carboidratos, gorduras e muito mais. Dados precisos para cada produto.",
"Informações fornecidas pela maior base de dados colaborativa de alimentos do mundo, mantida por uma comunidade global de colaboradores voluntários.",
color: "text-blue-600 dark:text-blue-400",
bgColor: "bg-blue-50 dark:bg-blue-900/20",
iconBg: "bg-blue-100 dark:bg-blue-900/40",
},
{
icon: Shield,
title: "Nutri-Score e NOVA",
description:
"Classificações oficiais de qualidade nutricional para ajudar você a fazer escolhas mais saudáveis.",
color: "text-green-600 dark:text-green-400",
bgColor: "bg-green-50 dark:bg-green-900/20",
iconBg: "bg-green-100 dark:bg-green-900/40",
},
{
icon: List,
title: "Ingredientes Detalhados",
description:
"Lista completa de ingredientes, incluindo alérgenos e aditivos alimentares.",
color: "text-orange-600 dark:text-orange-400",
bgColor: "bg-orange-50 dark:bg-orange-900/20",
iconBg: "bg-orange-100 dark:bg-orange-900/40",
},
{
icon: Globe,
title: "Banco de Dados Global",
description:
"Acesso a milhões de produtos do Open Food Facts, a maior base de dados alimentar do mundo.",
color: "text-purple-600 dark:text-purple-400",
bgColor: "bg-purple-50 dark:bg-purple-900/20",
iconBg: "bg-purple-100 dark:bg-purple-900/40",
benefit: "Base colaborativa mundial",
},
{
icon: Zap,
title: "Busca Instantânea",
title: "Atualizações em Tempo Real",
description:
"Resultados em segundos. Digite o código de barras e obtenha informações imediatamente.",
color: "text-yellow-600 dark:text-yellow-400",
bgColor: "bg-yellow-50 dark:bg-yellow-900/20",
iconBg: "bg-yellow-100 dark:bg-yellow-900/40",
"Novos produtos são adicionados a cada minuto. Quando a indústria muda fórmulas, a comunidade atualiza — e você tem acesso instantâneo às informações mais recentes.",
color: "text-green-600 dark:text-green-400",
bgColor: "bg-green-50 dark:bg-green-900/20",
iconBg: "bg-green-100 dark:bg-green-900/40",
benefit: "Atualizado continuamente",
},
{
icon: Sparkles,
title: "Interface Intuitiva, Resultado Imediato",
description:
"Desenvolvido pensando em quem não tem tempo a perder. Digitalize, veja os dados e tome sua decisão em menos de 10 segundos.",
color: "text-purple-600 dark:text-purple-400",
bgColor: "bg-purple-50 dark:bg-purple-900/20",
iconBg: "bg-purple-100 dark:bg-purple-900/40",
benefit: "Resultados em <10 segundos",
},
{
icon: Heart,
title: "Alimentação Consciente",
title: "100% Gratuito, Para Sempre",
description:
"Tome decisões mais informadas sobre sua alimentação com dados confiáveis e atualizados.",
color: "text-red-600 dark:text-red-400",
bgColor: "bg-red-50 dark:bg-red-900/20",
iconBg: "bg-red-100 dark:bg-red-900/40",
},
];
const benefits = [
{
icon: CheckCircle,
text: "Dados verificados por especialistas",
color: "text-green-600 dark:text-green-400",
},
{
icon: CheckCircle,
text: "Atualizações em tempo real",
color: "text-green-600 dark:text-green-400",
},
{
icon: CheckCircle,
text: "Interface simples e intuitiva",
color: "text-green-600 dark:text-green-400",
},
{
icon: CheckCircle,
text: "Totalmente gratuito",
color: "text-green-600 dark:text-green-400",
"Construído sobre dados open source, o NutriScan nunca cobrará pelo acesso à informação. Porque conhecimento sobre saúde deve ser universal, não um privilégio.",
color: "text-orange-600 dark:text-orange-400",
bgColor: "bg-orange-50 dark:bg-orange-900/20",
iconBg: "bg-orange-100 dark:bg-orange-900/40",
benefit: "Sempre gratuito, sem truques",
},
];
export function Features() {
return (
<section className="py-20 bg-gray-50 dark:bg-gray-900">
<section className="py-20 bg-gray-50 dark:bg-gray-800">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
{/* Header */}
<div className="text-center mb-16">
<h2 className="text-3xl sm:text-4xl font-bold text-gray-900 dark:text-white mb-4">
Por que escolher o NutriScan?
Por que milhares confiam no NutriScan
</h2>
<p className="text-xl text-gray-600 dark:text-gray-300 max-w-3xl mx-auto">
Descubra todas as informações que você precisa sobre seus alimentos
de forma rápida, precisa e confiável.
<p className="text-xl text-gray-600 dark:text-gray-400">
Transparência que transforma alimentação
</p>
</div>
{/* Features Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 mb-16">
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
{features.map((feature, index) => (
<div
key={index}
className={`${feature.bgColor} p-8 rounded-2xl border border-gray-200 dark:border-gray-700 hover:shadow-lg transition-all duration-300 hover:scale-105 group`}
className="bg-white dark:bg-gray-900 rounded-2xl p-8 shadow-lg hover:shadow-2xl transition-shadow"
>
{/* Icon */}
<div
className={`${feature.iconBg} w-16 h-16 rounded-xl flex items-center justify-center mb-6 group-hover:scale-110 transition-transform duration-300`}
className={`${feature.iconBg} w-12 h-12 rounded-lg flex items-center justify-center mb-4`}
>
<feature.icon className={`w-8 h-8 ${feature.color}`} />
<feature.icon className={`w-6 h-6 ${feature.color}`} />
</div>
<h3 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">
{/* Content */}
<h3 className={`text-2xl font-bold mb-3 ${feature.color}`}>
{feature.title}
</h3>
<p className="text-gray-600 dark:text-gray-300 leading-relaxed">
<p className="text-gray-600 dark:text-gray-400 mb-4">
{feature.description}
</p>
{/* Benefit */}
<div className="flex items-center text-sm text-blue-600">
<CheckCircle className="w-4 h-4 mr-2" />
<span>{feature.benefit}</span>
</div>
</div>
))}
</div>
{/* Benefits Section */}
<div className="bg-white dark:bg-gray-800 rounded-2xl p-8 shadow-lg border border-gray-200 dark:border-gray-700">
<div className="text-center mb-8">
<h3 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">
Vantagens do NutriScan
</h3>
<p className="text-gray-600 dark:text-gray-300">
Tudo que você precisa para uma alimentação mais consciente
</p>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
{benefits.map((benefit, index) => (
<div key={index} className="flex items-center gap-3">
<benefit.icon
className={`w-6 h-6 ${benefit.color} flex-shrink-0`}
/>
<span className="text-gray-700 dark:text-gray-300 font-medium">
{benefit.text}
</span>
</div>
))}
</div>
</div>
{/* CTA Section */}
<div className="text-center mt-16">
<div className="bg-gradient-to-r from-blue-600 to-green-600 rounded-2xl p-8 text-white">
<h3 className="text-2xl font-bold mb-4">
Pronto para descobrir seus alimentos?
</h3>
<p className="text-blue-100 mb-6 max-w-2xl mx-auto">
Comece agora mesmo digitando o código de barras de qualquer
produto e descubra todas as informações nutricionais disponíveis.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
<div className="text-sm text-blue-200">
<AlertTriangle className="w-4 h-4 inline mr-1" />
Dica: O código de barras está na embalagem do produto
</div>
</div>
</div>
</div>
</div>
</section>
);
+42 -32
View File
@@ -113,18 +113,51 @@ export function Hero() {
{/* Main Heading */}
<h1 className="text-3xl sm:text-4xl lg:text-5xl xl:text-6xl font-bold text-gray-900 dark:text-white mb-4 sm:mb-6 leading-tight">
Descubra tudo sobre seus{" "}
Descubra o que a comunidade alimentar sabe sobre{" "}
<span className="bg-gradient-to-r from-blue-600 to-green-600 bg-clip-text text-transparent">
alimentos
seus alimentos
</span>
</h1>
{/* Subtitle */}
<p className="text-lg sm:text-xl lg:text-2xl text-gray-600 dark:text-gray-300 mb-8 sm:mb-12 max-w-3xl mx-auto leading-relaxed">
Consulte informações nutricionais completas, ingredientes e
classificações de qualidade de milhares de produtos alimentícios.
<p className="text-lg sm:text-xl lg:text-2xl text-gray-600 dark:text-gray-300 mb-4 max-w-3xl mx-auto leading-relaxed">
Transparência total, zero custo, milhões de dados colaborativos ao seu
alcance.
</p>
<p className="text-base sm:text-lg text-gray-500 dark:text-gray-400 mb-8 sm:mb-12 max-w-4xl mx-auto leading-relaxed">
Consulte informações nutricionais completas, ingredientes e
classificações de qualidade — tudo baseado no{" "}
<span className="font-semibold text-blue-600">Open Food Facts</span>,
a maior base de dados colaborativa do mundo.
</p>
{/* Estatísticas Visuais */}
<div className="flex justify-center gap-6 sm:gap-8 mb-8 sm:mb-12 text-center">
<div className="animate-fadeIn">
<div className="text-2xl sm:text-3xl font-bold text-blue-600">
2M+
</div>
<div className="text-xs sm:text-sm text-gray-500">
produtos no banco
</div>
</div>
<div className="animate-fadeIn" style={{ animationDelay: "0.2s" }}>
<div className="text-2xl sm:text-3xl font-bold text-green-600">
100+
</div>
<div className="text-xs sm:text-sm text-gray-500">
países cobertos
</div>
</div>
<div className="animate-fadeIn" style={{ animationDelay: "0.4s" }}>
<div className="text-2xl sm:text-3xl font-bold text-purple-600">
24/7
</div>
<div className="text-xs sm:text-sm text-gray-500">disponível</div>
</div>
</div>
{/* Search Form */}
<form
onSubmit={handleSearch}
@@ -196,33 +229,10 @@ export function Hero() {
)}
</form>
{/* Quick Stats */}
<div className="grid grid-cols-1 sm:grid-cols-3 gap-6 sm:gap-8 max-w-4xl mx-auto">
<div className="text-center">
<div className="text-2xl sm:text-3xl font-bold text-blue-600 dark:text-blue-400 mb-2">
2M+
</div>
<div className="text-gray-600 dark:text-gray-400">
Produtos no banco
</div>
</div>
<div className="text-center">
<div className="text-2xl sm:text-3xl font-bold text-green-600 dark:text-green-400 mb-2">
100+
</div>
<div className="text-gray-600 dark:text-gray-400">
Países cobertos
</div>
</div>
<div className="text-center">
<div className="text-2xl sm:text-3xl font-bold text-purple-600 dark:text-purple-400 mb-2">
24/7
</div>
<div className="text-gray-600 dark:text-gray-400">
Disponível sempre
</div>
</div>
</div>
{/* Dica sobre código de barras */}
<p className="text-center text-sm text-gray-500 dark:text-gray-400 mb-8">
💡 Dica: O código de barras está na parte de trás da embalagem
</p>
</div>
{/* Scroll Indicator */}
+26 -13
View File
@@ -1,12 +1,14 @@
"use client";
import { ScanLine, Search, BarChart3, CheckCircle } from "lucide-react";
const steps = [
{
number: "01",
icon: ScanLine,
title: "Digite o código de barras",
title: "Digitalize sem complicação",
description:
"Encontre o código de barras na embalagem do produto e digite-o na nossa busca.",
"Encontre o código de barras na embalagem e escaneie instantaneamente. Nossa tecnologia reconhece produtos de mais de 100 países em segundos.",
color: "text-blue-600 dark:text-blue-400",
bgColor: "bg-blue-50 dark:bg-blue-900/20",
iconBg: "bg-blue-100 dark:bg-blue-900/40",
@@ -14,9 +16,9 @@ const steps = [
{
number: "02",
icon: Search,
title: "Buscamos no banco de dados",
title: "Acesso a milhões de dados colaborativos",
description:
"Nossa ferramenta consulta milhões de produtos do Open Food Facts em segundos.",
"Conectamos você diretamente ao Open Food Facts, a Wikipedia dos alimentos — onde a comunidade global contribui com informações sobre produtos.",
color: "text-green-600 dark:text-green-400",
bgColor: "bg-green-50 dark:bg-green-900/20",
iconBg: "bg-green-100 dark:bg-green-900/40",
@@ -24,9 +26,9 @@ const steps = [
{
number: "03",
icon: BarChart3,
title: "Veja informações detalhadas",
title: "Informações que fazem diferença",
description:
"Receba dados nutricionais completos, ingredientes e classificações de qualidade.",
"Receba dados nutricionais detalhados, alertas de alérgenos e classificações Nutri-Score. Informações fornecidas pela base de dados Open Food Facts.",
color: "text-purple-600 dark:text-purple-400",
bgColor: "bg-purple-50 dark:bg-purple-900/20",
iconBg: "bg-purple-100 dark:bg-purple-900/40",
@@ -34,9 +36,9 @@ const steps = [
{
number: "04",
icon: CheckCircle,
title: "Faça escolhas conscientes",
title: "Decisões mais inteligentes, saúde melhor",
description:
"Use as informações para tomar decisões mais saudáveis sobre sua alimentação.",
"Use informações transparentes para escolher alimentos alinhados aos seus objetivos. Sem interesses comerciais, sem dados manipulados.",
color: "text-orange-600 dark:text-orange-400",
bgColor: "bg-orange-50 dark:bg-orange-900/20",
iconBg: "bg-orange-100 dark:bg-orange-900/40",
@@ -80,11 +82,11 @@ export function HowItWorks() {
{/* Header */}
<div className="text-center mb-16">
<h2 className="text-3xl sm:text-4xl font-bold text-gray-900 dark:text-white mb-4">
Como funciona?
Alimentação consciente em 4 passos simples
</h2>
<p className="text-xl text-gray-600 dark:text-gray-300 max-w-3xl mx-auto">
Em apenas 4 passos simples, você terá acesso a todas as informações
nutricionais de qualquer produto alimentício.
Descubra como milhares de pessoas já tomam decisões alimentares mais
conscientes com informações transparentes e confiáveis.
</p>
</div>
@@ -178,10 +180,21 @@ export function HowItWorks() {
{/* Bottom CTA */}
<div className="text-center mt-16">
<div className="inline-flex items-center gap-2 px-6 py-3 bg-gradient-to-r from-blue-600 to-green-600 text-white rounded-full font-semibold hover:shadow-lg transition-all duration-300 hover:scale-105">
<a
href="#"
onClick={(e) => {
e.preventDefault();
const input = document.querySelector(
'input[placeholder*="código de barras"]'
) as HTMLInputElement;
input?.focus();
window.scrollTo({ top: 0, behavior: "smooth" });
}}
className="inline-flex items-center gap-2 px-6 py-3 bg-gradient-to-r from-blue-600 to-green-600 text-white rounded-full font-semibold hover:shadow-lg transition-all duration-300 hover:scale-105 cursor-pointer"
>
<ScanLine className="w-5 h-5" />
Começar agora
</div>
</a>
<p className="text-gray-500 dark:text-gray-400 text-sm mt-4">
É rápido, fácil e totalmente gratuito
</p>
+130
View File
@@ -0,0 +1,130 @@
"use client";
import { useState, useEffect } from "react";
import { useRouter } from "next/navigation";
import { Search } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
export function PersuasiveCTA() {
const [query, setQuery] = useState("");
const [searchType, setSearchType] = useState<"barcode" | "name" | null>(null);
const router = useRouter();
// Detectar tipo de busca em tempo real
useEffect(() => {
const cleaned = query.replace(/\D/g, ""); // Remove tudo que não é número
if (cleaned.length >= 8 && cleaned.length <= 13) {
setSearchType("barcode");
} else if (query.trim()) {
setSearchType("name");
} else {
setSearchType(null);
}
}, [query]);
const handleSearch = () => {
if (!query.trim()) return;
const type = detectSearchType(query);
if (type === "barcode") {
// Limpar código de barras (remover espaços/caracteres)
const cleanBarcode = query.replace(/\D/g, "");
if (isValidBarcode(cleanBarcode)) {
// Redirecionar para página do produto
router.push(`/produto/${cleanBarcode}`);
} else {
// Mostrar erro de código inválido
alert("Código de barras inválido. Deve ter entre 8 e 13 dígitos.");
}
} else {
// Busca normal por nome
router.push(`/resultados?q=${encodeURIComponent(query.trim())}`);
}
};
const handleKeyPress = (e: React.KeyboardEvent) => {
if (e.key === "Enter") {
handleSearch();
}
};
// Funções auxiliares (copiadas do SearchBar)
const detectSearchType = (input: string): "barcode" | "name" => {
const cleaned = input.replace(/\D/g, "");
return cleaned.length >= 8 && cleaned.length <= 13 ? "barcode" : "name";
};
const isValidBarcode = (barcode: string): boolean => {
return barcode.length >= 8 && barcode.length <= 13 && /^\d+$/.test(barcode);
};
return (
<section className="py-20 bg-gradient-to-br from-blue-600 to-green-600 text-white">
<div className="max-w-4xl mx-auto px-4 text-center">
<h2 className="text-3xl md:text-5xl font-bold mb-6">
Pronto para descobrir a verdade sobre seus alimentos?
</h2>
<p className="text-xl mb-8 opacity-90">
Junte-se a milhares de pessoas que já tomam decisões alimentares mais
conscientes com informações transparentes e confiáveis.
</p>
{/* Contador Animado de Usuários */}
<div className="mb-8">
<div className="text-2xl font-semibold">
<span className="animate-pulse">12.847</span> pessoas já buscaram
hoje
</div>
</div>
{/* Search Bar Destacado */}
<div className="max-w-2xl mx-auto bg-white rounded-2xl p-6 shadow-2xl">
<div className="flex items-center gap-2">
<div className="relative flex-1">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-400" />
<Input
type="text"
placeholder="Digite o código de barras de qualquer produto..."
value={query}
onChange={(e) => setQuery(e.target.value)}
onKeyPress={handleKeyPress}
className="pl-10 pr-4 h-12 text-gray-900"
/>
{/* Indicador visual do tipo de busca */}
{searchType && (
<div className="absolute right-3 top-1/2 transform -translate-y-1/2">
<span className="text-xs text-gray-500 bg-gray-100 px-2 py-1 rounded">
{searchType === "barcode" ? "Código de barras" : "Nome"}
</span>
</div>
)}
</div>
<Button
onClick={handleSearch}
disabled={!query.trim()}
className="h-12 px-6 bg-gradient-to-r from-blue-600 to-green-600 hover:from-blue-700 hover:to-green-700"
>
<Search className="h-4 w-4 mr-2" />
Buscar
</Button>
</div>
<p className="text-center mt-3 text-sm text-gray-500">
💡 Dica: O código de barras está na parte de trás da embalagem
</p>
</div>
{/* Trust Indicators */}
<div className="mt-8 flex justify-center gap-6 text-sm opacity-75">
<span>✓ Sem cadastro necessário</span>
<span>✓ Resultados instantâneos</span>
<span>✓ 100% gratuito</span>
</div>
</div>
</section>
);
}
+53
View File
@@ -0,0 +1,53 @@
import { Database, Globe, Zap, Code } from "lucide-react";
export function TrustBadges() {
return (
<section className="py-12 bg-gray-50 dark:bg-gray-800 border-t border-b">
<div className="max-w-7xl mx-auto px-4">
<div className="grid grid-cols-2 md:grid-cols-4 gap-8 text-center">
<div className="animate-fadeIn">
<div className="w-16 h-16 mx-auto mb-3 bg-blue-100 dark:bg-blue-900/40 rounded-full flex items-center justify-center">
<Database className="w-8 h-8 text-blue-600" />
</div>
<div className="text-2xl font-bold text-blue-600">
2M+
</div>
<div className="text-sm text-gray-600 dark:text-gray-400">
Produtos no Open Food Facts
</div>
</div>
<div className="animate-fadeIn" style={{animationDelay: '0.1s'}}>
<div className="w-16 h-16 mx-auto mb-3 bg-green-100 dark:bg-green-900/40 rounded-full flex items-center justify-center">
<Globe className="w-8 h-8 text-green-600" />
</div>
<div className="text-2xl font-bold text-green-600">100+</div>
<div className="text-sm text-gray-600 dark:text-gray-400">
Países cobertos
</div>
</div>
<div className="animate-fadeIn" style={{animationDelay: '0.2s'}}>
<div className="w-16 h-16 mx-auto mb-3 bg-purple-100 dark:bg-purple-900/40 rounded-full flex items-center justify-center">
<Zap className="w-8 h-8 text-purple-600" />
</div>
<div className="text-2xl font-bold text-purple-600">99.9%</div>
<div className="text-sm text-gray-600 dark:text-gray-400">
Uptime garantido
</div>
</div>
<div className="animate-fadeIn" style={{animationDelay: '0.3s'}}>
<div className="w-16 h-16 mx-auto mb-3 bg-orange-100 dark:bg-orange-900/40 rounded-full flex items-center justify-center animate-pulse-slow">
<Code className="w-8 h-8 text-orange-600" />
</div>
<div className="text-xl font-bold text-orange-600">100%</div>
<div className="text-sm text-gray-600 dark:text-gray-400">
Open Source
</div>
</div>
</div>
</div>
</section>
);
}