mail page ui correction maj compose 20 bis 13
This commit is contained in:
parent
ca3d1d832b
commit
a224c152b6
@ -116,6 +116,7 @@ export async function GET() {
|
|||||||
const allEmails: Email[] = [];
|
const allEmails: Email[] = [];
|
||||||
|
|
||||||
imap.once('ready', () => {
|
imap.once('ready', () => {
|
||||||
|
// Get all mailboxes first
|
||||||
imap.getBoxes((err, boxes) => {
|
imap.getBoxes((err, boxes) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error('Error getting mailboxes:', err);
|
console.error('Error getting mailboxes:', err);
|
||||||
@ -124,21 +125,17 @@ export async function GET() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Available mailboxes:', Object.keys(boxes));
|
// Get the list of available mailboxes
|
||||||
|
const availableMailboxes = Object.keys(boxes);
|
||||||
|
console.log('Available mailboxes:', availableMailboxes);
|
||||||
|
|
||||||
// Map of folder names to their types
|
// These are the folders we want to process based on your IMAP structure
|
||||||
const folderMap = {
|
const foldersToCheck = ['INBOX', 'Sent', 'Trash', 'Spam', 'Drafts', 'Archives', 'Archive'];
|
||||||
'INBOX': 'inbox',
|
|
||||||
'Sent': 'sent',
|
|
||||||
'Trash': 'trash',
|
|
||||||
'Drafts': 'drafts'
|
|
||||||
};
|
|
||||||
|
|
||||||
const foldersToCheck = Object.keys(folderMap);
|
|
||||||
let foldersProcessed = 0;
|
let foldersProcessed = 0;
|
||||||
|
|
||||||
const processFolder = (folderName: string) => {
|
const processFolder = (folderName: string) => {
|
||||||
console.log(`Opening folder: ${folderName}`);
|
console.log(`Processing folder: ${folderName}`);
|
||||||
|
|
||||||
imap.openBox(folderName, false, (err, box) => {
|
imap.openBox(folderName, false, (err, box) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(`Error opening ${folderName}:`, err);
|
console.error(`Error opening ${folderName}:`, err);
|
||||||
@ -149,7 +146,7 @@ export async function GET() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Successfully opened ${folderName}, messages:`, box.messages.total);
|
console.log(`Successfully opened ${folderName}, total messages: ${box.messages.total}`);
|
||||||
|
|
||||||
if (box.messages.total === 0) {
|
if (box.messages.total === 0) {
|
||||||
foldersProcessed++;
|
foldersProcessed++;
|
||||||
@ -160,14 +157,7 @@ export async function GET() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Search for all messages in the folder
|
// Search for all messages in the folder
|
||||||
const searchCriteria = ['ALL'];
|
imap.search(['ALL'], (err, results) => {
|
||||||
|
|
||||||
// For INBOX, exclude drafts
|
|
||||||
if (folderName === 'INBOX') {
|
|
||||||
searchCriteria.push(['!KEYWORD', '\\Draft']);
|
|
||||||
}
|
|
||||||
|
|
||||||
imap.search(searchCriteria, (err, results) => {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(`Search error in ${folderName}:`, err);
|
console.error(`Search error in ${folderName}:`, err);
|
||||||
foldersProcessed++;
|
foldersProcessed++;
|
||||||
@ -177,7 +167,7 @@ export async function GET() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort results to get the most recent messages first
|
// Get the most recent messages (up to 20)
|
||||||
const messageNumbers = results
|
const messageNumbers = results
|
||||||
.sort((a, b) => b - a)
|
.sort((a, b) => b - a)
|
||||||
.slice(0, 20);
|
.slice(0, 20);
|
||||||
@ -205,8 +195,7 @@ export async function GET() {
|
|||||||
starred: false,
|
starred: false,
|
||||||
body: '',
|
body: '',
|
||||||
to: '',
|
to: '',
|
||||||
folder: folderName, // Keep the original folder name
|
folder: folderName // Keep exact folder name
|
||||||
draft: false
|
|
||||||
};
|
};
|
||||||
|
|
||||||
msg.on('body', (stream, info) => {
|
msg.on('body', (stream, info) => {
|
||||||
@ -231,16 +220,10 @@ export async function GET() {
|
|||||||
email.id = attrs.uid;
|
email.id = attrs.uid;
|
||||||
email.read = attrs.flags?.includes('\\Seen') || false;
|
email.read = attrs.flags?.includes('\\Seen') || false;
|
||||||
email.starred = attrs.flags?.includes('\\Flagged') || false;
|
email.starred = attrs.flags?.includes('\\Flagged') || false;
|
||||||
email.draft = attrs.flags?.includes('\\Draft') || false;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
msg.once('end', () => {
|
msg.once('end', () => {
|
||||||
// Only add the email if it belongs in this folder
|
|
||||||
if (folderName === 'INBOX' && !email.draft) {
|
|
||||||
allEmails.push(email);
|
allEmails.push(email);
|
||||||
} else if (folderName !== 'INBOX') {
|
|
||||||
allEmails.push(email);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -249,7 +232,7 @@ export async function GET() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
f.once('end', () => {
|
f.once('end', () => {
|
||||||
console.log(`Finished processing ${folderName}`);
|
console.log(`Finished processing ${folderName}, emails found: ${allEmails.length}`);
|
||||||
foldersProcessed++;
|
foldersProcessed++;
|
||||||
if (foldersProcessed === foldersToCheck.length) {
|
if (foldersProcessed === foldersToCheck.length) {
|
||||||
finishProcessing();
|
finishProcessing();
|
||||||
@ -259,16 +242,13 @@ export async function GET() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Process folders sequentially
|
// Process each folder sequentially
|
||||||
foldersToCheck.forEach(folder => processFolder(folder));
|
foldersToCheck.forEach(folder => processFolder(folder));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function finishProcessing() {
|
function finishProcessing() {
|
||||||
console.log('All folders processed, total emails:', allEmails.length);
|
console.log('All folders processed, total emails:', allEmails.length);
|
||||||
// Sort all emails by date
|
|
||||||
allEmails.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
|
|
||||||
|
|
||||||
const response = {
|
const response = {
|
||||||
emails: allEmails,
|
emails: allEmails,
|
||||||
mailUrl: process.env.NEXTCLOUD_URL ? `${process.env.NEXTCLOUD_URL}/apps/mail/` : null
|
mailUrl: process.env.NEXTCLOUD_URL ? `${process.env.NEXTCLOUD_URL}/apps/mail/` : null
|
||||||
|
|||||||
@ -23,7 +23,8 @@ import {
|
|||||||
MoreVertical, Settings, Plus as PlusIcon, Trash2, Edit, Mail,
|
MoreVertical, Settings, Plus as PlusIcon, Trash2, Edit, Mail,
|
||||||
Inbox, Send, Star, Trash, Plus, ChevronLeft, ChevronRight,
|
Inbox, Send, Star, Trash, Plus, ChevronLeft, ChevronRight,
|
||||||
Search, ChevronDown, Folder, ChevronUp, Reply, Forward, ReplyAll,
|
Search, ChevronDown, Folder, ChevronUp, Reply, Forward, ReplyAll,
|
||||||
MoreHorizontal, FolderOpen, X, Paperclip, MessageSquare, Copy, EyeOff
|
MoreHorizontal, FolderOpen, X, Paperclip, MessageSquare, Copy, EyeOff,
|
||||||
|
AlertOctagon, Archive
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||||
|
|
||||||
@ -47,7 +48,7 @@ interface Email {
|
|||||||
read?: boolean;
|
read?: boolean;
|
||||||
starred?: boolean;
|
starred?: boolean;
|
||||||
deleted?: boolean;
|
deleted?: boolean;
|
||||||
category?: 'inbox' | 'sent' | 'trash';
|
category?: 'inbox' | 'sent' | 'trash' | 'spam' | 'drafts' | 'archives' | 'archive';
|
||||||
cc?: string;
|
cc?: string;
|
||||||
bcc?: string;
|
bcc?: string;
|
||||||
flags?: string[];
|
flags?: string[];
|
||||||
@ -429,6 +430,55 @@ function cleanEmailContent(content: string): string {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add a type for the available views
|
||||||
|
type MailView = 'inbox' | 'sent' | 'trash' | 'spam' | 'drafts' | 'archives' | 'archive' | 'starred';
|
||||||
|
|
||||||
|
// Update the sidebar navigation
|
||||||
|
const sidebarNavItems = [
|
||||||
|
{
|
||||||
|
view: 'inbox' as MailView,
|
||||||
|
label: 'Inbox',
|
||||||
|
icon: Inbox,
|
||||||
|
folder: 'INBOX'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
view: 'starred' as MailView,
|
||||||
|
label: 'Starred',
|
||||||
|
icon: Star,
|
||||||
|
folder: null // This is handled by the starred flag
|
||||||
|
},
|
||||||
|
{
|
||||||
|
view: 'sent' as MailView,
|
||||||
|
label: 'Sent',
|
||||||
|
icon: Send,
|
||||||
|
folder: 'Sent'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
view: 'drafts' as MailView,
|
||||||
|
label: 'Drafts',
|
||||||
|
icon: Edit,
|
||||||
|
folder: 'Drafts'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
view: 'spam' as MailView,
|
||||||
|
label: 'Spam',
|
||||||
|
icon: AlertOctagon,
|
||||||
|
folder: 'Spam'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
view: 'trash' as MailView,
|
||||||
|
label: 'Trash',
|
||||||
|
icon: Trash,
|
||||||
|
folder: 'Trash'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
view: 'archives' as MailView,
|
||||||
|
label: 'Archives',
|
||||||
|
icon: Archive,
|
||||||
|
folder: 'Archives'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
export default function MailPage() {
|
export default function MailPage() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
@ -437,7 +487,7 @@ export default function MailPage() {
|
|||||||
{ id: 1, name: 'Mail', email: 'alma@governance-labs.org', color: 'bg-blue-500' }
|
{ id: 1, name: 'Mail', email: 'alma@governance-labs.org', color: 'bg-blue-500' }
|
||||||
]);
|
]);
|
||||||
const [selectedAccount, setSelectedAccount] = useState<Account | null>(null);
|
const [selectedAccount, setSelectedAccount] = useState<Account | null>(null);
|
||||||
const [currentView, setCurrentView] = useState('inbox');
|
const [currentView, setCurrentView] = useState<MailView>('inbox');
|
||||||
const [showCompose, setShowCompose] = useState(false);
|
const [showCompose, setShowCompose] = useState(false);
|
||||||
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
||||||
const [selectedEmails, setSelectedEmails] = useState<string[]>([]);
|
const [selectedEmails, setSelectedEmails] = useState<string[]>([]);
|
||||||
@ -476,27 +526,28 @@ export default function MailPage() {
|
|||||||
console.log('Selected account:', selectedAccount);
|
console.log('Selected account:', selectedAccount);
|
||||||
}, [emails, currentView, selectedAccount]);
|
}, [emails, currentView, selectedAccount]);
|
||||||
|
|
||||||
// Update the filteredEmails logic
|
// Update the filteredEmails logic to match exactly with the folders
|
||||||
const filteredEmails = useMemo(() => {
|
const filteredEmails = useMemo(() => {
|
||||||
console.log('Filtering emails:', {
|
console.log('Filtering emails:', {
|
||||||
total: emails.length,
|
total: emails.length,
|
||||||
folders: emails.map(e => e.folder),
|
byFolder: emails.reduce((acc, email) => {
|
||||||
|
acc[email.folder] = (acc[email.folder] || 0) + 1;
|
||||||
|
return acc;
|
||||||
|
}, {} as Record<string, number>),
|
||||||
currentView
|
currentView
|
||||||
});
|
});
|
||||||
|
|
||||||
return emails.filter(email => {
|
// Find the current nav item
|
||||||
switch (currentView) {
|
const currentNavItem = sidebarNavItems.find(item => item.view === currentView);
|
||||||
case 'inbox':
|
|
||||||
return email.folder === 'INBOX' && !email.draft;
|
// If it's starred view, filter by starred flag
|
||||||
case 'starred':
|
if (currentView === 'starred') {
|
||||||
return Boolean(email.starred);
|
return emails.filter(email => email.starred);
|
||||||
case 'sent':
|
|
||||||
return email.folder === 'Sent';
|
|
||||||
case 'trash':
|
|
||||||
return email.folder === 'Trash';
|
|
||||||
default:
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Otherwise filter by folder
|
||||||
|
return emails.filter(email => {
|
||||||
|
return email.folder === currentNavItem?.folder;
|
||||||
});
|
});
|
||||||
}, [emails, currentView]);
|
}, [emails, currentView]);
|
||||||
|
|
||||||
@ -968,6 +1019,40 @@ export default function MailPage() {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Render the sidebar navigation
|
||||||
|
const renderSidebarNav = () => (
|
||||||
|
<nav className="p-3">
|
||||||
|
<ul className="space-y-0.5 px-2">
|
||||||
|
{sidebarNavItems.map((item) => (
|
||||||
|
<li key={item.view}>
|
||||||
|
<Button
|
||||||
|
variant={currentView === item.view ? 'secondary' : 'ghost'}
|
||||||
|
className={`w-full justify-start py-2 ${
|
||||||
|
currentView === item.view ? 'bg-gray-100 text-gray-900' : 'text-gray-600 hover:text-gray-900'
|
||||||
|
}`}
|
||||||
|
onClick={() => {
|
||||||
|
setCurrentView(item.view);
|
||||||
|
setSelectedEmail(null);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between w-full">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<item.icon className="h-4 w-4 mr-2" />
|
||||||
|
<span>{item.label}</span>
|
||||||
|
</div>
|
||||||
|
{item.view === 'inbox' && unreadCount > 0 && (
|
||||||
|
<span className="ml-auto bg-blue-600 text-white text-xs px-2 py-0.5 rounded-full">
|
||||||
|
{unreadCount}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-[calc(100vh-theme(spacing.12))] items-center justify-center bg-gray-100 mt-12">
|
<div className="flex h-[calc(100vh-theme(spacing.12))] items-center justify-center bg-gray-100 mt-12">
|
||||||
@ -1058,67 +1143,7 @@ export default function MailPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Navigation */}
|
{/* Navigation */}
|
||||||
<nav className="p-3">
|
{renderSidebarNav()}
|
||||||
<ul className="space-y-0.5 px-2">
|
|
||||||
<li>
|
|
||||||
<Button
|
|
||||||
variant={currentView === 'inbox' ? 'secondary' : 'ghost'}
|
|
||||||
className={`w-full justify-start py-2 ${
|
|
||||||
currentView === 'inbox' ? 'bg-gray-100 text-gray-900' : 'text-gray-600 hover:text-gray-900'
|
|
||||||
}`}
|
|
||||||
onClick={() => {setCurrentView('inbox'); setSelectedEmail(null);}}
|
|
||||||
>
|
|
||||||
<div className="flex items-center justify-between w-full">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Inbox className="h-4 w-4 mr-2" />
|
|
||||||
<span>Inbox</span>
|
|
||||||
</div>
|
|
||||||
{unreadCount > 0 && (
|
|
||||||
<span className="ml-auto bg-blue-600 text-white text-xs px-2 py-0.5 rounded-full">
|
|
||||||
{unreadCount}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Button>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Button
|
|
||||||
variant={currentView === 'starred' ? 'secondary' : 'ghost'}
|
|
||||||
className={`w-full justify-start py-2 ${
|
|
||||||
currentView === 'starred' ? 'bg-gray-100 text-gray-900' : 'text-gray-600 hover:text-gray-900'
|
|
||||||
}`}
|
|
||||||
onClick={() => {setCurrentView('starred'); setSelectedEmail(null);}}
|
|
||||||
>
|
|
||||||
<Star className="h-4 w-4 mr-2" />
|
|
||||||
<span>Starred</span>
|
|
||||||
</Button>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Button
|
|
||||||
variant={currentView === 'sent' ? 'secondary' : 'ghost'}
|
|
||||||
className={`w-full justify-start py-2 ${
|
|
||||||
currentView === 'sent' ? 'bg-gray-100 text-gray-900' : 'text-gray-600 hover:text-gray-900'
|
|
||||||
}`}
|
|
||||||
onClick={() => {setCurrentView('sent'); setSelectedEmail(null);}}
|
|
||||||
>
|
|
||||||
<Send className="h-4 w-4 mr-2" />
|
|
||||||
<span>Sent</span>
|
|
||||||
</Button>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Button
|
|
||||||
variant={currentView === 'trash' ? 'secondary' : 'ghost'}
|
|
||||||
className={`w-full justify-start py-2 ${
|
|
||||||
currentView === 'trash' ? 'bg-gray-100 text-gray-900' : 'text-gray-600 hover:text-gray-900'
|
|
||||||
}`}
|
|
||||||
onClick={() => {setCurrentView('trash'); setSelectedEmail(null);}}
|
|
||||||
>
|
|
||||||
<Trash className="h-4 w-4 mr-2" />
|
|
||||||
<span>Trash</span>
|
|
||||||
</Button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Main content area */}
|
{/* Main content area */}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user