feat(library): Steam wishlist tab with prices and discounts

The library page gets a Bibliothek | Wunschliste tab bar. The wishlist shows
the current store price, the discount and can show only games on sale.
This commit is contained in:
lobo committed 2026-09-24 13:58:44 +02:00
1 parent 89c5ba5253
commit aa5124ea1e
8 files changed
+281 -5

No files matched your search

+8 -5
View File
@@ -1,4 +1,4 @@
import {Link, NavLink} from 'react-router-dom';
import {Link, NavLink, useLocation} from 'react-router-dom';
import {useTranslation} from 'react-i18next';
import {Gamepad2, Languages, LayoutDashboard, Library, LogOut, Moon, Settings, Sun, UserRound} from 'lucide-react';
@@ -15,10 +15,11 @@ import {cn} from '@/lib/utils';
const NAV_ITEMS = [
{to: '/dashboard', labelKey: 'NAV.DASHBOARD', Icon: LayoutDashboard},
{to: '/library', labelKey: 'NAV.LIBRARY', Icon: Library},
// The wishlist is a tab of the library.
{to: '/library', labelKey: 'NAV.LIBRARY', Icon: Library, alsoActiveOn: ['/wishlist']},
];
const navLinkClass = ({isActive}) => cn(
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'
@@ -28,6 +29,7 @@ const navLinkClass = ({isActive}) => cn(
export default function AppHeader({isAuthenticated, onLogout}) {
const {t, i18n} = useTranslation();
const {isDarkMode, setDarkMode, setLanguage} = usePreferences();
const {pathname} = useLocation();
const toggleLanguage = () => setLanguage(i18n.language === 'de' ? 'en' : 'de');
const toggleTheme = () => setDarkMode(!isDarkMode);
@@ -45,8 +47,9 @@ export default function AppHeader({isAuthenticated, onLogout}) {
{isAuthenticated && (
<nav className="flex items-center gap-1">
{NAV_ITEMS.map(({to, labelKey, Icon}) => (
<NavLink key={to} to={to} className={navLinkClass} title={t(labelKey)}>
{NAV_ITEMS.map(({to, labelKey, Icon, alsoActiveOn = []}) => (
<NavLink key={to} to={to} title={t(labelKey)}
className={({isActive}) => navLinkClass(isActive || alsoActiveOn.includes(pathname))}>
<Icon className="size-4"/>
<span className="hidden sm:inline">{t(labelKey)}</span>
</NavLink>
+32
View File
@@ -0,0 +1,32 @@
import {NavLink} from 'react-router-dom';
import {useTranslation} from 'react-i18next';
import {Gift, Library} from 'lucide-react';
import {cn} from '@/lib/utils';
const TABS = [
{to: '/library', labelKey: 'LIBRARY.TITLE', icon: Library},
{to: '/wishlist', labelKey: 'WISHLIST.TITLE', icon: Gift},
];
export default function LibraryTabs() {
const {t} = useTranslation();
return (
<nav aria-label={t('LIBRARY.TITLE')} className="inline-flex rounded-lg bg-muted p-1 text-sm">
{TABS.map(({to, labelKey, icon: Icon}) => (
<NavLink
key={to}
to={to}
end
className={({isActive}) => cn(
'flex items-center gap-1.5 rounded-md px-3 py-1.5 font-medium transition-colors',
isActive ? 'bg-background text-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground',
)}
>
<Icon className="size-4"/>
{t(labelKey)}
</NavLink>
))}
</nav>
);
}