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:
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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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 */}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user