Users & Access — CRUD Standard + Completion Checklist
Audience: backend + frontend engineers on the Kesles Dashboard.
Scope: the 8 sub-panels in the Users & Access dashboard menu (Users, Roles, Permissions, Role Matrix, Role Access Map, User Roles, Platform Access, Devices, Sessions, Audit Logs).
Status: living document — update after each PR.
This document has two roles:
- Completion checklist for the Users & Access menu so every sub-panel has full CRUD/action (not just "coming soon").
- Platform standard that is the MANDATORY reference for other Kesles dashboards being built (mobile-partner-dashboard, pos-admin, etc.). Every new panel that has master data must follow this pattern, no exceptions.
1. CRUD Pattern Standard (MANDATORY for every Kesles dashboard)
Every master-data panel in any dashboard MUST implement this contract before being released to production. There is no "Phase 2 later" for primary actions.
1.1 Action set — primary (mandatory)
| Action | HTTP | Endpoint pattern | UI entry-point | Confirmation | Audit |
|---|---|---|---|---|---|
| List | GET | /{domain}/{resource}?limit&offset&search&status | main panel page, paginated | – | – |
| Detail | GET | /{domain}/{resource}/{id} | row click (or view button) | – | log read if data is sensitive (e.g. KTP photo URL) |
| Add | POST | /{domain}/{resource} | "+ Add Data" button in the panel header | form dialog with backend-side validation | 1 audit row: create |
| Edit | PATCH | /{domain}/{resource}/{id} | edit icon on the row | form dialog prefilled + diff-aware save | 1 audit row: update with before/after diff |
| Delete | DELETE | /{domain}/{resource}/{id} | trash icon on the row | confirmation dialog "type the name to delete" if hard-delete | 1 audit row: delete |
1.2 Action set — secondary (conditional)
Depending on the domain, you may need:
- Toggle status (activate/deactivate) —
PATCH .../{id}with payload{is_enabled: bool}or{status: 'active'|'inactive'}. Not DELETE — delete = permanent removal, toggle = soft-disable. - Revoke / terminate (session, token, device) —
POST .../{id}/revoke. Different from delete because the row stays for the audit trail. - Approve / reject —
POST .../{id}/approve+/{id}/reject, with areasonfield. - Bulk action —
POST .../bulk-{action}with an array of ids, atomic (all or nothing). - Assign / revoke relationship —
POST .../{parent_id}/{child_resource}+ the sameDELETE. ExamplePOST /users/{id}/roles/{role_code}.
1.3 Standard dialog shell
Every Users & Access dialog — and every future Kesles dashboard — must use a consistent shell:
| Need | Shell | Location |
|---|---|---|
| CRUD form (Add/Edit) + destructive confirm | StandardDialog | dashboard_shell_shared_widgets.dart |
| Read-only detail (more content) | DashboardDialogShell | ditto |
| Long form (purchase order, vendor, etc.) | DashboardFormDialogShell | ditto |
Rules:
- Avoid raw Material
AlertDialog— visuals don't match (border radius, action alignment, icon-in-circle). StandardDialoghas icon-in-circle + close button top-right + a consistent action row.- Destructive (Delete/Revoke/Terminate):
actionColor: const Color(0xFFE45447)(Kesles red). - Primary (Save/Submit): default Kesles blue actionColor — no override needed.
- Info-only redirect dialog: both buttons
Close+Got itareNavigator.pop— no UX trap. actionBusy+actionEnabledare used for the loading state;actionBusy=truedisables both buttons + shows a spinner on the action button.
Reference examples already consistent (Ship-13 audit):
- Sessions Terminate confirm, Devices Revoke confirm, Platform Access delete/assign/edit, Role Matrix redirect — all use
StandardDialog.
1.4 Standard per-panel UI
Required layout:
┌─────────────────────────────────────────────────────────────────┐
│ <Title> [Refresh] [+ Add Data]│
│ <Subtitle> │
├─────────────────────────────────────────────────────────────────┤
│ [Filter chip 1] [Filter 2] … [Search box] [Dropdown ▾] │
├─────────────────────────────────────────────────────────────────┤
│ ☐ Col1 Col2 Col3 Status Action │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ row1 … [edit] [delete] [•••] │ │
│ │ row2 … [edit] [delete] [•••] │ │
│ └─────────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ Showing 1–10 of 83 · < 1 2 3 … > │
└─────────────────────────────────────────────────────────────────┘
- Add button on the right of the header; if the role has no write-permission → the button is hidden (NOT disabled with a tooltip).
- Row action icons: at most 3 visible (Edit / Delete / More); the rest go in the
Icons.more_vertoverflow menu. - Every destructive action (Delete, Revoke, Force-logout, Reset password) MUST go through an
AlertDialogconfirmation — must not execute directly from the row. - Loading state: spinner on the action button while in-flight; the row is dimmed to opacity 0.5 while being edited/deleted.
- Error: red SnackBar with the backend message (
exception.messageafterreplaceFirst('Exception: ', '')). Don't expose the stacktrace. - Success: green SnackBar + auto-reload of the list.
1.5 Backend contract
- Consistent response shape for every endpoint:
or single:{ "items": [...], "meta": {"limit": 10, "offset": 0, "total": 83, "can_write": true} }
{ "item": {...} }. meta.can_writeis driven by the caller's role/permission — the FE uses this to hide/show buttons (don't re-check role in the FE).- Consistent error shape:
{ "error": "validation_failed", "message": "swift_code must be 8 or 11 characters", "field": "swift_code" }
- HTTP status: 200 read, 201 create, 200 update/delete, 400 bad request (malformed JSON), 401 unauth, 403 forbidden (insufficient role), 404 not found, 409 conflict (duplicate, FK violation translated), 422 validation error, 500 server error.
- Audit write for every mutating endpoint — insert into
auth.auth_audit_logs(db_kesles_merchant_auth) withactor_user_id,resource_type,resource_id,action,before,after,request_id. - RBAC check in the handler uses the standard helper (
profile.hasAnyRole(...)orprofile.hasPermission(...)) — don't hardcode role codes per file.
1.6 Testing acceptance
Before a panel is considered done:
-
go testpassing for the store helper + handler -
flutter analyzeclean - Manual smoke: List → Add → Edit → Delete → verify the audit row appears
- RBAC probe: log in as 5 roles → verify Add/Edit/Delete buttons only appear per matrix
- 409 path: trigger a duplicate (e.g. assign a role that already exists) → UI shows a readable error
- Concurrent edit: 2 tabs editing the same row → the second save 409/stale-check (optimistic locking via
updated_at)
2. Current State — Users & Access (as of 2026-04-23, post Ship-13)
| Panel | List | Add | Edit | Delete/Revoke | Overall status |
|---|---|---|---|---|---|
| Users | ✅ | ✅ | ✅ | ✅ (status toggle) | Complete |
| Roles | ✅ | ✅ | ✅ |