chore(frontend): drop the dead FundamentalsPanel dev harness
Left over from the fundamentals work. Verified orphaned before removing: not in vite.config.ts (default build input is index.html alone, and dist/ only ever contained index.html, so it never shipped), not referenced by any script, config or module, and imported by nothing. Its only mention anywhere was its own header comment — the many other "harness" hits in the repo are the factor / backtest IC harness, which is unrelated and stays. Removes frontend/src/dev entirely. The pattern it embodied — a fixture-seeded page for eyeballing one component without a backend — is still the only way to see a component render, since the frontend has no test runner. But that is worth recreating per component on the spot, not preserving as a stale file pinned to one panel. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,18 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en" class="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>FundamentalsPanel harness</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Instrument+Sans:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
</head>
|
||||
<body class="bg-[#0a0b11] text-gray-100 font-sans">
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/dev/harness.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,146 +0,0 @@
|
||||
/* Dev-only visual harness for FundamentalsPanel. Served at /harness.html by
|
||||
* `vite`. Not imported by the app. Renders the three key states so desktop and
|
||||
* mobile can be eyeballed with representative fixtures. */
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import '../styles/globals.css';
|
||||
import { FundamentalsPanel } from '../components/ticker/FundamentalsPanel';
|
||||
import type { FundamentalResponse, MetricItem } from '../lib/types';
|
||||
|
||||
function h(period: string, value: number | null) {
|
||||
return { period_end: period, value };
|
||||
}
|
||||
const P = ['2025-06-30', '2025-09-30', '2025-12-31', '2026-03-28'];
|
||||
|
||||
function dateFromToday(days: number): string {
|
||||
const date = new Date();
|
||||
date.setHours(12, 0, 0, 0);
|
||||
date.setDate(date.getDate() + days);
|
||||
return [
|
||||
date.getFullYear(),
|
||||
String(date.getMonth() + 1).padStart(2, '0'),
|
||||
String(date.getDate()).padStart(2, '0'),
|
||||
].join('-');
|
||||
}
|
||||
|
||||
function metric(key: string, value: number | null, hist: (number | null)[],
|
||||
industry: MetricItem['industry'] = null,
|
||||
caveat: string | null = null): MetricItem {
|
||||
return {
|
||||
key: key as MetricItem['key'], value,
|
||||
history: hist.map((v, i) => h(P[i], v)),
|
||||
industry, period_end: '2026-03-28', filed_date: '2026-05-01', caveat,
|
||||
source: 'sec',
|
||||
};
|
||||
}
|
||||
const ind = (median: number, favorable_percentile: number) =>
|
||||
({ label: 'SIC 35 peers', median, favorable_percentile, peer_count: 12 });
|
||||
|
||||
const legacy = {
|
||||
pe_ratio: null, revenue_growth: null, earnings_surprise: null, market_cap: null,
|
||||
next_earnings_date: null, fetched_at: null, unavailable_fields: {},
|
||||
setup_eligible: true, setup_block_code: null, setup_block_reason: null,
|
||||
};
|
||||
|
||||
const full: FundamentalResponse = {
|
||||
symbol: 'AAPL', ...legacy,
|
||||
earnings: {
|
||||
next: { date: dateFromToday(12), session: 'amc', days_until: 12 },
|
||||
recent: [
|
||||
{ announce_date: '2025-08-01', period_end: '2025-06-30', eps_estimate: 1.4, eps_actual: 1.6, surprise_pct: 14.3 },
|
||||
{ announce_date: '2025-11-01', period_end: '2025-09-30', eps_estimate: 1.7, eps_actual: 1.9, surprise_pct: 11.8 },
|
||||
{ announce_date: '2026-02-01', period_end: '2025-12-31', eps_estimate: 2.6, eps_actual: 2.4, surprise_pct: -7.7 },
|
||||
{ announce_date: '2026-05-01', period_end: '2026-03-28', eps_estimate: 1.5, eps_actual: 1.65, surprise_pct: 10.0 },
|
||||
],
|
||||
},
|
||||
metrics: [
|
||||
metric('revenue_growth_yoy', 18, [8, 11, 15, 18], ind(11, 82)),
|
||||
metric('eps_growth_yoy', 24, [10, 18, 22, 24], ind(15, 70)),
|
||||
metric('operating_margin', 32, [30, 31, 31, 32], ind(22, 88)),
|
||||
metric('fcf_margin', 28, [24, 25, 27, 28], ind(18, 80)),
|
||||
metric('net_debt', 16.2e9, [46e9, 44e9, 24e9, 16.2e9], null),
|
||||
metric('net_debt_to_ebitda', 1.4, [1.9, 1.7, 1.5, 1.4], ind(2.1, 68)),
|
||||
metric('share_count_change_yoy', -1.7, [-2.4, -2.2, -2.3, -1.7], null),
|
||||
],
|
||||
valuation: {
|
||||
pe: 29.2, fcf_yield: 3.8, market_cap_est: 3.2e12,
|
||||
pe_industry: ind(23.5, 30), fcf_yield_industry: ind(3.1, 70), price_date: '2026-05-01',
|
||||
},
|
||||
reads: {
|
||||
header: 'growth accelerating · margins improving · valuation priced above peers',
|
||||
by_key: {
|
||||
revenue_growth_yoy: 'accelerating', eps_growth_yoy: 'accelerating',
|
||||
operating_margin: 'improving', fcf_margin: 'improving',
|
||||
share_count_change_yoy: 'buying back', net_debt_to_ebitda: 'conservative leverage',
|
||||
pe: 'priced above peers', fcf_yield: 'above peers', net_debt: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const partial: FundamentalResponse = {
|
||||
symbol: 'NEWCO', ...legacy,
|
||||
earnings: { next: { date: dateFromToday(0), session: 'unknown', days_until: 0 }, recent: [] },
|
||||
metrics: [
|
||||
metric('revenue_growth_yoy', 12, [null, 8, 10, 12], null),
|
||||
metric(
|
||||
'eps_growth_yoy',
|
||||
null,
|
||||
[null, null, null, null],
|
||||
null,
|
||||
'Not comparable: share count changed at least 25%; possible split or corporate action.',
|
||||
),
|
||||
metric('operating_margin', 25, [24, 24, 25, 25], null),
|
||||
metric('fcf_margin', null, [null, null, null, null], null),
|
||||
metric('net_debt', null, [], null),
|
||||
metric('net_debt_to_ebitda', 1.9, [1.7, 1.8, 1.9, 1.9], null),
|
||||
metric(
|
||||
'share_count_change_yoy',
|
||||
null,
|
||||
[1.8, 2.0, 2.0, null],
|
||||
null,
|
||||
'Not comparable: share count changed at least 25%; possible split or corporate action.',
|
||||
),
|
||||
],
|
||||
valuation: {
|
||||
pe: 15.2, fcf_yield: null, market_cap_est: 5.4e8,
|
||||
pe_industry: null, fcf_yield_industry: null, price_date: '2026-05-01',
|
||||
},
|
||||
reads: {
|
||||
header: 'growth steady · margins stable',
|
||||
by_key: {
|
||||
revenue_growth_yoy: 'steady', operating_margin: 'stable',
|
||||
share_count_change_yoy: '2.1% dilution', net_debt_to_ebitda: null,
|
||||
pe: null, fcf_yield: null, eps_growth_yoy: null, fcf_margin: null, net_debt: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const empty: FundamentalResponse = {
|
||||
symbol: 'ADR', ...legacy,
|
||||
earnings: { next: null, recent: [] },
|
||||
metrics: [
|
||||
'revenue_growth_yoy', 'eps_growth_yoy', 'operating_margin', 'fcf_margin',
|
||||
'net_debt', 'net_debt_to_ebitda', 'share_count_change_yoy',
|
||||
].map((k) => metric(k, null, [])),
|
||||
valuation: null,
|
||||
reads: { header: null, by_key: {} },
|
||||
};
|
||||
|
||||
function Case({ title, data }: { title: string; data: FundamentalResponse }) {
|
||||
return (
|
||||
<div>
|
||||
<div className="mb-1.5 text-[11px] uppercase tracking-widest text-gray-500">{title}</div>
|
||||
<FundamentalsPanel data={data} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<div className="mx-auto max-w-3xl space-y-8 p-6">
|
||||
<p className="text-[11px] uppercase tracking-widest text-gray-500">
|
||||
Desktop width (~768px, two columns). Resize the browser to ~390px to check mobile (single column).
|
||||
</p>
|
||||
<Case title="Full" data={full} />
|
||||
<Case title="Partial · insufficient peers" data={partial} />
|
||||
<Case title="Empty" data={empty} />
|
||||
</div>,
|
||||
);
|
||||
Reference in New Issue
Block a user