feat(dashboard): allow reordering virtual model targets via drag handle - #77
feat(dashboard): allow reordering virtual model targets via drag handle#77weselben wants to merge 7 commits into
Conversation
Add a grip handle left of each target row's remove button. Drag a row onto another row (or focus the handle and use the arrow keys) to change its position. The full list is reorderable, including the primary target, so the failover priority and the round-robin queue order are editable for every strategy. The save payload already serializes targets in array order, so no backend change is needed.
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueThanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Frontend: models-vm-target-reorder.test.js pins drag-and-move (insert- between, not swap), payload order after a reorder, reopen round-trip, and weight/provider-pin survival. Backend: TestUpsertVirtualModelTargetOrderRoundTrips pins that a reorder save stores and returns targets in exactly the order the editor sent.
|
Thanks for testing — the PR body now explains the behavior in a Behavior section with your screenshots embedded. One clarification on your second screenshot: the dashed outline is not the model being replaced. It is the drop position: the dragged row will land exactly there, and the outlined row (plus everything between origin and drop row) shifts by one. Nothing is ever swapped away or lost — it is a move-in-between, and the contract tests added in a7d3ff5 pin that (drop last onto first → lands first, others keep relative order) alongside a Go round-trip test that the saved order is what the editor sent. Also new in this push:
|
After ArrowUp/ArrowDown the each block reuses the DOM node at the old index, so focus stayed on whatever model now occupies that slot. Track the requested focus index and move focus to the moved row's handle, so repeated arrows walk the same model through the list.
weselben
left a comment
There was a problem hiding this comment.
Review — feat(dashboard): allow reordering virtual model targets
Validate: PASS — 522 added lines, no secret patterns, diff sane.
Scope applied: unnecessary comments, test-line coverage, no fork-specific references, match merged-PR norms (baseline: ENTERPILOT#855 playground).
Findings (8)
- 🔵
web/dashboard/tests/models-vm-target-reorder.test.js:1— header comment references "(PR #77)", a fork-specific PR number. Meaningless in the upstream repo. Drop the PR reference; keep the behavior description. - 🟡
web/dashboard/src/pages/models/VmTargetRow.svelte:63—ondragleavefires when the cursor enters a child element (SearchSelect, weight input), so the dashed drop highlight flickers off while still over the row. Guard with arelatedTargetcontainment check before clearingvmDropIndex. - 🔵
web/dashboard/src/pages/models/vmForm.js:81-118— three multi-line comments above the new exports restate the code. Merged-PR norm in this area is one line. Trim each to a single line. - 🔵
web/dashboard/src/pages/models/virtualModelEditor.svelte.js:90-120— same: the store method comments (enterVmTargetDrop,dropVmTarget,requestVmFocusHandle) explain the caller's wiring, not the method. One line each. - 🔵
internal/admin/handler_virtualmodels_test.go:738—slicesEqualreimplements stdlibslices.Equal(Go 1.21+, repo is on 1.27). Importslices, delete the helper. - 🔵
web/dashboard/src/pages/models/VirtualModelEditor.svelte:84-95—vm.vmTargetCount() > 1 && !vm.vmFormManagedis computed inline on every row. Hoist to one reactive const in the component script. - 🔵
web/dashboard/tests/models-virtual-models.test.js:1074-1142— the twomoveFormTargettests duplicate the contract file (models-vm-target-reorder.test.js). Single source of truth: keep the contract file, drop these. - 🔵
web/dashboard/src/pages/models/vmForm.js:84— coverage gap against the 99% target:nullform and non-arraytargetsbranches ofvmFormTargetCount/flattenFormTargets/moveFormTargetare untested. Add a small table case to the contract file.
Notes
- No fork references in code comments other than the flagged test header.
- Component DOM wiring (drag handlers, focus effect) is not node-testable; consistent with how the rest of the dashboard ships.
- Behavior itself verified manually on a seeded demo container; contract tests pin the payload order.
Next step: pr-resolve for the 🟡; the 🔵 batch is a quick cleanup pass.
Verification pass on PR head (
|
| Check | Scope | Result |
|---|---|---|
gofmt -l |
whole repo | ✅ clean |
go vet |
changed pkgs + go vet ./... |
✅ clean (3 pre-existing internal/core json-tag warnings, untouched by this PR) |
go test -race |
internal/admin, internal/virtualmodels |
✅ pass |
go build ./... |
whole repo | ✅ clean |
ineffassign |
changed pkgs | ✅ clean |
errcheck |
changed pkgs | ✅ PR files clean (4 hits, all pre-existing in failover_test.go, resolve_bench_test.go, store_mongodb.go) |
staticcheck / golangci-lint |
— | |
npm test (dashboard) |
596 tests | ✅ pass |
svelte-check |
dashboard | ✅ 0 errors, 0 warnings |
Files that may need fixing (aside from the review findings)
None from the verification side. The review findings (1 🟡 + 7 🔵, see review above) cover everything actionable: comment trim, slicesEqual → stdlib, duplicate tests consolidation, dragleave flicker, fork-specific PR reference in the contract-test header, draggable hoist, and the null/non-array coverage cases.
Toolchain note for CI: if upstream CI pins golangci-lint, it will need a build made with go1.27+ before it can run on this repo. Everything else in the matrix is green on PR head.
- drop fork-specific PR reference from contract test header - guard dragleave with relatedTarget containment (drop highlight no longer flickers between child elements) - trim over-long comments to match merged-PR norms - slicesEqual -> stdlib slices.Equal in the Go contract test - hoist draggable expression to one $derived const - consolidate moveFormTarget tests into the contract file - cover null/malformed form branches of the target helpers
Resolution summary (6e843c2)Resolved: 8 / 8 inline findings — one commit applied to the PR head branch.
Verification on resolved head: PR: #77 |
The editor always numbered extra rows from 1, but flattenFormTargets skips an empty primary row: in a fresh/unsaved form (or after clearing the primary) UI index n mapped to flattened index n-1, so drops on the new/unsaved rows landed out of bounds and the move was silently dropped. Extras now start at 0 when the primary has no model, and the primary handle hides. enterVmTargetDrop also skips redundant writes so fast drags no longer re-render the list on every dragover event.
Prevent default on dragenter so browsers that require it allow the drop, and disable text selection on the handle so a fast grab-release cannot turn into a selection instead of a drag.
Final manual reviewManual verification on a seeded demo container (commit
No regressions in the rest of the editor. No backend changes — the backend round-trip test in Ready for upstream. |
|
moved to upstream repo - ENTERPILOT#879 |


TL;DR
The Virtual Model editor shows fallback targets in the order they were added. There is no way to change that order: users must delete and re-add targets to change the failover priority or the round-robin queue. Add a drag handle to each target row. Drag a row onto another row to move it. The full list is reorderable, including the primary target.
Files to review (9, +502 / -5):
web/dashboard/src/pages/models/VmTargetRow.svelte(start here)web/dashboard/src/pages/models/vmForm.jsflattenFormTargetsandmoveFormTarget.web/dashboard/src/pages/models/virtualModelEditor.svelte.jsvmDragIndex,vmDropIndex) and the drop action.web/dashboard/src/pages/models/VirtualModelEditor.svelteweb/dashboard/tests/models-vm-target-reorder.test.jsinternal/admin/handler_virtualmodels_test.godocs/features/virtual-models.mdxweb/dashboard/messages/en.json,pl.jsonmodels_move_targetlabel for the handle.Behavior
failover, position 1 is the first target the gateway tries. For balancing strategies, position 1 is the first slot in the queue.How
moveFormTargetsplices that list and writes the result back into the primary slot plus the extras array.dragenter/dragoverpreventDefaultsodropfires in every browser; text selection is disabled on the handle so a fast grab-release commits the drag. The drop target row is tracked invmDropIndexfor the highlight, written only on change to avoid re-render storms mid-drag.buildVirtualModelSavePayloadalready serializes targets in array order, and the backend reads that order as the failover priority / queue order.Contract tests
The behavior above is pinned so refactors cannot silently change it:
web/dashboard/tests/models-vm-target-reorder.test.js— insert-between move semantics (drop last onto first lands first, others keep relative order); payload lists targets in the new display order after a drag; reopening a reordered model restores the order; weights and provider pins survive; flattened-index alignment for empty and filled primaries; null/malformed form handling.TestUpsertVirtualModelTargetOrderRoundTrips(internal/admin/handler_virtualmodels_test.go) — a reorder PUT stores the targets in exactly the sent order, and the list view the dashboard renders returns the same order.Tests
npm testinweb/dashboard: 596 pass (6 in the reorder contract file).go test ./internal/admin/ ./internal/virtualmodels/: pass.gofmt -l: clean.go vet: clean.go build ./...: clean.ineffassign: clean.errcheck: clean on the touched files.npm run check: svelte-check reports 0 errors, 0 warnings.Follow-up
droponly fires oncedragover/dragenterarepreventDefaulted) is exactly whatnode:testcannot pin.Links
This PR description was generated with AI assistance.