fix(admin): keep the filter query intact in console pager and export links #49

Merged
developer merged 1 commits from feature/admin-pager-filter-encoding into development 2026-06-12 09:41:37 +00:00
Owner

Симптом

В админ-консоли пагинация и экспорт теряли активный фильтр. Пример: на /_gm/users?kind=robots ссылка «next» вела на /_gm/users?kind%3drobots&page=2 вместо …?kind=robots&page=2.

Корень

FilterQuery — это уже закодированная строка запроса (url.Values.Encode), но подставлялась после ? в href. Там html/template считает её одним значением query-параметра и повторно процентно-кодирует структурные =/&: kind=robotskind%3drobots. На messages хуже — мультифильтр (game=…&user=…) схлопывался в один мусорный параметр; ломалась и ссылка экспорта CSV.

Фикс

FilterQuery типизирован как template.URL — уже экранированный фрагмент отдаётся как есть (атрибутный && корректен, браузер декодирует обратно). Безопасно: вывод url.Values.Encode строго процентно-кодирован. games/complaints используют status={{.Status}} — одиночное значение в правильном контексте — и не затронуты.

Тесты

Новый TestPagerLinksPreserveFilterQuery (users + messages, включая CSV-ссылку). gofmt -l/go build/go vet чисто; пакеты adminconsole и server зелёные.

## Симптом В админ-консоли пагинация и экспорт теряли активный фильтр. Пример: на `/_gm/users?kind=robots` ссылка «next» вела на `/_gm/users?kind%3drobots&page=2` вместо `…?kind=robots&page=2`. ## Корень `FilterQuery` — это уже закодированная строка запроса (`url.Values.Encode`), но подставлялась **после `?`** в `href`. Там `html/template` считает её одним значением query-параметра и повторно процентно-кодирует структурные `=`/`&`: `kind=robots` → `kind%3drobots`. На `messages` хуже — мультифильтр (`game=…&user=…`) схлопывался в один мусорный параметр; ломалась и ссылка экспорта CSV. ## Фикс `FilterQuery` типизирован как `template.URL` — уже экранированный фрагмент отдаётся как есть (атрибутный `&` → `&` корректен, браузер декодирует обратно). Безопасно: вывод `url.Values.Encode` строго процентно-кодирован. `games`/`complaints` используют `status={{.Status}}` — одиночное значение в правильном контексте — и не затронуты. ## Тесты Новый `TestPagerLinksPreserveFilterQuery` (users + messages, включая CSV-ссылку). `gofmt -l`/`go build`/`go vet` чисто; пакеты `adminconsole` и `server` зелёные.
developer added 1 commit 2026-06-12 09:38:59 +00:00
fix(admin): keep the filter query intact in console pager and export links
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Successful in 8s
CI / integration (pull_request) Successful in 14s
CI / ui (pull_request) Has been skipped
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m6s
eeb078d528
The paginated users and messages lists interpolate the pre-encoded filter
query (url.Values.Encode) after the "?" in their pager and CSV-export links.
There html/template treats it as a single query value and percent-encodes the
structural "=" and "&" again, so "kind=robots" rendered as "kind%3drobots" and
the multi-pair message filter collapsed -- every page step dropped the active
filter.

Type FilterQuery as template.URL so the already-escaped fragment is emitted
verbatim (the attribute-level "&" -> "&" stays correct, the browser decodes
it back). It is safe because url.Values.Encode output is strictly
percent-encoded. games/complaints use status={{.Status}} -- a single value in
proper query-value context -- and were never affected.
owner approved these changes 2026-06-12 09:41:20 +00:00
developer merged commit f67a357e62 into development 2026-06-12 09:41:37 +00:00
developer deleted branch feature/admin-pager-filter-encoding 2026-06-12 09:41:37 +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#49