220 lines
9.8 KiB
TypeScript
220 lines
9.8 KiB
TypeScript
import { useState } from 'react';
|
||
import { Link } from '@tanstack/react-router';
|
||
import { useAuth } from '../../contexts/AuthContext';
|
||
import { Button } from '../ui/button';
|
||
import { ThemeToggle } from './ThemeToggle';
|
||
import { Menu, X, Zap } from 'lucide-react';
|
||
|
||
const mkMonths = ['Јануари', 'Февруари', 'Март', 'Април', 'Мај', 'Јуни', 'Јули', 'Август', 'Септември', 'Октомври', 'Ноември', 'Декември'];
|
||
const mkWeekdays = ['Понеделник', 'Вторник', 'Среда', 'Четврток', 'Петок', 'Сабота', 'Недела'];
|
||
|
||
const formatDateMk = () => {
|
||
const d = new Date();
|
||
return `${mkWeekdays[d.getDay()]}, ${d.getDate()} ${mkMonths[d.getMonth()]} ${d.getFullYear()}`;
|
||
};
|
||
|
||
export function Header() {
|
||
const { user, logout, isAuthenticated, hasRole } = useAuth();
|
||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
||
|
||
const toggleMobileMenu = () => {
|
||
setIsMobileMenuOpen(!isMobileMenuOpen);
|
||
};
|
||
|
||
const closeMobileMenu = () => {
|
||
setIsMobileMenuOpen(false);
|
||
};
|
||
|
||
const navLinks = [
|
||
{ to: '/', label: 'Почетна' },
|
||
{ to: '/sport', label: 'Спорт' },
|
||
{ to: '/art', label: 'Уметност' },
|
||
{ to: '/science', label: 'Наука' },
|
||
{ to: '/archive', label: 'Архива' },
|
||
{ to: '/live-blogs', label: 'LIVE' },
|
||
];
|
||
|
||
const adminLinks = [
|
||
{ to: '/admin', label: 'Admin' },
|
||
{ to: '/admin/live-blogs/create', label: '+ New Live' },
|
||
];
|
||
|
||
return (
|
||
<header className="sticky top-0 z-50 border-b-4 border-foreground bg-[hsl(var(--background))]">
|
||
<div className="border-b-2 border-foreground/20">
|
||
<div className="container mx-auto max-w-6xl px-4">
|
||
<div className="flex items-center justify-between py-2">
|
||
<div className="hidden md:flex items-center gap-2 text-xs font-mono uppercase tracking-wider text-muted-foreground">
|
||
<Zap className="w-3 h-3" />
|
||
<span>Сатирични вести од Македонија</span>
|
||
</div>
|
||
<div className="text-xs font-mono uppercase tracking-wider text-muted-foreground">
|
||
{formatDateMk()}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="container mx-auto max-w-6xl px-4 py-4">
|
||
<div className="flex items-center justify-between">
|
||
<Link to="/" className="group">
|
||
<h1 className="text-4xl md:text-5xl font-display tracking-tight">
|
||
<span className="inline-block transition-transform group-hover:-translate-y-1 group-hover:translate-x-1">P</span>
|
||
<span className="inline-block transition-transform group-hover:-translate-y-1 group-hover:translate-x-0.5 delay-75">l</span>
|
||
<span className="inline-block transition-transform group-hover:-translate-y-1 group-hover:translate-x-0 delay-100">a</span>
|
||
<span className="inline-block transition-transform group-hover:-translate-y-1 group-hover:translate-x-0.5 delay-150">c</span>
|
||
<span className="inline-block transition-transform group-hover:-translate-y-1 group-hover:translate-x-0 delay-200">e</span>
|
||
<span className="inline-block transition-transform group-hover:-translate-y-1 group-hover:translate-x-0.5 delay-250">b</span>
|
||
<span className="inline-block transition-transform group-hover:-translate-y-1 group-hover:translate-x-0 delay-300">o</span>
|
||
<span className="text-accent inline-block transition-transform group-hover:-translate-y-1 group-hover:translate-x-0.5 delay-350">.</span>
|
||
<span className="inline-block transition-transform group-hover:-translate-y-1 group-hover:translate-x-1 delay-400">m</span>
|
||
<span className="inline-block transition-transform group-hover:-translate-y-1 group-hover:translate-x-0.5 delay-500">k</span>
|
||
</h1>
|
||
</Link>
|
||
|
||
<div className="hidden md:flex items-center gap-1">
|
||
{navLinks.map((link, index) => (
|
||
<Link
|
||
key={link.to}
|
||
to={link.to}
|
||
className="px-4 py-2 font-body text-sm font-medium uppercase tracking-wider border-2 border-transparent hover:border-foreground hover:bg-accent hover:text-foreground transition-all duration-150"
|
||
style={{ animationDelay: `${index * 50}ms` }}
|
||
>
|
||
{link.label}
|
||
</Link>
|
||
))}
|
||
|
||
{isAuthenticated ? (
|
||
<>
|
||
{(hasRole('admin') || hasRole('contributor')) && (
|
||
<>
|
||
{adminLinks.map((link) => (
|
||
<Link
|
||
key={link.to}
|
||
to={link.to}
|
||
className="px-3 py-2 font-body text-sm font-medium uppercase tracking-wider border-2 border-accent bg-accent text-foreground hover:bg-foreground hover:text-accent transition-all duration-150"
|
||
>
|
||
{link.label}
|
||
</Link>
|
||
))}
|
||
</>
|
||
)}
|
||
|
||
<div className="flex items-center gap-3 ml-4 pl-4 border-l-2 border-foreground/20">
|
||
<span className="font-body text-xs uppercase tracking-wider text-muted-foreground">
|
||
{user?.username}
|
||
</span>
|
||
<Button
|
||
variant="brutal"
|
||
size="sm"
|
||
onClick={logout}
|
||
>
|
||
OUT
|
||
</Button>
|
||
</div>
|
||
</>
|
||
) : (
|
||
<Link to="/auth" className="px-4 py-2 font-body text-sm font-medium uppercase tracking-wider border-2 border-foreground bg-foreground text-background hover:bg-accent hover:text-foreground hover:border-accent transition-all duration-150 ml-2">
|
||
Login
|
||
</Link>
|
||
)}
|
||
|
||
<div className="ml-4 pl-4 border-l-2 border-foreground/20 flex items-center gap-3">
|
||
<a
|
||
href="https://www.buymeacoffee.com/placebomk"
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
className="hidden lg:block border-2 border-foreground bg-yellow-400 hover:bg-yellow-300 transition-colors px-3 py-1 font-body text-xs font-bold uppercase tracking-wider"
|
||
>
|
||
☕ Купи ми кафе
|
||
</a>
|
||
<ThemeToggle />
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex items-center gap-2 md:hidden">
|
||
<ThemeToggle />
|
||
<Button
|
||
variant="brutal"
|
||
size="icon"
|
||
onClick={toggleMobileMenu}
|
||
className="h-10 w-10"
|
||
aria-label={isMobileMenuOpen ? 'Close menu' : 'Open menu'}
|
||
>
|
||
{isMobileMenuOpen ? (
|
||
<X className="h-5 w-5" />
|
||
) : (
|
||
<Menu className="h-5 w-5" />
|
||
)}
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
|
||
{isMobileMenuOpen && (
|
||
<div className="md:hidden mt-4 pt-4 border-t-4 border-foreground animate-scale-in">
|
||
<div className="flex flex-col gap-2">
|
||
{navLinks.map((link, index) => (
|
||
<Link
|
||
key={link.to}
|
||
to={link.to}
|
||
className="px-4 py-3 font-body text-sm font-medium uppercase tracking-wider border-2 border-foreground hover:bg-accent hover:border-accent transition-all duration-150"
|
||
style={{ animationDelay: `${index * 50}ms` }}
|
||
onClick={closeMobileMenu}
|
||
>
|
||
{link.label}
|
||
</Link>
|
||
))}
|
||
|
||
{isAuthenticated ? (
|
||
<>
|
||
{(hasRole('admin') || hasRole('contributor')) && (
|
||
<div className="pt-3 mt-2 border-t-4 border-foreground">
|
||
<p className="font-body text-xs uppercase tracking-wider text-muted-foreground mb-2 pl-2">Admin</p>
|
||
{adminLinks.map((link) => (
|
||
<Link
|
||
key={link.to}
|
||
to={link.to}
|
||
className="px-4 py-3 font-body text-sm font-medium uppercase tracking-wider border-2 border-accent bg-accent hover:bg-foreground hover:text-accent transition-all duration-150 block"
|
||
onClick={closeMobileMenu}
|
||
>
|
||
{link.label}
|
||
</Link>
|
||
))}
|
||
</div>
|
||
)}
|
||
|
||
<div className="pt-3 mt-2 border-t-4 border-foreground">
|
||
<div className="flex items-center justify-between p-2">
|
||
<span className="font-body text-xs uppercase tracking-wider text-muted-foreground">
|
||
{user?.username}
|
||
</span>
|
||
<Button
|
||
variant="brutal"
|
||
size="sm"
|
||
onClick={() => {
|
||
logout();
|
||
closeMobileMenu();
|
||
}}
|
||
>
|
||
OUT
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</>
|
||
) : (
|
||
<Link
|
||
to="/auth"
|
||
className="px-4 py-3 font-body text-sm font-medium uppercase tracking-wider border-2 border-foreground bg-foreground text-background hover:bg-accent hover:text-foreground hover:border-accent transition-all duration-150 mt-2"
|
||
onClick={closeMobileMenu}
|
||
>
|
||
Login
|
||
</Link>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</header>
|
||
);
|
||
}
|