Просмотр исходного кода

feat(docs): enhance admin user management with sorting and password reset features

Code_Uwe 1 месяц назад
Родитель
Сommit
135e7955bc
3 измененных файлов с 217 добавлено и 38 удалено
  1. 77 4
      Docs/api.md
  2. 31 4
      Docs/frontend-api-usage.md
  3. 109 30
      Docs/frontend-ui.md

+ 77 - 4
Docs/api.md

@@ -497,9 +497,9 @@ Filter rule:
 
 ---
 
-### 4.13 `GET /api/admin/users` (RHL-012)
+### 4.13 `GET /api/admin/users` (RHL-012 + RHL-043)
 
-List user accounts (cursor-based pagination).
+List user accounts (cursor-based pagination with optional sort modes).
 
 **Authentication**: required.
 
@@ -511,6 +511,10 @@ List user accounts (cursor-based pagination).
 - `role` — one of `branch | admin | superadmin | dev`
 - `branchId` — `NLxx` (filters branch users)
 - `limit` — page size (default `50`, min `1`, max `200`)
+- `sort` — one of:
+  - `default` (existing baseline order)
+  - `role_rights` (`superadmin > dev > admin > branch`, deterministic tie-breakers)
+  - `branch_asc` (`NL` numeric ascending, users without NL at the end)
 - `cursor` — opaque cursor from the previous response
 
 **Response 200**
@@ -537,6 +541,10 @@ Notes:
 
 - `nextCursor` is `null` when there are no more results.
 - All returned user objects are safe (no password hash/token fields).
+- Invalid `sort` returns `400 VALIDATION_INVALID_FIELD` with `details.field = "sort"` and allowed values.
+- Cursor safety by sort mode:
+  - cursors are tied to the active sort mode,
+  - reusing a cursor with another sort mode returns `400 VALIDATION_INVALID_FIELD` (`details.field = "cursor"`).
 
 ---
 
@@ -651,7 +659,56 @@ Not found:
 
 ---
 
-### 4.16 `DELETE /api/admin/users/:userId` (RHL-012)
+### 4.16 `POST /api/admin/users/:userId` (RHL-043)
+
+Reset a user password and return a temporary plaintext password.
+
+**Authentication**: required.
+
+**Authorization**: user management capability required (`superadmin` / `dev`).
+
+**Route params**
+
+- `userId` — MongoDB ObjectId
+
+Behavior:
+
+- Generates a new temporary password server-side.
+- Stores only the bcrypt hash in the database.
+- Forces password rotation (`mustChangePassword = true`).
+- Clears legacy reset-token fields (`passwordResetToken`, `passwordResetExpiresAt`).
+
+Safety rules:
+
+- Resetting the currently authenticated user via this endpoint is forbidden:
+  - `400 VALIDATION_INVALID_FIELD`
+  - `details.reason = "SELF_PASSWORD_RESET_FORBIDDEN"`
+
+**Response 200**
+
+```json
+{
+	"ok": true,
+	"user": {
+		"id": "...",
+		"username": "...",
+		"email": "...",
+		"role": "branch",
+		"branchId": "NL02",
+		"mustChangePassword": true
+	},
+	"temporaryPassword": "TempPass123!"
+}
+```
+
+Notes:
+
+- `temporaryPassword` is returned only in this response and should be handled as sensitive data.
+- Not found returns `404 USER_NOT_FOUND`.
+
+---
+
+### 4.17 `DELETE /api/admin/users/:userId` (RHL-012)
 
 Delete a user.
 
@@ -666,13 +723,29 @@ Delete a user.
 **Response 200**
 
 ```json
-{ "ok": true }
+{
+	"ok": true,
+	"user": {
+		"id": "...",
+		"username": "...",
+		"email": "...",
+		"role": "branch",
+		"branchId": "NL01",
+		"mustChangePassword": true
+	}
+}
 ```
 
 Not found:
 
 - `404 USER_NOT_FOUND`
 
+Safety rule:
+
+- Deleting the currently authenticated user via this endpoint is forbidden:
+  - `400 VALIDATION_INVALID_FIELD`
+  - `details.reason = "SELF_DELETE_FORBIDDEN"`
+
 ---
 
 ## 5. API v1 freeze (RHL-008)

+ 31 - 4
Docs/frontend-api-usage.md

@@ -199,12 +199,13 @@ Search:
 
 - `search({ q, branch, scope, branches, from, to, limit, cursor })`
 
-Admin user management (RHL-012):
+Admin user management (RHL-012 + RHL-043):
 
