feat: group duplicate field signals
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
---
|
||||
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");
|
||||
const current = map.get(key);
|
||||
if (!current) map.set(key, { ...signal, observations: [signal] });
|
||||
else {
|
||||
current.observations.push(signal);
|
||||
current.last_seen_at = new Date(signal.last_seen_at) > new Date(current.last_seen_at) ? signal.last_seen_at : current.last_seen_at;
|
||||
current.missing_fields = [...new Set([...current.missing_fields, ...signal.missing_fields])];
|
||||
if (signal.quality === "incomplete") current.quality = "incomplete";
|
||||
}
|
||||
return map;
|
||||
}, new Map<string, PublicObservation & { observations: PublicObservation[] }>()).values()];
|
||||
---
|
||||
<section class="signal-section content-grid" aria-labelledby="signals-title">
|
||||
<div class="signal-heading"><div><span class="overline">Сырые данные источников</span><h2 id="signals-title">Полевые сигналы</h2><p>Показываем сразу, даже если часть полей отсутствует. Эти карточки не участвуют в расчёте активности до полного подтверждения.</p></div><span class="signal-count">{signals.length} сигналов · {groups.length} сюжетов</span></div>
|
||||
<div class="signal-grid">{groups.map(signal => {
|
||||
const provenance = [...new Map(signal.observations.map(item => [`${item.source_system}|${item.source_url}`, item])).values()];
|
||||
return <article class="signal-card" data-quality={signal.quality}>
|
||||
<div class="signal-card__top"><div class="source-strip">{provenance.map(item => <SourceBadge source={item.source_system} href={item.source_url}/>)}</div><span class="quality-chip"><i>{signal.quality === "incomplete" ? "!" : "?"}</i> {signal.quality === "incomplete" ? "Неполные данные" : "Ждёт проверки"}</span></div>
|
||||
<h3>{signal.fish_name}</h3><p>{signal.waterbody_name}</p>
|
||||
<dl><div><dt>Точка</dt><dd>{signal.x == null || signal.y == null ? "не указана" : `${signal.x}:${signal.y}`}</dd></div><div><dt>Вес</dt><dd>{signal.weight_g == null ? "не указан" : kg(signal.weight_g)}</dd></div><div><dt>Получено</dt><dd>{ago(signal.last_seen_at)}</dd></div></dl>
|
||||
{signal.observations.length > 1 && <p class="repeat-note">Совпали {signal.observations.length} наблюдения · все источники сохранены</p>}
|
||||
<p class="missing-note"><span>{signal.missing_fields.length ? "Не хватает:" : "Статус:"}</span> {signal.missing_fields.join(", ") || "проверяется соответствие справочнику"}</p>
|
||||
</article>;
|
||||
})}</div>
|
||||
</section>
|
||||
<style>
|
||||
.repeat-note{position:relative;z-index:1;margin:-8px 0 10px!important;padding:6px 9px;border-left:2px solid #6b8d74;color:#496357!important;font-size:10px!important}
|
||||
.signal-card__top .source-strip{margin:0}
|
||||
</style>
|
||||
Reference in New Issue
Block a user