feat: Implement initial portfolio website structure with core components and a new post creator tool.
This commit is contained in:
1 parent
036363b521
commit
aa07d6faeb
15 files changed
+1337
-1855
No files matched your search
+106
-150
@@ -1,171 +1,127 @@
|
||||
"use client";
|
||||
"use client";
|
||||
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { MenuIcon, XIcon } from "lucide-react";
|
||||
import Image from "next/image";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useState } from "react";
|
||||
import { Logo } from "./Logo";
|
||||
|
||||
// Definição de Tipos para consistência de dados
|
||||
interface NavItem {
|
||||
href: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
const navItems: NavItem[] = [
|
||||
const navItems = [
|
||||
{ href: "#home", label: "Home" },
|
||||
{ href: "#about", label: "Sobre" },
|
||||
{ href: "#skills", label: "Habilidades" },
|
||||
{ href: "#experience", label: "Experiência" },
|
||||
{ href: "#experience", label: "Experiencia" },
|
||||
{ href: "#projects", label: "Projetos" },
|
||||
{ href: "#modalities", label: "Contratacao" },
|
||||
{ href: "#contact", label: "Contato" },
|
||||
];
|
||||
|
||||
export function Header() {
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
const [scrolled, setScrolled] = useState(false);
|
||||
|
||||
// Hook de detecção de scroll para mudança de estado do Header
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
setScrolled(window.scrollY > 20);
|
||||
};
|
||||
window.addEventListener("scroll", handleScroll);
|
||||
return () => window.removeEventListener("scroll", handleScroll);
|
||||
}, []);
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<motion.header
|
||||
initial={{ y: -100 }}
|
||||
animate={{ y: 0 }}
|
||||
className={cn(
|
||||
"fixed top-0 left-0 right-0 z-[100] transition-all duration-500 ease-in-out px-6 py-4",
|
||||
scrolled
|
||||
? "bg-black/40 backdrop-blur-xl border-b border-white/5 py-3"
|
||||
: "bg-transparent py-6"
|
||||
)}
|
||||
>
|
||||
<nav className="max-w-7xl mx-auto flex items-center justify-between">
|
||||
{/* Logo com Identidade Visual de Engenheiro */}
|
||||
<motion.a
|
||||
href="#home"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
const element = document.querySelector("#home");
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
}
|
||||
}}
|
||||
whileHover={{ scale: 1.05 }}
|
||||
className="flex items-center gap-2 cursor-pointer"
|
||||
<header
|
||||
className="fixed left-0 right-0 top-0 z-50 border-b backdrop-blur-xl"
|
||||
style={{
|
||||
borderColor: "rgba(255,255,255,.1)",
|
||||
background: "rgba(10,10,10,.96)",
|
||||
}}
|
||||
>
|
||||
<div className="mx-auto flex h-[52px] w-full max-w-[1240px] items-center justify-between px-6 lg:px-16">
|
||||
<a href="#home" className="flex items-center gap-3 no-underline">
|
||||
<Logo variant="mark" size="sm" theme="dark" />
|
||||
<span
|
||||
className="font-display text-sm font-extrabold tracking-tight"
|
||||
style={{ color: "#FFFFFF" }}
|
||||
>
|
||||
<div className="w-8 h-8 flex items-center justify-center">
|
||||
<Image
|
||||
src="/edev_logo.svg"
|
||||
alt="Evandro.dev Logo"
|
||||
width={32}
|
||||
height={32}
|
||||
className="w-8 h-8"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
<span className="text-xl font-black tracking-tighter text-white">
|
||||
EVANDRO<span className="text-blue-500">.DEV</span>
|
||||
</span>
|
||||
</motion.a>
|
||||
evandro<span style={{ color: "#E8FF00" }}>.dev.br</span>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
{/* Desktop Navigation - Focada em Espaçamento Ótimo */}
|
||||
<div className="hidden md:flex items-center gap-1 bg-white/5 p-1 rounded-full border border-white/10 backdrop-blur-md">
|
||||
<nav className="hidden items-center lg:flex">
|
||||
<ul className="flex list-none items-center gap-2">
|
||||
{navItems.map((item) => (
|
||||
<a
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
const element = document.querySelector(item.href);
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
}
|
||||
}}
|
||||
className="px-5 py-2 text-xs font-bold uppercase tracking-widest text-slate-300 hover:text-white hover:bg-white/10 rounded-full transition-all duration-300 cursor-pointer"
|
||||
>
|
||||
{item.label}
|
||||
</a>
|
||||
<li key={item.href}>
|
||||
<a
|
||||
href={item.href}
|
||||
className="rounded-sm px-3 py-2 text-[10px] uppercase tracking-[0.14em] no-underline transition-all duration-200 hover:bg-white/10"
|
||||
style={{ color: "rgba(255,255,255,.86)" }}
|
||||
>
|
||||
{item.label}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Botão de Ação CTA (Call to Action) */}
|
||||
<div className="hidden md:block">
|
||||
<a
|
||||
href="/Profile.pdf"
|
||||
download="Evandro_Fonseca_Junior_CV.pdf"
|
||||
className="px-6 py-2 bg-white text-black text-xs font-bold uppercase tracking-widest rounded-full hover:bg-blue-500 hover:text-white transition-all duration-500 inline-block"
|
||||
>
|
||||
Download CV
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Mobile Toggle */}
|
||||
<button
|
||||
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||
className="md:hidden p-2 text-white bg-white/5 rounded-lg border border-white/10"
|
||||
>
|
||||
{isMenuOpen ? <XIcon size={20} /> : <MenuIcon size={20} />}
|
||||
</button>
|
||||
</ul>
|
||||
</nav>
|
||||
</motion.header>
|
||||
|
||||
{/* Mobile Menu Overlay com Framer Motion para fluidez */}
|
||||
<AnimatePresence>
|
||||
{isMenuOpen && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: "100%" }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
exit={{ opacity: 0, x: "100%" }}
|
||||
transition={{ type: "spring", damping: 25, stiffness: 200 }}
|
||||
className="fixed inset-0 z-[110] bg-black/95 backdrop-blur-2xl md:hidden flex flex-col items-center justify-center gap-8"
|
||||
<div className="flex items-center gap-4">
|
||||
<span
|
||||
className="hidden rounded-full border px-[10px] py-1 text-[9px] uppercase tracking-[0.12em] lg:inline-block"
|
||||
style={{
|
||||
borderColor: "rgba(255,255,255,.15)",
|
||||
color: "rgba(255,255,255,.35)",
|
||||
}}
|
||||
>
|
||||
<button
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
className="absolute top-8 right-8 p-4 text-white/50 hover:text-white"
|
||||
>
|
||||
<XIcon size={32} />
|
||||
</button>
|
||||
|
||||
{navItems.map((item, i) => (
|
||||
<motion.a
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: i * 0.1 }}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setIsMenuOpen(false);
|
||||
const element = document.querySelector(item.href);
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
}
|
||||
}}
|
||||
className="text-3xl font-black text-white uppercase tracking-tighter hover:text-blue-500 transition-colors cursor-pointer"
|
||||
>
|
||||
{item.label}
|
||||
</motion.a>
|
||||
v1.0 / 2026
|
||||
</span>
|
||||
<a
|
||||
href="/Profile.pdf"
|
||||
download="Evandro_Fonseca_Junior_CV.pdf"
|
||||
className="hidden rounded-sm px-4 py-2 text-[10px] font-semibold uppercase tracking-[0.1em] no-underline transition-all duration-200 hover:shadow-[0_0_0_1px_rgba(232,255,0,.8),0_8px_24px_rgba(232,255,0,.35)] lg:inline-flex"
|
||||
style={{ background: "#E8FF00", color: "#0A0A0A" }}
|
||||
>
|
||||
Download CV
|
||||
</a>
|
||||
<button
|
||||
type="button"
|
||||
className="flex h-8 w-8 flex-col items-center justify-center gap-1.5 border-none bg-transparent lg:hidden"
|
||||
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
||||
aria-label="Menu"
|
||||
>
|
||||
<span
|
||||
className={`h-0.5 w-5 bg-white transition-transform duration-200 ${mobileMenuOpen ? "translate-y-2 rotate-45" : ""}`}
|
||||
/>
|
||||
<span
|
||||
className={`h-0.5 w-5 bg-white transition-opacity duration-200 ${mobileMenuOpen ? "opacity-0" : ""}`}
|
||||
/>
|
||||
<span
|
||||
className={`h-0.5 w-5 bg-white transition-transform duration-200 ${mobileMenuOpen ? "-translate-y-2 -rotate-45" : ""}`}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`absolute left-0 right-0 top-[52px] overflow-hidden border-b backdrop-blur-xl transition-all duration-300 lg:hidden ${mobileMenuOpen ? "max-h-[420px] opacity-100" : "max-h-0 opacity-0"}`}
|
||||
style={{
|
||||
borderColor: "rgba(255,255,255,.1)",
|
||||
background: "rgba(10,10,10,.98)",
|
||||
}}
|
||||
>
|
||||
<nav className="px-6 py-4">
|
||||
<ul className="flex list-none flex-col gap-2">
|
||||
{navItems.map((item) => (
|
||||
<li key={item.href}>
|
||||
<a
|
||||
href={item.href}
|
||||
className="block rounded-sm px-3 py-3 text-[11px] uppercase tracking-[0.14em] no-underline transition-all duration-200 hover:bg-white/10"
|
||||
style={{ color: "rgba(255,255,255,.9)" }}
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
>
|
||||
{item.label}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
<motion.a
|
||||
href="/Profile.pdf"
|
||||
download="Evandro_Fonseca_Junior_CV.pdf"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: navItems.length * 0.1 }}
|
||||
className="mt-8 px-8 py-4 bg-white text-black text-xl font-black uppercase tracking-tighter rounded-full hover:bg-blue-500 hover:text-white transition-all duration-300"
|
||||
>
|
||||
Download CV
|
||||
</motion.a>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</>
|
||||
<li>
|
||||
<a
|
||||
href="/Profile.pdf"
|
||||
download="Evandro_Fonseca_Junior_CV.pdf"
|
||||
className="mt-2 inline-flex rounded-sm px-4 py-3 text-[10px] font-semibold uppercase tracking-[0.1em] no-underline"
|
||||
style={{ background: "#E8FF00", color: "#0A0A0A" }}
|
||||
>
|
||||
Download CV
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user