courrier preview
This commit is contained in:
parent
d12a6c4670
commit
b71336e749
104
components/email/ComposeEmailAdapter.tsx
Normal file
104
components/email/ComposeEmailAdapter.tsx
Normal file
@ -0,0 +1,104 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import ComposeEmail from './ComposeEmail';
|
||||
import { EmailMessage as NewEmailMessage } from '@/types/email';
|
||||
import {
|
||||
EmailMessage as OldEmailMessage,
|
||||
formatReplyEmail as oldFormatReplyEmail,
|
||||
formatForwardedEmail as oldFormatForwardedEmail
|
||||
} from '@/lib/utils/email-formatter';
|
||||
|
||||
interface ComposeEmailAdapterProps {
|
||||
initialEmail?: NewEmailMessage | 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<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapter component that converts between the new EmailMessage format
|
||||
* and the format expected by the legacy ComposeEmail component
|
||||
*/
|
||||
export default function ComposeEmailAdapter({
|
||||
initialEmail,
|
||||
type = 'new',
|
||||
onClose,
|
||||
onSend
|
||||
}: ComposeEmailAdapterProps) {
|
||||
// Convert the new EmailMessage format to the old format
|
||||
const [adaptedEmail, setAdaptedEmail] = useState<OldEmailMessage | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!initialEmail) {
|
||||
setAdaptedEmail(null);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Convert the new EmailMessage to the old format
|
||||
const oldFormat: OldEmailMessage = {
|
||||
id: initialEmail.id,
|
||||
messageId: initialEmail.messageId,
|
||||
subject: initialEmail.subject,
|
||||
from: initialEmail.from,
|
||||
to: initialEmail.to,
|
||||
cc: initialEmail.cc,
|
||||
bcc: initialEmail.bcc,
|
||||
date: initialEmail.date,
|
||||
// Convert new flags object to old format string array
|
||||
flags: initialEmail.flags ? {
|
||||
seen: initialEmail.flags.seen || false,
|
||||
flagged: initialEmail.flags.flagged || false,
|
||||
answered: initialEmail.flags.answered || false,
|
||||
deleted: initialEmail.flags.deleted || false,
|
||||
draft: initialEmail.flags.draft || false
|
||||
} : undefined,
|
||||
// Convert new content format to old format
|
||||
content: initialEmail.content.isHtml && initialEmail.content.html
|
||||
? initialEmail.content.html
|
||||
: initialEmail.content.text,
|
||||
html: initialEmail.content.isHtml ? initialEmail.content.html : undefined,
|
||||
text: initialEmail.content.text,
|
||||
attachments: initialEmail.attachments.map(att => ({
|
||||
filename: att.filename,
|
||||
contentType: att.contentType,
|
||||
content: att.content,
|
||||
size: att.size || 0
|
||||
}))
|
||||
};
|
||||
|
||||
console.log('ComposeEmailAdapter: Converted new format to old format', oldFormat);
|
||||
setAdaptedEmail(oldFormat);
|
||||
} catch (error) {
|
||||
console.error('Error adapting email for ComposeEmail:', error);
|
||||
setAdaptedEmail(null);
|
||||
}
|
||||
}, [initialEmail]);
|
||||
|
||||
// If still adapting, show loading
|
||||
if (initialEmail && !adaptedEmail) {
|
||||
return <div>Loading email...</div>;
|
||||
}
|
||||
|
||||
// Pass the adapted email to the original ComposeEmail component
|
||||
return (
|
||||
<ComposeEmail
|
||||
initialEmail={adaptedEmail}
|
||||
type={type}
|
||||
onClose={onClose}
|
||||
onSend={onSend}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -9,6 +9,7 @@ interface EmailContentDisplayProps {
|
||||
className?: string;
|
||||
showQuotedText?: boolean;
|
||||
type?: 'html' | 'text' | 'auto';
|
||||
debug?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -19,45 +20,76 @@ const EmailContentDisplay: React.FC<EmailContentDisplayProps> = ({
|
||||
content,
|
||||
className = '',
|
||||
showQuotedText = true,
|
||||
type = 'auto'
|
||||
type = 'auto',
|
||||
debug = false
|
||||
}) => {
|
||||
// Normalize the content to our standard format if needed
|
||||
const normalizedContent = useMemo(() => {
|
||||
// If content is already in our EmailContent format
|
||||
if (content &&
|
||||
typeof content === 'object' &&
|
||||
'text' in content &&
|
||||
'isHtml' in content) {
|
||||
return content as EmailContent;
|
||||
try {
|
||||
// Handle different input types
|
||||
if (!content) {
|
||||
return {
|
||||
html: undefined,
|
||||
text: 'No content available',
|
||||
isHtml: false,
|
||||
direction: 'ltr'
|
||||
} as EmailContent;
|
||||
}
|
||||
|
||||
// If content is already in our EmailContent format
|
||||
if (content &&
|
||||
typeof content === 'object' &&
|
||||
'text' in content &&
|
||||
'isHtml' in content) {
|
||||
return content as EmailContent;
|
||||
}
|
||||
|
||||
// Special case for simple string content
|
||||
if (typeof content === 'string') {
|
||||
return normalizeEmailContent({ content });
|
||||
}
|
||||
|
||||
// Otherwise normalize it
|
||||
return normalizeEmailContent(content);
|
||||
} catch (error) {
|
||||
console.error('Error normalizing content in EmailContentDisplay:', error);
|
||||
return {
|
||||
html: undefined,
|
||||
text: `Error processing email content: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
isHtml: false,
|
||||
direction: 'ltr'
|
||||
} as EmailContent;
|
||||
}
|
||||
|
||||
// Otherwise normalize it
|
||||
return normalizeEmailContent(content);
|
||||
}, [content]);
|
||||
|
||||
// Render the normalized content
|
||||
const htmlContent = useMemo(() => {
|
||||
if (!normalizedContent) return '';
|
||||
|
||||
// Override content type if specified
|
||||
let contentToRender: EmailContent = { ...normalizedContent };
|
||||
|
||||
if (type === 'html' && !contentToRender.isHtml) {
|
||||
// Force HTML rendering for text content
|
||||
contentToRender = {
|
||||
...contentToRender,
|
||||
isHtml: true,
|
||||
html: `<p>${contentToRender.text.replace(/\n/g, '<br>')}</p>`
|
||||
};
|
||||
} else if (type === 'text' && contentToRender.isHtml) {
|
||||
// Force text rendering
|
||||
contentToRender = {
|
||||
...contentToRender,
|
||||
isHtml: false
|
||||
};
|
||||
try {
|
||||
// Override content type if specified
|
||||
let contentToRender: EmailContent = { ...normalizedContent };
|
||||
|
||||
if (type === 'html' && !contentToRender.isHtml) {
|
||||
// Force HTML rendering for text content
|
||||
contentToRender = {
|
||||
...contentToRender,
|
||||
isHtml: true,
|
||||
html: `<p>${contentToRender.text.replace(/\n/g, '<br>')}</p>`
|
||||
};
|
||||
} else if (type === 'text' && contentToRender.isHtml) {
|
||||
// Force text rendering
|
||||
contentToRender = {
|
||||
...contentToRender,
|
||||
isHtml: false
|
||||
};
|
||||
}
|
||||
|
||||
return renderEmailContent(contentToRender);
|
||||
} catch (error) {
|
||||
console.error('Error rendering content in EmailContentDisplay:', error);
|
||||
return `<div class="error-message p-4 text-red-500">Error rendering email content: ${error instanceof Error ? error.message : 'Unknown error'}</div>`;
|
||||
}
|
||||
|
||||
return renderEmailContent(contentToRender);
|
||||
}, [normalizedContent, type]);
|
||||
|
||||
// Apply quoted text styling if needed
|
||||
@ -75,6 +107,20 @@ const EmailContentDisplay: React.FC<EmailContentDisplayProps> = ({
|
||||
dangerouslySetInnerHTML={{ __html: htmlContent }}
|
||||
/>
|
||||
|
||||
{/* Debug output if enabled */}
|
||||
{debug && (
|
||||
<div className="content-debug mt-4 p-2 text-xs bg-gray-100 border rounded">
|
||||
<p><strong>Content Type:</strong> {typeof content}</p>
|
||||
{typeof content === 'object' && (
|
||||
<p><strong>Keys:</strong> {Object.keys(content).join(', ')}</p>
|
||||
)}
|
||||
<p><strong>Normalized:</strong> {normalizedContent.isHtml ? 'HTML' : 'Text'}</p>
|
||||
<p><strong>Direction:</strong> {normalizedContent.direction}</p>
|
||||
<p><strong>Has HTML:</strong> {!!normalizedContent.html}</p>
|
||||
<p><strong>Text Length:</strong> {normalizedContent.text?.length || 0}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<style jsx>{`
|
||||
.email-content-display {
|
||||
width: 100%;
|
||||
@ -101,6 +147,11 @@ const EmailContentDisplay: React.FC<EmailContentDisplayProps> = ({
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.content-debug {
|
||||
font-family: monospace;
|
||||
color: #666;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -2,45 +2,12 @@
|
||||
|
||||
import { useState, useEffect, useMemo, useCallback } from 'react';
|
||||
import EmailPreview from './EmailPreview';
|
||||
import ComposeEmail from './ComposeEmail';
|
||||
import ComposeEmailAdapter from './ComposeEmailAdapter';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { formatReplyEmail, EmailMessage as FormatterEmailMessage } from '@/lib/utils/email-formatter';
|
||||
import { useEmailFetch } from '@/hooks/use-email-fetch';
|
||||
import { debounce } from '@/lib/utils/debounce';
|
||||
import { formatEmailContent } from '@/lib/utils/email-content';
|
||||
|
||||
// Add local EmailMessage interface
|
||||
interface EmailAddress {
|
||||
name: string;
|
||||
address: string;
|
||||
}
|
||||
|
||||
interface EmailMessage {
|
||||
id: string;
|
||||
messageId?: string;
|
||||
subject: string;
|
||||
from: EmailAddress[];
|
||||
to: EmailAddress[];
|
||||
cc?: EmailAddress[];
|
||||
bcc?: EmailAddress[];
|
||||
date: Date | string;
|
||||
flags?: {
|
||||
seen: boolean;
|
||||
flagged: boolean;
|
||||
answered: boolean;
|
||||
deleted: boolean;
|
||||
draft: boolean;
|
||||
};
|
||||
preview?: string;
|
||||
content?: string;
|
||||
html?: string;
|
||||
text?: string;
|
||||
hasAttachments?: boolean;
|
||||
attachments?: any[];
|
||||
folder?: string;
|
||||
size?: number;
|
||||
contentFetched?: boolean;
|
||||
}
|
||||
import { EmailMessage } from '@/types/email';
|
||||
import { adaptLegacyEmail } from '@/lib/utils/email-adapter';
|
||||
|
||||
interface EmailPanelProps {
|
||||
selectedEmail: {
|
||||
@ -48,7 +15,26 @@ interface EmailPanelProps {
|
||||
accountId: string;
|
||||
folder: string;
|
||||
} | null;
|
||||
onSendEmail: (email: any) => void;
|
||||
onSendEmail: (email: any) => Promise<void>;
|
||||
}
|
||||
|
||||
// Type for the legacy ComposeEmail component props
|
||||
interface ComposeEmailProps {
|
||||
initialEmail?: any;
|
||||
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<void>;
|
||||
}
|
||||
|
||||
export default function EmailPanel({
|
||||
@ -70,8 +56,8 @@ export default function EmailPanel({
|
||||
const [isComposing, setIsComposing] = useState<boolean>(false);
|
||||
const [composeType, setComposeType] = useState<'new' | 'reply' | 'reply-all' | 'forward'>('new');
|
||||
|
||||
// Format the email content
|
||||
const formattedEmail = useMemo(() => {
|
||||
// Convert the email to the standardized format
|
||||
const standardizedEmail = useMemo(() => {
|
||||
if (!email) {
|
||||
console.log('EmailPanel: No email provided');
|
||||
return null;
|
||||
@ -79,28 +65,13 @@ export default function EmailPanel({
|
||||
|
||||
console.log('EmailPanel: Raw email:', email);
|
||||
|
||||
// CRITICAL FIX: Simplify email formatting to prevent double processing
|
||||
// Just normalize the content structure, don't try to format content here
|
||||
// The actual formatting will happen in EmailPreview with formatEmailContent
|
||||
|
||||
// If all fields are already present, just return as is
|
||||
if (email.content && typeof email.content === 'object' && email.content.html && email.content.text) {
|
||||
return email;
|
||||
try {
|
||||
// Use the adapter utility to convert to the standardized format
|
||||
return adaptLegacyEmail(email);
|
||||
} catch (error) {
|
||||
console.error('EmailPanel: Error adapting email:', error);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create a standardized email object with consistent content structure
|
||||
return {
|
||||
...email,
|
||||
// Ensure content is an object with html and text properties
|
||||
content: {
|
||||
text: typeof email.content === 'object' ? email.content.text :
|
||||
typeof email.text === 'string' ? email.text :
|
||||
typeof email.content === 'string' ? email.content : '',
|
||||
html: typeof email.content === 'object' ? email.content.html :
|
||||
typeof email.html === 'string' ? email.html :
|
||||
typeof email.content === 'string' ? email.content : ''
|
||||
}
|
||||
};
|
||||
}, [email]);
|
||||
|
||||
// Debounced email fetch
|
||||
@ -141,6 +112,16 @@ export default function EmailPanel({
|
||||
setComposeType('new');
|
||||
};
|
||||
|
||||
// Wrap the onSendEmail function to ensure it returns a Promise
|
||||
const handleSendEmail = async (emailData: any) => {
|
||||
try {
|
||||
return await onSendEmail(emailData);
|
||||
} catch (error) {
|
||||
console.error('Error sending email:', error);
|
||||
throw error; // Re-throw to let ComposeEmail handle it
|
||||
}
|
||||
};
|
||||
|
||||
// If no email is selected and not composing
|
||||
if (!selectedEmail && !isComposing) {
|
||||
return (
|
||||
@ -194,16 +175,16 @@ export default function EmailPanel({
|
||||
return (
|
||||
<div className="h-full">
|
||||
{isComposing ? (
|
||||
<ComposeEmail
|
||||
initialEmail={formattedEmail}
|
||||
<ComposeEmailAdapter
|
||||
initialEmail={standardizedEmail}
|
||||
type={composeType}
|
||||
onClose={handleComposeClose}
|
||||
onSend={onSendEmail}
|
||||
onSend={handleSendEmail}
|
||||
/>
|
||||
) : (
|
||||
<div className="max-w-4xl mx-auto h-full">
|
||||
<EmailPreview
|
||||
email={formattedEmail}
|
||||
email={standardizedEmail}
|
||||
onReply={handleReply}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { useRef } from 'react';
|
||||
import { useRef, useMemo } from 'react';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
@ -8,10 +8,11 @@ import { Avatar, AvatarFallback } from '@/components/ui/avatar';
|
||||
import { Card } from '@/components/ui/card';
|
||||
import { EmailMessage, EmailAddress } from '@/types/email';
|
||||
import { formatEmailAddresses, formatEmailDate } from '@/lib/utils/email-utils';
|
||||
import { adaptLegacyEmail } from '@/lib/utils/email-adapter';
|
||||
import EmailContentDisplay from './EmailContentDisplay';
|
||||
|
||||
interface EmailPreviewProps {
|
||||
email: EmailMessage | null;
|
||||
email: EmailMessage | any;
|
||||
loading?: boolean;
|
||||
onReply?: (type: 'reply' | 'reply-all' | 'forward') => void;
|
||||
}
|
||||
@ -20,6 +21,31 @@ export default function EmailPreview({ email, loading = false, onReply }: EmailP
|
||||
// Add editorRef to match ComposeEmail exactly
|
||||
const editorRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Convert legacy email to standardized format if needed
|
||||
const standardizedEmail = useMemo(() => {
|
||||
if (!email) return null;
|
||||
|
||||
try {
|
||||
// Check if the email is already in the standardized format
|
||||
if (
|
||||
email.content &&
|
||||
typeof email.content === 'object' &&
|
||||
'isHtml' in email.content &&
|
||||
'text' in email.content
|
||||
) {
|
||||
console.log('EmailPreview: Email is already in standardized format');
|
||||
return email as EmailMessage;
|
||||
}
|
||||
|
||||
// Otherwise, adapt it
|
||||
console.log('EmailPreview: Adapting legacy email format');
|
||||
return adaptLegacyEmail(email);
|
||||
} catch (error) {
|
||||
console.error('Error adapting email:', error);
|
||||
return null;
|
||||
}
|
||||
}, [email]);
|
||||
|
||||
// Get sender initials for avatar
|
||||
const getSenderInitials = (name: string) => {
|
||||
if (!name) return '';
|
||||
@ -44,7 +70,7 @@ export default function EmailPreview({ email, loading = false, onReply }: EmailP
|
||||
}
|
||||
|
||||
// No email selected
|
||||
if (!email) {
|
||||
if (!standardizedEmail) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full p-6">
|
||||
<div className="text-center text-muted-foreground">
|
||||
@ -54,17 +80,20 @@ export default function EmailPreview({ email, loading = false, onReply }: EmailP
|
||||
);
|
||||
}
|
||||
|
||||
const sender = email.from && email.from.length > 0 ? email.from[0] : undefined;
|
||||
// Debug output for content structure
|
||||
console.log('EmailPreview: Standardized Email Content:', standardizedEmail.content);
|
||||
|
||||
const sender = standardizedEmail.from && standardizedEmail.from.length > 0 ? standardizedEmail.from[0] : undefined;
|
||||
|
||||
// Check for attachments
|
||||
const hasAttachments = email.attachments && email.attachments.length > 0;
|
||||
const hasAttachments = standardizedEmail.attachments && standardizedEmail.attachments.length > 0;
|
||||
|
||||
return (
|
||||
<Card className="flex flex-col h-full overflow-hidden border-0 shadow-none">
|
||||
{/* Email header */}
|
||||
<div className="p-6 border-b">
|
||||
<div className="mb-4">
|
||||
<h2 className="text-xl font-semibold mb-4">{email.subject}</h2>
|
||||
<h2 className="text-xl font-semibold mb-4">{standardizedEmail.subject}</h2>
|
||||
|
||||
<div className="flex items-start gap-3 mb-4">
|
||||
<Avatar className="h-10 w-10">
|
||||
@ -74,16 +103,16 @@ export default function EmailPreview({ email, loading = false, onReply }: EmailP
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="font-medium">{sender?.name || sender?.address}</div>
|
||||
<span className="text-sm text-muted-foreground">{formatEmailDate(email.date)}</span>
|
||||
<span className="text-sm text-muted-foreground">{formatEmailDate(standardizedEmail.date)}</span>
|
||||
</div>
|
||||
|
||||
<div className="text-sm text-muted-foreground truncate mt-1">
|
||||
To: {formatEmailAddresses(email.to)}
|
||||
To: {formatEmailAddresses(standardizedEmail.to)}
|
||||
</div>
|
||||
|
||||
{email.cc && email.cc.length > 0 && (
|
||||
{standardizedEmail.cc && standardizedEmail.cc.length > 0 && (
|
||||
<div className="text-sm text-muted-foreground truncate mt-1">
|
||||
Cc: {formatEmailAddresses(email.cc)}
|
||||
Cc: {formatEmailAddresses(standardizedEmail.cc)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@ -120,9 +149,9 @@ export default function EmailPreview({ email, loading = false, onReply }: EmailP
|
||||
{/* Attachments list */}
|
||||
{hasAttachments && (
|
||||
<div className="px-6 py-3 border-b bg-muted/20">
|
||||
<h3 className="text-sm font-medium mb-2">Attachments ({email.attachments.length})</h3>
|
||||
<h3 className="text-sm font-medium mb-2">Attachments ({standardizedEmail.attachments.length})</h3>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{email.attachments.map((attachment, index) => (
|
||||
{standardizedEmail.attachments.map((attachment, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center gap-2 bg-background rounded-md px-3 py-1.5 text-sm border"
|
||||
@ -151,24 +180,38 @@ export default function EmailPreview({ email, loading = false, onReply }: EmailP
|
||||
}}
|
||||
>
|
||||
<EmailContentDisplay
|
||||
content={email.content}
|
||||
content={standardizedEmail.content}
|
||||
type="auto"
|
||||
className="p-6"
|
||||
debug={process.env.NODE_ENV === 'development'}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Only in development mode: Show debugging info */}
|
||||
{/* Always show debugging info in development mode */}
|
||||
{process.env.NODE_ENV === 'development' && (
|
||||
<details className="mt-4 text-xs text-muted-foreground border rounded-md p-2">
|
||||
<details className="mt-4 text-xs text-muted-foreground border rounded-md p-2" open>
|
||||
<summary className="cursor-pointer">Email Debug Info</summary>
|
||||
<div className="mt-2 overflow-auto max-h-40 p-2 bg-gray-50 rounded">
|
||||
<p><strong>Email ID:</strong> {email.id}</p>
|
||||
<p><strong>Content Type:</strong> {email.content.isHtml ? 'HTML' : 'Plain Text'}</p>
|
||||
<p><strong>Text Direction:</strong> {email.content.direction || 'ltr'}</p>
|
||||
<div className="mt-2 overflow-auto max-h-80 p-2 bg-gray-50 rounded">
|
||||
<p><strong>Email ID:</strong> {standardizedEmail.id}</p>
|
||||
<p><strong>Content Type:</strong> {standardizedEmail.content.isHtml ? 'HTML' : 'Plain Text'}</p>
|
||||
<p><strong>Text Direction:</strong> {standardizedEmail.content.direction || 'ltr'}</p>
|
||||
<p><strong>Content Size:</strong>
|
||||
HTML: {email.content.html?.length || 0} chars,
|
||||
Text: {email.content.text?.length || 0} chars
|
||||
HTML: {standardizedEmail.content.html?.length || 0} chars,
|
||||
Text: {standardizedEmail.content.text?.length || 0} chars
|
||||
</p>
|
||||
<p><strong>Content Structure:</strong> {JSON.stringify(standardizedEmail.content, null, 2)}</p>
|
||||
<hr className="my-2" />
|
||||
<p><strong>Original Email Type:</strong> {typeof email}</p>
|
||||
<p><strong>Original Content Type:</strong> {typeof email.content}</p>
|
||||
{email && typeof email.content === 'object' && (
|
||||
<p><strong>Original Content Keys:</strong> {Object.keys(email.content).join(', ')}</p>
|
||||
)}
|
||||
{email && email.html && (
|
||||
<p><strong>Has HTML property:</strong> {email.html.length} chars</p>
|
||||
)}
|
||||
{email && email.text && (
|
||||
<p><strong>Has Text property:</strong> {email.text.length} chars</p>
|
||||
)}
|
||||
</div>
|
||||
</details>
|
||||
)}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user