diff --git a/apps/web/src/components/ActivityCard.astro b/apps/web/src/components/ActivityCard.astro index a17ae1f..ee1e6c3 100644 --- a/apps/web/src/components/ActivityCard.astro +++ b/apps/web/src/components/ActivityCard.astro @@ -1,12 +1,13 @@ --- import type { Activity } from "../lib/api"; import { activityLevel, ago, kg, plural } from "../lib/api"; +import FishingIcon from "./FishingIcon.astro"; const { item } = Astro.props as { item: Activity }; const level = activityLevel(item.activity_score); const limited = item.catches < 3; --- {String(item.activity_score).padStart(2,"0")} -
{item.waterbody}{level.short}{limited && Данных мало}

{item.fish}

⌖ {item.x}:{item.y}◷ {ago(item.last_confirmed_at)}

{item.explanation}

Работает сейчас{item.best_bait ?? "не указана"}
-
{item.catches}{plural(item.catches, ["улов", "улова", "уловов"])}
{item.unique_players}{plural(item.unique_players, ["игрок", "игрока", "игроков"])}
{kg(item.average_weight_g)}средний вес
{item.confidence_score}%уверенность
+
{item.waterbody}{level.short}{limited && Данных мало}

{item.fish}

{item.x}:{item.y} {ago(item.last_confirmed_at)}

{item.explanation}

Работает сейчас{item.best_bait ?? "не указана"}
+
{item.catches}{plural(item.catches, ["улов", "улова", "уловов"])}
{item.unique_players}{plural(item.unique_players, ["игрок", "игрока", "игроков"])}
{kg(item.average_weight_g)}средний вес
{item.confidence_score}%уверенность
diff --git a/apps/web/src/components/FishingIcon.astro b/apps/web/src/components/FishingIcon.astro new file mode 100644 index 0000000..92d4a48 --- /dev/null +++ b/apps/web/src/components/FishingIcon.astro @@ -0,0 +1,17 @@ +--- +type IconName = "hook" | "float" | "trophy" | "plus" | "pin" | "clock" | "ripple" | "angler" | "scale" | "lure" | "arrow"; +const { name, size = 18, label } = Astro.props as { name: IconName; size?: number; label?: string }; +--- + + {name === "hook" && <>} + {name === "float" && <>} + {name === "trophy" && <>} + {name === "plus" && <>} + {name === "pin" && <>} + {name === "clock" && <>} + {name === "ripple" && <>} + {name === "angler" && <>} + {name === "scale" && <>} + {name === "lure" && <>} + {name === "arrow" && <>} + diff --git a/apps/web/src/layouts/Layout.astro b/apps/web/src/layouts/Layout.astro index 91b99fb..99e7fab 100644 --- a/apps/web/src/layouts/Layout.astro +++ b/apps/web/src/layouts/Layout.astro @@ -1,5 +1,6 @@ --- import "../styles/global.css"; +import FishingIcon from "../components/FishingIcon.astro"; const { title = "RF4 Spotter" } = Astro.props; const path = Astro.url.pathname; --- @@ -8,11 +9,11 @@ const path = Astro.url.pathname; {title}
- Ни хвоста,ни чешуи - + Ни хвоста,ни чешуи +

Свежие данные и честная оценка

- + diff --git a/apps/web/src/pages/index.astro b/apps/web/src/pages/index.astro index ed0fe6f..e1a98db 100644 --- a/apps/web/src/pages/index.astro +++ b/apps/web/src/pages/index.astro @@ -1,6 +1,7 @@ --- import Layout from "../layouts/Layout.astro"; import ActivityCard from "../components/ActivityCard.astro"; +import FishingIcon from "../components/FishingIcon.astro"; import { activityLevel, ago, api, kg, plural, type Activity, type DictionaryItem } from "../lib/api"; const params = Astro.url.searchParams; @@ -30,7 +31,7 @@ const leaderLevel = items[0] ? activityLevel(items[0].activity_score) : null;
За выбранный период

Горячие точки

{items.length} {plural(items.length, ["точка", "точки", "точек"])}
{filterError ?

Некорректные фильтры

Выберите период и сортировку из предложенных значений.

Сбросить фильтры
: unavailable ?

Источник временно недоступен

Не показываем устаревшие догадки. Попробуйте позже.

: items.length ?
{items.map(item => )}
:

Пока нет свежих данных

Для выбранных фильтров нет одобренных наблюдений. Расширьте период или выберите другой водоём.

}
- {items[0] && leaderLevel && } + {items[0] && leaderLevel && }
Как читать данные

Не обещаем рыбу.
Показываем факты.

01

Свежесть

Чем старше сообщение, тем меньше оно влияет на активность.

02

Разные игроки

Десять уловов одного человека не равны десяти подтверждениям.

03

Уверенность

Каждая оценка объясняет, сколько данных за ней стоит.

