feat: add public rig detail pages
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
---
|
||||
import AtlasBreadcrumbs from "../../../components/AtlasBreadcrumbs.astro";
|
||||
import DataPassport from "../../../components/DataPassport.astro";
|
||||
import Layout from "../../../layouts/Layout.astro";
|
||||
import PageHero from "../../../components/PageHero.astro";
|
||||
import StatePanel from "../../../components/StatePanel.astro";
|
||||
import TackleGlyph from "../../../components/TackleGlyph.astro";
|
||||
import { ApiError, api, type Rig } from "../../../lib/api";
|
||||
import { safeHttpUrl } from "../../../lib/urls";
|
||||
|
||||
const { id } = Astro.params;
|
||||
let rig: Rig | undefined;
|
||||
let unavailable = false;
|
||||
try {
|
||||
rig = await api<Rig>(`/api/v1/tackle/rigs/${encodeURIComponent(id ?? "")}`);
|
||||
} catch (error) {
|
||||
unavailable = !(error instanceof ApiError) || error.status >= 500;
|
||||
}
|
||||
if (!rig && !unavailable) Astro.response.status = 404;
|
||||
if (unavailable) {
|
||||
Astro.response.status = 503;
|
||||
Astro.response.headers.set("Retry-After", "60");
|
||||
Astro.response.headers.set("Cache-Control", "no-store");
|
||||
}
|
||||
|
||||
const roleLabels: Record<string, string> = { lure: "Приманка", bait: "Наживка", rig: "Монтаж", rod: "Удилище", reel: "Катушка", line: "Леска", hook: "Крючок", float: "Поплавок", sinker: "Груз" };
|
||||
const missingLabels: Record<string, string> = { source_url: "ссылка на источник", source_checked_at: "дата проверки" };
|
||||
const missing = rig?.missing_fields.map((field) => missingLabels[field] ?? field) ?? [];
|
||||
const sourceHref = safeHttpUrl(rig?.source_url);
|
||||
---
|
||||
<Layout title={rig ? `${rig.name} — монтажи RF4` : "Монтаж не найден — RF4 Spotter"} description={rig ? `Подтверждённая карточка монтажа ${rig.name} в каталоге RF4 с составом и источником.` : "Такой карточки монтажа нет в каталоге RF4."} noindex={!rig || unavailable} errorPage={!rig || unavailable}>
|
||||
<AtlasBreadcrumbs items={[{ label: "Снасти", href: "/tackle" }, { label: "Монтажи" }, { label: rig?.name ?? "Не найдено" }]} />
|
||||
<PageHero eyebrow="Канонический монтаж" title={rig?.name ?? (unavailable ? "Каталог недоступен" : "Монтаж не найден")} description={rig ? "Состав монтажа показан в исходном порядке; неподтверждённые значения не дополняются догадками." : undefined} variant={rig ? "fish" : undefined} identity={rig?.name} />
|
||||
{unavailable ? <StatePanel tone="unavailable" title="Карточка временно недоступна" description="Каталог не ответил. Непроверенный состав не показываем." /> : !rig ? <StatePanel tone="error" title="Такого монтажа нет в каталоге" actionHref="/tackle" actionLabel="Открыть каталог снастей" /> : <>
|
||||
<section class="tackle-detail content-grid" aria-label="Состав монтажа">
|
||||
<div class="tackle-detail__identity"><TackleGlyph name={rig.name} size={72} /><span class="overline">Монтаж</span><h2>{rig.name}</h2><p>{rig.source_external_id ? `Идентификатор источника: ${rig.source_external_id}` : "Внешний идентификатор не указан."}</p></div>
|
||||
<div><span class="overline">Компоненты</span><ol class="rig-components">{rig.components.length ? rig.components.map(component => <li><span>{roleLabels[component.role] ?? component.role}</span>{component.tackle_item_id ? <a href={`/tackle/items/${component.tackle_item_id}`}>{component.raw_value ?? "Карточка снасти"}</a> : <span>{component.raw_value ?? "Значение не указано"}</span>}</li>) : <li>Состав не указан.</li>}</ol>{missing.length > 0 && <p class="tackle-detail__missing"><strong>Не хватает:</strong> {missing.join(", ")}.</p>}{sourceHref && <a data-action="secondary" href={sourceHref} rel="noreferrer">Открыть первоисточник</a>}</div>
|
||||
</section>
|
||||
<DataPassport sources={rig.source_system ? [rig.source_system] : []} sourceUrl={rig.source_url} observedAt={rig.source_checked_at} completeness={missing.length ? null : 100} status={missing.length ? "incomplete" : rig.source_checked_at ? "verified" : "unverified"} />
|
||||
</>}
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
.rig-components { display: grid; gap: 10px; margin: 14px 0 20px; padding-left: 22px; }
|
||||
.rig-components li { padding-left: 4px; color: var(--text-secondary); }
|
||||
.rig-components li > span:first-child { display: inline-block; min-width: 110px; margin-right: 8px; color: var(--text-subtle); font-size: 11px; font-weight: 750; text-transform: uppercase; letter-spacing: .06em; }
|
||||
.rig-components a { color: inherit; text-underline-offset: 3px; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user