diff --git a/apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx b/apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx index 4983c58fc6..55bbfef6f3 100644 --- a/apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx +++ b/apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx @@ -28,7 +28,9 @@ export function BulkRunRules() { const { emailAccountId } = useAccount(); const [isOpen, setIsOpen] = useState(false); - const [totalThreads, setTotalThreads] = useState(0); + const [processedThreadIds, setProcessedThreadIds] = useState>( + new Set(), + ); const { data, isLoading, error } = useThreads({ type: "inbox" }); @@ -40,9 +42,17 @@ export function BulkRunRules() { const [startDate, setStartDate] = useState(); const [endDate, setEndDate] = useState(); + const [runResult, setRunResult] = useState<{ + count: number; + } | null>(null); const abortRef = useRef<() => void>(undefined); + const remaining = new Set( + [...processedThreadIds].filter((id) => queue.has(id)), + ).size; + const completed = processedThreadIds.size - remaining; + return (
@@ -63,11 +73,12 @@ export function BulkRunRules() { (that have not been previously processed). - {!!queue.size && ( + {processedThreadIds.size > 0 && (
- Progress: {totalThreads - queue.size}/{totalThreads}{" "} - emails completed + {remaining > 0 + ? `Progress: ${completed}/${processedThreadIds.size} emails completed` + : `Success: Processed ${processedThreadIds.size} emails`}
)} @@ -76,13 +87,21 @@ export function BulkRunRules() {
{ + setStartDate(date); + setRunResult(null); + setProcessedThreadIds(new Set()); + }} value={startDate} placeholder="Set start date" disabled={running} /> { + setEndDate(date); + setRunResult(null); + setProcessedThreadIds(new Set()); + }} value={endDate} placeholder="Set end date (optional)" disabled={running} @@ -94,6 +113,8 @@ export function BulkRunRules() { disabled={running || !startDate || !emailAccountId} loading={running} onClick={async () => { + setRunResult(null); + setProcessedThreadIds(new Set()); if (!startDate) { toastError({ description: "Please select a start date", @@ -111,9 +132,21 @@ export function BulkRunRules() { abortRef.current = await onRun( emailAccountId, { startDate, endDate }, - (count) => - setTotalThreads((total) => total + count), - () => setRunning(false), + (ids) => { + setProcessedThreadIds((prev) => { + const next = new Set(prev); + for (const id of ids) { + next.add(id); + } + return next; + }); + }, + (status, count) => { + setRunning(false); + if (status === "success" && count === 0) { + setRunResult({ count }); + } + }, ); }} > @@ -127,6 +160,12 @@ export function BulkRunRules() { Cancel )} + + {runResult && runResult.count === 0 && ( +
+ No unread emails found in the selected date range. +
+ )}
) : ( @@ -145,11 +184,15 @@ export function BulkRunRules() { async function onRun( emailAccountId: string, { startDate, endDate }: { startDate: Date; endDate?: Date }, - incrementThreadsQueued: (count: number) => void, - onComplete: () => void, + onThreadsQueued: (threadIds: string[]) => void, + onComplete: ( + status: "success" | "error" | "cancelled", + count: number, + ) => void, ) { let nextPageToken = ""; const LIMIT = 25; + let totalProcessed = 0; let aborted = false; @@ -186,7 +229,8 @@ async function onRun( ? errorData.error : `Error: ${res.status}`, }); - break; + onComplete("error", totalProcessed); + return; } const data: ThreadsResponse = await res.json(); @@ -197,25 +241,32 @@ async function onRun( title: "Invalid response", description: "Failed to process emails. Please try again.", }); - break; + onComplete("error", totalProcessed); + return; } nextPageToken = data.nextPageToken || ""; const threadsWithoutPlan = data.threads.filter((t) => !t.plan); - incrementThreadsQueued(threadsWithoutPlan.length); + onThreadsQueued(threadsWithoutPlan.map((t) => t.id)); + totalProcessed += threadsWithoutPlan.length; runAiRules(emailAccountId, threadsWithoutPlan, false); - if (!nextPageToken || aborted) break; + if (aborted) { + onComplete("cancelled", totalProcessed); + return; + } + + if (!nextPageToken) break; // avoid gmail api rate limits // ai takes longer anyway await sleep(threadsWithoutPlan.length ? 5000 : 2000); } - onComplete(); + onComplete("success", totalProcessed); } run(); diff --git a/version.txt b/version.txt index 234ca1691d..e701e92e63 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -v2.20.9 +v2.20.10