courrier preview

This commit is contained in:
alma 2025-05-01 11:20:15 +02:00
parent c4958d7e4c
commit 7f87e4f00a
2 changed files with 82 additions and 112 deletions

View File

@ -74,6 +74,9 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
// Add any custom toolbar handlers here // Add any custom toolbar handlers here
} }
}, },
clipboard: {
matchVisual: false // Disable clipboard matching for better HTML handling
},
// Don't initialize better-table yet - we'll do it after content is loaded // Don't initialize better-table yet - we'll do it after content is loaded
'better-table': false, 'better-table': false,
}, },
@ -93,68 +96,54 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
direction direction
}); });
// Make sure content is properly sanitized before injecting it // Simplify complex email content to something Quill can handle better
const cleanContent = sanitizeHtml(processedContent || initialContent); const sanitizedContent = sanitizeHtml(processedContent || initialContent);
// First, directly set the content // Use direct innerHTML setting for the initial content
if (editorRef.current) { quillRef.current.root.innerHTML = sanitizedContent;
editorRef.current.innerHTML = cleanContent;
// Set the direction for the content
quillRef.current.format('direction', direction);
if (direction === 'rtl') {
quillRef.current.format('align', 'right');
} }
// Then let Quill parse and format it correctly // Set cursor at the beginning
setTimeout(() => { quillRef.current.setSelection(0, 0);
// Only proceed if editor ref is still available
if (!editorRef.current) return;
// Get the content from the editor element // Ensure the cursor and scroll position is at the top of the editor
const content = editorRef.current.innerHTML; if (editorRef.current) {
editorRef.current.scrollTop = 0;
// Clear the editor // Find and scroll parent containers that might have scroll
quillRef.current.setText(''); const scrollable = [
editorRef.current.closest('.ql-container'),
editorRef.current.closest('.rich-email-editor-container'),
editorRef.current.closest('.overflow-y-auto'),
document.querySelector('.overflow-y-auto')
];
// Insert clean content scrollable.forEach(el => {
quillRef.current.clipboard.dangerouslyPasteHTML(0, content); if (el instanceof HTMLElement) {
el.scrollTop = 0;
// Set the direction for the content }
quillRef.current.format('direction', direction); });
if (direction === 'rtl') { }
quillRef.current.format('align', 'right');
}
// Set cursor at the beginning (before the quoted content)
quillRef.current.setSelection(0, 0);
// Ensure the cursor and scroll position is at the top of the editor
if (editorRef.current) {
editorRef.current.scrollTop = 0;
// Find and scroll parent containers that might have scroll
const scrollable = [
editorRef.current.closest('.ql-container'),
editorRef.current.closest('.rich-email-editor-container'),
editorRef.current.closest('.overflow-y-auto'),
document.querySelector('.overflow-y-auto')
];
scrollable.forEach(el => {
if (el instanceof HTMLElement) {
el.scrollTop = 0;
}
});
}
}, 100);
} catch (err) { } catch (err) {
console.error('Error setting initial content:', err); console.error('Error setting initial content:', err);
// Fallback: just set text // Fallback: just set text
quillRef.current.setText(''); quillRef.current.setText('');
// Try simplest approach // Extract text as a last resort
try { try {
quillRef.current.clipboard.dangerouslyPasteHTML(initialContent); // Create a temporary div to extract text from HTML
const tempDiv = document.createElement('div');
tempDiv.innerHTML = initialContent;
const textContent = tempDiv.textContent || tempDiv.innerText || '';
quillRef.current.setText(textContent);
} catch (e) { } catch (e) {
console.error('Fallback failed too:', e); console.error('Fallback failed too:', e);
// Last resort: strip all HTML quillRef.current.setText('');
quillRef.current.setText(initialContent.replace(/<[^>]*>/g, ''));
} }
} }
} }
@ -187,24 +176,22 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
}; };
}, []); }, []);
// Update content from props if changed externally // Update content from props if changed externally - using a simpler approach
useEffect(() => { useEffect(() => {
if (quillRef.current && isReady) { if (quillRef.current && isReady && initialContent) {
const currentContent = quillRef.current.root.innerHTML; const currentContent = quillRef.current.root.innerHTML;
// Only update if content changed to avoid editor position reset // Only update if content changed to avoid editor position reset
if (initialContent !== currentContent) { if (initialContent !== currentContent) {
try { try {
// Preserve cursor position if possible
const selection = quillRef.current.getSelection();
// Process content to ensure correct direction // Process content to ensure correct direction
const { direction, html: processedContent } = processContentWithDirection(initialContent); const { direction, html: processedContent } = processContentWithDirection(initialContent);
// First clear the content // Sanitize the HTML
quillRef.current.root.innerHTML = ''; const sanitizedContent = sanitizeHtml(processedContent || initialContent);
// Then insert the new content at position 0 // SIMPLIFIED: Set content directly to the root element rather than using clipboard
quillRef.current.clipboard.dangerouslyPasteHTML(0, sanitizeHtml(processedContent || initialContent)); quillRef.current.root.innerHTML = sanitizedContent;
// Set the direction for the content // Set the direction for the content
quillRef.current.format('direction', direction); quillRef.current.format('direction', direction);
@ -215,14 +202,20 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
// Force update // Force update
quillRef.current.update(); quillRef.current.update();
// Restore selection if possible // Set selection to beginning
if (selection) { quillRef.current.setSelection(0, 0);
setTimeout(() => quillRef.current.setSelection(selection), 10);
}
} catch (err) { } catch (err) {
console.error('Error updating content:', err); console.error('Error updating content:', err);
// Fallback update method // Safer fallback that avoids clipboard API
quillRef.current.clipboard.dangerouslyPasteHTML(sanitizeHtml(initialContent)); try {
// Extract basic text if everything else fails
const tempDiv = document.createElement('div');
tempDiv.innerHTML = initialContent;
const textContent = tempDiv.textContent || tempDiv.innerText || '';
quillRef.current.setText(textContent);
} catch (e) {
console.error('All fallbacks failed:', e);
}
} }
} }
} }
@ -399,36 +392,13 @@ const RichEmailEditor: React.FC<RichEmailEditorProps> = ({
font-size: 13px !important; font-size: 13px !important;
} }
/* Status styles for email displays */ /* Email quote styling */
:global(.ql-editor td[class*="status"]), :global(.email-original-content) {
:global(.ql-editor td[class*="Status"]) { margin-top: 20px !important;
background-color: #f8f9fa !important; padding-top: 10px !important;
font-weight: 500 !important; border-top: 1px solid #ddd !important;
} color: #555 !important;
font-size: 13px !important;
/* Amount styles */
:global(.ql-editor td[class*="amount"]),
:global(.ql-editor td[class*="Amount"]),
:global(.ql-editor td[class*="price"]),
:global(.ql-editor td[class*="Price"]) {
text-align: right !important;
font-family: monospace !important;
}
/* Header row styles */
:global(.ql-editor tr:first-child td),
:global(.ql-editor th) {
background-color: #f8f9fa !important;
font-weight: 600 !important;
}
/* Improve table cells with specific content */
:global(.ql-editor td:has(div[class*="number"])),
:global(.ql-editor td:has(div[class*="Number"])),
:global(.ql-editor td:has(div[class*="invoice"])),
:global(.ql-editor td:has(div[class*="Invoice"])) {
font-family: monospace !important;
letter-spacing: 0.5px !important;
} }
/* Fix quoted paragraphs */ /* Fix quoted paragraphs */

View File

@ -279,14 +279,15 @@ export function formatReplyEmail(originalEmail: EmailMessage | LegacyEmailMessag
// Extract content using centralized utility // Extract content using centralized utility
const { text: originalTextContent, html: originalHtmlContent } = extractEmailContent(originalEmail); const { text: originalTextContent, html: originalHtmlContent } = extractEmailContent(originalEmail);
// Create content with appropriate quote formatting // Create a simpler HTML structure that's easier for Quill to handle
const replyBody = ` const replyBody = `
<br/> <br/>
<br/> <div class="email-original-content">
<blockquote style="border-left: 2px solid #ddd; padding-left: 10px; margin: 10px 0; color: #505050;"> <div>On ${dateStr}, ${fromStr} wrote:</div>
<p>On ${dateStr}, ${fromStr} wrote:</p> <blockquote style="margin: 10px 0; padding-left: 10px; border-left: 2px solid #ddd; color: #505050;">
${originalHtmlContent || originalTextContent.replace(/\n/g, '<br>')} ${originalTextContent.replace(/\n/g, '<br>')}
</blockquote> </blockquote>
</div>
`; `;
// Process the content with proper direction // Process the content with proper direction
@ -334,20 +335,19 @@ export function formatForwardedEmail(originalEmail: EmailMessage | LegacyEmailMe
// Extract content using centralized utility // Extract content using centralized utility
const { text: originalTextContent, html: originalHtmlContent } = extractEmailContent(originalEmail); const { text: originalTextContent, html: originalHtmlContent } = extractEmailContent(originalEmail);
// Create forwarded content with header information // Create a simpler forwarded content structure for better Quill compatibility
const forwardBody = ` const forwardBody = `
<br/> <br/>
<br/> <div class="email-original-content">
<div class="email-forwarded-content"> <div>---------- Forwarded message ---------</div>
<p>---------- Forwarded message ---------</p> <div><strong>From:</strong> ${fromStr}</div>
<p><strong>From:</strong> ${fromStr}</p> <div><strong>Date:</strong> ${dateStr}</div>
<p><strong>Date:</strong> ${dateStr}</p> <div><strong>Subject:</strong> ${subject || ''}</div>
<p><strong>Subject:</strong> ${subject || ''}</p> <div><strong>To:</strong> ${toStr}</div>
<p><strong>To:</strong> ${toStr}</p> ${ccStr ? `<div><strong>Cc:</strong> ${ccStr}</div>` : ''}
${ccStr ? `<p><strong>Cc:</strong> ${ccStr}</p>` : ''} <blockquote style="margin-top: 10px; padding-left: 10px; border-left: 2px solid #ddd; color: #505050;">
<div style="margin-top: 15px; border-top: 1px solid #eee; padding-top: 15px;"> ${originalTextContent.replace(/\n/g, '<br>')}
${originalHtmlContent || originalTextContent.replace(/\n/g, '<br>')} </blockquote>
</div>
</div> </div>
`; `;