import { useState, useEffect } from 'react'; import { Link } from 'react-router-dom'; import { Mail, MailOpen, Star, Paperclip, Plus, X, ChevronRight, Inbox, Send, RefreshCw, Trash2, ExternalLink } from 'lucide-react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { cachedEmailApi, stressfreiEmailApi, CachedEmail } from '../../services/api'; import { useAuth } from '../../context/AuthContext'; import Button from '../ui/Button'; import Card from '../ui/Card'; import EmailDetail from './EmailDetail'; import ComposeEmailModal from './ComposeEmailModal'; import TrashEmailList from './TrashEmailList'; import toast from 'react-hot-toast'; type EmailFolder = 'INBOX' | 'SENT' | 'TRASH'; interface ContractEmailsSectionProps { contractId: number; customerId: number; } export default function ContractEmailsSection({ contractId, customerId, }: ContractEmailsSectionProps) { const [selectedAccountId, setSelectedAccountId] = useState(null); const [selectedFolder, setSelectedFolder] = useState('INBOX'); const [selectedEmail, setSelectedEmail] = useState(null); const [showCompose, setShowCompose] = useState(false); const [replyToEmail, setReplyToEmail] = useState(null); const [deleteConfirmId, setDeleteConfirmId] = useState(null); const queryClient = useQueryClient(); const { hasPermission } = useAuth(); const canAccessTrash = hasPermission('emails:delete'); // Mailbox-Konten laden const { data: accountsData, isLoading: accountsLoading } = useQuery({ queryKey: ['mailbox-accounts', customerId], queryFn: () => cachedEmailApi.getMailboxAccounts(customerId), }); const accounts = accountsData?.data || []; // Erstes Konto automatisch auswählen useEffect(() => { if (accounts.length > 0 && !selectedAccountId) { setSelectedAccountId(accounts[0].id); } }, [accounts, selectedAccountId]); const selectedAccount = accounts.find((a) => a.id === selectedAccountId); // E-Mails für den Vertrag laden (nach Ordner gefiltert, nicht für TRASH) const { data: emailsData, isLoading, refetch: refetchEmails } = useQuery({ queryKey: ['emails', 'contract', contractId, selectedFolder], queryFn: () => cachedEmailApi.getForContract(contractId, { folder: selectedFolder as 'INBOX' | 'SENT' }), enabled: selectedFolder !== 'TRASH', }); const emails = emailsData?.data || []; // Papierkorb-E-Mails laden (für den ganzen Kunden, da Trash nicht vertragsgebunden) const { data: trashData, isLoading: trashLoading } = useQuery({ queryKey: ['emails', 'trash', customerId], queryFn: () => cachedEmailApi.getTrash(customerId), enabled: selectedFolder === 'TRASH' && canAccessTrash, }); const trashEmails = trashData?.data || []; // Ordner-Anzahlen für Badges (Vertrag) const { data: folderCountsData } = useQuery({ queryKey: ['contract-folder-counts', contractId], queryFn: () => cachedEmailApi.getContractFolderCounts(contractId), }); const folderCounts = folderCountsData?.data || { inbox: 0, inboxUnread: 0, sent: 0, sentUnread: 0, }; // Ordner-Anzahlen für das Konto (für Trash-Badge) const { data: accountFolderCountsData } = useQuery({ queryKey: ['folder-counts', selectedAccountId], queryFn: () => stressfreiEmailApi.getFolderCounts(selectedAccountId!), enabled: !!selectedAccountId && canAccessTrash, }); const accountFolderCounts = accountFolderCountsData?.data || { trash: 0, trashUnread: 0, }; // Einzelne E-Mail laden (mit Body) const { data: emailDetailData } = useQuery({ queryKey: ['email', selectedEmail?.id], queryFn: () => cachedEmailApi.getById(selectedEmail!.id), enabled: !!selectedEmail?.id, }); const emailDetail = emailDetailData?.data || selectedEmail; // Synchronisation const syncMutation = useMutation({ mutationFn: (accountId: number) => stressfreiEmailApi.syncEmails(accountId), onSuccess: () => { // E-Mail-Listen neu laden queryClient.invalidateQueries({ queryKey: ['emails'] }); // Ordner-Anzahlen aktualisieren queryClient.invalidateQueries({ queryKey: ['contract-folder-counts', contractId] }); if (selectedAccountId) { queryClient.invalidateQueries({ queryKey: ['folder-counts', selectedAccountId] }); } toast.success('Synchronisation abgeschlossen'); }, onError: (error: Error) => { toast.error(error.message || 'Synchronisation fehlgeschlagen'); }, }); // Stern umschalten const toggleStarMutation = useMutation({ mutationFn: (emailId: number) => cachedEmailApi.toggleStar(emailId), onSuccess: (_data, emailId) => { queryClient.invalidateQueries({ queryKey: ['emails', 'contract', contractId] }); queryClient.invalidateQueries({ queryKey: ['email', emailId] }); }, }); // Als gelesen/ungelesen markieren const toggleReadMutation = useMutation({ mutationFn: ({ emailId, isRead }: { emailId: number; isRead: boolean }) => cachedEmailApi.markAsRead(emailId, isRead), onSuccess: (_data, variables) => { queryClient.invalidateQueries({ queryKey: ['emails', 'contract', contractId] }); queryClient.invalidateQueries({ queryKey: ['email', variables.emailId] }); // Folder-Counts aktualisieren für Badge-Update queryClient.invalidateQueries({ queryKey: ['contract-folder-counts', contractId] }); if (selectedAccountId) { queryClient.invalidateQueries({ queryKey: ['folder-counts', selectedAccountId] }); } }, }); // Zuordnung aufheben const unassignMutation = useMutation({ mutationFn: (emailId: number) => cachedEmailApi.unassignFromContract(emailId), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['emails', 'contract', contractId] }); queryClient.invalidateQueries({ queryKey: ['contract-folder-counts', contractId] }); setSelectedEmail(null); toast.success('Zuordnung aufgehoben'); }, onError: (error: Error) => { toast.error(error.message || 'Fehler beim Aufheben der Zuordnung'); }, }); // E-Mail löschen (in Papierkorb) const deleteMutation = useMutation({ mutationFn: (emailId: number) => cachedEmailApi.delete(emailId), onSuccess: (_data, emailId) => { queryClient.invalidateQueries({ queryKey: ['emails'] }); queryClient.invalidateQueries({ queryKey: ['contract-folder-counts', contractId] }); if (selectedAccountId) { queryClient.invalidateQueries({ queryKey: ['folder-counts', selectedAccountId] }); } toast.success('E-Mail in Papierkorb verschoben'); setDeleteConfirmId(null); if (selectedEmail?.id === emailId) { setSelectedEmail(null); } }, onError: (error: Error) => { toast.error(error.message || 'Fehler beim Löschen der E-Mail'); setDeleteConfirmId(null); }, }); const handleSync = () => { if (selectedAccountId) { syncMutation.mutate(selectedAccountId); } }; const formatDate = (dateStr: string) => { const date = new Date(dateStr); const now = new Date(); const isToday = date.toDateString() === now.toDateString(); if (isToday) { return date.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' }); } return date.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit' }); }; const handleStarClick = (e: React.MouseEvent, emailId: number) => { e.stopPropagation(); toggleStarMutation.mutate(emailId); }; const handleReadToggle = (e: React.MouseEvent, email: CachedEmail) => { e.stopPropagation(); toggleReadMutation.mutate({ emailId: email.id, isRead: !email.isRead }); }; const handleSelectEmail = (email: CachedEmail) => { // E-Mail als gelesen markieren wenn noch nicht gelesen if (!email.isRead) { toggleReadMutation.mutate({ emailId: email.id, isRead: true }); } setSelectedEmail(email); }; const handleReply = () => { setReplyToEmail(emailDetail || null); setShowCompose(true); }; const handleNewEmail = () => { setReplyToEmail(null); setShowCompose(true); }; const handleUnassign = (e: React.MouseEvent, emailId: number) => { e.stopPropagation(); if (selectedEmail?.id === emailId) { setSelectedEmail(null); } unassignMutation.mutate(emailId); }; const handleDeleteClick = (e: React.MouseEvent, emailId: number) => { e.stopPropagation(); setDeleteConfirmId(emailId); }; const handleDeleteConfirm = (e: React.MouseEvent) => { e.stopPropagation(); if (deleteConfirmId) { deleteMutation.mutate(deleteConfirmId); } }; const handleDeleteCancel = (e: React.MouseEvent) => { e.stopPropagation(); setDeleteConfirmId(null); }; const handleFolderChange = (folder: EmailFolder) => { setSelectedFolder(folder); setSelectedEmail(null); }; // Für gesendete E-Mails: Empfänger extrahieren const getDisplayName = (email: CachedEmail) => { if (selectedFolder === 'SENT') { try { const toAddresses = JSON.parse(email.toAddresses); if (toAddresses.length > 0) { return `An: ${toAddresses[0]}${toAddresses.length > 1 ? ` (+${toAddresses.length - 1})` : ''}`; } } catch { return 'An: (Unbekannt)'; } } return email.fromName || email.fromAddress; }; // Keine Mailbox-Konten vorhanden if (!accountsLoading && accounts.length === 0) { return (

