feat: paginate fish waterbody detail
This commit is contained in:
@@ -5,16 +5,20 @@ import AtlasBreadcrumbs from "../../../components/AtlasBreadcrumbs.astro";
|
|||||||
import PageHero from "../../../components/PageHero.astro";
|
import PageHero from "../../../components/PageHero.astro";
|
||||||
import StatePanel from "../../../components/StatePanel.astro";
|
import StatePanel from "../../../components/StatePanel.astro";
|
||||||
import Layout from "../../../layouts/Layout.astro";
|
import Layout from "../../../layouts/Layout.astro";
|
||||||
|
import Pagination from "../../../components/Pagination.astro";
|
||||||
import { api, plural, type Activity, type DictionaryItem, type PaginatedActivity } from "../../../lib/api";
|
import { api, plural, type Activity, type DictionaryItem, type PaginatedActivity } from "../../../lib/api";
|
||||||
const { slug, fish: fishSlug } = Astro.params;
|
const { slug, fish: fishSlug } = Astro.params;
|
||||||
const requestedHours = Astro.url.searchParams.get("hours") ?? "72";
|
const requestedHours = Astro.url.searchParams.get("hours") ?? "72";
|
||||||
const hours = ["24", "72", "168"].includes(requestedHours) ? requestedHours : "72";
|
const hours = ["24", "72", "168"].includes(requestedHours) ? requestedHours : "72";
|
||||||
const periodLabel = { "24": "24 часа", "72": "72 часа", "168": "7 дней" }[hours];
|
const periodLabel = { "24": "24 часа", "72": "72 часа", "168": "7 дней" }[hours];
|
||||||
let water: DictionaryItem | undefined, fish: DictionaryItem | undefined, items: Activity[] = [], unavailable = false;
|
const requestedOffset = Number(Astro.url.searchParams.get("offset") ?? 0);
|
||||||
|
const offset = Number.isInteger(requestedOffset) && requestedOffset >= 0 ? requestedOffset : 0;
|
||||||
|
const activityLimit = 24;
|
||||||
|
let water: DictionaryItem | undefined, fish: DictionaryItem | undefined, items: Activity[] = [], totalItems = 0, unavailable = false;
|
||||||
try {
|
try {
|
||||||
const [waters, fishes] = await Promise.all([api<DictionaryItem[]>("/api/v1/waterbodies?limit=500"), api<DictionaryItem[]>("/api/v1/fishes?limit=500")]);
|
const [waters, fishes] = await Promise.all([api<DictionaryItem[]>("/api/v1/waterbodies?limit=500"), api<DictionaryItem[]>("/api/v1/fishes?limit=500")]);
|
||||||
water = waters.find(item => item.slug === slug); fish = fishes.find(item => item.slug === fishSlug);
|
water = waters.find(item => item.slug === slug); fish = fishes.find(item => item.slug === fishSlug);
|
||||||
if (water && fish) { const paginated = await api<PaginatedActivity>(`/api/v1/activity?hours=${hours}&waterbody=${encodeURIComponent(water.slug)}&fish=${encodeURIComponent(fish.slug)}&limit=100`); items = paginated.items; }
|
if (water && fish) { const paginated = await api<PaginatedActivity>(`/api/v1/activity?hours=${hours}&waterbody=${encodeURIComponent(water.slug)}&fish=${encodeURIComponent(fish.slug)}&limit=${activityLimit}&offset=${offset}`); items = paginated.items; totalItems = paginated.total; }
|
||||||
} catch { unavailable = true; }
|
} catch { unavailable = true; }
|
||||||
if (unavailable) {
|
if (unavailable) {
|
||||||
Astro.response.status = 503;
|
Astro.response.status = 503;
|
||||||
@@ -27,7 +31,8 @@ const schema = valid ? { "@type":"CollectionPage", name:`${fish!.name_ru} на $
|
|||||||
---
|
---
|
||||||
<Layout title={valid ? `${fish!.name_ru} на ${water!.name_ru} в RF4 — точки и приманки` : "Страница не найдена — RF4 Spotter"} description={valid ? `Где ловить ${fish!.name_ru} на ${water!.name_ru} в Russian Fishing 4: свежие координаты, приманки, активность и источники.` : "Такого сочетания рыбы и водоёма нет в каталоге."} noindex={!valid || unavailable} structuredData={schema} errorPage={!valid || unavailable}>
|
<Layout title={valid ? `${fish!.name_ru} на ${water!.name_ru} в RF4 — точки и приманки` : "Страница не найдена — RF4 Spotter"} description={valid ? `Где ловить ${fish!.name_ru} на ${water!.name_ru} в Russian Fishing 4: свежие координаты, приманки, активность и источники.` : "Такого сочетания рыбы и водоёма нет в каталоге."} noindex={!valid || unavailable} structuredData={schema} errorPage={!valid || unavailable}>
|
||||||
<AtlasBreadcrumbs items={[{ label: "Водоёмы", href: "/waterbodies" }, { label: water?.name_ru ?? "Не найдено", href: water ? `/waterbodies/${water.slug}` : undefined }, { label: fish?.name_ru ?? "Не найдено" }]} />
|
<AtlasBreadcrumbs items={[{ label: "Водоёмы", href: "/waterbodies" }, { label: water?.name_ru ?? "Не найдено", href: water ? `/waterbodies/${water.slug}` : undefined }, { label: fish?.name_ru ?? "Не найдено" }]} />
|
||||||
<PageHero eyebrow={water?.name_ru ?? "Каталог RF4"} title={fish?.name_ru ?? "Данные не найдены"} description={valid ? `${items.length} ${plural(items.length,["свежая точка","свежие точки","свежих точек"])} за ${periodLabel}.` : undefined} variant={valid ? "pair" : undefined} identity={fish?.name_ru} secondaryIdentity={water?.slug} />
|
<PageHero eyebrow={water?.name_ru ?? "Каталог RF4"} title={fish?.name_ru ?? "Данные не найдены"} description={valid ? `${totalItems} ${plural(totalItems,["свежая точка","свежие точки","свежих точек"])} за ${periodLabel}.` : undefined} variant={valid ? "pair" : undefined} identity={fish?.name_ru} secondaryIdentity={water?.slug} />
|
||||||
{valid && <ActivityPeriodFilter hours={hours} />}
|
{valid && <ActivityPeriodFilter hours={hours} />}
|
||||||
{unavailable ? <StatePanel tone="unavailable" title="Данные временно недоступны" description="Свежие наблюдения сейчас не получены. Попробуйте позднее." /> : !valid ? <StatePanel tone="error" title="Такого сочетания нет в справочнике" actionHref="/" actionLabel="Вернуться на главную" /> : <section class="catalog-results catalog-results--single content-grid"><div>{items.length ? items.map(item => <ActivityCard item={item} periodLabel={periodLabel}/>) : <StatePanel contained={false} title="Свежих точек пока нет" description="Данные появятся после новых подтверждённых наблюдений." />}</div></section>}
|
{unavailable ? <StatePanel tone="unavailable" title="Данные временно недоступны" description="Свежие наблюдения сейчас не получены. Попробуйте позднее." /> : !valid ? <StatePanel tone="error" title="Такого сочетания нет в справочнике" actionHref="/" actionLabel="Вернуться на главную" /> : <section class="catalog-results catalog-results--single content-grid"><div>{items.length ? items.map(item => <ActivityCard item={item} periodLabel={periodLabel}/>) : <StatePanel contained={false} title="Свежих точек пока нет" description="Данные появятся после новых подтверждённых наблюдений." />}</div></section>}
|
||||||
|
{valid && <Pagination path={`/waterbodies/${water!.slug}/${fish!.slug}`} params={new URLSearchParams({ hours })} total={totalItems} limit={activityLimit} offset={offset} itemLabel="точек" />}
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|||||||
Reference in New Issue
Block a user