Files
gpustack-ui/src/pages/usage/instances-tab/tables/use-instances-columns.tsx
T
2026-06-17 20:11:06 +08:00

135 lines
4.6 KiB
TypeScript

import {
buildInstanceTypeRecordFromMiB,
renderInstanceType
} from '@/pages/gpu-service/instances/utils/render-instance-type';
import { useIntl } from '@umijs/max';
import { useMemo } from 'react';
import { ResourceBreakdownItem } from '../../apis/resource';
import { instanceTypeLabel } from '../../utils/format-instance-type';
import { parseRollup } from '../../utils/time-buckets';
type GroupKey = 'gpu_type' | 'instance' | 'user';
/**
* Column factory for the GPU-instances breakdown tables, shared by the in-tab
* table and the export preview. Columns adapt to the active grouping. Sort
* indicators are uncontrolled (antd manages the header arrows); the table
* reports changes through its `onChange`.
*/
const useInstancesColumns = (groupKey: GroupKey) => {
const intl = useIntl();
return useMemo(() => {
const baseValueCols = [
{
title: intl.formatMessage({ id: 'usage.metric.gpuHours' }),
dataIndex: 'gpu_hours',
key: 'gpu_hours',
sorter: true,
render: (v: number) => (v ?? 0).toFixed(2)
},
{
title: intl.formatMessage({ id: 'usage.metric.instanceHours' }),
dataIndex: 'instance_hours',
key: 'instance_hours',
sorter: true,
render: (v: number) => (v ?? 0).toFixed(2)
}
];
// Instance Types breakdown: the pretty product name (or flavor slug for
// older rows; "CPU Only" when no GPU cards) plus a CPU + RAM spec popover —
// rendered through the canonical renderer so the formatting matches the GPU
// Instances list, but limited to the CPU/RAM categories.
const instanceTypeColType = {
title: intl.formatMessage({ id: 'usage.table.instanceType' }),
dataIndex: 'gpu_type',
key: 'gpu_type',
render: (_v: string, row: ResourceBreakdownItem) =>
renderInstanceType(
buildInstanceTypeRecordFromMiB({
name: row.instance_name,
product: row.product || row.gpu_type,
gpuCount: row.gpu_count,
unitCpuMilli: row.unit_cpu_milli,
unitMemoryMib: row.unit_memory_mib,
vramMib: row.vram_mib
}),
{
intl,
categories: ['cpu', 'ram'],
title:
(row.gpu_count ?? 0) > 0 ? instanceTypeLabel(row) : 'CPU Only'
}
)
};
// Instances breakdown: render through the canonical GPU Instances list
// renderer so the label + spec popover are identical. The breakdown row
// carries flat MiB fields, so adapt it into the ListItem shape first.
const instanceTypeColInstance = {
title: intl.formatMessage({ id: 'usage.table.instanceType' }),
dataIndex: 'gpu_type',
key: 'gpu_type',
render: (_v: string, row: ResourceBreakdownItem) =>
renderInstanceType(
buildInstanceTypeRecordFromMiB({
name: row.instance_name,
product: row.product || row.gpu_type,
gpuCount: row.gpu_count,
unitCpuMilli: row.unit_cpu_milli,
unitMemoryMib: row.unit_memory_mib,
vramMib: row.vram_mib,
localStorageMib: row.local_storage_mib,
ephemeralMib: row.ephemeral_mib,
persistentMib: row.persistent_mib
}),
{ intl }
)
};
// Last Active = the last active day. The backend sends a rollup-tz instant
// with its offset; parseRollup keeps that wall clock (no browser-tz convert),
// consistent with the trend chart buckets. Shown date-only.
const lastActiveCol = {
title: intl.formatMessage({ id: 'usage.table.lastActive' }),
dataIndex: 'last_active',
key: 'last_active',
render: (v?: string) => (v ? parseRollup(v).format('YYYY-MM-DD') : '-')
};
if (groupKey === 'gpu_type') {
return [
instanceTypeColType,
...baseValueCols,
{
title: intl.formatMessage({ id: 'usage.metric.activeInstances' }),
dataIndex: 'active_instances',
key: 'active_instances'
},
lastActiveCol
];
}
if (groupKey === 'instance') {
return [
{
title: intl.formatMessage({ id: 'usage.table.instance' }),
dataIndex: 'instance_name',
key: 'instance_name'
},
instanceTypeColInstance,
...baseValueCols,
lastActiveCol
];
}
// user tab
return [
{
title: intl.formatMessage({ id: 'usage.table.user' }),
dataIndex: 'user_name',
key: 'user_name'
},
...baseValueCols,
lastActiveCol
];
}, [groupKey, intl]);
};
export default useInstancesColumns;