feat(board): drag-and-drop cards between columns to change state#230
Conversation
The Board/Kanban layout was static — cards were plain links with no way to
move a work item between columns. This adds native HTML5 drag-and-drop:
- IssueLayoutBoard: cards are draggable; columns are drop targets with a
highlight while a droppable card hovers. On drop the board resolves the
column to a concrete state — for per-state columns that's the column's state,
for group-by-state-group columns it's a state in the card's own project that
belongs to that group (default first) — and calls onCardMove. Dropping on the
card's current column or the synthetic "No state" bucket is a no-op.
- IssueLayoutTypes: new optional onCardMove(issueId, targetStateId); when
omitted the board stays non-draggable (other layouts unaffected).
- IssueListPage (project board) and WorkspaceViewsPage (workspace kanban) wire
onCardMove to issueService.update({ state_id }) with an optimistic update and
revert/refetch on failure.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughIssue cards can now be dragged between board columns. The board resolves drop targets, highlights active columns, and the list and workspace views persist state changes with ordered per-card updates. ChangesBoard drag-and-drop flow
Sequence Diagram(s)sequenceDiagram
participant User
participant IssueLayoutBoard
participant IssueListPage as IssueListPage.handleCardMove
participant WorkspaceViewsPage as WorkspaceViewsPage.handleCardMove
participant issueService as issueService.update
User->>IssueLayoutBoard: drop issue card
IssueLayoutBoard->>IssueListPage: onCardMove(issueId, targetStateId)
IssueLayoutBoard->>WorkspaceViewsPage: onCardMove(issueId, targetStateId)
IssueListPage->>issueService: update state_id
WorkspaceViewsPage->>issueService: update state_id
alt update succeeds
issueService-->>IssueListPage: success
issueService-->>WorkspaceViewsPage: success
else update fails
issueService-->>IssueListPage: error
issueService-->>WorkspaceViewsPage: error
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/web/src/pages/IssueListPage.tsx`:
- Around line 590-600: Serialize issue state updates in handleCardMove so only
the latest drag for a given issue can win. Add the same per-issue
ordering/staleness guard pattern used by handleReorder around
issueService.update(...) and refetchIssues(), keyed by issueId, so older
in-flight PATCH responses or rollback refetches cannot overwrite a newer
optimistic state_id change.
In `@apps/web/src/pages/WorkspaceViewsPage.tsx`:
- Around line 506-521: The rollback in handleCardMove is unconditional and can
overwrite a newer successful move when an older issueService.update request
fails later. Add a per-issue request token/version inside WorkspaceViewsPage so
each move records the latest attempt for that issue, and in the catch handler
only restore prevStateId if the failing request is still the newest one. Use
handleCardMove, setIssues, and issueService.update to locate the logic and guard
the rollback against stale responses.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 0802854a-400b-499f-ac98-ab7f65d35b6e
📒 Files selected for processing (4)
apps/web/src/components/work-item/layouts/IssueLayoutBoard.tsxapps/web/src/components/work-item/layouts/IssueLayoutTypes.tsapps/web/src/pages/IssueListPage.tsxapps/web/src/pages/WorkspaceViewsPage.tsx
Addresses CodeRabbit review: dragging the same card repeatedly fired independent issueService.update calls, so a late older PATCH (or its rollback/refetch) could clobber a newer move. Both board consumers now chain PATCHes per issue id (commit in order) and tag each move with a sequence token, so only the latest move's failure rolls back (WorkspaceViewsPage) or refetches (IssueListPage). Mirrors the existing handleReorder serialization. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/web/src/pages/IssueListPage.tsx`:
- Around line 614-616: The rollback refetch in handleCardMove can apply stale
data after a route change because refetchIssues() still uses the current page
state. Capture the initiating workspaceSlug/projectId in handleCardMove and pass
them into the fallback refetch path, and in refetchIssues or the setIssues
caller discard results when they no longer match the active route. Use the
existing cardMoveSeq.current check plus the route identifiers to ensure only the
move from the current workspace/project can update issues.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 6a3301aa-789c-44c0-a8a8-977356954037
📒 Files selected for processing (2)
apps/web/src/pages/IssueListPage.tsxapps/web/src/pages/WorkspaceViewsPage.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/src/pages/WorkspaceViewsPage.tsx
Addresses CodeRabbit: the board move's rollback refetch could replace a different project's list if the user navigated away before the failed PATCH settled. Track the latest route key and only refetch when still on the project that initiated the move. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
What
Closes #175. The Board/Kanban layout was static — cards were plain links with no way to move a work item between columns. This adds native HTML5 drag-and-drop: dragging a card to another column changes its state.
How
IssueLayoutBoard— cards aredraggable; columns are drop targets that highlight while a droppable card hovers over them. On drop the board resolves the column to a concrete state:Dropping on the card's current column, or on the synthetic "No state" bucket, is a no-op.
IssueLayoutTypes— new optionalonCardMove(issueId, targetStateId). When omitted the board stays non-draggable, so other layouts and read-only contexts are unaffected.IssueListPage(project board) andWorkspaceViewsPage(workspace kanban) wireonCardMovetoissueService.update({ state_id })with an optimistic update and revert/refetch on failure.No backend changes — this reuses the existing issue-update endpoint.
Testing
tsc -b+ ESLint clean.AI assistance
Produced with the help of Claude Code (Claude Opus 4.8); AI-assisted commits carry a
Co-Authored-Bytrailer.Summary by CodeRabbit