feat: filter tackle catalog by unlock level
This commit is contained in:
@@ -12,6 +12,8 @@ const category = params.get("category") ?? "";
|
||||
const search = params.get("q") ?? "";
|
||||
const brand = params.get("brand") ?? "";
|
||||
const family = params.get("family") ?? "";
|
||||
const unlockLevelParam = params.get("unlock_level") ?? "";
|
||||
const unlockLevel = /^\d+$/.test(unlockLevelParam) ? unlockLevelParam : "";
|
||||
const requestedOffset = Number(params.get("offset") ?? 0);
|
||||
const limit = 48;
|
||||
const offset = Number.isInteger(requestedOffset) && requestedOffset >= 0 ? requestedOffset : 0;
|
||||
@@ -20,6 +22,7 @@ if (category) query.set("category", category);
|
||||
if (search) query.set("q", search);
|
||||
if (brand) query.set("brand", brand);
|
||||
if (family) query.set("family", family);
|
||||
if (unlockLevel) query.set("unlock_level", unlockLevel);
|
||||
let result: PaginatedTackleItems = { items: [], total: 0, limit, offset }, rigs: PaginatedRigs = { items: [], total: 0, limit, offset }, unavailable = false, rigsUnavailable = false;
|
||||
const [itemsResult, rigsResult] = await Promise.allSettled([
|
||||
api<PaginatedTackleItems>(`/api/v1/tackle/items?${query}`),
|
||||
@@ -34,6 +37,7 @@ if (category) filterParams.set("category", category);
|
||||
if (search) filterParams.set("q", search);
|
||||
if (brand) filterParams.set("brand", brand);
|
||||
if (family) filterParams.set("family", family);
|
||||
if (unlockLevel) filterParams.set("unlock_level", unlockLevel);
|
||||
---
|
||||
<Layout title="Снасти и приманки RF4 — RF4 Spotter" description="Канонический каталог снастей, приманок и сборок Russian Fishing 4 с источниками и отметками неполноты." noindex={unavailable} errorPage={unavailable}>
|
||||
<PageHero eyebrow="Канонический справочник" title="Снасти и приманки" description="Показываем только подтверждённые карточки. Пустые поля явно отмечены и не заменяются догадками." variant="fish" count={result.total} />
|
||||
@@ -42,11 +46,12 @@ if (family) filterParams.set("family", family);
|
||||
<label>Поиск<input name="q" value={search} maxlength="100" placeholder="Название снасти или приманки" /></label>
|
||||
<label>Бренд<input name="brand" value={brand} maxlength="100" placeholder="Например, RF4" /></label>
|
||||
<label>Семейство<input name="family" value={family} maxlength="100" placeholder="Название семейства" /></label>
|
||||
<label>Уровень открытия<input name="unlock_level" value={unlockLevel} inputmode="numeric" pattern="[0-9]+" min="0" maxlength="4" placeholder="Например, 20" /></label>
|
||||
<button data-action="primary">Фильтровать</button>
|
||||
{(category || search || brand || family) && <a data-action="quiet" href="/tackle">Сбросить</a>}
|
||||
{(category || search || brand || family || unlockLevel) && <a data-action="quiet" href="/tackle">Сбросить</a>}
|
||||
<a data-action="quiet" href="/tackle/analytics">Сочетания снастей</a>
|
||||
</form>
|
||||
{unavailable ? <StatePanel tone="unavailable" title="Каталог временно недоступен" description="Не показываем непроверенные карточки. Попробуйте обновить страницу позже." /> : result.items.length ? <section class="catalog-grid content-grid" aria-label="Карточки снастей">{result.items.map(item => <a href={`/tackle/items/${item.id}`}><TackleGlyph name={item.name} size={32} /><span>{categoryLabels[item.category] ?? item.category}</span><strong>{item.name}</strong><small>{[item.brand, item.family].filter(Boolean).join(" · ") || "Характеристики уточняются"}</small><div class="tackle-card-passport"><DataPassport sources={item.source_system ? [item.source_system] : []} sourceUrl={item.source_url} observedAt={item.source_checked_at} completeness={item.missing_fields.length ? null : 100} status={item.missing_fields.length ? "incomplete" : item.source_checked_at ? "verified" : "unverified"}/></div></a>)}</section> : <StatePanel title="Подтверждённых карточек пока нет" description="Каталог заполнится после разрешённой загрузки и ручной проверки источников." />}
|
||||
{unavailable ? <StatePanel tone="unavailable" title="Каталог временно недоступен" description="Не показываем непроверенные карточки. Попробуйте обновить страницу позже." /> : result.items.length ? <section class="catalog-grid content-grid" aria-label="Карточки снастей">{result.items.map(item => <a href={`/tackle/items/${item.id}`}><TackleGlyph name={item.name} size={32} /><span>{categoryLabels[item.category] ?? item.category}</span><strong>{item.name}</strong><small>{[item.brand, item.family].filter(Boolean).join(" · ") || "Характеристики уточняются"}</small>{item.unlock_level !== null && <small>Открывается с уровня {item.unlock_level}</small>}<div class="tackle-card-passport"><DataPassport sources={item.source_system ? [item.source_system] : []} sourceUrl={item.source_url} observedAt={item.source_checked_at} completeness={item.missing_fields.length ? null : 100} status={item.missing_fields.length ? "incomplete" : item.source_checked_at ? "verified" : "unverified"}/></div></a>)}</section> : <StatePanel title="Подтверждённых карточек пока нет" description="Каталог заполнится после разрешённой загрузки и ручной проверки источников." />}
|
||||
{!unavailable && <section class="rig-catalog content-grid" aria-labelledby="rig-catalog-title"><div class="section-heading"><div><span class="overline">Сборки снастей</span><h2 id="rig-catalog-title">Сборки</h2></div><span class="result-count">{rigs.total} всего</span></div>{rigsUnavailable ? <StatePanel contained={false} tone="unavailable" title="Сборки временно недоступны" description="Карточки отдельных снастей продолжают работать независимо." /> : rigs.items.length ? <div class="catalog-grid">{rigs.items.map(rig => <a href={`/tackle/rigs/${rig.id}`}><TackleGlyph name={rig.name} size={32} /><span>Сборка · {rig.component_count} компонентов</span><strong>{rig.name}</strong><small>{rig.source_system ?? "Источник не указан"}</small><div class="tackle-card-passport"><DataPassport sources={rig.source_system ? [rig.source_system] : []} sourceUrl={rig.source_url} observedAt={rig.source_checked_at} completeness={rig.missing_fields.length ? null : 100} status={rig.missing_fields.length ? "incomplete" : rig.source_checked_at ? "verified" : "unverified"}/></div></a>)}</div> : <StatePanel contained={false} title="Подтверждённых сборок пока нет" description="Сборки появятся после разрешённой загрузки и проверки источников." />}</section>}
|
||||
{!unavailable && <Pagination path="/tackle" params={filterParams} total={result.total} limit={limit} offset={offset} itemLabel="карточек" />}
|
||||
</Layout>
|
||||
|
||||
Reference in New Issue
Block a user