Implement fiscal-style research MVP flows
Some checks failed
PR Checks / typecheck-and-build (push) Has been cancelled
Some checks failed
PR Checks / typecheck-and-build (push) Has been cancelled
This commit is contained in:
@@ -5,7 +5,7 @@ import Link from 'next/link';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { Suspense } from 'react';
|
||||
import { format } from 'date-fns';
|
||||
import { Bot, Download, ExternalLink, Search, TimerReset } from 'lucide-react';
|
||||
import { Bot, Download, ExternalLink, NotebookPen, Search, TimerReset } from 'lucide-react';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { AppShell } from '@/components/shell/app-shell';
|
||||
import { Panel } from '@/components/ui/panel';
|
||||
@@ -13,7 +13,11 @@ import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { useAuthGuard } from '@/hooks/use-auth-guard';
|
||||
import { useLinkPrefetch } from '@/hooks/use-link-prefetch';
|
||||
import { queueFilingAnalysis, queueFilingSync } from '@/lib/api';
|
||||
import {
|
||||
createResearchJournalEntry,
|
||||
queueFilingAnalysis,
|
||||
queueFilingSync
|
||||
} from '@/lib/api';
|
||||
import type { Filing } from '@/lib/types';
|
||||
import { formatCurrencyByScale, type NumberScaleUnit } from '@/lib/format';
|
||||
import { queryKeys } from '@/lib/query/keys';
|
||||
@@ -131,6 +135,7 @@ function FilingsPageContent() {
|
||||
const [filterTickerInput, setFilterTickerInput] = useState('');
|
||||
const [searchTicker, setSearchTicker] = useState('');
|
||||
const [financialValueScale, setFinancialValueScale] = useState<NumberScaleUnit>('millions');
|
||||
const [actionNotice, setActionNotice] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const ticker = searchParams.get('ticker');
|
||||
@@ -152,7 +157,7 @@ function FilingsPageContent() {
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const response = await queryClient.ensureQueryData(options);
|
||||
const response = await queryClient.fetchQuery(options);
|
||||
setFilings(response.filings);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Unable to fetch filings');
|
||||
@@ -197,6 +202,30 @@ function FilingsPageContent() {
|
||||
}
|
||||
};
|
||||
|
||||
const addToJournal = async (filing: Filing) => {
|
||||
try {
|
||||
await createResearchJournalEntry({
|
||||
ticker: filing.ticker,
|
||||
accessionNumber: filing.accession_number,
|
||||
entryType: 'filing_note',
|
||||
title: `${filing.filing_type} filing note`,
|
||||
bodyMarkdown: [
|
||||
`Captured filing note for ${filing.company_name} (${filing.ticker}).`,
|
||||
`Filed: ${formatFilingDate(filing.filing_date)}`,
|
||||
`Accession: ${filing.accession_number}`,
|
||||
'',
|
||||
filing.analysis?.text ?? filing.analysis?.legacyInsights ?? 'Follow up on this filing from the stream.'
|
||||
].join('\n')
|
||||
});
|
||||
void queryClient.invalidateQueries({ queryKey: queryKeys.researchJournal(filing.ticker) });
|
||||
void queryClient.invalidateQueries({ queryKey: queryKeys.companyAnalysis(filing.ticker) });
|
||||
void queryClient.invalidateQueries({ queryKey: queryKeys.watchlist() });
|
||||
setActionNotice(`Saved ${filing.accession_number} to the ${filing.ticker} journal.`);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to add filing to journal');
|
||||
}
|
||||
};
|
||||
|
||||
const groupedByTicker = useMemo(() => {
|
||||
const counts = new Map<string, number>();
|
||||
|
||||
@@ -321,6 +350,7 @@ function FilingsPageContent() {
|
||||
)}
|
||||
>
|
||||
{error ? <p className="text-sm text-[#ffb5b5]">{error}</p> : null}
|
||||
{actionNotice ? <p className="mt-2 text-sm text-[color:var(--accent)]">{actionNotice}</p> : null}
|
||||
{loading ? (
|
||||
<p className="text-sm text-[color:var(--terminal-muted)]">Fetching filings...</p>
|
||||
) : filings.length === 0 ? (
|
||||
@@ -379,6 +409,14 @@ function FilingsPageContent() {
|
||||
<Bot className="size-3" />
|
||||
Analyze
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => void addToJournal(filing)}
|
||||
className="px-2 py-1 text-xs"
|
||||
>
|
||||
<NotebookPen className="size-3" />
|
||||
Add to journal
|
||||
</Button>
|
||||
{hasAnalysis ? (
|
||||
<Link
|
||||
href={`/analysis/reports/${filing.ticker}/${encodeURIComponent(filing.accession_number)}`}
|
||||
@@ -449,6 +487,14 @@ function FilingsPageContent() {
|
||||
<Bot className="size-3" />
|
||||
Analyze
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => void addToJournal(filing)}
|
||||
className="px-2 py-1 text-xs"
|
||||
>
|
||||
<NotebookPen className="size-3" />
|
||||
Journal
|
||||
</Button>
|
||||
{hasAnalysis ? (
|
||||
<Link
|
||||
href={`/analysis/reports/${filing.ticker}/${encodeURIComponent(filing.accession_number)}`}
|
||||
|
||||
Reference in New Issue
Block a user