Files
gpustack-ui/src/pages/usage/index.tsx
T
micheliaandjialin 62f34ffcaf feat(usage): resource usage metering page
Add the Usage page with Summary / Tokens / GPU Instances / Storage / Resource
Events tabs over the new metering endpoints: per-resource breakdowns with
date / scope / user / resource filters, trend charts, server-side sortable
tables (GPU-Hours, Instance-Hours, GB-Days, GB-Hours), Excel export with an
in-dialog preview, and KPI cards with help tooltips explaining each metric.

MaaS-only users (no Kubernetes cluster and no resource events) get a
tokens-only view with the tab bar dropped; GPU Service / the full page unlock
for admins, cluster owners, or anyone who has run a resource. Instance-type
rows reuse the GPU Instances list styling, and deleted users / instances /
volumes are flagged in breakdowns and filters.
2026-06-04 19:52:52 +08:00

82 lines
2.4 KiB
TypeScript

/**
* Usage page — top-level tab shell.
*
* - Summary: cross-resource overview (KPIs + trend + by-type breakdown + donut)
* - Tokens: existing per-request token usage page (untouched)
* - GPU Instances: per-instance compute usage
* - Storage: PV capacity usage
* - Resource Events: lifecycle event list (created / metered / deleted / ...)
*
* Resource Events is kept as a tab (not a separate route) so the page filter
* set stays consistent across all views.
*
* The previous implementation lived in this file; it now lives in
* ``components/token-tab.tsx`` so we can host it as a tab pane.
*/
import { useAccess } from '@umijs/max';
import { Tabs, TabsProps } from 'antd';
import React, { useMemo, useState } from 'react';
import GpuInstancesTab from './components/gpu-instances-tab';
import ResourceEvents from './components/resource-events';
import StorageTab from './components/storage-tab';
import SummaryTab from './components/summary-tab';
import TokenTab from './components/token-tab';
const Usage: React.FC = () => {
const access = useAccess();
// Land on the cross-resource Summary by default.
const [activeKey, setActiveKey] = useState<string>('summary');
const items: TabsProps['items'] = useMemo(
() => [
{
key: 'summary',
label: 'Summary',
children: <SummaryTab />
},
{
key: 'tokens',
label: 'Tokens',
children: <TokenTab />
},
{
key: 'gpu-instances',
label: 'GPU Instances',
children: <GpuInstancesTab />
},
{
key: 'storage',
label: 'Storage',
children: <StorageTab />
},
{
key: 'resource-events',
label: 'Resource Events',
children: <ResourceEvents />
}
],
[]
);
// Users who can't see GPU Service (MaaS-only: no cluster, no resource usage)
// only have token usage — drop the tab shell and show Tokens directly.
if (!access.canSeeGpuService) {
return <TokenTab />;
}
return (
<Tabs
activeKey={activeKey}
onChange={setActiveKey}
items={items}
destroyOnHidden
// The content area adds 24px top padding; on table pages that space is
// filled immediately, but here it sits empty above the tab bar and reads
// as too tall. Pull the tab bar up to ~flush with the header divider.
tabBarStyle={{ marginTop: -20 }}
/>
);
};
export default Usage;