Add K/M/B scale toggles for financial displays
This commit is contained in:
@@ -15,7 +15,13 @@ import { useAuthGuard } from '@/hooks/use-auth-guard';
|
||||
import { useTaskPoller } from '@/hooks/use-task-poller';
|
||||
import { getTask, listFilings, queueFilingAnalysis, queueFilingSync } from '@/lib/api';
|
||||
import type { Filing, Task } from '@/lib/types';
|
||||
import { formatCompactCurrency } from '@/lib/format';
|
||||
import { formatCurrencyByScale, type NumberScaleUnit } from '@/lib/format';
|
||||
|
||||
const FINANCIAL_VALUE_SCALE_OPTIONS: Array<{ value: NumberScaleUnit; label: string }> = [
|
||||
{ value: 'thousands', label: 'Thousands (K)' },
|
||||
{ value: 'millions', label: 'Millions (M)' },
|
||||
{ value: 'billions', label: 'Billions (B)' }
|
||||
];
|
||||
|
||||
export default function FilingsPage() {
|
||||
return (
|
||||
@@ -39,6 +45,17 @@ function hasFinancialSnapshot(filing: Filing) {
|
||||
return filing.filing_type === '10-K' || filing.filing_type === '10-Q';
|
||||
}
|
||||
|
||||
function asScaledFinancialSnapshot(
|
||||
value: number | null | undefined,
|
||||
scale: NumberScaleUnit
|
||||
) {
|
||||
if (value === null || value === undefined) {
|
||||
return 'n/a';
|
||||
}
|
||||
|
||||
return formatCurrencyByScale(value, scale);
|
||||
}
|
||||
|
||||
function resolveOriginalFilingUrl(filing: Filing) {
|
||||
if (filing.filing_url) {
|
||||
return filing.filing_url;
|
||||
@@ -93,6 +110,7 @@ function FilingsPageContent() {
|
||||
const [filterTickerInput, setFilterTickerInput] = useState('');
|
||||
const [searchTicker, setSearchTicker] = useState('');
|
||||
const [activeTask, setActiveTask] = useState<Task | null>(null);
|
||||
const [financialValueScale, setFinancialValueScale] = useState<NumberScaleUnit>('millions');
|
||||
|
||||
useEffect(() => {
|
||||
const ticker = searchParams.get('ticker');
|
||||
@@ -168,6 +186,10 @@ function FilingsPageContent() {
|
||||
return counts;
|
||||
}, [filings]);
|
||||
|
||||
const selectedFinancialScaleLabel = useMemo(() => {
|
||||
return FINANCIAL_VALUE_SCALE_OPTIONS.find((option) => option.value === financialValueScale)?.label ?? 'Millions (M)';
|
||||
}, [financialValueScale]);
|
||||
|
||||
if (isPending || !isAuthenticated) {
|
||||
return <div className="flex min-h-screen items-center justify-center text-sm text-[color:var(--terminal-muted)]">Opening filings stream...</div>;
|
||||
}
|
||||
@@ -248,7 +270,25 @@ function FilingsPageContent() {
|
||||
</Panel>
|
||||
</div>
|
||||
|
||||
<Panel title="Filing Ledger" subtitle={`${filings.length} records loaded${searchTicker ? ` for ${searchTicker}` : ''}.`}>
|
||||
<Panel
|
||||
title="Filing Ledger"
|
||||
subtitle={`${filings.length} records loaded${searchTicker ? ` for ${searchTicker}` : ''}. Values shown in ${selectedFinancialScaleLabel}.`}
|
||||
actions={(
|
||||
<div className="flex flex-wrap justify-end gap-2">
|
||||
{FINANCIAL_VALUE_SCALE_OPTIONS.map((option) => (
|
||||
<Button
|
||||
key={option.value}
|
||||
type="button"
|
||||
variant={option.value === financialValueScale ? 'primary' : 'ghost'}
|
||||
className="px-2 py-1 text-xs"
|
||||
onClick={() => setFinancialValueScale(option.value)}
|
||||
>
|
||||
{option.label}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
{error ? <p className="text-sm text-[#ffb5b5]">{error}</p> : null}
|
||||
{loading ? (
|
||||
<p className="text-sm text-[color:var(--terminal-muted)]">Fetching filings...</p>
|
||||
@@ -282,7 +322,7 @@ function FilingsPageContent() {
|
||||
<div className="rounded-md border border-[color:var(--line-weak)] px-2 py-1.5">
|
||||
<dt className="text-[color:var(--terminal-muted)]">Financial Snapshot</dt>
|
||||
<dd className="mt-1 text-[color:var(--terminal-bright)]">
|
||||
{financialForm ? (revenue ? formatCompactCurrency(revenue) : 'n/a') : 'Qualitative filing'}
|
||||
{financialForm ? asScaledFinancialSnapshot(revenue, financialValueScale) : 'Qualitative filing'}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="rounded-md border border-[color:var(--line-weak)] px-2 py-1.5">
|
||||
@@ -351,7 +391,7 @@ function FilingsPageContent() {
|
||||
</td>
|
||||
<td>{filing.filing_type}</td>
|
||||
<td>{formatFilingDate(filing.filing_date)}</td>
|
||||
<td>{financialForm ? (revenue ? formatCompactCurrency(revenue) : 'n/a') : 'Qualitative filing'}</td>
|
||||
<td>{financialForm ? asScaledFinancialSnapshot(revenue, financialValueScale) : 'Qualitative filing'}</td>
|
||||
<td className="max-w-[18rem]">{filing.company_name}</td>
|
||||
<td>{hasAnalysis ? 'Ready' : 'Not generated'}</td>
|
||||
<td>
|
||||
|
||||
Reference in New Issue
Block a user