courrier preview

This commit is contained in:
alma 2025-04-30 23:18:20 +02:00
parent f9e0b323ce
commit 3584f72bf5

View File

@ -12,10 +12,7 @@ import { Label } from '@/components/ui/label';
// Import sub-components // Import sub-components
import ComposeEmailHeader from './ComposeEmailHeader'; import ComposeEmailHeader from './ComposeEmailHeader';
import ComposeEmailForm from './ComposeEmailForm';
import ComposeEmailFooter from './ComposeEmailFooter';
import RichEmailEditor from './RichEmailEditor'; import RichEmailEditor from './RichEmailEditor';
import QuotedEmailContent from './QuotedEmailContent';
// Import from the centralized utils // Import from the centralized utils
import { import {
@ -33,9 +30,6 @@ import { EmailMessage, EmailAddress } from '@/types/email';
* *
* All code that needs to compose emails should import this component from: * All code that needs to compose emails should import this component from:
* @/components/email/ComposeEmail * @/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 // Define interface for the modern props
@ -57,12 +51,6 @@ interface ComposeEmailProps {
}) => Promise<void>; }) => Promise<void>;
} }
// Add a helper to fix table widths in HTML
function fixTableWidths(html: string): string {
if (!html) return html;
return html.replace(/<table(?![^>]*width)/g, '<table width="100%"');
}
export default function ComposeEmail(props: ComposeEmailProps) { export default function ComposeEmail(props: ComposeEmailProps) {
const { initialEmail, type = 'new', onClose, onSend } = props; const { initialEmail, type = 'new', onClose, onSend } = props;
@ -72,6 +60,7 @@ export default function ComposeEmail(props: ComposeEmailProps) {
const [bcc, setBcc] = useState<string>(''); const [bcc, setBcc] = useState<string>('');
const [subject, setSubject] = useState<string>(''); const [subject, setSubject] = useState<string>('');
const [emailContent, setEmailContent] = useState<string>(''); const [emailContent, setEmailContent] = useState<string>('');
const [quotedContent, setQuotedContent] = useState<string>('');
const [showCc, setShowCc] = useState<boolean>(false); const [showCc, setShowCc] = useState<boolean>(false);
const [showBcc, setShowBcc] = useState<boolean>(false); const [showBcc, setShowBcc] = useState<boolean>(false);
const [sending, setSending] = useState<boolean>(false); const [sending, setSending] = useState<boolean>(false);
@ -81,6 +70,8 @@ export default function ComposeEmail(props: ComposeEmailProps) {
type: string; type: string;
}>>([]); }>>([]);
const editorRef = useRef<HTMLDivElement>(null);
// Initialize the form when replying to or forwarding an email // Initialize the form when replying to or forwarding an email
useEffect(() => { useEffect(() => {
if (initialEmail && type !== 'new') { if (initialEmail && type !== 'new') {
@ -100,8 +91,11 @@ export default function ComposeEmail(props: ComposeEmailProps) {
// Set subject // Set subject
setSubject(formatted.subject); setSubject(formatted.subject);
// Set the email content (use HTML if available) // Set the quoted content (original email)
setEmailContent(formatted.content.html || formatted.content.text); setQuotedContent(formatted.content.html || formatted.content.text);
// Start with empty content for the reply
setEmailContent('');
} }
else if (type === 'forward') { else if (type === 'forward') {
// Get formatted data for forward // Get formatted data for forward
@ -110,8 +104,11 @@ export default function ComposeEmail(props: ComposeEmailProps) {
// Set subject // Set subject
setSubject(formatted.subject); setSubject(formatted.subject);
// Set the email content (use HTML if available) // Set the quoted content (original email)
setEmailContent(formatted.content.html || formatted.content.text); setQuotedContent(formatted.content.html || formatted.content.text);
// Start with empty content for the forward
setEmailContent('');
// If the original email has attachments, we should include them // If the original email has attachments, we should include them
if (initialEmail.attachments && initialEmail.attachments.length > 0) { if (initialEmail.attachments && initialEmail.attachments.length > 0) {
@ -154,12 +151,17 @@ export default function ComposeEmail(props: ComposeEmailProps) {
setSending(true); setSending(true);
try { try {
// Combine the new content with the quoted content
const fullContent = type !== 'new'
? `${emailContent}<div class="quoted-content">${quotedContent}</div>`
: emailContent;
await onSend({ await onSend({
to, to,
cc: cc || undefined, cc: cc || undefined,
bcc: bcc || undefined, bcc: bcc || undefined,
subject, subject,
body: emailContent, body: fullContent,
attachments attachments
}); });
@ -173,34 +175,26 @@ export default function ComposeEmail(props: ComposeEmailProps) {
} }
}; };
// Additional effect to ensure we scroll to the top and focus the editor // Focus and scroll to top when opened
useEffect(() => { 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(() => { setTimeout(() => {
// Focus the editor if (editorRef.current) {
editorContainer.focus(); editorRef.current.focus();
// Make sure all scroll containers are at the top // Scroll to top
editorContainer.scrollTop = 0; const scrollElements = [
editorRef.current,
// Find all possible scrollable parent containers document.querySelector('.overflow-y-auto'),
const scrollContainers = [ document.querySelector('.compose-email-body')
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 scrollElements.forEach(el => {
scrollContainers.forEach(container => { if (el instanceof HTMLElement) {
if (container) { el.scrollTop = 0;
container.scrollTop = 0;
} }
}); });
}, 100);
} }
}, 100);
}, []); }, []);
return ( return (
@ -209,7 +203,7 @@ export default function ComposeEmail(props: ComposeEmailProps) {
type={type} type={type}
onClose={onClose} onClose={onClose}
/> />
<div className="flex-1 overflow-y-auto p-4"> <div className="flex-1 overflow-y-auto p-4 compose-email-body">
<div className="h-full flex flex-col p-4 space-y-2 overflow-y-auto"> <div className="h-full flex flex-col p-4 space-y-2 overflow-y-auto">
{/* To Field */} {/* To Field */}
<div className="flex-none"> <div className="flex-none">
@ -281,17 +275,30 @@ export default function ComposeEmail(props: ComposeEmailProps) {
/> />
</div> </div>
{/* Message Body */} {/* Message Body - Simplified Editor */}
<div className="flex-1 min-h-[200px] flex flex-col overflow-hidden"> <div className="flex-1 min-h-[200px] flex flex-col overflow-hidden">
<Label htmlFor="message" className="flex-none block text-sm font-medium text-gray-700 mb-2">Message</Label> <Label htmlFor="message" className="flex-none block text-sm font-medium text-gray-700 mb-2">Message</Label>
<div className="flex-1 border border-gray-300 rounded-md overflow-hidden"> <div className="flex-1 border border-gray-300 rounded-md overflow-hidden">
<RichEmailEditor <div className="email-editor-container flex flex-col h-full">
initialContent={emailContent} {/* Simple editor for new content */}
onChange={setEmailContent} <div
minHeight="200px" ref={editorRef}
maxHeight="none" className="simple-editor p-3 min-h-[100px] outline-none"
preserveFormatting={true} contentEditable={true}
dangerouslySetInnerHTML={{ __html: emailContent }}
onInput={(e) => setEmailContent(e.currentTarget.innerHTML)}
/> />
{/* Quoted content from original email */}
{quotedContent && (
<div className="quoted-email-content p-3 border-t border-gray-200 bg-gray-50">
<div
className="email-quoted-content"
dangerouslySetInnerHTML={{ __html: quotedContent }}
/>
</div>
)}
</div>
</div> </div>
</div> </div>
@ -376,6 +383,56 @@ export default function ComposeEmail(props: ComposeEmailProps) {
</Button> </Button>
</div> </div>
</div> </div>
{/* Styles for email display */}
<style jsx global>{`
.simple-editor {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
font-size: 14px;
line-height: 1.6;
width: 100%;
flex: 1;
}
.email-quoted-content {
color: #505050;
font-size: 13px;
line-height: 1.5;
border-left: 2px solid #ddd;
padding-left: 10px;
margin-top: 5px;
}
.email-quoted-content blockquote {
margin: 5px 0;
padding-left: 10px;
border-left: 2px solid #ddd;
}
.email-quoted-content img {
max-width: 100%;
height: auto;
}
.email-quoted-content table {
border-collapse: collapse;
width: 100%;
max-width: 100%;
margin-bottom: 1rem;
}
.email-quoted-content th,
.email-quoted-content td {
padding: 5px;
border: 1px solid #ddd;
}
.email-quoted-content th {
background-color: #f8f9fa;
font-weight: 600;
text-align: left;
}
`}</style>
</div> </div>
); );
} }