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.
82 lines
2.4 KiB
TypeScript
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;
|