199 lines
7.3 KiB
TypeScript
199 lines
7.3 KiB
TypeScript
'use client';
|
|
|
|
import { ArrowUp, Home, Info, Mail, Menu, X } from 'lucide-react';
|
|
import React, { useEffect, useState } from 'react';
|
|
import Link from 'next/link';
|
|
import { usePathname } from 'next/navigation';
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
|
|
const Layout = ({ children }: { children: React.ReactNode }) => {
|
|
const pathname = usePathname();
|
|
const [showScrollTop, setShowScrollTop] = useState(false);
|
|
const [isScrolled, setIsScrolled] = useState(false);
|
|
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const handleScroll = () => {
|
|
setShowScrollTop(window.scrollY > 400);
|
|
setIsScrolled(window.scrollY > 20);
|
|
};
|
|
|
|
window.addEventListener('scroll', handleScroll, { passive: true });
|
|
return () => window.removeEventListener('scroll', handleScroll);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
setIsMobileMenuOpen(false);
|
|
}, [pathname]);
|
|
|
|
const scrollToTop = () => {
|
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
};
|
|
|
|
const isActive = (path: string) => pathname === path;
|
|
|
|
const navLinks = [
|
|
{ href: '/', label: 'Startseite', icon: Home },
|
|
{ href: '/ueber-uns', label: 'Über uns', icon: Info },
|
|
{ href: '/kontakt', label: 'Kontakt', icon: Mail },
|
|
];
|
|
|
|
return (
|
|
<div className="min-h-screen flex flex-col font-sans">
|
|
<header
|
|
className={`fixed top-0 left-0 right-0 z-[100] transition-all duration-300 flex items-center py-1 ${
|
|
isScrolled
|
|
? 'bg-white/80 backdrop-blur-lg border-b border-slate-200'
|
|
: 'bg-gradient-to-b from-white/50 to-transparent'
|
|
}`}
|
|
>
|
|
<div className="container-custom flex justify-between items-center w-full">
|
|
<Link
|
|
href="/"
|
|
className="relative z-10 flex items-center group"
|
|
aria-label="MB Grid Solutions - Zur Startseite"
|
|
>
|
|
<img
|
|
src="/assets/logo.png"
|
|
alt="MB Grid Solutions"
|
|
className={`transition-all duration-300 object-contain ${isScrolled ? 'h-[60px] md:h-[80px] my-[-5px]' : 'h-[100px] md:h-[140px] my-[-20px]'}`}
|
|
loading="eager"
|
|
/>
|
|
</Link>
|
|
|
|
{/* Desktop Navigation */}
|
|
<nav className="hidden md:flex items-center gap-1" aria-label="Hauptnavigation">
|
|
{navLinks.map((link) => (
|
|
<Link
|
|
key={link.href}
|
|
href={link.href}
|
|
className={`px-4 py-2 rounded-full text-sm font-medium transition-all ${
|
|
isActive(link.href)
|
|
? 'text-accent bg-accent/5'
|
|
: `${isScrolled ? 'text-slate-600' : 'text-slate-900'} hover:text-primary hover:bg-slate-50`
|
|
}`}
|
|
>
|
|
{link.label}
|
|
</Link>
|
|
))}
|
|
<Link
|
|
href="/kontakt"
|
|
className="ml-4 btn-primary !py-2 !px-5 !text-sm"
|
|
>
|
|
Projekt anfragen
|
|
</Link>
|
|
</nav>
|
|
|
|
{/* Mobile Menu Toggle */}
|
|
<button
|
|
className="md:hidden p-2 text-slate-600 hover:text-primary transition-colors"
|
|
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
|
|
aria-label="Menü öffnen"
|
|
>
|
|
{isMobileMenuOpen ? <X size={24} /> : <Menu size={24} />}
|
|
</button>
|
|
</div>
|
|
</header>
|
|
|
|
{/* Mobile Menu Overlay */}
|
|
<AnimatePresence>
|
|
{isMobileMenuOpen && (
|
|
<motion.div
|
|
initial={{ opacity: 0, y: -20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
exit={{ opacity: 0, y: -20 }}
|
|
className="fixed inset-0 z-[90] bg-white pt-24 px-6 md:hidden"
|
|
>
|
|
<nav className="flex flex-col gap-4">
|
|
{navLinks.map((link) => (
|
|
<Link
|
|
key={link.href}
|
|
href={link.href}
|
|
className={`flex items-center gap-4 p-4 rounded-xl text-lg font-semibold transition-all ${
|
|
isActive(link.href)
|
|
? 'text-accent bg-accent/5'
|
|
: 'text-slate-600 hover:text-primary hover:bg-slate-50'
|
|
}`}
|
|
>
|
|
<link.icon size={24} />
|
|
{link.label}
|
|
</Link>
|
|
))}
|
|
<Link
|
|
href="/kontakt"
|
|
className="mt-4 btn-primary w-full py-4 text-center"
|
|
>
|
|
Projekt anfragen
|
|
</Link>
|
|
</nav>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
|
|
<main className="flex-grow">
|
|
{children}
|
|
</main>
|
|
|
|
<button
|
|
onClick={scrollToTop}
|
|
className={`fixed bottom-8 right-8 w-12 h-12 bg-primary text-white rounded-full flex items-center justify-center cursor-pointer z-[80] shadow-xl transition-all duration-300 hover:-translate-y-1 hover:bg-accent ${
|
|
showScrollTop ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-4 pointer-events-none'
|
|
}`}
|
|
aria-label="Nach oben scrollen"
|
|
>
|
|
<ArrowUp size={20} strokeWidth={2.5} />
|
|
</button>
|
|
|
|
<footer className="bg-slate-900 text-slate-300 py-16 md:py-24">
|
|
<div className="container-custom">
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-12 mb-16">
|
|
<div className="lg:col-span-2">
|
|
<img
|
|
src="/assets/logo.png"
|
|
alt="MB Grid Solutions"
|
|
className="h-20 mb-8 brightness-0 invert opacity-80"
|
|
loading="lazy"
|
|
/>
|
|
<p className="text-slate-400 max-w-md leading-relaxed mb-8">
|
|
Ihr spezialisierter Partner für herstellerneutrale technische Beratung und Projektbegleitung bei Energiekabelprojekten bis 110 kV.
|
|
</p>
|
|
<div className="flex gap-4">
|
|
{/* Social links could go here */}
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<h4 className="text-white font-bold mb-6">Navigation</h4>
|
|
<nav className="flex flex-col gap-4">
|
|
{navLinks.map((link) => (
|
|
<Link key={link.href} href={link.href} className="hover:text-accent transition-colors">
|
|
{link.label}
|
|
</Link>
|
|
))}
|
|
</nav>
|
|
</div>
|
|
|
|
<div>
|
|
<h4 className="text-white font-bold mb-6">Rechtliches</h4>
|
|
<nav className="flex flex-col gap-4">
|
|
<Link href="/impressum" className="hover:text-accent transition-colors">Impressum</Link>
|
|
<Link href="/datenschutz" className="hover:text-accent transition-colors">Datenschutz</Link>
|
|
<Link href="/agb" className="hover:text-accent transition-colors">AGB</Link>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="pt-8 border-t border-slate-800 flex flex-col md:flex-row justify-between items-center gap-4 text-sm text-slate-500">
|
|
<p>© {new Date().getFullYear()} MB Grid Solutions GmbH. Alle Rechte vorbehalten.</p>
|
|
<p className="flex items-center gap-2">
|
|
Made with <span className="text-accent">precision</span> in Germany
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Layout;
|