feat: migrate task jobs to workflow notifications + timeline

This commit is contained in:
2026-03-02 14:29:31 -05:00
parent 36c4ed2ee2
commit d81a681905
33 changed files with 2437 additions and 292 deletions

View File

@@ -11,15 +11,13 @@ import { AppShell } from '@/components/shell/app-shell';
import { Panel } from '@/components/ui/panel';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { StatusPill } from '@/components/ui/status-pill';
import { useAuthGuard } from '@/hooks/use-auth-guard';
import { useLinkPrefetch } from '@/hooks/use-link-prefetch';
import { useTaskPoller } from '@/hooks/use-task-poller';
import { queueFilingAnalysis, queueFilingSync } from '@/lib/api';
import type { Filing, Task } from '@/lib/types';
import type { Filing } from '@/lib/types';
import { formatCurrencyByScale, type NumberScaleUnit } from '@/lib/format';
import { queryKeys } from '@/lib/query/keys';
import { filingsQueryOptions, taskQueryOptions } from '@/lib/query/options';
import { filingsQueryOptions } from '@/lib/query/options';
const FINANCIAL_VALUE_SCALE_OPTIONS: Array<{ value: NumberScaleUnit; label: string }> = [
{ value: 'thousands', label: 'Thousands (K)' },
@@ -115,7 +113,6 @@ function FilingsPageContent() {
const [syncTickerInput, setSyncTickerInput] = useState('');
const [filterTickerInput, setFilterTickerInput] = useState('');
const [searchTicker, setSearchTicker] = useState('');
const [activeTask, setActiveTask] = useState<Task | null>(null);
const [financialValueScale, setFinancialValueScale] = useState<NumberScaleUnit>('millions');
useEffect(() => {
@@ -153,29 +150,16 @@ function FilingsPageContent() {
}
}, [isPending, isAuthenticated, searchTicker, loadFilings]);
const polledTask = useTaskPoller({
taskId: activeTask?.id ?? null,
onTerminalState: async () => {
setActiveTask(null);
void queryClient.invalidateQueries({ queryKey: ['filings'] });
void queryClient.invalidateQueries({ queryKey: queryKeys.recentTasks(20) });
await loadFilings(searchTicker || undefined);
}
});
const liveTask = polledTask ?? activeTask;
const triggerSync = async () => {
if (!syncTickerInput.trim()) {
return;
}
try {
const { task } = await queueFilingSync({ ticker: syncTickerInput.trim().toUpperCase(), limit: 20 });
const latest = await queryClient.fetchQuery(taskQueryOptions(task.id));
setActiveTask(latest.task);
await queueFilingSync({ ticker: syncTickerInput.trim().toUpperCase(), limit: 20 });
void queryClient.invalidateQueries({ queryKey: queryKeys.recentTasks(20) });
void queryClient.invalidateQueries({ queryKey: ['filings'] });
await loadFilings(searchTicker || undefined);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to queue filing sync');
}
@@ -183,9 +167,7 @@ function FilingsPageContent() {
const triggerAnalysis = async (accessionNumber: string) => {
try {
const { task } = await queueFilingAnalysis(accessionNumber);
const latest = await queryClient.fetchQuery(taskQueryOptions(task.id));
setActiveTask(latest.task);
await queueFilingAnalysis(accessionNumber);
void queryClient.invalidateQueries({ queryKey: queryKeys.recentTasks(20) });
void queryClient.invalidateQueries({ queryKey: ['report'] });
} catch (err) {
@@ -230,16 +212,6 @@ function FilingsPageContent() {
</Button>
)}
>
{liveTask ? (
<Panel title="Active Task" subtitle={`${liveTask.task_type} is processing in the task engine.`}>
<div className="flex flex-col gap-2 rounded-lg border border-[color:var(--line-weak)] bg-[color:var(--panel-soft)] px-3 py-2 sm:flex-row sm:items-center sm:justify-between">
<p className="break-all text-sm text-[color:var(--terminal-bright)]">{liveTask.id}</p>
<StatusPill status={liveTask.status} />
</div>
{liveTask.error ? <p className="mt-2 text-sm text-[#ff9898]">{liveTask.error}</p> : null}
</Panel>
) : null}
<div className="grid grid-cols-1 gap-5 lg:grid-cols-[1.2fr_1fr]">
<Panel title="Sync Controller" subtitle="Queue ingestion jobs by ticker symbol.">
<form