Typspezifische Zusatzinfos in Vertragslisten
Jede Vertragszeile zeigt jetzt eine kontextspezifische Zusatzinfo an: - Strom/Gas: "Lieferadresse: Musterstr. 12, 12345 Berlin" - DSL/Glasfaser/Kabel: "Anschlussadresse: ..." - Mobilfunk: "Rufnummer: 0171 1234567" (Hauptkarte bevorzugt) - KFZ: "Kennzeichen: HB-AB 123" Sichtbar in: - Admin-Vertragsliste (/contracts) - Portal-Vertragsliste (Baumansicht) - Kunden-Detail → Verträge-Tab Backend: getAllContracts + getContractTreeForCustomer liefern mobileDetails (mit simCards), carInsuranceDetails und address mit. Frontend: Neuer Helper utils/contractInfo.ts mit getContractTypeInfo, aus dem sowohl Label als auch Wert pro Typ kommt. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ import CopyButton from '../../components/ui/CopyButton';
|
||||
import { Plus, Search, Eye, Edit, Trash2, User, Users, ChevronDown, ChevronRight, Info, X, ShieldAlert } from 'lucide-react';
|
||||
import { gdprApi } from '../../services/api';
|
||||
import { formatDate } from '../../utils/dateFormat';
|
||||
import { getContractTypeInfo } from '../../utils/contractInfo';
|
||||
import type { Contract, ContractType, ContractStatus } from '../../types';
|
||||
|
||||
const typeLabels: Record<ContractType, string> = {
|
||||
@@ -306,6 +307,14 @@ export default function ContractList() {
|
||||
<CopyButton value={(contract.providerName || contract.provider?.name || '') + ((contract.tariffName || contract.tariff?.name) ? ` - ${contract.tariffName || contract.tariff?.name}` : '')} />
|
||||
</p>
|
||||
)}
|
||||
{(() => {
|
||||
const typeInfo = getContractTypeInfo(contract as any);
|
||||
return typeInfo ? (
|
||||
<p className={`text-sm text-gray-600 ${isPredecessor ? 'ml-6' : ''}`}>
|
||||
<span className="font-medium text-gray-700">{typeInfo.label}:</span> {typeInfo.value}
|
||||
</p>
|
||||
) : null;
|
||||
})()}
|
||||
{contract.startDate && (
|
||||
<p className={`text-sm text-gray-500 ${isPredecessor ? 'ml-6' : ''}`}>
|
||||
Beginn: {formatDate(contract.startDate)}
|
||||
@@ -455,12 +464,19 @@ export default function ContractList() {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.data.map((contract) => (
|
||||
{data.data.map((contract) => {
|
||||
const typeInfo = getContractTypeInfo(contract as any);
|
||||
return (
|
||||
<tr key={contract.id} className="border-b hover:bg-gray-50">
|
||||
<td className="py-3 px-4 font-mono text-sm">
|
||||
<Link to={`/contracts/${contract.id}`} state={pushHistory('/contracts')} className="text-blue-600 hover:underline">
|
||||
{contract.contractNumber}
|
||||
</Link>
|
||||
{typeInfo && (
|
||||
<div className="text-xs text-gray-500 font-sans mt-0.5">
|
||||
<span className="font-medium text-gray-600">{typeInfo.label}:</span> {typeInfo.value}
|
||||
</div>
|
||||
)}
|
||||
</td>
|
||||
{!isCustomer && (
|
||||
<td className="py-3 px-4">
|
||||
@@ -529,7 +545,8 @@ export default function ContractList() {
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -16,6 +16,7 @@ import FileUpload from '../../components/ui/FileUpload';
|
||||
import { Edit, Plus, Trash2, MapPin, CreditCard, FileText, Gauge, Eye, EyeOff, Download, Globe, UserPlus, X, Search, Mail, Copy, Check, ChevronDown, ChevronRight, Info, Shield, ShieldCheck, ShieldX, ShieldAlert, Lock, ArrowLeft } from 'lucide-react';
|
||||
import CopyButton, { CopyableBlock } from '../../components/ui/CopyButton';
|
||||
import { formatDate } from '../../utils/dateFormat';
|
||||
import { getContractTypeInfo } from '../../utils/contractInfo';
|
||||
import type { Address, BankCard, IdentityDocument, Meter, Customer, CustomerRepresentative, CustomerSummary, CustomerConsent, ConsentType, ConsentStatus, RepresentativeAuthorization } from '../../types';
|
||||
|
||||
export default function CustomerDetail({ portalCustomerId }: { portalCustomerId?: number } = {}) {
|
||||
@@ -1671,6 +1672,14 @@ function ContractsTab({
|
||||
<CopyButton value={(contract.providerName || contract.provider?.name || '') + ((contract.tariffName || contract.tariff?.name) ? ` - ${contract.tariffName || contract.tariff?.name}` : '')} />
|
||||
</p>
|
||||
)}
|
||||
{(() => {
|
||||
const typeInfo = getContractTypeInfo(contract as any);
|
||||
return typeInfo ? (
|
||||
<p className={`text-sm text-gray-600 ${isPredecessor ? 'ml-6' : ''}`}>
|
||||
<span className="font-medium text-gray-700">{typeInfo.label}:</span> {typeInfo.value}
|
||||
</p>
|
||||
) : null;
|
||||
})()}
|
||||
{contract.startDate && (
|
||||
<p className={`text-sm text-gray-500 ${isPredecessor ? 'ml-6' : ''}`}>
|
||||
Beginn: {formatDate(contract.startDate)}
|
||||
|
||||
@@ -601,6 +601,13 @@ export interface ContractTreeNodeContract {
|
||||
provider?: { id: number; name: string } | null;
|
||||
tariff?: { id: number; name: string } | null;
|
||||
contractCategory?: { id: number; name: string } | null;
|
||||
customer?: { id: number; firstName: string; lastName: string; companyName: string | null; customerNumber: string } | null;
|
||||
address?: { street: string; houseNumber: string; postalCode: string; city: string } | null;
|
||||
mobileDetails?: {
|
||||
phoneNumber: string | null;
|
||||
simCards: { phoneNumber: string | null; isMain: boolean }[];
|
||||
} | null;
|
||||
carInsuranceDetails?: { licensePlate: string | null } | null;
|
||||
}
|
||||
|
||||
export interface ContractTreeNode {
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
interface ContractInfoData {
|
||||
type: string;
|
||||
address?: { street: string; houseNumber: string; postalCode: string; city: string } | null;
|
||||
mobileDetails?: {
|
||||
phoneNumber: string | null;
|
||||
simCards: { phoneNumber: string | null; isMain: boolean }[];
|
||||
} | null;
|
||||
carInsuranceDetails?: { licensePlate: string | null } | null;
|
||||
}
|
||||
|
||||
export interface ContractTypeInfo {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export function getContractTypeInfo(contract: ContractInfoData): ContractTypeInfo | null {
|
||||
const { type } = contract;
|
||||
|
||||
if (type === 'ELECTRICITY' || type === 'GAS') {
|
||||
const a = contract.address;
|
||||
if (!a) return null;
|
||||
return {
|
||||
label: 'Lieferadresse',
|
||||
value: `${a.street} ${a.houseNumber}, ${a.postalCode} ${a.city}`,
|
||||
};
|
||||
}
|
||||
|
||||
if (type === 'DSL' || type === 'FIBER' || type === 'CABLE') {
|
||||
const a = contract.address;
|
||||
if (!a) return null;
|
||||
return {
|
||||
label: 'Anschlussadresse',
|
||||
value: `${a.street} ${a.houseNumber}, ${a.postalCode} ${a.city}`,
|
||||
};
|
||||
}
|
||||
|
||||
if (type === 'MOBILE') {
|
||||
const md = contract.mobileDetails;
|
||||
if (!md) return null;
|
||||
const mainSim = md.simCards?.find((s) => s.isMain && s.phoneNumber);
|
||||
const anySim = md.simCards?.find((s) => s.phoneNumber);
|
||||
const phone = mainSim?.phoneNumber || anySim?.phoneNumber || md.phoneNumber;
|
||||
if (!phone) return null;
|
||||
return { label: 'Rufnummer', value: phone };
|
||||
}
|
||||
|
||||
if (type === 'CAR_INSURANCE') {
|
||||
const plate = contract.carInsuranceDetails?.licensePlate;
|
||||
if (!plate) return null;
|
||||
return { label: 'Kennzeichen', value: plate };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user