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;
|
className?: string;
|
||||||
showQuotedText?: boolean;
|
showQuotedText?: boolean;
|
||||||
type?: 'html' | 'text' | 'auto';
|
type?: 'html' | 'text' | 'auto';
|
||||||
|
debug?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -19,10 +20,22 @@ const EmailContentDisplay: React.FC<EmailContentDisplayProps> = ({
|
|||||||
content,
|
content,
|
||||||
className = '',
|
className = '',
|
||||||
showQuotedText = true,
|
showQuotedText = true,
|
||||||
type = 'auto'
|
type = 'auto',
|
||||||
|
debug = false
|
||||||
}) => {
|
}) => {
|
||||||
// Normalize the content to our standard format if needed
|
// Normalize the content to our standard format if needed
|
||||||
const normalizedContent = useMemo(() => {
|
const normalizedContent = useMemo(() => {
|
||||||
|
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 is already in our EmailContent format
|
||||||
if (content &&
|
if (content &&
|
||||||
typeof content === 'object' &&
|
typeof content === 'object' &&
|
||||||
@ -31,14 +44,29 @@ const EmailContentDisplay: React.FC<EmailContentDisplayProps> = ({
|
|||||||
return content as EmailContent;
|
return content as EmailContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Special case for simple string content
|
||||||
|
if (typeof content === 'string') {
|
||||||
|
return normalizeEmailContent({ content });
|
||||||
|
}
|
||||||
|
|
||||||
// Otherwise normalize it
|
// Otherwise normalize it
|
||||||
return normalizeEmailContent(content);
|
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;
|
||||||
|
}
|
||||||
}, [content]);
|
}, [content]);
|
||||||
|
|
||||||
// Render the normalized content
|
// Render the normalized content
|
||||||
const htmlContent = useMemo(() => {
|
const htmlContent = useMemo(() => {
|
||||||
if (!normalizedContent) return '';
|
if (!normalizedContent) return '';
|
||||||
|
|
||||||
|
try {
|
||||||
// Override content type if specified
|
// Override content type if specified
|
||||||
let contentToRender: EmailContent = { ...normalizedContent };
|
let contentToRender: EmailContent = { ...normalizedContent };
|
||||||
|
|
||||||
@ -58,6 +86,10 @@ const EmailContentDisplay: React.FC<EmailContentDisplayProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return renderEmailContent(contentToRender);
|
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>`;
|
||||||
|
}
|
||||||
}, [normalizedContent, type]);
|
}, [normalizedContent, type]);
|
||||||
|
|
||||||
// Apply quoted text styling if needed
|
// Apply quoted text styling if needed
|
||||||
@ -75,6 +107,20 @@ const EmailContentDisplay: React.FC<EmailContentDisplayProps> = ({
|
|||||||
dangerouslySetInnerHTML={{ __html: htmlContent }}
|
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>{`
|
<style jsx>{`
|
||||||
.email-content-display {
|
.email-content-display {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -101,6 +147,11 @@ const EmailContentDisplay: React.FC<EmailContentDisplayProps> = ({
|
|||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.content-debug {
|
||||||
|
font-family: monospace;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
`}</style>
|
`}</style>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -2,45 +2,12 @@
|
|||||||
|
|
||||||
import { useState, useEffect, useMemo, useCallback } from 'react';
|
import { useState, useEffect, useMemo, useCallback } from 'react';
|
||||||
import EmailPreview from './EmailPreview';
|
import EmailPreview from './EmailPreview';
|
||||||
import ComposeEmail from './ComposeEmail';
|
import ComposeEmailAdapter from './ComposeEmailAdapter';
|
||||||
import { Loader2 } from 'lucide-react';
|
import { Loader2 } from 'lucide-react';
|
||||||
import { formatReplyEmail, EmailMessage as FormatterEmailMessage } from '@/lib/utils/email-formatter';
|
|
||||||
import { useEmailFetch } from '@/hooks/use-email-fetch';
|
import { useEmailFetch } from '@/hooks/use-email-fetch';
|
||||||
import { debounce } from '@/lib/utils/debounce';
|
import { debounce } from '@/lib/utils/debounce';
|
||||||
import { formatEmailContent } from '@/lib/utils/email-content';
|
import { EmailMessage } from '@/types/email';
|
||||||
|
import { adaptLegacyEmail } from '@/lib/utils/email-adapter';
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface EmailPanelProps {
|
interface EmailPanelProps {
|
||||||
selectedEmail: {
|
selectedEmail: {
|
||||||
@ -48,7 +15,26 @@ interface EmailPanelProps {
|
|||||||
accountId: string;
|
accountId: string;
|
||||||
folder: string;
|
folder: string;
|
||||||
} | null;
|
} | 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({
|
export default function EmailPanel({
|
||||||
@ -70,8 +56,8 @@ export default function EmailPanel({
|
|||||||
const [isComposing, setIsComposing] = useState<boolean>(false);
|
const [isComposing, setIsComposing] = useState<boolean>(false);
|
||||||
const [composeType, setComposeType] = useState<'new' | 'reply' | 'reply-all' | 'forward'>('new');
|
const [composeType, setComposeType] = useState<'new' | 'reply' | 'reply-all' | 'forward'>('new');
|
||||||
|
|
||||||
// Format the email content
|
// Convert the email to the standardized format
|
||||||
const formattedEmail = useMemo(() => {
|
const standardizedEmail = useMemo(() => {
|
||||||
if (!email) {
|
if (!email) {
|
||||||
console.log('EmailPanel: No email provided');
|
console.log('EmailPanel: No email provided');
|
||||||
return null;
|
return null;
|
||||||
@ -79,28 +65,13 @@ export default function EmailPanel({
|
|||||||
|
|
||||||
console.log('EmailPanel: Raw email:', email);
|
console.log('EmailPanel: Raw email:', email);
|
||||||
|
|
||||||
// CRITICAL FIX: Simplify email formatting to prevent double processing
|
try {
|
||||||
// Just normalize the content structure, don't try to format content here
|
// Use the adapter utility to convert to the standardized format
|
||||||
// The actual formatting will happen in EmailPreview with formatEmailContent
|
return adaptLegacyEmail(email);
|
||||||
|
} catch (error) {
|
||||||
// If all fields are already present, just return as is
|
console.error('EmailPanel: Error adapting email:', error);
|
||||||
if (email.content && typeof email.content === 'object' && email.content.html && email.content.text) {
|
return null;
|
||||||
return email;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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]);
|
}, [email]);
|
||||||
|
|
||||||
// Debounced email fetch
|
// Debounced email fetch
|
||||||
@ -141,6 +112,16 @@ export default function EmailPanel({
|
|||||||
setComposeType('new');
|
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 no email is selected and not composing
|
||||||
if (!selectedEmail && !isComposing) {
|
if (!selectedEmail && !isComposing) {
|
||||||
return (
|
return (
|
||||||
@ -194,16 +175,16 @@ export default function EmailPanel({
|
|||||||
return (
|
return (
|
||||||
<div className="h-full">
|
<div className="h-full">
|
||||||
{isComposing ? (
|
{isComposing ? (
|
||||||
<ComposeEmail
|
<ComposeEmailAdapter
|
||||||
initialEmail={formattedEmail}
|
initialEmail={standardizedEmail}
|
||||||
type={composeType}
|
type={composeType}
|
||||||
onClose={handleComposeClose}
|
onClose={handleComposeClose}
|
||||||
onSend={onSendEmail}
|
onSend={handleSendEmail}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="max-w-4xl mx-auto h-full">
|
<div className="max-w-4xl mx-auto h-full">
|
||||||
<EmailPreview
|
<EmailPreview
|
||||||
email={formattedEmail}
|
email={standardizedEmail}
|
||||||
onReply={handleReply}
|
onReply={handleReply}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useRef } from 'react';
|
import { useRef, useMemo } from 'react';
|
||||||
import { Loader2 } from 'lucide-react';
|
import { Loader2 } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
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 { Card } from '@/components/ui/card';
|
||||||
import { EmailMessage, EmailAddress } from '@/types/email';
|
import { EmailMessage, EmailAddress } from '@/types/email';
|
||||||
import { formatEmailAddresses, formatEmailDate } from '@/lib/utils/email-utils';
|
import { formatEmailAddresses, formatEmailDate } from '@/lib/utils/email-utils';
|
||||||
|
import { adaptLegacyEmail } from '@/lib/utils/email-adapter';
|
||||||
import EmailContentDisplay from './EmailContentDisplay';
|
import EmailContentDisplay from './EmailContentDisplay';
|
||||||
|
|
||||||
interface EmailPreviewProps {
|
interface EmailPreviewProps {
|
||||||
email: EmailMessage | null;
|
email: EmailMessage | any;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
onReply?: (type: 'reply' | 'reply-all' | 'forward') => void;
|
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
|
// Add editorRef to match ComposeEmail exactly
|
||||||
const editorRef = useRef<HTMLDivElement>(null);
|
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
|
// Get sender initials for avatar
|
||||||
const getSenderInitials = (name: string) => {
|
const getSenderInitials = (name: string) => {
|
||||||
if (!name) return '';
|
if (!name) return '';
|
||||||
@ -44,7 +70,7 @@ export default function EmailPreview({ email, loading = false, onReply }: EmailP
|
|||||||
}
|
}
|
||||||
|
|
||||||
// No email selected
|
// No email selected
|
||||||
if (!email) {
|
if (!standardizedEmail) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center h-full p-6">
|
<div className="flex items-center justify-center h-full p-6">
|
||||||
<div className="text-center text-muted-foreground">
|
<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
|
// Check for attachments
|
||||||
const hasAttachments = email.attachments && email.attachments.length > 0;
|
const hasAttachments = standardizedEmail.attachments && standardizedEmail.attachments.length > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="flex flex-col h-full overflow-hidden border-0 shadow-none">
|
<Card className="flex flex-col h-full overflow-hidden border-0 shadow-none">
|
||||||
{/* Email header */}
|
{/* Email header */}
|
||||||
<div className="p-6 border-b">
|
<div className="p-6 border-b">
|
||||||
<div className="mb-4">
|
<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">
|
<div className="flex items-start gap-3 mb-4">
|
||||||
<Avatar className="h-10 w-10">
|
<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-1 min-w-0">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="font-medium">{sender?.name || sender?.address}</div>
|
<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>
|
||||||
|
|
||||||
<div className="text-sm text-muted-foreground truncate mt-1">
|
<div className="text-sm text-muted-foreground truncate mt-1">
|
||||||
To: {formatEmailAddresses(email.to)}
|
To: {formatEmailAddresses(standardizedEmail.to)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{email.cc && email.cc.length > 0 && (
|
{standardizedEmail.cc && standardizedEmail.cc.length > 0 && (
|
||||||
<div className="text-sm text-muted-foreground truncate mt-1">
|
<div className="text-sm text-muted-foreground truncate mt-1">
|
||||||
Cc: {formatEmailAddresses(email.cc)}
|
Cc: {formatEmailAddresses(standardizedEmail.cc)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -120,9 +149,9 @@ export default function EmailPreview({ email, loading = false, onReply }: EmailP
|
|||||||
{/* Attachments list */}
|
{/* Attachments list */}
|
||||||
{hasAttachments && (
|
{hasAttachments && (
|
||||||
<div className="px-6 py-3 border-b bg-muted/20">
|
<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">
|
<div className="flex flex-wrap gap-2">
|
||||||
{email.attachments.map((attachment, index) => (
|
{standardizedEmail.attachments.map((attachment, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className="flex items-center gap-2 bg-background rounded-md px-3 py-1.5 text-sm border"
|
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
|
<EmailContentDisplay
|
||||||
content={email.content}
|
content={standardizedEmail.content}
|
||||||
type="auto"
|
type="auto"
|
||||||
className="p-6"
|
className="p-6"
|
||||||
|
debug={process.env.NODE_ENV === 'development'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Only in development mode: Show debugging info */}
|
{/* Always show debugging info in development mode */}
|
||||||
{process.env.NODE_ENV === 'development' && (
|
{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>
|
<summary className="cursor-pointer">Email Debug Info</summary>
|
||||||
<div className="mt-2 overflow-auto max-h-40 p-2 bg-gray-50 rounded">
|
<div className="mt-2 overflow-auto max-h-80 p-2 bg-gray-50 rounded">
|
||||||
<p><strong>Email ID:</strong> {email.id}</p>
|
<p><strong>Email ID:</strong> {standardizedEmail.id}</p>
|
||||||
<p><strong>Content Type:</strong> {email.content.isHtml ? 'HTML' : 'Plain Text'}</p>
|
<p><strong>Content Type:</strong> {standardizedEmail.content.isHtml ? 'HTML' : 'Plain Text'}</p>
|
||||||
<p><strong>Text Direction:</strong> {email.content.direction || 'ltr'}</p>
|
<p><strong>Text Direction:</strong> {standardizedEmail.content.direction || 'ltr'}</p>
|
||||||
<p><strong>Content Size:</strong>
|
<p><strong>Content Size:</strong>
|
||||||
HTML: {email.content.html?.length || 0} chars,
|
HTML: {standardizedEmail.content.html?.length || 0} chars,
|
||||||
Text: {email.content.text?.length || 0} chars
|
Text: {standardizedEmail.content.text?.length || 0} chars
|
||||||
</p>
|
</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>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user