save email as pdf likae attachment version 2
This commit is contained in:
@@ -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';
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Reply, Star, Paperclip, Link2, X, Download, ExternalLink, Trash2, Undo2, Save } from 'lucide-react';
|
||||
import { Reply, Star, Paperclip, Link2, X, Download, ExternalLink, Trash2, Undo2, Save, FileDown } from 'lucide-react';
|
||||
import { CachedEmail, cachedEmailApi } from '../../services/api';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import Button from '../ui/Button';
|
||||
@@ -7,6 +7,7 @@ import { Link } from 'react-router-dom';
|
||||
import { useAuth } from '../../context/AuthContext';
|
||||
import toast from 'react-hot-toast';
|
||||
import SaveAttachmentModal from './SaveAttachmentModal';
|
||||
import SaveEmailAsPdfModal from './SaveEmailAsPdfModal';
|
||||
|
||||
interface EmailDetailProps {
|
||||
email: CachedEmail;
|
||||
@@ -37,6 +38,7 @@ export default function EmailDetail({
|
||||
const [showRestoreConfirm, setShowRestoreConfirm] = useState(false);
|
||||
const [showPermanentDeleteConfirm, setShowPermanentDeleteConfirm] = useState(false);
|
||||
const [saveAttachmentFilename, setSaveAttachmentFilename] = useState<string | null>(null);
|
||||
const [showSaveAsPdfModal, setShowSaveAsPdfModal] = useState(false);
|
||||
const queryClient = useQueryClient();
|
||||
const { hasPermission } = useAuth();
|
||||
|
||||
@@ -219,6 +221,15 @@ export default function EmailDetail({
|
||||
<Reply className="w-4 h-4 mr-1" />
|
||||
Antworten
|
||||
</Button>
|
||||
{/* E-Mail als PDF speichern */}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setShowSaveAsPdfModal(true)}
|
||||
title="E-Mail als PDF speichern"
|
||||
>
|
||||
<FileDown className="w-4 h-4" />
|
||||
</Button>
|
||||
{/* Löschen-Button nur für User mit emails:delete Permission */}
|
||||
{hasPermission('emails:delete') && (
|
||||
<Button
|
||||
@@ -481,6 +492,15 @@ export default function EmailDetail({
|
||||
attachmentFilename={saveAttachmentFilename}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* E-Mail als PDF speichern Modal */}
|
||||
{showSaveAsPdfModal && (
|
||||
<SaveEmailAsPdfModal
|
||||
isOpen={true}
|
||||
onClose={() => setShowSaveAsPdfModal(false)}
|
||||
emailId={email.id}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,3 +4,4 @@ export { default as ComposeEmailModal } from './ComposeEmailModal';
|
||||
export { default as AssignToContractModal } from './AssignToContractModal';
|
||||
export { default as EmailClientTab } from './EmailClientTab';
|
||||
export { default as ContractEmailsSection } from './ContractEmailsSection';
|
||||
export { default as SaveEmailAsPdfModal } from './SaveEmailAsPdfModal';
|
||||
|
||||
Reference in New Issue
Block a user