From 1dab069a4d517ed42b403205cabf09b23b33b512 Mon Sep 17 00:00:00 2001 From: lobo Date: Thu, 8 Oct 2026 13:17:29 +0200 Subject: [PATCH] feat(header): add community link to nav Add an icon-only nav link to "/" so logged-in users can get back to the community stats. Make room for it: less nav link padding below sm, points chip no longer shrinks from lg, and the view-as badge hangs below the header at every width. --- public/i18n/de.json | 1 + public/i18n/en.json | 1 + src/components/AppHeader.jsx | 15 ++++++++------- src/components/AppHeader.test.jsx | 27 +++++++++++++++++++++++++-- 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/public/i18n/de.json b/public/i18n/de.json index 8db92ba..aa5ff42 100644 --- a/public/i18n/de.json +++ b/public/i18n/de.json @@ -380,6 +380,7 @@ "LIGHT_MODE": "Helles Design", "PLAY_NEXT": "Nächstes Spiel", "STATS": "Statistik", + "COMMUNITY": "Community", "POINTS": "Pile Points", "ADMIN": "Admin", "VIEW_AS": { diff --git a/public/i18n/en.json b/public/i18n/en.json index ddfd930..037de4f 100644 --- a/public/i18n/en.json +++ b/public/i18n/en.json @@ -380,6 +380,7 @@ "LIGHT_MODE": "Light mode", "PLAY_NEXT": "Play next", "STATS": "Stats", + "COMMUNITY": "Community", "POINTS": "Pile Points", "ADMIN": "Admin", "VIEW_AS": { diff --git a/src/components/AppHeader.jsx b/src/components/AppHeader.jsx index e8aed81..857fb9c 100644 --- a/src/components/AppHeader.jsx +++ b/src/components/AppHeader.jsx @@ -1,7 +1,7 @@ import {useEffect, useState} from 'react'; import {Link, NavLink, useLocation, useNavigate} from 'react-router-dom'; import {useTranslation} from 'react-i18next'; -import {Bell, ChartColumn, Coins, Dices, Eye, Languages, LayoutDashboard, Library, Link2, LogOut, Mail, Megaphone, Moon, Newspaper, ScrollText, Search, Settings, Shield, Sun} from 'lucide-react'; +import {Bell, ChartColumn, Coins, Dices, Eye, Languages, LayoutDashboard, Library, Link2, LogOut, Mail, Megaphone, Moon, Newspaper, ScrollText, Search, Settings, Shield, Sun, Users} from 'lucide-react'; import {toast} from 'sonner'; import {Badge} from '@/components/ui/badge'; @@ -34,10 +34,11 @@ const NAV_ITEMS = [ {to: '/library', labelKey: 'NAV.LIBRARY', Icon: Library, alsoActiveOn: ['/wishlist', '/backlog']}, {to: '/play-next', labelKey: 'NAV.PLAY_NEXT', Icon: Dices}, {to: '/stats', labelKey: 'NAV.STATS', Icon: ChartColumn}, + {to: '/', labelKey: 'NAV.COMMUNITY', Icon: Users, iconOnly: true}, ]; const navLinkClass = (isActive) => cn( - 'flex items-center gap-2 rounded-lg px-2 py-2 text-sm font-medium transition-colors sm:px-3', + 'flex items-center gap-2 rounded-lg px-1 py-2 text-sm font-medium transition-colors sm:px-3', isActive ? 'bg-accent text-accent-foreground' : 'text-muted-foreground hover:bg-muted hover:text-foreground', @@ -128,23 +129,23 @@ export default function AppHeader({isAuthenticated, onLogout}) { {isAuthenticated && ( )}
- {/* Below md the badge hangs on the header's bottom edge: in the row it would push the page sideways. */} + {/* The badge hangs on the header's bottom edge at every width: in the row it would push the page sideways. */} {isAuthenticated && viewingAs && ( - {t('NAV.VIEW_AS.BADGE', {role: t(`ROLES.${viewingAs}`)})} + {t('NAV.VIEW_AS.BADGE', {role: t(`ROLES.${viewingAs}`)})} )} {isAuthenticated && wallet && ( - )} diff --git a/src/components/AppHeader.test.jsx b/src/components/AppHeader.test.jsx index 7799129..831b9f9 100644 --- a/src/components/AppHeader.test.jsx +++ b/src/components/AppHeader.test.jsx @@ -16,11 +16,11 @@ afterEach(() => { useTranslation().i18n.language = 'de'; }); -const loggedInAs = (me, routes = {}) => { +const loggedInAs = (me, routes = {}, options = {}) => { localStorage.setItem('tbp_session', '1'); fetchRoutes({'GET /api/users/me': json(200, me), 'GET /api/library/steam-profile': json(200, {}), 'GET /api/inbox': json(200, {results: [], unread: 0}), ...routes}); - renderWithProviders( {}}/>); + renderWithProviders( {}}/>, options); }; const openMenu = () => fireEvent.keyDown(screen.getByRole('button', {name: 'NAV.MENU'}), {key: 'Enter'}); @@ -44,6 +44,29 @@ describe('AppHeader', () => { screen.getByText('ToBePlayed').style.display = 'none'; expect(screen.getByRole('link', {name: 'ToBePlayed'})).toBeTruthy(); + expect(screen.queryByTitle('NAV.COMMUNITY')).toBeNull(); + }); + + it('links to the community stats when logged in', () => { + loggedInAs({role: 'USER', activeRole: 'USER'}); + + const link = screen.getByTitle('NAV.COMMUNITY'); + + expect(link.getAttribute('href')).toBe('/'); + expect(link.getAttribute('aria-current')).toBe('page'); + }); + + it('shows the community link as an icon without a text label', () => { + loggedInAs({role: 'USER', activeRole: 'USER'}); + + expect(screen.getByTitle('NAV.COMMUNITY').textContent).toBe(''); + }); + + it('marks community active only on the landing page', () => { + loggedInAs({role: 'USER', activeRole: 'USER'}, {}, {entries: ['/dashboard']}); + + expect(screen.getByTitle('NAV.COMMUNITY').getAttribute('aria-current')).toBeNull(); + expect(screen.getByTitle('NAV.DASHBOARD').getAttribute('aria-current')).toBe('page'); }); it('shows how many inbox notes are unread', async () => {