-- `adminListUsers({ q, role, branchId, limit, cursor })`
+- `adminListUsers({ q, role, branchId, sort, limit, cursor })`
 - `adminCreateUser({ username, email, role, branchId, initialPassword })`
 - `adminUpdateUser(userId, patch)`
 - `adminDeleteUser(userId)`
+- `adminResetUserPassword(userId)`
 
 Low-level:
 
@@ -683,7 +684,7 @@ RBAC note:
 - Branch users will only get results for their own branch.
 - Admin/superadmin/dev users can use `scope=all` or `scope=multi` for cross-branch search.
 
-### 4.5 Admin: User management (RHL-012)
+### 4.5 Admin: User management (RHL-012 + RHL-043)
 
 User management endpoints are served under:
 
@@ -702,7 +703,13 @@ A forbidden call returns:
 
 Use:
 
-- `adminListUsers({ q, role, branchId, limit, cursor })`
+- `adminListUsers({ q, role, branchId, sort, limit, cursor })`
+
+Sort modes:
+
+- `default`
+- `role_rights`
+- `branch_asc`
 
 Example:
 
@@ -714,6 +721,7 @@ export async function loadFirstPage() {
 		q: "max",
 		role: "branch",
 		branchId: "NL01",
+		sort: "role_rights",
 		limit: 50,
 	});
 }
@@ -728,6 +736,7 @@ Response shape:
 Notes:
 
 - Pagination is cursor-based. Keep `nextCursor` in client state.
+- Cursors are mode-aware. Do not reuse a cursor across different `sort` modes.
 
 #### 4.5.2 Create user
 
@@ -786,6 +795,24 @@ Notes:
 
 - Not found returns `404 USER_NOT_FOUND`.
 
+#### 4.5.5 Reset temporary password
+
+Use:
+
+- `adminResetUserPassword(userId)`
+
+Behavior:
+
+- Resets the target user password on the server.
+- Returns a temporary plaintext password in the response payload.
+- Sets `mustChangePassword=true` for the target user.
+
+Safety rules:
+
+- Resetting your own currently authenticated user via this endpoint is blocked:
+  - `400 VALIDATION_INVALID_FIELD`
+  - `details.reason = "SELF_PASSWORD_RESET_FORBIDDEN"`
+
 ---
 
 ## 5. Error handling

+ 109 - 30
Docs/frontend-ui.md

@@ -1,4 +1,4 @@
-# Frontend UI: App Shell, Routing, Auth/RBAC, Explorer, Search, Date Range Filter, Navigation Polish, Profile Password Change, User Management, Role Refinement, and Must-Change-Password Gate (RHL-019 / RHL-020 / RHL-021 / RHL-022 / RHL-023 / RHL-024 / RHL-025 / RHL-037 / RHL-032 / RHL-009 / RHL-012 / RHL-041 / RHL-044)
+# Frontend UI: App Shell, Routing, Auth/RBAC, Explorer, Search, Date Range Filter, Navigation Polish, Profile Password Change, User Management, Role Refinement, Must-Change-Password Gate, and User Management UX Hardening (RHL-019 / RHL-020 / RHL-021 / RHL-022 / RHL-023 / RHL-024 / RHL-025 / RHL-037 / RHL-032 / RHL-009 / RHL-012 / RHL-041 / RHL-044 / RHL-043)
 
 This document describes the **frontend routing scaffold**, the **application shell layout**, and the core UI modules for the RHL Lieferscheine app.
 
@@ -17,6 +17,13 @@ Timeline:
 - **RHL-041**: Role refinement (`superadmin`) + capability separation (branch access vs user management).
 - **RHL-012**: User management UI (list/create/update/delete users) + guarded admin endpoints.
 - **RHL-044**: Must-change-password enforcement (central protected-route gate, `/profile` redirect, and post-change resume flow).
+- **RHL-043**: User management UX hardening + layout cleanup:
+  - safer delete flow (typed username confirmation),
+  - branch input hardening (`NL` + numeric draft) + branch existence validation with fail-open note,
+  - loaded-count + users sorting toolbar,
+  - temporary-password controls (reset/show/copy + tooltips) in table and edit dialog,
+  - sticky icon-only action column polish,
+  - AppShell cleanup (remove sidebar placeholder, desktop width to `75%`).
 
 > **Language policy**
 >
@@ -28,22 +35,27 @@ Timeline:
 
 ## 1. Scope
 
