feat(financials): add compact surface UI and graphing states

This commit is contained in:
2026-03-12 15:25:21 -04:00
parent c274f4d55b
commit 33ce48f53c
13 changed files with 1941 additions and 197 deletions

View File

@@ -361,6 +361,30 @@ function GraphingPageContent() {
surface: graphState.surface,
metric: graphState.metric
}), [graphState.metric, graphState.surface, results]);
const partialCoverageMessage = useMemo(() => {
const notMeaningfulCount = comparison.companies.filter((company) => company.status === 'not_meaningful').length;
const missingCount = comparison.companies.filter((company) => company.status === 'no_metric_data').length;
const errorCount = comparison.companies.filter((company) => company.status === 'error').length;
const parts: string[] = [];
if (notMeaningfulCount > 0) {
parts.push(`${notMeaningfulCount} ${notMeaningfulCount === 1 ? 'company marks' : 'companies mark'} this metric as not meaningful for the selected pack`);
}
if (missingCount > 0) {
parts.push(`${missingCount} ${missingCount === 1 ? 'company has' : 'companies have'} no metric data`);
}
if (errorCount > 0) {
parts.push(`${errorCount} ${errorCount === 1 ? 'company failed' : 'companies failed'} to load`);
}
if (parts.length === 0) {
return 'Partial coverage detected. Some companies are missing values for this metric.';
}
return `Partial coverage detected. ${parts.join('. ')}.`;
}, [comparison.companies]);
const hasCurrencyScale = selectedMetric?.unit === 'currency';
@@ -527,7 +551,7 @@ function GraphingPageContent() {
<>
{comparison.hasPartialData ? (
<div className="mb-4 rounded-xl border border-[color:var(--line-weak)] bg-[color:var(--panel-soft)] px-3 py-3 text-sm text-[color:var(--terminal-muted)]">
Partial coverage detected. Some companies are missing values for this metric or failed to load, but the remaining series still render.
{partialCoverageMessage}
</div>
) : null}
<div className="mb-4 flex flex-wrap gap-2">
@@ -613,6 +637,7 @@ function GraphingPageContent() {
<thead>
<tr>
<th>Company</th>
<th>Pack</th>
<th>Latest</th>
<th>Prior</th>
<th>Change</th>
@@ -637,6 +662,7 @@ function GraphingPageContent() {
<span className="text-xs text-[color:var(--terminal-muted)]">{row.companyName}</span>
</div>
</td>
<td>{row.fiscalPack ?? 'n/a'}</td>
<td>{selectedMetric ? formatMetricValue(row.latestValue, selectedMetric.unit, graphState.scale) : 'n/a'}</td>
<td>{selectedMetric ? formatMetricValue(row.priorValue, selectedMetric.unit, graphState.scale) : 'n/a'}</td>
<td className={cn(row.changeValue !== null && row.changeValue >= 0 ? 'text-[color:var(--accent)]' : row.changeValue !== null ? 'text-[#ffb5b5]' : 'text-[color:var(--terminal-muted)]')}>
@@ -647,6 +673,8 @@ function GraphingPageContent() {
<td>
{row.status === 'ready' ? (
<span className="text-[color:var(--accent)]">Ready</span>
) : row.status === 'not_meaningful' ? (
<span className="text-[color:var(--terminal-muted)]">Not meaningful for this pack</span>
) : row.status === 'no_metric_data' ? (
<span className="text-[color:var(--terminal-muted)]">No metric data</span>
) : (