219 lines
9.6 KiB
TypeScript
219 lines
9.6 KiB
TypeScript
'use client';
|
|
|
|
import Link from 'next/link';
|
|
import { usePathname } from 'next/navigation';
|
|
import { useLanguage } from '@/lib/language-context';
|
|
import { languages, Language, getTranslation } from '@/lib/translations';
|
|
import { useCustomerAuth } from '@/presentation/providers/customer-auth-provider';
|
|
import { Globe, LogIn, LogOut, Sparkles, UtensilsCrossed } from 'lucide-react';
|
|
|
|
export const LANGUAGE_BANNER_HEIGHT = 48;
|
|
export const LANGUAGE_BANNER_HEIGHT_LOGGED_IN_DESKTOP = 56;
|
|
export const LANGUAGE_BANNER_HEIGHT_LOGGED_IN_MOBILE = 160;
|
|
export const LANGUAGE_BANNER_HEIGHT_MENU_MANAGER_MOBILE = 160;
|
|
export const NAVBAR_HEIGHT = 68;
|
|
|
|
export function getLanguageBannerHeight(
|
|
isAuthenticated: boolean,
|
|
isMobile = false,
|
|
isMenuManager = false,
|
|
): number {
|
|
if (!isAuthenticated) return LANGUAGE_BANNER_HEIGHT;
|
|
if (isMobile) {
|
|
return isMenuManager
|
|
? LANGUAGE_BANNER_HEIGHT_MENU_MANAGER_MOBILE
|
|
: LANGUAGE_BANNER_HEIGHT_LOGGED_IN_MOBILE;
|
|
}
|
|
return LANGUAGE_BANNER_HEIGHT_LOGGED_IN_DESKTOP;
|
|
}
|
|
|
|
export function getHeaderTotalHeight(
|
|
isAuthenticated: boolean,
|
|
isMobile = false,
|
|
isMenuManager = false,
|
|
): number {
|
|
return getLanguageBannerHeight(isAuthenticated, isMobile, isMenuManager) + NAVBAR_HEIGHT;
|
|
}
|
|
|
|
/** @deprecated Use getHeaderTotalHeight() for dynamic height. */
|
|
export const HEADER_TOTAL_HEIGHT = LANGUAGE_BANNER_HEIGHT + NAVBAR_HEIGHT;
|
|
|
|
export default function LanguageSwitcher() {
|
|
const { language, setLanguage } = useLanguage();
|
|
const t = getTranslation(language);
|
|
const pathname = usePathname();
|
|
const { email, isAuthenticated, isMenuManager, logout } = useCustomerAuth();
|
|
const isLoginActive = !isAuthenticated && pathname === '/login';
|
|
const isAdminActive = pathname === '/admin';
|
|
|
|
const emailInitial = email?.charAt(0).toUpperCase() ?? '?';
|
|
|
|
const handleSelect = (lang: Language) => {
|
|
setLanguage(lang);
|
|
};
|
|
|
|
const handleLogout = async () => {
|
|
await logout();
|
|
};
|
|
|
|
const loginButton = (
|
|
<Link
|
|
href="/login"
|
|
aria-current={isLoginActive ? 'page' : undefined}
|
|
className={`group relative inline-flex shrink-0 items-center gap-1.5 overflow-hidden rounded-full px-3.5 py-2 text-[11px] font-bold uppercase tracking-[0.1em] transition-all duration-300 active:scale-[0.96] touch-manipulation min-h-[36px] sm:min-h-[38px] sm:px-4 ${
|
|
isLoginActive
|
|
? 'bg-white text-[#0f5a4a] shadow-lg shadow-black/25 ring-2 ring-[#f4d47f]/60'
|
|
: 'border border-[#f4d47f]/45 bg-gradient-to-br from-[#c99a2e] via-[#e8c56a] to-[#b8892f] text-[#1a1206] shadow-md shadow-black/30 hover:scale-[1.03] hover:shadow-lg hover:shadow-[#c99a2e]/40'
|
|
}`}
|
|
>
|
|
<span className="relative flex h-5 w-5 items-center justify-center rounded-full bg-[#1a1206]/10">
|
|
<LogIn className="h-3.5 w-3.5" />
|
|
<Sparkles className="absolute -right-0.5 -top-0.5 h-2 w-2 text-[#fff8e7] opacity-80" />
|
|
</span>
|
|
<span className="whitespace-nowrap max-sm:hidden">{t.nav.login}</span>
|
|
<span className="whitespace-nowrap sm:hidden">Login</span>
|
|
</Link>
|
|
);
|
|
|
|
const menuManagementLink = (variant: 'desktop' | 'mobile' | 'mobile-full' = 'desktop') => (
|
|
<Link
|
|
href="/admin"
|
|
aria-current={isAdminActive ? 'page' : undefined}
|
|
className={`group relative inline-flex shrink-0 items-center justify-center gap-1.5 overflow-hidden rounded-full font-bold transition-all duration-300 active:scale-[0.96] touch-manipulation ${
|
|
variant === 'mobile-full'
|
|
? 'flex-1 min-h-[44px] border border-[#f4d47f]/60 bg-gradient-to-br from-[#c99a2e] via-[#e8c56a] to-[#b8892f] px-3 py-2.5 text-[11px] text-[#1a1206] shadow-md'
|
|
: variant === 'mobile'
|
|
? 'min-h-[40px] border border-[#f4d47f]/60 bg-gradient-to-br from-[#c99a2e] to-[#d4a73d] px-3 py-2 text-[10px] text-[#1a1206]'
|
|
: 'min-h-[38px] border border-[#f4d47f]/50 bg-gradient-to-br from-[#c99a2e] via-[#e8c56a] to-[#b8892f] px-3 py-1.5 text-[10px] text-[#1a1206] shadow-md hover:scale-[1.03] sm:px-3.5 sm:text-[11px]'
|
|
} ${isAdminActive ? 'ring-2 ring-white/70' : ''}`}
|
|
>
|
|
<UtensilsCrossed className="h-3.5 w-3.5 shrink-0" />
|
|
<span className="whitespace-nowrap uppercase tracking-[0.06em]">
|
|
{variant === 'mobile' ? 'Menu' : t.auth.customer.menuManagement}
|
|
</span>
|
|
</Link>
|
|
);
|
|
|
|
const logoutButton = (variant: 'desktop' | 'mobile' | 'mobile-full' = 'desktop') => (
|
|
<button
|
|
type="button"
|
|
onClick={() => void handleLogout()}
|
|
aria-label={t.auth.customer.logout}
|
|
className={`group relative inline-flex shrink-0 items-center justify-center gap-1.5 overflow-hidden rounded-full border border-white/30 bg-white/10 font-bold text-white shadow-md transition-all duration-300 hover:bg-white/20 active:scale-[0.96] touch-manipulation ${
|
|
variant === 'mobile-full'
|
|
? 'flex-1 min-h-[44px] px-3 py-2.5 text-[11px]'
|
|
: variant === 'mobile'
|
|
? 'min-h-[40px] min-w-[40px] px-3 py-2'
|
|
: 'min-h-[38px] px-3 py-1.5 text-[10px] sm:px-3.5 sm:text-[11px]'
|
|
}`}
|
|
>
|
|
<LogOut className="h-3.5 w-3.5 shrink-0" />
|
|
{variant !== 'mobile' && (
|
|
<span className="whitespace-nowrap uppercase tracking-[0.06em]">
|
|
{t.auth.customer.logout}
|
|
</span>
|
|
)}
|
|
</button>
|
|
);
|
|
|
|
return (
|
|
<div
|
|
className={`lang-banner w-full overflow-visible border-b border-[#c99a2e]/25 bg-gradient-to-r from-[#0a4a3d] via-[#0f5a4a] to-[#0a4a3d] ${
|
|
!isAuthenticated
|
|
? 'h-12'
|
|
: 'sm:h-14'
|
|
}`}
|
|
role="navigation"
|
|
aria-label="Language selection"
|
|
>
|
|
{/* ── Row 1: languages (+ desktop auth inline) ── */}
|
|
<div
|
|
className={`mx-auto flex max-w-7xl items-center gap-2 px-3 sm:px-6 ${
|
|
isAuthenticated ? 'h-12 sm:h-14' : 'h-12'
|
|
}`}
|
|
>
|
|
<div className="hidden shrink-0 items-center gap-1.5 text-[10px] font-semibold uppercase tracking-[0.2em] text-[#d4a73d]/90 lg:flex">
|
|
<Globe className="h-3.5 w-3.5" />
|
|
<span>Language</span>
|
|
</div>
|
|
|
|
<div className="flex min-w-0 flex-1 items-center gap-1 overflow-x-auto overscroll-x-contain [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden sm:gap-1.5">
|
|
{languages.map((lang) => {
|
|
const isActive = language === lang.code;
|
|
return (
|
|
<button
|
|
key={lang.code}
|
|
type="button"
|
|
onClick={() => handleSelect(lang.code)}
|
|
aria-pressed={isActive}
|
|
aria-label={`${lang.name} (${lang.native})`}
|
|
className={`flex shrink-0 items-center gap-1 rounded-full px-2.5 py-2 text-[11px] font-semibold transition-all min-h-[34px] touch-manipulation sm:min-h-[36px] sm:gap-1.5 sm:px-3 sm:py-1.5 sm:text-xs ${
|
|
isActive
|
|
? 'bg-gradient-to-r from-[#c99a2e] to-[#e8c56a] text-[#1a1206] shadow-lg shadow-[#c99a2e]/35 ring-1 ring-[#f4d47f]/50'
|
|
: 'text-white/80 hover:bg-white/12 hover:text-white active:bg-white/20'
|
|
}`}
|
|
>
|
|
<span className="text-sm sm:text-base" aria-hidden="true">
|
|
{lang.flag}
|
|
</span>
|
|
<span className="whitespace-nowrap max-[400px]:hidden min-[401px]:inline">
|
|
{lang.native}
|
|
</span>
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
{/* Desktop: guest login OR logged-in account strip (single row, no clipping) */}
|
|
<div className="hidden shrink-0 sm:flex sm:items-center">
|
|
{!isAuthenticated ? (
|
|
loginButton
|
|
) : (
|
|
<div className="flex items-center gap-2.5 border-s border-white/15 ps-3">
|
|
<div className="hidden min-w-0 max-w-[150px] md:block">
|
|
<p className="text-[8px] font-medium uppercase tracking-[0.14em] text-[#d4a73d]/90 leading-none">
|
|
{t.auth.customer.loggedInAs}
|
|
</p>
|
|
<p className="truncate text-[11px] font-semibold text-white leading-tight mt-0.5" title={email ?? ''}>
|
|
{email}
|
|
</p>
|
|
</div>
|
|
<div className="flex items-center gap-1.5">
|
|
{isMenuManager && menuManagementLink('desktop')}
|
|
{logoutButton('desktop')}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Mobile guest: login icon only */}
|
|
{!isAuthenticated && <div className="shrink-0 sm:hidden">{loginButton}</div>}
|
|
</div>
|
|
|
|
{/* ── Mobile logged-in: creative account card ── */}
|
|
{isAuthenticated && (
|
|
<div className="px-3 pb-2.5 sm:hidden">
|
|
<div className="rounded-2xl border border-[#f4d47f]/20 bg-gradient-to-br from-white/[0.08] to-white/[0.03] p-2.5 shadow-inner backdrop-blur-sm">
|
|
<div className="mb-2.5 flex items-center gap-2.5">
|
|
<div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-[#c99a2e] to-[#e8c56a] text-sm font-bold text-[#1a1206] shadow-md ring-2 ring-white/20">
|
|
{emailInitial}
|
|
</div>
|
|
<div className="min-w-0 flex-1">
|
|
<p className="text-[9px] font-semibold uppercase tracking-[0.14em] text-[#d4a73d]/90">
|
|
{t.auth.customer.loggedInAs}
|
|
</p>
|
|
<p className="truncate text-sm font-semibold text-white" title={email ?? ''}>
|
|
{email}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex gap-2">
|
|
{isMenuManager && menuManagementLink('mobile-full')}
|
|
{logoutButton('mobile-full')}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
} |