courrier refactor rebuild 2
This commit is contained in:
parent
c993fe738e
commit
1c4b38ec8f
@ -97,6 +97,61 @@ export default function CourrierPage() {
|
||||
const [showLoginNeeded, setShowLoginNeeded] = useState(false);
|
||||
const [sidebarOpen, setSidebarOpen] = useState(true);
|
||||
const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
|
||||
const [accountsDropdownOpen, setAccountsDropdownOpen] = useState(false);
|
||||
const [currentView, setCurrentView] = useState('INBOX');
|
||||
const [unreadCount, setUnreadCount] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
// Mock accounts for the sidebar
|
||||
const [accounts, setAccounts] = useState<Account[]>([
|
||||
{ id: 0, name: 'All', email: '', color: 'bg-gray-500' },
|
||||
{ id: 1, name: 'Mail', email: 'user@example.com', color: 'bg-blue-500', folders: mailboxes }
|
||||
]);
|
||||
const [selectedAccount, setSelectedAccount] = useState<Account | null>(null);
|
||||
|
||||
// Update account folders when mailboxes change
|
||||
useEffect(() => {
|
||||
setAccounts(prev => {
|
||||
const updated = [...prev];
|
||||
if (updated[1]) {
|
||||
updated[1].folders = mailboxes;
|
||||
}
|
||||
return updated;
|
||||
});
|
||||
}, [mailboxes]);
|
||||
|
||||
// Calculate unread count (this would be replaced with actual data in production)
|
||||
useEffect(() => {
|
||||
// Example: counting unread emails in the inbox
|
||||
const unreadInInbox = emails.filter(email => !email.read && currentFolder === 'INBOX').length;
|
||||
setUnreadCount(unreadInInbox);
|
||||
}, [emails, currentFolder]);
|
||||
|
||||
// Helper to get folder icons
|
||||
const getFolderIcon = (folder: string) => {
|
||||
const folderLower = folder.toLowerCase();
|
||||
|
||||
if (folderLower.includes('inbox')) {
|
||||
return Inbox;
|
||||
} else if (folderLower.includes('sent')) {
|
||||
return Send;
|
||||
} else if (folderLower.includes('trash')) {
|
||||
return Trash;
|
||||
} else if (folderLower.includes('archive')) {
|
||||
return Archive;
|
||||
} else if (folderLower.includes('draft')) {
|
||||
return Edit;
|
||||
} else if (folderLower.includes('spam') || folderLower.includes('junk')) {
|
||||
return AlertOctagon;
|
||||
} else {
|
||||
return Folder;
|
||||
}
|
||||
};
|
||||
|
||||
// Helper to format folder names
|
||||
const formatFolderName = (folder: string) => {
|
||||
return folder.charAt(0).toUpperCase() + folder.slice(1).toLowerCase();
|
||||
};
|
||||
|
||||
// Check for more emails
|
||||
const hasMoreEmails = page < totalPages;
|
||||
@ -160,6 +215,12 @@ export default function CourrierPage() {
|
||||
setComposeType('new');
|
||||
setShowComposeModal(true);
|
||||
};
|
||||
|
||||
// Handle mailbox change
|
||||
const handleMailboxChange = (folder: string) => {
|
||||
changeFolder(folder);
|
||||
setCurrentView(folder);
|
||||
};
|
||||
|
||||
// Handle sending email
|
||||
const handleSendEmail = async (emailData: EmailData) => {
|
||||
@ -171,6 +232,30 @@ export default function CourrierPage() {
|
||||
await deleteEmails(selectedEmailIds);
|
||||
setShowDeleteConfirm(false);
|
||||
};
|
||||
|
||||
// Render sidebar navigation
|
||||
const renderSidebarNav = () => (
|
||||
<nav className="p-3">
|
||||
<ul className="space-y-0.5 px-2">
|
||||
{mailboxes.map((folder) => (
|
||||
<li key={folder}>
|
||||
<Button
|
||||
variant={currentFolder === folder ? 'secondary' : 'ghost'}
|
||||
className={`w-full justify-start py-2 ${
|
||||
currentFolder === folder ? 'bg-gray-100 text-gray-900' : 'text-gray-600 hover:text-gray-900'
|
||||
}`}
|
||||
onClick={() => handleMailboxChange(folder)}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
{React.createElement(getFolderIcon(folder), { className: "h-4 w-4" })}
|
||||
<span className="ml-2">{formatFolderName(folder)}</span>
|
||||
</div>
|
||||
</Button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
|
||||
// Check login on mount
|
||||
useEffect(() => {
|
||||
@ -190,10 +275,181 @@ export default function CourrierPage() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-screen flex-col">
|
||||
{/* Loading Fix for development */}
|
||||
<>
|
||||
<SimplifiedLoadingFix />
|
||||
|
||||
{/* Main layout */}
|
||||
<main className="w-full h-screen bg-black">
|
||||
<div className="w-full h-full px-4 pt-12 pb-4">
|
||||
<div className="flex h-full bg-carnet-bg">
|
||||
{/* Sidebar */}
|
||||
<div className={`${sidebarOpen ? 'w-60' : 'w-16'} bg-white/95 backdrop-blur-sm border-r border-gray-100 flex flex-col transition-all duration-300 ease-in-out
|
||||
${mobileSidebarOpen ? 'fixed inset-y-0 left-0 z-40' : 'hidden'} md:block`}>
|
||||
{/* Courrier Title */}
|
||||
<div className="p-3 border-b border-gray-100">
|
||||
<div className="flex items-center gap-2">
|
||||
<Mail className="h-6 w-6 text-gray-600" />
|
||||
<span className="text-xl font-semibold text-gray-900">COURRIER</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Compose button and refresh button */}
|
||||
<div className="p-2 border-b border-gray-100 flex items-center gap-2">
|
||||
<Button
|
||||
className="flex-1 bg-blue-600 text-white rounded-lg hover:bg-blue-700 flex items-center justify-center transition-all py-1.5 text-sm"
|
||||
onClick={handleComposeNew}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<PlusIcon className="h-3.5 w-3.5" />
|
||||
<span>Compose</span>
|
||||
</div>
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-9 w-9 text-gray-400 hover:text-gray-600"
|
||||
onClick={() => {
|
||||
setLoading(true);
|
||||
loadEmails().finally(() => setLoading(false));
|
||||
}}
|
||||
>
|
||||
<RefreshCw className={`h-4 w-4 ${loading ? 'animate-spin' : ''}`} />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Accounts Section */}
|
||||
<div className="p-3 border-b border-gray-100">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="w-full justify-between mb-2 text-sm font-medium text-gray-500"
|
||||
onClick={() => setAccountsDropdownOpen(!accountsDropdownOpen)}
|
||||
>
|
||||
<span>Accounts</span>
|
||||
{accountsDropdownOpen ? <ChevronUp className="h-4 w-4" /> : <ChevronDown className="h-4 w-4" />}
|
||||
</Button>
|
||||
|
||||
{accountsDropdownOpen && (
|
||||
<div className="space-y-1 pl-2">
|
||||
{accounts.map(account => (
|
||||
<div key={account.id} className="relative group">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="w-full justify-between px-2 py-1.5 text-sm group"
|
||||
onClick={() => setSelectedAccount(account)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className={`w-2.5 h-2.5 rounded-full ${account.color}`}></div>
|
||||
<span className="font-medium text-gray-700">{account.name}</span>
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
{/* Show folders for email accounts (not for "All" account) without the "Folders" header */}
|
||||
{account.id !== 0 && (
|
||||
<div className="pl-4 mt-1 mb-2 space-y-0.5 border-l border-gray-200">
|
||||
{account.folders && account.folders.length > 0 ? (
|
||||
account.folders.map((folder) => (
|
||||
<Button
|
||||
key={folder}
|
||||
variant="ghost"
|
||||
className={`w-full justify-start py-1 px-2 text-xs ${
|
||||
currentView === folder ? 'bg-gray-100 text-gray-900' : 'text-gray-600 hover:text-gray-900'
|
||||
}`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleMailboxChange(folder);
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center justify-between w-full gap-1.5">
|
||||
<div className="flex items-center gap-1.5">
|
||||
{React.createElement(getFolderIcon(folder), { className: "h-3.5 w-3.5" })}
|
||||
<span className="truncate">{folder}</span>
|
||||
</div>
|
||||
{folder === 'INBOX' && unreadCount > 0 && (
|
||||
<span className="ml-auto bg-blue-600 text-white text-xs px-1.5 py-0.5 rounded-full text-[10px]">
|
||||
{unreadCount}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</Button>
|
||||
))
|
||||
) : (
|
||||
<div className="px-2 py-2">
|
||||
<div className="flex flex-col space-y-2">
|
||||
{/* Create placeholder folder items with shimmer effect */}
|
||||
{Array.from({ length: 5 }).map((_, index) => (
|
||||
<div key={index} className="flex items-center gap-1.5 animate-pulse">
|
||||
<div className="h-3.5 w-3.5 bg-gray-200 rounded-sm"></div>
|
||||
<div className="h-3 w-24 bg-gray-200 rounded"></div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Navigation */}
|
||||
{renderSidebarNav()}
|
||||
</div>
|
||||
|
||||
{/* Email List and Content View */}
|
||||
<div className="flex-1 flex overflow-hidden">
|
||||
{/* Email List */}
|
||||
<EmailList
|
||||
emails={emails}
|
||||
selectedEmailIds={selectedEmailIds}
|
||||
selectedEmail={selectedEmail}
|
||||
currentFolder={currentFolder}
|
||||
isLoading={isLoading}
|
||||
totalEmails={emails.length}
|
||||
hasMoreEmails={hasMoreEmails}
|
||||
onSelectEmail={handleEmailSelect}
|
||||
onToggleSelect={toggleEmailSelection}
|
||||
onToggleSelectAll={toggleSelectAll}
|
||||
onBulkAction={handleBulkAction}
|
||||
onToggleStarred={toggleStarred}
|
||||
onLoadMore={handleLoadMore}
|
||||
onSearch={searchEmails}
|
||||
/>
|
||||
|
||||
{/* Email Content View */}
|
||||
<div className="flex-1 bg-white/95 backdrop-blur-sm flex flex-col">
|
||||
{selectedEmail ? (
|
||||
<EmailDetailView
|
||||
email={selectedEmail}
|
||||
onBack={() => handleEmailSelect('')}
|
||||
onReply={handleReply}
|
||||
onReplyAll={handleReplyAll}
|
||||
onForward={handleForward}
|
||||
onToggleStar={() => toggleStarred(selectedEmail.id)}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex-1 flex flex-col items-center justify-center text-center p-8">
|
||||
<Mail className="h-12 w-12 text-gray-300 mb-4" />
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-1">Select an email to read</h3>
|
||||
<p className="text-sm text-gray-500 max-w-sm">
|
||||
Choose an email from the list or compose a new message to get started.
|
||||
</p>
|
||||
<Button
|
||||
className="mt-6 bg-blue-600 hover:bg-blue-700"
|
||||
onClick={handleComposeNew}
|
||||
>
|
||||
<PlusIcon className="mr-2 h-4 w-4" />
|
||||
Compose New
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
{/* Login needed alert */}
|
||||
<LoginNeededAlert
|
||||
show={showLoginNeeded}
|
||||
@ -201,95 +457,17 @@ export default function CourrierPage() {
|
||||
onClose={() => setShowLoginNeeded(false)}
|
||||
/>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="flex flex-1 overflow-hidden">
|
||||
{/* Sidebar (Desktop) */}
|
||||
{sidebarOpen && (
|
||||
<aside className="hidden md:flex md:w-64 bg-gray-50 border-r border-gray-200 flex-col">
|
||||
{/* Account switching and compose button */}
|
||||
<div className="px-4 py-3 border-b border-gray-200">
|
||||
<Button
|
||||
className="w-full bg-blue-600 hover:bg-blue-700"
|
||||
onClick={handleComposeNew}
|
||||
>
|
||||
<PlusIcon className="mr-2 h-4 w-4" />
|
||||
Compose
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Folder Navigation */}
|
||||
<EmailSidebarContent
|
||||
mailboxes={mailboxes}
|
||||
currentFolder={currentFolder}
|
||||
onFolderChange={changeFolder}
|
||||
/>
|
||||
</aside>
|
||||
)}
|
||||
|
||||
{/* Email List and Content View */}
|
||||
<div className="flex-1 flex">
|
||||
{/* Email List */}
|
||||
<EmailList
|
||||
emails={emails}
|
||||
selectedEmailIds={selectedEmailIds}
|
||||
selectedEmail={selectedEmail}
|
||||
currentFolder={currentFolder}
|
||||
isLoading={isLoading}
|
||||
totalEmails={emails.length}
|
||||
hasMoreEmails={hasMoreEmails}
|
||||
onSelectEmail={handleEmailSelect}
|
||||
onToggleSelect={toggleEmailSelection}
|
||||
onToggleSelectAll={toggleSelectAll}
|
||||
onBulkAction={handleBulkAction}
|
||||
onToggleStarred={toggleStarred}
|
||||
onLoadMore={handleLoadMore}
|
||||
onSearch={searchEmails}
|
||||
/>
|
||||
|
||||
{/* Email Content View */}
|
||||
<div className="flex-1 bg-white/95 backdrop-blur-sm flex flex-col">
|
||||
{selectedEmail ? (
|
||||
<EmailDetailView
|
||||
email={selectedEmail}
|
||||
onBack={() => handleEmailSelect('')}
|
||||
onReply={handleReply}
|
||||
onReplyAll={handleReplyAll}
|
||||
onForward={handleForward}
|
||||
onToggleStar={() => toggleStarred(selectedEmail.id)}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex-1 flex flex-col items-center justify-center text-center p-8">
|
||||
<Mail className="h-12 w-12 text-gray-300 mb-4" />
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-1">Select an email to read</h3>
|
||||
<p className="text-sm text-gray-500 max-w-sm">
|
||||
Choose an email from the list or compose a new message to get started.
|
||||
</p>
|
||||
<Button
|
||||
className="mt-6 bg-blue-600 hover:bg-blue-700"
|
||||
onClick={handleComposeNew}
|
||||
>
|
||||
<PlusIcon className="mr-2 h-4 w-4" />
|
||||
Compose New
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Compose Modal */}
|
||||
<Dialog open={showComposeModal} onOpenChange={setShowComposeModal}>
|
||||
<DialogContent className="max-w-4xl p-0">
|
||||
<ComposeEmail
|
||||
initialEmail={selectedEmail}
|
||||
type={composeType}
|
||||
onClose={() => setShowComposeModal(false)}
|
||||
onSend={async (emailData: EmailData) => {
|
||||
await sendEmail(emailData);
|
||||
}}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
{showComposeModal && (
|
||||
<ComposeEmail
|
||||
initialEmail={selectedEmail}
|
||||
type={composeType}
|
||||
onClose={() => setShowComposeModal(false)}
|
||||
onSend={async (emailData: EmailData) => {
|
||||
await sendEmail(emailData);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Delete Confirmation Dialog */}
|
||||
<DeleteConfirmDialog
|
||||
@ -298,6 +476,6 @@ export default function CourrierPage() {
|
||||
onConfirm={handleDeleteConfirm}
|
||||
onCancel={() => setShowDeleteConfirm(false)}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -239,40 +239,196 @@ export default function ComposeEmail(props: ComposeEmailAllProps) {
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className="w-full max-w-3xl mx-auto flex flex-col min-h-[60vh] max-h-[80vh]">
|
||||
<ComposeEmailHeader
|
||||
type={type}
|
||||
onClose={onClose}
|
||||
/>
|
||||
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
<ComposeEmailForm
|
||||
to={to}
|
||||
setTo={setTo}
|
||||
cc={cc}
|
||||
setCc={setCc}
|
||||
bcc={bcc}
|
||||
setBcc={setBcc}
|
||||
subject={subject}
|
||||
setSubject={setSubject}
|
||||
emailContent={emailContent}
|
||||
setEmailContent={setEmailContent}
|
||||
showCc={showCc}
|
||||
setShowCc={setShowCc}
|
||||
showBcc={showBcc}
|
||||
setShowBcc={setShowBcc}
|
||||
attachments={attachments}
|
||||
onAttachmentAdd={handleAttachmentAdd}
|
||||
onAttachmentRemove={handleAttachmentRemove}
|
||||
/>
|
||||
<div className="fixed inset-0 bg-gray-600/30 backdrop-blur-sm z-50 flex items-center justify-center">
|
||||
<div className="w-full max-w-2xl h-[90vh] bg-white rounded-xl shadow-xl flex flex-col mx-4">
|
||||
{/* Modal Header */}
|
||||
<div className="flex-none flex items-center justify-between px-6 py-3 border-b border-gray-200">
|
||||
<h3 className="text-lg font-semibold text-gray-900">
|
||||
{type === 'reply' ? 'Reply' : type === 'forward' ? 'Forward' : type === 'reply-all' ? 'Reply All' : 'New Message'}
|
||||
</h3>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="hover:bg-gray-100 rounded-full"
|
||||
onClick={onClose}
|
||||
>
|
||||
<X className="h-5 w-5 text-gray-500" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Modal Body */}
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<div className="h-full flex flex-col p-6 space-y-4 overflow-y-auto">
|
||||
{/* To Field */}
|
||||
<div className="flex-none">
|
||||
<Label htmlFor="to" className="block text-sm font-medium text-gray-700">To</Label>
|
||||
<Input
|
||||
id="to"
|
||||
value={to}
|
||||
onChange={(e) => setTo(e.target.value)}
|
||||
placeholder="recipient@example.com"
|
||||
className="w-full mt-1 bg-white border-gray-300 text-gray-900"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* CC/BCC Toggle Buttons */}
|
||||
<div className="flex-none flex items-center gap-4">
|
||||
<button
|
||||
type="button"
|
||||
className="text-blue-600 hover:text-blue-700 text-sm font-medium"
|
||||
onClick={() => setShowCc(!showCc)}
|
||||
>
|
||||
{showCc ? 'Hide Cc' : 'Add Cc'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="text-blue-600 hover:text-blue-700 text-sm font-medium"
|
||||
onClick={() => setShowBcc(!showBcc)}
|
||||
>
|
||||
{showBcc ? 'Hide Bcc' : 'Add Bcc'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* CC Field */}
|
||||
{showCc && (
|
||||
<div className="flex-none">
|
||||
<Label htmlFor="cc" className="block text-sm font-medium text-gray-700">Cc</Label>
|
||||
<Input
|
||||
id="cc"
|
||||
value={cc}
|
||||
onChange={(e) => setCc(e.target.value)}
|
||||
placeholder="cc@example.com"
|
||||
className="w-full mt-1 bg-white border-gray-300 text-gray-900"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* BCC Field */}
|
||||
{showBcc && (
|
||||
<div className="flex-none">
|
||||
<Label htmlFor="bcc" className="block text-sm font-medium text-gray-700">Bcc</Label>
|
||||
<Input
|
||||
id="bcc"
|
||||
value={bcc}
|
||||
onChange={(e) => setBcc(e.target.value)}
|
||||
placeholder="bcc@example.com"
|
||||
className="w-full mt-1 bg-white border-gray-300 text-gray-900"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Subject Field */}
|
||||
<div className="flex-none">
|
||||
<Label htmlFor="subject" className="block text-sm font-medium text-gray-700">Subject</Label>
|
||||
<Input
|
||||
id="subject"
|
||||
value={subject}
|
||||
onChange={(e) => setSubject(e.target.value)}
|
||||
placeholder="Enter subject"
|
||||
className="w-full mt-1 bg-white border-gray-300 text-gray-900"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Message Body */}
|
||||
<div className="flex-1 min-h-[200px] flex flex-col">
|
||||
<Label htmlFor="message" className="flex-none block text-sm font-medium text-gray-700 mb-2">Message</Label>
|
||||
<Textarea
|
||||
value={emailContent}
|
||||
onChange={(e) => setEmailContent(e.target.value)}
|
||||
placeholder="Write your message here..."
|
||||
className="flex-1 w-full bg-white border border-gray-300 rounded-md p-4 text-black overflow-y-auto focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||||
style={{
|
||||
minHeight: '200px',
|
||||
maxHeight: 'calc(100vh - 400px)',
|
||||
resize: 'none'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Attachments */}
|
||||
{attachments.length > 0 && (
|
||||
<div className="border rounded-md p-3 mt-4">
|
||||
<h3 className="text-sm font-medium mb-2 text-gray-700">Attachments</h3>
|
||||
<div className="space-y-2">
|
||||
{attachments.map((file, index) => (
|
||||
<div key={index} className="flex items-center justify-between text-sm border rounded p-2">
|
||||
<span className="truncate max-w-[200px] text-gray-800">{file.name}</span>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => handleAttachmentRemove(index)}
|
||||
className="h-6 w-6 p-0 text-gray-500 hover:text-gray-700"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Modal Footer */}
|
||||
<div className="flex-none flex items-center justify-between px-6 py-3 border-t border-gray-200 bg-white">
|
||||
<div className="flex items-center gap-2">
|
||||
{/* File Input for Attachments */}
|
||||
<input
|
||||
type="file"
|
||||
id="file-attachment"
|
||||
className="hidden"
|
||||
multiple
|
||||
onChange={(e) => {
|
||||
if (e.target.files && e.target.files.length > 0) {
|
||||
handleAttachmentAdd(e.target.files);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="file-attachment">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="rounded-full bg-white hover:bg-gray-100 border-gray-300"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
document.getElementById('file-attachment')?.click();
|
||||
}}
|
||||
>
|
||||
<Paperclip className="h-4 w-4 text-gray-600" />
|
||||
</Button>
|
||||
</label>
|
||||
{sending && <span className="text-xs text-gray-500 ml-2">Preparing attachment...</span>}
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="text-gray-600 hover:text-gray-700 hover:bg-gray-100"
|
||||
onClick={onClose}
|
||||
disabled={sending}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
className="bg-blue-600 text-white hover:bg-blue-700"
|
||||
onClick={handleSend}
|
||||
disabled={sending}
|
||||
>
|
||||
{sending ? (
|
||||
<>
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
Sending...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<SendHorizontal className="mr-2 h-4 w-4" />
|
||||
Send
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ComposeEmailFooter
|
||||
sending={sending}
|
||||
onSend={handleSend}
|
||||
onCancel={onClose}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -369,47 +525,201 @@ function LegacyAdapter({
|
||||
if (!showCompose) return null;
|
||||
|
||||
return (
|
||||
<Card className="w-full max-w-3xl mx-auto flex flex-col min-h-[60vh] max-h-[80vh]">
|
||||
<ComposeEmailHeader
|
||||
type={determineType()}
|
||||
onClose={() => {
|
||||
if (onCancel) onCancel();
|
||||
setShowCompose(false);
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
<ComposeEmailForm
|
||||
to={composeTo}
|
||||
setTo={setComposeTo}
|
||||
cc={composeCc}
|
||||
setCc={setComposeCc}
|
||||
bcc={composeBcc}
|
||||
setBcc={setComposeBcc}
|
||||
subject={composeSubject}
|
||||
setSubject={setComposeSubject}
|
||||
emailContent={composeBody}
|
||||
setEmailContent={setComposeBody}
|
||||
showCc={showCc}
|
||||
setShowCc={setShowCc}
|
||||
showBcc={showBcc}
|
||||
setShowBcc={setShowBcc}
|
||||
attachments={convertAttachments()}
|
||||
onAttachmentAdd={handleFileSelection}
|
||||
onAttachmentRemove={(index) => {
|
||||
setAttachments(attachments.filter((_, i) => i !== index));
|
||||
}}
|
||||
/>
|
||||
<div className="fixed inset-0 bg-gray-600/30 backdrop-blur-sm z-50 flex items-center justify-center">
|
||||
<div className="w-full max-w-2xl h-[90vh] bg-white rounded-xl shadow-xl flex flex-col mx-4">
|
||||
{/* Modal Header */}
|
||||
<div className="flex-none flex items-center justify-between px-6 py-3 border-b border-gray-200">
|
||||
<h3 className="text-lg font-semibold text-gray-900">
|
||||
{determineType() === 'reply' ? 'Reply' : determineType() === 'forward' ? 'Forward' : determineType() === 'reply-all' ? 'Reply All' : 'New Message'}
|
||||
</h3>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="hover:bg-gray-100 rounded-full"
|
||||
onClick={() => {
|
||||
if (onCancel) onCancel();
|
||||
setShowCompose(false);
|
||||
}}
|
||||
>
|
||||
<X className="h-5 w-5 text-gray-500" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Modal Body */}
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<div className="h-full flex flex-col p-6 space-y-4 overflow-y-auto">
|
||||
{/* To Field */}
|
||||
<div className="flex-none">
|
||||
<Label htmlFor="to" className="block text-sm font-medium text-gray-700">To</Label>
|
||||
<Input
|
||||
id="to"
|
||||
value={composeTo}
|
||||
onChange={(e) => setComposeTo(e.target.value)}
|
||||
placeholder="recipient@example.com"
|
||||
className="w-full mt-1 bg-white border-gray-300 text-gray-900"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* CC/BCC Toggle Buttons */}
|
||||
<div className="flex-none flex items-center gap-4">
|
||||
<button
|
||||
type="button"
|
||||
className="text-blue-600 hover:text-blue-700 text-sm font-medium"
|
||||
onClick={() => setShowCc(!showCc)}
|
||||
>
|
||||
{showCc ? 'Hide Cc' : 'Add Cc'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="text-blue-600 hover:text-blue-700 text-sm font-medium"
|
||||
onClick={() => setShowBcc(!showBcc)}
|
||||
>
|
||||
{showBcc ? 'Hide Bcc' : 'Add Bcc'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* CC Field */}
|
||||
{showCc && (
|
||||
<div className="flex-none">
|
||||
<Label htmlFor="cc" className="block text-sm font-medium text-gray-700">Cc</Label>
|
||||
<Input
|
||||
id="cc"
|
||||
value={composeCc}
|
||||
onChange={(e) => setComposeCc(e.target.value)}
|
||||
placeholder="cc@example.com"
|
||||
className="w-full mt-1 bg-white border-gray-300 text-gray-900"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* BCC Field */}
|
||||
{showBcc && (
|
||||
<div className="flex-none">
|
||||
<Label htmlFor="bcc" className="block text-sm font-medium text-gray-700">Bcc</Label>
|
||||
<Input
|
||||
id="bcc"
|
||||
value={composeBcc}
|
||||
onChange={(e) => setComposeBcc(e.target.value)}
|
||||
placeholder="bcc@example.com"
|
||||
className="w-full mt-1 bg-white border-gray-300 text-gray-900"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Subject Field */}
|
||||
<div className="flex-none">
|
||||
<Label htmlFor="subject" className="block text-sm font-medium text-gray-700">Subject</Label>
|
||||
<Input
|
||||
id="subject"
|
||||
value={composeSubject}
|
||||
onChange={(e) => setComposeSubject(e.target.value)}
|
||||
placeholder="Enter subject"
|
||||
className="w-full mt-1 bg-white border-gray-300 text-gray-900"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Message Body */}
|
||||
<div className="flex-1 min-h-[200px] flex flex-col">
|
||||
<Label htmlFor="message" className="flex-none block text-sm font-medium text-gray-700 mb-2">Message</Label>
|
||||
<Textarea
|
||||
value={composeBody}
|
||||
onChange={(e) => setComposeBody(e.target.value)}
|
||||
placeholder="Write your message here..."
|
||||
className="flex-1 w-full bg-white border border-gray-300 rounded-md p-4 text-black overflow-y-auto focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||||
style={{
|
||||
minHeight: '200px',
|
||||
maxHeight: 'calc(100vh - 400px)',
|
||||
resize: 'none'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Attachments */}
|
||||
{attachments.length > 0 && (
|
||||
<div className="border rounded-md p-3 mt-4">
|
||||
<h3 className="text-sm font-medium mb-2 text-gray-700">Attachments</h3>
|
||||
<div className="space-y-2">
|
||||
{attachments.map((file, index) => (
|
||||
<div key={index} className="flex items-center justify-between text-sm border rounded p-2">
|
||||
<span className="truncate max-w-[200px] text-gray-800">{file.name || file.filename}</span>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setAttachments(attachments.filter((_, i) => i !== index))}
|
||||
className="h-6 w-6 p-0 text-gray-500 hover:text-gray-700"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Modal Footer */}
|
||||
<div className="flex-none flex items-center justify-between px-6 py-3 border-t border-gray-200 bg-white">
|
||||
<div className="flex items-center gap-2">
|
||||
{/* File Input for Attachments */}
|
||||
<input
|
||||
type="file"
|
||||
id="file-attachment-legacy"
|
||||
className="hidden"
|
||||
multiple
|
||||
onChange={(e) => {
|
||||
if (e.target.files && e.target.files.length > 0) {
|
||||
handleFileSelection(e.target.files);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="file-attachment-legacy">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="rounded-full bg-white hover:bg-gray-100 border-gray-300"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
document.getElementById('file-attachment-legacy')?.click();
|
||||
}}
|
||||
>
|
||||
<Paperclip className="h-4 w-4 text-gray-600" />
|
||||
</Button>
|
||||
</label>
|
||||
{sending && <span className="text-xs text-gray-500 ml-2">Preparing attachment...</span>}
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="text-gray-600 hover:text-gray-700 hover:bg-gray-100"
|
||||
onClick={() => {
|
||||
if (onCancel) onCancel();
|
||||
setShowCompose(false);
|
||||
}}
|
||||
disabled={sending}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
className="bg-blue-600 text-white hover:bg-blue-700"
|
||||
onClick={handleLegacySend}
|
||||
disabled={sending}
|
||||
>
|
||||
{sending ? (
|
||||
<>
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
Sending...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<SendHorizontal className="mr-2 h-4 w-4" />
|
||||
Send
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ComposeEmailFooter
|
||||
sending={sending}
|
||||
onSend={handleLegacySend}
|
||||
onCancel={() => {
|
||||
if (onCancel) onCancel();
|
||||
setShowCompose(false);
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user