Kunden-Tabs: ExternalLink-Icon neben jedem Reiter

Tabs-Komponente bekommt optionalen tabHrefBuilder(tabId)-Prop.
Wenn gesetzt, erscheint neben jedem Tab-Label ein kleines
ExternalLink-Icon, das den Tab via ?tab=<id> in einem neuen
Browser-Tab öffnet.

CustomerDetail übergibt den Builder. URL-Param wird eh schon
für den Tab-Sync genutzt – Anhängen reicht.

Click-stopPropagation verhindert, dass der Klick auf das Icon
gleichzeitig den Tab-Wechsel auslöst.
This commit is contained in:
2026-06-03 18:15:23 +02:00
parent fcc3b04725
commit 84cbf01706
3 changed files with 55 additions and 14 deletions
+40 -13
View File
@@ -1,4 +1,5 @@
import { ReactNode, useState, useEffect } from 'react';
import { ExternalLink } from 'lucide-react';
interface Tab {
id: string;
@@ -11,9 +12,21 @@ interface TabsProps {
defaultTab?: string;
activeTab?: string;
onTabChange?: (tabId: string) => void;
/**
* Optional: liefert die URL, unter der ein einzelner Tab in einem
* neuen Tab geöffnet werden kann. Wenn gesetzt, erscheint neben jedem
* Tab-Label ein kleines „im neuen Tab öffnen"-Icon.
*/
tabHrefBuilder?: (tabId: string) => string;
}
export default function Tabs({ tabs, defaultTab, activeTab: controlledTab, onTabChange }: TabsProps) {
export default function Tabs({
tabs,
defaultTab,
activeTab: controlledTab,
onTabChange,
tabHrefBuilder,
}: TabsProps) {
const [internalTab, setInternalTab] = useState(defaultTab || tabs[0]?.id);
const activeTab = controlledTab ?? internalTab;
@@ -31,19 +44,33 @@ export default function Tabs({ tabs, defaultTab, activeTab: controlledTab, onTab
return (
<div>
<div className="border-b border-gray-200">
<nav className="flex -mb-px space-x-8">
<nav className="flex -mb-px space-x-6">
{tabs.map((tab) => (
<button
key={tab.id}
onClick={() => handleTabChange(tab.id)}
className={`py-4 px-1 border-b-2 font-medium text-sm whitespace-nowrap ${
activeTab === tab.id
? 'border-blue-500 text-blue-600'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
}`}
>
{tab.label}
</button>
<div key={tab.id} className="flex items-center gap-1">
<button
onClick={() => handleTabChange(tab.id)}
className={`py-4 px-1 border-b-2 font-medium text-sm whitespace-nowrap ${
activeTab === tab.id
? 'border-blue-500 text-blue-600'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
}`}
>
{tab.label}
</button>
{tabHrefBuilder && (
<a
href={tabHrefBuilder(tab.id)}
target="_blank"
rel="noopener noreferrer"
onClick={(e) => e.stopPropagation()}
className="text-gray-400 hover:text-blue-600 p-0.5"
title={`${tab.label} in neuem Tab öffnen`}
aria-label={`${tab.label} in neuem Tab öffnen`}
>
<ExternalLink className="w-3.5 h-3.5" />
</a>
)}
</div>
))}
</nav>
</div>
@@ -411,7 +411,13 @@ export default function CustomerDetail({ portalCustomerId }: { portalCustomerId?
)}
<Card>
<Tabs tabs={tabs} defaultTab={defaultTab} activeTab={activeTab} onTabChange={handleTabChange} />
<Tabs
tabs={tabs}
defaultTab={defaultTab}
activeTab={activeTab}
onTabChange={handleTabChange}
tabHrefBuilder={(tabId) => `${location.pathname}?tab=${tabId}`}
/>
</Card>
<AddressModal