fix: deleted tag

This commit is contained in:
jialin
2026-04-22 13:16:48 +08:00
committed by jialin
parent 5f3b9b88ff
commit d48aa99dcc
20 changed files with 248 additions and 130 deletions
+15 -4
View File
@@ -41,24 +41,35 @@ export default function useChartConfig() {
borderColor: 'transparent', borderColor: 'transparent',
formatter(params: any, callback?: (val: any) => any) { formatter(params: any, callback?: (val: any) => any) {
let result = `<span class="tooltip-x-name">${params[0].axisValue}</span>`; let result = `<span class="tooltip-x-name">${params[0].axisValue}</span>`;
let visibleItemCount = 0;
params.forEach((item: any) => { params.forEach((item: any) => {
let value = isFunction(callback) let value = isFunction(callback)
? callback?.(item.data.value) ? callback?.(item.data.value)
: item.data.value; : item.data?.value;
if (value === null || value === undefined) {
return;
}
visibleItemCount += 1;
const borderRadius = item.seriesType === 'bar' ? '2px' : '8px'; const borderRadius = item.seriesType === 'bar' ? '2px' : '8px';
result += `<span class="tooltip-item"> result += `<span class="tooltip-item">
<span class="tooltip-item-name"> <span class="tooltip-item-name">
<span style="display:inline-block;margin-right:5px;border-radius:${borderRadius};width:8px;height:8px;background-color:${item.color};"></span> <span class="tooltip-item-dot" style="border-radius:${borderRadius};background-color:${item.color};"></span>
<span class="tooltip-title">${item.seriesName}</span>: <span class="tooltip-item-title">${item.seriesName}</span>:
</span> </span>
<span class="tooltip-value">${value}</span> <span class="tooltip-value">${value}</span>
</span>`; </span>`;
}); });
return `<div class="tooltip-wrapper">${result}</div>`; const wrapperClassName =
visibleItemCount >= 12
? 'tooltip-wrapper tooltip-grid'
: 'tooltip-wrapper';
return `<div class="${wrapperClassName}">${result}</div>`;
} }
}; };
+33 -1
View File
@@ -744,7 +744,7 @@ body {
font-size: 11px; font-size: 11px;
background-color: transparent; background-color: transparent;
min-width: 100px; min-width: 100px;
max-width: 360px; max-width: 500px;
&.scatter { &.scatter {
display: grid; display: grid;
@@ -752,6 +752,16 @@ body {
gap: 8px; gap: 8px;
} }
&.tooltip-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 4px 12px;
.tooltip-x-name {
grid-column: 1 / -1;
}
}
.tooltip-x-name { .tooltip-x-name {
font-size: var(--font-size-small); font-size: var(--font-size-small);
color: var(--ant-color-text-tertiary); color: var(--ant-color-text-tertiary);
@@ -766,12 +776,34 @@ body {
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
.tooltip-item-name {
display: inline-flex;
align-items: center;
min-width: 0;
flex: 1;
overflow: hidden;
}
.tooltip-item-dot {
display: inline-block;
width: 8px;
height: 8px;
margin-right: 5px;
flex-shrink: 0;
}
.tooltip-item-title { .tooltip-item-title {
margin-right: 2px; margin-right: 2px;
display: inline-block;
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
.tooltip-value { .tooltip-value {
margin-left: 10px; margin-left: 10px;
flex-shrink: 0;
color: var(--ant-color-text); color: var(--ant-color-text);
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
+2 -1
View File
@@ -27,5 +27,6 @@ export default {
'usage.export.table': 'Export Table Data', 'usage.export.table': 'Export Table Data',
'usage.user.currentAccount': 'Current Account', 'usage.user.currentAccount': 'Current Account',
'usage.table.cluster': 'Cluster', 'usage.table.cluster': 'Cluster',
'usage.table.provider': 'Provider' 'usage.table.provider': 'Provider',
'usage.table.deleted': 'Deleted'
}; };
+2 -1
View File
@@ -27,5 +27,6 @@ export default {
'usage.export.table': 'Export Table Data', 'usage.export.table': 'Export Table Data',
'usage.user.currentAccount': 'Current Account', 'usage.user.currentAccount': 'Current Account',
'usage.table.cluster': 'Cluster', 'usage.table.cluster': 'Cluster',
'usage.table.provider': 'Provider' 'usage.table.provider': 'Provider',
'usage.table.deleted': 'Deleted'
}; };
+2 -1
View File
@@ -27,5 +27,6 @@ export default {
'usage.export.table': 'Export Table Data', 'usage.export.table': 'Export Table Data',
'usage.user.currentAccount': 'Current Account', 'usage.user.currentAccount': 'Current Account',
'usage.table.cluster': 'Cluster', 'usage.table.cluster': 'Cluster',
'usage.table.provider': 'Provider' 'usage.table.provider': 'Provider',
'usage.table.deleted': 'Deleted'
}; };
+2 -1
View File
@@ -27,5 +27,6 @@ export default {
'usage.export.table': 'Export Table Data', 'usage.export.table': 'Export Table Data',
'usage.user.currentAccount': 'Current Account', 'usage.user.currentAccount': 'Current Account',
'usage.table.cluster': 'Cluster', 'usage.table.cluster': 'Cluster',
'usage.table.provider': 'Provider' 'usage.table.provider': 'Provider',
'usage.table.deleted': 'Deleted'
}; };
+2 -1
View File
@@ -27,5 +27,6 @@ export default {
'usage.export.table': '导出表格数据', 'usage.export.table': '导出表格数据',
'usage.user.currentAccount': '当前账户', 'usage.user.currentAccount': '当前账户',
'usage.table.cluster': '集群', 'usage.table.cluster': '集群',
'usage.table.provider': '提供商' 'usage.table.provider': '提供商',
'usage.table.deleted': '已删除'
}; };
@@ -97,6 +97,8 @@ const RequestTokenInner: React.FC<RequestTokenInnerProps> = (props) => {
})); }));
}, [requestData, tokenData]); }, [requestData, tokenData]);
console.log('xAxisData', xAxisData, tokenData);
return ( return (
<CardWrapperBox> <CardWrapperBox>
<CardWrapper style={{ width: '100%', position: 'relative' }}> <CardWrapper style={{ width: '100%', position: 'relative' }}>
+39 -14
View File
@@ -28,6 +28,20 @@ const ControlLabel = styled.span`
margin-right: 8px; margin-right: 8px;
`; `;
function getColorForIndex(index: number) {
const hue = (index * 137.5) % 360;
return `oklch(70% 0.12 ${hue})`;
}
const colors = [
baseColorMap.base,
baseColorMap.baseR3,
baseColorMap.baseL1,
baseColorMap.baseR1,
baseColorMap.baseR2,
baseColorMap.baseL2
];
interface DailyUsageProps { interface DailyUsageProps {
timeSeriesData: TimeSeriesData | null; timeSeriesData: TimeSeriesData | null;
metric: string; metric: string;
@@ -60,22 +74,33 @@ const DailyUsage: React.FC<DailyUsageProps> = (props) => {
} }
const series = timeSeriesData.series; const series = timeSeriesData.series;
const xAxis = series[0]?.timeline?.map((item) => item.date) || []; const dateSet = new Set<string>();
const colors = [ for (const item of series) {
baseColorMap.base, for (const point of item.timeline) {
baseColorMap.baseR3, dateSet.add(point.date);
baseColorMap.baseL1, }
baseColorMap.baseR1, }
baseColorMap.baseR2,
baseColorMap.baseL2
];
const chartSeries = series.map((item, index) => ({ // sort dates in ascending order
name: item.label, const xAxis = Array.from(dateSet).sort(
data: item.timeline.map((t) => ({ time: t.date, value: t.value })), (a, b) => dayjs(a).valueOf() - dayjs(b).valueOf()
color: colors[index % colors.length] );
}));
const chartSeries = series.map((item, index) => {
const timelineMap = new Map(
item.timeline.map((point) => [
point.date,
{ time: point.date, value: point.value }
])
);
return {
name: item.label,
data: xAxis.map((date) => timelineMap.get(date) ?? null),
color: colors[index % colors.length]
};
});
// API requests use line chart, tokens use bar chart // API requests use line chart, tokens use bar chart
return { return {
+7 -4
View File
@@ -84,7 +84,8 @@ const ExportData: React.FC<{
}) => { }) => {
fetchExportData({ fetchExportData({
...pageParams, ...pageParams,
granularity: nextChartFilters.granularity, granularity: 'day',
sort_by: '-date',
group_by: ['date', 'user', 'model', 'api_key'], group_by: ['date', 'user', 'model', 'api_key'],
filters: nextFilters, filters: nextFilters,
scope: initialScope, scope: initialScope,
@@ -221,9 +222,10 @@ const ExportData: React.FC<{
...pageParams, ...pageParams,
page, page,
perPage: pageSize, perPage: pageSize,
granularity, granularity: 'day',
group_by: ['date', 'user', 'model', 'api_key'], group_by: ['date', 'user', 'model', 'api_key'],
filters, filters,
sort_by: '-date',
scope: initialScope, scope: initialScope,
start_date: commonFilters.start_date, start_date: commonFilters.start_date,
end_date: commonFilters.end_date end_date: commonFilters.end_date
@@ -239,9 +241,10 @@ const ExportData: React.FC<{
setPageParams(INITIAL_PAGE_PARAMS); setPageParams(INITIAL_PAGE_PARAMS);
fetchExportData({ fetchExportData({
...INITIAL_PAGE_PARAMS, ...INITIAL_PAGE_PARAMS,
granularity, granularity: 'day',
group_by: ['date', 'user', 'model', 'api_key'], group_by: ['date', 'user', 'model', 'api_key'],
filters, filters,
sort_by: '-date',
scope: initialScope, scope: initialScope,
start_date: commonFilters.start_date, start_date: commonFilters.start_date,
end_date: commonFilters.end_date end_date: commonFilters.end_date
@@ -263,7 +266,7 @@ const ExportData: React.FC<{
closable: false closable: false
}} }}
keyboard={false} keyboard={false}
width={1200} width={1280}
style={{ style={{
top: '10%' top: '10%'
}} }}
+54 -56
View File
@@ -7,7 +7,7 @@ import ProviderLogo from '@/pages/maas-provider/components/provider-logo';
import { useModel } from '@@/plugin-model'; import { useModel } from '@@/plugin-model';
import { DownloadOutlined, SyncOutlined } from '@ant-design/icons'; import { DownloadOutlined, SyncOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Button, DatePicker, Dropdown, MenuProps, Segmented } from 'antd'; import { Button, DatePicker, Dropdown, MenuProps } from 'antd';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import React from 'react'; import React from 'react';
import { GroupOption } from '../config'; import { GroupOption } from '../config';
@@ -120,8 +120,6 @@ const FilterBar: React.FC<FilterBarProps> = (props) => {
} }
] ]
}); });
// const [activeModels, setActiveModels] = React.useState<valueType[][]>([]);
// const [activeApiKeys, setActiveApiKeys] = React.useState<valueType[][]>([]);
const initialInfo = useModel('@@initialState'); const initialInfo = useModel('@@initialState');
const { initialState } = initialInfo || {}; const { initialState } = initialInfo || {};
@@ -149,8 +147,15 @@ const FilterBar: React.FC<FilterBarProps> = (props) => {
} }
]; ];
const renderTag = (tag: string) => {
return (
<span className="text-tertiary" style={{ fontSize: 12, marginRight: 4 }}>
[{tag}]
</span>
);
};
const handleOnModelsChange = (value: valueType[][], selectedOptions: any) => { const handleOnModelsChange = (value: valueType[][], selectedOptions: any) => {
// setActiveModels(value);
const selectedValues: string[] = value.map((item) => { const selectedValues: string[] = value.map((item) => {
if (Array.isArray(item)) { if (Array.isArray(item)) {
return item[item.length - 1] as string; // Get the last value in the array return item[item.length - 1] as string; // Get the last value in the array
@@ -165,7 +170,6 @@ const FilterBar: React.FC<FilterBarProps> = (props) => {
value: valueType[][], value: valueType[][],
selectedOptions: any selectedOptions: any
) => { ) => {
// setActiveApiKeys(value);
const selectedValues: string[] = value.map((item) => { const selectedValues: string[] = value.map((item) => {
if (Array.isArray(item)) { if (Array.isArray(item)) {
return item[item.length - 1] as string; // Get the last value in the array return item[item.length - 1] as string; // Get the last value in the array
@@ -194,10 +198,14 @@ const FilterBar: React.FC<FilterBarProps> = (props) => {
const optionRender = (option: any) => { const optionRender = (option: any) => {
const { data } = option; const { data } = option;
console.log('optionRender', option);
if (!data.isParent) { if (!data.isParent) {
return <AutoTooltip ghost>{data.label}</AutoTooltip>; return (
<span className="flex-center gap-4">
<AutoTooltip ghost>{data.label}</AutoTooltip>
{data.deleted &&
renderTag(intl.formatMessage({ id: 'usage.table.deleted' }))}
</span>
);
} }
if (data.type === 'deployments') { if (data.type === 'deployments') {
@@ -221,6 +229,36 @@ const FilterBar: React.FC<FilterBarProps> = (props) => {
); );
}; };
const apiKeyOptionRender = (option: any) => {
const { data } = option;
if (!data.isParent) {
return (
<span className="flex-center gap-4">
<AutoTooltip ghost>{data.label}</AutoTooltip>
{data.deleted &&
renderTag(intl.formatMessage({ id: 'usage.table.deleted' }))}
</span>
);
}
return (
<AutoTooltip ghost>
<span>{data.label}</span>
</AutoTooltip>
);
};
const singleOptionRender = (option: any) => {
const { data } = option;
return (
<span className="flex-center gap-4">
<AutoTooltip ghost>{data.label}</AutoTooltip>
{data.deleted &&
renderTag(intl.formatMessage({ id: 'usage.table.deleted' }))}
</span>
);
};
const onPickerChange = (picker: DateType) => { const onPickerChange = (picker: DateType) => {
handleOnPickerChange(picker); handleOnPickerChange(picker);
handlePickerChange(picker); handlePickerChange(picker);
@@ -231,57 +269,15 @@ const FilterBar: React.FC<FilterBarProps> = (props) => {
? normalizeRangeValue(startDate, endDate, picker) ? normalizeRangeValue(startDate, endDate, picker)
: [dayjs().add(-DefaultDateConfig.defaultRange, 'd'), dayjs()]; : [dayjs().add(-DefaultDateConfig.defaultRange, 'd'), dayjs()];
const renderFooter = () => {
return (
<Segmented
size="middle"
shape="round"
value={picker}
styles={{
root: {
width: '100%',
display: 'flex',
marginBlock: 8
},
item: {
flex: 1
},
label: {
display: 'flex',
justifyContent: 'center',
flex: 1
}
}}
onChange={onPickerChange}
options={[
{
label: 'Day',
value: 'date'
},
{
label: 'Month',
value: 'month'
},
{
label: 'Week',
value: 'week'
}
]}
></Segmented>
);
};
const userOptionRender = (option: any) => { const userOptionRender = (option: any) => {
const { data } = option; const { data } = option;
return ( return (
<span className="flex-center gap-8"> <span className="flex-center gap-4">
<span>{data.label}</span> <AutoTooltip ghost>{data.label}</AutoTooltip>
{data.isCurrent && ( {data.isCurrent &&
<span className="text-tertiary"> renderTag(intl.formatMessage({ id: 'usage.user.currentAccount' }))}
{' '} {data.deleted &&
[{intl.formatMessage({ id: 'usage.user.currentAccount' })}] renderTag(intl.formatMessage({ id: 'usage.table.deleted' }))}
</span>
)}
</span> </span>
); );
}; };
@@ -394,6 +390,7 @@ const FilterBar: React.FC<FilterBarProps> = (props) => {
options={apiKeyOptions} options={apiKeyOptions}
showCheckedStrategy="SHOW_CHILD" showCheckedStrategy="SHOW_CHILD"
value={activeApiKeys} value={activeApiKeys}
optionNode={apiKeyOptionRender}
onChange={handleOnApiKeysChange} onChange={handleOnApiKeysChange}
getPopupContainer={(triggerNode) => triggerNode.parentNode} getPopupContainer={(triggerNode) => triggerNode.parentNode}
></SealCascader> ></SealCascader>
@@ -412,6 +409,7 @@ const FilterBar: React.FC<FilterBarProps> = (props) => {
wrapper: { flex: 1, maxWidth: 240, minWidth: 100 } wrapper: { flex: 1, maxWidth: 240, minWidth: 100 }
}} }}
value={selectedApiKeys} value={selectedApiKeys}
optionRender={singleOptionRender}
onChange={onApiKeysChange} onChange={onApiKeysChange}
/> />
)} )}
+4 -1
View File
@@ -45,7 +45,7 @@ export interface TimeSeriesData {
series: TimeSeriesItem[]; series: TimeSeriesItem[];
} }
export type BreakdownItem = UsageFilterItem & { export type BreakdownItem = {
cluster_name: string; cluster_name: string;
model_name: string; model_name: string;
user_name: string; user_name: string;
@@ -60,6 +60,9 @@ export type BreakdownItem = UsageFilterItem & {
last_active: string; last_active: string;
provider_type: string; provider_type: string;
provider_name: string; provider_name: string;
user: UsageFilterItem;
model: UsageFilterItem;
api_key: UsageFilterItem;
}; };
export interface UsageMeta { export interface UsageMeta {
+16 -1
View File
@@ -1,6 +1,7 @@
// columns.ts // columns.ts
import AutoTooltip from '@/components/auto-tooltip'; import AutoTooltip from '@/components/auto-tooltip';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Tag } from 'antd';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { BreakdownItem as ListItem } from '../config/types'; import { BreakdownItem as ListItem } from '../config/types';
@@ -19,9 +20,23 @@ const useModelsColumns = (): Array<{
key: 'api_key_name', key: 'api_key_name',
render: (text: string, record: ListItem) => ( render: (text: string, record: ListItem) => (
<span className="flex items-center"> <span className="flex items-center">
<AutoTooltip ghost style={{ maxWidth: 400 }}> <AutoTooltip ghost title={<span>{text}</span>}>
<span className="text-primary">{text}</span> <span className="text-primary">{text}</span>
</AutoTooltip> </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>
)}
</span> </span>
) )
}, },
+24 -6
View File
@@ -2,6 +2,7 @@
import AutoTooltip from '@/components/auto-tooltip'; import AutoTooltip from '@/components/auto-tooltip';
import ProviderLogo from '@/pages/maas-provider/components/provider-logo'; import ProviderLogo from '@/pages/maas-provider/components/provider-logo';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Tag } from 'antd';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { BreakdownItem as ListItem } from '../config/types'; import { BreakdownItem as ListItem } from '../config/types';
@@ -24,9 +25,27 @@ const useModelsColumns = (): Array<{
key: 'model_name', key: 'model_name',
render: (text: string, record: ListItem) => ( render: (text: string, record: ListItem) => (
<span className="flex items-center"> <span className="flex items-center">
<AutoTooltip ghost style={{ maxWidth: 400 }}> <AutoTooltip
ghost
style={{ maxWidth: 400 }}
title={<span>{text}</span>}
>
<span className="text-primary">{text}</span> <span className="text-primary">{text}</span>
</AutoTooltip> </AutoTooltip>
{record.model?.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>
)}
</span> </span>
) )
}, },
@@ -40,15 +59,14 @@ const useModelsColumns = (): Array<{
}, },
{ {
title: intl.formatMessage({ id: 'usage.table.provider' }), title: intl.formatMessage({ id: 'usage.table.provider' }),
dataIndex: 'provider_name', dataIndex: ['model', 'identity', 'value', 'provider_type'],
key: 'provider_name', key: 'provider_type',
render: (text: string, record: ListItem) => ( render: (text: string, record: ListItem) => (
<span className="flex-center gap-8"> <span className="flex-center gap-8">
<ProviderLogo provider={record.provider_type || 'deployments'} /> <ProviderLogo provider={text || 'deployments'} />
<AutoTooltip ghost style={{ maxWidth: 400 }}> <AutoTooltip ghost style={{ maxWidth: 400 }}>
{text || {text ||
(!record.provider_type && (!text && intl.formatMessage({ id: 'menu.models.deployment' }))}
intl.formatMessage({ id: 'menu.models.deployment' }))}
</AutoTooltip> </AutoTooltip>
</span> </span>
) )
+20 -1
View File
@@ -1,6 +1,7 @@
// columns.ts // columns.ts
import AutoTooltip from '@/components/auto-tooltip'; import AutoTooltip from '@/components/auto-tooltip';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Tag } from 'antd';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { BreakdownItem as ListItem } from '../config/types'; import { BreakdownItem as ListItem } from '../config/types';
@@ -23,9 +24,27 @@ const useModelsColumns = (): Array<{
key: 'user_name', key: 'user_name',
render: (text: string, record: ListItem) => ( render: (text: string, record: ListItem) => (
<span className="flex items-center"> <span className="flex items-center">
<AutoTooltip ghost style={{ maxWidth: 400 }}> <AutoTooltip
ghost
style={{ maxWidth: 400 }}
title={<span>{text}</span>}
>
<span className="text-primary">{text}</span> <span className="text-primary">{text}</span>
</AutoTooltip> </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>
)}
</span> </span>
) )
}, },
@@ -32,8 +32,8 @@ export default function useQueryUsageMetaData() {
if (!currentUserId) return users; if (!currentUserId) return users;
const sortedUsers = [...users].sort((a, b) => { const sortedUsers = [...users].sort((a, b) => {
if (a.identity.current.user_id === currentUserId) return -1; if (a.identity.current?.user_id === currentUserId) return -1;
if (b.identity.current.user_id === currentUserId) return 1; if (b.identity.current?.user_id === currentUserId) return 1;
return 0; return 0;
}); });
@@ -58,7 +58,7 @@ export default function useQueryUsageMetaData() {
sortedUsers.map((item) => ({ sortedUsers.map((item) => ({
value: item.label, value: item.label,
isCurrent: isCurrent:
item.identity.current.user_id === initialState?.currentUser?.id, item.identity.current?.user_id === initialState?.currentUser?.id,
...item ...item
})) || [], })) || [],
api_keys: groupToOptions(res?.filters?.api_keys || [], { api_keys: groupToOptions(res?.filters?.api_keys || [], {
+2 -2
View File
@@ -30,7 +30,7 @@ const APIKeys: React.FC<{
}>({ }>({
page: 1, page: 1,
perPage: 10, perPage: 10,
sort_by: '' sort_by: '-total_tokens'
}); });
const pendingPageResetRef = useRef(false); const pendingPageResetRef = useRef(false);
@@ -113,7 +113,7 @@ const APIKeys: React.FC<{
<Table <Table
columns={columns} columns={columns}
dataSource={dataSource.dataList} dataSource={dataSource.dataList}
rowKey={getBreakdownRowKey} rowKey={(record) => getBreakdownRowKey(record, 'api_keys')}
loading={{ loading={{
spinning: loading, spinning: loading,
size: 'middle' size: 'middle'
+2 -2
View File
@@ -30,7 +30,7 @@ const Models: React.FC<{
}>({ }>({
page: 1, page: 1,
perPage: 10, perPage: 10,
sort_by: '' sort_by: '-total_tokens'
}); });
const pendingPageResetRef = useRef(false); const pendingPageResetRef = useRef(false);
@@ -115,7 +115,7 @@ const Models: React.FC<{
<Table <Table
columns={columns} columns={columns}
dataSource={dataSource.dataList} dataSource={dataSource.dataList}
rowKey={getBreakdownRowKey} rowKey={(record) => getBreakdownRowKey(record, 'models')}
loading={{ loading={{
spinning: loading, spinning: loading,
size: 'middle' size: 'middle'
+2 -2
View File
@@ -30,7 +30,7 @@ const Users: React.FC<{
}>({ }>({
page: 1, page: 1,
perPage: 10, perPage: 10,
sort_by: '' sort_by: '-total_tokens'
}); });
const pendingPageResetRef = useRef(false); const pendingPageResetRef = useRef(false);
@@ -116,7 +116,7 @@ const Users: React.FC<{
<Table <Table
columns={columns} columns={columns}
dataSource={dataSource.dataList} dataSource={dataSource.dataList}
rowKey={getBreakdownRowKey} rowKey={(record) => getBreakdownRowKey(record, 'users')}
loading={{ loading={{
spinning: loading, spinning: loading,
size: 'middle' size: 'middle'
+15 -28
View File
@@ -1,35 +1,22 @@
import { BreakdownItem } from '../config/types'; import { BreakdownItem } from '../config/types';
export const getBreakdownRowKey = (record: BreakdownItem): string => { export const getBreakdownRowKey = (
const identity = record?.identity; record: BreakdownItem,
const current = identity?.current; type: string
const value = identity?.value; ): string => {
if (type === 'models') {
return `${record.model.label}`;
}
const key = [ if (type === 'users') {
current?.model_id, return `${record.user.label}`;
current?.user_id, }
current?.api_key_id,
value?.model_name,
value?.user_name,
value?.api_key_name,
value?.cluster_name,
value?.provider_name,
value?.provider_type,
record?.label,
record?.cluster_name,
record?.model_name,
record?.user_name,
record?.api_key_name,
record?.last_active,
record?.input_tokens,
record?.output_tokens,
record?.total_tokens,
record?.api_requests
]
.filter((item) => item !== null && item !== undefined && item !== '')
.join('__');
return key || JSON.stringify(record); if (type === 'api_keys') {
return `${record.api_key.label}`;
}
return record.last_active;
}; };
export default getBreakdownRowKey; export default getBreakdownRowKey;