Как и в списке партий группы: карточка целиком ведёт в партию, ссылки
на профили внутри мешали выбору. Элемент возвращён к <button>.
Плюс правка подписи кнопки создания группы: «+ Создать группу» ->
«Создать группу».
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- рейтинг в блоках «Новички» (топ игроков и общая статистика) выводится
серебряным (--silver, класс .lb-score.provisional), а не золотым;
- MIN_GAMES 3 -> 5 (scoring.py) — в ранжированный топ попадают с 5 игр;
подпись «Новички (меньше N игр)» берёт порог из API, справка обновлена;
- pytest: 57 passed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Новый хук useRenameGroup (PATCH /api/groups/{id} — эндпойнт уже был на
бэкенде) с инвалидацией всех мест, где видно имя группы (шапка группы,
переключатель групп, главная). В настройках — карточка «Название» с
полем и кнопкой «Сохранить» (по образцу смены ника в аккаунте).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Карточка «Статистика игроков» на странице группы переименована в
«Список игроков», под списком — поле приглашения по нику (с проверкой
максимума участников и отправкой по Enter). В настройках группы остался
только состав с удалением участников.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Карточка партии целиком ведёт на страницу партии — вложенные ссылки на
профили мешали выбору партии (легко промахнуться в профиль). Ссылки на
профили остаются на странице партии, в топах и списке участников.
Карточка возвращена к семантичному <button>.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Единое правило: название страницы показывает верхняя панель (или активная
вкладка админки), тело страницы его не повторяет. Убраны дубли на общей
статистике, в справке и в админ-разделах (аккаунты, фракции, журнал);
пояснительные абзацы сохранены. Заголовки-сущности (имя группы, дата
партии) остаются h2 в теле.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- .btn-block в global.css заменяет 9 повторов style={{width:100%}};
- .badge-live — единый бейдж «идёт» (3 копии inline-стиля);
- незавершённые партии на главной используют стандартный .card,
как список партий (была единственная карточка на самодельных стилях);
- в списке партий добавлен бейдж 🎲 случайной фракции — как на странице
партии (раньше в списке он терялся);
- переключатель групп: кнопка «+» подписана «+ Создать группу»
(по образцу «+ Добавить игрока»), убран пустой span.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- «Winrate» -> «Винрейт» в карточке статистики; в справке добавлена
расшифровка «Винрейт (WR)» — WR остаётся в компактных таблицах;
- кнопки сохранения: «ОК» -> «Сохранить» (аккаунт, фракции, аккаунты
в админке); отмена в модалках/формах — везде «Отмена», не «Назад»;
- пустые состояния приведены к шаблону «X пока нет.»;
- роли участников группы переведены: owner/member -> владелец/участник,
бейдж admin -> «админ»; «Имя обновлено» -> «Ник обновлён»;
- «N игр.» в значении числа игроков -> «N игроков» со склонением
(новый хелпер plural в domain/format.ts);
- «минимум 2 участника» -> «минимум 2 игрока» (в партии — игроки,
в группе — участники).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Новый formatDate в domain/format.ts для date-only строк (played_at,
last_match_at). Применён: список партий, заголовок страницы партии
(раньше там было «2026-07-13»), записи в админке, «последняя партия»
на странице группы.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- админка, записи: удаление партии раньше срабатывало сразу по клику на
иконку и показывало «Удалено» даже при ошибке — теперь модалка
подтверждения и честная обработка ошибок;
- ачивки и dev-удаление аккаунта: window.confirm заменён на ConfirmDialog
(в приложении принят именно он);
- настройки группы: удаление участника — с подтверждением.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Новый компонент Switch (разметка .switch из global.css). Применён:
- создание группы: дополнения теперь свитчи, как в настройках группы;
- настройки группы: базовые дополнения показываются, как при создании
(включены, заблокированы, подпись «всегда включено»), а не скрываются;
- админка: «Группировать по группам» и флаг «выбыл» в правке партии.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Новый компонент PlayerLink (ссылка на /u/:id, кликабельно всё поле строки,
stopPropagation для вложенности в кликабельные карточки). Применён:
- Leaderboard и общая статистика (рефакторинг ручных Link);
- страница партии: результаты и состав;
- настройки группы: список участников;
- список партий и незавершённые на главной (карточки button -> div,
т.к. ссылки внутри button — невалидная разметка).
«пригласил: X» в приглашениях остаётся текстом — в API нет id пригласившего.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- deploy/pi/README: пошагово от чистого Pi (Docker → SSH-ключ туннеля на самом Pi → 2 файла → up),
с объяснением назначения ключа и кодирования в base64 (TUNNEL_KEY_B64).
- .env.example/README: PowerShell-команда base64 через Resolve-Path (абсолютный путь — иначе
[IO.File]::ReadAllBytes искал относительно домашнего каталога .NET и падал).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Теперь на Pi нужны только docker-compose.yml и .env — без репозитория, сборки и файла ключа.
- docker-compose.yml: pull_policy: always (образы тянутся из реестра, сборка не запускается);
ключ туннеля больше не монтируется томом — берётся из .env (TUNNEL_KEY_B64, base64);
у VPS_TUNNEL_HOST убран :? (ломал бы buildx bake на ПК, где переменная не задана) — проверка
обязательности перенесена в tunnel.sh (рантайм).
- deploy/tunnel/tunnel.sh: приватный ключ из TUNNEL_KEY_B64 (base64) с фолбэком на смонтированный
/key/id_tunnel (для dev/test, где репозиторий на хосте).
- .env.example: TUNNEL_KEY_B64 + как сгенерировать base64 (Git Bash / PowerShell).
- deploy/pi/README: полный поток «голый Pi → 2 файла»: Docker, curl compose+.env из Gitea, up;
обновление, автозапуск, бэкап как опция.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Заглушка вместо Pi, пока он не готов: поднимает forbiddenstars.ru с ПК через слот VPS 9000,
APP_ENV=production, локальная x86-сборка (без реестра), отдельный проект forbidden-stars-temp
со своими постоянными томами под прод-данные. Позже данные мигрируют на Pi (backup → restore).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- scripts/build-push.ps1: arm64 buildx bake + push, аналог build-push.sh для Windows-дева (ASCII).
- deploy/pi/README: указать .ps1 для Windows и почему `bash ...` в PowerShell не работает.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- docker-compose.yml: image у app и tunnel → путь реестра ${IMAGE_REGISTRY}/...:${IMAGE_TAG}
(build: оставлен фолбэком для сборки на Pi). Тома/настройки прежние.
- scripts/build-push.sh: buildx bake под linux/arm64 + push обоих образов в реестр (запуск на ПК).
- .env.example: блок ОБРАЗЫ ПРОДА (IMAGE_REGISTRY=gitea.arseniev.info/notbigghost, IMAGE_TAG=latest).
- deploy/pi/README: запуск/обновление через docker compose pull; альтернатива — сборка на Pi;
заметка про HTTPS-реестр / insecure-registries.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- docker-compose.yml: слот туннеля параметризован (${VPS_TUNNEL_PORT:-9000}), VPS_TUNNEL_HOST
стал обязательным (${...:?}), лимиты ресурсов из .env (APP_MEM_LIMIT/APP_CPUS),
stop_grace_period 30s (чек-пойнт WAL SQLite), security_opt no-new-privileges на обоих сервисах;
healthcheck/логирование/тома сохранены (имена томов не менялись — данные не осиротеют).
- .env.example: VPS_TUNNEL_PORT по умолчанию НЕ задаётся (каждый контур берёт свой слот:
dev→9001, prod→9000, test→9001); добавлены ADMIN_NICKNAME, DEV/PROD_ACHIEVEMENTS_DIR,
APP_MEM_LIMIT/APP_CPUS.
- deploy/pi/README: чек-лист .env для Pi; запуск одним `docker compose up -d --build`.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Справка: новая страница /help с описанием подсчёта очков игроков (формула League
Points, ничьи, выбывшие = последнее место, порог 3 партии для топа); кнопка
«Справка» добавлена в боковое меню. Задел под будущие правила и подсказки.
- index.html больше не кэшируется (no-store на Caddy и в FastAPI): при недоступности
хоста надёжно показывается страница-заглушка вместо зависшей «Загрузки».
Новая страница /help: формула League Points, нормировка по числу игроков, ничьи,
выбывшие = последнее место, порог 3 партии для топа, прочие показатели. Текст
соответствует services/scoring.py. Задел под будущие правила и подсказки.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Открытие бокового меню переведено на нативный onClick: на тач-устройствах
pointerup часто не приходит из-за pointercancel, из-за чего меню не открывалось.
Долгое удержание (10с → админка) оставлено на таймере и подавляет этот клик.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Выбор места при завершении партии получает опцию «✕ выбыл»; выбывшему
авто-проставляется общее последнее место (ничья последнего ранга), плюс флаг
eliminated для отрисовки красным крестиком. В результатах и списке партий
выбывшие показываются крестиком и идут в конец. Подсчёт очков не меняется
(используются обычные числовые места).
Бэкенд: модель MatchParticipant.eliminated + миграция 0009 (идемпотентная);
схемы (place стал необязательным, +eliminated); match_service (хелпер
_resolve_finish_places, finish/update); проброс eliminated в роутерах и stats.
Фронт: выбор места с крестиком в MatchDetailPage и AdminMatchEdit, отрисовка в
результатах и MatchList, стиль .place-badge.eliminated; регенерация schema.d.ts.
Тесты: завершение с выбывшим (последнее место, засчитан), все-выбывшие → 422.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Новая система уведомлений поверх готовой SSE-шины: события пишутся в БД,
живут 72 часа и чистятся (при чтении списка + фоновой задачей), всплывают
сверху экрана в момент прихода, доступны через колокольчик в правом верхнем
углу (бейдж непрочитанных + панель).
Типы: приглашение в группу (→ /group), старт/финиш партии участникам кроме
инициатора (→ /match/{id}). Титулы — готовый хелпер-задел (не подключён, т.к.
выдача титулов игрокам ещё не реализована).
Бэкенд: модель Notification + миграция 0008 (идемпотентная), notification_service,
notify.notifications_changed, роутер /api/notifications (GET + /read), триггеры
в groups/matches, фоновая чистка в lifespan, защита hub.publish от закрытого loop.
Фронт: useNotifications/useMarkNotificationsRead, NotificationBell/Panel/Toaster,
перекомпоновка top-bar, стили; useServerEvents знает тип notifications.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>