Files
leocrm/frontend/src/components/shared/Tabs.tsx
T
leocrm-bot 700b7a71ad T07b: frontend feature pages — companies + contacts + settings + audit + dashboard + search
- 11 new feature pages (CompaniesList/Detail/Form, ContactsList/Detail/Form,
  SettingsProfile/Roles/Users, AuditLog, GlobalSearchResults)
- 3 page updates (Dashboard with StatCard+ActivityFeed, Settings with tree nav+Outlet,
  TopBar with SearchDropdown)
- 13 new routes in routes/index.tsx
- i18n updates (de.json + en.json) with companies/contacts/settings/audit/search keys
- 12 new test files + 2 existing test fixes (TopBar, AppShell)
- 7 shared components (DataGrid, Tabs, SearchDropdown, CsvImportDialog, StatCard,
  ActivityFeed, UnsavedChangesGuard)
- 16 new API hooks in hooks.ts
- Verification: 141 tests pass, build succeeds, tsc --noEmit clean
2026-06-29 11:01:39 +02:00

63 lines
2.0 KiB
TypeScript

import React, { useState } from 'react';
import clsx from 'clsx';
export interface TabItem {
key: string;
label: string;
content: React.ReactNode;
badge?: number;
}
export interface TabsProps {
tabs: TabItem[];
defaultKey?: string;
className?: string;
}
export function Tabs({ tabs, defaultKey, className }: TabsProps) {
const [activeKey, setActiveKey] = useState(defaultKey || tabs[0]?.key || '');
const activeTab = tabs.find((t) => t.key === activeKey);
return (
<div className={clsx('w-full', className)}>
<div className="border-b border-secondary-200" role="tablist">
<div className="flex gap-1 px-6 overflow-x-auto">
{tabs.map((tab) => (
<button
key={tab.key}
role="tab"
aria-selected={activeKey === tab.key}
aria-controls={`panel-${tab.key}`}
id={`tab-${tab.key}`}
tabIndex={activeKey === tab.key ? 0 : -1}
onClick={() => setActiveKey(tab.key)}
className={clsx(
'px-4 py-3 text-sm font-medium border-b-2 min-h-touch whitespace-nowrap',
'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 rounded-t-md',
activeKey === tab.key
? 'border-primary-600 text-primary-700'
: 'border-transparent text-secondary-600 hover:text-secondary-900 hover:border-secondary-300'
)}
>
{tab.label}
{tab.badge !== undefined && tab.badge > 0 && (
<span className="ml-2 inline-flex items-center justify-center px-2 py-0.5 rounded-full text-xs bg-primary-100 text-primary-700">
{tab.badge}
</span>
)}
</button>
))}
</div>
</div>
<div
id={`panel-${activeKey}`}
role="tabpanel"
aria-labelledby={`tab-${activeKey}`}
className="px-6 py-4"
>
{activeTab?.content}
</div>
</div>
);
}