import { useState, useRef, useEffect } from 'react'; import { Send, Paperclip, X, FileText } from 'lucide-react'; import Modal from '../ui/Modal'; import Button from '../ui/Button'; import { stressfreiEmailApi, CachedEmail, MailboxAccount, EmailAttachment } from '../../services/api'; import { useMutation } from '@tanstack/react-query'; interface ComposeEmailModalProps { isOpen: boolean; onClose: () => void; account: MailboxAccount; replyTo?: CachedEmail; onSuccess?: () => void; contractId?: number; // Optional: Vertrag dem die gesendete E-Mail zugeordnet wird } export default function ComposeEmailModal({ isOpen, onClose, account, replyTo, onSuccess, contractId, }: ComposeEmailModalProps) { const [to, setTo] = useState(''); const [cc, setCc] = useState(''); const [subject, setSubject] = useState(''); const [body, setBody] = useState(''); const [attachments, setAttachments] = useState([]); const [error, setError] = useState(null); const fileInputRef = useRef(null); // Formular bei Modal-Öffnung initialisieren useEffect(() => { if (isOpen) { if (replyTo) { // Antwort: Felder vorausfüllen setTo(replyTo.fromAddress || ''); // Betreff: "Re:" nur hinzufügen wenn nicht schon vorhanden const existingSubject = replyTo.subject || ''; const hasRePrefix = /^(Re|Aw|Fwd|Wg):\s*/i.test(existingSubject); setSubject(hasRePrefix ? existingSubject : `Re: ${existingSubject}`); // Ursprüngliche Nachricht zitieren const originalDate = new Date(replyTo.receivedAt).toLocaleString('de-DE'); const quotedText = replyTo.textBody ? `\n\n--- Ursprüngliche Nachricht ---\nVon: ${replyTo.fromName || replyTo.fromAddress}\nAm: ${originalDate}\n\n${replyTo.textBody}` : ''; setBody(quotedText); } else { // Neue E-Mail: Felder leer setTo(''); setSubject(''); setBody(''); } setCc(''); setAttachments([]); setError(null); } }, [isOpen, replyTo]); // Maximale Dateigröße: 10 MB const MAX_FILE_SIZE = 10 * 1024 * 1024; // Maximale Gesamtgröße aller Anhänge: 25 MB const MAX_TOTAL_SIZE = 25 * 1024 * 1024; // Datei zu Base64 konvertieren const fileToBase64 = (file: File): Promise => { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => { // data:application/pdf;base64,JVBERi0... -> JVBERi0... const result = reader.result as string; const base64 = result.split(',')[1]; resolve(base64); }; reader.onerror = reject; }); }; // Dateien hinzufügen const handleFileSelect = async (e: React.ChangeEvent) => { const files = e.target.files; if (!files) return; const newAttachments: EmailAttachment[] = []; let currentTotalSize = attachments.reduce( (sum, att) => sum + (att.content.length * 0.75), // Base64 ist ~33% größer 0 ); for (const file of Array.from(files)) { // Einzelne Dateigröße prüfen if (file.size > MAX_FILE_SIZE) { setError(`Datei "${file.name}" ist zu groß (max. 10 MB)`); continue; } // Gesamtgröße prüfen if (currentTotalSize + file.size > MAX_TOTAL_SIZE) { setError('Maximale Gesamtgröße der Anhänge erreicht (25 MB)'); break; } try { const content = await fileToBase64(file); newAttachments.push({ filename: file.name, content, contentType: file.type || 'application/octet-stream', }); currentTotalSize += file.size; } catch { setError(`Fehler beim Lesen von "${file.name}"`); } } if (newAttachments.length > 0) { setAttachments((prev) => [...prev, ...newAttachments]); } // Input zurücksetzen damit gleiche Datei erneut gewählt werden kann if (fileInputRef.current) { fileInputRef.current.value = ''; } }; // Anhang entfernen const removeAttachment = (index: number) => { setAttachments((prev) => prev.filter((_, i) => i !== index)); }; // Dateigröße formatieren const formatFileSize = (base64Content: string): string => { const bytes = base64Content.length * 0.75; // Base64 Dekodierung if (bytes < 1024) return `${Math.round(bytes)} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; }; const sendMutation = useMutation({ mutationFn: () => stressfreiEmailApi.sendEmail(account.id, { to: to.split(',').map((e) => e.trim()).filter(Boolean), cc: cc ? cc.split(',').map((e) => e.trim()).filter(Boolean) : undefined, subject, text: body, inReplyTo: replyTo?.messageId, references: replyTo?.messageId ? [replyTo.messageId] : undefined, attachments: attachments.length > 0 ? attachments : undefined, contractId, }), onSuccess: () => { onSuccess?.(); handleClose(); }, onError: (err) => { setError(err instanceof Error ? err.message : 'Fehler beim Senden'); }, }); const handleClose = () => { // Formular wird beim nächsten Öffnen durch useEffect initialisiert onClose(); }; const handleSend = () => { if (!to.trim()) { setError('Bitte Empfänger angeben'); return; } if (!subject.trim()) { setError('Bitte Betreff angeben'); return; } setError(null); sendMutation.mutate(); }; return (
{/* From */}
{account.email}
{/* To */}
setTo(e.target.value)} placeholder="empfaenger@example.com" className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" />

Mehrere Empfänger mit Komma trennen

{/* CC */}
setCc(e.target.value)} placeholder="cc@example.com" className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" />
{/* Subject */}
setSubject(e.target.value)} placeholder="Betreff eingeben" className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" />
{/* Body */}