Project

General

Profile

Actions

Feature #1526

closed
RA

Feature #1510: Implement internal user roles with ticket progression and assignment controls

Admin dashboard: set internal role and a validated redmine_user_id

Feature #1526: Admin dashboard: set internal role and a validated redmine_user_id

Added by Redmine Admin 5 days ago. Updated 5 days ago.

Status:
Shipped
Priority:
Normal
Assignee:
-
Start date:
07/31/2026
Due date:
% Done:

0%

Estimated time:
spec_ref:

docs/superpowers/specs/2026-07-31-internal-roles-design.md

build_status:
build_number:
ci_run_url:
scan_status:
scan_report_url:
deploy_status:
preprod_url:
deployed_at:
branch:
pr_url:
security_key:
severity:
paused:

Description

**Surface:** cross-surface (backend + frontend)

Let an admin configure a client as `developer` / `cto` / ordinary and set their `redmine_user_id`, from the existing admin client surface. `PATCH /admin/clients/{client_id}` under the existing `require_admin` guard; `AdminClientOut` grows both fields.

The Redmine id is hand-typed, so it is **validated against the Redmine port on write**: a bad id is refused at configuration time rather than exploding later at assign time.

**Project membership is NOT built here** — it already exists end to end (`POST`/`DELETE /admin/clients/{client_id}/projects` over `AssignProjectToClient`/`UnassignProjectFromClient`, surfaced by `ClientProjectsDialog.tsx` from `ClientTable.tsx:340`, with `useAssignProject`/`useUnassignProject`). This subtask only proves role + existing membership is enough.

## Acceptance criteria
- [ ] `PATCH /admin/clients/{client_id}` accepts `{internal_role, redmine_user_id}` (each optional, each nullable); 403 non-admin, 401 unauthenticated, 404 unknown client, 422 for a role outside {cto, developer, null}.
- [ ] The endpoint **never reads or writes `is_admin`** — asserted explicitly; the two axes stay orthogonal.
- [ ] **`redmine_user_id` is validated against the Redmine port on write**: an id that is not a real Redmine user is refused with 422 and an actionable message, and nothing is persisted. Covered with the port stubbed to reject.
- [ ] A developer saved with a null `redmine_user_id` succeeds (the mapping can come later) but is **visibly flagged** in the UI as not yet assignable.
- [ ] Clearing the id and/or the role reverts the user to ordinary-client behaviour on the very next request — a test asserts transition attempts then fail with 403.
- [ ] Setting a role does not grant project access by itself: a CTO without membership still gets the usual 403 on that project.
- [ ] `AdminClientOut` (hence `GET /admin/overview`) returns both fields.
- [ ] The admin overview lets the admin change both inline, surfaces success and failure, and invalidates the overview query so the table reflects the new state without a reload.
- [ ] **END-TO-END:** a freshly created internal user given `internal_role=cto` **plus** project membership, entirely through the existing admin dashboard, can then open that project's ticket panel and move a ticket forward. No new membership surface is added anywhere in this epic.
- [ ] New i18n keys in **both** `src/i18n/messages/en.ts` and `fr.ts` (real French, identical ICU placeholders); `npx tsc --noEmit` proves the mirror.
- [ ] Vitest covers initial render, successful save, failed save (422 inline), and the unmapped-developer hint.
- [ ] Backend gates green (`python -m pytest -q` serial, `ruff check .`, `mypy src`) and all four frontend gates green.

Actions

Also available in: PDF Atom