# 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` 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.