feat: user-activity admin table with filters + pagination (#1021 fatia 3) - #1024
Merged
Conversation
…a 3) Final slice of #1021 — the UI. A new admin-only page "Atividades dos usuários" (`/admin/activity`) lists the user_activity rows captured in fatia 2, alongside the administrative audit log. - Page: columns When · User · Event · App · IP; anonymous access shows as "Anonymous"; hashed-colour avatars like the Users/Audit pages. - Sub-nav: two pills (user activity ⇄ administrative audit) on both pages, reusing the existing .admin-nav-link.is-active style (no new CSS); both keep nav_section="audit" so the top nav stays highlighted. The top-nav link is unchanged (the new view is one click away). - Filters the store already supports exactly (no LIKE machinery): event select, user + app dropdowns of distinct values (new distinct_usernames / distinct_specs), and a period select (24h/7d/30d -> since). - Server-side pagination (50/page, #999 pattern) with filters preserved on the pager links (percent-encoded). - i18n across pt/en/es/fr incl. a Fluent plural in the pager status. - Declares urlencoding = "2" directly (already transitive; 2.1.3 in the lock) for the pager query-string encoding. Tests: UI integration (admin-only gate, rendered rows, event/user filters, 60-row pagination counted by the per-row data-avatar marker) and the store distinct helpers (SQLite + postgres-it). Swapped ti-login-2 (outside the shipped Tabler subset) for ti-login to keep the icon_subset gate green. Closes #1021 (External-click capture noted as a follow-up). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
Author
Review (self, adversarial pass) — MERGE-OK
Store Closes #1021 (External-click capture is a noted follow-up). No blockers. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
O quê
Fatia 3 (final) de #1021 — a UI: a tabela "Atividades dos usuários" na aba Atividades, com filtros e paginação server-side. Fecha o épico (fundação #1022 + captura #1023 já mergeadas).
O que entra
/admin/activity(admin-only,RequireAdmin) — "Atividades dos usuários". Colunas Quando · Usuário · Evento · Aplicação · IP; acesso anônimo aparece como "Anônimo"; avatar colorido por hash igual às páginas Users/Audit./admin/activitye/admin/audit), reusando o estilo.admin-nav-link.is-active(sem CSS novo). Ambas mantêmnav_section="audit", então o item "Atividades" do topo fica destacado nas duas. O link do topo continua apontando p//admin/audit(zero mudança de comportamento existente) — a nova view fica a um clique.distinct_usernames/distinct_specs, no padrão do dropdown de autores da auditoria), e período (24h/7d/30d →since).list_page/count_filtered), com os filtros preservados nos links do pager (query-string percent-encoded).urlencoding = "2"declarada direto (já estava na árvore transitivamente; verificada 2.1.3 no lock) p/ encodar os valores de filtro no query-string do pager.Testes
activity_ui.rs): gate admin-only (sem sessão ≠ 200); renderiza as linhas; filtro por evento estreita (login vs acessos); filtro por usuário estreita; paginação (60 registros → 50 na pág.1, 10 na pág.2), contando linhas reais da tabela pelo marcadordata-avatar="(não conta os dropdowns nem o script).distinct_usernames/distinct_specs(exclui NULL, ordenado) em SQLite + assert no teste postgres-it real.cargo test(default, incl. oload_default_succeedsdo Fluent), clippy limpo,i18n-check, postgres-it contra Postgres 16 real, e o gateicon_subset(troqueiti-login-2, fora do subset, porti-login, já presente).Épico #1021 — completo com esta fatia
Follow-up conhecido (fora do épico, anotado na #1021): captura de clique em app External (o bounce precede o guard de acesso — precisa de reorder do handler).
🤖 Generated with Claude Code