Vertrags-Forms: Mini-Links zu Stammdaten in neuem Tab

ContractEmailsSection (Vertragsansicht): Zusätzlich zu "Postfach
öffnen" gibt es jetzt "Stressfrei wechseln Adressen" → Tab in der
Kundenakte.

ContractForm (Bearbeiten): Kleine ExternalLink-Icons neben den
Select-Labels:
- Lieferadresse + Rechnungsadresse → Kundenakte/Adressen
- Bankkarte → Kundenakte/Bankkarten
- Ausweis → Kundenakte/Ausweise
- Anbieter + Tarif → Settings/Anbieter & Tarife
- Vertriebsplattform → Settings/Vertriebsplattformen

Select-Komponente nimmt jetzt ReactNode als label (statt nur string),
um JSX-Labels mit eingebettetem Link zu erlauben. Rückwärts-
kompatibel zu allen bestehenden String-Aufrufen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-01 07:47:40 +02:00
parent 5269092d2a
commit 4fb700cf57
3 changed files with 71 additions and 11 deletions
@@ -298,6 +298,16 @@ export default function ContractEmailsSection({
<ExternalLink className="w-3 h-3" />
Postfach öffnen
</Link>
<Link
to={`/customers/${customerId}?tab=stressfrei`}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1 text-xs text-blue-600 hover:text-blue-800 hover:underline font-normal"
title="Stressfrei-Wechseln-Adressen des Kunden in neuem Tab öffnen"
>
<ExternalLink className="w-3 h-3" />
Stressfrei wechseln Adressen
</Link>
</div>
}
actions={
+2 -2
View File
@@ -1,7 +1,7 @@
import { SelectHTMLAttributes, forwardRef } from 'react';
import { SelectHTMLAttributes, forwardRef, ReactNode } from 'react';
interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
label?: string;
label?: ReactNode;
error?: string;
options: { value: string | number; label: string }[];
placeholder?: string;
+59 -9
View File
@@ -1,5 +1,5 @@
import { useEffect, useMemo, useState } from 'react';
import { useNavigate, useParams, useSearchParams, useLocation } from 'react-router-dom';
import { useNavigate, useParams, useSearchParams, useLocation, Link } from 'react-router-dom';
import { popHistory } from '../../utils/navigation';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useForm } from 'react-hook-form';
@@ -12,7 +12,29 @@ import Select from '../../components/ui/Select';
import type { ContractType } from '../../types';
import { formatDate } from '../../utils/dateFormat';
import { useProviderSettings } from '../../hooks/useProviderSettings';
import { Plus, Trash2, Eye, EyeOff, Info, X, ArrowLeft } from 'lucide-react';
import { Plus, Trash2, Eye, EyeOff, Info, X, ArrowLeft, ExternalLink } from 'lucide-react';
// Kleine Helper-Komponente: Label + Mini-Link im neuen Tab. Wird neben
// Select-Feldern eingesetzt, deren Stammdaten an anderer Stelle gepflegt
// werden (Kundenakte, Settings) damit der User direkt von dort in die
// passende Verwaltungsseite springen kann.
function LabelWithLink({ children, to, title }: { children: React.ReactNode; to: string; title: string }) {
return (
<span className="inline-flex items-center gap-2">
<span>{children}</span>
<Link
to={to}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center text-blue-600 hover:text-blue-800"
title={title}
onClick={(e) => e.stopPropagation()}
>
<ExternalLink className="w-3 h-3" />
</Link>
</span>
);
}
import { calculateConsumption, calculateMultiMeterConsumption } from '../../utils/energyCalculations';
// Contract types are now loaded dynamically from the database
@@ -752,7 +774,11 @@ export default function ContractForm() {
</div>
<Select
label="Vertriebsplattform"
label={
<LabelWithLink to="/settings/platforms" title="Vertriebsplattformen verwalten (neuer Tab)">
Vertriebsplattform
</LabelWithLink>
}
{...register('salesPlatformId')}
options={platforms.map((p) => ({ value: p.id, label: p.name }))}
/>
@@ -799,7 +825,11 @@ export default function ContractForm() {
<Card className="mb-6" title="Kundendaten verknüpfen">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
<Select
label="Lieferadresse"
label={
<LabelWithLink to={`/customers/${customerId}?tab=addresses`} title="Adressen des Kunden in neuem Tab öffnen">
Lieferadresse
</LabelWithLink>
}
{...register('addressId')}
options={addresses
.filter((a) => a.type === 'DELIVERY_RESIDENCE')
@@ -810,7 +840,11 @@ export default function ContractForm() {
/>
<Select
label="Rechnungsadresse"
label={
<LabelWithLink to={`/customers/${customerId}?tab=addresses`} title="Adressen des Kunden in neuem Tab öffnen">
Rechnungsadresse
</LabelWithLink>
}
{...register('billingAddressId')}
options={addresses
.filter((a) => a.type === 'BILLING')
@@ -823,7 +857,11 @@ export default function ContractForm() {
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<Select
label="Bankkarte"
label={
<LabelWithLink to={`/customers/${customerId}?tab=bankcards`} title="Bankkarten des Kunden in neuem Tab öffnen">
Bankkarte
</LabelWithLink>
}
{...register('bankCardId')}
options={bankCards.map((c) => ({
value: c.id,
@@ -832,7 +870,11 @@ export default function ContractForm() {
/>
<Select
label="Ausweis"
label={
<LabelWithLink to={`/customers/${customerId}?tab=documents`} title="Ausweise des Kunden in neuem Tab öffnen">
Ausweis
</LabelWithLink>
}
{...register('identityDocumentId')}
options={documents.map((d) => ({
value: d.id,
@@ -846,12 +888,20 @@ export default function ContractForm() {
<Card className="mb-6" title="Anbieter & Tarif">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<Select
label="Anbieter"
label={
<LabelWithLink to="/settings/providers" title="Anbieter & Tarife verwalten (neuer Tab)">
Anbieter
</LabelWithLink>
}
{...register('providerId')}
options={providers.map((p) => ({ value: p.id, label: p.name }))}
/>
<Select
label="Tarif"
label={
<LabelWithLink to="/settings/providers" title="Anbieter & Tarife verwalten (neuer Tab)">
Tarif
</LabelWithLink>
}
{...register('tariffId')}
options={availableTariffs.map((t) => ({ value: t.id, label: t.name }))}
disabled={!selectedProviderId}