Files
Neon-Desk/components/analysis/valuation-facts-table.tsx
francy51 c222179170 🎨 style(analysis): improve UI clarity and visual hierarchy
Enhance the company analysis overview page with better data presentation
and visual design:

- Fix business description display by filtering raw API data artifacts
- Improve metadata layout with consolidated single-line format
- Fix price chart Y-axis scaling to auto-scale to data range
- Replace 'n/a' with cleaner em dash (—) for empty states
- Add visual indicators and color-coded backgrounds to bull/bear sections
- Improve empty state messaging with centered icons

These changes improve information density, visual hierarchy, and overall
user experience across the analysis page.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-03-12 21:29:03 -04:00

68 lines
2.7 KiB
TypeScript

import { Fragment } from 'react';
import { Panel } from '@/components/ui/panel';
import { formatCompactCurrency, formatScaledNumber } from '@/lib/format';
import type { CompanyAnalysis } from '@/lib/types';
type ValuationFactsTableProps = {
analysis: CompanyAnalysis;
};
function formatRatio(value: number | null) {
return value === null ? '—' : `${value.toFixed(2)}x`;
}
function formatShares(value: number | null) {
return value === null ? '—' : formatScaledNumber(value, { maximumFractionDigits: 2 });
}
function formatCompactCurrencyOrDash(value: number | null) {
return value === null ? '—' : formatCompactCurrency(value);
}
export function ValuationFactsTable(props: ValuationFactsTableProps) {
const items = [
{ label: 'Source', value: props.analysis.valuationSnapshot.source },
{ label: 'Market cap', value: formatCompactCurrencyOrDash(props.analysis.valuationSnapshot.marketCap) },
{ label: 'Enterprise value', value: formatCompactCurrencyOrDash(props.analysis.valuationSnapshot.enterpriseValue) },
{ label: 'Shares outstanding', value: formatShares(props.analysis.valuationSnapshot.sharesOutstanding) },
{ label: 'Trailing P/E', value: formatRatio(props.analysis.valuationSnapshot.trailingPe) },
{ label: 'EV / Revenue', value: formatRatio(props.analysis.valuationSnapshot.evToRevenue) },
{ label: 'EV / EBITDA', value: formatRatio(props.analysis.valuationSnapshot.evToEbitda) }
];
const rows = Array.from({ length: Math.ceil(items.length / 2) }, (_, index) => items.slice(index * 2, index * 2 + 2));
return (
<Panel
title="Valuation"
className="pt-2"
>
<div className="overflow-x-auto">
<table className="w-full border-collapse table-fixed">
<tbody>
{rows.map((row) => (
<tr key={row.map((item) => item.label).join('-')} className="border-t border-[color:var(--line-weak)]">
{row.map((item) => (
<Fragment key={item.label}>
<th className="w-[18%] py-2 pr-3 text-left align-top text-[11px] font-medium uppercase tracking-[0.14em] text-[color:var(--terminal-muted)]">
{item.label}
</th>
<td className="w-[32%] py-2 pr-4 text-sm text-[color:var(--terminal-bright)]">
{item.value}
</td>
</Fragment>
))}
{row.length === 1 ? (
<>
<th className="w-[18%] py-2 pr-3" />
<td className="w-[32%] py-2 pr-4" />
</>
) : null}
</tr>
))}
</tbody>
</table>
</div>
</Panel>
);
}