feat(footer, header, landing): enhance UI components and improve navigation
- Updated Footer component with new links and dynamic year display. - Refactored Header component to improve mobile navigation and theme toggling. - Enhanced DonationSection and Features components with improved layout and animations. - Updated HowItWorks and Impact sections for better user engagement and clarity. - Improved product page layout and error handling in API interactions. - General UI refinements across various components for a cohesive user experience.
This commit is contained in:
1 parent
b0f95704e1
commit
f98c648311
21 files changed
+3335
-12057
No files matched your search
@@ -1,159 +1,164 @@
|
||||
"use client";
|
||||
|
||||
import React, { useRef } from "react";
|
||||
import { motion, useScroll, useSpring } from "framer-motion";
|
||||
import { Calendar, Lightbulb, Heart, Target } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
// 1. Definição da Interface de Dados
|
||||
interface StoryStep {
|
||||
year: string;
|
||||
title: string;
|
||||
description: string;
|
||||
icon: React.ElementType;
|
||||
color: string;
|
||||
side: "left" | "right";
|
||||
}
|
||||
|
||||
const STORY_DATA: StoryStep[] = [
|
||||
{
|
||||
year: "2024",
|
||||
title: "A Pergunta Simples",
|
||||
description: "O que realmente tem nesse produto? Decifrar tabelas nutricionais era um desafio individual que percebemos ser global.",
|
||||
icon: Calendar,
|
||||
color: "blue",
|
||||
side: "left",
|
||||
},
|
||||
{
|
||||
year: "Descoberta",
|
||||
title: "Não Estávamos Sozinhos",
|
||||
description: "Milhares de pessoas buscam transparência alimentar. A necessidade de dados abertos tornou-se uma urgência técnica.",
|
||||
icon: Lightbulb,
|
||||
color: "green",
|
||||
side: "right",
|
||||
},
|
||||
{
|
||||
year: "Solução",
|
||||
title: "NutriScan Nasce",
|
||||
description: "Nascido da Engenharia para o mundo. Uma ponte entre o código de barras e a verdade nutricional.",
|
||||
icon: Heart,
|
||||
color: "purple",
|
||||
side: "left",
|
||||
},
|
||||
{
|
||||
year: "Hoje",
|
||||
title: "Nossa Missão",
|
||||
description: "Democratizar a informação. Conectar cidadãos à maior base de dados colaborativa do planeta.",
|
||||
icon: Target,
|
||||
color: "orange",
|
||||
side: "right",
|
||||
},
|
||||
];
|
||||
|
||||
export function StorySection() {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// 2. Lógica de Scroll Progressivo (Engenharia de Movimento)
|
||||
const { scrollYProgress } = useScroll({
|
||||
target: containerRef,
|
||||
offset: ["start end", "end end"],
|
||||
});
|
||||
|
||||
const scaleY = useSpring(scrollYProgress, {
|
||||
stiffness: 100,
|
||||
damping: 30,
|
||||
restDelta: 0.001
|
||||
});
|
||||
|
||||
return (
|
||||
<section className="py-20 bg-white dark:bg-gray-900">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-3xl sm:text-4xl lg:text-5xl font-bold text-gray-900 dark:text-white mb-6">
|
||||
Nossa{" "}
|
||||
<span className="bg-gradient-to-r from-blue-600 to-green-600 bg-clip-text text-transparent">
|
||||
história
|
||||
</span>
|
||||
<section ref={containerRef} className="py-32 bg-white dark:bg-[#030712] relative overflow-hidden">
|
||||
<div className="max-w-7xl mx-auto px-6">
|
||||
|
||||
{/* Header com Animação de Entrada */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
className="text-center mb-32"
|
||||
>
|
||||
<h2 className="text-4xl md:text-6xl font-black text-gray-900 dark:text-white mb-6 tracking-tighter">
|
||||
Nossa <span className="bg-gradient-to-r from-blue-600 to-green-500 bg-clip-text text-transparent">história</span>
|
||||
</h2>
|
||||
<p className="text-xl text-gray-600 dark:text-gray-300 max-w-3xl mx-auto">
|
||||
Tudo começou com uma pergunta simples que mudou tudo.
|
||||
<p className="text-lg text-gray-600 dark:text-gray-400 max-w-2xl mx-auto font-medium">
|
||||
De uma dúvida individual a uma plataforma de transparência global.
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Timeline */}
|
||||
<div className="relative">
|
||||
{/* Timeline Line */}
|
||||
<div className="absolute left-1/2 transform -translate-x-1/2 w-1 h-full bg-gradient-to-b from-blue-600 to-green-600 rounded-full hidden md:block"></div>
|
||||
{/* 3. A Linha do Tempo Reativa */}
|
||||
<div className="absolute left-1/2 transform -translate-x-1/2 w-[2px] h-full bg-gray-100 dark:bg-gray-800 hidden md:block">
|
||||
<motion.div
|
||||
style={{ scaleY }}
|
||||
className="absolute top-0 w-full h-full bg-gradient-to-b from-blue-500 via-green-500 to-orange-500 origin-top"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Timeline Items */}
|
||||
<div className="space-y-16">
|
||||
{/* 2024 - The Question */}
|
||||
<div className="relative flex items-center">
|
||||
<div className="w-full md:w-1/2 md:pr-8">
|
||||
<div className="bg-gradient-to-br from-blue-50 to-blue-100 dark:from-blue-900/20 dark:to-blue-800/20 rounded-2xl p-8 shadow-lg">
|
||||
<div className="flex items-center mb-4">
|
||||
<div className="w-12 h-12 bg-blue-500 rounded-lg flex items-center justify-center mr-4">
|
||||
<Calendar className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-medium text-blue-600 dark:text-blue-400">
|
||||
2024
|
||||
</div>
|
||||
<div className="text-lg font-bold text-gray-900 dark:text-white">
|
||||
A Pergunta Simples
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<blockquote className="text-xl font-semibold text-gray-800 dark:text-gray-200 mb-4">
|
||||
“O que realmente tem nesse produto?”
|
||||
</blockquote>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Após inúmeras idas ao mercado tentando decifrar tabelas
|
||||
nutricionais e listas intermináveis de ingredientes,
|
||||
percebemos que não estávamos sozinhos nessa busca.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Timeline Dot */}
|
||||
<div className="absolute left-1/2 transform -translate-x-1/2 w-4 h-4 bg-blue-500 rounded-full border-4 border-white dark:border-gray-900 shadow-lg hidden md:block"></div>
|
||||
|
||||
<div className="w-full md:w-1/2 md:pl-8"></div>
|
||||
</div>
|
||||
|
||||
{/* The Discovery */}
|
||||
<div className="relative flex items-center">
|
||||
<div className="w-full md:w-1/2 md:pr-8"></div>
|
||||
|
||||
{/* Timeline Dot */}
|
||||
<div className="absolute left-1/2 transform -translate-x-1/2 w-4 h-4 bg-green-500 rounded-full border-4 border-white dark:border-gray-900 shadow-lg hidden md:block"></div>
|
||||
|
||||
<div className="w-full md:w-1/2 md:pl-8">
|
||||
<div className="bg-gradient-to-br from-green-50 to-green-100 dark:from-green-900/20 dark:to-green-800/20 rounded-2xl p-8 shadow-lg">
|
||||
<div className="flex items-center mb-4">
|
||||
<div className="w-12 h-12 bg-green-500 rounded-lg flex items-center justify-center mr-4">
|
||||
<Lightbulb className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-medium text-green-600 dark:text-green-400">
|
||||
Descoberta
|
||||
</div>
|
||||
<div className="text-lg font-bold text-gray-900 dark:text-white">
|
||||
Não Estávamos Sozinhos
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Milhares de pessoas querem fazer escolhas mais conscientes,
|
||||
mas esbarram na falta de informação acessível. A necessidade
|
||||
era real e urgente.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* The Solution */}
|
||||
<div className="relative flex items-center">
|
||||
<div className="w-full md:w-1/2 md:pr-8">
|
||||
<div className="bg-gradient-to-br from-purple-50 to-purple-100 dark:from-purple-900/20 dark:to-purple-800/20 rounded-2xl p-8 shadow-lg">
|
||||
<div className="flex items-center mb-4">
|
||||
<div className="w-12 h-12 bg-purple-500 rounded-lg flex items-center justify-center mr-4">
|
||||
<Heart className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-medium text-purple-600 dark:text-purple-400">
|
||||
Solução
|
||||
</div>
|
||||
<div className="text-lg font-bold text-gray-900 dark:text-white">
|
||||
NutriScan Nasce
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Foi assim que o NutriScan ganhou vida – não como mais um
|
||||
aplicativo de nutrição, mas como uma ponte entre você e a
|
||||
verdade sobre o que consome.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Timeline Dot */}
|
||||
<div className="absolute left-1/2 transform -translate-x-1/2 w-4 h-4 bg-purple-500 rounded-full border-4 border-white dark:border-gray-900 shadow-lg hidden md:block"></div>
|
||||
|
||||
<div className="w-full md:w-1/2 md:pl-8"></div>
|
||||
</div>
|
||||
|
||||
{/* The Mission */}
|
||||
<div className="relative flex items-center">
|
||||
<div className="w-full md:w-1/2 md:pr-8"></div>
|
||||
|
||||
{/* Timeline Dot */}
|
||||
<div className="absolute left-1/2 transform -translate-x-1/2 w-4 h-4 bg-orange-500 rounded-full border-4 border-white dark:border-gray-900 shadow-lg hidden md:block"></div>
|
||||
|
||||
<div className="w-full md:w-1/2 md:pl-8">
|
||||
<div className="bg-gradient-to-br from-orange-50 to-orange-100 dark:from-orange-900/20 dark:to-orange-800/20 rounded-2xl p-8 shadow-lg">
|
||||
<div className="flex items-center mb-4">
|
||||
<div className="w-12 h-12 bg-orange-500 rounded-lg flex items-center justify-center mr-4">
|
||||
<Target className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-medium text-orange-600 dark:text-orange-400">
|
||||
Hoje
|
||||
</div>
|
||||
<div className="text-lg font-bold text-gray-900 dark:text-white">
|
||||
Nossa Missão
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Democratizar o acesso às informações nutricionais,
|
||||
conectando você à maior base de dados colaborativa de
|
||||
alimentos do mundo.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-24 md:space-y-32">
|
||||
{STORY_DATA.map((step, index) => (
|
||||
<TimelineItem key={index} step={step} index={index} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
// 4. Sub-componente com Encapsulamento de Animação
|
||||
function TimelineItem({ step }: { step: StoryStep; index: number }) {
|
||||
const isLeft = step.side === "left";
|
||||
const colorMap = {
|
||||
blue: "bg-blue-500 text-blue-500 border-blue-200 dark:border-blue-900/30",
|
||||
green: "bg-green-500 text-green-500 border-green-200 dark:border-green-900/30",
|
||||
purple: "bg-purple-500 text-purple-500 border-purple-200 dark:border-purple-900/30",
|
||||
orange: "bg-orange-500 text-orange-500 border-orange-200 dark:border-orange-900/30",
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn(
|
||||
"relative flex flex-col md:flex-row items-center justify-between w-full",
|
||||
isLeft ? "md:flex-row" : "md:flex-row-reverse"
|
||||
)}>
|
||||
{/* Conteúdo */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: isLeft ? -50 : 50 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
transition={{ duration: 0.6, delay: 0.1 }}
|
||||
className="w-full md:w-[45%] group"
|
||||
>
|
||||
<div className={cn(
|
||||
"p-8 rounded-[2rem] bg-gray-50 dark:bg-gray-900/50 border border-gray-100 dark:border-gray-800 transition-all duration-500",
|
||||
"hover:shadow-2xl hover:border-transparent group-hover:bg-white dark:group-hover:bg-gray-900"
|
||||
)}>
|
||||
<div className="flex items-center gap-4 mb-6">
|
||||
<div className={cn("w-12 h-12 rounded-xl flex items-center justify-center text-white shadow-lg", colorMap[step.color as keyof typeof colorMap].split(' ')[0])}>
|
||||
<step.icon size={24} />
|
||||
</div>
|
||||
<div>
|
||||
<span className={cn("text-xs font-black uppercase tracking-widest", colorMap[step.color as keyof typeof colorMap].split(' ')[1])}>
|
||||
{step.year}
|
||||
</span>
|
||||
<h3 className="text-xl font-bold text-gray-900 dark:text-white leading-tight">
|
||||
{step.title}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-gray-600 dark:text-gray-400 leading-relaxed">
|
||||
{step.description}
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Ponto Central com Efeito de Pulso */}
|
||||
<div className="absolute left-1/2 transform -translate-x-1/2 w-4 h-4 z-20 hidden md:block">
|
||||
<motion.div
|
||||
initial={{ scale: 0 }}
|
||||
whileInView={{ scale: 1 }}
|
||||
className={cn("w-full h-full rounded-full ring-4 ring-white dark:ring-[#030712] shadow-sm", colorMap[step.color as keyof typeof colorMap].split(' ')[0])}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="hidden md:block w-[45%]" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user