-
+
É rápido, fácil e totalmente gratuito
diff --git a/app/components/landing/PersuasiveCTA.tsx b/app/components/landing/PersuasiveCTA.tsx
new file mode 100644
index 0000000..0baf50e
--- /dev/null
+++ b/app/components/landing/PersuasiveCTA.tsx
@@ -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 (
+
+
+
+ Pronto para descobrir a verdade sobre seus alimentos?
+
+
+ Junte-se a milhares de pessoas que já tomam decisões alimentares mais
+ conscientes com informações transparentes e confiáveis.
+
+
+ {/* Contador Animado de Usuários */}
+
+
+ 12.847 pessoas já buscaram
+ hoje
+
+
+
+ {/* Search Bar Destacado */}
+
+
+
+
+
setQuery(e.target.value)}
+ onKeyPress={handleKeyPress}
+ className="pl-10 pr-4 h-12 text-gray-900"
+ />
+
+ {/* Indicador visual do tipo de busca */}
+ {searchType && (
+
+
+ {searchType === "barcode" ? "Código de barras" : "Nome"}
+
+
+ )}
+
+
+
+ Buscar
+
+
+
+ 💡 Dica: O código de barras está na parte de trás da embalagem
+
+
+
+ {/* Trust Indicators */}
+
+ ✓ Sem cadastro necessário
+ ✓ Resultados instantâneos
+ ✓ 100% gratuito
+
+
+
+ );
+}
diff --git a/app/components/landing/TrustBadges.tsx b/app/components/landing/TrustBadges.tsx
new file mode 100644
index 0000000..202938b
--- /dev/null
+++ b/app/components/landing/TrustBadges.tsx
@@ -0,0 +1,53 @@
+import { Database, Globe, Zap, Code } from "lucide-react";
+
+export function TrustBadges() {
+ return (
+
+
+
+
+
+
+
+
+ 2M+
+
+
+ Produtos no Open Food Facts
+
+
+
+
+
+
+
+
100+
+
+ Países cobertos
+
+
+
+
+
+
+
+
99.9%
+
+ Uptime garantido
+
+
+
+
+
+
+
+
100%
+
+ Open Source
+
+
+
+
+
+ );
+}
diff --git a/app/components/pricing/BillingToggle.tsx b/app/components/pricing/BillingToggle.tsx
deleted file mode 100644
index 5b1b115..0000000
--- a/app/components/pricing/BillingToggle.tsx
+++ /dev/null
@@ -1,60 +0,0 @@
-"use client";
-
-// useState removido - não utilizado
-import { Badge } from "@/components/ui/badge";
-
-interface BillingToggleProps {
- isAnnual: boolean;
- onToggle: (isAnnual: boolean) => void;
- className?: string;
-}
-
-export function BillingToggle({
- isAnnual,
- onToggle,
- className = "",
-}: BillingToggleProps) {
- return (
-
-
- {/* Toggle Background */}
-
-
- {/* Monthly Option */}
-
onToggle(false)}
- className={`relative z-10 px-6 py-2 text-sm font-medium rounded-md transition-colors duration-200 ${
- !isAnnual
- ? "text-gray-900 dark:text-white"
- : "text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300"
- }`}
- >
- Mensal
-
-
- {/* Annual Option */}
-
onToggle(true)}
- className={`relative z-10 px-6 py-2 text-sm font-medium rounded-md transition-colors duration-200 ${
- isAnnual
- ? "text-gray-900 dark:text-white"
- : "text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300"
- }`}
- >
- Anual
-
-
-
- {/* Save Badge */}
- {isAnnual && (
-
- Economize 20%
-
- )}
-
- );
-}
diff --git a/app/components/pricing/PricingCard.tsx b/app/components/pricing/PricingCard.tsx
deleted file mode 100644
index f8f4cf0..0000000
--- a/app/components/pricing/PricingCard.tsx
+++ /dev/null
@@ -1,267 +0,0 @@
-"use client";
-
-import { CheckCircle2, XCircle } from "lucide-react";
-import { Button } from "@/components/ui/button";
-import { Badge } from "@/components/ui/badge";
-import { Card, CardContent, CardHeader } from "@/components/ui/card";
-
-interface Feature {
- text: string;
- included: boolean;
- highlight?: boolean;
- note?: string;
-}
-
-interface PricingCardProps {
- plan: "basic" | "premium" | "free";
- isAnnual?: boolean;
- isDisabled?: boolean;
- isSelected?: boolean;
- className?: string;
-}
-
-const planData: Record<
- string,
- {
- name: string;
- monthlyPrice: number;
- annualPrice: number;
- description: string;
- badge: string | null;
- features: Feature[];
- }
-> = {
- basic: {
- name: "Básico",
- monthlyPrice: 5,
- annualPrice: 4,
- description: "Perfeito para uso pessoal e pequenas consultas",
- badge: null,
- features: [
- { text: "Até 100 consultas/mês", included: true },
- { text: "Busca por código de barras", included: true },
- { text: "Busca por nome", included: true },
- { text: "Informações nutricionais completas", included: true },
- { text: "Histórico (últimas 10)", included: true },
- { text: "Filtros avançados", included: false },
- { text: "Listas de compras", included: false },
- { text: "Comparação de produtos", included: false },
- { text: "Exportação de dados", included: false },
- { text: "API de acesso", included: false },
- { text: "Suporte prioritário", included: false },
- ],
- },
- premium: {
- name: "Premium",
- monthlyPrice: 10,
- annualPrice: 8,
- description: "Ideal para profissionais e uso intensivo",
- badge: "Popular",
- features: [
- { text: "Até 500 consultas/mês", included: true },
- { text: "Busca por código de barras", included: true },
- { text: "Busca por nome", included: true },
- { text: "Informações nutricionais completas", included: true },
- { text: "Histórico ilimitado", included: true },
- { text: "Filtros avançados", included: true },
- { text: "Listas de compras ilimitadas", included: true },
- { text: "Comparação de produtos (até 5)", included: true },
- { text: "Exportação de dados (CSV/JSON)", included: true },
- { text: "API de acesso (1000 req/mês)", included: true },
- { text: "Suporte prioritário", included: true },
- ],
- },
- free: {
- name: "Free Beta",
- monthlyPrice: 0,
- annualPrice: 0,
- description: "Acesso completo durante fase beta",
- badge: "Beta",
- features: [
- { text: "Consultas ilimitadas", included: true, highlight: true },
- { text: "Busca por código de barras", included: true },
- { text: "Busca por nome", included: true },
- { text: "Informações nutricionais completas", included: true },
- { text: "Histórico ilimitado", included: true },
- { text: "Filtros avançados", included: true },
- { text: "Listas de compras ilimitadas", included: true },
- { text: "Comparação de produtos", included: true },
- { text: "Exportação de dados", included: true },
- { text: "Badge Beta Tester", included: true, highlight: true },
- { text: "Acesso temporário", included: true, note: "Durante fase beta" },
- ],
- },
-};
-
-// featureIcons removido - não utilizado
-
-export function PricingCard({
- plan,
- isAnnual = false,
- className = "",
-}: PricingCardProps) {
- const data = planData[plan];
- const price = isAnnual ? data.annualPrice : data.monthlyPrice;
- const isPopular = plan === "premium";
- const isFree = plan === "free";
-
- // getFeatureIcon removido - não utilizado
-
- return (
-
- {/* Badge */}
- {data.badge && (
-
-
- {data.badge}
-
-
- )}
-
-
-
-
- {data.name}
-
-
- {data.description}
-
-
-
-
-
- {/* Preço */}
-
-
-
- ${price}
-
- {!isFree && (
-
- /mês
-
- )}
-
- {isAnnual && !isFree && (
-
- Economize ${(data.monthlyPrice - data.annualPrice) * 12}/ano
-
- )}
-
-
- {/* Botões */}
-
-
- {isFree ? "Começar Grátis" : "Começar Agora"}
-
-
- Falar com Vendas
-
-
-
- {/* Features */}
-
- {data.features.map((feature, index) => {
- return (
-
-
- {feature.included ? (
-
- ) : (
-
- )}
-
-
-
- {feature.text}
-
- {feature.note && (
-
- {feature.note}
-
- )}
-
-
- );
- })}
-
-
-
- );
-}
diff --git a/app/components/pricing/PricingFAQ.tsx b/app/components/pricing/PricingFAQ.tsx
deleted file mode 100644
index 9e32a4b..0000000
--- a/app/components/pricing/PricingFAQ.tsx
+++ /dev/null
@@ -1,92 +0,0 @@
-"use client";
-
-// ChevronDown removido - não utilizado
-import {
- Accordion,
- AccordionContent,
- AccordionItem,
- AccordionTrigger,
-} from "@/components/ui/accordion";
-
-interface PricingFAQProps {
- className?: string;
-}
-
-const faqData = [
- {
- question: "Como funciona o período de teste?",
- answer:
- "Oferecemos acesso gratuito durante nossa fase beta, sem limitações de tempo ou funcionalidades. Você pode usar todas as funcionalidades do Premium sem custo algum.",
- },
- {
- question: "Posso mudar de plano a qualquer momento?",
- answer:
- "Sim! Você pode fazer upgrade ou downgrade do seu plano a qualquer momento. As mudanças são aplicadas imediatamente e os valores são calculados proporcionalmente.",
- },
- {
- question: "O que acontece quando eu atingo o limite de consultas?",
- answer:
- "Quando você atinge o limite mensal de consultas, você pode fazer upgrade para um plano superior ou aguardar o próximo ciclo de cobrança. Também oferecemos pacotes adicionais de consultas.",
- },
- {
- question: "Como faço para cancelar minha assinatura?",
- answer:
- "Você pode cancelar sua assinatura a qualquer momento através da sua conta. O cancelamento é efetivado no final do período de cobrança atual, e você mantém acesso até lá.",
- },
- {
- question: "Vocês oferecem reembolso?",
- answer:
- "Sim! Oferecemos reembolso completo nos primeiros 30 dias após a assinatura. Entre em contato conosco se não estiver satisfeito com o serviço.",
- },
- {
- question: "Os dados são seguros e privados?",
- answer:
- "Absolutamente! Todos os dados são criptografados e armazenados de forma segura. Não compartilhamos suas informações pessoais ou histórico de consultas com terceiros.",
- },
- {
- question: "A API está disponível em todos os planos?",
- answer:
- "A API está disponível apenas nos planos Premium e Free Beta. O plano Básico não inclui acesso à API, mas você pode fazer upgrade a qualquer momento.",
- },
- {
- question: "Há suporte técnico disponível?",
- answer:
- "Sim! Oferecemos suporte por email para todos os usuários e suporte prioritário (email + chat) para usuários Premium. Nossa equipe responde em até 24 horas.",
- },
-];
-
-export function PricingFAQ({ className = "" }: PricingFAQProps) {
- return (
-
-
-
- Perguntas Frequentes
-
-
- Tire suas dúvidas sobre nossos planos e funcionalidades
-
-
-
-
- {faqData.map((item, index) => (
-
-
-
- {item.question}
-
-
-
-
- {item.answer}
-
-
-
- ))}
-
-
- );
-}
diff --git a/app/components/pricing/PricingHero.tsx b/app/components/pricing/PricingHero.tsx
deleted file mode 100644
index 93b968b..0000000
--- a/app/components/pricing/PricingHero.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-"use client";
-
-interface PricingHeroProps {
- className?: string;
-}
-
-export function PricingHero({ className = "" }: PricingHeroProps) {
- return (
-
-
- Escolha o plano perfeito para você
-
-
- Acesse informações nutricionais completas de milhares de produtos. Desde
- uso pessoal até profissional, temos a solução ideal para suas
- necessidades.
-
-
- );
-}
diff --git a/app/globals.css b/app/globals.css
index e35c86e..f87cb94 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -2,6 +2,17 @@
@tailwind components;
@tailwind utilities;
+/* Scrollbar utilities */
+@layer utilities {
+ .scrollbar-hide {
+ -ms-overflow-style: none; /* IE and Edge */
+ scrollbar-width: none; /* Firefox */
+ }
+ .scrollbar-hide::-webkit-scrollbar {
+ display: none; /* Chrome, Safari and Opera */
+ }
+}
+
/* Otimizações de performance para animações */
@layer utilities {
.animate-in {
@@ -27,6 +38,60 @@
}
}
+ /* Animações personalizadas para UI/UX */
+ .animate-fade-in {
+ animation: fadeIn 0.6s ease-out forwards;
+ }
+
+ .animate-slide-up {
+ animation: slideUp 0.6s ease-out forwards;
+ }
+
+ .animate-pulse-slow {
+ animation: pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+ }
+
+ .animate-bounce-gentle {
+ animation: bounceGentle 2s infinite;
+ }
+
+ @keyframes fadeIn {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+ }
+
+ @keyframes slideUp {
+ from {
+ opacity: 0;
+ transform: translateY(2rem);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+ }
+
+ @keyframes bounceGentle {
+ 0%,
+ 100% {
+ transform: translateY(0);
+ }
+ 50% {
+ transform: translateY(-0.25rem);
+ }
+ }
+
+ /* Padrão de grid para background */
+ .bg-grid-pattern {
+ background-image: linear-gradient(rgba(0, 0, 0, 0.1) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(0, 0, 0, 0.1) 1px, transparent 1px);
+ background-size: 20px 20px;
+ }
+
/* Otimização para listas grandes */
.product-grid {
contain: layout style paint;
diff --git a/app/layout.tsx b/app/layout.tsx
index b84eb2f..6785154 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -2,7 +2,6 @@ import "./globals.css";
import { Metadata } from "next";
import { Inter } from "next/font/google";
import { ThemeProvider } from "@/app/providers";
-import { FloatingDonateButton } from "@/app/components/FloatingDonateButton";
// Use Inter como fallback já que é uma fonte similar à Geist
const inter = Inter({ subsets: ["latin"] });
@@ -65,7 +64,6 @@ export default function RootLayout({
{children}
-
);
diff --git a/app/page.tsx b/app/page.tsx
index bdde784..9d7defd 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -3,6 +3,8 @@ import { Footer } from "./components/Footer";
import { Hero } from "./components/landing/Hero";
import { Features } from "./components/landing/Features";
import { HowItWorks } from "./components/landing/HowItWorks";
+import { DonationSection } from "./components/landing/DonationSection";
+import { TrustBadges } from "./components/landing/TrustBadges";
export default function Home() {
return (
@@ -10,12 +12,14 @@ export default function Home() {
+
+
diff --git a/app/pricing/page.tsx b/app/pricing/page.tsx
deleted file mode 100644
index b05f8a6..0000000
--- a/app/pricing/page.tsx
+++ /dev/null
@@ -1,261 +0,0 @@
-"use client";
-
-import { useState } from "react";
-import { Header } from "@/app/components/header";
-import { Footer } from "@/app/components/Footer";
-import { PricingHero } from "@/app/components/pricing/PricingHero";
-// BillingToggle removido - não utilizado
-import { PricingCard } from "@/app/components/pricing/PricingCard";
-import { PricingFAQ } from "@/app/components/pricing/PricingFAQ";
-import { Button } from "@/components/ui/button";
-import { ArrowRight, CheckCircle2 } from "lucide-react";
-
-export default function PricingPage() {
- const [isAnnual] = useState(false);
-
- return (
-