diff --git a/src/pages/usage/apis/resource.ts b/src/pages/usage/apis/resource.ts index f35a3e0d..221afb7d 100644 --- a/src/pages/usage/apis/resource.ts +++ b/src/pages/usage/apis/resource.ts @@ -64,6 +64,10 @@ export interface ResourceBreakdownItem extends ResourceBreakdownSummary { volume_name?: string; user_id?: number; user_name?: string; + // The grouped entity (instance / volume / user) no longer exists. The name + // fields keep the clean (stale) name; the tables show a DeletedTag off this + // flag plus the id, matching the Tokens tab. + deleted?: boolean; // Grouped-trend rows carry the sub-group label (sku / instance / user / …) // alongside ``date`` so the chart can pivot one series per group. group?: string; @@ -277,12 +281,16 @@ function flattenItem( }; if (it.date) flat.date = it.date; const id = it.id ?? undefined; - // Deleted entities get a "(Deleted)" suffix, matching the Token breakdown. + const deleted = !!it.deleted; const rawKey = it.key ?? undefined; - const key = it.deleted && rawKey != null ? `${rawKey} (Deleted)` : rawKey; + // The chart series legend keeps the "(Deleted)" suffix (a legend can't render + // a tag); the tables show a DeletedTag off ``flat.deleted`` + the id and so + // use the clean name. + const key = deleted && rawKey != null ? `${rawKey} (Deleted)` : rawKey; // Generic group label — for a compound (date + dim) trend row the key is the // sub-group value (the switch below targets single-dimension table rows). if (rawKey != null) flat.group = key; + flat.deleted = deleted; switch (groupBy) { case 'resource_type': flat.resource_type = key; @@ -292,15 +300,15 @@ function flattenItem( flat.gpu_type = key; break; case 'instance': - flat.instance_name = key; + flat.instance_name = rawKey; flat.instance_id = id; break; case 'volume': - flat.volume_name = key; + flat.volume_name = rawKey; flat.volume_id = id; break; case 'user': - flat.user_name = key; + flat.user_name = rawKey; flat.user_id = id; break; default: diff --git a/src/pages/usage/components/deleted-tag.tsx b/src/pages/usage/components/deleted-tag.tsx index a5a607e0..10f090a6 100644 --- a/src/pages/usage/components/deleted-tag.tsx +++ b/src/pages/usage/components/deleted-tag.tsx @@ -32,7 +32,7 @@ const DeletedTag: React.FC = ({ id }) => { }} > {label} - {id != null && ( + {!!id && ( ·#{id} diff --git a/src/pages/usage/hooks/use-apikeys-columns.tsx b/src/pages/usage/hooks/use-apikeys-columns.tsx index fb864ee3..a55a8549 100644 --- a/src/pages/usage/hooks/use-apikeys-columns.tsx +++ b/src/pages/usage/hooks/use-apikeys-columns.tsx @@ -1,15 +1,11 @@ // columns.ts import { AutoTooltip } from '@gpustack/core-ui'; import { useIntl } from '@umijs/max'; -import { Tag } from 'antd'; import { useMemo } from 'react'; +import DeletedTag from '../components/deleted-tag'; import { BreakdownItem as ListItem } from '../config/types'; -const useModelsColumns = (): Array<{ - title: string; - dataIndex: string | string[]; - key: string; -}> => { +const useModelsColumns = () => { const intl = useIntl(); return useMemo(() => { @@ -19,23 +15,18 @@ const useModelsColumns = (): Array<{ dataIndex: ['api_key', 'identity', 'value', 'api_key_name'], key: 'api_key_name', render: (text: string, record: ListItem) => ( - + {text}}> - {text} + + {text} + {record.api_key?.deleted && ( - - {intl.formatMessage({ id: 'usage.table.deleted' })} - + )} ) diff --git a/src/pages/usage/hooks/use-models-columns.tsx b/src/pages/usage/hooks/use-models-columns.tsx index 2c063a37..4600744e 100644 --- a/src/pages/usage/hooks/use-models-columns.tsx +++ b/src/pages/usage/hooks/use-models-columns.tsx @@ -2,8 +2,8 @@ import { getGPUStackPlugin } from '@/plugins'; import { AutoTooltip } from '@gpustack/core-ui'; import { useIntl } from '@umijs/max'; -import { Tag } from 'antd'; import { useMemo } from 'react'; +import DeletedTag from '../components/deleted-tag'; import { BreakdownItem as ListItem } from '../config/types'; // Plugin slot: enterprise plugins can contribute extra columns to the @@ -51,27 +51,22 @@ const useModelsColumns = (): ColumnDef[] => { dataIndex: ['route', 'label'], key: 'route_name', render: (text: string, record: ListItem) => ( - + {text}} > - {text} + + {text} + {record.route?.deleted && ( - - {intl.formatMessage({ id: 'usage.table.deleted' })} - + )} ) diff --git a/src/pages/usage/hooks/use-users-columns.tsx b/src/pages/usage/hooks/use-users-columns.tsx index d3bc1ec9..60aad3ba 100644 --- a/src/pages/usage/hooks/use-users-columns.tsx +++ b/src/pages/usage/hooks/use-users-columns.tsx @@ -1,19 +1,15 @@ // columns.ts import { AutoTooltip } from '@gpustack/core-ui'; import { useIntl } from '@umijs/max'; -import { Tag } from 'antd'; import { useMemo } from 'react'; +import DeletedTag from '../components/deleted-tag'; import { BreakdownItem as ListItem } from '../config/types'; interface ColumnsHookProps { sortOrder: string[]; } -const useModelsColumns = (): Array<{ - title: string; - dataIndex: string | string[]; - key: string; -}> => { +const useModelsColumns = () => { const intl = useIntl(); return useMemo(() => { @@ -23,27 +19,22 @@ const useModelsColumns = (): Array<{ dataIndex: ['user', 'identity', 'value', 'user_name'], key: 'user_name', render: (text: string, record: ListItem) => ( - + {text}} > - {text} + + {text} + {record.user?.deleted && ( - - {intl.formatMessage({ id: 'usage.table.deleted' })} - + )} ) diff --git a/src/pages/usage/instances-tab/tables/use-instances-columns.tsx b/src/pages/usage/instances-tab/tables/use-instances-columns.tsx index 3c08b1e8..dc299db5 100644 --- a/src/pages/usage/instances-tab/tables/use-instances-columns.tsx +++ b/src/pages/usage/instances-tab/tables/use-instances-columns.tsx @@ -2,9 +2,11 @@ import { buildInstanceTypeRecordFromMiB, renderInstanceType } from '@/pages/gpu-service/instances/utils/render-instance-type'; +import { AutoTooltip } from '@gpustack/core-ui'; import { useIntl } from '@umijs/max'; import { useMemo } from 'react'; import { ResourceBreakdownItem } from '../../apis/resource'; +import DeletedTag from '../../components/deleted-tag'; import { instanceTypeSeriesLabel } from '../../utils/format-instance-type'; import { parseRollup } from '../../utils/time-buckets'; @@ -108,6 +110,22 @@ const useInstancesColumns = (groupKey: GroupKey) => { key: 'last_active', render: (v?: string) => (v ? parseRollup(v).format('YYYY-MM-DD') : '-') }; + // Name cell with a DeletedTag when the entity no longer exists — mirrors the + // Tokens tab. The id keeps two deleted rows sharing a stale name distinct. + const renderName = (text: string, id?: number, deleted?: boolean) => ( + + + {text || '-'} + + {deleted && } + + ); if (groupKey === 'gpu_type') { return [ instanceTypeColType, @@ -123,9 +141,11 @@ const useInstancesColumns = (groupKey: GroupKey) => { if (groupKey === 'instance') { return [ { - title: intl.formatMessage({ id: 'usage.table.instance' }), + title: intl.formatMessage({ id: 'common.table.name' }), dataIndex: 'instance_name', - key: 'instance_name' + key: 'instance_name', + render: (text: string, row: ResourceBreakdownItem) => + renderName(text, row.instance_id, row.deleted) }, instanceTypeColInstance, ...baseValueCols, @@ -135,9 +155,11 @@ const useInstancesColumns = (groupKey: GroupKey) => { // user tab return [ { - title: intl.formatMessage({ id: 'usage.table.user' }), + title: intl.formatMessage({ id: 'common.table.name' }), dataIndex: 'user_name', - key: 'user_name' + key: 'user_name', + render: (text: string, row: ResourceBreakdownItem) => + renderName(text, row.user_id, row.deleted) }, ...baseValueCols, lastActiveCol diff --git a/src/pages/usage/storage-tab/tables/use-storage-columns.tsx b/src/pages/usage/storage-tab/tables/use-storage-columns.tsx index 5a3f2f5c..14fe1afe 100644 --- a/src/pages/usage/storage-tab/tables/use-storage-columns.tsx +++ b/src/pages/usage/storage-tab/tables/use-storage-columns.tsx @@ -1,6 +1,8 @@ +import { AutoTooltip } from '@gpustack/core-ui'; import { useIntl } from '@umijs/max'; import { useMemo } from 'react'; import { ResourceBreakdownItem } from '../../apis/resource'; +import DeletedTag from '../../components/deleted-tag'; import { parseRollup } from '../../utils/time-buckets'; type GroupKey = 'volume' | 'user'; @@ -39,12 +41,30 @@ const useStorageColumns = (groupKey: GroupKey) => { key: 'last_active', render: (v?: string) => (v ? parseRollup(v).format('YYYY-MM-DD') : '-') }; + // Name cell with a DeletedTag when the entity no longer exists — mirrors the + // Tokens tab. The id keeps two deleted rows sharing a stale name distinct. + const renderName = (text: string, id?: number, deleted?: boolean) => ( + + + {text || '-'} + + {deleted && } + + ); if (groupKey === 'volume') { return [ { - title: intl.formatMessage({ id: 'usage.tabs.storage' }), + title: intl.formatMessage({ id: 'common.table.name' }), dataIndex: 'volume_name', - key: 'volume_name' + key: 'volume_name', + render: (text: string, row: ResourceBreakdownItem) => + renderName(text, row.volume_id, row.deleted) }, { title: intl.formatMessage({ id: 'usage.table.type' }), @@ -65,9 +85,11 @@ const useStorageColumns = (groupKey: GroupKey) => { } return [ { - title: intl.formatMessage({ id: 'usage.table.user' }), + title: intl.formatMessage({ id: 'common.table.name' }), dataIndex: 'user_name', - key: 'user_name' + key: 'user_name', + render: (text: string, row: ResourceBreakdownItem) => + renderName(text, row.user_id, row.deleted) }, ...valueCols, {