Group and custom-field reordering, and a working reparent drag - #278
Merged
Conversation
Groups gain an order column (migration backfills 0, so untouched systems keep their alphabetical view via the (order, name) sort), carried through the schemas, the list endpoints, the export, the native importer, and the OpenPlural sheaf extension, with both parity suites classifying it. A new PUT /v1/groups/reorder applies an ordered id list as order = index in one transaction, tenant-scoped, with duplicate and foreign ids rejected; PUT /v1/fields/reorder is its twin for custom-field definitions, whose order column existed but had no web surface. Up/down arrows move a group among its siblings on the Groups page and reorder fields in Settings; drag stays reparenting. Public profiles and share links follow the owner's order for both groups and member-card fields, name as tiebreak. Drag-to-reparent itself was broken in both engines, each its own way: Chromium cancels a drag whose source DOM mutates during dragstart, and setting draggingId there re-rendered the row and mounted the drop zone in that same tick, so the drag aborted instantly (cursor moved, row never lifted); the state update is now deferred one tick. Firefox never started the drag at all because nothing populated the drag data store, which it requires; dragstart now sets it. Drop handlers preventDefault for hygiene. The custom-field importer's order coercion is also hardened to match the new group path, so a crafted non-integer order can no longer abort an import.
SiteRelEnby
enabled auto-merge
September 8, 2026 01:53
…e merge Both files were newly created and never staged - the feature commit used commit -a, which only stages modifications to tracked files. Local runs passed because the files sat in the working tree; CI built from the commit, had no migration, and every request touching groups.order failed with UndefinedColumnError.
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.
Adds manual ordering for groups and custom fields, and fixes drag-to-reparent on the Groups page, which turned out to be broken in both browser engines.
Group reordering did not exist: groups had no order column and the list endpoint hardcoded alphabetical. They now carry
order, sorted(order, name)so untouched systems keep today's A-Z view, applied everywhere the app lists groups and now also on public profiles and share links (owner's arrangement, name tiebreak - the same order visitors would infer from the app anyway once the owner curates it). The column rides the export, the native importer, and the OpenPlural sheaf extension, with both parity suites classifying it. Custom fields had the column all along but no web surface; both now share the same affordance: up/down arrows (drag on groups is taken by reparenting), backed by symmetric atomic bulk endpointsPUT /v1/groups/reorderandPUT /v1/fields/reorder(ordered ids -> order = index, tenant-scoped, duplicates and foreign ids rejected, declared ahead of the/{id}routes).The reparent drag failed differently per engine: Chromium cancels a drag whose source DOM mutates during dragstart, and setting the dragging state there re-rendered the row and mounted the drop zone in the same tick, aborting the drag instantly (the cursor moved, the row never lifted); the state update is now deferred one tick. Firefox additionally never starts a drag with an empty drag data store, which dragstart never populated; it now does. Both verified live on localdev.
Also hardens the custom-field importer's order coercion (a crafted non-integer order aborted the whole import) to match the new group path.
Migration adds the column with a fail-fast lock_timeout; metadata-only on modern Postgres. Full behavioural suite and the public_profiles config pass; ruff, tsc, eslint, and a production build are clean. New endpoint tests cover happy path, duplicates, foreign ids via a second account, and unnamed-rows-keep-position; import round-trip covers order preservation and junk coercion.