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-107

2.4.9 Link management UI — add / remove relationship links

Done
Description

Estimate: 16m · Depends on: 2.4.5, 2.4.8

Make the 2.4.5 relationships panel INTERACTIVE — wire the shipped linkWorkItems / unlinkWorkItems (1.4.4) into it per the 2.4.8 design. Thin Server Actions (createLinkAction / removeLinkAction) over the gated service methods (no raw Prisma — 4-layer rule); a client add-link control opened from the panel header (kind selector + issue-search Combobox); an inline remove on each row; revalidatePath on the detail route so the groups AND the readiness banner update (adding a non-terminal blocker flips Ready→Blocked; removing it flips back). The typed link errors map to inline messages (no crash). The 2.4.5 "Manage in Epic 5" read-only note is REPLACED by the real control.

Candidate read. The issue-search Combobox needs an in-workspace candidate lookup (by identifier / title, excluding self + already-linked). Add a lightweight workItemsService.listLinkCandidates (workspace-scoped, explicit workspaceId filter — finding #26; cross-project allowed, matching the link model's same-workspace rule); full cross-cutting search is Epic 6, so this is a simple prefix/contains read, not the search engine. Scope: management lives on the DETAIL panel; the edit page's related-issues block stays READ-ONLY (an editor keeps context there — 2.4.5).

Acceptance criteria

  • From the relationships panel, add a link of each of the five kinds to another in-workspace issue via the Combobox search; the new link appears in its group on revalidation, and the readiness banner re-judges (a new non-terminal is_blocked_by target flips Ready→Blocked).
  • Remove a link inline (with confirm) → it disappears from its group + the banner re-judges; relates_to removes both reciprocal rows (service already does this).
  • Self-link / duplicate / cycle / cross-workspace are rejected with an INLINE message (the typed errors → 422/409), nothing persisted, no crash; the add control is permission-gated (workspace member; the gated service + the trigger backstop).
  • Server Actions call ONE gated service method each (no raw Prisma); listLinkCandidates carries an explicit workspaceId filter.
  • Vitest (real Postgres) for the actions + listLinkCandidates (incl. excludes-self / excludes-already-linked / cross-workspace empty); a component test for the add control (kind + combobox + submit + error surfacing); the detail + edit routes stay green on the shell-a11y sweep (the add dialog/combobox is accessible — reuse the 2.3.4 Combobox semantics).

Context refs

  • design/work-items/links.mock.html + design-notes.md (2.4.8 — REQUIRED, match its layout/primitives)
  • lib/services/workItemsService.tslinkWorkItems / unlinkWorkItems + listCandidateParents (the candidate-read pattern to mirror for listLinkCandidates)
  • lib/workItems/linkErrors.ts + lib/dto/workItemLinks.ts (kinds + LinkWorkItemsInput)
  • app/(authed)/issues/[key]/_components/RelationshipsPanel.tsx (2.4.5 — make it interactive) + components/ui/Combobox (2.3.4)
  • 2.3.3 / 2.3.6 Server-Action + revalidatePath conventions
Status
Done
Type
Sub-task