Adds user management
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
# Unit 5 – Code Generation Summary
|
||||
|
||||
## What Was Built
|
||||
|
||||
Unit 5 implements the **User Management** feature: listing users, inviting new users, and changing roles.
|
||||
|
||||
---
|
||||
|
||||
## Backend Changes
|
||||
|
||||
### Bug Fixes
|
||||
- **UsersController**: Fixed invite link URL from `/setup/complete?token=…` to `/invite/complete?token=…`
|
||||
|
||||
### New Interface Method
|
||||
- `IInvitationService.GetPendingInvitationByEmailAsync(string email)` — returns `(bool IsPending, string? Token)`
|
||||
|
||||
### New Service Implementation
|
||||
- `InvitationService.GetPendingInvitationByEmailAsync` — queries `Invitations` for a non-accepted, non-expired record by email
|
||||
|
||||
### New Models (`IdentityRequests.cs`)
|
||||
- `UserDto` — maps a user to the API response including `InvitationPending` and `InviteLink`
|
||||
- `ChangeRoleRequest` — carries `NewRole`
|
||||
|
||||
### Updated Controller (`UsersController`)
|
||||
- Now injects `UserManager<ApplicationUser>` alongside `IInvitationService`
|
||||
- `GET /api/v1/Users` — returns all users with role, status, and pending invite info
|
||||
- `PUT /api/v1/Users/{userId}/role` — enforces RBAC rules (Owner constraint, Admin can only promote User→Admin)
|
||||
|
||||
### Database Migration
|
||||
- **No migration needed** — all tables (`Users`, `Roles`, `UserRoles`, `Invitations`) already exist
|
||||
|
||||
---
|
||||
|
||||
## Frontend Changes
|
||||
|
||||
### New UI Components (`frontend/src/components/ui/`)
|
||||
| File | Component |
|
||||
|------|-----------|
|
||||
| `dialog.tsx` | Dialog, DialogContent, DialogHeader, DialogTitle, etc. |
|
||||
| `select.tsx` | Select, SelectTrigger, SelectContent, SelectItem, etc. |
|
||||
| `table.tsx` | Table, TableHeader, TableBody, TableRow, TableHead, TableCell |
|
||||
| `badge.tsx` | Badge with `default / secondary / destructive / outline` variants |
|
||||
|
||||
### New API Hook (`useUsers.ts`)
|
||||
- `useUsers()` — `GET /api/v1/Users`, staleTime 30s, queryKey `['users']`
|
||||
- `useInviteUser()` — `POST /api/v1/Users/invite`, invalidates `['users']`
|
||||
- `useChangeRole()` — `PUT /api/v1/Users/{userId}/role`, invalidates `['users']`
|
||||
|
||||
### Migrated Hook (`useInvitation.ts`)
|
||||
- Converted from `useState/useEffect` to TanStack Query
|
||||
- Uses `InvitationValidation` from `types.ts` (field `isValid`, not the old local `valid`)
|
||||
|
||||
### Updated Files
|
||||
| File | Change |
|
||||
|------|--------|
|
||||
| `types.ts` | Added `UserListItem`, `InviteUserPayload`, `InviteUserResponse`, `ChangeRolePayload` |
|
||||
| `InviteCompletePage.tsx` | `validationQuery.data?.valid` → `validationQuery.data?.isValid` (2 places) |
|
||||
| `mocks/invitation/handlers.ts` | Response shape updated to `{ isValid, email, name, errorCode }` |
|
||||
| `mocks/users/handlers.ts` | Full user list + invite + change-role mock handlers |
|
||||
|
||||
### New Page & Component
|
||||
- `UsersPage.tsx` — table with role/status badges, per-row dropdown (copy link, change role)
|
||||
- `components/users/InviteUserDialog.tsx` — 2-step dialog: email+role form → invite link display
|
||||
|
||||
### i18n
|
||||
- `en/translation.json` and `nl/translation.json` — `users.*` key section added (title, table columns, status labels, role labels, invite dialog, action labels)
|
||||
|
||||
---
|
||||
|
||||
## Tests
|
||||
|
||||
| File | Coverage |
|
||||
|------|---------|
|
||||
| `useUsers.test.ts` | `useUsers`, `useInviteUser`, `useChangeRole` — happy path + error |
|
||||
| `UsersPage.test.tsx` | Render, table rows, pending badge, copy link action, API error, dialog open |
|
||||
| `InviteUserDialog.test.tsx` | Form validation, successful flow (step 2), API error, dialog reset |
|
||||
| `InviteCompletePage.test.tsx` | No changes needed — mock now returns correct `isValid` shape |
|
||||
|
||||
---
|
||||
|
||||
## Key Design Decisions
|
||||
|
||||
- **`isValid` vs `valid`**: The existing `InvitationValidation` type in `types.ts` already uses `isValid`. The old `useInvitation.ts` had a local interface with `valid`. Migration aligns the hook to the canonical type.
|
||||
- **No server-side invite email**: Invite flow is link-only (per functional design). The link is shown in the dialog step 2 and copied to clipboard.
|
||||
- **Role RBAC in frontend**: `availableRolesFor()` in `UsersPage` restricts the dropdown options; backend enforces the same rules server-side.
|
||||
- **Pending invite for accepted users**: `GetPendingInvitationByEmailAsync` only returns a token if `!IsAccepted && !IsExpired` — accepted users correctly show no pending invite.
|
||||
Reference in New Issue
Block a user