-### 1.1 Implemented (as of RHL-012 + RHL-041 + RHL-037 + RHL-025 + RHL-032 + RHL-009 + RHL-044)
+### 1.1 Implemented (as of RHL-012 + RHL-041 + RHL-037 + RHL-025 + RHL-032 + RHL-009 + RHL-044 + RHL-043)
 
 - **Public** `/login` route with a functional login form (shadcn/ui primitives).
 
 - **Protected** application shell for all authenticated routes:
   - Top navigation (branding, quick navigation, actions)
-  - Sidebar placeholder area
+  - Centered main content container
   - Main content area
 
+  Width policy:
+
+  - mobile/tablet: full width
+  - desktop (`lg+`): centered `w-3/4` content for a wider but bounded workspace
+
 - **Session guard** for protected routes:
   - Session identity is checked via `GET /api/auth/me`.
   - When unauthenticated: redirect to `/login?reason=expired&next=<original-url>`.
 
 - **In-shell auth gating (UX improvement)**:
   - Auth loading/error/redirect states render **inside** the AppShell main content.
-  - AppShell (TopNav + sidebar) remains stable; no “blank spinner screens”.
+  - AppShell (TopNav + main content frame) remains stable; no “blank spinner screens”.
 
 - **Logout**:
   - Logout action calls `GET /api/auth/logout` and redirects to `/login?reason=logged-out`.
@@ -84,18 +96,24 @@ Timeline:
       - `isAdminLike(role)` → `admin | superadmin | dev`
       - `canManageUsers(role)` → `superadmin | dev`
 
-- **User management UI (RHL-012)**:
+- **User management UI (RHL-012 + RHL-043 hardening)**:
   - Route: `/admin/users` (protected)
   - Permission gating:
     - allowed: `superadmin`, `dev`
     - forbidden: `admin`, `branch`
 
-  - Features (v1):
+  - Features:
     - list users with filters (username/email search, role, branchId)
+    - loaded-count indicator (`X Benutzer geladen`) in the users toolbar
+    - sort toolbar (`Standard`, `Rolle (Rechte)`, `Niederlassung (NL)`)
     - create user dialog (initial password + must-change-password flag on create)
+    - create/edit branch input hardening (`NL` prefix + numeric input draft)
+    - create/edit branch existence checks against `/api/branches` with fail-open note
     - update user (role/branchId consistency enforced)
-    - delete user
-    - cursor-based pagination (“Mehr laden”)
+    - delete user with typed-username confirmation gate
+    - temporary password controls (reset, reveal/hide, copy) in table + edit dialog
+    - sticky right action column with icon-only actions (edit/delete)
+    - cursor-based pagination (“Mehr laden”), consistent with selected sort mode
 
 - **UI RBAC (branch-level)**:
   - `BranchGuard` prevents branch users from accessing other branches’ URLs.
@@ -174,10 +192,9 @@ Timeline:
   - add a dedicated “Herunterladen” UI action (download variant)
   - optional in-app PDF viewer experience (instead of a new tab)
 
-- User management UX hardening (separate follow-up ticket):
-  - delete with explicit username confirmation
-  - branch user creation UX (fixed `NL` prefix + numeric-only input)
-  - optional branch existence check / warning when creating branch users
+- Optional user management enhancements (not implemented in RHL-043):
+  - additional branch list filtering in branch pickers (create/edit)
+  - one-click “copy username” helper
 
 ---
 
@@ -270,23 +287,18 @@ File: `components/app-shell/AppShell.jsx`
 AppShell is the stable frame for all protected pages:
 
 - TopNav is always visible.
-- The sidebar is currently a placeholder (reserved for future navigation/filter UI).
-- Route pages render inside the main content area.
+- Route pages render inside the main content area (no sidebar rail).
 
-#### 4.1.1 Width policy and sidebar docking (2xl+)
-
-On very wide screens the UI should remain readable.
+#### 4.1.1 Width policy
 
 Current strategy (implemented in `AppShell.jsx` + `TopNav.jsx`):
 
-- Use a centered content column (~45% of the viewport at `2xl+`).
-- Keep the Explorer/Search content inside that centered column.
-- Render the sidebar in the left gutter and align it to the right edge so it “docks” to the centered content **without consuming the centered width**.
-
-Below `2xl`:
+- keep content full-width on smaller screens,
+- switch to a centered `lg:w-3/4` container on desktop for both:
+  - TopNav inner row,
+  - protected page content area.
 
-- Use full width for usability.
-- Hide the sidebar placeholder to avoid shrinking the main content area.
+This keeps header and content visually aligned and avoids horizontal “jump” between navigation and page body.
 
 ### 4.2 TopNav structure (RHL-032)
 
