5.4.3 `componentsService` — admin CRUD + default-assignee-at-create (first-alphabetical) + move-or-remove delete + per-issue assignment, revision diffs
Estimate: 30m · Depends on: 5.4.1
The taxonomy half. lib/services/componentsService.ts + typed errors + DTOs + HTTP-only routes.
Admin CRUD (project-admin-gated, the 6.4 two-tier check): create/rename (case-insensitively unique per project), edit description, set/clear defaultAssigneeId (must be a member who can view the project — the assignableMembersService scoping; SetNull covers departure). Delete = the verified move-or-remove flow: the API takes { moveToComponentId?: string } — with a target, reassignItems moves every join row (skipping duplicates where the issue already carries the target); without, joins are removed; either way issues are untouched and the response reports the affected count. The whole flow is ONE transaction (the Restrict FK backstops a missed path).
Per-issue assignment — setComponents(workItemId, componentIds[], ctx) / add/remove, edit-gated, validating same-project components; revision diffs { components: { added/removed: [name] } }. Components ride getIssueDetail (bounded, the 5.3.3 slot pattern). listComponents(projectId) for the admin page + pickers (name order, with per-component item counts for the admin list + delete dialog).
Default-assignee-at-create (the verified Jira rule): in workItemsService.createWorkItem, when the input carries component ids and NO assignee, resolve the first-alphabetical component (by nameLower) having a non-null defaultAssigneeId and assign it — inside the existing create transaction, recorded in the create revision. No assignee mutation on later component changes (create-time only — the mirror rule).
Acceptance criteria
- CRUD + uniqueness + default-assignee validation + the move-or-remove delete (both branches, counts reported, duplicate-join skip) round-trip, all project-admin-gated; per-issue set/add/remove edit-gated with same-project validation; viewer 403 / cross-workspace 404.
- A create with components + no assignee lands on the first-alphabetical default (verified with two defaulted components); a create WITH an assignee never overrides; later component edits never touch assignee.
- Revision diffs written for assignment changes;
getIssueDetailcarries components bounded; one method = one tx; routes HTTP-only; coverage ≥90%.
Context refs
- 5.4.1 repos (incl.
reassignItems); the verified component rules in the Story 5.4 description workItemsService.createWorkItem(the create-tx to extend) +assignableMembersService(6.4 scoping)- The 6.4 admin-gate pattern (
settings/project/members/page.tsx) - The links revision-diff precedent