feat: mail grouping with group headers in MailList, connected to GroupPanel

This commit is contained in:
Agent Zero
2026-07-30 15:05:37 +02:00
parent 78724ce8f1
commit 679c6abc6d
2 changed files with 46 additions and 4 deletions
+37 -3
View File
@@ -9,11 +9,13 @@ import { useTranslation } from 'react-i18next';
import type { Mail } from '@/api/mail';
import { decodeMimeHeader } from '@/api/mail';
import { EmptyState } from '@/components/ui/EmptyState';
import { Loader2, Paperclip, Star } from 'lucide-react';
import { Loader2, Paperclip, Star, ChevronDown } from 'lucide-react';
import { formatSmartDate } from '@/utils/date';
import type { GroupedMails } from '@/components/mail/MailGroupPanel';
export interface MailListProps {
mails: Mail[];
groupedMails?: GroupedMails[] | null;
selectedMailId: string | null;
onSelectMail: (mail: Mail) => void;
loading: boolean;
@@ -30,6 +32,7 @@ function formatDate(dateStr: string): string {
export function MailList({
mails,
groupedMails,
selectedMailId,
onSelectMail,
loading,
@@ -161,7 +164,7 @@ export function MailList({
{loading && mails.length > 0 && <Loader2 className="w-4 h-4 animate-spin text-secondary-400" />}
</div>
{/* Mail list — infinite scroll, no pagination */}
{/* Mail list — infinite scroll with optional grouping */}
<ul
ref={scrollRef}
className="divide-y divide-secondary-100 flex-1 overflow-y-auto"
@@ -169,7 +172,38 @@ export function MailList({
data-testid="mail-list-items"
onScroll={handleScroll}
>
{mails.map((mail) => renderMailItem(mail))}
{groupedMails && groupedMails.length > 0 ? (
groupedMails.map((group) => (
<li key={group.key} className="bg-secondary-50/50">
<div className="flex items-center gap-2 px-3 py-2 bg-secondary-100/70 sticky top-0 z-10">
<ChevronDown className="w-3.5 h-3.5 text-secondary-500" />
<span className="text-xs font-semibold text-secondary-700">{group.label}</span>
<span className="text-[10px] text-secondary-400">({group.mails.length})</span>
</div>
<ul className="divide-y divide-secondary-100">
{group.mails.map((mail) => renderMailItem(mail))}
</ul>
{group.subGroups && group.subGroups.length > 0 && (
<ul className="pl-3 border-l border-secondary-200">
{group.subGroups.map((sub) => (
<li key={sub.key}>
<div className="flex items-center gap-2 px-3 py-1.5 bg-secondary-50">
<ChevronDown className="w-3 h-3 text-secondary-400" />
<span className="text-[11px] font-medium text-secondary-600">{sub.label}</span>
<span className="text-[10px] text-secondary-400">({sub.mails.length})</span>
</div>
<ul className="divide-y divide-secondary-100">
{sub.mails.map((mail) => renderMailItem(mail))}
</ul>
</li>
))}
</ul>
)}
</li>
))
) : (
mails.map((mail) => renderMailItem(mail))
)}
{loading && mails.length > 0 && (
<li className="flex items-center justify-center py-4">
<Loader2 className="w-5 h-5 animate-spin text-secondary-400" />