fix(usage): sort deleted filter options to the bottom of every dropdown

Deleted users / models / API keys / instances / volumes now sink below the
live entries in each usage tab's filter, while the current account stays on
top and live entries keep their order (stable sort).
This commit is contained in:
jialin
2026-07-10 17:41:54 +08:00
committed by jialin
parent 1f861c5844
commit ab92641541
3 changed files with 38 additions and 31 deletions
+10 -9
View File
@@ -26,11 +26,16 @@ const EMPTY: ResourceMetaOptions = {
volumes: []
};
// Deleted entries sink to the bottom of the dropdown; live ones keep their
// incoming order (sort is stable).
const toOptions = (items: ResourceFilterOption[]): SelectOption[] =>
items.map((i) => ({ value: i.id, label: i.label, deleted: i.deleted }));
items
.map((i) => ({ value: i.id, label: i.label, deleted: i.deleted }))
.sort((a, b) => Number(!!a.deleted) - Number(!!b.deleted));
// Tag the signed-in user's own entry and sort it first, matching the Tokens
// tab's "[Current Account]" treatment.
// Tag the signed-in user's own entry. Ordering: current account first, deleted
// entries last, everything else keeps its incoming order — matching the Tokens
// tab's "[Current Account]" treatment while pushing stale rows out of the way.
const toUserOptions = (
items: ResourceFilterOption[],
currentUserId?: number
@@ -41,12 +46,8 @@ const toUserOptions = (
deleted: i.deleted,
isCurrent: currentUserId != null && i.id === currentUserId
}));
if (currentUserId == null) return options;
return options.sort((a, b) => {
if (a.isCurrent) return -1;
if (b.isCurrent) return 1;
return 0;
});
const rank = (o: SelectOption) => (o.isCurrent ? 0 : o.deleted ? 2 : 1);
return options.sort((a, b) => rank(a) - rank(b));
};
/**
+23 -15
View File
@@ -40,18 +40,17 @@ export default function useQueryUsageMetaData() {
routes: []
});
// the current user sort in the first place
// Current account first, deleted entries last, everything else keeps its
// incoming order (sort is stable).
const sortUsers = (users: UsageFilterItem[]) => {
const currentUserId = initialState?.currentUser?.id;
if (!currentUserId) return users;
const sortedUsers = [...users].sort((a, b) => {
if (a.identity.current?.user_id === currentUserId) return -1;
if (b.identity.current?.user_id === currentUserId) return 1;
return 0;
});
return sortedUsers;
const rank = (u: UsageFilterItem) =>
currentUserId && u.identity.current?.user_id === currentUserId
? 0
: u.deleted
? 2
: 1;
return [...users].sort((a, b) => rank(a) - rank(b));
};
const queryMetaData = async () => {
@@ -74,12 +73,21 @@ export default function useQueryUsageMetaData() {
value: optionValue(item.identity.current?.api_key_id, index),
label: item.identity.value.api_key_name || ''
})
}),
// Deleted keys sink to the bottom within each user group.
}).map((group) => ({
...group,
children: [...group.children].sort(
(a, b) => Number(!!(a as any).deleted) - Number(!!(b as any).deleted)
)
})),
// Deleted models sink to the bottom of the dropdown.
routes:
(res?.filters?.routes || []).map((item, index) => ({
...item,
value: optionValue(item.identity.current?.route_id, index)
})) || []
(res?.filters?.routes || [])
.map((item, index) => ({
...item,
value: optionValue(item.identity.current?.route_id, index)
}))
.sort((a, b) => Number(!!a.deleted) - Number(!!b.deleted)) || []
};
setResult(data);
};
+5 -7
View File
@@ -271,13 +271,11 @@ const SummaryTab: React.FC = () => {
isCurrent: currentUserId != null && id === currentUserId
});
});
// Sort the signed-in user first, tagged "[Current Account]" (matches the
// Tokens tab), regardless of which source it came from.
return Array.from(map.values()).sort((a, b) => {
if (a.isCurrent) return -1;
if (b.isCurrent) return 1;
return 0;
});
// Signed-in user first (tagged "[Current Account]", matches the Tokens
// tab), deleted entries last, everything else keeps its order — regardless
// of which source it came from.
const rank = (o: SelectOption) => (o.isCurrent ? 0 : o.deleted ? 2 : 1);
return Array.from(map.values()).sort((a, b) => rank(a) - rank(b));
}, [resourceUsers, tokenMeta, currentUserId]);
// user id → the identity object the token series filters by. Built from the