fix: deleted tag
This commit is contained in:
@@ -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
@@ -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;
|
||||||
|
|||||||
@@ -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'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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' }}>
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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%'
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -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}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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 || [], {
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user