fix: models, apikeys Cascader
This commit is contained in:
@@ -1,11 +1,13 @@
|
|||||||
import { Tabs } from 'antd';
|
import { Tabs } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { GroupOption } from '../config';
|
||||||
import { UsageFilterItem } from '../config/types';
|
import { UsageFilterItem } from '../config/types';
|
||||||
import ApiKeysTable from '../tables/apikeys-table';
|
import ApiKeysTable from '../tables/apikeys-table';
|
||||||
import ModelsTable from '../tables/models-table';
|
import ModelsTable from '../tables/models-table';
|
||||||
import UsersTable from '../tables/users-table';
|
import UsersTable from '../tables/users-table';
|
||||||
|
|
||||||
type FilterOptionType = Omit<UsageFilterItem, 'label' | 'deleted'>;
|
type FilterOptionType = Omit<UsageFilterItem, 'label' | 'deleted'>;
|
||||||
|
type GroupOptionType = GroupOption<UsageFilterItem>;
|
||||||
|
|
||||||
const BreakdownTabs: React.FC<{
|
const BreakdownTabs: React.FC<{
|
||||||
dateRange: {
|
dateRange: {
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ const DailyUsage: React.FC<DailyUsageProps> = (props) => {
|
|||||||
chartData={chartData}
|
chartData={chartData}
|
||||||
seriesData={[]}
|
seriesData={[]}
|
||||||
xAxisData={xAxisData}
|
xAxisData={xAxisData}
|
||||||
height={360}
|
height={280}
|
||||||
smooth={false}
|
smooth={false}
|
||||||
legendData={legendData}
|
legendData={legendData}
|
||||||
labelFormatter={labelFormatter}
|
labelFormatter={labelFormatter}
|
||||||
|
|||||||
@@ -1,14 +1,19 @@
|
|||||||
|
import AutoTooltip from '@/components/auto-tooltip';
|
||||||
|
import SealCascader from '@/components/seal-form/seal-cascader';
|
||||||
import SimpleSelect from '@/components/seal-form/simple-select';
|
import SimpleSelect from '@/components/seal-form/simple-select';
|
||||||
import useRangePickerPreset from '@/pages/dashboard/hooks/use-rangepicker-preset';
|
import useRangePickerPreset from '@/pages/dashboard/hooks/use-rangepicker-preset';
|
||||||
|
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 } 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 { UsageFilterItem } from '../config/types';
|
import { UsageFilterItem } from '../config/types';
|
||||||
import FilterBarCss from '../styles/filter-bar.less';
|
import FilterBarCss from '../styles/filter-bar.less';
|
||||||
|
|
||||||
|
type valueType = string | number | null;
|
||||||
const DefaultDateConfig = {
|
const DefaultDateConfig = {
|
||||||
maxRange: 90,
|
maxRange: 90,
|
||||||
defaultRange: 29
|
defaultRange: 29
|
||||||
@@ -23,9 +28,9 @@ interface FilterBarProps {
|
|||||||
selectedModels: string[];
|
selectedModels: string[];
|
||||||
selectedUsers: string[];
|
selectedUsers: string[];
|
||||||
selectedApiKeys: string[];
|
selectedApiKeys: string[];
|
||||||
modelOptions: OptionType[];
|
modelOptions: GroupOption<UsageFilterItem>[];
|
||||||
userOptions: OptionType[];
|
userOptions: OptionType[];
|
||||||
apiKeyOptions: OptionType[];
|
apiKeyOptions: GroupOption<UsageFilterItem>[];
|
||||||
onScopeChange: (value: string) => void;
|
onScopeChange: (value: string) => void;
|
||||||
onDateChange: (dates: any, dateStrings: [string, string]) => void;
|
onDateChange: (dates: any, dateStrings: [string, string]) => void;
|
||||||
onModelsChange: (value: string[]) => void;
|
onModelsChange: (value: string[]) => void;
|
||||||
@@ -88,6 +93,11 @@ const FilterBar: React.FC<FilterBarProps> = (props) => {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
const [activeModels, setActiveModels] = React.useState<valueType[][]>([]);
|
||||||
|
const [activeApiKeys, setActiveApiKeys] = React.useState<valueType[][]>([]);
|
||||||
|
const [activeUserApiKeys, setActiveUserApiKeys] = React.useState<valueType[]>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
const initialInfo = useModel('@@initialState');
|
const initialInfo = useModel('@@initialState');
|
||||||
const { initialState } = initialInfo || {};
|
const { initialState } = initialInfo || {};
|
||||||
|
|
||||||
@@ -104,17 +114,79 @@ const FilterBar: React.FC<FilterBarProps> = (props) => {
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const handleOnModelsChange = (value: valueType[][], selectedOptions: any) => {
|
||||||
|
setActiveModels(value);
|
||||||
|
const selectedValues: string[] = value.map((item) => {
|
||||||
|
if (Array.isArray(item)) {
|
||||||
|
return item[item.length - 1] as string; // Get the last value in the array
|
||||||
|
}
|
||||||
|
return item as string;
|
||||||
|
});
|
||||||
|
onModelsChange(selectedValues);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOnApiKeysChange = (
|
||||||
|
value: valueType[][],
|
||||||
|
selectedOptions: any
|
||||||
|
) => {
|
||||||
|
setActiveApiKeys(value);
|
||||||
|
const selectedValues: string[] = value.map((item) => {
|
||||||
|
if (Array.isArray(item)) {
|
||||||
|
return item[item.length - 1] as string; // Get the last value in the array
|
||||||
|
}
|
||||||
|
return item as string;
|
||||||
|
});
|
||||||
|
onApiKeysChange(selectedValues);
|
||||||
|
};
|
||||||
|
|
||||||
|
const displayRender = (labels: any[], option: any) => {
|
||||||
|
return (
|
||||||
|
<AutoTooltip
|
||||||
|
ghost
|
||||||
|
maxWidth={150}
|
||||||
|
title={
|
||||||
|
<span>
|
||||||
|
{labels[0]} / {labels[1]}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{labels[0]} / {labels[1]}
|
||||||
|
</AutoTooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const optionRender = (option: any) => {
|
||||||
|
const { data } = option;
|
||||||
|
console.log('optionRender', option);
|
||||||
|
|
||||||
|
if (!data.isParent) {
|
||||||
|
return <AutoTooltip ghost>{data.label}</AutoTooltip>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.type === 'deployments') {
|
||||||
|
return (
|
||||||
|
<span className={FilterBarCss.optionsWrapper}>
|
||||||
|
<ProviderLogo provider={data.type as string} />
|
||||||
|
<AutoTooltip ghost>
|
||||||
|
{intl.formatMessage({ id: 'menu.models.deployment' })}
|
||||||
|
</AutoTooltip>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className={FilterBarCss.optionsWrapper}>
|
||||||
|
<ProviderLogo provider={data.type as string} />
|
||||||
|
<AutoTooltip ghost>
|
||||||
|
<span>{data.label}</span>
|
||||||
|
</AutoTooltip>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={FilterBarCss.wrapper}>
|
<div className={FilterBarCss.wrapper}>
|
||||||
<div className={FilterBarCss.filters}>
|
<div className={FilterBarCss.filters}>
|
||||||
{/* {initialState?.currentUser?.is_admin && (
|
|
||||||
<BaseSelect
|
|
||||||
options={scopeOptions}
|
|
||||||
value={scope}
|
|
||||||
onChange={onScopeChange}
|
|
||||||
style={{ width: 150 }}
|
|
||||||
/>
|
|
||||||
)} */}
|
|
||||||
<DatePicker.RangePicker
|
<DatePicker.RangePicker
|
||||||
maxDate={dayjs()}
|
maxDate={dayjs()}
|
||||||
defaultValue={[
|
defaultValue={[
|
||||||
@@ -128,46 +200,117 @@ const FilterBar: React.FC<FilterBarProps> = (props) => {
|
|||||||
value={[dayjs(startDate), dayjs(endDate)]}
|
value={[dayjs(startDate), dayjs(endDate)]}
|
||||||
onChange={onDateChange}
|
onChange={onDateChange}
|
||||||
/>
|
/>
|
||||||
<SimpleSelect
|
<div
|
||||||
allowClear
|
style={{
|
||||||
showSearch
|
maxWidth: 400,
|
||||||
mode="multiple"
|
flex: 1,
|
||||||
options={modelOptions}
|
minWidth: 200
|
||||||
maxTagCount={0}
|
|
||||||
placeholder="Model"
|
|
||||||
styles={{
|
|
||||||
wrapper: { flex: 1, maxWidth: 300, minWidth: 150 }
|
|
||||||
}}
|
}}
|
||||||
value={selectedModels}
|
>
|
||||||
onChange={onModelsChange}
|
<SealCascader
|
||||||
/>
|
showSearch
|
||||||
|
multiple={true}
|
||||||
|
classNames={{
|
||||||
|
popup: {
|
||||||
|
root: 'cascader-popup-wrapper gpu-selector'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
styles={{
|
||||||
|
root: {
|
||||||
|
width: '100%'
|
||||||
|
},
|
||||||
|
popup: {
|
||||||
|
list: {
|
||||||
|
flex: 1
|
||||||
|
},
|
||||||
|
listItem: {
|
||||||
|
padding: '5px 10px'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
maxTagCount={1}
|
||||||
|
size="small"
|
||||||
|
placeholder={intl.formatMessage({
|
||||||
|
id: 'dashboard.usage.selectmodel'
|
||||||
|
})}
|
||||||
|
options={modelOptions}
|
||||||
|
showCheckedStrategy="SHOW_CHILD"
|
||||||
|
displayRender={displayRender}
|
||||||
|
optionNode={optionRender}
|
||||||
|
value={activeModels}
|
||||||
|
onChange={handleOnModelsChange}
|
||||||
|
getPopupContainer={(triggerNode) => triggerNode.parentNode}
|
||||||
|
></SealCascader>
|
||||||
|
</div>
|
||||||
{initialState?.currentUser?.is_admin && (
|
{initialState?.currentUser?.is_admin && (
|
||||||
|
<>
|
||||||
|
<SimpleSelect
|
||||||
|
allowClear
|
||||||
|
showSearch
|
||||||
|
mode="multiple"
|
||||||
|
options={userOptions}
|
||||||
|
placeholder="User"
|
||||||
|
styles={{
|
||||||
|
wrapper: { flex: 1, maxWidth: 240, minWidth: 150 }
|
||||||
|
}}
|
||||||
|
value={selectedUsers}
|
||||||
|
onChange={onUsersChange}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
maxWidth: 400,
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 200
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SealCascader
|
||||||
|
showSearch
|
||||||
|
multiple={true}
|
||||||
|
classNames={{
|
||||||
|
popup: {
|
||||||
|
root: 'cascader-popup-wrapper gpu-selector'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
styles={{
|
||||||
|
root: {
|
||||||
|
width: '100%'
|
||||||
|
},
|
||||||
|
popup: {
|
||||||
|
list: {
|
||||||
|
flex: 1
|
||||||
|
},
|
||||||
|
listItem: {
|
||||||
|
padding: '5px 10px'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
maxTagCount={1}
|
||||||
|
size="small"
|
||||||
|
placeholder="API Key"
|
||||||
|
options={apiKeyOptions}
|
||||||
|
showCheckedStrategy="SHOW_CHILD"
|
||||||
|
value={activeApiKeys}
|
||||||
|
onChange={handleOnApiKeysChange}
|
||||||
|
getPopupContainer={(triggerNode) => triggerNode.parentNode}
|
||||||
|
></SealCascader>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{!initialState?.currentUser?.is_admin && (
|
||||||
<SimpleSelect
|
<SimpleSelect
|
||||||
allowClear
|
allowClear
|
||||||
showSearch
|
showSearch
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
options={userOptions}
|
options={apiKeyOptions?.[0]?.children || []}
|
||||||
placeholder="User"
|
maxTagCount={0}
|
||||||
|
placeholder="API Key"
|
||||||
styles={{
|
styles={{
|
||||||
wrapper: { flex: 1, maxWidth: 240, minWidth: 150 }
|
wrapper: { flex: 1, maxWidth: 240, minWidth: 100 }
|
||||||
}}
|
}}
|
||||||
value={selectedUsers}
|
value={selectedApiKeys}
|
||||||
onChange={onUsersChange}
|
onChange={onApiKeysChange}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<SimpleSelect
|
|
||||||
allowClear
|
|
||||||
showSearch
|
|
||||||
mode="multiple"
|
|
||||||
options={apiKeyOptions}
|
|
||||||
maxTagCount={0}
|
|
||||||
placeholder="API Key"
|
|
||||||
styles={{
|
|
||||||
wrapper: { flex: 1, maxWidth: 240, minWidth: 100 }
|
|
||||||
}}
|
|
||||||
value={selectedApiKeys}
|
|
||||||
onChange={onApiKeysChange}
|
|
||||||
/>
|
|
||||||
<Button
|
<Button
|
||||||
type="text"
|
type="text"
|
||||||
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { UsageFilterItem } from './types';
|
||||||
|
|
||||||
export const groupByOptions = [
|
export const groupByOptions = [
|
||||||
{
|
{
|
||||||
value: '',
|
value: '',
|
||||||
@@ -106,3 +108,63 @@ export const generateColumns = (data: SourceItem[]) => {
|
|||||||
}))
|
}))
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface DataItem {
|
||||||
|
identity: {
|
||||||
|
value: {
|
||||||
|
provider_name: string | null;
|
||||||
|
provider_type: string | null;
|
||||||
|
model_name: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GroupedOption {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
providerType: string;
|
||||||
|
children: {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
identity: UsageFilterItem['identity'];
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GroupOption<TChild> {
|
||||||
|
value: string | number | null;
|
||||||
|
label: string;
|
||||||
|
type: string;
|
||||||
|
isParent: boolean;
|
||||||
|
children: (TChild & { value: string })[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function groupToOptions<T, TChild>(
|
||||||
|
data: T[],
|
||||||
|
options: {
|
||||||
|
getGroupKey: (item: T) => string;
|
||||||
|
getGroupType: (item: T) => string;
|
||||||
|
getChild: (item: T) => TChild;
|
||||||
|
}
|
||||||
|
): GroupOption<TChild>[] {
|
||||||
|
const groupedMap = new Map<string, GroupOption<TChild>>();
|
||||||
|
|
||||||
|
data.forEach((item) => {
|
||||||
|
const groupKey = options.getGroupKey(item);
|
||||||
|
const groupType = options.getGroupType(item);
|
||||||
|
|
||||||
|
if (!groupedMap.has(groupKey)) {
|
||||||
|
groupedMap.set(groupKey, {
|
||||||
|
value: groupKey,
|
||||||
|
label: groupKey,
|
||||||
|
type: groupType,
|
||||||
|
isParent: true,
|
||||||
|
children: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
groupedMap.get(groupKey)!.children.push(options.getChild(item));
|
||||||
|
});
|
||||||
|
|
||||||
|
return Array.from(groupedMap.values());
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ export interface UsageFilterItem {
|
|||||||
api_key_name: string | null;
|
api_key_name: string | null;
|
||||||
access_key: string | null;
|
access_key: string | null;
|
||||||
api_key_is_custom: boolean | null;
|
api_key_is_custom: boolean | null;
|
||||||
|
provider_type: string | null;
|
||||||
|
provider_name: string | null;
|
||||||
};
|
};
|
||||||
current: {
|
current: {
|
||||||
model_id: string | null;
|
model_id: string | null;
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
import { exportJsonToExcel } from '@/utils/excel-reader';
|
import { exportJsonToExcel } from '@/utils/excel-reader';
|
||||||
import { useModel } from '@@/plugin-model';
|
import { useModel } from '@@/plugin-model';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import _ from 'lodash';
|
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import BreakdownTabs from './components/breakdown-tabs';
|
import BreakdownTabs from './components/breakdown-tabs';
|
||||||
import DailyUsage from './components/daily-usage';
|
import DailyUsage from './components/daily-usage';
|
||||||
import FilterBar from './components/filter-bar';
|
import FilterBar from './components/filter-bar';
|
||||||
import { generateColumns, transformTimelineToTable } from './config';
|
import {
|
||||||
|
generateColumns,
|
||||||
|
GroupOption,
|
||||||
|
transformTimelineToTable
|
||||||
|
} from './config';
|
||||||
import { UsageFilterItem } from './config/types';
|
import { UsageFilterItem } from './config/types';
|
||||||
import useExportTable from './hooks/use-export-table';
|
import useExportTable from './hooks/use-export-table';
|
||||||
import useQueryUsageMetaData from './services/use-query-meta-data';
|
import useQueryUsageMetaData from './services/use-query-meta-data';
|
||||||
@@ -17,6 +20,7 @@ const DefaultDateConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type FilterOptionType = Omit<UsageFilterItem, 'label' | 'deleted'>;
|
type FilterOptionType = Omit<UsageFilterItem, 'label' | 'deleted'>;
|
||||||
|
type GroupOptionType = GroupOption<UsageFilterItem>;
|
||||||
|
|
||||||
const Usage: React.FC = () => {
|
const Usage: React.FC = () => {
|
||||||
const initialInfo = useModel('@@initialState');
|
const initialInfo = useModel('@@initialState');
|
||||||
@@ -99,9 +103,12 @@ const Usage: React.FC = () => {
|
|||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
if (selected.models?.length > 0) {
|
if (selected.models?.length > 0) {
|
||||||
|
const modelsSet = new Set(selected.models);
|
||||||
// filter the options
|
// filter the options
|
||||||
filters.models = modelOptions
|
filters.models = modelOptions
|
||||||
.filter((item) => selected.models?.includes(item.value || ''))
|
.flatMap((group) =>
|
||||||
|
group.children.filter((item) => modelsSet.has(item.value))
|
||||||
|
)
|
||||||
.map((item) => ({
|
.map((item) => ({
|
||||||
identity: item.identity
|
identity: item.identity
|
||||||
}));
|
}));
|
||||||
@@ -116,8 +123,11 @@ const Usage: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (selected.api_keys?.length > 0) {
|
if (selected.api_keys?.length > 0) {
|
||||||
|
const apiKeysSet = new Set(selected.api_keys);
|
||||||
filters.api_keys = apiKeyOptions
|
filters.api_keys = apiKeyOptions
|
||||||
.filter((item) => selected.api_keys?.includes(item.value || ''))
|
.flatMap((group) =>
|
||||||
|
group.children.filter((item) => apiKeysSet.has(item.value))
|
||||||
|
)
|
||||||
.map((item) => ({
|
.map((item) => ({
|
||||||
identity: item.identity
|
identity: item.identity
|
||||||
}));
|
}));
|
||||||
@@ -135,10 +145,6 @@ const Usage: React.FC = () => {
|
|||||||
currentSelectedFilters = commonFilters,
|
currentSelectedFilters = commonFilters,
|
||||||
currentChartFilters = chartFilters
|
currentChartFilters = chartFilters
|
||||||
) => {
|
) => {
|
||||||
const filteredChartFilters = _.pickBy(
|
|
||||||
currentChartFilters,
|
|
||||||
(value: string) => value
|
|
||||||
);
|
|
||||||
fetchTimeSeriesData({
|
fetchTimeSeriesData({
|
||||||
...currentChartFilters,
|
...currentChartFilters,
|
||||||
start_date: currentSelectedFilters.start_date,
|
start_date: currentSelectedFilters.start_date,
|
||||||
@@ -170,6 +176,7 @@ const Usage: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleFilterChange = (type: string, value: string[]) => {
|
const handleFilterChange = (type: string, value: string[]) => {
|
||||||
|
console.log('handleFilterChange:', type, value);
|
||||||
setCommonFilters((prev) => ({ ...prev, [type]: value }));
|
setCommonFilters((prev) => ({ ...prev, [type]: value }));
|
||||||
fetchData({ ...commonFilters, [type]: value }, chartFilters);
|
fetchData({ ...commonFilters, [type]: value }, chartFilters);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { useQueryData } from '@/hooks/use-query-data-list';
|
import { useQueryData } from '@/hooks/use-query-data-list';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { queryUsageMetaData } from '../apis';
|
import { queryUsageMetaData } from '../apis';
|
||||||
|
import { GroupOption, groupToOptions } from '../config';
|
||||||
import { UsageFilterItem, UsageMeta } from '../config/types';
|
import { UsageFilterItem, UsageMeta } from '../config/types';
|
||||||
|
|
||||||
type OptionType = UsageFilterItem & {
|
type UserOptionType = UsageFilterItem & {
|
||||||
value: string;
|
value: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -15,9 +16,9 @@ export default function useQueryUsageMetaData() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const [result, setResult] = useState<{
|
const [result, setResult] = useState<{
|
||||||
models: OptionType[];
|
models: GroupOption<UsageFilterItem>[];
|
||||||
users: OptionType[];
|
users: UserOptionType[];
|
||||||
api_keys: OptionType[];
|
api_keys: GroupOption<UsageFilterItem>[];
|
||||||
}>({
|
}>({
|
||||||
models: [],
|
models: [],
|
||||||
users: [],
|
users: [],
|
||||||
@@ -28,21 +29,31 @@ export default function useQueryUsageMetaData() {
|
|||||||
const res = await fetchData({});
|
const res = await fetchData({});
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
models:
|
models: groupToOptions(res?.filters?.models || [], {
|
||||||
res?.filters?.models?.map((item) => ({
|
getGroupKey: (item) => item.identity.value.provider_name || 'gpustack',
|
||||||
|
getGroupType: (item) =>
|
||||||
|
item.identity.value.provider_type || 'deployments',
|
||||||
|
getChild: (item) => ({
|
||||||
|
...item,
|
||||||
value: item.label,
|
value: item.label,
|
||||||
...item
|
label: item.identity.value.model_name || ''
|
||||||
})) || [],
|
})
|
||||||
|
}),
|
||||||
users:
|
users:
|
||||||
res?.filters?.users?.map((item) => ({
|
res?.filters?.users?.map((item) => ({
|
||||||
value: item.label,
|
value: item.label,
|
||||||
...item
|
...item
|
||||||
})) || [],
|
})) || [],
|
||||||
api_keys:
|
api_keys: groupToOptions(res?.filters?.api_keys || [], {
|
||||||
res?.filters?.api_keys?.map((item) => ({
|
getGroupKey: (item) => item.identity.value.user_name || 'unknown_user',
|
||||||
|
getGroupType: (item) =>
|
||||||
|
item.identity.value.api_key_is_custom ? 'custom' : 'default',
|
||||||
|
getChild: (item) => ({
|
||||||
|
...item,
|
||||||
value: item.label,
|
value: item.label,
|
||||||
...item
|
label: item.identity.value.api_key_name || ''
|
||||||
})) || []
|
})
|
||||||
|
})
|
||||||
};
|
};
|
||||||
setResult(data);
|
setResult(data);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,3 +12,10 @@
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.optionsWrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding-right: 4px;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user