Skip to content

feat(089): admin users view#2

Merged
hendrikebbers merged 9 commits intomainfrom
feat/089-admin-users-view
Apr 26, 2026
Merged

feat(089): admin users view#2
hendrikebbers merged 9 commits intomainfrom
feat/089-admin-users-view

Conversation

@herbie-bot
Copy link
Copy Markdown

Summary

Adds a read-only Users page to the admin area that lists every registered CRM user in a paginated table (avatar, name, email). Restricted to the IT-ADMIN role at both the backend endpoint and the server-rendered Next.js page; the sidebar item is hidden for users without the role.

Spec

  • Folder: specs/089-admin-users-view/
  • Design: specs/089-admin-users-view/design.md
  • Behaviors: specs/089-admin-users-view/behaviors.md
  • Steps: specs/089-admin-users-view/steps.md

Changes

Backend

  • UserController.listUsers(Pageable) returns Page<UserDto> from UserService.findAll, defaulting to size 20.
  • Method-level @RequiresItAdmin; getMe stays open to any authenticated user.
  • Tests: UserControllerTest (pagination contract: default size, explicit size, response shape) and 6 new SecurityRoleIntegrationTest cases (401/403/200 across role combinations + /api/users/me regression). PreAuthorizeAnnotationTest pins @RequiresItAdmin on the new method.

Frontend

  • New /admin/users server component gated by ROLE_IT_ADMIN, falling back to <ForbiddenPage />.
  • New UsersClient paginated table built on @open-elements/ui Table* primitives — avatar (32×32 circular <img> with <User> icon fallback for null avatarUrl), page-size selector (10/20/50/100/200, persisted under pageSize.users, default 20), prev/next pagination with hidden/disabled states, total-count display (singular/plural), skeleton loading, dedicated empty and error states.
  • getUsers(params) API helper following the existing getContacts/getApiKeys pattern.
  • Sidebar Users nav item under the existing admin CollapsibleGroup (visibility inherited from canSeeAdmin).
  • en/de translations for nav.users and a new users namespace.

Test coverage

  • 23 behavioral scenarios in behaviors.md mapped to tests in steps.md (Behavior Coverage table).
  • Backend: 40 tests pass (12 annotation, 25 role-integration, 3 controller).
  • Frontend: 18 new tests for the users page; full suite shows 0 new failures vs main (44 pre-existing failures unchanged).
  • Spec-review: every design element covered.
  • Quality-review: 3 Improvement findings addressed in commit 95dfe69 (bare <table>Table* primitives, silent fetch error → catch + error UI, import order in PreAuthorizeAnnotationTest).

Notes for the reviewer

  • Two scenarios depend on Radix Select interactions that are unreliable in jsdom (UI selection of a new page size). They are covered indirectly through (a) the localStorage-hydration test and (b) explicit assertions on the exported PAGE_SIZE_OPTIONS / DEFAULT_PAGE_SIZE / PAGE_SIZE_STORAGE_KEY constants. The behavior is implemented in the onValueChange handler.
  • Manual smoke test in a browser was not run by the agent — please verify the page loads and the IT-ADMIN gate behaves as expected before merging.

Closes #1

🤖 Generated with Claude Code

hendrikebbers and others added 9 commits April 26, 2026 15:58
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes spec 089 step 1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- new server page /admin/users gated by IT-ADMIN role
- client component with avatar/name/email columns, fallback icon
  for null avatarUrl, page-size selector (10/20/50/100/200) persisted
  in localStorage, prev/next pagination, total count display, skeleton
  loading and empty state
- new getUsers API function and Users sidebar nav item
- en/de translations for the new namespace and nav entry

Closes spec 089 steps 2-5.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- annotation-level check that listUsers carries @RequiresItAdmin
- 401/403/200 role coverage in SecurityRoleIntegrationTest
- pagination contract tests (default size 20, explicit size, page shape)
- /api/users/me regression check that the existing endpoint stays open
  to authenticated users

Closes spec 089 step 6.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
14 tests covering loading skeleton, empty state, avatar rendering with
and without avatarUrl, default page size 20, localStorage hydration,
total count (singular/plural), pagination button visibility, prev/next
disabled boundaries, and prev/next navigation requests.

Closes spec 089 step 8.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- replace bare <table> with shadcn Table primitives in users-client
  per the typescript convention (no raw HTML elements where a UI lib
  component exists)
- handle getUsers rejection: log to console, surface a translated
  loadError message via a dedicated error UI; previously a network
  failure was silently rendered as the empty state, which is misleading
- add users.loadError translation keys (de + en) and a test that
  verifies the error branch renders without falling through to empty
- drop unused SessionProvider wrapper from the test
- fix import order in PreAuthorizeAnnotationTest

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hendrikebbers hendrikebbers merged commit cfe52dc into main Apr 26, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Admin users view: paginated read-only user list

2 participants