diff --git a/app/mail/page.tsx b/app/mail/page.tsx index 245ef84..0e141f4 100644 --- a/app/mail/page.tsx +++ b/app/mail/page.tsx @@ -1,163 +1,633 @@ 'use client'; -import { Card } from "@/components/ui/card" -import { Button } from "@/components/ui/button" -import { Input } from "@/components/ui/input" -import { Mail, Search, Star, Inbox, Send, Archive, Trash, AlertCircle } from "lucide-react" -import { useState, useEffect } from "react" +import { useState, useEffect } from 'react'; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; +import { Checkbox } from "@/components/ui/checkbox"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; +import { Avatar, AvatarFallback } from "@/components/ui/avatar"; +import { MoreVertical, Settings, Plus as PlusIcon, Trash2, Edit, Mail, Inbox, Send, Star, Trash, Plus, ChevronLeft, ChevronRight, Search, ChevronDown, Folder, ChevronUp, Reply, Forward, ReplyAll, MoreHorizontal, FolderOpen, X } from 'lucide-react'; +import { Label } from "@/components/ui/label"; +import { Paperclip, Copy, EyeOff } from 'lucide-react'; + +interface Account { + id: number; + name: string; + email: string; + color: string; +} interface Email { - id: number - accountId: number - from: string - fromName: string - to: string - subject: string - body: string - date: string - read: boolean - starred: boolean - category: string + id: number; + accountId: number; + from: string; + fromName: string; + to: string; + subject: string; + body: string; + date: string; + read: boolean; + starred: boolean; + category: string; +} + +interface Mailbox { + name: string; + path: string; + children?: Mailbox[]; } export default function MailPage() { - const [emails, setEmails] = useState([]) - const [loading, setLoading] = useState(true) - const [error, setError] = useState(null) + // Single account for now since we're using IMAP + const [accounts] = useState([ + { id: 1, name: 'Work', email: 'contact@governance-labs.org', color: 'bg-blue-500' } + ]); + const [emails, setEmails] = useState([]); + const [mailboxes, setMailboxes] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + const [selectedAccount] = useState(1); // Only one account for now + const [currentView, setCurrentView] = useState('inbox'); + const [selectedEmail, setSelectedEmail] = useState(null); + const [sidebarOpen, setSidebarOpen] = useState(true); + const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false); + const [composeOpen, setComposeOpen] = useState(false); + const [accountsDropdownOpen, setAccountsDropdownOpen] = useState(false); + const [foldersDropdownOpen, setFoldersDropdownOpen] = useState(false); + const [showAccountActions, setShowAccountActions] = useState(null); + const [showEmailActions, setShowEmailActions] = useState(false); + const [selectedEmails, setSelectedEmails] = useState([]); + const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); + const [deleteType, setDeleteType] = useState<'email' | 'emails' | 'account'>('email'); + const [itemToDelete, setItemToDelete] = useState(null); + const [showBulkActions, setShowBulkActions] = useState(false); + const [showCc, setShowCc] = useState(false); + const [showBcc, setShowBcc] = useState(false); + + // Fetch emails from IMAP API useEffect(() => { async function fetchEmails() { try { - setError(null) - const res = await fetch('/api/mail') + setError(null); + setLoading(true); + const res = await fetch('/api/mail'); if (!res.ok) { - const errorData = await res.json().catch(() => ({})) - throw new Error(errorData.error || 'Failed to fetch emails') + throw new Error('Failed to fetch emails'); } - const data = await res.json() - setEmails(data.messages || []) + const data = await res.json(); + if (data.error) { + throw new Error(data.error); + } + setEmails(data.messages || []); } catch (error) { - console.error('Error fetching emails:', error) - setError('Unable to connect to mail server. Please try again later.') - setEmails([]) + console.error('Error fetching emails:', error); + setError('Unable to load emails. Please try again later.'); } finally { - setLoading(false) + setLoading(false); } } - fetchEmails() - }, []) + fetchEmails(); + }, [currentView]); // Refetch when view changes + + // Fetch mailboxes from IMAP API + useEffect(() => { + async function fetchMailboxes() { + try { + const res = await fetch('/api/mail', { method: 'POST' }); + if (!res.ok) { + throw new Error('Failed to fetch mailboxes'); + } + const data = await res.json(); + if (data.error) { + throw new Error(data.error); + } + setMailboxes(data.mailboxes || []); + } catch (error) { + console.error('Error fetching mailboxes:', error); + } + } + + fetchMailboxes(); + }, []); + + // Filter emails based on current view + const filteredEmails = emails.filter(email => + currentView === 'starred' ? email.starred : email.category === currentView + ); + + // Handle email selection + const handleEmailClick = (emailId: number) => { + const updatedEmails = emails.map(email => + email.id === emailId ? { ...email, read: true } : email + ); + setEmails(updatedEmails); + setSelectedEmail(emailId); + }; + + // Toggle starred status + const toggleStarred = async (emailId: number, e: React.MouseEvent) => { + e.stopPropagation(); + // TODO: Implement IMAP flag toggle + const updatedEmails = emails.map(email => + email.id === emailId ? { ...email, starred: !email.starred } : email + ); + setEmails(updatedEmails); + }; + + // Format date for display + const formatDate = (dateString: string) => { + const date = new Date(dateString); + const now = new Date(); + + if (date.toDateString() === now.toDateString()) { + return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); + } else { + return date.toLocaleDateString([], { month: 'short', day: 'numeric' }); + } + }; + + // Get selected email + const getSelectedEmail = () => { + return emails.find(email => email.id === selectedEmail); + }; + + // Get account color + const getAccountColor = (accountId: number) => { + const account = accounts.find(acc => acc.id === accountId); + return account ? account.color : 'bg-gray-500'; + }; + + // Handle bulk selection + const toggleEmailSelection = (emailId: number, e: React.MouseEvent) => { + e.stopPropagation(); + setSelectedEmails(prev => + prev.includes(emailId) + ? prev.filter(id => id !== emailId) + : [...prev, emailId] + ); + setShowBulkActions(true); + }; + + // Handle select all + const toggleSelectAll = () => { + if (selectedEmails.length === filteredEmails.length) { + setSelectedEmails([]); + setShowBulkActions(false); + } else { + setSelectedEmails(filteredEmails.map(email => email.id)); + setShowBulkActions(true); + } + }; + + // Handle bulk delete + const handleBulkDelete = () => { + setDeleteType('emails'); + setShowDeleteConfirm(true); + }; + + // Handle delete confirmation + const handleDeleteConfirm = async () => { + // TODO: Implement IMAP delete + if (deleteType === 'email' && itemToDelete) { + setEmails(emails.filter(email => email.id !== itemToDelete)); + setSelectedEmail(null); + } else if (deleteType === 'emails') { + setEmails(emails.filter(email => !selectedEmails.includes(email.id))); + setSelectedEmails([]); + setShowBulkActions(false); + } + setShowDeleteConfirm(false); + }; + + // Modified account action handler + const handleAccountAction = (accountId: number, action: 'edit' | 'delete') => { + setShowAccountActions(null); + if (action === 'delete') { + setDeleteType('account'); + setItemToDelete(accountId); + setShowDeleteConfirm(true); + } + }; + + if (loading) { + return ( +
+
+ +

Loading your emails...

+
+
+ ); + } + + if (error) { + return ( +
+
+ +

{error}

+ +
+
+ ); + } return ( -
+
{/* Sidebar */} -
- - -
+ + {/* Compose button */} +
+ +
+ + {/* Navigation */} + + + {/* Account info */} +
+
+ + + {accounts[0].email.charAt(0).toUpperCase()} + + + {sidebarOpen && ( +
+

+ {accounts[0].name} +

+

+ {accounts[0].email} +

+
+ )} +
+
- {/* Main Content */} -
-
-
-
-
- + {/* Main content */} +
+ {/* Header */} +
+
+ +
+
+
+
-
- {loading ? ( -
-

Loading emails...

-
- ) : error ? ( -
- -

{error}

- + {/* Email list */} +
+ {filteredEmails.length > 0 ? ( +
+ {filteredEmails.map((email) => ( +
handleEmailClick(email.id)} + > + toggleEmailSelection(email.id, e)} + className="h-4 w-4" + /> + +
+
+
+ {email.fromName} +
+
+ {email.subject} + {formatDate(email.date)} +
+
+
+ ))}
) : ( -
- {emails.length === 0 ? ( -
- -

No emails found

-
- ) : ( - emails.map((email) => ( - -
- -
-
-

{email.fromName}

- - {new Date(email.date).toLocaleDateString()} - -
-

{email.subject}

-

{email.body}

-
-
-
- )) - )} +
+ +

No emails in this folder

)} -
+
+ + {/* Compose email modal */} + {composeOpen && ( +
+ + + New Message + + + +
+
+ + +
+
+
+ +
+ {!showCc && ( + + )} + {!showBcc && ( + + )} +
+
+ +
+ {showCc && ( +
+
+ + +
+ +
+ )} + {showBcc && ( +
+
+ + +
+ +
+ )} +
+ + +
+
+ +
+ +