Skip to content

feat(portal_users): bulk-delete customer portal users from the admin list - #196

Open
pat-lewczuk wants to merge 1 commit into
mainfrom
feat/portal-users-bulk-delete
Open

pat-lewczuk wants to merge 1 commit into
mainfrom
feat/portal-users-bulk-delete

Conversation

@pat-lewczuk

Copy link
Copy Markdown
Member

Problem

Backend → Portal klienta → Użytkownicy klientów could only delete one account at a time, through the row menu. After an event that means clicking through participants one by one.

What changed

A new app module portal_users injects a bulk action into the core table with UMES — no eject of customer_accounts, no patched node_modules.

  • Widget portal_users.injection.users-bulk-delete targets data-table:customer_accounts.admin.users:bulk-actions.
  • Injecting a bulk action is exactly what makes DataTable set enableRowSelection, so the row checkboxes and the Delete selected button appear together — no change needed in the core page.
  • Gated on the existing core feature customer_accounts.manage, the same one DELETE /api/customer_accounts/admin/users/<id> enforces, so there is no new ACL feature to declare, grant and sync.
  • Deletion goes through that core endpoint once per selected user, carrying the optimistic-lock header built from each row's updatedAt. That keeps everything the single-row path gives you: stale deletes refused, sessions revoked, customer_accounts.user.deleted emitted. Sequential on purpose — an admin list is small and each delete does real work server-side.
  • A destructive confirm dialog states how many accounts are about to go.
  • Partial failures are reported with the count that succeeded and the reason for each that did not.

One wrinkle worth reviewing

The core users page keeps its rows in client state (reloadToken) and passes no refreshButton, so an injected widget gets no refresh callback and router.refresh() would not refetch the list. The action therefore reloads through the URL and passes its message to FlashMessages via the ?flash=&type= query string — the same mechanism CrudForm's successRedirect uses. It returns { ok: false } with no message so the table does not also flash its own.

Testing

  • yarn generate registers the widget and the injection table (verified in .mercato/generated/injection-widgets.generated.ts and injection-tables.generated.ts).
  • yarn typecheck passes.
  • Local dev server smoke-checked after registering the module: /api/customer_accounts/admin/users → 401, /backend/customer_accounts/users → 307. Nothing 500s on boot.
  • Not exercised in a browser — the Chrome extension available here has no permission for localhost, so selecting rows and running the delete needs a manual pass: check the checkboxes appear, the confirm text counts correctly, the list reloads, and a partial failure reports sensibly.
  • yarn lint is broken in this repo independently of this change (next lint was removed in Next 15).

🤖 Generated with Claude Code

https://claude.ai/code/session_01VLqyzgTthFCZsqQWE9gh4a

…list

The Customer Users screen (backend -> Customer Portal -> Customer Users)
could only delete one account at a time through the row menu, which does
not scale when cleaning up participants after an event.

Add an app module that injects a bulk action into the core table via UMES
instead of ejecting customer_accounts. Injecting a bulk action is what
makes DataTable turn on row selection, so the checkboxes and the "Delete
selected" button appear together.

- portal_users.injection.users-bulk-delete targets the spot
  data-table:customer_accounts.admin.users:bulk-actions
- gated on the existing core feature customer_accounts.manage, the same
  one the DELETE endpoint enforces, so no new ACL feature to grant
- deletes through the core endpoint per user, carrying the optimistic-lock
  header built from each row's updatedAt, so stale deletes are still
  refused and session revocation plus the deleted event still run
- the core page keeps its rows in client state and exposes no refresh
  callback to widgets, so the action reloads through the URL and hands the
  result to FlashMessages via the query string
- partial failures report how many were deleted and why the rest were not

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLqyzgTthFCZsqQWE9gh4a
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.

1 participant