From 08702fb643259d30a0b65a604cf29464ff9a90ef Mon Sep 17 00:00:00 2001 From: ppanel-web Date: Sat, 21 Feb 2026 23:03:02 +0000 Subject: [PATCH] fix(admin): stabilize node sorting with duplicate sort values --- apps/admin/src/sections/nodes/index.tsx | 30 +++++++++++++++---------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/apps/admin/src/sections/nodes/index.tsx b/apps/admin/src/sections/nodes/index.tsx index 60f2ec8..e2afc8a 100644 --- a/apps/admin/src/sections/nodes/index.tsx +++ b/apps/admin/src/sections/nodes/index.tsx @@ -227,8 +227,7 @@ export default function Nodes() { }} onSort={async (source, target, items) => { // NOTE: `items` is the current page's items from ProTable. - // We should avoid mutating it in-place, and we should persist the sort - // changes reliably (await the API call). + // Avoid mutating it in-place, and persist sort changes reliably. const sourceIndex = items.findIndex( (item) => String(item.id) === source ); @@ -239,19 +238,27 @@ export default function Nodes() { if (sourceIndex === -1 || targetIndex === -1) return items; const prevSortById = new Map(items.map((it) => [it.id, it.sort])); - const originalSorts = items.map((item) => item.sort); const next = items.slice(); const [movedItem] = next.splice(sourceIndex, 1); next.splice(targetIndex, 0, movedItem!); - // Keep the existing `sort` values bound to positions (so reordering swaps - // sort values instead of inventing new ones). - const updatedItems = next.map((item, index) => { - const originalSort = originalSorts[index]; - const newSort = originalSort !== undefined ? originalSort : item.sort; - return { ...item, sort: newSort }; - }); + // IMPORTANT: + // Some installations have duplicate / empty `sort` values (commonly 0 or null) + // which makes the order appear "random" after refresh and also makes + // "swap sort values" strategies a no-op. + // + // To make the ordering stable, we re-index the current page to a strictly + // increasing sequence. + const numericSorts = items + .map((it) => (typeof it.sort === "number" ? it.sort : Number.NaN)) + .filter((v) => Number.isFinite(v)) as number[]; + const baseSort = numericSorts.length ? Math.min(...numericSorts) : 0; + + const updatedItems = next.map((item, index) => ({ + ...item, + sort: baseSort + index, + })); const changedItems = updatedItems.filter( (item) => item.sort !== prevSortById.get(item.id) @@ -259,8 +266,7 @@ export default function Nodes() { if (changedItems.length > 0) { await resetSortWithNode({ - // Send all changed rows (within the current page) so backend can - // persist the new ordering. + // Send all changed rows (within the current page) so backend can persist. sort: changedItems.map((item) => ({ id: item.id, sort: item.sort,