This commit is contained in:
2026-02-27 08:27:43 +00:00
parent 7bf55af5b3
commit bc4c20929c
48 changed files with 4344 additions and 275 deletions
@@ -1,7 +1,7 @@
import { useEffect, useRef, useState } from 'react';
import { Link } from 'react-router';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { ChevronLeft, ChevronRight, Loader2, Mail, Paperclip, RefreshCw } from 'lucide-react';
import { ChevronLeft, ChevronRight, Inbox, Loader2, Mail, Paperclip, RefreshCw, Send, ShieldAlert, Trash2 } from 'lucide-react';
import { toast } from 'sonner';
import { Button } from '@/components/ui/button';
import { useClient } from 'hooks/useClient';
@@ -18,6 +18,13 @@ type GoogleStatus = {
const LIMIT = 50;
const FOLDERS = [
{ key: 'inbox', label: 'Inbox', icon: Inbox },
{ key: 'sent', label: 'Sent', icon: Send },
{ key: 'spam', label: 'Spam', icon: ShieldAlert },
{ key: 'trash', label: 'Trash', icon: Trash2 },
] as const;
const formatDate = (iso: string) => {
const date = new Date(iso);
const now = new Date();
@@ -32,6 +39,7 @@ export const EmailList = () => {
const client = useClient();
const queryClient = useQueryClient();
const [selectedId, setSelectedId] = useGlobal<string | null>('EMAIL_SELECTED', null);
const [folder, setFolder] = useGlobal<string>('EMAIL_FOLDER', 'inbox');
const [page, setPage] = useState(1);
const { jobs, createJob } = useJobs({ type: 'gmail-sync' });
const isSyncing = jobs.some((j) => j.status === 'queued' || j.status === 'running');
@@ -42,11 +50,16 @@ export const EmailList = () => {
});
const { data, isLoading } = useQuery({
queryKey: ['email-messages', page],
queryKey: ['email-messages', page, folder],
queryFn: () =>
client.get<{ messages: EmailSummary[]; total: number }>(`/email/messages?page=${page}&limit=${LIMIT}`),
client.get<{ messages: EmailSummary[]; total: number }>(`/email/messages?page=${page}&limit=${LIMIT}&folder=${folder}`),
});
const handleFolderChange = (newFolder: string) => {
setFolder(newFolder);
setPage(1);
};
const handleSync = async () => {
try {
await createJob({ lane: 'google-api', type: 'gmail-sync', notify: false });
@@ -106,8 +119,25 @@ export const EmailList = () => {
return (
<div className="flex h-full flex-col overflow-y-auto">
<div className="flex items-center gap-2 border-b px-3 py-2">
<Mail className="h-4 w-4 opacity-60" />
<span className="text-sm font-medium">Inbox</span>
<div className="flex items-center gap-1">
{FOLDERS.map((f) => {
const Icon = f.icon;
const isActive = folder === f.key;
return (
<button
key={f.key}
onClick={() => handleFolderChange(f.key)}
className={`flex items-center gap-1 rounded px-2 py-1 text-xs transition-colors cursor-pointer ${
isActive ? 'bg-accent font-medium' : 'opacity-60 hover:opacity-100 hover:bg-accent/50'
}`}
title={f.label}
>
<Icon className="h-3.5 w-3.5" />
<span className="hidden sm:inline">{f.label}</span>
</button>
);
})}
</div>
<span className="text-xs opacity-50">{total}</span>
<button
onClick={handleSync}