diff --git a/components/email/RichEmailEditor.tsx b/components/email/RichEmailEditor.tsx
index e2f79b73..4297ef05 100644
--- a/components/email/RichEmailEditor.tsx
+++ b/components/email/RichEmailEditor.tsx
@@ -18,7 +18,7 @@ interface RichEmailEditorProps {
/**
* Clean up problematic table structures that cause issues with quill-better-table
*/
-function cleanupTableStructures(htmlContent: string): string {
+function cleanupTableStructures(htmlContent: string, isReplyOrForward: boolean = false): string {
if (!htmlContent) return htmlContent;
try {
@@ -40,101 +40,103 @@ function cleanupTableStructures(htmlContent: string): string {
htmlContent.includes('
0 &&
- (isForwardedEmail || isReplyEmail);
+ // For reply/forward content, force convert ALL tables to divs to avoid Quill errors
+ const shouldConvertAllTables = isReplyOrForward || isForwardedEmail || isReplyEmail;
- if (hasComplexTables) {
- console.log(`Found ${tables.length} tables in complex email content`);
+ if (tables.length > 0) {
+ console.log(`Found ${tables.length} tables in ${shouldConvertAllTables ? 'reply/forward' : 'regular'} content`);
let convertedCount = 0;
tables.forEach(table => {
- // Special handling for tables inside blockquotes (quoted content)
- if (table.closest('blockquote') ||
- (isReplyEmail && table.innerHTML.includes('wrote:'))) {
- console.log('Preserving table inside quoted content');
+ // In reply/forward mode, convert ALL tables to divs to avoid quill-better-table issues
+ if (shouldConvertAllTables) {
+ const replacementDiv = document.createElement('div');
+ replacementDiv.className = 'converted-table';
+ replacementDiv.style.border = '1px solid #ddd';
+ replacementDiv.style.margin = '10px 0';
+ replacementDiv.style.padding = '10px';
- // Add width attribute to all cells to prevent width calculation issues
- const cells = table.querySelectorAll('td, th');
- cells.forEach(cell => {
- if (cell instanceof HTMLTableCellElement) {
- if (!cell.hasAttribute('width')) {
+ // Preserve the original table structure visually
+ // Create a simplified HTML representation of the table
+ let tableHtml = '';
+
+ // Process each row
+ const rows = table.querySelectorAll('tr');
+ rows.forEach(row => {
+ const rowDiv = document.createElement('div');
+ rowDiv.style.display = 'flex';
+ rowDiv.style.flexWrap = 'wrap';
+ rowDiv.style.marginBottom = '5px';
+
+ // Process each cell in the row
+ const cells = row.querySelectorAll('td, th');
+ cells.forEach(cell => {
+ const cellDiv = document.createElement('div');
+ cellDiv.style.flex = '1';
+ cellDiv.style.padding = '5px';
+ cellDiv.style.borderBottom = '1px solid #eee';
+ cellDiv.innerHTML = cell.innerHTML;
+ rowDiv.appendChild(cellDiv);
+ });
+
+ replacementDiv.appendChild(rowDiv);
+ });
+
+ // If no rows were processed, just use the table's inner HTML
+ if (rows.length === 0) {
+ replacementDiv.innerHTML = table.innerHTML;
+ }
+
+ // Replace the table with the div
+ if (table.parentNode) {
+ table.parentNode.replaceChild(replacementDiv, table);
+ convertedCount++;
+ }
+ }
+ // For regular content, just add width attributes to make quill-better-table happy
+ else {
+ // Skip simple tables that are likely to work fine with Quill
+ // Check more conditions to identify simple tables
+ const isSimpleTable =
+ table.rows.length <= 3 &&
+ table.querySelectorAll('td, th').length <= 6 &&
+ !table.querySelector('table') && // No nested tables
+ !table.innerHTML.includes('rowspan') && // No rowspan
+ !table.innerHTML.includes('colspan'); // No colspan
+
+ if (isSimpleTable) {
+ console.log('Preserving simple table structure');
+ // Add width attribute
+ table.setAttribute('width', '100%');
+
+ // Add width to cells
+ const cells = table.querySelectorAll('td, th');
+ cells.forEach(cell => {
+ if (cell instanceof HTMLTableCellElement && !cell.hasAttribute('width')) {
cell.setAttribute('width', '100');
}
- cell.style.padding = '4px';
- cell.style.textAlign = 'left';
- cell.style.verticalAlign = 'top';
+ });
+ } else {
+ // Convert complex tables to divs
+ const replacementDiv = document.createElement('div');
+ replacementDiv.className = 'converted-table';
+ replacementDiv.style.border = '1px solid #ddd';
+ replacementDiv.style.margin = '10px 0';
+ replacementDiv.style.padding = '10px';
+
+ // Copy the table's innerHTML
+ replacementDiv.innerHTML = table.innerHTML;
+
+ // Replace the table with the div
+ if (table.parentNode) {
+ table.parentNode.replaceChild(replacementDiv, table);
+ convertedCount++;
}
- });
-
- // Apply minimal styling to ensure it renders correctly
- table.setAttribute('style', 'border-collapse: collapse; width: 100%; max-width: 100%; margin: 8px 0;');
- // Add explicit width attribute to the table
- table.setAttribute('width', '100%');
- return;
- }
-
- // Preserve the main forwarded email header table
- if (isForwardedEmail &&
- table.innerHTML.includes('From:') &&
- table.innerHTML.includes('Date:') &&
- table.innerHTML.includes('Subject:')) {
- console.log('Preserving forwarded email header table');
- // Ensure the table has proper styling
- table.setAttribute('style', 'margin: 10px 0; border-collapse: collapse; font-size: 13px; color: #333;');
- // Add explicit width attribute
- table.setAttribute('width', '100%');
-
- // Add width to cells
- const cells = table.querySelectorAll('td, th');
- cells.forEach(cell => {
- if (cell instanceof HTMLTableCellElement && !cell.hasAttribute('width')) {
- // First column (labels) narrower
- if (cell.textContent?.includes(':')) {
- cell.setAttribute('width', '80');
- } else {
- cell.setAttribute('width', '400');
- }
- }
- });
- return;
- }
-
- // Skip simple tables that are likely to work fine with Quill
- // Check more conditions to identify simple tables
- const isSimpleTable =
- table.rows.length <= 3 &&
- table.querySelectorAll('td, th').length <= 6 &&
- !table.querySelector('table') && // No nested tables
- !table.innerHTML.includes('style=') && // No complex styling
- !table.innerHTML.includes('rowspan') && // No rowspan
- !table.innerHTML.includes('colspan'); // No colspan
-
- if (isSimpleTable) {
- console.log('Preserving simple table structure');
- // Add width attribute
- table.setAttribute('width', '100%');
- return;
- }
-
- // Convert complex tables to divs
- const replacementDiv = document.createElement('div');
- replacementDiv.className = 'converted-table';
- replacementDiv.style.border = '1px solid #ddd';
- replacementDiv.style.margin = '10px 0';
- replacementDiv.style.padding = '10px';
-
- // Copy the table's innerHTML
- replacementDiv.innerHTML = table.innerHTML;
-
- // Replace the table with the div
- if (table.parentNode) {
- table.parentNode.replaceChild(replacementDiv, table);
- convertedCount++;
+ }
}
});
- console.log(`Converted ${convertedCount} complex tables to divs to prevent Quill errors`);
+ console.log(`Converted ${convertedCount} tables to divs to prevent Quill errors`);
return tempDiv.innerHTML;
}
@@ -157,6 +159,7 @@ const RichEmailEditor: React.FC = ({
const toolbarRef = useRef(null);
const quillRef = useRef(null);
const [isReady, setIsReady] = useState(false);
+ const [isReplyOrForward, setIsReplyOrForward] = useState(false);
// Initialize Quill editor when component mounts
useEffect(() => {
@@ -164,23 +167,41 @@ const RichEmailEditor: React.FC = ({
const initializeQuill = async () => {
if (!editorRef.current || !toolbarRef.current) return;
+ // First, detect if content is reply/forward to determine editor mode
+ const contentIsReplyOrForward = initialContent ? (
+ initialContent.includes('wrote:') ||
+ initialContent.includes(' = ({
clipboard: {
matchVisual: false // Disable clipboard matching for better HTML handling
},
- // Don't initialize better-table yet - we'll do it after content is loaded
- 'better-table': false,
+ // Only enable better-table for regular content, not for replies/forwards
+ 'better-table': tableModule && !contentIsReplyOrForward ? true : false,
},
placeholder: placeholder,
theme: 'snow',
@@ -226,17 +247,10 @@ const RichEmailEditor: React.FC = ({
hasBlockquote: initialContent.includes(' = ({
containsQuoteHeader: sanitizedContent.includes('wrote:'),
hasTable: sanitizedContent.includes(' = ({
}
} else {
// Special handling for reply or forwarded content
- if (isReplyOrForward) {
+ if (contentIsReplyOrForward) {
console.log('Using special handling for reply/forward content');
- // Clean up any problematic table structures, with special care for quoted content
- const cleanedContent = cleanupTableStructures(sanitizedContent);
+ // For reply/forward content, convert ALL tables to divs
+ const cleanedContent = cleanupTableStructures(sanitizedContent, true);
// Use direct innerHTML setting with minimal processing for reply/forward content
quillRef.current.root.innerHTML = cleanedContent;
} else {
+ // For regular content, use normal processing
+ const cleanedContent = cleanupTableStructures(sanitizedContent, false);
+
// Use direct innerHTML setting for regular content
- quillRef.current.root.innerHTML = sanitizedContent;
+ quillRef.current.root.innerHTML = cleanedContent;
}
// Set the direction for the content
@@ -352,43 +369,6 @@ const RichEmailEditor: React.FC = ({
if (editorContainer) {
editorContainer.classList.add('email-editor-container');
}
-
- // Safe initialization of better-table module if available
- if (tableModule) {
- try {
- // Wait a small delay to ensure content is properly set before initializing table module
- setTimeout(() => {
- if (quillRef.current) {
- // First check if content has tables and whether it's a reply/forward
- const hasReplyForwardContent =
- quillRef.current.root.innerHTML.includes('wrote:') ||
- quillRef.current.root.innerHTML.includes('blockquote') ||
- quillRef.current.root.innerHTML.includes('Forwarded message');
-
- const hasTables = quillRef.current.root.innerHTML.includes(' = ({
});
// Check if content is reply or forward to use special handling
- const isReplyOrForward =
+ const contentIsReplyOrForward =
initialContent.includes('wrote:') ||
initialContent.includes(' = ({
containsQuoteHeader: sanitizedContent.includes('wrote:'),
hasTable: sanitizedContent.includes(' = ({
quillRef.current.setText(textContent || 'No content available');
}
} else {
- // Special handling for reply or forward content
- let contentToSet = sanitizedContent;
-
- if (isReplyOrForward) {
+ // Process content based on type
+ if (contentIsReplyOrForward) {
console.log('Using special handling for reply/forward content update');
- // Clean up tables with special care for quoted content
- contentToSet = cleanupTableStructures(sanitizedContent);
- // Set content directly to the root element
+ // For reply/forward content, convert ALL tables to divs
+ const cleanedContent = cleanupTableStructures(sanitizedContent, true);
+
+ // Set content without table handling
if (quillRef.current && quillRef.current.root) {
- // Temporarily disable the better-table module if it's initialized
- if (quillRef.current.getModule('better-table')) {
- quillRef.current.getModule('better-table').hideTableTools();
- }
-
- // Set content without table handling by the module
- quillRef.current.root.innerHTML = contentToSet;
+ quillRef.current.root.innerHTML = cleanedContent;
// Delay applying formatting to ensure Quill is fully ready
setTimeout(() => {
@@ -505,9 +493,11 @@ const RichEmailEditor: React.FC = ({
}, 100);
}
} else {
- // For regular content, use Quill's normal process
+ // For regular content, use normal processing
+ const cleanedContent = cleanupTableStructures(sanitizedContent, false);
+
if (quillRef.current && quillRef.current.root) {
- quillRef.current.root.innerHTML = contentToSet;
+ quillRef.current.root.innerHTML = cleanedContent;
// Safely apply formatting
try {
@@ -549,7 +539,7 @@ const RichEmailEditor: React.FC = ({
}
}
}
- }, [initialContent, isReady]);
+ }, [initialContent, isReady, isReplyOrForward]);
return (