PdfDragButton: PDF per Drag-and-Drop aus Bankkarte/Ausweis ziehen

Neue wiederverwendbare Komponente PdfDragButton: ziehbares Element,
mit dem der hinterlegte Scan direkt aus dem Browser in ein Mail-Fenster
(Anhang) oder den Datei-Explorer gezogen werden kann (Chromium-
DownloadURL, Format <mime>:<name>:<absolute-url>).

Bewusste Plattform-Grenze: PDF per Strg+V als Datei einfuegen geht im
Browser nicht (Web-Clipboard darf keine OS-Datei-Zwischenablage
befuellen) -> Drag-and-Drop. DownloadURL nur Chrome/Edge, nicht
Firefox (dort Klick-Fallback: Datei im Tab oeffnen).

Eingebaut an: ContractForm (neben IBAN/Ausweisnummer-Copy), Vertrags-
ansicht (Bankkarte/Ausweis-Card), Kundenakte-Tabs Bankkarten+Ausweise.
Nur bei vorhandenem documentPath. Nutzt bestehende fileUrl()-Download-
URL (Token als Query, Per-File-Ownership-Check unveraendert) - kein
neuer Endpoint, keine neue Angriffsflaeche.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-30 11:50:01 +02:00
co-authored by Claude Opus 4.8
parent 1018412afb
commit a509bb7826
5 changed files with 153 additions and 0 deletions
@@ -19,6 +19,7 @@ import toast from 'react-hot-toast';
import JpgToPdfModal from '../../components/ui/JpgToPdfModal';
import { calculateConsumption, calculateCosts, calculateMultiMeterConsumption } from '../../utils/energyCalculations';
import CopyButton, { CopyableBlock } from '../../components/ui/CopyButton';
import PdfDragButton from '../../components/ui/PdfDragButton';
import AutosaveDateInput from '../../components/ui/AutosaveDateInput';
import { formatDate } from '../../utils/dateFormat';
import { useProviderSettings } from '../../hooks/useProviderSettings';
@@ -2651,6 +2652,14 @@ export default function ContractDetail() {
{c.bankCard.iban}
<CopyButton value={c.bankCard.iban} />
</p>
{c.bankCard.documentPath && (
<div className="mt-1">
<PdfDragButton
path={c.bankCard.documentPath}
filename={`Bankkarte-${c.bankCard.iban}`}
/>
</div>
)}
{c.bankCard.bankName && <p className="text-gray-500">{c.bankCard.bankName}</p>}
{c.bankCard.description && (
<p className="text-sm text-gray-600 mt-1 italic whitespace-pre-line">
@@ -2666,6 +2675,14 @@ export default function ContractDetail() {
<CopyButton value={c.identityDocument.documentNumber} />
</p>
<p className="text-gray-500">{c.identityDocument.type}</p>
{c.identityDocument.documentPath && (
<div className="mt-1">
<PdfDragButton
path={c.identityDocument.documentPath}
filename={`Ausweis-${c.identityDocument.documentNumber}`}
/>
</div>
)}
</Card>
)}
</div>
@@ -10,6 +10,7 @@ import Button from '../../components/ui/Button';
import Input from '../../components/ui/Input';
import Select from '../../components/ui/Select';
import CopyButton from '../../components/ui/CopyButton';
import PdfDragButton from '../../components/ui/PdfDragButton';
import CustomerInfoModal from '../../components/contracts/CustomerInfoModal';
import { buildContractLabelParts } from '../../utils/contractLabel';
import type { ContractType } from '../../types';
@@ -978,6 +979,12 @@ export default function ContractForm() {
{selectedBankCard && (
<CopyButton value={selectedBankCard.iban} title="IBAN kopieren" />
)}
{selectedBankCard?.documentPath && (
<PdfDragButton
path={selectedBankCard.documentPath}
filename={`Bankkarte-${selectedBankCard.iban}`}
/>
)}
</span>
}
{...register('bankCardId')}
@@ -1004,6 +1011,12 @@ export default function ContractForm() {
{selectedDocument && (
<CopyButton value={selectedDocument.documentNumber} title="Ausweisnummer kopieren" />
)}
{selectedDocument?.documentPath && (
<PdfDragButton
path={selectedDocument.documentPath}
filename={`Ausweis-${selectedDocument.documentNumber}`}
/>
)}
</span>
}
{...register('identityDocumentId')}
@@ -18,6 +18,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, Cake, RefreshCw, ExternalLink, Images } from 'lucide-react';
import JpgToPdfModal from '../../components/ui/JpgToPdfModal';
import CopyButton, { CopyableBlock } from '../../components/ui/CopyButton';
import PdfDragButton from '../../components/ui/PdfDragButton';
import BirthdayManagementModal from '../../components/BirthdayManagementModal';
import { formatDate } from '../../utils/dateFormat';
import { getContractTypeInfo } from '../../utils/contractInfo';
@@ -1002,6 +1003,10 @@ function BankCardsTab({
<Download className="w-4 h-4" />
Download
</a>
<PdfDragButton
path={card.documentPath}
filename={`Bankkarte-${card.iban}`}
/>
{canEdit && (
<>
<FileUpload
@@ -1238,6 +1243,10 @@ function DocumentsTab({
<Download className="w-4 h-4" />
Download
</a>
<PdfDragButton
path={doc.documentPath}
filename={`Ausweis-${doc.documentNumber}`}
/>
{canEdit && (
<>
<FileUpload