import { useEffect, useState } from 'react'; import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useForm } from 'react-hook-form'; import { contractApi, customerApi, platformApi, cancellationPeriodApi, contractDurationApi, providerApi, contractCategoryApi } from '../../services/api'; import Card from '../../components/ui/Card'; import Button from '../../components/ui/Button'; import Input from '../../components/ui/Input'; import Select from '../../components/ui/Select'; import type { ContractType } from '../../types'; import { Plus, Trash2, Eye, EyeOff, Info, X } from 'lucide-react'; // Contract types are now loaded dynamically from the database const statusOptions = [ { value: 'DRAFT', label: 'Entwurf' }, { value: 'PENDING', label: 'Ausstehend' }, { value: 'ACTIVE', label: 'Aktiv' }, { value: 'CANCELLED', label: 'Gekündigt' }, { value: 'EXPIRED', label: 'Abgelaufen' }, { value: 'DEACTIVATED', label: 'Deaktiviert' }, ]; // Status-Erklärungen für Info-Modal const statusDescriptions = [ { status: 'DRAFT', label: 'Entwurf', description: 'Vertrag wird noch vorbereitet', color: 'text-gray-600' }, { status: 'PENDING', label: 'Ausstehend', description: 'Wartet auf Aktivierung', color: 'text-yellow-600' }, { status: 'ACTIVE', label: 'Aktiv', description: 'Vertrag läuft normal', color: 'text-green-600' }, { status: 'EXPIRED', label: 'Abgelaufen', description: 'Laufzeit vorbei, läuft aber ohne Kündigung weiter', color: 'text-orange-600' }, { status: 'CANCELLED', label: 'Gekündigt', description: 'Aktive Kündigung eingereicht, Vertrag endet', color: 'text-red-600' }, { status: 'DEACTIVATED', label: 'Deaktiviert', description: 'Manuell beendet/archiviert', color: 'text-gray-500' }, ]; function StatusInfoModal({ isOpen, onClose }: { isOpen: boolean; onClose: () => void }) { if (!isOpen) return null; return (

Vertragsstatus-Übersicht

