Implement fiscal-style research MVP flows
Some checks failed
PR Checks / typecheck-and-build (push) Has been cancelled

This commit is contained in:
2026-03-07 09:51:18 -05:00
parent f69e5b671b
commit 52136271d3
26 changed files with 2719 additions and 243 deletions

View File

@@ -4,7 +4,7 @@ import { useQueryClient } from '@tanstack/react-query';
import Link from 'next/link';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { format } from 'date-fns';
import { ArrowLeft, BrainCircuit, RefreshCcw } from 'lucide-react';
import { ArrowLeft, BrainCircuit, NotebookPen, RefreshCcw } from 'lucide-react';
import { useParams } from 'next/navigation';
import { AppShell } from '@/components/shell/app-shell';
import { useAuthGuard } from '@/hooks/use-auth-guard';
@@ -14,6 +14,7 @@ import { aiReportQueryOptions } from '@/lib/query/options';
import type { CompanyAiReportDetail } from '@/lib/types';
import { Button } from '@/components/ui/button';
import { Panel } from '@/components/ui/panel';
import { createResearchJournalEntry } from '@/lib/api';
function formatFilingDate(value: string) {
const date = new Date(value);
@@ -44,6 +45,8 @@ export default function AnalysisReportPage() {
const [report, setReport] = useState<CompanyAiReportDetail | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [savingToJournal, setSavingToJournal] = useState(false);
const [journalNotice, setJournalNotice] = useState<string | null>(null);
const loadReport = useCallback(async () => {
if (!accessionNumber) {
@@ -174,6 +177,49 @@ export default function AnalysisReportPage() {
<BrainCircuit className="size-3.5" />
Full text view
</div>
{journalNotice ? (
<p className="mb-3 text-xs text-[color:var(--accent)]">{journalNotice}</p>
) : null}
<div className="mb-4 flex flex-wrap gap-2">
<Button
variant="secondary"
disabled={savingToJournal}
onClick={async () => {
if (!report) {
return;
}
setSavingToJournal(true);
setJournalNotice(null);
try {
await createResearchJournalEntry({
ticker: report.ticker,
accessionNumber: report.accessionNumber,
entryType: 'filing_note',
title: `${report.filingType} AI memo`,
bodyMarkdown: [
`Stored AI memo for ${report.companyName} (${report.ticker}).`,
`Accession: ${report.accessionNumber}`,
'',
report.summary
].join('\n')
});
void queryClient.invalidateQueries({ queryKey: queryKeys.researchJournal(report.ticker) });
void queryClient.invalidateQueries({ queryKey: queryKeys.companyAnalysis(report.ticker) });
void queryClient.invalidateQueries({ queryKey: queryKeys.watchlist() });
setJournalNotice('Saved to the company research journal.');
} catch (err) {
setError(err instanceof Error ? err.message : 'Unable to save report to journal');
} finally {
setSavingToJournal(false);
}
}}
>
<NotebookPen className="size-4" />
{savingToJournal ? 'Saving...' : 'Add to journal'}
</Button>
</div>
<p className="whitespace-pre-wrap text-sm leading-7 text-[color:var(--terminal-bright)]">
{report.summary}
</p>