Skip to content

feat: manual drag and drop ordering for board labels#8149

Open
Lightshadow02 wants to merge 5 commits into
nextcloud:mainfrom
Lightshadow02:feat/label-manual-order
Open

feat: manual drag and drop ordering for board labels#8149
Lightshadow02 wants to merge 5 commits into
nextcloud:mainfrom
Lightshadow02:feat/label-manual-order

Conversation

@Lightshadow02

Copy link
Copy Markdown
  • Resolves: long-standing request to control which label shows first on cards
  • Target version: main

Summary

Labels currently render in creation order (or alphabetically depending on the surface), and board managers have no way to control which label appears first on card badges. This adds manual ordering:

  • New nullable order column on deck_labels (migration) and order field on the label JSON objects (additive — clients that ignore it are unaffected)
  • LabelService::reorder(boardId, labelIds) writes positions 0..n; requires board MANAGE permission; rejects incomplete/foreign/duplicate lists; new labels append at the end of an existing manual order; board clone preserves the order
  • New routes: PUT /boards/{boardId}/labels/reorder on the page routes and the /api/v1.x REST surface (registered before the {labelId} wildcard so the static segment is not shadowed). API addition, documented in docs/API.md
  • All label listings sort by manual order with unordered labels last (LabelMapper), including findAssignedLabelsForBoard used by the archived-cards view
  • Frontend: drag & drop in the board sidebar Tags tab (vue-smooth-dnd, same pattern as stacks/cards; disabled for non-managers and archived boards; the inline edit form is a non-drag area), optimistic update with rollback + error toast, and a shared sortLabels() helper applied to every render surface (card badges, tag selector, filter, reference widgets)
  • First jest unit tests in the repo for the helper; the jest config in package.json pointed at the non-installed vue-jest package (installed transformer is @vue/vue2-jest), fixed in a separate commit (shared with fix: align attachment frontend requests with registered OCS routes #8148 — trivial to drop on whichever merges second)
  • PHPUnit coverage: reorder happy path + permission/archived/incomplete/empty/foreign rejections, entity serialization

How to test

  1. Board sidebar → Tags tab → drag labels into the wanted order
  2. Card badges, the card tag selector and the filter popover follow the new order; F5 persists it
  3. PUT /index.php/apps/deck/boards/{boardId}/labels/reorder with {"labelIds":[...]} returns the labels in the new order

Checklist

  • Code is properly formatted
  • Sign-off message is added to all commits
  • Tests (unit, integration, api and/or acceptance) are included
  • Documentation (manuals or wiki) has been updated or is not required

🤖 Generated with Claude Code

Lightshadow02 and others added 5 commits July 10, 2026 10:51
Signed-off-by: HUGO LOUREIRO <149155946+Lightshadow02@users.noreply.github.com>

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: HUGO LOUREIRO <149155946+Lightshadow02@users.noreply.github.com>

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The jest config referenced vue-jest which is not a dependency; the
installed transformer is @vue/vue2-jest. Went unnoticed as the repo had
no jest tests yet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: HUGO LOUREIRO <149155946+Lightshadow02@users.noreply.github.com>
Signed-off-by: HUGO LOUREIRO <149155946+Lightshadow02@users.noreply.github.com>

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- lib/Migration: restore blank line between declare(strict_types=1)
  and the namespace declaration to match cs:check and the sibling
  migration.
- LabelMapper::findAssignedLabelsForBoard now selects the label
  order column (quoted via selectAlias since ORDER is reserved) so
  the archived-cards view receives manual ordering like findAll().
  Label::$order is already typed int via addType() and hydrates the
  same way selectAlias('c.id', 'card_id') already does in this
  method.
- store/main.js reorderLabelsOfCurrentBoard: capture the board id at
  entry and guard every commit plus the loadStacks dispatch against
  the current board having changed (or become null) during the
  awaits, fixing a board-switch race that could apply another
  board's label order or revert state for the wrong board.
- Apply the shared sortLabels() helper (instead of ad-hoc
  alphabetical .sort()) to the remaining label render surfaces that
  still ignored manual order: Controls.vue's "Filter by tag" list,
  CardReferenceWidget.vue and CommentReferenceWidget.vue (the rich
  object reference widgets embedded in comments/description). Also
  removed CardSidebarTabDetails.vue's labelsSorted computed, which
  was dead code no longer referenced by its template (TagSelector
  sorts internally).
- TagsTabSidebar.vue: add non-drag-area-selector=".label-form" to
  the labels Container, mirroring Stack.vue's non-drag-area-selector
  pattern, so selecting text in the inline label edit form no longer
  starts a drag.
- docs/API.md: document PUT /boards/{boardId}/labels/reorder and the
  label object's new order field.

Signed-off-by: HUGO LOUREIRO <149155946+Lightshadow02@users.noreply.github.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant