shinkan-jinkendo/frontend/src/components/DesktopSidebar.jsx
Lars d7ed0c0e9b
All checks were successful
Deploy Development / deploy (push) Successful in 36s
Test Suite / pytest-backend (push) Successful in 32s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 7s
Test Suite / playwright-tests (push) Successful in 32s
feat(compliance): P-01 Rechtstextseiten technisch anlegen (0.8.69)
Öffentliche Routen /impressum /datenschutz /nutzungsbedingungen
/medienrichtlinie ohne Auth erreichbar. LegalPage-Komponente mit
deutlichem Platzhalterhinweis und strukturierten Pflichtfeldern je
Rechtstext. Links in LoginPage-Footer und DesktopSidebar-Footer.

KRIT-01 technischer Teil geschlossen. Juristische Inhalte bleiben
offen — Betreiber + Rechtsanwalt erforderlich.

- frontend/src/pages/LegalPage.jsx (neu)
- frontend/src/App.jsx: 4 öffentliche Routen
- frontend/src/pages/LoginPage.jsx: Rechtstext-Links im Footer
- frontend/src/components/DesktopSidebar.jsx: Links im Sidebar-Footer
- tests/dev-smoke-test.spec.js: 5 neue P-01-Tests

version: 0.8.69 (backend + frontend)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 09:41:45 +02:00

112 lines
3.7 KiB
JavaScript

import { NavLink, Link, useLocation } from 'react-router-dom'
import { LogOut } from 'lucide-react'
import { getMainNavItems } from '../config/appNav'
import { useOrgInbox } from '../context/OrgInboxContext'
import ActiveClubSwitcher from './ActiveClubSwitcher'
function sidebarLinkActive(pathname, item, routerIsActive) {
if (item.to.startsWith('/admin')) return pathname.startsWith('/admin')
return routerIsActive
}
/**
* Desktop-Sidebar (≥1024px) — Sichtbarkeit via CSS (.desktop-sidebar).
*/
export default function DesktopSidebar({
showAdminNav,
user,
onLogout
}) {
const loc = useLocation()
const { canAccessOrgInbox, inboxCount } = useOrgInbox()
const items = getMainNavItems(showAdminNav, { showInbox: canAccessOrgInbox })
const tier = user?.tier || ''
return (
<aside className="desktop-sidebar" aria-label="Hauptnavigation">
<div className="desktop-sidebar__brand">
<div className="desktop-sidebar__logo" aria-hidden />
<div className="desktop-sidebar__title">Shinkan Jinkendo</div>
</div>
<nav className="desktop-sidebar__nav">
{items.map((item) => (
<NavLink
key={item.to}
to={item.to}
end={!!item.end}
className={({ isActive }) =>
'desktop-sidebar__link' +
(sidebarLinkActive(loc.pathname, item, isActive)
? ' desktop-sidebar__link--active'
: '')
}
>
<item.Icon size={20} strokeWidth={2} />
<span>{item.label}</span>
{item.to === '/inbox' && inboxCount > 0 ? (
<span className="desktop-sidebar__badge" aria-label={`${inboxCount} offen`}>
{inboxCount > 99 ? '99+' : inboxCount}
</span>
) : null}
</NavLink>
))}
</nav>
<div className="desktop-sidebar__footer">
<ActiveClubSwitcher variant="sidebar" />
<div className="desktop-sidebar__footer-row">
<div className="desktop-sidebar__user">
<div
style={{
width: 32,
height: 32,
borderRadius: '50%',
background: 'var(--accent)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'white',
fontWeight: 600,
fontSize: '14px'
}}
>
{(user?.name || user?.email || '?').trim().slice(0, 1).toUpperCase()}
</div>
<div className="desktop-sidebar__user-text">
<span className="desktop-sidebar__user-name">
{user?.name || user?.email || 'User'}
</span>
{tier ? (
<span className="desktop-sidebar__user-tier">{tier}</span>
) : null}
</div>
</div>
<button
type="button"
className="desktop-sidebar__logout"
onClick={onLogout}
title="Abmelden"
>
<LogOut size={18} />
</button>
</div>
</div>
<div
style={{
padding: '0.5rem 1rem 0.75rem',
display: 'flex',
gap: '0.6rem',
flexWrap: 'wrap',
fontSize: '0.7rem',
}}
>
<Link to="/impressum" style={{ color: 'var(--text3)' }}>Impressum</Link>
<Link to="/datenschutz" style={{ color: 'var(--text3)' }}>Datenschutz</Link>
<Link to="/nutzungsbedingungen" style={{ color: 'var(--text3)' }}>Nutzungsbedingungen</Link>
<Link to="/medienrichtlinie" style={{ color: 'var(--text3)' }}>Medienrichtlinie</Link>
</div>
</aside>
)
}