From ab92641541f5ee28bd8815c9ea57fb9807e7f365 Mon Sep 17 00:00:00 2001 From: jialin Date: Fri, 10 Jul 2026 16:45:48 +0800 Subject: [PATCH] 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). --- src/pages/usage/hooks/use-resource-meta.ts | 19 +++++----- .../usage/services/use-query-meta-data.ts | 38 +++++++++++-------- src/pages/usage/summary-tab/index.tsx | 12 +++--- 3 files changed, 38 insertions(+), 31 deletions(-) diff --git a/src/pages/usage/hooks/use-resource-meta.ts b/src/pages/usage/hooks/use-resource-meta.ts index 8572656a..6809bc5d 100644 --- a/src/pages/usage/hooks/use-resource-meta.ts +++ b/src/pages/usage/hooks/use-resource-meta.ts @@ -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)); }; /** diff --git a/src/pages/usage/services/use-query-meta-data.ts b/src/pages/usage/services/use-query-meta-data.ts index ebe3612a..cb3dd4f0 100644 --- a/src/pages/usage/services/use-query-meta-data.ts +++ b/src/pages/usage/services/use-query-meta-data.ts @@ -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); }; diff --git a/src/pages/usage/summary-tab/index.tsx b/src/pages/usage/summary-tab/index.tsx index 535b194d..154e569f 100644 --- a/src/pages/usage/summary-tab/index.tsx +++ b/src/pages/usage/summary-tab/index.tsx @@ -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