Keine E-Mail-Konten vorhanden

Erstellen Sie eine E-Mail-Adresse beim Kunden mit aktivierter Mailbox

); } return ( E-Mails Postfach öffnen Stressfrei wechseln Adressen } actions={
{selectedFolder !== 'TRASH' && ( )} {selectedAccount && ( )}
} > {/* Header mit Account-Auswahl und Ordner-Tabs */}
{/* Account Selector */} {accounts.length > 1 ? (
) : (
{selectedAccount?.email}
)} {/* Folder Tabs */}
{canAccessTrash && ( )}
{/* Content */} {(selectedFolder === 'TRASH' ? trashLoading : isLoading) ? (
) : (selectedFolder === 'TRASH' ? trashEmails.length === 0 : emails.length === 0) ? (

{selectedFolder === 'INBOX' ? 'Keine E-Mails zugeordnet' : selectedFolder === 'SENT' ? 'Keine E-Mails über diesen Vertrag gesendet' : 'Papierkorb ist leer'}

{selectedFolder === 'INBOX' && (

E-Mails können im E-Mail-Tab des Kunden zugeordnet werden

)}
) : (
{/* Email List */}
{selectedFolder === 'TRASH' ? ( { if (selectedEmail?.id === emailId) { setSelectedEmail(null); } // Trash und normale E-Mails neu laden + Folder-Counts aktualisieren queryClient.invalidateQueries({ queryKey: ['emails'] }); queryClient.invalidateQueries({ queryKey: ['folder-counts', selectedAccountId] }); queryClient.invalidateQueries({ queryKey: ['contract-folder-counts', contractId] }); }} onEmailDeleted={(emailId) => { if (selectedEmail?.id === emailId) { setSelectedEmail(null); } queryClient.invalidateQueries({ queryKey: ['emails', 'trash'] }); queryClient.invalidateQueries({ queryKey: ['folder-counts', selectedAccountId] }); }} isLoading={trashLoading} /> ) : (
{emails.map((email) => (
handleSelectEmail(email)} className={[ 'flex items-start gap-2 p-3 cursor-pointer transition-colors', selectedEmail?.id === email.id ? 'bg-blue-100' : ['hover:bg-gray-100', !email.isRead ? 'bg-white' : 'bg-gray-50/50'].join(' ') ].join(' ')} style={{ borderLeft: selectedEmail?.id === email.id ? '4px solid #2563eb' : '4px solid transparent' }} > {/* Read Status */} {/* Star */} {/* Delete Button (nur mit Permission) */} {hasPermission('emails:delete') && ( )} {/* Email Content */}
{/* From/To & Date */}
{getDisplayName(email)} {formatDate(email.receivedAt)}
{/* Subject */}
{email.subject || '(Kein Betreff)'} {email.hasAttachments && ( )}
{/* Contract Badge */} {email.contract && (
{email.contract.contractNumber} {/* X-Button nur für INBOX oder manuell zugeordnete gesendete E-Mails */} {(selectedFolder === 'INBOX' || (selectedFolder === 'SENT' && !email.isAutoAssigned)) && ( )}
)}
{/* Chevron */}
))}
)}
{/* Email Detail */}
{emailDetail && selectedEmail ? ( {}} onDeleted={() => { setSelectedEmail(null); queryClient.invalidateQueries({ queryKey: ['emails'] }); queryClient.invalidateQueries({ queryKey: ['contract-folder-counts', contractId] }); if (selectedAccountId) { queryClient.invalidateQueries({ queryKey: ['folder-counts', selectedAccountId] }); } }} isSentFolder={selectedFolder === 'SENT'} isContractView={selectedFolder !== 'TRASH'} isTrashView={selectedFolder === 'TRASH'} onRestored={() => { setSelectedEmail(null); queryClient.invalidateQueries({ queryKey: ['emails'] }); queryClient.invalidateQueries({ queryKey: ['contract-folder-counts', contractId] }); if (selectedAccountId) { queryClient.invalidateQueries({ queryKey: ['folder-counts', selectedAccountId] }); } }} accountId={emailDetail?.stressfreiEmailId} /> ) : (

Wählen Sie eine E-Mail aus

)}
)} {/* Lösch-Bestätigung Modal */} {deleteConfirmId && (

E-Mail löschen?

Die E-Mail wird in den Papierkorb verschoben.

)} {/* Compose Modal */} {selectedAccount && ( { setShowCompose(false); setReplyToEmail(null); }} account={selectedAccount} replyTo={replyToEmail || undefined} contractId={contractId} onSuccess={() => { // Gesendete E-Mails im Vertrag aktualisieren queryClient.invalidateQueries({ queryKey: ['emails', 'contract', contractId, 'SENT'] }); // Folder-Counts aktualisieren queryClient.invalidateQueries({ queryKey: ['contract-folder-counts', contractId] }); // Falls wir im Gesendet-Ordner sind, Liste neu laden if (selectedFolder === 'SENT') { refetchEmails(); } }} /> )}
); }