Content
View differences
Updated by Alexander Coles 6 days ago
Coordinate the retirement of `generic-drag-and-drop.controller.ts` and all of its remaining consumers by migrating them to the shared Stimulus `sortable-lists` suite on Atlassian Pragmatic Drag and Drop.
The implementation is split into vertical child Work Packages so each surface can be reviewed and verified independently. This parent owns the shared contract, sequencing, and final removal gate.
### Decided contract
#### Move wire
The full move wire is:
* `list_type` — sortable item/list family;
* `list_id` — destination list, including empty destinations; and
* `prev_id` — relative ordering coordinate.
For complete lists, blank
Blank `prev_id` means move to top. The paginated status adapter in #AUTOWORK-285 interprets a blank predecessor as the start of the requested page and validates explicit anchors against global status order. Shared backend helpers reject a nonblank unknown/out-of-scope anchor or a self-reference with 422 and no mutation. Destination changes and reorder persistence are transactional.
Backlogs retains its currently more lenient unknown-anchor behavior; aligning it is tracked by #AGILE-370.
#### Response policy
The suite places rows optimistically in the DOM for every consumer, but server reconciliation is policy-driven:
* these admin/settings consumers retain their existing update/replace component streams and add `method="morph"`; and
* Backlogs alone opts into the event-only success response from #AGILE-322 — an event-only turbo-stream carrying `WORK_PACKAGE_MOVED_EVENT`; a bare `204 No Content` was explicitly rejected there because it has no body to carry the event (see https://github.com/opf/openproject/pull/24157).
The suite therefore sends `optimistic=true` only when the event-only response contract is explicitly selected.
#### Shared ordering families
* `acts_as_list` consumers use a strict scoped move-after helper.
* Array-backed orders use a shared insert-after-key helper.
* Resource Planner adapts its custom repack operation to `prev_id`.
#### Move menus
For consumers whose move destinations are represented in the loaded rows, server-rendered Server-rendered top/up/down/bottom menu items use #DREAM-775's shared descriptor. Client-side availability gating resolves a `prev_id`, and menu moves use the same persistence request as dragging.
Paginated statuses are an explicit exception: #AUTOWORK-285 / https://github.com/opf/openproject/pull/25223 retains server-backed directional menus because they move across page boundaries. Preserve those menus and disable reordering under filters. Its follow-up [DREAM-839](https://community.openproject.org/wp/DREAM-839) changes table presentation without substituting loaded-row-only menu behavior.
### Child Work Packages
1. #DREAM-786 — core suite/backend extensions plus the complete Project attributes vertical slice.
2. #DREAM-789 — enumerations, user custom field sections, phase definitions, and export templates.
3. #DREAM-790 — user custom fields and built-in attributes.
4. #DREAM-791 — grouped work package types and form configuration.
5. #DREAM-792 — meeting sections and agenda items.
6. #DREAM-793 — manually ordered Resource Planner work packages.
Cross-cutting children:
* #DREAM-774 — document the extended DOM and request/response contract.
* #DREAM-775 — shared four-direction move-menu descriptor.
### Additional coordinated work
* #AUTOWORK-285 migrates status dragging; it is in review and PR https://github.com/opf/openproject/pull/25223 is open as of 2026-09-14.
* [DREAM-838](https://community.openproject.org/wp/DREAM-838) documents and verifies the shared BorderBoxTable integration. It reuses #DREAM-786's tag-independent row handling and existing component hooks.
* [DREAM-837](https://community.openproject.org/wp/DREAM-837) coordinates the semantic-table migrations deferred by #DREAM-697. Statuses and document types use the shared integration; the three display-only migrations do not gate retirement of the legacy drag controller.
* #DREAM-793 already owns Resource Planner's BorderBoxTable migration to sortable-lists. Keep its persistence and consumer work there.
### Sequencing and relations
#DREAM-697 #DREAM-697's markup migration gates only #DREAM-789 now: its former monolithic PR was split into stacked slices, and its four children are closed. #DREAM-789 follows #DREAM-802 has satisfied #DREAM-789's list-markup prerequisite. (phase definitions + export templates). Rich-header surfaces deferred by #DREAM-697 remain in are handled within their own DREAM-671 clusters. cluster and do not expand the shared BorderBoxList API merely to satisfy this migration.
#DREAM-786 (shared core The children are sequenced with Follows relations: #DREAM-774 and Project attributes) and #DREAM-774 (contract documentation) are closed. #DREAM-775 follow #DREAM-786, and #DREAM-789 through #DREAM-793 remain in specification as of 2026-09-14; their completion has not been inferred from closed prerequisites.
Keep each remaining cluster follows all three. Recommended order: #DREAM-789, #DREAM-790, #DREAM-791, then the existing sequencing between these clusters. Shared table integration [DREAM-838](https://community.openproject.org/wp/DREAM-838) precedes [DREAM-839](https://community.openproject.org/wp/DREAM-839), [DREAM-840](https://community.openproject.org/wp/DREAM-840) high-risk #DREAM-792 and is coordinated with #DREAM-793. [DREAM-839](https://community.openproject.org/wp/DREAM-839) also follows AUTOWORK-285; [DREAM-840](https://community.openproject.org/wp/DREAM-840) follows DREAM-789's enumeration work. Actual work-package relations must be maintained separately from these description links. #DREAM-793 last.
Experiment PR https://github.com/opf/openproject/pull/23893 established that the suite can support the Project attributes surface, but also showed that teaching the frontend an absolute-position wire is the wrong trade. #DREAM-786 salvaged its multi-type list contract, per-type move-URL resolution, leaf-scoped acceptance, and the Project attributes migration itself; the absolute-position mode was discarded.
### Acceptance criteria
* [ ] All six cluster Work Packages, #DREAM-774/#DREAM-775, #AUTOWORK-285 Packages and shared table integration [DREAM-838](https://community.openproject.org/wp/DREAM-838) #DREAM-774/#DREAM-775 are complete.
* [ ] Every former shared-controller consumer uses `sortable-lists` and the decided relative-anchor contract.
* [ ] Admin/settings consumers reconcile through morphing their existing component response boundaries.
* [ ] Directional menu moves and drag moves share one persistence path where loaded-row anchors express the full move scope; paginated statuses retain the documented server-backed cross-page exception. per surface.
* [ ] `generic-drag-and-drop.controller.ts`, its tests, dead position-mode behavior, Angular-service bridge, and consumer wiring are removed.
* [ ] `data-target-container-accessor` is removed from the migrated consumers.
* [ ] No meetings-specific or form-configuration-specific Dragula escape hatch remains.
* [ ] A final inventory confirms no remaining reference to the shared controller.
### Out of scope
* Display-only table migrations in [DREAM-837](https://community.openproject.org/wp/DREAM-837). Status/document-type presentation follow-ups are tracked there and do not independently block legacy-controller removal once their drag consumers are migrated.
* Backlogs advanced interactions and its existing event-only response contract.
* Independent Dragula consumers tracked by #DREAM-785 and #DREAM-787.
* Native hierarchy sorting tracked by #DREAM-788.
* Angular surfaces tracked by #DREAM-755, #DREAM-756 and its children, and #DREAM-761.
* Removal of the Dragula package itself, tracked by #DREAM-762.
The implementation is split into vertical child Work Packages so each surface can be reviewed and verified independently. This parent owns the shared contract, sequencing, and final removal gate.
### Decided contract
#### Move wire
The full move wire is:
* `list_type` — sortable item/list family;
For complete lists, blank
Blank
Backlogs retains its currently more lenient unknown-anchor behavior; aligning it is tracked by #AGILE-370.
#### Response policy
The suite places rows optimistically in the DOM for every consumer, but server reconciliation is policy-driven:
* these admin/settings consumers retain their existing update/replace component streams and add `method="morph"`; and
#### Shared ordering families
* `acts_as_list` consumers use a strict scoped move-after helper.
For consumers whose move destinations are represented in the loaded rows, server-rendered
Paginated statuses are an explicit exception: #AUTOWORK-285 / https://github.com/opf/openproject/pull/25223 retains server-backed directional menus because they move across page boundaries. Preserve those menus and disable reordering under filters. Its follow-up [DREAM-839](https://community.openproject.org/wp/DREAM-839) changes table presentation without substituting loaded-row-only menu behavior.
### Child Work Packages
1. #DREAM-786 — core suite/backend extensions plus the complete Project attributes vertical slice.
* #DREAM-774 — document the extended DOM and request/response contract.
* #AUTOWORK-285 migrates status dragging; it is in review and PR https://github.com/opf/openproject/pull/25223 is open as of 2026-09-14.
* [DREAM-838](https://community.openproject.org/wp/DREAM-838) documents and verifies the shared BorderBoxTable integration. It reuses #DREAM-786's tag-independent row handling and existing component hooks.
* [DREAM-837](https://community.openproject.org/wp/DREAM-837) coordinates the semantic-table migrations deferred by #DREAM-697. Statuses and document types use the shared integration; the three display-only migrations do not gate retirement of the legacy drag controller.
* #DREAM-793 already owns Resource Planner's BorderBoxTable migration to sortable-lists. Keep its persistence and consumer work there.
### Sequencing and relations
#DREAM-697
#DREAM-786 (shared core
Keep
Experiment PR https://github.com/opf/openproject/pull/23893 established that the suite can support the Project attributes surface, but also showed that teaching the frontend an absolute-position wire is the wrong trade. #DREAM-786 salvaged its multi-type list contract, per-type move-URL resolution, leaf-scoped acceptance, and the Project attributes migration itself; the absolute-position mode was discarded.
### Acceptance criteria
* [ ] All six cluster Work Packages, #DREAM-774/#DREAM-775, #AUTOWORK-285
* [ ] Every former shared-controller consumer uses `sortable-lists` and the decided relative-anchor contract.
* [ ] Admin/settings consumers reconcile through morphing their existing component response boundaries.
* [ ] Directional menu moves and drag moves share one persistence path where loaded-row anchors express the full move scope; paginated statuses retain the documented server-backed cross-page exception.
* [ ] `generic-drag-and-drop.controller.ts`, its tests, dead position-mode behavior, Angular-service bridge, and consumer wiring are removed.
* [ ] `data-target-container-accessor` is removed from the migrated consumers.
* [ ] No meetings-specific or form-configuration-specific Dragula escape hatch remains.
* [ ] A final inventory confirms no remaining reference to the shared controller.
### Out of scope
* Display-only table migrations in [DREAM-837](https://community.openproject.org/wp/DREAM-837). Status/document-type presentation follow-ups are tracked there and do not independently block legacy-controller removal once their drag consumers are migrated.
* Backlogs advanced interactions and its existing event-only response contract.