27a6e2952c
useButtonType: Hamburger-, Profil-, Kategorie-, Ein-/Ausklapp- und Logout-
Buttons (kein Formular in diesen Dateien) erhalten type="button".
noSvgWithoutTitle: Icons neben sichtbarem Text werden dekorativ
(aria-hidden="true"). Zwei bislang unbenannte interaktive Elemente erhalten
zusaetzlich ein aria-label, weil ihr Text im eingeklappten Sidebar-Zustand
verschwindet bzw. ganz fehlte: die Dashboard-/Marketplace-Links und der
"Einstellungen"-Button in der Seitenleiste (t('dashboard')/t('marketplace')/
t('settings'), alle bereits vorhandene Schluessel) sowie der mobile
Schliessen-Button (tCommon('close')).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
78 lines
3.1 KiB
TypeScript
78 lines
3.1 KiB
TypeScript
'use client';
|
|
|
|
import { useTranslations } from 'next-intl';
|
|
import { useSidebarStore } from '@/lib/stores/sidebar-store';
|
|
import { useAuthStore } from '@/lib/stores/auth-store';
|
|
import { LocaleSwitcher } from '@/components/locale-switcher';
|
|
|
|
export function SidebarFooter() {
|
|
const t = useTranslations('sidebar');
|
|
const tHeader = useTranslations('header');
|
|
const isCollapsed = useSidebarStore((s) => s.isCollapsed);
|
|
const user = useAuthStore((s) => s.user);
|
|
|
|
const userInitial = user
|
|
? (user.displayName ?? user.username).charAt(0).toUpperCase()
|
|
: '?';
|
|
|
|
const userName = user?.displayName ?? user?.username ?? t('userPlaceholder');
|
|
const roleLabel = user ? tHeader(`role.${user.role}`) : '';
|
|
|
|
return (
|
|
<div className="border-t border-sidebar-border p-3 flex flex-col gap-2">
|
|
{/* Locale switcher */}
|
|
{!isCollapsed && (
|
|
<div className="px-2 py-1">
|
|
<LocaleSwitcher />
|
|
</div>
|
|
)}
|
|
|
|
{/* Settings */}
|
|
<button
|
|
type="button"
|
|
aria-label={t('settings')}
|
|
className="flex items-center gap-3 rounded-md px-2 py-1.5 text-sm text-sidebar-foreground hover:bg-muted transition-colors"
|
|
>
|
|
<svg
|
|
aria-hidden="true"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="18"
|
|
height="18"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="2"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
className="shrink-0"
|
|
>
|
|
<circle cx="12" cy="12" r="3" />
|
|
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" />
|
|
</svg>
|
|
{!isCollapsed && (
|
|
<span className="truncate">{t('settings')}</span>
|
|
)}
|
|
</button>
|
|
|
|
{/* User info - wired to auth store */}
|
|
<div className="flex items-center gap-3 px-2 py-1.5">
|
|
<div className="h-7 w-7 shrink-0 rounded-full bg-primary flex items-center justify-center text-xs font-medium text-primary-foreground">
|
|
{userInitial}
|
|
</div>
|
|
{!isCollapsed && (
|
|
<div className="flex flex-col overflow-hidden">
|
|
<span className="text-sm text-sidebar-foreground truncate">
|
|
{userName}
|
|
</span>
|
|
{user && (
|
|
<span className="text-xs text-muted-foreground truncate">
|
|
{roleLabel}
|
|
</span>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|