feat(feedback): обратная связь от пользователей с разбором в админке и ролями #64

Merged
developer merged 7 commits from feature/feedback into development 2026-06-15 11:30:09 +00:00
Owner

Сквозная фича обратной связи: экран Settings → Info → «Обратная связь» (сообщение ≤1024 символов + один опциональный аттач), разбор в server-rendered админке (/_gm/feedback), ответ оператора с доставкой в приложение и бейджем, и первая ролевая модель (account_roles; feedback_banned запрещает только отправку фидбека).

Слои

  • БД: миграция 00004_feedback (feedback_messages + account_roles) + go-jet.
  • Backend: домен internal/feedback, роли internal/account/roles.go, REST-эндпоинты, раздел админки + панель ролей в /users, push-под-вид admin_reply.
  • Провод: FlatBuffers feedback.submit/get/unread; гейт гостя на gateway (Op.NonGuest + is_guest через резолв) → guest_forbidden до backend.
  • UI: маршрут /feedback + экран, пикер файла с гейтом по расширению, бейдж (общий ⚙️ + «1» на Инфо), детект канала, i18n en/ru.

Проверки локально (всё зелёное): Go build/vet/gofmt, все unit-тесты backend/gateway/pkg, полный интеграционный набор (-tags=integration), гейт гостя в connectsrv; UI pnpm check/unit (217)/build/e2e (132, Chromium+WebKit).

Каветы

  • Аттач ≤ 1 000 000 Б (≈1 МБ), чтобы влезть под лимит края 1 MiB без его ослабления.
  • Канал ios/android — feature-detection window.Capacitor; на реальной native-сборке проверить вживую (фоллбэк web).
  • Убрана колонка attachment_type (тип из расширения) — залогировано в PLAN.md (Stage 19).

Документация обновлена в этом же PR: PLAN.md (Stage 19 + трекер + refinements), ARCHITECTURE.md (§15 + §12), FUNCTIONAL.md(+_ru), TESTING.md, UI_DESIGN.md, README.

Сквозная фича обратной связи: экран **Settings → Info → «Обратная связь»** (сообщение ≤1024 символов + один опциональный аттач), разбор в server-rendered админке (`/_gm/feedback`), ответ оператора с доставкой в приложение и бейджем, и первая **ролевая модель** (`account_roles`; `feedback_banned` запрещает только отправку фидбека). **Слои** - БД: миграция `00004_feedback` (`feedback_messages` + `account_roles`) + go-jet. - Backend: домен `internal/feedback`, роли `internal/account/roles.go`, REST-эндпоинты, раздел админки + панель ролей в `/users`, push-под-вид `admin_reply`. - Провод: FlatBuffers `feedback.submit/get/unread`; **гейт гостя на gateway** (`Op.NonGuest` + `is_guest` через резолв) → `guest_forbidden` до backend. - UI: маршрут `/feedback` + экран, пикер файла с гейтом по расширению, бейдж (общий ⚙️ + «1» на Инфо), детект канала, i18n en/ru. **Проверки локально (всё зелёное):** Go build/vet/gofmt, все unit-тесты backend/gateway/pkg, полный интеграционный набор (`-tags=integration`), гейт гостя в `connectsrv`; UI `pnpm check`/unit (217)/build/e2e (132, Chromium+WebKit). **Каветы** - Аттач ≤ **1 000 000 Б** (≈1 МБ), чтобы влезть под лимит края 1 MiB без его ослабления. - Канал `ios/android` — feature-detection `window.Capacitor`; на реальной native-сборке проверить вживую (фоллбэк `web`). - Убрана колонка `attachment_type` (тип из расширения) — залогировано в `PLAN.md` (Stage 19). Документация обновлена в этом же PR: `PLAN.md` (Stage 19 + трекер + refinements), `ARCHITECTURE.md` (§15 + §12), `FUNCTIONAL.md`(+`_ru`), `TESTING.md`, `UI_DESIGN.md`, README.
developer added 1 commit 2026-06-15 10:23:51 +00:00
feat(feedback): in-app user feedback with admin review and account roles
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 12s
CI / ui (pull_request) Successful in 47s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m12s
419ea11b14
User-facing Feedback screen (Settings -> Info, registered accounts only): a
message (<=1024 runes) plus one optional attachment, an anti-spam gate (one
unreviewed message at a time), and the operator's inline reply with a
Settings/Info badge. Server-rendered admin console section (/_gm/feedback):
unread/read/archived queue with per-user search, detail with read/reply/
archive/delete/delete-all, safe attachment serving (nosniff, images inline via
<img>, others download-only). Introduces account_roles, the first per-account
role table; feedback_banned blocks only feedback submission, granted/revoked
from /users and the delete-with-block action.

