187 lines
6.5 KiB
TypeScript
187 lines
6.5 KiB
TypeScript
'use client';
|
|
|
|
import { useRef } from 'react';
|
|
import { Loader2 } from 'lucide-react';
|
|
import { Button } from '@/components/ui/button';
|
|
import { ScrollArea } from '@/components/ui/scroll-area';
|
|
import { Avatar, AvatarFallback } from '@/components/ui/avatar';
|
|
import { Card } from '@/components/ui/card';
|
|
import { EmailMessage } from '@/types/email';
|
|
import { formatEmailDate } from '@/lib/utils/email-utils';
|
|
import EmailContentDisplay from './EmailContentDisplay';
|
|
|
|
interface EmailPreviewProps {
|
|
email: EmailMessage | null;
|
|
loading?: boolean;
|
|
onReply?: (type: 'reply' | 'reply-all' | 'forward') => void;
|
|
}
|
|
|
|
export default function EmailPreview({ email, loading = false, onReply }: EmailPreviewProps) {
|
|
const editorRef = useRef<HTMLDivElement>(null);
|
|
|
|
// Display loading state
|
|
if (loading) {
|
|
return (
|
|
<div className="flex items-center justify-center h-full p-6">
|
|
<div className="text-center">
|
|
<Loader2 className="h-8 w-8 animate-spin mx-auto mb-4 text-primary" />
|
|
<p>Loading email...</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// No email selected
|
|
if (!email) {
|
|
return (
|
|
<div className="flex items-center justify-center h-full p-6">
|
|
<div className="text-center text-muted-foreground">
|
|
<p>Select an email to view</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Extract sender name from email.from (which is a string in our standardized format)
|
|
let senderName = '';
|
|
let senderEmail = '';
|
|
|
|
if (email.from) {
|
|
// If it's a string, try to extract name and email with regex
|
|
const senderInfo = email.from.match(/^(?:"?([^"]*)"?\s)?<?([^\s>]+@[^\s>]+)>?$/);
|
|
senderName = senderInfo ? senderInfo[1] || senderInfo[2] : email.from;
|
|
senderEmail = senderInfo ? senderInfo[2] : email.from;
|
|
}
|
|
|
|
// Check for attachments
|
|
const hasAttachments = email.attachments && email.attachments.length > 0;
|
|
|
|
// Get sender initials for avatar
|
|
const getSenderInitials = (name: string) => {
|
|
if (!name) return '';
|
|
return name
|
|
.split(" ")
|
|
.map((n) => n?.[0] || '')
|
|
.join("")
|
|
.toUpperCase()
|
|
.slice(0, 2);
|
|
};
|
|
|
|
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>
|
|
|
|
<div className="flex items-start gap-3 mb-4">
|
|
<Avatar className="h-10 w-10">
|
|
<AvatarFallback>{getSenderInitials(senderName)}</AvatarFallback>
|
|
</Avatar>
|
|
|
|
<div className="flex-1 min-w-0">
|
|
<div className="flex items-center justify-between">
|
|
<div className="font-medium">{senderName}</div>
|
|
<span className="text-sm text-muted-foreground">{formatEmailDate(email.date)}</span>
|
|
</div>
|
|
|
|
<div className="text-sm text-muted-foreground truncate mt-1">
|
|
To: {email.to}
|
|
</div>
|
|
|
|
{email.cc && (
|
|
<div className="text-sm text-muted-foreground truncate mt-1">
|
|
Cc: {email.cc}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Action buttons */}
|
|
{onReply && (
|
|
<div className="flex gap-2 mt-4">
|
|
<Button
|
|
size="sm"
|
|
variant="outline"
|
|
onClick={() => onReply('reply')}
|
|
>
|
|
Reply
|
|
</Button>
|
|
<Button
|
|
size="sm"
|
|
variant="outline"
|
|
onClick={() => onReply('reply-all')}
|
|
>
|
|
Reply All
|
|
</Button>
|
|
<Button
|
|
size="sm"
|
|
variant="outline"
|
|
onClick={() => onReply('forward')}
|
|
>
|
|
Forward
|
|
</Button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Attachments list */}
|
|
{hasAttachments && email.attachments && (
|
|
<div className="px-6 py-3 border-b bg-muted/20">
|
|
<h3 className="text-sm font-medium mb-2">Attachments ({email.attachments.length})</h3>
|
|
<div className="flex flex-wrap gap-2">
|
|
{email.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"
|
|
>
|
|
<div className="flex-shrink-0">
|
|
<svg className="h-4 w-4 text-muted-foreground" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"></path></svg>
|
|
</div>
|
|
<span>{attachment.filename}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Email body */}
|
|
<ScrollArea className="flex-1">
|
|
<div className="p-6">
|
|
<div
|
|
ref={editorRef}
|
|
className="email-content-container rounded-lg overflow-hidden bg-white shadow-sm"
|
|
style={{
|
|
minHeight: '300px',
|
|
border: '1px solid #e2e8f0'
|
|
}}
|
|
>
|
|
<EmailContentDisplay
|
|
content={email.content}
|
|
type="auto"
|
|
className="p-6"
|
|
debug={process.env.NODE_ENV === 'development'}
|
|
/>
|
|
</div>
|
|
|
|
{/* Debugging info - simplified */}
|
|
{process.env.NODE_ENV === 'development' && (
|
|
<details className="mt-4 text-xs text-muted-foreground border rounded-md p-2">
|
|
<summary className="cursor-pointer">Email Debug Info</summary>
|
|
<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>Content Type:</strong> {email.content.isHtml ? 'HTML' : 'Plain Text'}</p>
|
|
<p><strong>Text Direction:</strong> {email.content.direction}</p>
|
|
<p><strong>Content Size:</strong>
|
|
HTML: {email.content.html?.length || 0} chars,
|
|
Text: {email.content.text?.length || 0} chars
|
|
</p>
|
|
</div>
|
|
</details>
|
|
)}
|
|
</div>
|
|
</ScrollArea>
|
|
</Card>
|
|
);
|
|
}
|