Connecting (chaining) tickets in project timeline - #45
Merged
wdembinski merged 7 commits intoSep 10, 2026
Merged
Conversation
The knob sat ~4px past the bar's own right edge with nothing painted in between, so a pointer crossing that gap rode over no hit-testable descendant of the row's `<g>` — `:hover` dropped mid-crossing, hiding the knob before the pointer landed on it. Add a short transparent, pointerEvents:'auto' bridge rect spanning from the bar's edge through the knob (the resize handles' own fill:transparent trick), so the row stays hovered continuously from bar to knob. Clicks on the bridge carry no handler of their own and bubble to the row `<g>`, same as clicking the bar. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
GraphPane ran React Flow fully controlled with no onNodesChange, so React Flow couldn't apply live drag deltas -- a dragged node only moved once onNodeDragStop synced positions back through the nodes memo. Switch to useNodesState/onNodesChange so drags render live, while still reconciling tickets+positions into it (new/removed tickets and refreshed ticket data), and keep the debounced ticketGraph:saveLayout on drag stop. Also remove <Background /> so the canvas no longer shows the dotted grid. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
An edge click resolves to its kind (links then chainLinks, the same split handleEdgesDelete already used) and reopens GraphLinkPicker in an edit mode: switch dependency <-> chain (a gated remove-then-add across the two id spaces) or delete outright, or just retune a chain edge's gate. The remove half of a switch, and an outright delete, run through GraphPane's existing removeTicketLink/removeChainLink so failures surface via the same deleteError MessageBar the Delete key already uses. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
An epic now lays out as an epicZone group node instead of an ordinary ticket card, with every ticket whose epicTaskId names it nested as a child (parentId + extent:'parent') at a position relative to the zone. The zone sizes to enclose its children (or a small empty band for a childless epic) and keeps the deterministic grid fallback and per-project layout persistence working for both group and child nodes. Also drops the mislabelled "Epic" caption TicketNode printed on children rather than the epic itself — dead now that an epic never renders as a TicketNode at all. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Draw a shaded band behind an epic's row and its contiguous child rows in the Gantt (ganttEpicBands, the label column's tint, and the chart's own rect) - the Timeline's analogue of the graph view's epicZone container node. An epic's own bar now unions its dated children's spans whenever it has any, collapsed or expanded alike (previously only when collapsed) - without that, an undated epic with dated children would draw no row at all in the expanded case, leaving its children ungrouped. GanttRow carries the new barFromChildren flag so the timeline still knows when a bar has nothing coherent to reschedule TO. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
groupTickets bucketed every ticket including epics, and an epic's own epicTaskId is null just like a true orphan's — so it fell into NO_EPIC_GROUP even though its children already promoted it to a header, making it render twice. Skip epic rows when bucketing and seed every epic's group header up front so a childless epic still gets an (empty) header instead of vanishing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Closes out the chaining-tickets fix round. RELEASE.md §1 (typecheck, test, build) is green, forced with no cache. The pure-logic coverage the plan named already existed for the Timeline/Backlog (ganttEpicBands in ganttLayout.test.ts, the NO_EPIC_GROUP regression in backlogView.test.ts) but not for the Graph view's layoutNodes, which stayed un-exported and untested through Phase 4 — exports it and adds graphLayout.test.ts, asserting an epic renders as an epicZone, a child gets parentId/extent set to it, a saved position wins over the grid/stack fallback for a zone and its children alike, and a zone the plan didn't cover (an epicTaskId naming an epic outside the project) still falls back to a top-level card. Also extends scripts/verify-connections.mjs with an epic+child getTicketGraphLayout/saveTicketGraphLayout scenario, proving the store's indifference to which taskId is an epic rather than assuming it — proven able to fail by mutating the child's expected position and confirming the script exits non-zero, then restored. All six defects the human reported against the merged feature check out against the code already on this branch: the timeline connect-knob bridge rect, GraphPane's useNodesState-driven live drag, the removed dot-grid Background, the onEdgeClick edit picker, both epicZone/ganttEpicBands zones, and groupTickets skipping isEpic rows. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
That feature just landed but I can't use it. Why?