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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user