@@ -667,7 +679,7 @@ The project uses Sonner (shadcn/ui integration) for toast notifications.
 
 ---
 
-## 11. User management UI (RHL-012)
+## 11. User management UI (RHL-012 + RHL-043)
 
 ### 11.1 Route
 
@@ -685,8 +697,13 @@ Files:
 
 - `components/admin/users/AdminUsersPage.jsx` (gating)
 - `components/admin/users/AdminUsersClient.jsx` (list + filters + actions)
+- `components/admin/users/AdminUsersTableToolbar.jsx` (loaded count + sort dropdown)
 - `components/admin/users/UsersTable.jsx` (table)
+- `components/admin/users/UserTemporaryPasswordField.jsx` (temporary password controls)
+- `components/admin/users/DeleteUserDialog.jsx` (delete safety dialog)
+- `components/admin/users/BranchNumberInput.jsx` (fixed `NL` prefix + numeric input)
 - `components/admin/users/CreateUserDialog.jsx` + `components/admin/users/create-user/*` (create flow)
+- `components/admin/users/EditUserDialog.jsx` + `components/admin/users/edit-user/*` (edit flow)
 
 The UI uses the API client wrappers from `lib/frontend/apiClient.js`:
 
@@ -694,6 +711,63 @@ The UI uses the API client wrappers from `lib/frontend/apiClient.js`:
 - `adminCreateUser`
 - `adminUpdateUser`
 - `adminDeleteUser`
+- `adminResetUserPassword`
+
+### 11.4 UX hardening rules (RHL-043)
+
+- Delete flow requires explicit typed confirmation:
+  - dialog shows the target user details,
+  - user must type the username,
+  - delete button stays disabled until the normalized value matches.
+
+- Branch-role input hardening (`role=branch`) in create and edit:
+  - fixed prefix `NL`,
+  - numeric input draft for the branch number,
+  - normalization output examples:
+    - `1` -> `NL01`
+    - `32` -> `NL32`
+    - `200` -> `NL200`
+  - typing stability: drafts like `01` remain visible while typing.
+
+- Branch existence validation:
+  - if `/api/branches` is available and selected branch does not exist:
+    - inline error is shown,
+    - submit is blocked.
+  - if `/api/branches` cannot be loaded:
+    - submit is not hard-blocked (fail-open),
+    - a small informational note is shown.
+
+### 11.5 Table UX details
+
+- Users toolbar (above table):
+  - loaded count badge (`X Benutzer geladen`)
+  - sorting dropdown:
+    - `Standard`
+    - `Rolle (Rechte)`
+    - `Niederlassung (NL)`
+
+- Table layout:
+  - fixed/sticky right `Aktion` column for edit/delete actions,
+  - icon-only edit/delete actions to reduce horizontal space.
+
+- Temporary password column:
+  - masked display by default (`••••••`),
+  - controls:
+    - reset temporary password,
+    - reveal/hide,
+    - copy to clipboard,
+  - copy feedback:
+    - success checkmark state after copy,
+    - fallback copy path for browsers/environments where `navigator.clipboard` is not usable.
+
+- Tooltips:
+  - temporary-password control buttons use tooltips for discoverability and consistent icon-only UX.
+
+### 11.6 Sorting and pagination behavior
+
+- Sorting is applied server-side (`/api/admin/users?sort=...`) and remains stable across `Mehr laden`.
+- Cursor payload carries sort context to prevent cross-mode cursor reuse.
+- Frontend query state includes `sort` so changing sort resets the list cleanly.
 
 ---
 
@@ -767,7 +841,13 @@ npm run build
 - Explorer drill-down.
 - Search scopes (admin-like) + Search restrictions (branch).
 - Open PDFs.
-- User management UI (superadmin/dev): list/create/update/delete.
+- User management UI (superadmin/dev):
+  - list/create/update/delete
+  - delete typed confirmation
+  - branch input (`NL` + number) and branch existence warning/blocking
+  - temporary password reset/reveal/copy controls
+  - sorting (`Standard`, `Rolle (Rechte)`, `Niederlassung (NL)`)
+  - sticky action column behavior on horizontal overflow
 
 ### 16.2 Server
 
@@ -785,7 +865,6 @@ npm run build
 
 ## 17. Planned follow-ups
 
-- User management UX hardening (separate ticket):
-  - delete with explicit username confirmation
-  - branch user creation UX (fixed `NL` prefix + numeric-only input)
-  - optional branch existence check / warning when creating branch users
+- Optional user-management polish:
+  - additional branch-list filtering in the create/edit branch picker UX
+  - optional one-click username copy helper