'use client'; import { useState, useRef, useEffect } from 'react'; import { X, Paperclip, ChevronDown, ChevronUp, SendHorizontal, Loader2 } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; import DOMPurify from 'isomorphic-dompurify'; import { Label } from '@/components/ui/label'; // Import sub-components import ComposeEmailHeader from './ComposeEmailHeader'; import ComposeEmailForm from './ComposeEmailForm'; import ComposeEmailFooter from './ComposeEmailFooter'; import RichEmailEditor from './RichEmailEditor'; import QuotedEmailContent from './QuotedEmailContent'; // Import from the centralized utils import { formatReplyEmail, formatForwardedEmail, formatEmailAddresses } from '@/lib/utils/email-utils'; import { EmailMessage, EmailAddress } from '@/types/email'; /** * CENTRAL EMAIL COMPOSER COMPONENT * * This is the unified, centralized email composer component used throughout the application. * It handles new emails, replies, and forwards with proper text direction. * * All code that needs to compose emails should import this component from: * @/components/email/ComposeEmail * * It uses the centralized email formatter from @/lib/utils/email-formatter.ts * for consistent handling of email content and text direction. */ // Define interface for the modern props interface ComposeEmailProps { initialEmail?: EmailMessage | null; type?: 'new' | 'reply' | 'reply-all' | 'forward'; onClose: () => void; onSend: (emailData: { to: string; cc?: string; bcc?: string; subject: string; body: string; attachments?: Array<{ name: string; content: string; type: string; }>; }) => Promise; } // Add a helper to fix table widths in HTML function fixTableWidths(html: string): string { if (!html) return html; return html.replace(/]*width)/g, '(''); const [cc, setCc] = useState(''); const [bcc, setBcc] = useState(''); const [subject, setSubject] = useState(''); const [emailContent, setEmailContent] = useState(''); const [showCc, setShowCc] = useState(false); const [showBcc, setShowBcc] = useState(false); const [sending, setSending] = useState(false); const [attachments, setAttachments] = useState>([]); // Initialize the form when replying to or forwarding an email useEffect(() => { if (initialEmail && type !== 'new') { try { // Set recipients based on type if (type === 'reply' || type === 'reply-all') { // Get formatted data for reply const formatted = formatReplyEmail(initialEmail, type); // Set the recipients setTo(formatted.to); if (formatted.cc) { setCc(formatted.cc); setShowCc(true); } // Set subject setSubject(formatted.subject); // Set the email content (use HTML if available) setEmailContent(formatted.content.html || formatted.content.text); } else if (type === 'forward') { // Get formatted data for forward const formatted = formatForwardedEmail(initialEmail); // Set subject setSubject(formatted.subject); // Set the email content (use HTML if available) setEmailContent(formatted.content.html || formatted.content.text); // If the original email has attachments, we should include them if (initialEmail.attachments && initialEmail.attachments.length > 0) { const formattedAttachments = initialEmail.attachments.map(att => ({ name: att.filename || 'attachment', type: att.contentType || 'application/octet-stream', content: att.content || '' })); setAttachments(formattedAttachments); } } } catch (error) { console.error('Error initializing compose form:', error); } } }, [initialEmail, type]); // Handle file attachments const handleAttachmentAdd = async (files: FileList) => { const newAttachments = Array.from(files).map(file => ({ name: file.name, type: file.type, content: URL.createObjectURL(file) })); setAttachments(prev => [...prev, ...newAttachments]); }; const handleAttachmentRemove = (index: number) => { setAttachments(prev => prev.filter((_, i) => i !== index)); }; // Handle sending email const handleSend = async () => { if (!to) { alert('Please specify at least one recipient'); return; } setSending(true); try { await onSend({ to, cc: cc || undefined, bcc: bcc || undefined, subject, body: emailContent, attachments }); // Reset form and close onClose(); } catch (error) { console.error('Error sending email:', error); alert('Failed to send email. Please try again.'); } finally { setSending(false); } }; // Additional effect to ensure we scroll to the top and focus the editor useEffect(() => { // Focus the editor and ensure it's scrolled to the top const editorContainer = document.querySelector('.ql-editor') as HTMLElement; if (editorContainer) { // Set timeout to ensure DOM is fully rendered setTimeout(() => { // Focus the editor editorContainer.focus(); // Make sure all scroll containers are at the top editorContainer.scrollTop = 0; // Find all possible scrollable parent containers const scrollContainers = [ document.querySelector('.ql-container') as HTMLElement, document.querySelector('.rich-email-editor-container') as HTMLElement, document.querySelector('.h-full.flex.flex-col.p-6') as HTMLElement ]; // Scroll all containers to top scrollContainers.forEach(container => { if (container) { container.scrollTop = 0; } }); }, 100); } }, []); return (
{/* Improve scrolling by wrapping form in a ScrollArea */}
{/* Email fields section - fixed size */}
{/* To Field */}
setTo(e.target.value)} placeholder="recipient@example.com" className="w-full mt-1 bg-white border-gray-300 text-gray-900" />
{/* CC/BCC Toggle Buttons */}
{/* CC Field */} {showCc && (
setCc(e.target.value)} placeholder="cc@example.com" className="w-full mt-1 bg-white border-gray-300 text-gray-900" />
)} {/* BCC Field */} {showBcc && (
setBcc(e.target.value)} placeholder="bcc@example.com" className="w-full mt-1 bg-white border-gray-300 text-gray-900" />
)} {/* Subject Field */}
setSubject(e.target.value)} placeholder="Enter subject" className="w-full mt-1 bg-white border-gray-300 text-gray-900" />
{/* Message Body - with improved overflow handling */}
{/* Attachments */} {attachments.length > 0 && (

Attachments

{attachments.map((file, index) => (
{file.name}
))}
)}
{/* Modal Footer - now inside the main modal container and visually attached */}
{/* File Input for Attachments */} { if (e.target.files && e.target.files.length > 0) { handleAttachmentAdd(e.target.files); } }} /> {sending && Preparing attachment...}
{/* Add styles for better formatting */}
); }