Public Access
feat(ui): modern shadcn/ui theme with violet accent across all pages
Add shadcn/ui (Radix + Tailwind 4) components, zinc/violet theme tokens and self-hosted Inter. New header with user menu, Steam cover art, skeletons and toasts. Replace @mdi/font with lucide-react. Fix: top-owned list on the landing page only showed with top-played entries; numbers use the app language.
This commit is contained in:
1 parent
76a9ac3e6f
commit
e480369a98
43 files changed
+3804
-979
No files matched your search
@@ -0,0 +1,98 @@
|
||||
import {Link, NavLink} from 'react-router-dom';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {Gamepad2, Languages, LayoutDashboard, Library, LogOut, Moon, Settings, Sun, UserRound} from 'lucide-react';
|
||||
|
||||
import {Button} from '@/components/ui/button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import {usePreferences} from '@/context/PreferencesContext.jsx';
|
||||
import {cn} from '@/lib/utils';
|
||||
|
||||
const NAV_ITEMS = [
|
||||
{to: '/dashboard', labelKey: 'NAV.DASHBOARD', Icon: LayoutDashboard},
|
||||
{to: '/library', labelKey: 'NAV.LIBRARY', Icon: Library},
|
||||
];
|
||||
|
||||
const navLinkClass = ({isActive}) => cn(
|
||||
'flex items-center gap-2 rounded-lg px-3 py-2 text-sm font-medium transition-colors',
|
||||
isActive
|
||||
? 'bg-accent text-accent-foreground'
|
||||
: 'text-muted-foreground hover:bg-muted hover:text-foreground',
|
||||
);
|
||||
|
||||
export default function AppHeader({isAuthenticated, onLogout}) {
|
||||
const {t, i18n} = useTranslation();
|
||||
const {isDarkMode, setDarkMode, setLanguage} = usePreferences();
|
||||
|
||||
const toggleLanguage = () => setLanguage(i18n.language === 'de' ? 'en' : 'de');
|
||||
const toggleTheme = () => setDarkMode(!isDarkMode);
|
||||
const ThemeIcon = isDarkMode ? Sun : Moon;
|
||||
|
||||
return (
|
||||
<header className="sticky top-0 z-50 border-b bg-background/80 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
||||
<div className="mx-auto flex h-16 max-w-5xl items-center gap-2 px-4 md:px-8">
|
||||
<Link to={isAuthenticated ? '/dashboard' : '/'} className="mr-2 flex items-center gap-2 font-bold tracking-tight whitespace-nowrap">
|
||||
<span className="flex size-8 items-center justify-center rounded-lg bg-primary text-primary-foreground">
|
||||
<Gamepad2 className="size-5"/>
|
||||
</span>
|
||||
<span className="text-lg">Game Log</span>
|
||||
</Link>
|
||||
|
||||
{isAuthenticated && (
|
||||
<nav className="flex items-center gap-1">
|
||||
{NAV_ITEMS.map(({to, labelKey, Icon}) => (
|
||||
<NavLink key={to} to={to} className={navLinkClass} title={t(labelKey)}>
|
||||
<Icon className="size-4"/>
|
||||
<span className="hidden sm:inline">{t(labelKey)}</span>
|
||||
</NavLink>
|
||||
))}
|
||||
</nav>
|
||||
)}
|
||||
|
||||
<div className="ml-auto flex items-center gap-1">
|
||||
{isAuthenticated ? (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size="icon" aria-label={t('NAV.MENU')}>
|
||||
<UserRound className="size-5"/>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-52">
|
||||
<DropdownMenuItem asChild>
|
||||
<Link to="/settings"><Settings/>{t('NAV.SETTINGS')}</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onSelect={toggleLanguage}>
|
||||
<Languages/>{t('NAV.LANGUAGE')}: {i18n.language.toUpperCase()}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onSelect={toggleTheme}>
|
||||
<ThemeIcon/>{t(isDarkMode ? 'NAV.LIGHT_MODE' : 'NAV.DARK_MODE')}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator/>
|
||||
<DropdownMenuItem variant="destructive" onSelect={onLogout}>
|
||||
<LogOut/>{t('MAIN.LOGOUT')}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
) : (
|
||||
<>
|
||||
<Button variant="ghost" size="sm" onClick={toggleLanguage}>
|
||||
{i18n.language.toUpperCase()}
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon" onClick={toggleTheme} aria-label={t(isDarkMode ? 'NAV.LIGHT_MODE' : 'NAV.DARK_MODE')}>
|
||||
<ThemeIcon className="size-5"/>
|
||||
</Button>
|
||||
<Button asChild className="ml-1">
|
||||
<Link to="/login">{t('MAIN.LOGIN')}</Link>
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user