From 3260f914958321ef24e92c4adc8f825d0c8bd045 Mon Sep 17 00:00:00 2001 From: IK Date: Sun, 20 Sep 2026 20:34:42 +0700 Subject: [PATCH] feat: clarify spot card call to action --- apps/web/src/components/ActivityCard.astro | 1 + apps/web/src/styles/query-context.css | 1 + apps/web/tests/evidence-card.spec.ts | 2 ++ docs/ROADMAP.md | 2 +- 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/web/src/components/ActivityCard.astro b/apps/web/src/components/ActivityCard.astro index e4430fe..0836ade 100644 --- a/apps/web/src/components/ActivityCard.astro +++ b/apps/web/src/components/ActivityCard.astro @@ -20,6 +20,7 @@ const precision = { exact: "точные", approximate: "приблизител

{item.explanation}

{limited ? "Нужно ещё подтверждений" : item.best_bait ? "Работает сейчас" : "Наживка не указана"}{item.best_bait ?? "не указана"}
+ Открыть точку
{kg(item.average_weight_g)}средний вес
{kg(item.max_weight_g)}макс. вес
diff --git a/apps/web/src/styles/query-context.css b/apps/web/src/styles/query-context.css index a1486b2..781deef 100644 --- a/apps/web/src/styles/query-context.css +++ b/apps/web/src/styles/query-context.css @@ -7,5 +7,6 @@ .detail-action-heading{margin:27px 0 8px;font:400 24px Georgia,serif;color:var(--lime)} .tackle-card-passport{grid-column:1/-1;min-width:0}.tackle-card-passport .data-passport{margin-top:14px} .spot-hero__actions{display:flex;align-items:center;flex-wrap:wrap;gap:8px}.spot-hero__actions small{color:#a9b8b5;font-size:11px}.plan-save[data-saved="true"]{background:var(--lime);color:var(--deep)} +.card-cta{display:inline-flex;align-items:center;gap:8px;margin-top:16px;color:var(--text);font-size:12px;font-weight:750;text-underline-offset:3px}.card-cta span{color:var(--success);font-size:17px;line-height:1} @media(max-width:720px){.spot-hero__actions{align-items:flex-start;flex-direction:column}.spot-hero__actions small{min-height:15px}} @media(max-width:720px){.query-summary{display:block;padding:16px 14px 0;width:100%}.query-summary h2{font-size:23px}.query-summary__filters{justify-content:flex-start;flex-wrap:nowrap;overflow-x:auto;padding-top:11px;scroll-snap-type:x proximity;scrollbar-width:none}.query-summary__filters::-webkit-scrollbar{display:none}.query-summary__filters span{flex:0 0 auto;scroll-snap-align:start}.query-summary__filters a{position:sticky;right:0;flex:0 0 auto;padding:6px 10px;background:var(--paper)}} diff --git a/apps/web/tests/evidence-card.spec.ts b/apps/web/tests/evidence-card.spec.ts index 223e6af..a1f9620 100644 --- a/apps/web/tests/evidence-card.spec.ts +++ b/apps/web/tests/evidence-card.spec.ts @@ -7,11 +7,13 @@ test("activity cards explain freshness, sample size and coordinate precision", a const count = await passport.count(); expect(count).toBeGreaterThan(0); const first = passport.first(); + const firstCard = page.locator(".spot-card").first(); await expect(first).toContainText("Свежесть"); await expect(first).toContainText("Наблюдения"); await expect(first).toContainText("Игроки"); await expect(first).toContainText("Координаты"); await expect(first).toContainText("Период"); + await expect(firstCard).toContainText("Открыть точку"); if (await first.locator('[data-passport-status="incomplete"]').count()) { await expect(first).toContainText("Минимум для рекомендации: 3 наблюдения"); } diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index bdfcf42..ca869d7 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -93,7 +93,7 @@ Результаты сравнительного аудита и ссылки сохранены в [ux-competitive-audit.md](ux-competitive-audit.md). Целевой критерий проекта: RF4 Spotter должен быстрее и честнее отвечать на вопрос «куда идти, на что ловить и почему этому можно доверять», чем каталог или лента community-постов. - [ ] **U01 · UX-контракт и scorecard.** Контракт маршрутов, смысловой порядок ответа, словарь статусов и целевые метрики зафиксированы в [ux-contract.md](ux-contract.md); для home, spot, waterbody, plan и tackle описаны первый ответ и обязательное объяснение. Осталось провести ручной task-based review и собрать completion rate на пилоте. -- [ ] **U02 · Главный сценарий «рыба → водоём → точка → снасть».** Главная уже сохраняет рыбу, водоём, период и сортировку в shareable URL и после поиска явно показывает контекст запроса, включая мобильный режим; empty-state предлагает вернуться к полному набору данных. Осталось показать 3–5 вариантов с понятным CTA и добавить режим map. Acceptance: первый полезный вариант виден без регистрации, back/refresh сохраняют контекст, mobile не теряет фильтры. +- [ ] **U02 · Главный сценарий «рыба → водоём → точка → снасть».** Главная сохраняет рыбу, водоём, период и сортировку в shareable URL, явно показывает контекст запроса и теперь даёт текстовый CTA «Открыть точку» на каждой карточке, включая mobile; empty-state предлагает вернуться к полному набору данных. Осталось показать 3–5 вариантов и добавить режим map. Acceptance: первый полезный вариант виден без регистрации, back/refresh сохраняют контекст, mobile не теряет фильтры. - [ ] **U03 · Evidence/trust card.** Общий evidence-контракт используется на activity-карточках, detail точки, водоёма и карточках снастей: freshness с текстовым `Свежо`/`Устарело`, явный период расчёта, completeness, confidence/статус, source badges и доступные доменные поля; при выборке меньше 3 явно показан минимум для рекомендации. Targeted E2E и unit-тест проверяют публичные пути, период и 48-часовой порог. Осталось добавить конфликт источников. - [ ] **U04 · List/map и progressive disclosure.** List остаётся честным базовым режимом: фильтры, сортировка, URL-состояние и evidence-карточки уже работают без имитации координатной карты. Следующий шаг — единый list/map-контракт после подтверждения геометрии; на mobile карта должна открываться отдельным действием. Вторичные raw/provenance-поля не исчезают и раскрываются по запросу. - [ ] **U05 · Mobile-first и сохранённый план рыбалки.** `/plan` поддерживает список до 5 локальных вариантов, удаление, очистку, переход к точке, print/PDF и восстановление из shareable URL; кнопка «Поделиться планом» использует native share или clipboard fallback. Detail-кнопка сохраняет данные с `aria-pressed` и восстанавливается после reload. Осталось сделать отдельную проверку print на 320/390 px и расширить сравнение полями метода/риска.