🎨 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>
This commit is contained in:
@@ -11,39 +11,46 @@ type CompanyOverviewCardProps = {
|
||||
|
||||
export function CompanyOverviewCard(props: CompanyOverviewCardProps) {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const description =
|
||||
props.analysis.companyProfile.description ??
|
||||
"No annual filing business description is available yet.";
|
||||
|
||||
// Get the actual business description, filtering out raw data artifacts
|
||||
const rawDescription = props.analysis.companyProfile.description;
|
||||
const isRawData = rawDescription && (
|
||||
rawDescription.includes('http://') ||
|
||||
rawDescription.includes('P1Y') ||
|
||||
rawDescription.includes('P3Y') ||
|
||||
rawDescription.includes('FY false')
|
||||
);
|
||||
|
||||
const description = !rawDescription || isRawData
|
||||
? "No business description available."
|
||||
: rawDescription;
|
||||
|
||||
const needsClamp = description.length > 320;
|
||||
|
||||
// Combine metadata into a single line
|
||||
const metadata = [
|
||||
props.analysis.company.ticker,
|
||||
props.analysis.company.sector ?? props.analysis.companyProfile.industry,
|
||||
props.analysis.company.cik ? `CIK ${props.analysis.company.cik}` : null,
|
||||
].filter(Boolean).join(' · ');
|
||||
|
||||
return (
|
||||
<Panel className="h-full pt-2">
|
||||
<div className="space-y-5">
|
||||
{/* Header with company name and metadata */}
|
||||
<div>
|
||||
<div>
|
||||
<h2 className="text-2xl font-semibold text-[color:var(--terminal-bright)]">
|
||||
{props.analysis.company.companyName}
|
||||
</h2>
|
||||
<p className="mt-1 text-xs uppercase tracking-[0.18em] text-[color:var(--terminal-muted)]">
|
||||
{props.analysis.company.ticker}
|
||||
</p>
|
||||
<p className="mt-3 text-sm text-[color:var(--terminal-muted)]">
|
||||
{props.analysis.company.sector ??
|
||||
props.analysis.companyProfile.industry ??
|
||||
"Sector unavailable"}
|
||||
{props.analysis.company.category
|
||||
? ` · ${props.analysis.company.category}`
|
||||
: ""}
|
||||
{props.analysis.company.cik
|
||||
? ` · CIK ${props.analysis.company.cik}`
|
||||
: ""}
|
||||
</p>
|
||||
</div>
|
||||
<h2 className="text-2xl font-semibold text-[color:var(--terminal-bright)]">
|
||||
{props.analysis.company.companyName}
|
||||
</h2>
|
||||
<p className="mt-1 text-xs uppercase tracking-[0.18em] text-[color:var(--terminal-muted)]">
|
||||
{metadata}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Business description section */}
|
||||
<div className="border-t border-[color:var(--line-weak)] py-4">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-xs uppercase tracking-[0.14em] text-[color:var(--terminal-muted)]">
|
||||
Business description
|
||||
</p>
|
||||
@@ -58,7 +65,7 @@ export function CompanyOverviewCard(props: CompanyOverviewCardProps) {
|
||||
href={props.analysis.companyProfile.website}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="inline-flex items-center gap-1 text-xs uppercase tracking-[0.14em] text-[color:var(--accent)] hover:text-[color:var(--accent-strong)]"
|
||||
className="inline-flex shrink-0 items-center gap-1 text-xs uppercase tracking-[0.14em] text-[color:var(--accent)] hover:text-[color:var(--accent-strong)]"
|
||||
>
|
||||
Website
|
||||
<ExternalLink className="size-3.5" />
|
||||
|
||||
@@ -72,6 +72,8 @@ export function PriceHistoryCard(props: PriceHistoryCardProps) {
|
||||
tickLine={{ stroke: CHART_MUTED }}
|
||||
tick={{ fill: CHART_MUTED }}
|
||||
tickFormatter={(value: number) => `$${value.toFixed(0)}`}
|
||||
domain={[(dataMin) => dataMin * 0.05, (dataMax) => dataMax * 1.05]}
|
||||
allowDataOverflow
|
||||
/>
|
||||
<Tooltip
|
||||
formatter={(value) => formatCurrency(Array.isArray(value) ? value[0] : value)}
|
||||
|
||||
@@ -8,18 +8,22 @@ type ValuationFactsTableProps = {
|
||||
};
|
||||
|
||||
function formatRatio(value: number | null) {
|
||||
return value === null ? 'n/a' : `${value.toFixed(2)}x`;
|
||||
return value === null ? '—' : `${value.toFixed(2)}x`;
|
||||
}
|
||||
|
||||
function formatShares(value: number | null) {
|
||||
return value === null ? 'n/a' : formatScaledNumber(value, { maximumFractionDigits: 2 });
|
||||
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: props.analysis.valuationSnapshot.marketCap === null ? 'n/a' : formatCompactCurrency(props.analysis.valuationSnapshot.marketCap) },
|
||||
{ label: 'Enterprise value', value: props.analysis.valuationSnapshot.enterpriseValue === null ? 'n/a' : formatCompactCurrency(props.analysis.valuationSnapshot.enterpriseValue) },
|
||||
{ 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) },
|
||||
|
||||
Reference in New Issue
Block a user