MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-102

2.4.4 Inline controls: workflow-aware status + assignee

Done
Description

Estimate: 14m · Depends on: 2.4.1, 2.3.6

The two write affordances the scope names — and the ONLY ones on the detail page (everything else is the Edit form). Both REUSE 2.3.6's components + gated actions; this Subtask wires them inline, it does not build new mutation primitives.

Status control (workflow-aware). The header status pill becomes a StatusPicker (2.3.6) that offers only the LEGAL next statuses for the current one — the project's transitions in restricted mode, any status in open mode (2.2's canTransition / the workflow context already in the getIssueDetail bundle). Selecting one calls 2.2.4's gated updateStatus via a detail-page Server Action (changeStatusAction — reuse 2.3.6's if its signature fits, else a thin sibling); an illegal/stale transition surfaces the typed error inline; success toasts + revalidatePaths the detail route so the pill + the activity trail reconcile.

Assignee control. The sidebar assignee field becomes an AssigneePicker (2.3.6, a workspace-member combobox) that reassigns via the now-status-free updateWorkItem (2.3.6's finding-#46 cleanup) through an updateAssigneeAction — assignee is a non-status patch field, so it rides the ordinary patch path (which still writes a revision). "Unassign" is supported (null assignee). Optimistic + toast + revalidate. Permission: any project member can change status/assignee in v1 (no per-field RBAC until Epic 6) — but the Server Actions still carry the explicit workspaceId/membership gate (finding #26).

Acceptance criteria

  • Status control offers exactly the legal next statuses (restricted: transition rows; open: all); choosing one persists via 2.2.4's updateStatus; an illegal/stale move surfaces the typed error inline and does not change the pill.
  • Assignee control reassigns (and unassigns) via the status-free updateWorkItem; a revision row is written for the change.
  • Both controls are Server-Action-gated (workspace + membership); a forged cross-workspace target 404s; no client-trusted ids.
  • Both reuse 2.3.6's StatusPicker/AssigneePicker — no parallel components; the detail page and edit form share them.
  • Vitest (real Postgres) over the two actions: legal status change, illegal rejection, reassign + unassign, cross-workspace 404. Component test: the status control lists only legal targets. shell-a11y stays green with the controls rendered.

Context refs

  • 2.3.6's StatusPicker / AssigneePicker + changeStatusAction / the status-free updateWorkItem (finding #46) — reuse, don't rebuild
  • workItemsService.updateStatus (2.2.4) + workflowsService.canTransition (2.2.3) — the gated paths
  • 2.4.1's IssueDetailDto workflow context (statuses + legal transitions) — the picker's option source
  • motir-core/CLAUDE.md — Server Action / 4-layer / workspaceId-gate rules
Status
Done
Type
Sub-task