style(usage): unify name column header + DeletedTag across breakdown tables

This commit is contained in:
jialin
2026-07-09 15:17:53 +08:00
committed by jialin
parent 7d9f68da2d
commit e79d49f2a8
7 changed files with 98 additions and 69 deletions
+13 -5
View File
@@ -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:
+1 -1
View File
@@ -32,7 +32,7 @@ const DeletedTag: React.FC<DeletedTagProps> = ({ id }) => {
}}
>
{label}
{id != null && (
{!!id && (
<span className="text-tertiary">
<span style={{ margin: '0 2px' }}>·</span>#{id}
</span>
+11 -20
View File
@@ -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) => (
<span className="flex items-center">
<span className="flex items-center gap-8">
<AutoTooltip ghost title={<span>{text}</span>}>
<span className="text-primary">{text}</span>
<span
className={
record.api_key?.deleted ? 'text-tertiary' : 'text-primary'
}
>
{text}
</span>
</AutoTooltip>
{record.api_key?.deleted && (
<Tag
style={{
marginLeft: 8,
borderRadius: 12,
color: 'var(--ant-color-text-tertiary)',
borderColor: 'var(--ant-color-split)',
backgroundColor: 'transparent'
}}
variant="outlined"
>
{intl.formatMessage({ id: 'usage.table.deleted' })}
</Tag>
<DeletedTag id={record.api_key?.identity?.current?.api_key_id} />
)}
</span>
)
+10 -15
View File
@@ -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) => (
<span className="flex items-center">
<span className="flex items-center gap-8">
<AutoTooltip
ghost
style={{ maxWidth: 400 }}
title={<span>{text}</span>}
>
<span className="text-primary">{text}</span>
<span
className={
record.route?.deleted ? 'text-tertiary' : 'text-primary'
}
>
{text}
</span>
</AutoTooltip>
{record.route?.deleted && (
<Tag
style={{
marginLeft: 8,
borderRadius: 12,
color: 'var(--ant-color-text-tertiary)',
borderColor: 'var(--ant-color-split)',
backgroundColor: 'transparent'
}}
variant="outlined"
>
{intl.formatMessage({ id: 'usage.table.deleted' })}
</Tag>
<DeletedTag id={record.route?.identity?.current?.route_id} />
)}
</span>
)
+11 -20
View File
@@ -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) => (
<span className="flex items-center">
<span className="flex items-center gap-8">
<AutoTooltip
ghost
style={{ maxWidth: 400 }}
title={<span>{text}</span>}
>
<span className="text-primary">{text}</span>
<span
className={
record.user?.deleted ? 'text-tertiary' : 'text-primary'
}
>
{text}
</span>
</AutoTooltip>
{record.user?.deleted && (
<Tag
style={{
marginLeft: 8,
borderRadius: 12,
color: 'var(--ant-color-text-tertiary)',
borderColor: 'var(--ant-color-split)',
backgroundColor: 'transparent'
}}
variant="outlined"
>
{intl.formatMessage({ id: 'usage.table.deleted' })}
</Tag>
<DeletedTag id={record.user?.identity?.current?.user_id} />
)}
</span>
)
@@ -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) => (
<span className="flex items-center gap-8">
<AutoTooltip
ghost
style={{
maxWidth: 400,
...(deleted ? { color: 'var(--ant-color-text-tertiary)' } : null)
}}
>
{text || '-'}
</AutoTooltip>
{deleted && <DeletedTag id={id ?? null} />}
</span>
);
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
@@ -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) => (
<span className="flex items-center gap-8">
<AutoTooltip
ghost
style={{
maxWidth: 400,
...(deleted ? { color: 'var(--ant-color-text-tertiary)' } : null)
}}
>
{text || '-'}
</AutoTooltip>
{deleted && <DeletedTag id={id ?? null} />}
</span>
);
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,
{