Fix Folgevertrag aus deaktiviertem Vertrag + Kundendaten-Modal erweitern
1) Folgevertrag-Bug: Backend legt den Folgevertrag korrekt mit previousContractId an. Der Verlust passierte im Frontend - das Vorgaenger-Dropdown holt Vertraege ueber getAll, das DEACTIVATED standardmaessig ausblendet. Beim Bearbeiten des Folgevertrags war der deaktivierte Vorgaenger nicht als Option da -> Verknuepfung ging beim Speichern verloren. Fix: getAllContracts + Controller + contractApi.getAll um includeDeactivated erweitert; Vorgaenger-Dropdown nutzt es und markiert deaktivierte Vertraege mit '· deaktiviert'. Verifiziert (Flag inkludiert deaktivierte; Folgevertrag setzt previousContractId). 2) Kundendaten-Modal: zeigt jetzt zusaetzlich Lieferadresse + (falls abweichend) Rechnungsadresse des geoeffneten Vertrags, die Stressfrei- Adresse des Vertrags einzeln und darunter alle weiteren Stressfrei- Adressen des Kunden. CustomerInfoModal nimmt optionale Vertragskontext- Props; ContractDetail + ContractForm uebergeben sie. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { X, RefreshCw, User } from 'lucide-react';
|
||||
import { customerApi } from '../../services/api';
|
||||
import { customerApi, stressfreiEmailApi } from '../../services/api';
|
||||
import type { Address } from '../../types';
|
||||
import { CopyableValue } from '../ui/CopyButton';
|
||||
import Button from '../ui/Button';
|
||||
|
||||
@@ -8,6 +9,17 @@ interface CustomerInfoModalProps {
|
||||
customerId: number;
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
// Optional: Kontext des geöffneten Vertrags. Wenn gesetzt, werden die
|
||||
// Liefer-/Rechnungsadresse dieses Vertrags und seine Stressfrei-Adresse
|
||||
// gesondert angezeigt.
|
||||
deliveryAddress?: Address | null;
|
||||
billingAddress?: Address | null;
|
||||
contractStressfreiEmailId?: number | null;
|
||||
}
|
||||
|
||||
function formatAddress(a?: Address | null): string {
|
||||
if (!a) return '';
|
||||
return `${a.street} ${a.houseNumber ?? ''}, ${a.postalCode} ${a.city}`.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -15,7 +27,14 @@ interface CustomerInfoModalProps {
|
||||
* Vertragsdetail-Seite per Info-Icon neben dem Kundennamen geöffnet.
|
||||
* Jedes Feld hat einen Copy-Button rechts. Modal-only, schreibt nichts.
|
||||
*/
|
||||
export default function CustomerInfoModal({ customerId, open, onClose }: CustomerInfoModalProps) {
|
||||
export default function CustomerInfoModal({
|
||||
customerId,
|
||||
open,
|
||||
onClose,
|
||||
deliveryAddress,
|
||||
billingAddress,
|
||||
contractStressfreiEmailId,
|
||||
}: CustomerInfoModalProps) {
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ['customer-info-modal', customerId],
|
||||
queryFn: () => customerApi.getById(customerId),
|
||||
@@ -23,6 +42,14 @@ export default function CustomerInfoModal({ customerId, open, onClose }: Custome
|
||||
staleTime: 30_000,
|
||||
});
|
||||
|
||||
// Stressfrei-Wechseln-Adressen des Kunden (nur aktive).
|
||||
const { data: stressfreiData } = useQuery({
|
||||
queryKey: ['customer-info-modal-stressfrei', customerId],
|
||||
queryFn: () => stressfreiEmailApi.getByCustomer(customerId),
|
||||
enabled: open,
|
||||
staleTime: 30_000,
|
||||
});
|
||||
|
||||
if (!open) return null;
|
||||
|
||||
const c = data?.data;
|
||||
@@ -32,9 +59,20 @@ export default function CustomerInfoModal({ customerId, open, onClose }: Custome
|
||||
: '';
|
||||
|
||||
const primaryAddress = c?.addresses?.find((a) => a.isDefault) || c?.addresses?.[0];
|
||||
const addressString = primaryAddress
|
||||
? `${primaryAddress.street} ${primaryAddress.houseNumber ?? ''}, ${primaryAddress.postalCode} ${primaryAddress.city}`.trim()
|
||||
: '';
|
||||
const addressString = primaryAddress ? formatAddress(primaryAddress) : '';
|
||||
|
||||
const deliveryStr = formatAddress(deliveryAddress);
|
||||
// Rechnungsadresse nur zeigen, wenn sie tatsächlich abweicht.
|
||||
const billingStr =
|
||||
billingAddress && billingAddress.id !== deliveryAddress?.id ? formatAddress(billingAddress) : '';
|
||||
|
||||
const stressfreiList = stressfreiData?.data ?? [];
|
||||
const contractStressfrei = contractStressfreiEmailId
|
||||
? stressfreiList.find((s) => s.id === contractStressfreiEmailId)
|
||||
: undefined;
|
||||
const otherStressfrei = stressfreiList.filter((s) => s.id !== contractStressfreiEmailId);
|
||||
|
||||
const hasContractContext = !!(deliveryStr || billingStr || contractStressfrei || otherStressfrei.length);
|
||||
|
||||
const formatDate = (iso?: string) => {
|
||||
if (!iso) return '';
|
||||
@@ -107,6 +145,34 @@ export default function CustomerInfoModal({ customerId, open, onClose }: Custome
|
||||
value={c.commercialRegisterNumber}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Vertragsbezogene Zusatzinfos */}
|
||||
{hasContractContext && (
|
||||
<div className="pt-3 mt-3 border-t border-gray-200 space-y-3">
|
||||
{deliveryStr && (
|
||||
<CopyableValue label="Lieferadresse (Vertrag)" value={deliveryStr} />
|
||||
)}
|
||||
{billingStr && (
|
||||
<CopyableValue label="Rechnungsadresse (Vertrag)" value={billingStr} />
|
||||
)}
|
||||
{contractStressfrei && (
|
||||
<CopyableValue
|
||||
label="Stressfrei-Adresse (Vertrag)"
|
||||
value={contractStressfrei.email}
|
||||
/>
|
||||
)}
|
||||
{otherStressfrei.length > 0 && (
|
||||
<div>
|
||||
<dt className="text-sm text-gray-500 mb-1">Weitere Stressfrei-Adressen</dt>
|
||||
<dd className="space-y-1">
|
||||
{otherStressfrei.map((s) => (
|
||||
<CopyableValue key={s.id} value={s.email} />
|
||||
))}
|
||||
</dd>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</dl>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user