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:
@@ -97,6 +97,14 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung
|
||||
|
||||
## ✅ Erledigt
|
||||
|
||||
- [x] **🆕 Kunden-Detail-Tabs: Pro-Tab-Link „in neuem Tab öffnen"**
|
||||
- `Tabs`-Komponente um optionalen Prop `tabHrefBuilder(tabId)` erweitert.
|
||||
Wenn gesetzt, erscheint neben jedem Tab-Label ein kleines
|
||||
`ExternalLink`-Icon. CustomerDetail übergibt den Builder mit
|
||||
`?tab=<id>`-Query-Param, der eh schon vom URL-Sync genutzt wird.
|
||||
- `target="_blank"` + `rel="noopener noreferrer"` + Klick-stopPropagation,
|
||||
damit der Tab-Wechsel nicht parallel zur Tab-Aktivierung passiert.
|
||||
|
||||
- [x] **🆕 Vertrag: Kunden-/Vertragsnummer bei Vertriebsplattform**
|
||||
- Zwei neue optionale Felder
|
||||
`Contract.customerNumberAtSalesPlatform` +
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user