feat: explain activity data quality

This commit is contained in:
ik
2026-09-03 18:23:43 +07:00
parent c3ddd71f59
commit 97afd2730e
9 changed files with 63 additions and 22 deletions
+7 -4
View File
@@ -1,15 +1,18 @@
---
import Layout from "../../layouts/Layout.astro";
import { api, kg, type Catch, type Spot } from "../../lib/api";
import { api, kg, type Activity, type Catch, type Spot } from "../../lib/api";
const { id } = Astro.params;
let spot: Spot | null = null, catches: Catch[] = [], unavailable = false;
try { [spot, catches] = await Promise.all([api<Spot>(`/api/v1/spots/${id}`), api<Catch[]>(`/api/v1/spots/${id}/catches`)]); } catch { unavailable = true; }
let spot: Spot | null = null, catches: Catch[] = [], activity: Activity | null = null, unavailable = false;
try {
const [spotResult, catchResult, activityRows] = await Promise.all([api<Spot>(`/api/v1/spots/${id}`), api<Catch[]>(`/api/v1/spots/${id}/catches`), api<Activity[]>("/api/v1/activity?hours=24&limit=100")]);
spot = spotResult; catches = catchResult; activity = activityRows.find(item => item.spot_id === id) ?? null;
} catch { unavailable = true; }
---
<Layout title={spot ? `${spot.waterbody} ${spot.x}:${spot.y} — RF4 Spotter` : "Точка — RF4 Spotter"}>
<a class="back" href="/">← Все активные точки</a>
{unavailable || !spot ? <div class="state"><h1>Точка недоступна</h1><p>API не ответил или такой точки нет.</p></div> : <>
<section class="spot-hero"><div><span class="eyebrow">{spot.waterbody}</span><h1>Точка {spot.x}:{spot.y}</h1><p>{spot.description}</p></div><div class="pin">{spot.x}<span>:</span>{spot.y}</div></section>
<div class="periods"><div><strong>{spot.catches_24h}</strong><span>за 24 часа</span></div><div><strong>{spot.catches_3d}</strong><span>за 3 дня</span></div><div><strong>{spot.catches_7d}</strong><span>за 7 дней</span></div></div>
<section class="detail-grid"><div><div class="section-heading"><h2>Последние уловы</h2></div><div class="catch-list">{catches.map(item => <article><div><strong>{item.fish}</strong><span>{item.bait ?? "Приманка не указана"}</span></div><div><strong>{kg(item.weight_g)}</strong><span>{item.player_name ?? "Анонимно"}</span></div></article>)}</div></div><aside><span class="eyebrow">Лучшие приманки</span><ol>{spot.top_baits.map(name => <li>{name}</li>)}</ol><p class="note">Статистика построена только по одобренным демо-наблюдениям.</p></aside></section>
<section class="detail-grid"><div><div class="section-heading"><h2>Последние уловы</h2></div>{catches.length ? <div class="catch-list">{catches.map(item => <article><div><strong>{item.fish}</strong><span>{item.bait ?? "Приманка не указана"}</span></div><div><strong>{kg(item.weight_g)}</strong><span>{item.player_name ?? "Анонимно"}</span></div></article>)}</div> : <div class="state compact-state"><h2>Уловов пока нет</h2><p>Для этой точки нет одобренных наблюдений.</p></div>}</div><aside><span class="eyebrow">Оценка за 24 часа</span>{activity ? <><h2>{activity.activity_score} / 100</h2><p>{activity.explanation}</p><p class="note">Основано на {activity.catches} наблюдениях от {activity.unique_players} игроков. Уверенность: {activity.confidence_score}%.</p></> : <p>За последние 24 часа данных для расчёта нет.</p>}<span class="eyebrow">Лучшие приманки</span>{spot.top_baits.length ? <ol>{spot.top_baits.map(name => <li>{name}</li>)}</ol> : <p>Недостаточно данных.</p>}<p class="note">Учитываются только одобренные наблюдения.</p></aside></section>
</>}
</Layout>