save email as pdf likae attachment version 2

This commit is contained in:
2026-02-04 19:49:09 +01:00
parent 8c65fecef0
commit f33d157b9b
21 changed files with 1143 additions and 151 deletions
@@ -0,0 +1,194 @@
import { useQuery } from '@tanstack/react-query';
import { contractApi } from '../../services/api';
import Modal from '../ui/Modal';
import Badge from '../ui/Badge';
import Card from '../ui/Card';
import CopyButton from '../ui/CopyButton';
import type { ContractType, ContractStatus } from '../../types';
const typeLabels: Record<ContractType, string> = {
ELECTRICITY: 'Strom',
GAS: 'Gas',
DSL: 'DSL',
CABLE: 'Kabelinternet',
FIBER: 'Glasfaser',
MOBILE: 'Mobilfunk',
TV: 'TV',
CAR_INSURANCE: 'KFZ-Versicherung',
};
const statusLabels: Record<ContractStatus, string> = {
DRAFT: 'Entwurf',
PENDING: 'Ausstehend',
ACTIVE: 'Aktiv',
CANCELLED: 'Gekündigt',
EXPIRED: 'Abgelaufen',
DEACTIVATED: 'Deaktiviert',
};
const statusVariants: Record<ContractStatus, 'success' | 'warning' | 'danger' | 'default'> = {
ACTIVE: 'success',
PENDING: 'warning',
CANCELLED: 'danger',
EXPIRED: 'danger',
DRAFT: 'default',
DEACTIVATED: 'default',
};
interface ContractDetailModalProps {
contractId: number;
isOpen: boolean;
onClose: () => void;
}
export default function ContractDetailModal({ contractId, isOpen, onClose }: ContractDetailModalProps) {
const { data, isLoading, error } = useQuery({
queryKey: ['contract', contractId],
queryFn: () => contractApi.getById(contractId),
enabled: isOpen,
});
const c = data?.data;
return (
<Modal isOpen={isOpen} onClose={onClose} title="Vertragsdetails" size="xl">
{isLoading && (
<div className="text-center py-8 text-gray-500">Laden...</div>
)}
{error && (
<div className="text-center py-8 text-red-600">Fehler beim Laden des Vertrags</div>
)}
{c && (
<div className="space-y-4">
{/* Header */}
<div className="flex items-center gap-3 pb-4 border-b">
<span className="text-xl font-bold font-mono flex items-center gap-2">
{c.contractNumber}
<CopyButton value={c.contractNumber} />
</span>
<Badge>{typeLabels[c.type as ContractType] || c.type}</Badge>
<Badge variant={statusVariants[c.status as ContractStatus] || 'default'}>
{statusLabels[c.status as ContractStatus] || c.status}
</Badge>
</div>
{/* Anbieter & Tarif */}
{(c.providerName || c.provider?.name || c.tariffName || c.tariff?.name) && (
<Card title="Anbieter & Tarif">
<div className="grid grid-cols-2 gap-4">
{(c.providerName || c.provider?.name) && (
<div>
<dt className="text-sm text-gray-500">Anbieter</dt>
<dd className="flex items-center gap-1">
{c.providerName || c.provider?.name}
<CopyButton value={c.providerName || c.provider?.name || ''} />
</dd>
</div>
)}
{(c.tariffName || c.tariff?.name) && (
<div>
<dt className="text-sm text-gray-500">Tarif</dt>
<dd className="flex items-center gap-1">
{c.tariffName || c.tariff?.name}
<CopyButton value={c.tariffName || c.tariff?.name || ''} />
</dd>
</div>
)}
{c.customerNumberAtProvider && (
<div>
<dt className="text-sm text-gray-500">Kundennummer beim Anbieter</dt>
<dd className="font-mono flex items-center gap-1">
{c.customerNumberAtProvider}
<CopyButton value={c.customerNumberAtProvider} />
</dd>
</div>
)}
</div>
</Card>
)}
{/* Laufzeit */}
<Card title="Laufzeit">
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{c.startDate && (
<div>
<dt className="text-sm text-gray-500">Vertragsbeginn</dt>
<dd>{new Date(c.startDate).toLocaleDateString('de-DE')}</dd>
</div>
)}
{c.endDate && (
<div>
<dt className="text-sm text-gray-500">Vertragsende</dt>
<dd>{new Date(c.endDate).toLocaleDateString('de-DE')}</dd>
</div>
)}
{c.contractDuration && (
<div>
<dt className="text-sm text-gray-500">Laufzeit</dt>
<dd>{c.contractDuration.description}</dd>
</div>
)}
{c.cancellationPeriod && (
<div>
<dt className="text-sm text-gray-500">Kündigungsfrist</dt>
<dd>{c.cancellationPeriod.description}</dd>
</div>
)}
</div>
</Card>
{/* Portal-Zugangsdaten */}
{(c.portalUsername || c.provider?.portalUrl) && (
<Card title="Portal-Zugangsdaten">
<div className="grid grid-cols-2 gap-4">
{c.provider?.portalUrl && (
<div>
<dt className="text-sm text-gray-500">Portal-URL</dt>
<dd>
<a
href={c.provider.portalUrl}
target="_blank"
rel="noopener noreferrer"
className="text-blue-600 hover:underline"
>
{c.provider.portalUrl}
</a>
</dd>
</div>
)}
{c.portalUsername && (
<div>
<dt className="text-sm text-gray-500">Benutzername</dt>
<dd className="font-mono flex items-center gap-1">
{c.portalUsername}
<CopyButton value={c.portalUsername} />
</dd>
</div>
)}
</div>
</Card>
)}
{/* Adresse */}
{c.address && (
<Card title="Lieferadresse">
<p>
{c.address.street} {c.address.houseNumber}
</p>
<p>{c.address.postalCode} {c.address.city}</p>
</Card>
)}
{/* Notizen */}
{c.notes && (
<Card title="Notizen">
<p className="whitespace-pre-wrap text-gray-700">{c.notes}</p>
</Card>
)}
</div>
)}
</Modal>
);
}
@@ -0,0 +1 @@
export { default as ContractDetailModal } from './ContractDetailModal';