feat(usage): mark current account first in resource tab user filters
Apply the Tokens tab's current-user-first ordering and [Current Account] marker to the Summary/Storage/Instances tab user filters.
This commit is contained in:
@@ -27,6 +27,7 @@ interface SelectOption {
|
||||
value: number;
|
||||
label: string;
|
||||
deleted?: boolean;
|
||||
isCurrent?: boolean;
|
||||
}
|
||||
|
||||
// Optional per-tab entity filter (GPU instance on the GPU tab / volume on the
|
||||
@@ -137,20 +138,25 @@ const ResourceFilterBar: React.FC<ResourceFilterBarProps> = (props) => {
|
||||
)
|
||||
: [dayjs().add(-DefaultDateConfig.defaultRange, 'd'), dayjs()];
|
||||
|
||||
const userOptionRender = (option: any) => (
|
||||
<span className="flex-center gap-4">
|
||||
<AutoTooltip ghost>{option?.data?.label}</AutoTooltip>
|
||||
{option?.data?.deleted && (
|
||||
<span
|
||||
className="text-tertiary"
|
||||
style={{ fontSize: 12, marginRight: 4 }}
|
||||
>
|
||||
[{intl.formatMessage({ id: 'usage.table.deleted' })}]
|
||||
</span>
|
||||
)}
|
||||
const renderTag = (tag: string) => (
|
||||
<span className="text-tertiary" style={{ fontSize: 12, marginRight: 4 }}>
|
||||
[{tag}]
|
||||
</span>
|
||||
);
|
||||
|
||||
const userOptionRender = (option: any) => {
|
||||
const { data } = option;
|
||||
return (
|
||||
<span className="flex-center gap-4">
|
||||
<AutoTooltip ghost>{data?.label}</AutoTooltip>
|
||||
{data?.isCurrent &&
|
||||
renderTag(intl.formatMessage({ id: 'usage.user.currentAccount' }))}
|
||||
{data?.deleted &&
|
||||
renderTag(intl.formatMessage({ id: 'usage.table.deleted' }))}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={FilterBarCss.wrapper}>
|
||||
<div className={FilterBarCss.filters}>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useModel } from '@@/plugin-model';
|
||||
import { useEffect, useState } from 'react';
|
||||
import {
|
||||
queryResourceFilterMeta,
|
||||
@@ -8,6 +9,9 @@ export interface SelectOption {
|
||||
value: number;
|
||||
label: string;
|
||||
deleted?: boolean;
|
||||
// The signed-in user's own entry, sorted first and tagged "[Current Account]"
|
||||
// in the filter dropdown (matches the Tokens tab).
|
||||
isCurrent?: boolean;
|
||||
}
|
||||
|
||||
export interface ResourceMetaOptions {
|
||||
@@ -25,6 +29,26 @@ const EMPTY: ResourceMetaOptions = {
|
||||
const toOptions = (items: ResourceFilterOption[]): SelectOption[] =>
|
||||
items.map((i) => ({ value: i.id, label: i.label, deleted: i.deleted }));
|
||||
|
||||
// Tag the signed-in user's own entry and sort it first, matching the Tokens
|
||||
// tab's "[Current Account]" treatment.
|
||||
const toUserOptions = (
|
||||
items: ResourceFilterOption[],
|
||||
currentUserId?: number
|
||||
): SelectOption[] => {
|
||||
const options = items.map((i) => ({
|
||||
value: i.id,
|
||||
label: i.label,
|
||||
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;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads the resource tabs' filter dropdown sources in one call:
|
||||
* - ``creators`` — "filter by user" (Tokens-tab equivalent of /usage/meta
|
||||
@@ -39,12 +63,14 @@ export default function useResourceMeta(
|
||||
scope: 'self' | 'all' = 'all'
|
||||
): ResourceMetaOptions {
|
||||
const [meta, setMeta] = useState<ResourceMetaOptions>(EMPTY);
|
||||
const { initialState } = useModel('@@initialState');
|
||||
const currentUserId = initialState?.currentUser?.id;
|
||||
|
||||
useEffect(() => {
|
||||
queryResourceFilterMeta(scope)
|
||||
.then((res) =>
|
||||
setMeta({
|
||||
creators: toOptions(res.creators),
|
||||
creators: toUserOptions(res.creators, currentUserId),
|
||||
instances: toOptions(res.instances),
|
||||
volumes: toOptions(res.volumes)
|
||||
})
|
||||
@@ -53,7 +79,7 @@ export default function useResourceMeta(
|
||||
// Network/auth errors surface via the global interceptor; leave the
|
||||
// dropdowns empty rather than crashing the tab.
|
||||
});
|
||||
}, [scope]);
|
||||
}, [scope, currentUserId]);
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import { useCoolAccents } from '@/hooks/use-cool-colors';
|
||||
import BarChart from '@/pages/_components/bar-chart';
|
||||
import PieChart from '@/pages/_components/pie-chart';
|
||||
import { formatLargeNumber } from '@/utils';
|
||||
import { useModel } from '@@/plugin-model';
|
||||
import { CardWrapper } from '@gpustack/core-ui';
|
||||
import { useAccess, useIntl } from '@umijs/max';
|
||||
import { Col, Row } from 'antd';
|
||||
@@ -210,6 +211,8 @@ const DomainSection: React.FC<{
|
||||
const SummaryTab: React.FC = () => {
|
||||
const access = useAccess();
|
||||
const intl = useIntl();
|
||||
const { initialState } = useModel('@@initialState');
|
||||
const currentUserId = initialState?.currentUser?.id;
|
||||
const t = (id: string) => intl.formatMessage({ id });
|
||||
// One vivid primary per summary card (Tokens / Compute / Storage).
|
||||
const coolColors = useCoolAccents()(3);
|
||||
@@ -244,16 +247,31 @@ const SummaryTab: React.FC = () => {
|
||||
const userOptions = useMemo<SelectOption[]>(() => {
|
||||
const map = new Map<number, SelectOption>();
|
||||
resourceUsers.forEach((u) =>
|
||||
map.set(u.value, { value: u.value, label: u.label, deleted: u.deleted })
|
||||
map.set(u.value, {
|
||||
value: u.value,
|
||||
label: u.label,
|
||||
deleted: u.deleted,
|
||||
isCurrent: u.isCurrent
|
||||
})
|
||||
);
|
||||
(tokenMeta?.users || []).forEach((u) => {
|
||||
const id = u.identity.current?.user_id;
|
||||
if (id != null && !map.has(id)) {
|
||||
map.set(id, { value: id, label: u.label });
|
||||
map.set(id, {
|
||||
value: id,
|
||||
label: u.label,
|
||||
isCurrent: currentUserId != null && id === currentUserId
|
||||
});
|
||||
}
|
||||
});
|
||||
return Array.from(map.values());
|
||||
}, [resourceUsers, tokenMeta]);
|
||||
// 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;
|
||||
});
|
||||
}, [resourceUsers, tokenMeta, currentUserId]);
|
||||
|
||||
// user id → the identity object the token series filters by. Built from the
|
||||
// token meta so the trend's ``users`` filter carries the real identity.
|
||||
|
||||
Reference in New Issue
Block a user