Skip to content

feat(layouts): inline property editing in list and spreadsheet#231

Merged
martian56 merged 3 commits into
mainfrom
feat/inline-edit
Jun 26, 2026
Merged

feat(layouts): inline property editing in list and spreadsheet#231
martian56 merged 3 commits into
mainfrom
feat/inline-edit

Conversation

@martian56

@martian56 martian56 commented Jun 26, 2026

Copy link
Copy Markdown
Member

What

Closes #176. Property cells were read-only links — every change meant opening the detail page. State, priority, assignees, labels, and start/due dates are now editable in place in the list rows and spreadsheet cells.

How

  • EditableCells.tsx (new) — EditableStateCell / EditablePriorityCell / EditableAssigneeCell / EditableLabelCell wrap the existing read-only display cells (StatePill, PriorityIcon, WorkItemAvatarGroup, LabelChips) in the shared Dropdown picker. Dates reuse DatePickerTrigger. A single openId per layout keeps only one picker open at a time.
  • IssueLayoutTypes — new optional onUpdateIssue(issueId, patch) + IssueInlinePatch. When omitted, cells render read-only (calendar/gantt/board unaffected).
  • IssueLayoutList — property pills moved out of the row's navigating <Link> into an interactive sibling region (whole-row hover preserved) so editing a pill no longer triggers navigation.
  • IssueLayoutSpreadsheet — each property column becomes an editable cell.
  • IssueListPage — a per-issue serialized persistIssueUpdate (PATCH chain + sequence token + route guard) backs both inline edits and the board drag-to-column, with optimistic update and refetch-on-failure.