- migration 00004_feedback (feedback_messages + account_roles) + jetgen
- backend internal/feedback (store+service), internal/account/roles.go
- wire: FlatBuffers feedback.submit/get/unread; gateway guest gate (Op.NonGuest,
  is_guest via session resolve) -> guest_forbidden before any backend call
- reply push reuses NotificationEvent with a new admin_reply sub-kind
- UI: /feedback route + screen, attachment picker, badge, channel detection, i18n
- tests: feedback unit (Go+UI), gateway guest-gate, inttest lifecycle, e2e
- docs: PLAN stage 19, ARCHITECTURE s15, FUNCTIONAL(+ru), TESTING, READMEs
developer added 1 commit 2026-06-15 10:45:01 +00:00
fix(feedback): theme buttons, badge the Info button, simplify admin actions
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 12s
CI / ui (pull_request) Successful in 47s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m8s
5287794a72
- style the About feedback button and the form's Send/attach/remove buttons with
  the accent/border tokens used by the New Game CTA, so they follow light/dark
  theme (the previous .btn/.ghost classes were not defined globally); the attach
  button is a neutral 📎 icon button with an aria-label
- show a round badge on the About 'Feedback' button when a reply is waiting
- admin: one 'ban from feedback' checkbox shared by Delete and Delete-all (via
  button formaction); hide Mark read when already read and Archive when archived
- e2e: match the About button by substring (its name gains the badge)
developer added 1 commit 2026-06-15 10:59:19 +00:00
fix(feedback): show the operator reply only on the player's latest message
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 47s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m8s
1ae43080ec
A reply was bound to 'the latest message that has a reply', so after the player
read a reply and sent a new (unanswered) message, the old reply kept showing as
'Ответ на ваше последнее сообщение'. Bind it to the single most-recent message
instead: sending any new message immediately drops the previous reply (the new
message has no reply yet), well before the one-week window. Client clears the
reply optimistically on submit; the mock mirrors it; inttest covers the case.
developer added 1 commit 2026-06-15 11:07:48 +00:00
feat(feedback): show the sender's interface language and connector bot in admin
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 47s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m7s
2a4ce319d9
In the console feedback detail, show the sender's interface language (account
preferred_language) always, and — for a message that arrived through an external
connector (currently Telegram) — the bot they last used (en/ru, from the
account's service_language).
developer added 1 commit 2026-06-15 11:11:15 +00:00
docs(feedback): note interface language + connector bot in the admin detail
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 12s
CI / ui (pull_request) Successful in 47s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 2m6s
49b67a0354
developer added 2 commits 2026-06-15 11:25:37 +00:00
Store the sender's interface language (lang) and, for a message that arrived
through an external connector (Telegram), the bot language (channel_lang) on the
feedback row at submit time, so the operator console shows the state as it was
rather than the account's current settings (same snapshot discipline as a
suspension reason). Added additively in migration 00005. The console detail reads
these columns instead of loading the account live.
feat(feedback): render links in the operator reply (open in a new tab)
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 47s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m6s
277954c47f
Linkify the operator reply on the feedback screen: http/https/ftp/mailto/tel URLs
become anchors (target=_blank rel=noopener), everything else stays escaped text.
A small whitelisted regex (no dependency) — the reply is operator-authored, so
explicit schemes suffice; dangerous schemes (javascript:/data:) are never linked,
and \b avoids matching a scheme inside a word (hotel:, email:).
owner approved these changes 2026-06-15 11:29:14 +00:00
developer merged commit 711fe6e594 into development 2026-06-15 11:30:09 +00:00
developer deleted branch feature/feedback 2026-06-15 11:30:10 +00:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: developer/scrabble-game#64