diff --git a/apps/web/src/styles/global.css b/apps/web/src/styles/global.css index 7b0742c..4dcfb60 100644 --- a/apps/web/src/styles/global.css +++ b/apps/web/src/styles/global.css @@ -15,3 +15,9 @@ footer{min-height:118px;background:var(--deep);color:#dbe4df;padding:28px max(32 @media(max-width:720px){.moderation-app{width:calc(100% - 28px)}.moderation-card{grid-template-columns:1fr}.moderation-proof{grid-row:2}.moderation-actions{grid-column:1;display:block}.moderation-actions>div{margin-top:12px}.admin-login{display:block}.admin-login button{width:100%;margin-top:12px}} @media(max-width:480px){.filters{grid-template-columns:1fr}.spot-stats strong{font-size:16px}.topbar nav{gap:4px}.topbar nav a{padding:0 5px}.brand-name{font-size:14px}.brand-name strong{font-size:17px}.moderation-actions>div{display:grid}.moderation-actions button{width:100%}} @media(prefers-reduced-motion:reduce){*{scroll-behavior:auto!important;transition-duration:.01ms!important}} + +/* RF4 field-guide iconography and float activity gauge */ +.fish-icon{display:inline-block;flex:none;vertical-align:-.2em}.brand-mark .fish-icon{transform:rotate(-12deg)}.topbar nav a.active .fish-icon{color:#6b9229}.spot-meta span{display:flex;align-items:center;gap:4px}.bait-line>.fish-icon,.best-lure .fish-icon{color:#e19a43;transform:rotate(14deg)}.card-arrow{font-size:0}.detail-head>a{transition:background .2s ease,color .2s ease}.detail-head>a:hover{background:var(--lime);color:var(--deep)} +.float-gauge{width:122px;height:142px;position:relative;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;padding-bottom:8px;isolation:isolate}.float-gauge:before,.float-gauge:after{content:"";position:absolute;left:3px;right:3px;border:1px solid #ffffff1d;border-radius:50%}.float-gauge:before{height:28px;bottom:25px;box-shadow:0 0 0 10px #ffffff08}.float-gauge:after{height:15px;bottom:32px}.float-gauge__line{position:absolute;left:50%;top:0;width:1px;height:105px;background:linear-gradient(#ffffff55,#ffffff12);z-index:-1}.float-gauge__water{position:absolute;left:11px;right:11px;bottom:39px;height:calc(var(--level)*.62);max-height:62px;background:linear-gradient(180deg,#c9f45b08,#c9f45b36);border-top:1px solid var(--lime);clip-path:polygon(0 6px,18% 0,36% 6px,54% 0,72% 6px,90% 0,100% 4px,100% 100%,0 100%)}.float-gauge__bob{position:absolute;left:50%;bottom:calc(39px + var(--level)*.62);width:18px;height:54px;transform:translate(-50%,50%);border:1px solid #e6f8ad;border-radius:50% 50% 42% 42%;background:linear-gradient(180deg,var(--lime) 0 35%,#f4f7ed 36% 58%,#e47e3b 59%);box-shadow:0 5px 18px #c9f45b38}.float-gauge__bob i{position:absolute;left:50%;top:-24px;width:1px;height:25px;background:#dfe9dc}.float-gauge strong{position:relative;z-index:2;font:400 31px/1 Georgia,serif;text-shadow:0 2px 8px var(--deep)}.float-gauge small{position:relative;z-index:2;color:#a9b8b5;font-size:9px;text-transform:uppercase}.best-lure>div{grid-template-columns:25px 1fr auto} +@media(max-width:720px){.float-gauge{width:100px}} +@media(prefers-reduced-motion:no-preference){.pulse-orb span{animation:ripple-pulse 2.4s ease-out infinite}.float-gauge__bob{animation:bob 2.8s ease-in-out infinite}}@keyframes ripple-pulse{50%{box-shadow:0 0 0 15px #c9f45b08}}@keyframes bob{50%{translate:0 -3px}} diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 7dc223b..90375e7 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -71,6 +71,7 @@ - [ ] Провести Lighthouse-проверку основных страниц и устранить критические проблемы производительности. - [x] Провести UI/UX-аудит desktop/mobile и сформировать приоритетный план (`docs/UI_UX_AUDIT.md`). - [x] Исправить единую шкалу активности и русские числительные во всех публичных представлениях (пакет A UI/UX-аудита; unit-тест границ и E2E согласованности). +- [x] Дополнить визуальный язык авторскими рыболовными SVG-иконками и шкалой активности в форме поплавка; анимации учитывают `prefers-reduced-motion`. - [ ] Сократить мобильный путь до результатов и улучшить фильтры (пакет B UI/UX-аудита). - [ ] Упростить форму улова, добавить помощь форматов и сохранение значений при ошибке (пакет C UI/UX-аудита). - [ ] Заменить slug-фильтры и улучшить мобильное представление рекордов/detail (пакет D UI/UX-аудита). diff --git a/docs/UI_UX_AUDIT.md b/docs/UI_UX_AUDIT.md index ba56da0..60a996d 100644 --- a/docs/UI_UX_AUDIT.md +++ b/docs/UI_UX_AUDIT.md @@ -34,6 +34,13 @@ ## План работ +### Визуальный язык — выполнено + +- [x] Добавлен единый набор тонких рыболовных SVG-иконок без внешней библиотеки. +- [x] Unicode-символы навигации, метрик, координат, времени и приманки заменены тематическими пиктограммами. +- [x] Кольцевая диаграмма лидера заменена авторской шкалой-поплавком: высота поплавка и воды связана с индексом активности. +- [x] Добавлена деликатная рябь и покачивание с учётом `prefers-reduced-motion`. + ### Пакет A — достоверность интерфейса - [x] Вынести шкалу активности и пользовательские формулировки в общий helper.