fix(ui): ограничить карточки оптимизатора с сохранением URL

This commit is contained in:
Denozordec committed 2026-10-04 18:03:59 +07:00
1 parent 7265709b1c
commit 059fd93520
10 files changed
+123 -11

No files matched your search

+45
View File
@@ -0,0 +1,45 @@
"use client"
import { useEffect, useMemo, type ReactNode } from "react"
import { useSearchParams } from "next/navigation"
import type { HomeEntry } from "@/lib/route-optimizer-data"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
export function OptimizerHomesList({ homes, children }: { homes: HomeEntry[]; children: (entry: HomeEntry) => ReactNode }) {
const params = useSearchParams()
const query = params.get("home_q") ?? ""
const rawPage = params.get("home_page") ?? "1"
const requestedPage = /^[1-9]\d*$/.test(rawPage) && Number.isSafeInteger(Number(rawPage)) ? Number(rawPage) - 1 : 0
const filtered = useMemo(() => {
const value = query.trim().toLowerCase()
return value ? homes.filter(({ home }) => `${home.label} ${home.id} ${home.ip} ${home.site} ${home.country}`.toLowerCase().includes(value)) : homes
}, [homes, query])
const pages = Math.max(1, Math.ceil(filtered.length / 5))
const page = Math.min(requestedPage, pages - 1)
function change(queryValue: string, pageValue: number, push = false) {
const url = new URL(window.location.href)
if (queryValue) url.searchParams.set("home_q", queryValue); else url.searchParams.delete("home_q")
if (pageValue) url.searchParams.set("home_page", String(pageValue + 1)); else url.searchParams.delete("home_page")
window.history[push ? "pushState" : "replaceState"](null, "", url.pathname + url.search + url.hash)
}
useEffect(() => {
if (requestedPage < pages) return
const url = new URL(window.location.href)
if (page) url.searchParams.set("home_page", String(page + 1)); else url.searchParams.delete("home_page")
window.history.replaceState(null, "", url.pathname + url.search + url.hash)
}, [requestedPage, pages, page])
const controlled = homes.length > 5 || !!query || requestedPage > 0
return <section aria-label="Домашние устройства оптимизатора" className="flex min-w-0 flex-col gap-4">
{controlled && <div className="rounded-xl border border-border bg-background p-4 flex min-w-0 flex-col gap-3">
<Input aria-label="Поиск домашних устройств оптимизатора" placeholder="Имя, IP или площадка…" value={query} onChange={event => change(event.target.value, 0)} />
<p role="status" className="text-xs text-muted-foreground">Показаны {filtered.length ? page * 5 + 1 : 0}–{Math.min((page + 1) * 5, filtered.length)} из {filtered.length} устройств. Всего {homes.length}.</p>
<div className="flex flex-wrap gap-2">
<Button type="button" variant="outline" className="min-h-11" disabled={!page} aria-label="Предыдущая страница домашних устройств" onClick={() => change(query, page - 1, true)}>Назад</Button>
<Button type="button" variant="outline" className="min-h-11" disabled={page >= pages - 1} aria-label="Следующая страница домашних устройств" onClick={() => change(query, page + 1, true)}>Далее</Button>
</div>
{!filtered.length && <p className="text-sm text-muted-foreground">Домашние устройства по запросу не найдены.</p>}
</div>}
{filtered.slice(page * 5, (page + 1) * 5).map(children)}
</section>
}