courrier preview
This commit is contained in:
parent
d6e3acd17a
commit
7fcf4d5ea8
@ -894,6 +894,7 @@ export default function CourrierPage() {
|
||||
}
|
||||
}}
|
||||
onClose={() => setShowComposeModal(false)}
|
||||
accounts={accounts}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@ -2,11 +2,19 @@
|
||||
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import {
|
||||
X, Paperclip, SendHorizontal, Loader2, Plus
|
||||
X, Paperclip, SendHorizontal, Loader2, Plus, ChevronDown
|
||||
} from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import DOMPurify from 'isomorphic-dompurify';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
|
||||
// Import from the centralized utils
|
||||
import {
|
||||
@ -29,16 +37,22 @@ interface ComposeEmailProps {
|
||||
bcc?: string;
|
||||
subject: string;
|
||||
body: string;
|
||||
fromAccount?: string;
|
||||
attachments?: Array<{
|
||||
name: string;
|
||||
content: string;
|
||||
type: string;
|
||||
}>;
|
||||
}) => Promise<void>;
|
||||
accounts?: Array<{
|
||||
id: string;
|
||||
email: string;
|
||||
display_name?: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
const { initialEmail, type = 'new', onClose, onSend } = props;
|
||||
const { initialEmail, type = 'new', onClose, onSend, accounts = [] } = props;
|
||||
|
||||
// Email form state
|
||||
const [to, setTo] = useState<string>('');
|
||||
@ -49,6 +63,11 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
const [showCc, setShowCc] = useState<boolean>(false);
|
||||
const [showBcc, setShowBcc] = useState<boolean>(false);
|
||||
const [sending, setSending] = useState<boolean>(false);
|
||||
const [selectedAccount, setSelectedAccount] = useState<{
|
||||
id: string;
|
||||
email: string;
|
||||
display_name?: string;
|
||||
} | null>(accounts.length > 0 ? accounts[0] : null);
|
||||
const [attachments, setAttachments] = useState<Array<{
|
||||
name: string;
|
||||
content: string;
|
||||
@ -171,6 +190,7 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
bcc: bcc || undefined,
|
||||
subject,
|
||||
body: emailContent,
|
||||
fromAccount: selectedAccount?.id,
|
||||
attachments
|
||||
});
|
||||
|
||||
@ -197,7 +217,7 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
return (
|
||||
<div className="flex flex-col h-full max-h-[80vh] bg-white border rounded-md shadow-md">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between p-4 border-b bg-gray-50">
|
||||
<div className="flex items-center justify-between p-3 border-b bg-gray-50">
|
||||
<h2 className="text-lg font-medium text-gray-800">{getComposeTitle()}</h2>
|
||||
<Button variant="ghost" size="icon" onClick={onClose}>
|
||||
<X className="h-5 w-5" />
|
||||
@ -206,48 +226,91 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
|
||||
{/* Email Form */}
|
||||
<div className="flex-1 overflow-y-auto bg-white">
|
||||
<div className="p-4 space-y-4">
|
||||
<div className="p-2 space-y-2">
|
||||
{/* From */}
|
||||
<div className="border-b pb-1">
|
||||
<div className="flex items-center">
|
||||
<span className="w-16 text-gray-700 font-medium">From:</span>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full flex justify-between items-center h-8 px-2 py-1 text-left font-normal"
|
||||
>
|
||||
<span className="truncate">
|
||||
{selectedAccount ?
|
||||
(selectedAccount.display_name ?
|
||||
`${selectedAccount.display_name} <${selectedAccount.email}>` :
|
||||
selectedAccount.email) :
|
||||
'Select account'}
|
||||
</span>
|
||||
<ChevronDown className="h-4 w-4 opacity-50" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start" className="w-[240px]">
|
||||
<DropdownMenuLabel>Select account</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
{accounts.length > 0 ? (
|
||||
accounts.map(account => (
|
||||
<DropdownMenuItem
|
||||
key={account.id}
|
||||
onClick={() => setSelectedAccount(account)}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
{account.display_name ?
|
||||
`${account.display_name} <${account.email}>` :
|
||||
account.email}
|
||||
</DropdownMenuItem>
|
||||
))
|
||||
) : (
|
||||
<DropdownMenuItem disabled>No accounts available</DropdownMenuItem>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Recipients */}
|
||||
<div className="border-b pb-2">
|
||||
<div className="flex items-center mb-2">
|
||||
<div className="border-b pb-1">
|
||||
<div className="flex items-center">
|
||||
<span className="w-16 text-gray-700 font-medium">To:</span>
|
||||
<Input
|
||||
type="text"
|
||||
value={to}
|
||||
onChange={(e) => setTo(e.target.value)}
|
||||
placeholder="recipient@example.com"
|
||||
className="flex-1 border-0 shadow-none focus-visible:ring-0 px-0 bg-white text-gray-800"
|
||||
className="flex-1 border-0 shadow-none focus-visible:ring-0 px-0 h-8 bg-white text-gray-800"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{showCc && (
|
||||
<div className="flex items-center mb-2">
|
||||
<div className="flex items-center">
|
||||
<span className="w-16 text-gray-700 font-medium">Cc:</span>
|
||||
<Input
|
||||
type="text"
|
||||
value={cc}
|
||||
onChange={(e) => setCc(e.target.value)}
|
||||
placeholder="cc@example.com"
|
||||
className="flex-1 border-0 shadow-none focus-visible:ring-0 px-0 bg-white text-gray-800"
|
||||
className="flex-1 border-0 shadow-none focus-visible:ring-0 px-0 h-8 bg-white text-gray-800"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showBcc && (
|
||||
<div className="flex items-center mb-2">
|
||||
<div className="flex items-center">
|
||||
<span className="w-16 text-gray-700 font-medium">Bcc:</span>
|
||||
<Input
|
||||
type="text"
|
||||
value={bcc}
|
||||
onChange={(e) => setBcc(e.target.value)}
|
||||
placeholder="bcc@example.com"
|
||||
className="flex-1 border-0 shadow-none focus-visible:ring-0 px-0 bg-white text-gray-800"
|
||||
className="flex-1 border-0 shadow-none focus-visible:ring-0 px-0 h-8 bg-white text-gray-800"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* CC/BCC Toggle Links */}
|
||||
<div className="flex gap-3 ml-16 mb-1">
|
||||
<div className="flex gap-3 ml-16">
|
||||
{!showCc && (
|
||||
<button
|
||||
className="text-blue-600 text-sm hover:underline"
|
||||
@ -269,7 +332,7 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
</div>
|
||||
|
||||
{/* Subject */}
|
||||
<div className="border-b pb-2">
|
||||
<div className="border-b pb-1">
|
||||
<div className="flex items-center">
|
||||
<span className="w-16 text-gray-700 font-medium">Subject:</span>
|
||||
<Input
|
||||
@ -277,7 +340,7 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
value={subject}
|
||||
onChange={(e) => setSubject(e.target.value)}
|
||||
placeholder="Subject"
|
||||
className="flex-1 border-0 shadow-none focus-visible:ring-0 px-0 bg-white text-gray-800"
|
||||
className="flex-1 border-0 shadow-none focus-visible:ring-0 px-0 h-8 bg-white text-gray-800"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -285,7 +348,7 @@ export default function ComposeEmail(props: ComposeEmailProps) {
|
||||
{/* Message Body */}
|
||||
<div
|
||||
ref={editorRef}
|
||||
className="min-h-[300px] outline-none p-2 border rounded-md bg-white text-gray-800"
|
||||
className="min-h-[320px] outline-none p-2 border rounded-md bg-white text-gray-800 flex-1"
|
||||
contentEditable={true}
|
||||
dangerouslySetInnerHTML={{ __html: emailContent }}
|
||||
onInput={(e) => setEmailContent(e.currentTarget.innerHTML)}
|
||||
|
||||
@ -18,12 +18,18 @@ interface ComposeEmailAdapterProps {
|
||||
bcc?: string;
|
||||
subject: string;
|
||||
body: string;
|
||||
fromAccount?: string;
|
||||
attachments?: Array<{
|
||||
name: string;
|
||||
content: string;
|
||||
type: string;
|
||||
}>;
|
||||
}) => Promise<void>;
|
||||
accounts?: Array<{
|
||||
id: string;
|
||||
email: string;
|
||||
display_name?: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -34,7 +40,8 @@ export default function ComposeEmailAdapter({
|
||||
initialEmail,
|
||||
type = 'new',
|
||||
onClose,
|
||||
onSend
|
||||
onSend,
|
||||
accounts = []
|
||||
}: ComposeEmailAdapterProps) {
|
||||
// Convert the new EmailMessage format to the old format
|
||||
const [adaptedEmail, setAdaptedEmail] = useState<any | null>(null);
|
||||
@ -130,6 +137,7 @@ export default function ComposeEmailAdapter({
|
||||
type={type}
|
||||
onClose={onClose}
|
||||
onSend={onSend}
|
||||
accounts={accounts}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -11,7 +11,6 @@ import { Separator } from '@/components/ui/separator';
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import EmailPanel from './EmailPanel';
|
||||
import { EmailMessage } from '@/lib/services/email-service';
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { sendEmail } from '@/lib/services/email-service';
|
||||
import { useSession } from "next-auth/react";
|
||||
@ -22,7 +21,7 @@ interface EmailLayoutProps {
|
||||
|
||||
export default function EmailLayout({ className = '' }: EmailLayoutProps) {
|
||||
// Email state
|
||||
const [emails, setEmails] = useState<EmailMessage[]>([]);
|
||||
const [emails, setEmails] = useState<any[]>([]);
|
||||
const [selectedEmail, setSelectedEmail] = useState<{
|
||||
emailId: string;
|
||||
accountId: string;
|
||||
@ -31,6 +30,11 @@ export default function EmailLayout({ className = '' }: EmailLayoutProps) {
|
||||
const [currentFolder, setCurrentFolder] = useState<string>('INBOX');
|
||||
const [folders, setFolders] = useState<string[]>([]);
|
||||
const [mailboxes, setMailboxes] = useState<string[]>([]);
|
||||
const [accounts, setAccounts] = useState<Array<{
|
||||
id: string;
|
||||
email: string;
|
||||
display_name?: string;
|
||||
}>>([]);
|
||||
|
||||
// UI state
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
@ -49,6 +53,7 @@ export default function EmailLayout({ className = '' }: EmailLayoutProps) {
|
||||
// Load emails on component mount and when folder changes
|
||||
useEffect(() => {
|
||||
loadEmails();
|
||||
loadAccounts(); // Load accounts when component mounts
|
||||
}, [currentFolder, page]);
|
||||
|
||||
// Function to load emails
|
||||
@ -117,6 +122,25 @@ export default function EmailLayout({ className = '' }: EmailLayoutProps) {
|
||||
}
|
||||
};
|
||||
|
||||
// Function to load accounts
|
||||
const loadAccounts = async () => {
|
||||
try {
|
||||
const response = await fetch('/api/courrier/accounts');
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
if (data.accounts) {
|
||||
setAccounts(data.accounts.map((acc: any) => ({
|
||||
id: acc.id || acc.email,
|
||||
email: acc.email,
|
||||
display_name: acc.display_name
|
||||
})));
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error loading accounts:', err);
|
||||
}
|
||||
};
|
||||
|
||||
// Handle folder change
|
||||
const handleFolderChange = (folder: string) => {
|
||||
setCurrentFolder(folder);
|
||||
@ -367,8 +391,8 @@ export default function EmailLayout({ className = '' }: EmailLayoutProps) {
|
||||
<div className="flex-1 h-full overflow-hidden">
|
||||
<EmailPanel
|
||||
selectedEmail={selectedEmail}
|
||||
folder={currentFolder}
|
||||
onSendEmail={handleSendEmail}
|
||||
accounts={accounts}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -16,6 +16,11 @@ interface EmailPanelProps {
|
||||
folder: string;
|
||||
} | null;
|
||||
onSendEmail: (email: any) => Promise<void>;
|
||||
accounts?: Array<{
|
||||
id: string;
|
||||
email: string;
|
||||
display_name?: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
// Type for the legacy ComposeEmail component props
|
||||
@ -29,6 +34,7 @@ interface ComposeEmailProps {
|
||||
bcc?: string;
|
||||
subject: string;
|
||||
body: string;
|
||||
fromAccount?: string;
|
||||
attachments?: Array<{
|
||||
name: string;
|
||||
content: string;
|
||||
@ -39,7 +45,8 @@ interface ComposeEmailProps {
|
||||
|
||||
export default function EmailPanel({
|
||||
selectedEmail,
|
||||
onSendEmail
|
||||
onSendEmail,
|
||||
accounts = []
|
||||
}: EmailPanelProps) {
|
||||
// Use the new email fetch hook
|
||||
const { email, loading, error, fetchEmail } = useEmailFetch({
|
||||
@ -172,6 +179,7 @@ export default function EmailPanel({
|
||||
type={composeType}
|
||||
onClose={handleComposeClose}
|
||||
onSend={handleSendEmail}
|
||||
accounts={accounts}
|
||||
/>
|
||||
) : (
|
||||
<div className="max-w-4xl mx-auto h-full">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user