Skip to content

Commit a7a596c

Browse files
committed
Refine landing UX: All Runs card and ready-link status messaging
1 parent f86c19a commit a7a596c

1 file changed

Lines changed: 17 additions & 39 deletions

File tree

site/src/pages/index.astro

Lines changed: 17 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,6 @@ const runs = forecastData
1818
}))
1919
.sort((a, b) => String(b.created_at).localeCompare(String(a.created_at)));
2020
21-
const allBacktests = forecastData.flatMap((data: any) => data.backtest ?? []);
22-
23-
const leaderboardMap = new Map<string, { total: number; count: number }>();
24-
for (const row of allBacktests) {
25-
const model = row.model;
26-
const smape = Number(row.smape);
27-
if (!model || Number.isNaN(smape)) continue;
28-
const current = leaderboardMap.get(model) ?? { total: 0, count: 0 };
29-
leaderboardMap.set(model, { total: current.total + smape, count: current.count + 1 });
30-
}
31-
32-
const leaderboard = Array.from(leaderboardMap.entries())
33-
.map(([model, stats]) => ({ model, avgSmape: stats.total / stats.count, samples: stats.count }))
34-
.sort((a, b) => a.avgSmape - b.avgSmape);
35-
3621
3722
const defaultPayload = JSON.stringify({
3823
start_datetime: '2026-01-01T00:00:00',
@@ -106,30 +91,16 @@ const defaultPayload = JSON.stringify({
10691
</div>
10792
</div>
10893
</section>
109-
110-
{leaderboard.length > 0 && (
111-
<section class="mt-4 rounded-xl border border-slate-700 bg-slate-900/70 p-4 overflow-x-auto">
112-
<h2 class="text-lg font-semibold mb-2">Leaderboard (avg SMAPE)</h2>
113-
<table class="min-w-[420px] text-sm w-full">
114-
<thead><tr class="text-slate-300"><th class="text-left p-2">Rank</th><th class="text-left p-2">Model</th><th class="text-left p-2">Avg SMAPE</th><th class="text-left p-2">Samples</th></tr></thead>
115-
<tbody>
116-
{leaderboard.map((row, idx) => (
117-
<tr>
118-
<td class="p-2">{idx + 1}</td><td class={`p-2 ${idx===0?'text-emerald-400 font-semibold':''}`}>{row.model}</td><td class="p-2">{row.avgSmape.toFixed(4)}</td><td class="p-2">{row.samples}</td>
119-
</tr>
120-
))}
121-
</tbody>
122-
</table>
123-
</section>
124-
)}
125-
126-
<section class="mt-4 grid gap-3">
127-
{runs.map((run) => (
128-
<article class="rounded-xl border border-slate-700 bg-slate-900/70 p-4">
129-
<h3 class="font-semibold text-sky-300"><a href={run.path}>{run.slug ?? run.title}</a></h3>
130-
<p class="text-sm text-slate-400 mt-1">{run.backend} · {run.granularity} · horizon {run.horizon} · history {run.history_points} · forecast {run.forecast_points}</p>
131-
</article>
132-
))}
94+
<section class="mt-4 rounded-xl border border-slate-700 bg-slate-900/70 p-4">
95+
<h2 class="text-lg font-semibold mb-3">All Runs</h2>
96+
<div class="grid gap-3">
97+
{runs.map((run) => (
98+
<article class="rounded-xl border border-slate-700 bg-slate-900/50 p-4">
99+
<h3 class="font-semibold text-sky-300"><a href={run.path}>{run.slug ?? run.title}</a></h3>
100+
<p class="text-sm text-slate-400 mt-1">{run.backend} · {run.granularity} · horizon {run.horizon} · history {run.history_points} · forecast {run.forecast_points}</p>
101+
</article>
102+
))}
103+
</div>
133104
</section>
134105

135106
<script type="module">
@@ -159,7 +130,11 @@ const defaultPayload = JSON.stringify({
159130
statusListEl.innerHTML = runs.map((r) => {
160131
const emoji = r.status === 'completed' ? (r.conclusion === 'success' ? '✅' : '❌') : '⏳';
161132
const statusText = r.status === 'completed' ? (r.conclusion || 'completed') : (r.status || 'queued');
133+
const forecastHref = `/forecasts/${r.slug}`;
162134
const href = r.html_url || r.actions_url || 'https://github.com/bryanwhiting/weatherman/actions/workflows/forecast-request.yml';
135+
if (r.status === 'completed' && r.conclusion === 'success') {
136+
return `<div class="rounded border border-slate-700 bg-slate-900/50 p-2 text-xs text-slate-300">✅ <code>${r.slug}</code> — success · Your forecast is ready: <a href="${forecastHref}" class="text-emerald-300 underline">${forecastHref}</a>, or refresh this browser · <a href="${href}" target="_blank" rel="noreferrer" class="text-sky-300 underline">run</a></div>`;
137+
}
163138
return `<div class="rounded border border-slate-700 bg-slate-900/50 p-2 text-xs text-slate-300">${emoji} <code>${r.slug}</code> — ${statusText} · <a href="${href}" target="_blank" rel="noreferrer" class="text-sky-300 underline">open</a></div>`;
164139
}).join('');
165140
};
@@ -192,6 +167,9 @@ const defaultPayload = JSON.stringify({
192167
updated_at: rj.updated_at,
193168
});
194169
if (rj.status === 'completed') {
170+
if (rj.conclusion === 'success') {
171+
statusEl.innerHTML = `✅ Your forecast is ready: <a href="/forecasts/${slug}" class="text-emerald-300 underline">/forecasts/${slug}</a>, or refresh this browser`;
172+
}
195173
clearInterval(timer);
196174
activeTimers.delete(slug);
197175
}

0 commit comments

Comments
 (0)