SportBuddy/apps/frontend/src/components/Navigation.tsx
2025-10-26 15:44:27 +01:00

309 lines
16 KiB
TypeScript

'use client';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { useState, useEffect, useRef } from 'react';
import { ThemeToggle } from './ThemeToggle';
import { useSession, signOut } from '@/lib/auth-client';
export default function Navigation() {
const pathname = usePathname();
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const [isUserMenuOpen, setIsUserMenuOpen] = useState(false);
const { data: session } = useSession();
const userMenuRef = useRef<HTMLDivElement>(null);
const navLinks = [
{ href: '/', label: 'Domov' },
{ href: '/activities', label: 'Aktivity' },
{ href: '/venues', label: 'Športoviská' },
{ href: '/my-activities', label: 'Moje aktivity' },
];
const handleSignOut = async () => {
await signOut();
window.location.href = '/';
};
// Close user menu when clicking outside
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (userMenuRef.current && !userMenuRef.current.contains(event.target as Node)) {
setIsUserMenuOpen(false);
}
};
if (isUserMenuOpen) {
document.addEventListener('mousedown', handleClickOutside);
}
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [isUserMenuOpen]);
return (
<nav className="sticky top-3 z-50 px-4 sm:px-6 lg:px-8">
<div className="max-w-7xl mx-auto site-nav transition-colors duration-300 acrylic rounded-2xl px-4 sm:px-6 lg:px-8" style={{ boxShadow: 'var(--shadow-lg)' }}>
<div className="flex justify-between items-center h-20 px-2">
{/* Logo */}
<Link href="/" className="flex items-center space-x-3 group">
<div className="w-12 h-12 gradient-primary rounded-lg flex items-center justify-center group-hover:scale-105 transition-transform duration-200" style={{ boxShadow: 'var(--shadow-sm)' }}>
<svg className="w-7 h-7 text-[color:var(--fluent-text-secondary)]" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 2L2 7L12 12L22 7L12 2Z" fill="currentColor" fillOpacity="0.9"/>
<path d="M2 17L12 22L22 17" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
<path d="M2 12L12 17L22 12" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
<circle cx="12" cy="7" r="1.5" fill="rgba(250, 250, 250, 0.95)"/>
</svg>
</div>
<span className="text-2xl font-semibold text-[color:var(--fluent-text)] hidden sm:block">
SportBuddy
</span>
</Link>
{/* Desktop Navigation */}
<div className="hidden md:flex items-center space-x-2">
{navLinks.map((link) => (
<Link
key={link.href}
href={link.href}
className={`
px-6 py-2.5 rounded-md font-medium text-lg
hover-glow reveal-effect
transition-all duration-200 ease-out
${pathname === link.href
? 'bg-[color:var(--fluent-accent-light)] text-[color:var(--fluent-accent)] font-semibold'
: 'text-[color:var(--fluent-text-secondary)] hover:text-[color:var(--fluent-text)]'
}
`}
>
{link.label}
</Link>
))}
</div>
{/* Auth Buttons / User Menu */}
<div className="hidden md:flex items-center space-x-4">
<ThemeToggle />
{session ? (
<div className="relative" ref={userMenuRef}>
<button
onClick={() => setIsUserMenuOpen(!isUserMenuOpen)}
className="flex items-center space-x-3 px-4 py-2 rounded-lg hover-glow reveal-effect transition-all duration-200"
style={{ boxShadow: 'var(--shadow-sm)' }}
>
<div className="w-10 h-10 gradient-primary rounded-full flex items-center justify-center">
<svg className="w-6 h-6 text-[color:var(--fluent-text-secondary)]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
</div>
<span className="text-lg font-semibold text-[color:var(--fluent-text)]">{session.user?.name}</span>
</button>
{isUserMenuOpen && (
<div
className="fixed left-0 right-0 mt-3 mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 z-50"
style={{
top: 'calc(3rem + 5rem)', // top-3 + navbar height
animation: 'slideDown 0.3s ease-out both'
}}
>
<div className="acrylic-strong border border-[color:var(--fluent-border)] rounded-xl overflow-hidden reveal-effect w-56 ml-auto"
style={{
boxShadow: 'var(--shadow-xl)'
}}>
<Link
href="/dashboard"
className="block px-4 py-3 text-base font-medium text-[color:var(--fluent-text)] hover:bg-[color:var(--fluent-surface-secondary)]/50 transition-all"
onClick={() => setIsUserMenuOpen(false)}
>
<div className="flex items-center space-x-3">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
</svg>
<span>Dashboard</span>
</div>
</Link>
<button
onClick={handleSignOut}
className="w-full text-left px-4 py-3 text-base font-medium text-red-600 dark:text-red-400 hover:bg-red-50/50 dark:hover:bg-red-950/20 transition-all"
>
<div className="flex items-center space-x-3">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
</svg>
<span>Odhlásiť sa</span>
</div>
</button>
</div>
</div>
)}
</div>
) : (
<>
<Link
href="/auth/signin"
className="px-6 py-2.5 text-lg font-semibold bg-blue-50/50 text-blue-600 hover:text-blue-700 hover:bg-blue-100/60 dark:bg-blue-950/40 dark:text-blue-400 dark:hover:text-blue-300 dark:hover:bg-blue-950/60 rounded-lg hover-glow reveal-effect transition-all duration-200"
style={{
boxShadow: 'var(--shadow-sm)',
backdropFilter: 'blur(20px)',
WebkitBackdropFilter: 'blur(20px)'
}}
>
Prihlásiť sa
</Link>
<Link
href="/auth/signup"
className="px-6 py-2.5 text-lg bg-[color:var(--fluent-accent)]/90 text-[color:var(--fluent-text-secondary)] dark:text-[color:var(--fluent-text)] rounded-lg font-semibold hover:bg-[color:var(--fluent-accent-hover)] hover-glow reveal-effect transition-all duration-200"
style={{
boxShadow: 'var(--shadow-sm)',
backdropFilter: 'blur(20px)',
WebkitBackdropFilter: 'blur(20px)'
}}
>
Registrovať
</Link>
</>
)}
</div>
{/* Mobile Theme Toggle & Menu Button */}
<div className="md:hidden flex items-center space-x-3">
<ThemeToggle />
<button
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
className="p-2 rounded-md hover:bg-[color:var(--fluent-surface-secondary)] transition-all duration-200"
>
<div className="w-6 h-6 flex flex-col justify-center items-center space-y-1.5">
<span
className={`block w-6 h-0.5 bg-[color:var(--fluent-text)] transition-all duration-300 ${
isMobileMenuOpen ? 'rotate-45 translate-y-2' : ''
}`}
/>
<span
className={`block w-6 h-0.5 bg-[color:var(--fluent-text)] transition-all duration-300 ${
isMobileMenuOpen ? 'opacity-0' : ''
}`}
/>
<span
className={`block w-6 h-0.5 bg-[color:var(--fluent-text)] transition-all duration-300 ${
isMobileMenuOpen ? '-rotate-45 -translate-y-2' : ''
}`}
/>
</div>
</button>
</div>
</div>
{/* Mobile Menu */}
<div
className={`md:hidden overflow-hidden transition-all duration-300 ease-out border-t border-[color:var(--fluent-divider)] ${
isMobileMenuOpen ? 'max-h-[500px] opacity-100' : 'max-h-0 opacity-0 border-t-0'
}`}
>
<div className="pb-3 pt-2 space-y-1">
{navLinks.map((link, index) => (
<Link
key={link.href}
href={link.href}
className={`
block px-4 py-3 rounded-md font-medium text-base
transition-all duration-150
${pathname === link.href
? 'bg-[color:var(--fluent-accent-light)] text-[color:var(--fluent-accent)]'
: 'text-[color:var(--fluent-text-secondary)] hover:bg-[color:var(--fluent-surface-secondary)] hover:text-[color:var(--fluent-text)]'
}
`}
style={{
animation: isMobileMenuOpen ? `slideDown 0.4s ease-out ${index * 0.08}s both` : 'none'
}}
onClick={() => setIsMobileMenuOpen(false)}
>
{link.label}
</Link>
))}
<div className="pt-3 border-t border-[color:var(--fluent-divider)] space-y-3 mt-3">
{session ? (
<>
<div className="flex items-center space-x-3 px-4 py-3">
<div className="w-10 h-10 gradient-primary rounded-full flex items-center justify-center">
<svg className="w-6 h-6 text-[color:var(--fluent-text-secondary)]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
</div>
<span className="text-base font-semibold text-[color:var(--fluent-text)]">{session.user?.name}</span>
</div>
<Link
href="/dashboard"
className="block px-4 py-3 text-base font-semibold bg-blue-50/50 text-blue-600 hover:text-blue-700 hover:bg-blue-100/60 dark:bg-blue-950/40 dark:text-blue-400 dark:hover:text-blue-300 dark:hover:bg-blue-950/60 rounded-lg hover-glow reveal-effect transition-all duration-200"
style={{
boxShadow: 'var(--shadow-sm)',
backdropFilter: 'blur(20px)',
WebkitBackdropFilter: 'blur(20px)',
animation: isMobileMenuOpen ? `slideDown 0.4s ease-out ${navLinks.length * 0.08 + 0.08}s both` : 'none'
}}
onClick={() => setIsMobileMenuOpen(false)}
>
<div className="flex items-center space-x-3">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
</svg>
<span>Dashboard</span>
</div>
</Link>
<button
onClick={handleSignOut}
className="w-full block px-4 py-3 text-base text-left font-semibold text-red-600 dark:text-red-400 hover:bg-red-50/50 dark:hover:bg-red-950/20 rounded-lg hover-glow reveal-effect transition-all duration-200"
style={{
boxShadow: 'var(--shadow-sm)',
backdropFilter: 'blur(20px)',
WebkitBackdropFilter: 'blur(20px)',
animation: isMobileMenuOpen ? `slideDown 0.4s ease-out ${navLinks.length * 0.08 + 0.16}s both` : 'none'
}}
>
<div className="flex items-center space-x-3">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
</svg>
<span>Odhlásiť sa</span>
</div>
</button>
</>
) : (
<>
<Link
href="/auth/signin"
className="block px-4 py-3 text-base font-semibold bg-blue-50/50 text-blue-600 hover:text-blue-700 hover:bg-blue-100/60 dark:bg-blue-950/40 dark:text-blue-400 dark:hover:text-blue-300 dark:hover:bg-blue-950/60 rounded-lg hover-glow reveal-effect transition-all duration-200 text-center"
style={{
boxShadow: 'var(--shadow-sm)',
backdropFilter: 'blur(20px)',
WebkitBackdropFilter: 'blur(20px)',
animation: isMobileMenuOpen ? `slideDown 0.4s ease-out ${navLinks.length * 0.08 + 0.08}s both` : 'none'
}}
onClick={() => setIsMobileMenuOpen(false)}
>
Prihlásiť sa
</Link>
<Link
href="/auth/signup"
className="block px-4 py-3 text-base bg-[color:var(--fluent-accent)]/90 text-[color:var(--fluent-text-secondary)] dark:text-[color:var(--fluent-text)] rounded-lg text-center font-semibold hover:bg-[color:var(--fluent-accent-hover)] hover-glow reveal-effect transition-all duration-200"
style={{
boxShadow: 'var(--shadow-sm)',
backdropFilter: 'blur(20px)',
WebkitBackdropFilter: 'blur(20px)',
animation: isMobileMenuOpen ? `slideDown 0.4s ease-out ${navLinks.length * 0.08 + 0.16}s both` : 'none'
}}
onClick={() => setIsMobileMenuOpen(false)}
>
Registrovať
</Link>
</>
)}
</div>
</div>
</div>
</div>
</nav>
);
}