Initial commit: CMS completo com gerenciamento de leads e personalização de tema
This commit is contained in:
230
frontend/src/components/Header.tsx
Normal file
230
frontend/src/components/Header.tsx
Normal file
@@ -0,0 +1,230 @@
|
||||
"use client";
|
||||
|
||||
import Link from 'next/link';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useTheme } from "next-themes";
|
||||
import { useLanguage } from '@/contexts/LanguageContext';
|
||||
|
||||
export default function Header() {
|
||||
const [isSearchOpen, setIsSearchOpen] = useState(false);
|
||||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
||||
const { theme, setTheme } = useTheme();
|
||||
const { language, setLanguage, t } = useLanguage();
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
// Prevent scrolling when mobile menu is open
|
||||
useEffect(() => {
|
||||
if (isMobileMenuOpen) {
|
||||
document.body.style.overflow = 'hidden';
|
||||
} else {
|
||||
document.body.style.overflow = 'unset';
|
||||
}
|
||||
return () => {
|
||||
document.body.style.overflow = 'unset';
|
||||
};
|
||||
}, [isMobileMenuOpen]);
|
||||
|
||||
const toggleTheme = () => {
|
||||
setTheme(theme === 'dark' ? 'light' : 'dark');
|
||||
};
|
||||
|
||||
const cycleLanguage = () => {
|
||||
const langs: ('PT' | 'EN' | 'ES')[] = ['PT', 'EN', 'ES'];
|
||||
const currentIndex = langs.indexOf(language);
|
||||
const nextIndex = (currentIndex + 1) % langs.length;
|
||||
setLanguage(langs[nextIndex]);
|
||||
};
|
||||
|
||||
return (
|
||||
<header className="w-full bg-white dark:bg-secondary shadow-sm sticky top-0 z-50 transition-colors duration-300">
|
||||
<div className="container mx-auto px-4 h-20 flex items-center justify-between gap-4">
|
||||
<Link href="/" className="flex items-center gap-3 shrink-0 group mr-auto z-50 relative">
|
||||
<i className="ri-building-2-fill text-4xl text-primary group-hover:scale-105 transition-transform"></i>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-3xl font-bold text-secondary dark:text-white font-headline leading-none">OCCTO</span>
|
||||
<span className="text-[10px] font-bold text-primary bg-primary/10 px-2 py-1 rounded-md uppercase tracking-wider">ENG.</span>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<div className="hidden md:flex items-center gap-4">
|
||||
{/* Search Bar */}
|
||||
<div className={`flex items-center bg-gray-100 dark:bg-white/10 rounded-full transition-all duration-300 ${isSearchOpen ? 'w-64 px-4 py-2' : 'w-10 h-10 justify-center cursor-pointer hover:bg-gray-200 dark:hover:bg-white/20'}`} onClick={() => !isSearchOpen && setIsSearchOpen(true)}>
|
||||
<i className={`ri-search-line text-gray-500 dark:text-gray-300 ${isSearchOpen ? 'mr-2' : 'text-lg'}`}></i>
|
||||
{isSearchOpen && (
|
||||
<input
|
||||
type="text"
|
||||
placeholder={t('nav.search')}
|
||||
autoFocus
|
||||
onBlur={() => setIsSearchOpen(false)}
|
||||
className="bg-transparent border-none outline-none text-sm w-full text-gray-600 dark:text-gray-200 placeholder-gray-400"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<nav className="flex items-center gap-6 mr-4">
|
||||
<Link href="/" className="flex items-center gap-2 text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-primary font-medium transition-colors group">
|
||||
<i className="ri-home-4-line text-lg group-hover:scale-110 transition-transform"></i>
|
||||
<span className="hidden lg:inline">{t('nav.home')}</span>
|
||||
</Link>
|
||||
<Link href="/servicos" className="flex items-center gap-2 text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-primary font-medium transition-colors group">
|
||||
<i className="ri-tools-line text-lg group-hover:scale-110 transition-transform"></i>
|
||||
<span className="hidden lg:inline">{t('nav.services')}</span>
|
||||
</Link>
|
||||
<Link href="/projetos" className="flex items-center gap-2 text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-primary font-medium transition-colors group">
|
||||
<i className="ri-briefcase-line text-lg group-hover:scale-110 transition-transform"></i>
|
||||
<span className="hidden lg:inline">{t('nav.projects')}</span>
|
||||
</Link>
|
||||
<Link href="/contato" className="flex items-center gap-2 text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-primary font-medium transition-colors group">
|
||||
<i className="ri-mail-send-line text-lg group-hover:scale-110 transition-transform"></i>
|
||||
<span className="hidden lg:inline">{t('nav.contact')}</span>
|
||||
</Link>
|
||||
<Link href="/sobre" className="flex items-center gap-2 text-gray-600 dark:text-gray-300 hover:text-primary dark:hover:text-primary font-medium transition-colors group">
|
||||
<i className="ri-user-line text-lg group-hover:scale-110 transition-transform"></i>
|
||||
<span className="hidden lg:inline">{t('nav.about')}</span>
|
||||
</Link>
|
||||
</nav>
|
||||
|
||||
<div className="shrink-0 ml-2">
|
||||
<Link
|
||||
href="/contato"
|
||||
className="px-6 py-2.5 bg-primary text-white rounded-lg font-bold hover:bg-orange-600 transition-colors flex items-center gap-2"
|
||||
>
|
||||
<i className="ri-whatsapp-line"></i>
|
||||
<span className="hidden xl:inline">{t('nav.contact_us')}</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 pl-4 border-l border-gray-200 dark:border-white/10">
|
||||
{/* Theme Toggle */}
|
||||
<button
|
||||
onClick={toggleTheme}
|
||||
className="w-10 h-10 rounded-full bg-gray-100 dark:bg-white/10 flex items-center justify-center text-gray-600 dark:text-yellow-400 hover:bg-gray-200 dark:hover:bg-white/20 transition-colors cursor-pointer"
|
||||
aria-label="Alternar tema"
|
||||
>
|
||||
{mounted && theme === 'dark' ? (
|
||||
<i className="ri-sun-line text-xl"></i>
|
||||
) : (
|
||||
<i className="ri-moon-line text-xl"></i>
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* Language Dropdown */}
|
||||
<div className="relative group">
|
||||
<button
|
||||
className="h-10 px-3 rounded-full bg-gray-100 dark:bg-white/10 flex items-center justify-center gap-2 text-gray-600 dark:text-white hover:bg-gray-200 dark:hover:bg-white/20 transition-colors font-bold text-sm cursor-pointer"
|
||||
aria-label="Alterar idioma"
|
||||
>
|
||||
<span>{language === 'PT' ? '🇧🇷' : language === 'EN' ? '🇺🇸' : '🇪🇸'}</span>
|
||||
<span>{language}</span>
|
||||
<i className="ri-arrow-down-s-line text-xs opacity-50"></i>
|
||||
</button>
|
||||
|
||||
<div className="absolute top-full right-0 pt-2 w-32 hidden group-hover:block animate-in fade-in slide-in-from-top-2 duration-200">
|
||||
<div className="bg-white dark:bg-secondary rounded-xl shadow-xl border border-gray-100 dark:border-white/10 overflow-hidden">
|
||||
<button onClick={() => setLanguage('PT')} className="w-full px-4 py-3 text-left hover:bg-gray-50 dark:hover:bg-white/5 flex items-center gap-3 transition-colors cursor-pointer">
|
||||
<span className="text-lg">🇧🇷</span>
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-200">Português</span>
|
||||
</button>
|
||||
<button onClick={() => setLanguage('EN')} className="w-full px-4 py-3 text-left hover:bg-gray-50 dark:hover:bg-white/5 flex items-center gap-3 transition-colors cursor-pointer">
|
||||
<span className="text-lg">🇺🇸</span>
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-200">English</span>
|
||||
</button>
|
||||
<button onClick={() => setLanguage('ES')} className="w-full px-4 py-3 text-left hover:bg-gray-50 dark:hover:bg-white/5 flex items-center gap-3 transition-colors cursor-pointer">
|
||||
<span className="text-lg">🇪🇸</span>
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-200">Español</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu Button */}
|
||||
<button
|
||||
className="md:hidden text-2xl text-secondary dark:text-white z-50 relative w-10 h-10 flex items-center justify-center cursor-pointer"
|
||||
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
|
||||
aria-label="Menu"
|
||||
>
|
||||
{isMobileMenuOpen ? <i className="ri-close-line"></i> : <i className="ri-menu-line"></i>}
|
||||
</button>
|
||||
|
||||
{/* Mobile Menu Overlay */}
|
||||
<div className={`fixed inset-0 bg-white dark:bg-secondary z-40 transition-transform duration-300 ease-in-out md:hidden flex flex-col pt-24 px-6 overflow-y-auto ${isMobileMenuOpen ? 'translate-x-0' : 'translate-x-full'}`}>
|
||||
|
||||
{/* Mobile Search */}
|
||||
<div className="mb-6 relative shrink-0">
|
||||
<i className="ri-search-line absolute left-4 top-1/2 -translate-y-1/2 text-gray-400"></i>
|
||||
<input
|
||||
type="text"
|
||||
placeholder={t('nav.search')}
|
||||
className="w-full pl-11 pr-4 py-3 bg-gray-50 dark:bg-white/5 border border-gray-100 dark:border-white/10 rounded-xl text-gray-900 dark:text-white focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-all"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<nav className="flex flex-col gap-4 text-base font-medium">
|
||||
<Link href="/" onClick={() => setIsMobileMenuOpen(false)} className="flex items-center gap-3 py-2 border-b border-gray-100 dark:border-white/10 text-secondary dark:text-white">
|
||||
<i className="ri-home-4-line text-primary text-lg"></i>
|
||||
{t('nav.home')}
|
||||
</Link>
|
||||
<Link href="/servicos" onClick={() => setIsMobileMenuOpen(false)} className="flex items-center gap-3 py-2 border-b border-gray-100 dark:border-white/10 text-secondary dark:text-white">
|
||||
<i className="ri-tools-line text-primary text-lg"></i>
|
||||
{t('nav.services')}
|
||||
</Link>
|
||||
<Link href="/projetos" onClick={() => setIsMobileMenuOpen(false)} className="flex items-center gap-3 py-2 border-b border-gray-100 dark:border-white/10 text-secondary dark:text-white">
|
||||
<i className="ri-briefcase-line text-primary text-lg"></i>
|
||||
{t('nav.projects')}
|
||||
</Link>
|
||||
<Link href="/contato" onClick={() => setIsMobileMenuOpen(false)} className="flex items-center gap-3 py-2 border-b border-gray-100 dark:border-white/10 text-secondary dark:text-white">
|
||||
<i className="ri-mail-send-line text-primary text-lg"></i>
|
||||
{t('nav.contact')}
|
||||
</Link>
|
||||
<Link href="/sobre" onClick={() => setIsMobileMenuOpen(false)} className="flex items-center gap-3 py-2 border-b border-gray-100 dark:border-white/10 text-secondary dark:text-white">
|
||||
<i className="ri-user-line text-primary text-lg"></i>
|
||||
{t('nav.about')}
|
||||
</Link>
|
||||
</nav>
|
||||
|
||||
<div className="mt-6 flex flex-col gap-4 pb-8 shrink-0">
|
||||
<Link
|
||||
href="/contato"
|
||||
onClick={() => setIsMobileMenuOpen(false)}
|
||||
className="w-full py-4 bg-primary text-white rounded-xl font-bold text-center flex items-center justify-center gap-2 shadow-lg shadow-primary/20"
|
||||
>
|
||||
<i className="ri-whatsapp-line text-xl"></i>
|
||||
{t('nav.contact_us')}
|
||||
</Link>
|
||||
|
||||
<div
|
||||
className="flex items-center justify-between p-4 bg-gray-50 dark:bg-white/5 rounded-xl cursor-pointer hover:bg-gray-100 dark:hover:bg-white/10 transition-colors"
|
||||
onClick={toggleTheme}
|
||||
>
|
||||
<span className="text-sm font-bold text-gray-500 dark:text-gray-400">{t('nav.theme')}</span>
|
||||
<button
|
||||
className="w-10 h-10 rounded-full bg-white dark:bg-white/10 flex items-center justify-center text-gray-600 dark:text-yellow-400 shadow-sm transition-colors"
|
||||
>
|
||||
{mounted && theme === 'dark' ? (
|
||||
<i className="ri-sun-line text-xl"></i>
|
||||
) : (
|
||||
<i className="ri-moon-line text-xl"></i>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between p-4 bg-gray-50 dark:bg-white/5 rounded-xl">
|
||||
<span className="text-sm font-bold text-gray-500 dark:text-gray-400">{t('nav.language')}</span>
|
||||
<div className="flex gap-2">
|
||||
<button onClick={() => setLanguage('PT')} className={`w-10 h-10 rounded-lg flex items-center justify-center text-xl cursor-pointer transition-all ${language === 'PT' ? 'bg-white dark:bg-white/10 shadow-sm scale-110' : 'opacity-50 hover:opacity-100'}`}>🇧🇷</button>
|
||||
<button onClick={() => setLanguage('EN')} className={`w-10 h-10 rounded-lg flex items-center justify-center text-xl cursor-pointer transition-all ${language === 'EN' ? 'bg-white dark:bg-white/10 shadow-sm scale-110' : 'opacity-50 hover:opacity-100'}`}>🇺🇸</button>
|
||||
<button onClick={() => setLanguage('ES')} className={`w-10 h-10 rounded-lg flex items-center justify-center text-xl cursor-pointer transition-all ${language === 'ES' ? 'bg-white dark:bg-white/10 shadow-sm scale-110' : 'opacity-50 hover:opacity-100'}`}>🇪🇸</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user