sync
This commit is contained in:
@@ -16,7 +16,7 @@ const limited = item.catches < 3;
|
||||
<FishSilhouette name={item.fish}/>
|
||||
<div class="spot-meta"><span><FishingIcon name="pin" size={14}/> {item.x}:{item.y}</span><span><FishingIcon name="clock" size={14}/> {ago(item.last_confirmed_at)}</span></div>
|
||||
<p class="data-note">{item.explanation}</p>
|
||||
<DataPassport sources={item.sources} observedAt={item.last_confirmed_at} completeness={limited ? 75 : 100} confidence={item.confidence_score}/>
|
||||
<DataPassport sources={item.sources} observedAt={item.last_confirmed_at} confidence={item.confidence_score}/>
|
||||
<div class="bait-line"><FishingIcon name="lure" size={25}/><div><span>Работает сейчас</span><strong>{item.best_bait ?? "не указана"}</strong></div></div>
|
||||
</div>
|
||||
<div class="spot-stats"><div><strong>{item.catches}</strong><span>{plural(item.catches, ["улов", "улова", "уловов"])}</span></div><div><strong>{item.unique_players}</strong><span>{plural(item.unique_players, ["игрок", "игрока", "игроков"])}</span></div><div><strong>{kg(item.average_weight_g)}</strong><span>средний вес</span></div><div><strong>{item.confidence_score}%</strong><span>уверенность</span></div></div><span class="card-arrow"><FishingIcon name="arrow" size={22}/></span>
|
||||
|
||||
@@ -1,19 +1,10 @@
|
||||
---
|
||||
import type { Catch } from "../lib/api";
|
||||
const { catches } = Astro.props as { catches: Catch[] };
|
||||
const now = Date.now();
|
||||
const step = 12 * 60 * 60 * 1000;
|
||||
const buckets = Array.from({ length: 6 }, (_, index) => ({ index, count: 0 }));
|
||||
for (const item of catches) {
|
||||
const timestamp = new Date(item.caught_at ?? item.reported_at).getTime();
|
||||
const age = Math.floor((now - timestamp) / step);
|
||||
if (age >= 0 && age < buckets.length) buckets[buckets.length - 1 - age].count += 1;
|
||||
}
|
||||
const { buckets } = Astro.props as { buckets: { start: string; end: string; count: number }[] };
|
||||
const max = Math.max(1, ...buckets.map(bucket => bucket.count));
|
||||
---
|
||||
<section class="activity-timeline" aria-labelledby="timeline-title">
|
||||
<header><div><span class="overline">Последние 72 часа</span><h2 id="timeline-title">Леска активности</h2></div><p>По показанным ниже уловам · шаг 12 часов</p></header>
|
||||
<div class="timeline-chart" role="img" aria-label={`Распределение ${catches.length} показанных уловов за последние 72 часа`}>
|
||||
{buckets.map((bucket, index) => <div class="timeline-slot"><span class="timeline-line" style={`--height:${Math.max(8, bucket.count / max * 100)}%`}><i></i></span><strong>{bucket.count}</strong><small>{index === 0 ? "−72 ч" : index === 5 ? "сейчас" : `−${(5-index)*12} ч`}</small></div>)}
|
||||
<header><div><span class="overline">Последние 72 часа</span><h2 id="timeline-title">Леска активности</h2></div><p>Все одобренные записи · по времени поступления · шаг 12 часов</p></header>
|
||||
<div class="timeline-chart">
|
||||
{buckets.map((bucket, index) => <div class="timeline-slot"><span aria-hidden="true" class="timeline-line" style={`--height:${bucket.count / max * 100}%`}>{bucket.count > 0 && <i></i>}</span><strong>{bucket.count}</strong><small>{`${(6-index)*12}–${(5-index)*12} ч назад`}</small></div>)}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -8,7 +8,7 @@ const { catches } = Astro.props as { catches: Catch[] };
|
||||
const timestamp = item.caught_at ?? item.reported_at;
|
||||
return <article>
|
||||
<div><strong>{item.fish}</strong><span>{item.bait ?? "Приманка не указана"}</span><SourceBadge source={item.source_system} href={item.source_url}/></div>
|
||||
<div><strong>{kg(item.weight_g)}</strong><span>{item.player_name ?? "Анонимно"}</span><time datetime={timestamp} title={new Date(timestamp).toLocaleString("ru-RU")}>{ago(timestamp)}</time></div>
|
||||
<div><strong>{kg(item.weight_g)}</strong><span>{item.player_name ?? "Анонимно"}</span><span>{item.caught_at ? "Время улова" : "Получено · время улова неизвестно"}</span><time datetime={timestamp}>{ago(timestamp)} · {new Date(timestamp).toLocaleString("ru-RU", { timeZone: "UTC" })} UTC</time></div>
|
||||
</article>;
|
||||
})}
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
import SourceBadge from "./SourceBadge.astro";
|
||||
import { ago } from "../lib/api";
|
||||
type Props = { sources: string[]; sourceUrl?: string | null; observedAt: string; completeness: number; confidence?: number | null; status?: "verified" | "unverified" | "incomplete" };
|
||||
const { sources, sourceUrl, observedAt, completeness, confidence = null, status = "verified" } = Astro.props;
|
||||
type Props = { sources: string[]; sourceUrl?: string | null; observedAt: string; completeness?: number | null; confidence?: number | null; status?: "verified" | "unverified" | "incomplete" };
|
||||
const { sources, sourceUrl, observedAt, completeness = null, confidence = null, status = "verified" } = Astro.props;
|
||||
const statusLabels = { verified: "Учтено", unverified: "Ждёт проверки", incomplete: "Неполные данные" };
|
||||
const completenessLabel = completeness >= 100 ? "Полные" : `${Math.max(0, completeness)}% полей`;
|
||||
const completenessLabel = completeness == null ? "Не рассчитана" : `${Math.min(100, Math.max(0, completeness))}% полей`;
|
||||
---
|
||||
<section class="data-passport" aria-label="Паспорт данных">
|
||||
<header><span>Паспорт данных</span><strong data-passport-status={status}>{statusLabels[status]}</strong></header>
|
||||
|
||||
@@ -3,7 +3,8 @@ import SourceBadge from "./SourceBadge.astro";
|
||||
import { ago, kg, type PublicObservation } from "../lib/api";
|
||||
const { signals } = Astro.props as { signals: PublicObservation[] };
|
||||
const groups = [...signals.reduce((map, signal) => {
|
||||
const key = [signal.fish_name, signal.waterbody_name, signal.x, signal.y, signal.weight_g].join("|").toLocaleLowerCase("ru");
|
||||
// Similar fields do not establish that two sources describe the same catch.
|
||||
const key = JSON.stringify([signal.source_system, signal.id]);
|
||||
const current = map.get(key);
|
||||
if (!current) map.set(key, { ...signal, observations: [signal] });
|
||||
else {
|
||||
|
||||
Reference in New Issue
Block a user