{statusDescriptions.map(({ status, label, description, color }) => (
{label} {description}
))}
); } export default function ContractForm() { const { id } = useParams(); const [searchParams] = useSearchParams(); const navigate = useNavigate(); const queryClient = useQueryClient(); const isEdit = !!id; const preselectedCustomerId = searchParams.get('customerId'); const { register, handleSubmit, reset, watch, setValue, formState: { errors } } = useForm({ defaultValues: { customerId: preselectedCustomerId || '', type: 'ELECTRICITY', status: 'DRAFT', previousContractId: '', }, }); const contractType = watch('type') as ContractType; const customerId = watch('customerId'); // Fetch existing contract for edit const { data: contract } = useQuery({ queryKey: ['contract', id], queryFn: () => contractApi.getById(parseInt(id!)), enabled: isEdit, }); // Fetch customers for dropdown const { data: customersData } = useQuery({ queryKey: ['customers-all'], queryFn: () => customerApi.getAll({ limit: 1000 }), }); // Fetch customer details when selected const { data: customerDetail } = useQuery({ queryKey: ['customer', customerId], queryFn: () => customerApi.getById(parseInt(customerId)), enabled: !!customerId, }); // Fetch contracts for same customer (for predecessor selection) const { data: customerContractsData } = useQuery({ queryKey: ['customer-contracts-for-predecessor', customerId], queryFn: () => contractApi.getAll({ customerId: parseInt(customerId), limit: 1000 }), enabled: !!customerId, }); // Fetch platforms const { data: platformsData } = useQuery({ queryKey: ['platforms'], queryFn: () => platformApi.getAll(), }); // Fetch cancellation periods const { data: cancellationPeriodsData } = useQuery({ queryKey: ['cancellation-periods'], queryFn: () => cancellationPeriodApi.getAll(), }); // Fetch contract durations const { data: contractDurationsData } = useQuery({ queryKey: ['contract-durations'], queryFn: () => contractDurationApi.getAll(), }); // Fetch providers const { data: providersData } = useQuery({ queryKey: ['providers'], queryFn: () => providerApi.getAll(), }); // Fetch contract categories (types) const { data: contractCategoriesData } = useQuery({ queryKey: ['contract-categories'], queryFn: () => contractCategoryApi.getAll(), }); // Watch providerId for tariff filtering const selectedProviderId = watch('providerId'); // Reset tariffId when providerId changes (but not on initial load) const [previousProviderId, setPreviousProviderId] = useState(null); // SIM-Karten State für Mobilfunk interface SimCardInput { id?: number; phoneNumber: string; simCardNumber: string; pin: string; puk: string; hasExistingPin?: boolean; // Zeigt an ob PIN bereits in DB vorhanden hasExistingPuk?: boolean; // Zeigt an ob PUK bereits in DB vorhanden isMultisim: boolean; isMain: boolean; } const [simCards, setSimCards] = useState([]); // Rufnummern State für Internet-Verträge interface PhoneNumberInput { id?: number; phoneNumber: string; sipUsername: string; sipPassword: string; hasExistingSipPassword?: boolean; sipServer: string; isMain: boolean; } const [phoneNumbers, setPhoneNumbers] = useState([]); const [isContractLoaded, setIsContractLoaded] = useState(false); // Portal-Benutzername: Manuell oder Stressfrei-Adresse const [usernameType, setUsernameType] = useState<'manual' | 'stressfrei'>('manual'); const [selectedStressfreiEmailId, setSelectedStressfreiEmailId] = useState(''); // Passwort-Sichtbarkeit const [showPortalPassword, setShowPortalPassword] = useState(false); const [showInternetPassword, setShowInternetPassword] = useState(false); const [showSipPasswords, setShowSipPasswords] = useState>({}); const [showSimPins, setShowSimPins] = useState>({}); const [showSimPuks, setShowSimPuks] = useState>({}); // Status-Info Modal const [showStatusInfo, setShowStatusInfo] = useState(false); // For new contracts, mark as "loaded" immediately so provider change detection works useEffect(() => { if (!isEdit) { setIsContractLoaded(true); } }, [isEdit]); // Set preselected customer from URL params (for new contracts) useEffect(() => { if (!isEdit && preselectedCustomerId && customersData?.data) { // Only set if the customer exists in the list const customerExists = customersData.data.some(c => c.id.toString() === preselectedCustomerId); if (customerExists) { setValue('customerId', preselectedCustomerId); } } }, [isEdit, preselectedCustomerId, customersData, setValue]); // Reset tariffId when providerId changes (but only after initial contract load) useEffect(() => { // Only reset tariff if: // 1. Contract has been loaded (or we're creating new) // 2. Provider actually changed (not empty/null -> value which happens on load) // Note: previousProviderId can be '' (empty string) initially, so we need to check for truthy if (isContractLoaded && previousProviderId && selectedProviderId !== previousProviderId) { setValue('tariffId', ''); } setPreviousProviderId(selectedProviderId); }, [selectedProviderId, previousProviderId, setValue, isContractLoaded]); // Set default type when categories are loaded (only for new contracts) useEffect(() => { if (!isEdit && contractCategoriesData?.data && contractCategoriesData.data.length > 0) { const currentType = watch('type'); // Only set if current type is empty or doesn't exist in categories const categories = contractCategoriesData.data.filter(c => c.isActive); const typeExists = categories.some(c => c.code === currentType); if (!currentType || !typeExists) { const firstCategory = categories.sort((a, b) => a.sortOrder - b.sortOrder)[0]; if (firstCategory) { setValue('type', firstCategory.code); } } } }, [contractCategoriesData, isEdit, setValue, watch]); // Bootstrap: Set customerId first so customerDetail can load (needed for dropdowns) useEffect(() => { if (isEdit && contract?.data && !customerId) { setValue('customerId', contract.data.customerId.toString()); } }, [isEdit, contract, customerId, setValue]); // Reset form when contract data AND ALL dropdown data are loaded // This ensures dropdowns can properly display the selected values // We need customerDetail for addresses/bankCards/documents and providersData for tariffs useEffect(() => { if (contract?.data && platformsData?.data && contractCategoriesData?.data && providersData?.data && customerDetail?.data) { const c = contract.data; reset({ customerId: c.customerId.toString(), type: c.type, status: c.status, addressId: c.addressId?.toString() || '', billingAddressId: c.billingAddressId?.toString() || '', bankCardId: c.bankCardId?.toString() || '', identityDocumentId: c.identityDocumentId?.toString() || '', salesPlatformId: c.salesPlatformId?.toString() || '', providerId: c.providerId?.toString() || '', tariffId: c.tariffId?.toString() || '', providerName: c.providerName || '', tariffName: c.tariffName || '', customerNumberAtProvider: c.customerNumberAtProvider || '', priceFirst12Months: c.priceFirst12Months || '', priceFrom13Months: c.priceFrom13Months || '', priceAfter24Months: c.priceAfter24Months || '', startDate: c.startDate ? c.startDate.split('T')[0] : '', endDate: c.endDate ? c.endDate.split('T')[0] : '', cancellationPeriodId: c.cancellationPeriodId?.toString() || '', contractDurationId: c.contractDurationId?.toString() || '', commission: c.commission || '', portalUsername: c.portalUsername || '', notes: c.notes || '', // Energy details meterId: c.energyDetails?.meterId?.toString() || '', maloId: c.energyDetails?.maloId || '', annualConsumption: c.energyDetails?.annualConsumption || '', annualConsumptionKwh: c.energyDetails?.annualConsumptionKwh || '', basePrice: c.energyDetails?.basePrice || '', unitPrice: c.energyDetails?.unitPrice || '', bonus: c.energyDetails?.bonus || '', previousProviderName: c.energyDetails?.previousProviderName || '', previousCustomerNumber: c.energyDetails?.previousCustomerNumber || '', // Internet details downloadSpeed: c.internetDetails?.downloadSpeed || '', uploadSpeed: c.internetDetails?.uploadSpeed || '', routerModel: c.internetDetails?.routerModel || '', routerSerialNumber: c.internetDetails?.routerSerialNumber || '', installationDate: c.internetDetails?.installationDate ? c.internetDetails.installationDate.split('T')[0] : '', internetUsername: c.internetDetails?.internetUsername || '', homeId: c.internetDetails?.homeId || '', activationCode: c.internetDetails?.activationCode || '', // Mobile details requiresMultisim: c.mobileDetails?.requiresMultisim || false, dataVolume: c.mobileDetails?.dataVolume || '', includedMinutes: c.mobileDetails?.includedMinutes || '', includedSMS: c.mobileDetails?.includedSMS || '', deviceModel: c.mobileDetails?.deviceModel || '', deviceImei: c.mobileDetails?.deviceImei || '', // Legacy phoneNumber: c.mobileDetails?.phoneNumber || '', simCardNumber: c.mobileDetails?.simCardNumber || '', // TV details receiverModel: c.tvDetails?.receiverModel || '', smartcardNumber: c.tvDetails?.smartcardNumber || '', tvPackage: c.tvDetails?.package || '', // Car insurance details licensePlate: c.carInsuranceDetails?.licensePlate || '', hsn: c.carInsuranceDetails?.hsn || '', tsn: c.carInsuranceDetails?.tsn || '', vin: c.carInsuranceDetails?.vin || '', vehicleType: c.carInsuranceDetails?.vehicleType || '', firstRegistration: c.carInsuranceDetails?.firstRegistration ? c.carInsuranceDetails.firstRegistration.split('T')[0] : '', noClaimsClass: c.carInsuranceDetails?.noClaimsClass || '', insuranceType: c.carInsuranceDetails?.insuranceType || 'LIABILITY', deductiblePartial: c.carInsuranceDetails?.deductiblePartial || '', deductibleFull: c.carInsuranceDetails?.deductibleFull || '', policyNumber: c.carInsuranceDetails?.policyNumber || '', previousInsurer: c.carInsuranceDetails?.previousInsurer || '', // Kündigungsdaten cancellationConfirmationDate: c.cancellationConfirmationDate ? c.cancellationConfirmationDate.split('T')[0] : '', cancellationConfirmationOptionsDate: c.cancellationConfirmationOptionsDate ? c.cancellationConfirmationOptionsDate.split('T')[0] : '', wasSpecialCancellation: c.wasSpecialCancellation || false, // Vorgänger-Vertrag previousContractId: c.previousContractId?.toString() || '', }); // Load simCards if available if (c.mobileDetails?.simCards && c.mobileDetails.simCards.length > 0) { setSimCards(c.mobileDetails.simCards.map(sc => ({ id: sc.id, phoneNumber: sc.phoneNumber || '', simCardNumber: sc.simCardNumber || '', pin: '', // PIN/PUK werden nicht im Klartext geladen puk: '', hasExistingPin: !!sc.pin, // true wenn verschlüsselter Wert vorhanden hasExistingPuk: !!sc.puk, // true wenn verschlüsselter Wert vorhanden isMultisim: sc.isMultisim, isMain: sc.isMain, }))); } else { setSimCards([]); } // Load phoneNumbers if available (Internet contracts) if (c.internetDetails?.phoneNumbers && c.internetDetails.phoneNumbers.length > 0) { setPhoneNumbers(c.internetDetails.phoneNumbers.map(pn => ({ id: pn.id, phoneNumber: pn.phoneNumber || '', sipUsername: pn.sipUsername || '', sipPassword: '', // SIP-Passwort wird nicht im Klartext geladen hasExistingSipPassword: !!pn.sipPasswordEncrypted, sipServer: pn.sipServer || '', isMain: pn.isMain, }))); } else { setPhoneNumbers([]); } // Portal-Benutzername-Typ initialisieren if (c.stressfreiEmailId) { setUsernameType('stressfrei'); setSelectedStressfreiEmailId(c.stressfreiEmailId.toString()); } else { setUsernameType('manual'); setSelectedStressfreiEmailId(''); } // Mark contract as loaded so provider change detection works correctly setIsContractLoaded(true); } }, [contract, reset, platformsData, contractCategoriesData, providersData, customerDetail]); // Watch für Enddatum-Berechnung const startDate = watch('startDate'); const contractDurationId = watch('contractDurationId'); // Berechne Vertragsende aus Beginn + Laufzeit useEffect(() => { if (startDate && contractDurationId && contractDurationsData?.data) { const duration = contractDurationsData.data.find(d => d.id === parseInt(contractDurationId)); if (duration) { const start = new Date(startDate); const code = duration.code; // Parse duration code (z.B. "12M", "24M", "1J", "2J") const match = code.match(/^(\d+)([MTJ])$/); if (match) { const value = parseInt(match[1]); const unit = match[2]; let endDate = new Date(start); if (unit === 'T') { endDate.setDate(endDate.getDate() + value); } else if (unit === 'M') { endDate.setMonth(endDate.getMonth() + value); } else if (unit === 'J') { endDate.setFullYear(endDate.getFullYear() + value); } // Setze das berechnete Enddatum setValue('endDate', endDate.toISOString().split('T')[0]); } } } }, [startDate, contractDurationId, contractDurationsData, setValue]); const createMutation = useMutation({ mutationFn: contractApi.create, onSuccess: (_, variables) => { queryClient.invalidateQueries({ queryKey: ['contracts'] }); if (variables.customerId) { queryClient.invalidateQueries({ queryKey: ['customer', variables.customerId.toString()] }); } queryClient.invalidateQueries({ queryKey: ['customers'] }); // Navigate back to customer's contracts tab if coming from customer if (preselectedCustomerId) { navigate(`/customers/${preselectedCustomerId}?tab=contracts`); } else { navigate('/contracts'); } }, }); const updateMutation = useMutation({ mutationFn: (data: any) => contractApi.update(parseInt(id!), data), onSuccess: (_, variables) => { queryClient.invalidateQueries({ queryKey: ['contracts'] }); queryClient.invalidateQueries({ queryKey: ['contract', id] }); if (variables.customerId) { queryClient.invalidateQueries({ queryKey: ['customer', variables.customerId.toString()] }); } queryClient.invalidateQueries({ queryKey: ['customers'] }); navigate(`/contracts/${id}`); }, }); const onSubmit = (data: any) => { // Helper to safely parse integers - returns undefined for empty/invalid values const safeParseInt = (val: string | number | undefined | null): number | undefined => { if (val === undefined || val === null || val === '') return undefined; const parsed = parseInt(String(val)); return isNaN(parsed) ? undefined : parsed; }; // Find the selected category to get its ID const selectedCategory = contractCategories.find(c => c.code === data.type); // Validate required fields const customerIdParsed = safeParseInt(data.customerId); if (!customerIdParsed) { alert('Bitte wählen Sie einen Kunden aus'); return; } if (!data.type || !selectedCategory) { alert('Bitte wählen Sie einen Vertragstyp aus'); return; } // Helper: Leere Strings werden zu null, damit Prisma das Feld auf NULL setzt const emptyToNull = (val: string | undefined | null): string | null => { if (val === undefined || val === null || val === '') return null; return val; }; const contractData: any = { customerId: customerIdParsed, type: data.type, contractCategoryId: selectedCategory.id, status: data.status, addressId: safeParseInt(data.addressId) ?? null, billingAddressId: safeParseInt(data.billingAddressId) ?? null, bankCardId: safeParseInt(data.bankCardId) ?? null, identityDocumentId: safeParseInt(data.identityDocumentId) ?? null, salesPlatformId: safeParseInt(data.salesPlatformId) ?? null, providerId: safeParseInt(data.providerId) ?? null, tariffId: safeParseInt(data.tariffId) ?? null, providerName: emptyToNull(data.providerName), tariffName: emptyToNull(data.tariffName), customerNumberAtProvider: emptyToNull(data.customerNumberAtProvider), priceFirst12Months: emptyToNull(data.priceFirst12Months), priceFrom13Months: emptyToNull(data.priceFrom13Months), priceAfter24Months: emptyToNull(data.priceAfter24Months), startDate: data.startDate ? new Date(data.startDate) : null, endDate: data.endDate ? new Date(data.endDate) : null, cancellationPeriodId: safeParseInt(data.cancellationPeriodId) ?? null, contractDurationId: safeParseInt(data.contractDurationId) ?? null, commission: data.commission ? parseFloat(data.commission) : null, // Portal-Benutzername: entweder manuell oder Stressfrei-Adresse portalUsername: usernameType === 'manual' ? emptyToNull(data.portalUsername) : null, stressfreiEmailId: usernameType === 'stressfrei' && selectedStressfreiEmailId ? parseInt(selectedStressfreiEmailId) : null, portalPassword: data.portalPassword || undefined, // Passwort: undefined = nicht ändern notes: emptyToNull(data.notes), cancellationConfirmationDate: data.cancellationConfirmationDate ? new Date(data.cancellationConfirmationDate) : null, cancellationConfirmationOptionsDate: data.cancellationConfirmationOptionsDate ? new Date(data.cancellationConfirmationOptionsDate) : null, wasSpecialCancellation: data.wasSpecialCancellation || false, previousContractId: safeParseInt(data.previousContractId) ?? null, }; // Add type-specific details if (['ELECTRICITY', 'GAS'].includes(data.type)) { contractData.energyDetails = { meterId: safeParseInt(data.meterId) ?? null, maloId: emptyToNull(data.maloId), annualConsumption: data.annualConsumption ? parseFloat(data.annualConsumption) : null, annualConsumptionKwh: data.annualConsumptionKwh ? parseFloat(data.annualConsumptionKwh) : null, basePrice: data.basePrice ? parseFloat(data.basePrice) : null, unitPrice: data.unitPrice ? parseFloat(data.unitPrice) : null, bonus: data.bonus ? parseFloat(data.bonus) : null, previousProviderName: emptyToNull(data.previousProviderName), previousCustomerNumber: emptyToNull(data.previousCustomerNumber), }; } if (['DSL', 'CABLE', 'FIBER'].includes(data.type)) { contractData.internetDetails = { downloadSpeed: safeParseInt(data.downloadSpeed) ?? null, uploadSpeed: safeParseInt(data.uploadSpeed) ?? null, routerModel: emptyToNull(data.routerModel), routerSerialNumber: emptyToNull(data.routerSerialNumber), installationDate: data.installationDate ? new Date(data.installationDate) : null, // Internet-Zugangsdaten internetUsername: emptyToNull(data.internetUsername), internetPassword: data.internetPassword || undefined, // Passwort: undefined = nicht ändern // Glasfaser-spezifisch homeId: emptyToNull(data.homeId), // Vodafone DSL/Kabel spezifisch activationCode: emptyToNull(data.activationCode), // Rufnummern mit SIP-Daten phoneNumbers: phoneNumbers.length > 0 ? phoneNumbers.map(pn => ({ id: pn.id, phoneNumber: pn.phoneNumber || '', isMain: pn.isMain ?? false, sipUsername: emptyToNull(pn.sipUsername), sipPassword: pn.sipPassword || undefined, // Passwort: undefined = nicht ändern sipServer: emptyToNull(pn.sipServer), })) : undefined, }; } if (data.type === 'MOBILE') { contractData.mobileDetails = { requiresMultisim: data.requiresMultisim || false, dataVolume: data.dataVolume ? parseFloat(data.dataVolume) : null, includedMinutes: safeParseInt(data.includedMinutes) ?? null, includedSMS: safeParseInt(data.includedSMS) ?? null, deviceModel: emptyToNull(data.deviceModel), deviceImei: emptyToNull(data.deviceImei), // Legacy phoneNumber: emptyToNull(data.phoneNumber), simCardNumber: emptyToNull(data.simCardNumber), // SIM-Karten simCards: simCards.length > 0 ? simCards.map(sc => ({ id: sc.id, phoneNumber: emptyToNull(sc.phoneNumber), simCardNumber: emptyToNull(sc.simCardNumber), pin: sc.pin || undefined, // Passwort: undefined = nicht ändern puk: sc.puk || undefined, // Passwort: undefined = nicht ändern isMultisim: sc.isMultisim, isMain: sc.isMain, })) : undefined, }; } if (data.type === 'TV') { contractData.tvDetails = { receiverModel: emptyToNull(data.receiverModel), smartcardNumber: emptyToNull(data.smartcardNumber), package: emptyToNull(data.tvPackage), }; } if (data.type === 'CAR_INSURANCE') { contractData.carInsuranceDetails = { licensePlate: emptyToNull(data.licensePlate), hsn: emptyToNull(data.hsn), tsn: emptyToNull(data.tsn), vin: emptyToNull(data.vin), vehicleType: emptyToNull(data.vehicleType), firstRegistration: data.firstRegistration ? new Date(data.firstRegistration) : null, noClaimsClass: emptyToNull(data.noClaimsClass), insuranceType: data.insuranceType, deductiblePartial: data.deductiblePartial ? parseFloat(data.deductiblePartial) : null, deductibleFull: data.deductibleFull ? parseFloat(data.deductibleFull) : null, policyNumber: emptyToNull(data.policyNumber), previousInsurer: emptyToNull(data.previousInsurer), }; } if (isEdit) { updateMutation.mutate(contractData); } else { createMutation.mutate(contractData); } }; const isLoading = createMutation.isPending || updateMutation.isPending; const error = createMutation.error || updateMutation.error; const customer = customerDetail?.data; const addresses = customer?.addresses || []; const bankCards = customer?.bankCards?.filter((c) => c.isActive) || []; const documents = customer?.identityDocuments?.filter((d) => d.isActive) || []; const meters = customer?.meters?.filter((m) => m.isActive) || []; const stressfreiEmails = customer?.stressfreiEmails?.filter((e: { isActive: boolean }) => e.isActive) || []; const platforms = platformsData?.data || []; const cancellationPeriods = cancellationPeriodsData?.data || []; const contractDurations = contractDurationsData?.data || []; const providers = providersData?.data?.filter(p => p.isActive) || []; const contractCategories = contractCategoriesData?.data?.filter(c => c.isActive).sort((a, b) => a.sortOrder - b.sortOrder) || []; const typeOptions = contractCategories.map(c => ({ value: c.code, label: c.name })); // Available predecessor contracts (same customer, excluding current contract if editing) const predecessorContracts = (customerContractsData?.data || []) .filter(c => !isEdit || c.id !== parseInt(id!)) .sort((a, b) => new Date(b.startDate || 0).getTime() - new Date(a.startDate || 0).getTime()); // Get tariffs for selected provider const selectedProvider = providers.find(p => p.id === parseInt(selectedProviderId || '0')); const availableTariffs = selectedProvider?.tariffs?.filter(t => t.isActive) || []; // Helper to format customer label with optional birth date const formatCustomerLabel = (c: { customerNumber: string; companyName?: string; firstName: string; lastName: string; birthDate?: string }) => { const name = c.companyName || `${c.firstName} ${c.lastName}`; const birthDateStr = c.birthDate ? ` (geb. ${new Date(c.birthDate).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' })})` : ''; return `${c.customerNumber} - ${name}${birthDateStr}`; }; // Build customer options, ensuring current customer from contract is always included const customerOptions = (() => { const customers = customersData?.data || []; const options = customers.map((c) => ({ value: c.id.toString(), label: formatCustomerLabel(c), })); // If editing and contract's customer isn't in the list yet, add it if (isEdit && contract?.data?.customer) { const contractCustomer = contract.data.customer; const exists = options.some((o) => o.value === contractCustomer.id.toString()); if (!exists) { options.unshift({ value: contractCustomer.id.toString(), label: formatCustomerLabel(contractCustomer), }); } } return options; })(); return (

{isEdit ? 'Vertrag bearbeiten' : 'Neuer Vertrag'}

{error && (
{error instanceof Error ? error.message : 'Ein Fehler ist aufgetreten'}
)}
({ value: p.id, label: p.name }))} /> {/* Vorgänger-Vertrag auswählen (nur wenn Kunde gewählt) */} {customerId && ( a.type === 'DELIVERY_RESIDENCE') .map((a) => ({ value: a.id, label: `${a.street} ${a.houseNumber}, ${a.postalCode} ${a.city}`, }))} /> ({ value: c.id, label: `${c.iban} (${c.accountHolder})`, }))} /> ({ value: p.id, label: p.name }))} />

