fix: usage ux

This commit is contained in:
jialin
2026-04-22 13:16:48 +08:00
committed by jialin
parent 084822814d
commit ad7fc3a993
7 changed files with 532 additions and 236 deletions
+16 -7
View File
@@ -68,12 +68,14 @@ export const SimpleCardItem: React.FC<{
<div className={cx({ bordered: bordered }, styles.wrapper)} style={style}>
<div className="title">{title}</div>
<div className="content">
<span
className={cx([iconType], 'icon')}
style={{
backgroundColor: color || 'transparent'
}}
></span>
{iconType && (
<span
className={cx([iconType], 'icon')}
style={{
backgroundColor: color || 'transparent'
}}
></span>
)}
<span>{content}</span>
</div>
</div>
@@ -89,8 +91,12 @@ export const SimpleCard: React.FC<{
}[];
height?: string | number;
bordered?: boolean;
styles?: {
wrapper?: React.CSSProperties;
item?: React.CSSProperties;
};
}> = (props) => {
const { dataList, bordered } = props;
const { dataList, bordered, styles } = props;
return (
<SimpleCardItemWrapper style={{ height: props.height || '100%' }}>
@@ -102,6 +108,9 @@ export const SimpleCard: React.FC<{
bordered={bordered}
color={item.color}
iconType={item.iconType}
style={{
...styles?.item
}}
></SimpleCardItem>
))}
</SimpleCardItemWrapper>
@@ -1,6 +1,7 @@
import { useIntl } from '@umijs/max';
import { DatePickerProps } from 'antd';
import dayjs, { type Dayjs } from 'dayjs';
import { useState } from 'react';
interface RangePickerPreset {
range: number;
@@ -17,10 +18,17 @@ export default function useRangePickerPreset(options?: RangePickerPreset): {
label: React.ReactNode;
value: [Dayjs, Dayjs] | (() => [Dayjs, Dayjs]);
}[];
handleOnPickerChange: (
value: 'date' | 'week' | 'month' | 'quarter' | 'year'
) => void;
picker: 'date' | 'week' | 'month' | 'quarter' | 'year';
range: number;
} {
const { range = 60, disabledDate, presetRanges } = options || {};
const intl = useIntl();
const [picker, setPicker] = useState<
'date' | 'week' | 'month' | 'quarter' | 'year'
>('month');
const getYearMonth = (date: Dayjs) => date.year() * 12 + date.month();
@@ -55,6 +63,12 @@ export default function useRangePickerPreset(options?: RangePickerPreset): {
return false;
};
const handleOnPickerChange = (
value: 'date' | 'week' | 'month' | 'quarter' | 'year'
) => {
setPicker(value);
};
const rangePresets: {
label: React.ReactNode;
value: [Dayjs, Dayjs] | (() => [Dayjs, Dayjs]);
@@ -79,7 +93,9 @@ export default function useRangePickerPreset(options?: RangePickerPreset): {
return {
disabledRangeDaysDate,
handleOnPickerChange,
rangePresets,
picker,
range
};
}
+5 -6
View File
@@ -1,10 +1,9 @@
import CardWrapper from '@/components/card-wrapper';
import { SimpleCard } from '@/components/card-wrapper/simple-card';
import MixLineBar from '@/components/echarts/mix-line-bar';
import BaseSelect from '@/components/seal-form/base/select';
import { baseColorMap } from '@/pages/dashboard/config';
import { formatLargeNumber } from '@/utils';
import { Divider, Segmented } from 'antd';
import { Segmented } from 'antd';
import dayjs from 'dayjs';
import React, { useMemo } from 'react';
import styled from 'styled-components';
@@ -151,7 +150,7 @@ const DailyUsage: React.FC<DailyUsageProps> = (props) => {
<ControlsWrapper>
<div className="group">
<BaseSelect
variant="outlined"
variant="borderless"
prefix={<ControlLabel>Metric</ControlLabel>}
options={metricOptions}
value={metric}
@@ -160,7 +159,7 @@ const DailyUsage: React.FC<DailyUsageProps> = (props) => {
/>
<BaseSelect
variant="outlined"
variant="borderless"
prefix={<ControlLabel>Group by</ControlLabel>}
options={groupByOptions}
value={groupBy}
@@ -176,9 +175,9 @@ const DailyUsage: React.FC<DailyUsageProps> = (props) => {
onChange={onGranularityChange}
></Segmented>
</ControlsWrapper>
<Divider style={{ margin: 0 }} />
{/* <Divider style={{ margin: 0 }} /> */}
<SimpleCard dataList={summaryCards} height={80} />
{/* <SimpleCard dataList={summaryCards} height={80} /> */}
<MixLineBar
chartData={chartData}
seriesData={[]}
+124
View File
@@ -0,0 +1,124 @@
import AutoTooltip from '@/components/auto-tooltip';
import ModalFooter from '@/components/modal-footer';
import ScrollerModal from '@/components/scroller-modal';
import { useIntl } from '@umijs/max';
import { Table, TableColumnType } from 'antd';
import React, { useEffect } from 'react';
import { useUsageFilters } from '../hooks/use-usage-filters';
import FilterBar from './filter-bar';
const ExportData: React.FC<{
open: boolean;
onCancel: () => void;
initialScope: string;
metaData: any;
}> = (props) => {
const { open, onCancel, initialScope, metaData } = props || {};
const intl = useIntl();
const {
filters,
commonFilters,
fetchData,
loading,
timeSeriesData,
filterBar
} = useUsageFilters({
initialScope: initialScope,
metaData,
chartFilters: {},
summaryColumns: {}
});
const exportTableColumns: TableColumnType[] = [
{
title: intl.formatMessage({ id: 'resources.table.index' }),
width: 80,
render(text: any, row: any, index: number) {
return index + 1;
}
},
{
title: intl.formatMessage({ id: 'dashboard.usage.export.date' }),
dataIndex: 'date'
},
{
title: intl.formatMessage({ id: 'dashboard.usage.export.user' }),
dataIndex: 'user_name',
render: (text: string) => {
return <AutoTooltip ghost>{text}</AutoTooltip>;
}
},
{
title: intl.formatMessage({ id: 'dashboard.usage.export.model' }),
dataIndex: 'model_id',
render: (text: string, record: any) => {
return <AutoTooltip ghost>{text}</AutoTooltip>;
}
},
{
title: 'API Key',
dataIndex: 'api_key',
render: (text: string, record: any) => {
return <AutoTooltip ghost>{text}</AutoTooltip>;
}
}
];
const handleSubmit = () => {
filterBar.onExportChart();
};
const handleOnCancel = () => {
onCancel?.();
};
useEffect(() => {
if (open) {
fetchData(commonFilters, {});
} else {
}
}, [open]);
return (
<ScrollerModal
title={intl.formatMessage({ id: 'dashboard.usage.export' })}
open={open}
centered={false}
onCancel={handleOnCancel}
destroyOnHidden={true}
closeIcon={true}
mask={{
closable: false
}}
keyboard={false}
width={1200}
style={{
top: '10%'
}}
footer={
<ModalFooter
onOk={handleSubmit}
onCancel={handleOnCancel}
okText={intl.formatMessage({ id: 'common.button.export' })}
></ModalFooter>
}
>
<FilterBar {...filterBar} pageType="modal"></FilterBar>
<Table
columns={exportTableColumns}
tableLayout={'auto'}
style={{ width: '100%', marginTop: '16px', minHeight: 300 }}
dataSource={timeSeriesData.series || []}
loading={loading}
rowKey="id"
virtual
scroll={{ y: 400 }}
pagination={false}
></Table>
</ScrollerModal>
);
};
export default ExportData;
+84 -42
View File
@@ -6,7 +6,7 @@ import ProviderLogo from '@/pages/maas-provider/components/provider-logo';
import { useModel } from '@@/plugin-model';
import { DownloadOutlined, SyncOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Button, DatePicker, Dropdown, MenuProps } from 'antd';
import { Button, DatePicker, Dropdown, MenuProps, Segmented } from 'antd';
import dayjs from 'dayjs';
import React from 'react';
import { GroupOption } from '../config';
@@ -22,6 +22,7 @@ type OptionType = UsageFilterItem & {
value: string;
};
interface FilterBarProps {
pageType?: 'page' | 'modal';
scope: string;
startDate: string;
endDate: string;
@@ -38,22 +39,20 @@ interface FilterBarProps {
onApiKeysChange: (value: string[]) => void;
onExport?: () => void;
handleSearch?: () => void;
onExportChart: () => void;
onExportTable: () => void;
onExportChart?: () => void;
onExportTable?: () => void;
}
const FilterBar: React.FC<FilterBarProps> = (props) => {
const {
scope,
pageType = 'page',
startDate,
endDate,
selectedModels,
selectedUsers,
selectedApiKeys,
modelOptions,
userOptions,
apiKeyOptions,
onScopeChange,
onDateChange,
onModelsChange,
onUsersChange,
@@ -63,41 +62,40 @@ const FilterBar: React.FC<FilterBarProps> = (props) => {
handleSearch
} = props;
const intl = useIntl();
const { disabledRangeDaysDate, rangePresets } = useRangePickerPreset({
range: DefaultDateConfig.maxRange,
disabledDate: true,
presetRanges: [
{
label: intl.formatMessage({
id: 'dashboard.usage.datePicker.last7days'
}),
value: [dayjs().add(-6, 'd'), dayjs()]
},
{
label: intl.formatMessage({
id: 'dashboard.usage.datePicker.last30days'
}),
value: [dayjs().add(-29, 'd'), dayjs()]
},
{
label: intl.formatMessage({
id: 'dashboard.usage.datePicker.last60days'
}),
value: [dayjs().add(-59, 'd'), dayjs()]
},
{
label: intl.formatMessage({
id: 'dashboard.usage.datePicker.last90days'
}),
value: [dayjs().add(-89, 'd'), dayjs()]
}
]
});
const { disabledRangeDaysDate, rangePresets, picker, handleOnPickerChange } =
useRangePickerPreset({
range: DefaultDateConfig.maxRange,
disabledDate: true,
presetRanges: [
{
label: intl.formatMessage({
id: 'dashboard.usage.datePicker.last7days'
}),
value: [dayjs().add(-6, 'd'), dayjs()]
},
{
label: intl.formatMessage({
id: 'dashboard.usage.datePicker.last30days'
}),
value: [dayjs().add(-29, 'd'), dayjs()]
},
{
label: intl.formatMessage({
id: 'dashboard.usage.datePicker.last60days'
}),
value: [dayjs().add(-59, 'd'), dayjs()]
},
{
label: intl.formatMessage({
id: 'dashboard.usage.datePicker.last90days'
}),
value: [dayjs().add(-89, 'd'), dayjs()]
}
]
});
const [activeModels, setActiveModels] = React.useState<valueType[][]>([]);
const [activeApiKeys, setActiveApiKeys] = React.useState<valueType[][]>([]);
const [activeUserApiKeys, setActiveUserApiKeys] = React.useState<valueType[]>(
[]
);
const initialInfo = useModel('@@initialState');
const { initialState } = initialInfo || {};
@@ -184,6 +182,46 @@ const FilterBar: React.FC<FilterBarProps> = (props) => {
);
};
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={handleOnPickerChange}
options={[
{
label: 'Day',
value: 'date'
},
{
label: 'Week',
value: 'week'
},
{
label: 'Month',
value: 'month'
}
]}
></Segmented>
);
};
return (
<div className={FilterBarCss.wrapper}>
<div className={FilterBarCss.filters}>
@@ -193,12 +231,14 @@ const FilterBar: React.FC<FilterBarProps> = (props) => {
dayjs().add(-DefaultDateConfig.defaultRange, 'd'),
dayjs()
]}
picker={picker}
disabledDate={disabledRangeDaysDate}
presets={rangePresets}
allowClear={false}
style={{ width: 240 }}
value={[dayjs(startDate), dayjs(endDate)]}
onChange={onDateChange}
renderExtraFooter={renderFooter}
/>
<div
style={{
@@ -318,9 +358,11 @@ const FilterBar: React.FC<FilterBarProps> = (props) => {
icon={<SyncOutlined></SyncOutlined>}
></Button>
</div>
<Dropdown menu={{ items: exportMenuItems }}>
<Button icon={<DownloadOutlined />} />
</Dropdown>
{pageType === 'page' && (
<Dropdown menu={{ items: exportMenuItems }}>
<Button icon={<DownloadOutlined />} />
</Dropdown>
)}
</div>
);
};
+207
View File
@@ -0,0 +1,207 @@
import { exportJsonToExcel } from '@/utils/excel-reader';
import dayjs from 'dayjs';
import { useMemo, useState } from 'react';
import {
generateColumns,
GroupOption,
transformTimelineToTable
} from '../config';
import { UsageFilterItem } from '../config/types';
import useQueryTimeSeriesData from '../services/use-query-timeseries-data';
const DefaultDateConfig = {
defaultRange: 29
};
type UserOptionType = UsageFilterItem & {
value: string;
};
type FilterOptionType = Omit<UsageFilterItem, 'label' | 'deleted'>;
type GroupOptionType = GroupOption<UsageFilterItem>;
interface UseUsageFiltersParams {
initialScope: string;
metaData: {
models?: GroupOptionType[];
users?: UserOptionType[];
api_keys?: GroupOptionType[];
};
chartFilters: {
metric: string;
group_by: string;
granularity: string;
};
summaryColumns: {
title: string;
dataIndex: string;
key: string;
}[];
}
export const useUsageFilters = ({
initialScope,
metaData,
chartFilters,
summaryColumns
}: UseUsageFiltersParams) => {
const {
detailData: timeSeriesData,
loading,
fetchData: fetchTimeSeriesData
} = useQueryTimeSeriesData();
const [commonFilters, setCommonFilters] = useState({
scope: initialScope,
models: [] as string[],
users: [] as string[],
api_keys: [] as string[],
start_date: dayjs()
.subtract(DefaultDateConfig.defaultRange, 'days')
.format('YYYY-MM-DD'),
end_date: dayjs().format('YYYY-MM-DD')
});
const modelOptions = metaData?.models || [];
const userOptions = metaData?.users || [];
const apiKeyOptions = metaData?.api_keys || [];
const buildFilters = (selected: typeof commonFilters) => {
const filters: {
models?: FilterOptionType[];
users?: FilterOptionType[];
api_keys?: FilterOptionType[];
} = {};
if (selected.models.length > 0) {
const modelsSet = new Set(selected.models);
filters.models = modelOptions
.flatMap((group) =>
group.children.filter((item) => modelsSet.has(item.value))
)
.map((item) => ({
identity: item.identity
}));
}
if (selected.users.length > 0) {
filters.users = userOptions
.filter((item) => selected.users.includes(item.value || ''))
.map((item) => ({
identity: item.identity
}));
}
if (selected.api_keys.length > 0) {
const apiKeysSet = new Set(selected.api_keys);
filters.api_keys = apiKeyOptions
.flatMap((group) =>
group.children.filter((item) => apiKeysSet.has(item.value))
)
.map((item) => ({
identity: item.identity
}));
}
return filters;
};
const filters = useMemo(
() => buildFilters(commonFilters),
[commonFilters, modelOptions, userOptions, apiKeyOptions]
);
const fetchData = (
currentSelectedFilters = commonFilters,
currentChartFilters = chartFilters
) => {
fetchTimeSeriesData({
...currentChartFilters,
start_date: currentSelectedFilters.start_date,
end_date: currentSelectedFilters.end_date,
filters: buildFilters(currentSelectedFilters)
});
};
const handleScopeChange = (value: string) => {
const next = { ...commonFilters, scope: value };
setCommonFilters(next);
fetchData(next, chartFilters);
};
const handleDateChange = (_: any, dateStrings: [string, string]) => {
const [start_date, end_date] = dateStrings;
const next = { ...commonFilters, start_date, end_date };
setCommonFilters(next);
fetchData(next, chartFilters);
};
const handleFilterChange = (
type: 'models' | 'users' | 'api_keys',
value: string[]
) => {
const next = { ...commonFilters, [type]: value };
setCommonFilters(next);
fetchData(next, chartFilters);
};
const handleSearch = () => {
fetchData(commonFilters, chartFilters);
};
const handleOnExportChart = () => {
const columns = generateColumns(timeSeriesData?.series || []);
const tableData = transformTimelineToTable(timeSeriesData?.series || []);
exportJsonToExcel({
fileName: `${chartFilters.metric}_${chartFilters.group_by}_${chartFilters.granularity}_${commonFilters.start_date}_${commonFilters.end_date}.xlsx`,
sheets: [
{
jsonData: tableData,
sheetName: chartFilters.metric,
fields: columns.map((col) => col.dataIndex).filter(Boolean),
fieldLabels: Object.fromEntries(
columns.map((col) => [col.dataIndex, col.title])
),
formatMap: {}
},
{
jsonData: [timeSeriesData?.summary || {}],
sheetName: 'summary',
fields: summaryColumns.map((col) => col.dataIndex),
fieldLabels: Object.fromEntries(
summaryColumns.map((col) => [col.dataIndex, col.title])
),
formatMap: {}
}
]
});
};
const filterBar = {
scope: commonFilters.scope,
startDate: commonFilters.start_date,
endDate: commonFilters.end_date,
selectedModels: commonFilters.models,
selectedUsers: commonFilters.users,
selectedApiKeys: commonFilters.api_keys,
modelOptions,
userOptions,
apiKeyOptions,
handleSearch,
onScopeChange: handleScopeChange,
onDateChange: handleDateChange,
onModelsChange: (value: string[]) => handleFilterChange('models', value),
onUsersChange: (value: string[]) => handleFilterChange('users', value),
onApiKeysChange: (value: string[]) => handleFilterChange('api_keys', value),
onExportChart: handleOnExportChart
};
return {
filters,
loading,
commonFilters,
timeSeriesData,
fetchData,
filterBar
};
};
+80 -181
View File
@@ -1,31 +1,21 @@
import { exportJsonToExcel } from '@/utils/excel-reader';
import { SimpleCard } from '@/components/card-wrapper/simple-card';
import { baseColorMap } from '@/pages/dashboard/config';
import { formatLargeNumber } from '@/utils';
import { useModel } from '@@/plugin-model';
import dayjs from 'dayjs';
import React, { useEffect, useMemo, useState } from 'react';
import BreakdownTabs from './components/breakdown-tabs';
import DailyUsage from './components/daily-usage';
import ExportData from './components/export-data';
import FilterBar from './components/filter-bar';
import {
generateColumns,
GroupOption,
transformTimelineToTable
} from './config';
import { UsageFilterItem } from './config/types';
import useExportTable from './hooks/use-export-table';
import { useUsageFilters } from './hooks/use-usage-filters';
import useQueryUsageMetaData from './services/use-query-meta-data';
import useQueryTimeSeriesData from './services/use-query-timeseries-data';
const DefaultDateConfig = {
defaultRange: 29
};
type FilterOptionType = Omit<UsageFilterItem, 'label' | 'deleted'>;
type GroupOptionType = GroupOption<UsageFilterItem>;
const Usage: React.FC = () => {
const initialInfo = useModel('@@initialState');
const { initialState } = initialInfo || {};
const { exportTable } = useExportTable();
const [openExportModal, setOpenExportModal] = useState(false);
const summaryColumns = [
{
@@ -64,195 +54,98 @@ const Usage: React.FC = () => {
group_by: 'model',
granularity: 'day'
});
const [commonFilters, setCommonFilters] = useState<{
models: string[];
users: string[];
api_keys: string[];
start_date: string;
end_date: string;
scope: string;
}>({
scope: initialState?.currentUser?.is_admin ? 'all' : 'self',
models: [],
users: [],
api_keys: [],
start_date: dayjs()
.subtract(DefaultDateConfig.defaultRange, 'days')
.format('YYYY-MM-DD'),
end_date: dayjs().format('YYYY-MM-DD')
});
const { detailData: metaData, fetchData: fetchMetaData } =
useQueryUsageMetaData();
const { detailData: timeSeriesData, fetchData: fetchTimeSeriesData } =
useQueryTimeSeriesData();
const modelOptions = metaData?.models || [];
const userOptions = metaData?.users || [];
const apiKeyOptions = metaData?.api_keys || [];
const buildFilters = (selected: {
models: string[];
users: string[];
api_keys: string[];
}) => {
const filters: {
models?: FilterOptionType[];
users?: FilterOptionType[];
api_keys?: FilterOptionType[];
} = {};
if (selected.models?.length > 0) {
const modelsSet = new Set(selected.models);
// filter the options
filters.models = modelOptions
.flatMap((group) =>
group.children.filter((item) => modelsSet.has(item.value))
)
.map((item) => ({
identity: item.identity
}));
}
if (selected.users?.length > 0) {
filters.users = userOptions
.filter((item) => selected.users?.includes(item.value || ''))
.map((item) => ({
identity: item.identity
}));
}
if (selected.api_keys?.length > 0) {
const apiKeysSet = new Set(selected.api_keys);
filters.api_keys = apiKeyOptions
.flatMap((group) =>
group.children.filter((item) => apiKeysSet.has(item.value))
)
.map((item) => ({
identity: item.identity
}));
}
return filters;
};
const filters = useMemo(
() => buildFilters(commonFilters),
[commonFilters, modelOptions, userOptions, apiKeyOptions]
);
const fetchData = (
currentSelectedFilters = commonFilters,
currentChartFilters = chartFilters
) => {
fetchTimeSeriesData({
...currentChartFilters,
start_date: currentSelectedFilters.start_date,
end_date: currentSelectedFilters.end_date,
filters: buildFilters(currentSelectedFilters)
const { filters, commonFilters, fetchData, timeSeriesData, filterBar } =
useUsageFilters({
initialScope: initialState?.currentUser?.is_admin ? 'all' : 'self',
metaData,
chartFilters,
summaryColumns
});
};
useEffect(() => {
fetchMetaData();
fetchData(commonFilters, chartFilters);
}, []);
const handleScopeChange = (value: string) => {
setCommonFilters((prev) => ({ ...prev, scope: value }));
fetchData(
{
...commonFilters,
scope: value
},
chartFilters
);
};
const handleDateChange = (_dates: any, dateStrings: [string, string]) => {
const [start_date, end_date] = dateStrings;
setCommonFilters((prev) => ({ ...prev, start_date, end_date }));
fetchData({ ...commonFilters, start_date, end_date }, chartFilters);
};
const handleFilterChange = (type: string, value: string[]) => {
console.log('handleFilterChange:', type, value);
setCommonFilters((prev) => ({ ...prev, [type]: value }));
fetchData({ ...commonFilters, [type]: value }, chartFilters);
};
const handleChartFilterChange = (type: string, value: string) => {
setChartFilters((prev) => ({ ...prev, [type]: value }));
fetchData(commonFilters, { ...chartFilters, [type]: value });
};
const handleSearch = () => {
fetchData(commonFilters, chartFilters);
};
const summaryCards = useMemo(() => {
const summary = timeSeriesData?.summary || {
input_tokens: 0,
output_tokens: 0,
total_tokens: 0,
api_requests: 0,
models_called: 0
};
return [
{
label: formatLargeNumber(summary.input_tokens) as string,
value: 'Input tokens',
color: baseColorMap.baseR3
// iconType: 'roundRect'
},
{
label: formatLargeNumber(summary.output_tokens) as string,
value: 'Output tokens',
color: baseColorMap.base
// iconType: 'roundRect'
},
{
label: formatLargeNumber(summary.total_tokens) as string,
value: 'Total tokens',
color: baseColorMap.baseL1
// iconType: 'roundRect'
},
{
label: formatLargeNumber(summary.api_requests) as string,
value: 'API requests',
color: baseColorMap.baseR1
// iconType: 'circle'
},
{
label: summary.models_called.toString(),
value: 'Models used',
color: baseColorMap.baseR2
// iconType: 'roundRect'
}
];
}, [timeSeriesData.summary]);
console.log('timeSeriesData:', timeSeriesData);
const handleOnExportChart = () => {
const columns = generateColumns(timeSeriesData.series);
const tableData = transformTimelineToTable(timeSeriesData.series);
exportJsonToExcel({
fileName: `${chartFilters.metric}_${chartFilters.group_by}_${chartFilters.granularity}_${commonFilters.start_date}_${commonFilters.end_date}.xlsx`,
sheets: [
{
jsonData: tableData || [],
sheetName: `${chartFilters.metric}`,
fields: columns
.map((col) => col.dataIndex)
.filter(Boolean) as string[],
fieldLabels: columns.reduce(
(map, col) => {
map[col.dataIndex] = col.title;
return map;
},
{} as Record<string, any>
),
formatMap: {}
},
{
jsonData: [timeSeriesData.summary || {}],
sheetName: `summary`,
fields: summaryColumns
.map((col) => col.dataIndex)
.filter(Boolean) as string[],
fieldLabels: summaryColumns.reduce(
(map, col) => {
map[col.dataIndex] = col.title;
return map;
},
{} as Record<string, any>
),
formatMap: {}
}
]
});
const handleExportChart = () => {
setOpenExportModal(true);
};
return (
<div>
<FilterBar
scope={commonFilters.scope}
startDate={commonFilters.start_date}
endDate={commonFilters.end_date}
selectedModels={commonFilters.models || []}
selectedUsers={commonFilters.users || []}
selectedApiKeys={commonFilters.api_keys || []}
modelOptions={metaData?.models || []}
userOptions={metaData?.users || []}
apiKeyOptions={metaData?.api_keys || []}
handleSearch={handleSearch}
onScopeChange={handleScopeChange}
onDateChange={handleDateChange}
onModelsChange={(value) => handleFilterChange('models', value)}
onUsersChange={(value) => handleFilterChange('users', value)}
onApiKeysChange={(value) => handleFilterChange('api_keys', value)}
onExportChart={handleOnExportChart}
{...filterBar}
onExportTable={exportTable}
onExportChart={handleExportChart}
/>
<div
style={{
marginBlock: 24
}}
>
<SimpleCard
dataList={summaryCards}
height={80}
styles={{
item: {
borderBottom: '1px solid var(--ant-color-split)',
backgroundColor: 'var(--ant-color-fill-quaternary)',
borderRadius: '6px 6px 0 0'
}
}}
/>
</div>
<DailyUsage
timeSeriesData={timeSeriesData}
metric={chartFilters.metric}
@@ -272,6 +165,12 @@ const Usage: React.FC = () => {
}}
scope={commonFilters.scope}
></BreakdownTabs>
<ExportData
metaData={metaData}
initialScope={commonFilters.scope}
open={openExportModal}
onCancel={() => setOpenExportModal(false)}
></ExportData>
</div>
);
};