From c222179170086b6cfaf48875423c0dccfd5cdaeb Mon Sep 17 00:00:00 2001 From: francy51 Date: Thu, 12 Mar 2026 21:29:03 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20style(analysis):=20improve=20UI?= =?UTF-8?q?=20clarity=20and=20visual=20hierarchy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- components/analysis/company-overview-card.tsx | 55 +++++++++++-------- components/analysis/price-history-card.tsx | 2 + components/analysis/valuation-facts-table.tsx | 12 ++-- next-env.d.ts | 2 +- 4 files changed, 42 insertions(+), 29 deletions(-) diff --git a/components/analysis/company-overview-card.tsx b/components/analysis/company-overview-card.tsx index 1fb56e2..6bb79f0 100644 --- a/components/analysis/company-overview-card.tsx +++ b/components/analysis/company-overview-card.tsx @@ -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 (
+ {/* Header with company name and metadata */}
-
-

- {props.analysis.company.companyName} -

-

- {props.analysis.company.ticker} -

-

- {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}` - : ""} -

-
+

+ {props.analysis.company.companyName} +

+

+ {metadata} +

+ {/* Business description section */}
-
+

Business description

@@ -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 diff --git a/components/analysis/price-history-card.tsx b/components/analysis/price-history-card.tsx index c0cf47d..50702c3 100644 --- a/components/analysis/price-history-card.tsx +++ b/components/analysis/price-history-card.tsx @@ -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 /> formatCurrency(Array.isArray(value) ? value[0] : value)} diff --git a/components/analysis/valuation-facts-table.tsx b/components/analysis/valuation-facts-table.tsx index 6988768..f166e23 100644 --- a/components/analysis/valuation-facts-table.tsx +++ b/components/analysis/valuation-facts-table.tsx @@ -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) }, diff --git a/next-env.d.ts b/next-env.d.ts index c4b7818..9edff1c 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/dev/types/routes.d.ts"; +import "./.next/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.