Hinweis: Ist die Laufzeit ≤ 4 Wochen, 1 Monat oder 30 Tage, gilt der Vertrag als unbefristet mit der jeweiligen Kündigungsfrist.

setValue('startDate', '')} /> ({ value: cp.id, label: cp.description }))} /> setValue('cancellationConfirmationDate', '')} /> setValue('cancellationConfirmationOptionsDate', '')} />
{usernameType === 'manual' && ( )} {usernameType === 'stressfrei' && (
{/* Energy-specific fields */} {['ELECTRICITY', 'GAS'].includes(contractType) && (
{contractType === 'GAS' && ( )}
)} {/* Internet-specific fields */} {['DSL', 'CABLE', 'FIBER'].includes(contractType) && ( <>
setValue('installationDate', '')} /> {/* HomeID nur bei Glasfaser */} {contractType === 'FIBER' && ( )} {/* Aktivierungscode bei Vodafone + DSL oder Kabel */} {selectedProvider?.name?.toLowerCase().includes('vodafone') && ['DSL', 'CABLE'].includes(contractType) && ( )}

Hier können Sie Festnetz-Rufnummern mit SIP-Zugangsdaten erfassen.

{phoneNumbers.length > 0 && (
{phoneNumbers.map((pn, index) => (
Rufnummer {index + 1}
{ const updated = [...phoneNumbers]; updated[index].phoneNumber = e.target.value; setPhoneNumbers(updated); }} placeholder="z.B. 030 123456" /> { const updated = [...phoneNumbers]; updated[index].sipUsername = e.target.value; setPhoneNumbers(updated); }} />
{ const updated = [...phoneNumbers]; updated[index].sipPassword = e.target.value; setPhoneNumbers(updated); }} placeholder={pn.hasExistingSipPassword ? "Leer = beibehalten" : ""} className="block w-full px-3 py-2 pr-10 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" />
{ const updated = [...phoneNumbers]; updated[index].sipServer = e.target.value; setPhoneNumbers(updated); }} placeholder="z.B. sip.provider.de" />
))}
)}
)} {/* Mobile-specific fields */} {contractType === 'MOBILE' && ( <>
{/* Multisim erforderlich */}

Hier können Sie alle SIM-Karten zum Vertrag erfassen (Hauptkarte und Multisim-Karten).

{simCards.length > 0 && (
{simCards.map((card, index) => (
SIM-Karte {index + 1}
{ const updated = [...simCards]; updated[index].phoneNumber = e.target.value; setSimCards(updated); }} placeholder="z.B. 0171 1234567" /> { const updated = [...simCards]; updated[index].simCardNumber = e.target.value; setSimCards(updated); }} placeholder="ICCID" />
{ const updated = [...simCards]; updated[index].pin = e.target.value; setSimCards(updated); }} placeholder={card.hasExistingPin ? "Leer = beibehalten" : "4-stellig"} className="block w-full px-3 py-2 pr-10 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" />
{ const updated = [...simCards]; updated[index].puk = e.target.value; setSimCards(updated); }} placeholder={card.hasExistingPuk ? "Leer = beibehalten" : "8-stellig"} className="block w-full px-3 py-2 pr-10 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" />
))}
)}
)} {/* TV-specific fields */} {contractType === 'TV' && (
)} {/* Car Insurance-specific fields */} {contractType === 'CAR_INSURANCE' && (
setValue('firstRegistration', '')} />
)}