Files
tessera-ctl/apps/web/src/components/settings/settings-sidebar.tsx
T
schalli ee97b4ed9f feat(260929-dzu): Einstellungen > Eigene Module fuer jeden Benutzer, Verwaltung nur gemeinsam
- gemeinsame Oberflaeche (CustomModuleManager, Formular, Loeschdialog) fuer Verwaltung und Einstellungen
- Einstellungen: persoenliche Eintraege, Verwaltung sendet shared: true und zeigt nur gemeinsame
- Navigation, Texte de/en, Komponententests

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 10:17:22 +02:00

114 lines
4.1 KiB
TypeScript

'use client';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { useTranslations } from 'next-intl';
/**
* Settings sub-sidebar (D-19, D-20).
* Phase 05 categories: Dashboard > Widgets, Calendar.
* Active item uses sidebar-accent pattern with aria-current="page".
*/
export function SettingsSidebar() {
const t = useTranslations('settings');
const pathname = usePathname();
const items = [
{
label: t('categoryWidgets'),
href: '/settings/dashboard',
},
{
label: t('categoryCalendar'),
href: '/settings/dashboard/calendar',
},
];
const isActive = (href: string) => {
if (href === '/settings/dashboard') {
// Active when exactly on /settings/dashboard (not on sub-routes like /calendar)
return pathname === '/settings/dashboard';
}
return pathname.startsWith(href);
};
return (
// a11y/useSemanticElements: role="navigation" nicht auf das <aside> gehoben --
// darunter stehen zwei <nav>-Bereiche (Allgemein, Dashboard); ein drittes
// Navigations-Landmark auf dem Rahmen waere doppelt. Jedes <nav> traegt
// stattdessen die Beschriftung seiner eigenen Kategorie.
<aside className="flex w-full shrink-0 flex-col border-b border-border py-2 md:w-[220px] md:border-b-0">
{/* Allgemein category — above Dashboard (Surface C, 07-06) */}
<div className="px-5 pb-1 pt-3">
<h2 className="text-xs font-semibold text-muted-foreground">
{t('categoryGeneral')}
</h2>
</div>
<nav className="mb-2 flex flex-col gap-0.5 px-3" aria-label={t('categoryGeneral')}>
<Link
href="/settings/general/account"
className={`nav-item flex h-9 items-center rounded-lg px-2.5 text-sm transition-colors ${
isActive('/settings/general/account')
? 'bg-sidebar-accent text-sidebar-accent-foreground font-semibold'
: 'text-sidebar-foreground hover:bg-sidebar-hover'
}`}
aria-current={
isActive('/settings/general/account') ? 'page' : undefined
}
>
{t('categoryAccount')}
</Link>
<Link
href="/settings/general/desktop"
className={`nav-item flex h-9 items-center rounded-lg px-2.5 text-sm transition-colors ${
isActive('/settings/general/desktop')
? 'bg-sidebar-accent text-sidebar-accent-foreground font-semibold'
: 'text-sidebar-foreground hover:bg-sidebar-hover'
}`}
aria-current={
isActive('/settings/general/desktop') ? 'page' : undefined
}
>
{t('categoryDesktopApp')}
</Link>
<Link
href="/settings/custom-modules"
className={`nav-item flex h-9 items-center rounded-lg px-2.5 text-sm transition-colors ${
isActive('/settings/custom-modules')
? 'bg-sidebar-accent text-sidebar-accent-foreground font-semibold'
: 'text-sidebar-foreground hover:bg-sidebar-hover'
}`}
aria-current={
isActive('/settings/custom-modules') ? 'page' : undefined
}
>
{t('categoryCustomModules')}
</Link>
</nav>
{/* Dashboard category — existing items unchanged */}
<div className="px-5 pb-1 pt-4">
<h2 className="text-xs font-semibold text-muted-foreground">
{t('categoryDashboard')}
</h2>
</div>
<nav className="flex flex-col gap-0.5 px-3" aria-label={t('categoryDashboard')}>
{items.map((item) => (
<Link
key={item.href}
href={item.href}
className={`nav-item flex h-9 items-center rounded-lg px-2.5 text-sm transition-colors ${
isActive(item.href)
? 'bg-sidebar-accent text-sidebar-accent-foreground font-semibold'
: 'text-sidebar-foreground hover:bg-sidebar-hover'
}`}
aria-current={isActive(item.href) ? 'page' : undefined}
>
{item.label}
</Link>
))}
</nav>
</aside>
);
}