diff --git a/apps/web/src/pages/waterbodies/[slug].astro b/apps/web/src/pages/waterbodies/[slug].astro index a104dcb..a3d9802 100644 --- a/apps/web/src/pages/waterbodies/[slug].astro +++ b/apps/web/src/pages/waterbodies/[slug].astro @@ -8,6 +8,7 @@ import PageHero from "../../components/PageHero.astro"; import StatePanel from "../../components/StatePanel.astro"; import DataPassport from "../../components/DataPassport.astro"; import Layout from "../../layouts/Layout.astro"; +import Pagination from "../../components/Pagination.astro"; import { api, plural, type Activity, type DictionaryItem, type MediaAsset, type PaginatedActivity } from "../../lib/api"; import { findMediaByLabel, mediaRoleLabel } from "../../lib/media"; import { safeHttpUrl } from "../../lib/urls"; @@ -15,12 +16,15 @@ const { slug } = Astro.params; const requestedHours = Astro.url.searchParams.get("hours") ?? "72"; const hours = ["24", "72", "168"].includes(requestedHours) ? requestedHours : "72"; const periodLabel = { "24": "24 часа", "72": "72 часа", "168": "7 дней" }[hours]; -let water: DictionaryItem | undefined, items: Activity[] = [], media: MediaAsset[] = [], 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, items: Activity[] = [], media: MediaAsset[] = [], totalItems = 0, unavailable = false; try { const [waters, waterMedia] = await Promise.all([api("/api/v1/waterbodies?limit=500"), api("/api/v1/media/catalog?entity_type=waterbody")]); media = waterMedia; water = waters.find(item => item.slug === slug); - if (water) { const paginated = await api(`/api/v1/activity?hours=${hours}&waterbody=${encodeURIComponent(water.slug)}&limit=100`); items = paginated.items; } + if (water) { const paginated = await api(`/api/v1/activity?hours=${hours}&waterbody=${encodeURIComponent(water.slug)}&limit=${activityLimit}&offset=${offset}`); items = paginated.items; totalItems = paginated.total; } } catch { unavailable = true; } if (unavailable) { Astro.response.status = 503; @@ -36,11 +40,12 @@ const schema = water ? { "@context":"https://schema.org", "@type":"CollectionPag --- - + {water && } {coverImage &&
{mediaRoleLabel("waterbody_cover")}

{water!.name_ru}

Материал показан с прямой атрибуцией. Координаты активных точек ниже относятся к данным наблюдений, а не к геометрии изображения.

} {supportingMedia.length > 0 &&
{supportingMedia.map(asset =>
{mediaRoleLabel(asset.media_role ?? "")}
)}
} {water && (water.description || water.unlock_level !== undefined || water.source_url || water.source_fish_species) &&
Канонические сведения

{water.name_ru}

{water.description &&

{water.description}

}{water.unlock_level !== undefined && water.unlock_level !== null &&

Открывается с уровня: {water.unlock_level}

}{water.source_fish_species?.length &&

В карточке источника указано видов рыб: {water.source_fish_species.length}.

}{water.source_aliases?.length &&

Алиасы источника: {water.source_aliases.join(", ")}.

}
Источник{sourceHref ?

{water.source_system ?? "Внешний источник"} · исходная карточка

:

Источник для описания ещё не подтверждён.

}{water.source_point_urls?.length &&

Сохранено ссылок на точки: {water.source_point_urls.length}.

}{water.source_image_urls?.length &&

Изображений-кандидатов: {water.source_image_urls.length}; публикация требует отдельной проверки.

}{water.source_checked_at &&

Проверено: {new Date(water.source_checked_at).toLocaleDateString("ru-RU")}

}
} {water && } {unavailable ? : !water ? :
{items.length ? items.map(item => ) : }
} + {water && }