Board cards already support changing state via drag-and-drop (#175); full board-card inline pickers are intentionally out of scope here to avoid conflicting with the card drag interaction.

Testing

  • tsc -b + ESLint clean.
  • Browser-verified in the project Work items list: changed a work item's state from a cell (No state → Done); the change persisted across a full reload (server round-trip), the picker opened/closed correctly, and clicking the title still navigates to the detail page. No console errors.

AI assistance

Produced with the help of Claude Code (Claude Opus 4.8); AI-assisted commits carry a Co-Authored-By trailer.

Summary by CodeRabbit

  • New Features
    • Added in-place editing for issue fields (state, priority, assignees, labels, and start/due dates) with dropdown pickers across list, board, and spreadsheet views.
    • Edits update immediately with optimistic saving.
  • Bug Fixes
    • Improved handling of rapid successive updates by queuing changes per issue and re-syncing only the affected item if an update fails.
    • Standardized overdue due-date detection and styling via shared logic.
  • Chores
    • Enhanced date picker trigger styling by supporting optional custom classes.

Property cells were read-only links — every change meant opening the detail
page. State, priority, assignees, labels, and start/due dates can now be edited
in place in the list rows and spreadsheet cells.

- EditableCells: state/priority/assignee/label cells that wrap the existing
  read-only display cells in the shared Dropdown picker; dates reuse
  DatePickerTrigger. A single openId per layout keeps one picker open at a time.
- IssueLayoutTypes: new optional onUpdateIssue(issueId, patch) + IssueInlinePatch.
  When omitted, cells stay read-only (calendar/gantt/board unaffected).
- IssueLayoutList: property pills moved out of the row's navigating <Link> into
  an interactive sibling region (whole-row hover preserved) so editing a pill no
  longer navigates.
- IssueLayoutSpreadsheet: each property column becomes an editable cell.
- IssueListPage: a per-issue serialized persistIssueUpdate (chain + sequence
  token + route guard) backs both inline edits and board drag-to-column, with
  optimistic update and refetch-on-failure.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9ed4eb82-ba2e-4472-acd6-e49e28f181b2

📥 Commits

Reviewing files that changed from the base of the PR and between 0662ba5 and abd6629.

📒 Files selected for processing (1)
  • apps/web/src/lib/issueRowHelpers.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/src/lib/issueRowHelpers.ts

📝 Walkthrough

Walkthrough

The PR adds inline editing for work-item state, priority, assignee, label, and date fields across list, board, and spreadsheet layouts. It also introduces shared patch types, editable picker cells, overdue-date helpers, and optimistic per-issue update persistence.

Changes

Inline work-item editing

Layer / File(s) Summary
Contracts and picker cells
apps/web/src/components/work-item/EditableCells.tsx, apps/web/src/components/work-item/layouts/IssueLayoutTypes.ts
Adds IssueInlinePatch, extends IssueLayoutProps with onUpdateIssue, and defines shared editable dropdown cells for state, priority, assignees, and labels.
Date trigger and overdue helper
apps/web/src/components/work-item/DatePickerTrigger.tsx, apps/web/src/lib/issueRowHelpers.ts, apps/web/src/components/work-item/IssueRowCells.tsx
Allows custom trigger classes, extracts overdue detection into isOverdue, and reuses that helper in due-date display logic.
Issue update persistence
apps/web/src/pages/IssueListPage.tsx
Serializes optimistic per-issue PATCH updates, reuses the same path for board moves and inline edits, and passes the update callback into the layouts.
Board inline editing
apps/web/src/components/work-item/layouts/IssueLayoutBoard.tsx
Switches board cards between read-only content and inline-edit controls for priority, state, assignee, label, and dates when updates are enabled.
List row inline editing
apps/web/src/components/work-item/layouts/IssueLayoutList.tsx
Switches list rows between read-only values and inline-edit controls for priority, state, assignee, labels, and dates when updates are enabled.
Spreadsheet inline editing
apps/web/src/components/work-item/layouts/IssueLayoutSpreadsheet.tsx
Renders editable spreadsheet cells for the same work-item fields with shared open-state handling, while keeping read-only fallbacks when updates are unavailable.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Devlaner/devlane#230: Both changes touch IssueListPage update chaining and the board move path that now shares the inline-edit persistence flow.

Suggested labels

enhancement, UI

Poem

A rabbit hopped from field to field,
and made each work item softly yield.
One click, one hop, the labels sing—
a tiny edit for every spring.
🐇

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the change, but it omits several required template sections such as Type of change, Surface, Why this approach, and Checklist. Add the missing template sections, especially Type of change, Surface, Why this approach, Database/migrations, Breaking changes, and Checklist.
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title is conventional, concise, and clearly describes the main UI change.
Linked Issues check ✅ Passed The PR implements inline editing for the scoped work-item layouts and fields, and persists changes through the issue-update flow required by #176.
Out of Scope Changes check ✅ Passed The added helper and DatePickerTrigger styling support the inline-edit feature and do not introduce unrelated behavior.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/inline-edit

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 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/components/work-item/layouts/IssueLayoutList.tsx`:
- Around line 252-263: The editable due-date branch in IssueLayoutList is
replacing DueDateCell and losing overdue/completed/cancelled status styling;
update the onUpdateIssue path so it still preserves the same due-date semantics
while remaining editable. Use the existing DueDateCell behavior as the reference
point, and make the DatePickerTrigger in IssueLayoutList derive and display the
same status cue from issue, issueState, and now instead of rendering a plain
picker-only cell.

In `@apps/web/src/components/work-item/layouts/IssueLayoutSpreadsheet.tsx`:
- Around line 145-156: The editable due date branch in IssueLayoutSpreadsheet
currently replaces DueDateCell with DatePickerTrigger, which removes overdue
highlighting and the completed/cancelled exception. Update the onUpdateIssue
path to preserve the same status cues by either reusing DueDateCell behavior
around DatePickerTrigger or passing the issue state/now-based styling into the
editable control, so spreadsheet mode keeps the overdue indicator when dates are
editable.

In `@apps/web/src/pages/IssueListPage.tsx`:
- Around line 760-762: The board layout still lacks inline edit wiring, so
IssueLayoutBoard remains read-only while the list and spreadsheet layouts
already support onUpdateIssue. Update the IssueListPage render path for
IssueLayoutBoard to pass the same inline update handler used by
handleInlineUpdate, and then thread that prop through IssueLayoutBoard so board
cards can edit issue properties in place alongside onCardMove.
- Around line 116-120: The per-issue PATCH sequencing in IssueListPage is
incorrectly suppressing older failures when a later update succeeds, which can
leave stale optimistic state in local UI. Update the issue update flow that uses
issueUpdateChains and issueUpdateSeq so each failed PATCH still triggers a
refetch or rollback for its own issue even if its seq is no longer the latest,
while keeping the existing per-issue serialization for in-order commits. Ensure
the logic around the update handler in IssueListPage does not gate failure
recovery solely on the current seq token.
- Around line 602-621: `persistIssueUpdate` is using `routeKeyRef.current` as a
navigation guard, but the ref can be stale until the passive effect runs,
allowing a failed `issueService.update` to refetch the previous project’s list
after a route change. Update `routeKeyRef.current` synchronously with the
current `workspaceSlug/projectId` route key, or move that ref sync into
`useLayoutEffect`, so the guard in `persistIssueUpdate` always compares against
the latest route before calling `refetchIssues()`.
🪄 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: 8b3252ab-3658-48e3-b1d9-3112e397e976

📥 Commits

Reviewing files that changed from the base of the PR and between 6ec8479 and c8e9b96.

📒 Files selected for processing (5)
  • apps/web/src/components/work-item/EditableCells.tsx
  • apps/web/src/components/work-item/layouts/IssueLayoutList.tsx
  • apps/web/src/components/work-item/layouts/IssueLayoutSpreadsheet.tsx
  • apps/web/src/components/work-item/layouts/IssueLayoutTypes.ts
  • apps/web/src/pages/IssueListPage.tsx

Comment thread apps/web/src/components/work-item/layouts/IssueLayoutList.tsx
Comment thread apps/web/src/pages/IssueListPage.tsx
Comment thread apps/web/src/pages/IssueListPage.tsx
Comment thread apps/web/src/pages/IssueListPage.tsx Outdated
- preserve the overdue cue on editable due-date cells (list, spreadsheet, board)
  via a shared isOverdue helper and a danger tone on the date trigger.
- reconcile after failures: a per-issue chain that fails now re-fetches just that
  issue once the chain drains, so an older rejected PATCH can't leave a stale
  optimistic value even when a newer update to another field succeeds.
- update the route-guard ref in a layout effect (pre-paint) instead of a passive
  effect, so a late failure can't reconcile against a stale route.
- add inline property editing to board cards too (priority, state, due, labels,
  assignees) via a CellGuard that stops the card's Link navigation and drag while
  a picker is used.
- move isOverdue to lib/issueRowHelpers (pure helper) to satisfy react-refresh.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@martian56

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/src/components/work-item/layouts/IssueLayoutBoard.tsx (1)

347-434: 🎯 Functional Correctness | 🟠 Major

Move editable controls out of the card <Link>

BoardCard still wraps the editable cells (EditablePriorityCell, EditableStateCell, EditableLabelCell, EditableAssigneeCell) and DatePickerTrigger inside an anchor. Those render <button> triggers and an <input type="date">, which is invalid interactive nesting and can break focus/keyboard behavior. CellGuard only stops propagation; it doesn’t fix the markup. Split navigation from editing, or make the card container non-anchor.

🤖 Prompt for 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.

In `@apps/web/src/components/work-item/layouts/IssueLayoutBoard.tsx` around lines
347 - 434, The editable controls in IssueLayoutBoard are still rendered inside
the BoardCard Link, which creates invalid interactive nesting for the editable
cells and DatePickerTrigger. Move the editing UI for EditablePriorityCell,
EditableStateCell, EditableLabelCell, EditableAssigneeCell, and
DatePickerTrigger out of the card anchor, or switch the card container to a
non-link wrapper and keep navigation separate. Use the
BoardCard/IssueLayoutBoard structure and the CellGuard-wrapped editable sections
to locate and refactor the layout.
🤖 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/lib/issueRowHelpers.ts`:
- Line 42: The isOverdue state check in issueRowHelpers currently only excludes
completed and cancelled, so issues grouped as canceled can still be marked
overdue. Update the isOverdue logic to treat both spellings of the canceled
state group the same way as completed, using the existing stateGroup check in
issueRowHelpers so callers that normalize either canceled or cancelled are
handled consistently.

---

Outside diff comments:
In `@apps/web/src/components/work-item/layouts/IssueLayoutBoard.tsx`:
- Around line 347-434: The editable controls in IssueLayoutBoard are still
rendered inside the BoardCard Link, which creates invalid interactive nesting
for the editable cells and DatePickerTrigger. Move the editing UI for
EditablePriorityCell, EditableStateCell, EditableLabelCell,
EditableAssigneeCell, and DatePickerTrigger out of the card anchor, or switch
the card container to a non-link wrapper and keep navigation separate. Use the
BoardCard/IssueLayoutBoard structure and the CellGuard-wrapped editable sections
to locate and refactor the layout.
🪄 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: 889c81c6-324d-4b6d-abb8-ac6283533800

📥 Commits

Reviewing files that changed from the base of the PR and between c8e9b96 and 0662ba5.

📒 Files selected for processing (7)
  • apps/web/src/components/work-item/DatePickerTrigger.tsx
  • apps/web/src/components/work-item/IssueRowCells.tsx
  • apps/web/src/components/work-item/layouts/IssueLayoutBoard.tsx
  • apps/web/src/components/work-item/layouts/IssueLayoutList.tsx
  • apps/web/src/components/work-item/layouts/IssueLayoutSpreadsheet.tsx
  • apps/web/src/lib/issueRowHelpers.ts
  • apps/web/src/pages/IssueListPage.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/src/components/work-item/layouts/IssueLayoutList.tsx
  • apps/web/src/components/work-item/layouts/IssueLayoutSpreadsheet.tsx

Comment thread apps/web/src/lib/issueRowHelpers.ts Outdated
CodeRabbit: isOverdue only skipped "cancelled"/"completed", but the API's state
group uses the "canceled" spelling, so cancelled issues with a past due date
still flagged overdue. Accept both spellings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@martian56 martian56 merged commit 0177e69 into main Jun 26, 2026
8 checks passed
@martian56 martian56 deleted the feat/inline-edit branch June 26, 2026 06:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] Add inline property editing to work-item layouts

1 participant