feat: add simple select component
This commit is contained in:
@@ -0,0 +1,147 @@
|
|||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import type { SelectProps } from 'antd';
|
||||||
|
import { Checkbox, Select, Tag } from 'antd';
|
||||||
|
import { CheckboxChangeEvent } from 'antd/es/checkbox';
|
||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import AutoTooltip from '../auto-tooltip';
|
||||||
|
|
||||||
|
const OptionWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const DropdownWrapper = styled.div`
|
||||||
|
.ant-select-item {
|
||||||
|
padding-inline-start: 8px;
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--ant-select-option-active-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ant-select-item-option-selected:not(.ant-select-item-option-disabled) {
|
||||||
|
background-color: unset;
|
||||||
|
font-weight: unset;
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--ant-select-option-active-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const SelectAllWrapper = styled.div`
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 5px 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
border-bottom: 1px solid var(--ant-color-split);
|
||||||
|
.ant-checkbox-wrapper {
|
||||||
|
color: var(--ant-color-text-tertiary);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const TagWrapper = styled(Tag)`
|
||||||
|
border-radius: 12px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const SimpleSelect: React.FC<SelectProps> = (props) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const { options, ...restProps } = props;
|
||||||
|
|
||||||
|
const [allSelection, setAllSelection] = React.useState<{
|
||||||
|
checked: boolean;
|
||||||
|
indeterminate: boolean;
|
||||||
|
}>({
|
||||||
|
checked: false,
|
||||||
|
indeterminate: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const optionRender = (option: any, info: any) => {
|
||||||
|
const { value, label } = option;
|
||||||
|
return (
|
||||||
|
<OptionWrapper>
|
||||||
|
{restProps.value?.includes(value) ? (
|
||||||
|
<Checkbox checked></Checkbox>
|
||||||
|
) : (
|
||||||
|
<Checkbox></Checkbox>
|
||||||
|
)}
|
||||||
|
<AutoTooltip ghost>{label}</AutoTooltip>
|
||||||
|
</OptionWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOnCheckboxChange = (e: CheckboxChangeEvent) => {
|
||||||
|
const isChecked = e.target.checked;
|
||||||
|
const allValues = options?.map((opt: any) => opt.value) || [];
|
||||||
|
setAllSelection({
|
||||||
|
checked: isChecked,
|
||||||
|
indeterminate: false
|
||||||
|
});
|
||||||
|
|
||||||
|
restProps.onChange?.(isChecked ? allValues : [], options || []);
|
||||||
|
};
|
||||||
|
|
||||||
|
const dropdownRender = (originPanel: React.ReactNode) => {
|
||||||
|
return (
|
||||||
|
<DropdownWrapper>
|
||||||
|
{restProps.mode === 'multiple' && (
|
||||||
|
<SelectAllWrapper>
|
||||||
|
<Checkbox
|
||||||
|
checked={allSelection.checked}
|
||||||
|
indeterminate={allSelection.indeterminate}
|
||||||
|
onChange={handleOnCheckboxChange}
|
||||||
|
>
|
||||||
|
{intl.formatMessage({ id: 'common.checbox.all' })}
|
||||||
|
</Checkbox>
|
||||||
|
</SelectAllWrapper>
|
||||||
|
)}
|
||||||
|
{originPanel}
|
||||||
|
</DropdownWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOnChange = (value: any, option: any) => {
|
||||||
|
const selectedValues = Array.isArray(value) ? value : [value];
|
||||||
|
const allSelected = options?.map((opt: any) => opt.value) || [];
|
||||||
|
const isAllSelected = selectedValues.length === allSelected?.length;
|
||||||
|
|
||||||
|
setAllSelection({
|
||||||
|
checked: isAllSelected,
|
||||||
|
indeterminate: !isAllSelected && selectedValues.length > 0
|
||||||
|
});
|
||||||
|
|
||||||
|
restProps.onChange?.(selectedValues, option);
|
||||||
|
};
|
||||||
|
|
||||||
|
const TagRender = (props: any) => {
|
||||||
|
const { label } = props;
|
||||||
|
const count = props.isMaxTag ? label.slice(0, -3).slice(1) : label;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TagWrapper
|
||||||
|
bordered={false}
|
||||||
|
style={{
|
||||||
|
height: 24,
|
||||||
|
backgroundColor: 'var(--ant-color-fill-tertiary)',
|
||||||
|
fontSize: 'var(--ant-font-size)'
|
||||||
|
}}
|
||||||
|
className="flex-center"
|
||||||
|
>
|
||||||
|
{intl.formatMessage({ id: 'common.select.count' }, { count: count })}
|
||||||
|
</TagWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
{...restProps}
|
||||||
|
options={options}
|
||||||
|
maxTagCount={0}
|
||||||
|
dropdownRender={dropdownRender}
|
||||||
|
optionRender={optionRender}
|
||||||
|
menuItemSelectedIcon={false}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
tagRender={TagRender}
|
||||||
|
></Select>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SimpleSelect;
|
||||||
@@ -97,7 +97,7 @@ export default {
|
|||||||
'common.form.field.input.required': 'required',
|
'common.form.field.input.required': 'required',
|
||||||
'common.form.field.select.required': 'required',
|
'common.form.field.select.required': 'required',
|
||||||
'common.select.option': 'All',
|
'common.select.option': 'All',
|
||||||
'common.checbox.all': 'All',
|
'common.checbox.all': 'Select all',
|
||||||
'common.select.all': 'All {type}',
|
'common.select.all': 'All {type}',
|
||||||
'common.data.unkonwn': 'Unknown',
|
'common.data.unkonwn': 'Unknown',
|
||||||
'common.data.none': 'No Data',
|
'common.data.none': 'No Data',
|
||||||
@@ -250,5 +250,6 @@ export default {
|
|||||||
'Oops! Something went wrong. Try refreshing the page.',
|
'Oops! Something went wrong. Try refreshing the page.',
|
||||||
'common.tips.escape.disable':
|
'common.tips.escape.disable':
|
||||||
'Click Cancel or the X at the top right to close.',
|
'Click Cancel or the X at the top right to close.',
|
||||||
'common.button.clearSelection': 'Clear Selection'
|
'common.button.clearSelection': 'Clear Selection',
|
||||||
|
'common.select.count': '{count} selected'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -250,7 +250,8 @@ export default {
|
|||||||
'Oops! Something went wrong. Try refreshing the page.',
|
'Oops! Something went wrong. Try refreshing the page.',
|
||||||
'common.tips.escape.disable':
|
'common.tips.escape.disable':
|
||||||
'Click Cancel or the X at the top right to close.',
|
'Click Cancel or the X at the top right to close.',
|
||||||
'common.button.clearSelection': 'Clear Selection'
|
'common.button.clearSelection': 'Clear Selection',
|
||||||
|
'common.select.count': '{count} selected'
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
@@ -267,5 +268,6 @@ export default {
|
|||||||
// 11. 'common.page.wentwrong': 'Something went wrong.',
|
// 11. 'common.page.wentwrong': 'Something went wrong.',
|
||||||
// 12. 'common.page.refresh.tips': 'Oops! Something went wrong. Try refreshing the page.'
|
// 12. 'common.page.refresh.tips': 'Oops! Something went wrong. Try refreshing the page.'
|
||||||
// 13. 'common.tips.escape.disable': 'Click Cancel or the X at the top right to close.'
|
// 13. 'common.tips.escape.disable': 'Click Cancel or the X at the top right to close.'
|
||||||
// 14. 'common.button.clearSelection': 'Clear Selection'
|
// 14. 'common.button.clearSelection': 'Clear Selection',
|
||||||
|
// 15. 'common.select.count': '{count} selected'
|
||||||
// ========== End of To-Do List ==========
|
// ========== End of To-Do List ==========
|
||||||
|
|||||||
@@ -249,9 +249,11 @@ export default {
|
|||||||
'Упс! Что-то пошло не так. Попробуйте обновить страницу.',
|
'Упс! Что-то пошло не так. Попробуйте обновить страницу.',
|
||||||
'common.tips.escape.disable':
|
'common.tips.escape.disable':
|
||||||
'Чтобы закрыть, нажмите "Отмена" или крестик (X) в правом верхнем углу.',
|
'Чтобы закрыть, нажмите "Отмена" или крестик (X) в правом верхнем углу.',
|
||||||
'common.button.clearSelection': 'Clear Selection'
|
'common.button.clearSelection': 'Clear Selection',
|
||||||
|
'common.select.count': '{count} selected'
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
// 1. 'common.button.clearSelection': 'Clear Selection'
|
// 1. 'common.button.clearSelection': 'Clear Selection'
|
||||||
|
// 2. 'common.select.count': '{count} selected'
|
||||||
// ========== End of To-Do List ==========
|
// ========== End of To-Do List ==========
|
||||||
|
|||||||
@@ -243,5 +243,6 @@ export default {
|
|||||||
'common.page.wentwrong': '哎呀,出了点问题',
|
'common.page.wentwrong': '哎呀,出了点问题',
|
||||||
'common.page.refresh.tips': '出了点问题,试试刷新页面吧!',
|
'common.page.refresh.tips': '出了点问题,试试刷新页面吧!',
|
||||||
'common.tips.escape.disable': '请点击「取消」按钮或右上角 X 关闭窗口',
|
'common.tips.escape.disable': '请点击「取消」按钮或右上角 X 关闭窗口',
|
||||||
'common.button.clearSelection': '清除选择'
|
'common.button.clearSelection': '清除选择',
|
||||||
|
'common.select.count': '已选 {count} 项'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import dayjs from 'dayjs';
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { DASHBOARD_USAGE_API } from '../../apis';
|
import { DASHBOARD_USAGE_API } from '../../apis';
|
||||||
import { TableRow } from '../../config/types';
|
import { TableRow } from '../../config/types';
|
||||||
|
import FilterBar from './filter-bar';
|
||||||
import useUsageData from './use-usage-data';
|
import useUsageData from './use-usage-data';
|
||||||
|
|
||||||
const ExportData: React.FC<{
|
const ExportData: React.FC<{
|
||||||
@@ -17,7 +18,6 @@ const ExportData: React.FC<{
|
|||||||
const { open, onCancel } = props || {};
|
const { open, onCancel } = props || {};
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const {
|
const {
|
||||||
FilterBar,
|
|
||||||
init,
|
init,
|
||||||
setResult,
|
setResult,
|
||||||
loading,
|
loading,
|
||||||
@@ -25,7 +25,11 @@ const ExportData: React.FC<{
|
|||||||
userList,
|
userList,
|
||||||
modelList,
|
modelList,
|
||||||
query,
|
query,
|
||||||
setQuery
|
setQuery,
|
||||||
|
handleExport,
|
||||||
|
handleDateChange,
|
||||||
|
handleUsersChange,
|
||||||
|
handleModelsChange
|
||||||
} = useUsageData<{
|
} = useUsageData<{
|
||||||
items: TableRow[];
|
items: TableRow[];
|
||||||
}>({
|
}>({
|
||||||
@@ -169,7 +173,16 @@ const ExportData: React.FC<{
|
|||||||
></ModalFooter>
|
></ModalFooter>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<FilterBar></FilterBar>
|
<FilterBar
|
||||||
|
disabledDate={false}
|
||||||
|
url={DASHBOARD_USAGE_API}
|
||||||
|
query={query}
|
||||||
|
userList={userList}
|
||||||
|
modelList={modelList}
|
||||||
|
handleDateChange={handleDateChange}
|
||||||
|
handleUsersChange={handleUsersChange}
|
||||||
|
handleModelsChange={handleModelsChange}
|
||||||
|
></FilterBar>
|
||||||
<Table
|
<Table
|
||||||
columns={exportTableColumns}
|
columns={exportTableColumns}
|
||||||
tableLayout={'auto'}
|
tableLayout={'auto'}
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
import SimpleSelect from '@/components/seal-form/simple-select';
|
||||||
|
import { DownloadOutlined } from '@ant-design/icons';
|
||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { Button, DatePicker, Tooltip } from 'antd';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { DASHBOARD_STATS_API } from '../../apis';
|
||||||
|
import useRangePickerPreset from '../../hooks/use-rangepicker-preset';
|
||||||
|
|
||||||
|
const DefaultDateConfig = {
|
||||||
|
maxRange: 60,
|
||||||
|
defaultRange: 29
|
||||||
|
};
|
||||||
|
|
||||||
|
const FilterWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
.selection {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
.ant-select-selection-overflow-item > span {
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface FilterBarProps {
|
||||||
|
query: any;
|
||||||
|
userList: any[];
|
||||||
|
modelList: any[];
|
||||||
|
handleDateChange: (dates: any, dateStrings: [string, string]) => void;
|
||||||
|
handleUsersChange: (value: any) => void;
|
||||||
|
handleModelsChange: (value: any) => void;
|
||||||
|
handleExport?: () => void;
|
||||||
|
url: string;
|
||||||
|
disabledDate?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FilterBar: React.FC<FilterBarProps> = (props) => {
|
||||||
|
const {
|
||||||
|
query,
|
||||||
|
userList,
|
||||||
|
modelList,
|
||||||
|
handleDateChange,
|
||||||
|
handleUsersChange,
|
||||||
|
handleModelsChange,
|
||||||
|
handleExport,
|
||||||
|
url,
|
||||||
|
disabledDate
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const { disabledRangeDaysDate, rangePresets } = useRangePickerPreset({
|
||||||
|
range: DefaultDateConfig.maxRange,
|
||||||
|
disabledDate: disabledDate
|
||||||
|
});
|
||||||
|
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const filterOptions = (inputValue: any, option: any) => {
|
||||||
|
return option.label?.toLowerCase().includes(inputValue.toLowerCase());
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FilterWrapper>
|
||||||
|
<div className="selection">
|
||||||
|
<DatePicker.RangePicker
|
||||||
|
maxDate={dayjs()}
|
||||||
|
defaultValue={[
|
||||||
|
dayjs().add(-DefaultDateConfig.defaultRange, 'd'),
|
||||||
|
dayjs()
|
||||||
|
]}
|
||||||
|
disabledDate={disabledRangeDaysDate}
|
||||||
|
presets={rangePresets}
|
||||||
|
allowClear={false}
|
||||||
|
style={{ width: 220 }}
|
||||||
|
value={[dayjs(query.start_date), dayjs(query.end_date)]}
|
||||||
|
onChange={handleDateChange}
|
||||||
|
></DatePicker.RangePicker>
|
||||||
|
<SimpleSelect
|
||||||
|
allowClear
|
||||||
|
showSearch
|
||||||
|
mode="multiple"
|
||||||
|
options={userList}
|
||||||
|
maxTagCount={0}
|
||||||
|
filterOption={filterOptions}
|
||||||
|
placeholder={intl.formatMessage({
|
||||||
|
id: 'dashboard.usage.selectuser'
|
||||||
|
})}
|
||||||
|
style={{ maxWidth: 200, minWidth: 160 }}
|
||||||
|
value={query.user_ids}
|
||||||
|
onChange={handleUsersChange}
|
||||||
|
></SimpleSelect>
|
||||||
|
<SimpleSelect
|
||||||
|
allowClear
|
||||||
|
showSearch
|
||||||
|
mode="multiple"
|
||||||
|
options={modelList}
|
||||||
|
maxTagCount={0}
|
||||||
|
filterOption={filterOptions}
|
||||||
|
placeholder={intl.formatMessage({
|
||||||
|
id: 'dashboard.usage.selectmodel'
|
||||||
|
})}
|
||||||
|
value={query.model_ids}
|
||||||
|
style={{ maxWidth: 200, minWidth: 160 }}
|
||||||
|
onChange={handleModelsChange}
|
||||||
|
></SimpleSelect>
|
||||||
|
{url === DASHBOARD_STATS_API && (
|
||||||
|
<Tooltip title={intl.formatMessage({ id: 'common.button.export' })}>
|
||||||
|
<Button icon={<DownloadOutlined />} onClick={handleExport}></Button>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</FilterWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FilterBar;
|
||||||
@@ -8,6 +8,7 @@ import { baseColorMap } from '../../config';
|
|||||||
import { DashboardContext } from '../../config/dashboard-context';
|
import { DashboardContext } from '../../config/dashboard-context';
|
||||||
import { DashboardUsageData } from '../../config/types';
|
import { DashboardUsageData } from '../../config/types';
|
||||||
import ExportData from './export-data';
|
import ExportData from './export-data';
|
||||||
|
import FilterBar from './filter-bar';
|
||||||
import RequestTokenInner from './request-token-inner';
|
import RequestTokenInner from './request-token-inner';
|
||||||
import TopUser from './top-user';
|
import TopUser from './top-user';
|
||||||
import useUsageData from './use-usage-data';
|
import useUsageData from './use-usage-data';
|
||||||
@@ -21,16 +22,27 @@ const UsageInner: FC<{ maxWidth: number }> = ({ maxWidth }) => {
|
|||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { model_usage } = useContext(DashboardContext);
|
const { model_usage } = useContext(DashboardContext);
|
||||||
|
|
||||||
const { usageData, handleOnCancel, handleExport, FilterBar, init, open } =
|
const {
|
||||||
useUsageData<DashboardUsageData>({
|
usageData,
|
||||||
url: DASHBOARD_STATS_API,
|
query,
|
||||||
disabledDate: true,
|
userList,
|
||||||
defaultData: {
|
modelList,
|
||||||
api_request_history: [],
|
handleOnCancel,
|
||||||
completion_token_history: [],
|
init,
|
||||||
prompt_token_history: []
|
handleExport,
|
||||||
}
|
handleDateChange,
|
||||||
});
|
handleUsersChange,
|
||||||
|
handleModelsChange,
|
||||||
|
open
|
||||||
|
} = useUsageData<DashboardUsageData>({
|
||||||
|
url: DASHBOARD_STATS_API,
|
||||||
|
disabledDate: true,
|
||||||
|
defaultData: {
|
||||||
|
api_request_history: [],
|
||||||
|
completion_token_history: [],
|
||||||
|
prompt_token_history: []
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const topUserData = useMemo(() => {
|
const topUserData = useMemo(() => {
|
||||||
// top 10 users
|
// top 10 users
|
||||||
@@ -104,10 +116,19 @@ const UsageInner: FC<{ maxWidth: number }> = ({ maxWidth }) => {
|
|||||||
<TitleWrapper>
|
<TitleWrapper>
|
||||||
{intl.formatMessage({ id: 'dashboard.usage' })}
|
{intl.formatMessage({ id: 'dashboard.usage' })}
|
||||||
</TitleWrapper>
|
</TitleWrapper>
|
||||||
<FilterBar></FilterBar>
|
<FilterBar
|
||||||
|
url={DASHBOARD_STATS_API}
|
||||||
|
query={query}
|
||||||
|
userList={userList}
|
||||||
|
modelList={modelList}
|
||||||
|
disabledDate={true}
|
||||||
|
handleDateChange={handleDateChange}
|
||||||
|
handleUsersChange={handleUsersChange}
|
||||||
|
handleModelsChange={handleModelsChange}
|
||||||
|
handleExport={handleExport}
|
||||||
|
></FilterBar>
|
||||||
</div>
|
</div>
|
||||||
<RequestTokenInner
|
<RequestTokenInner
|
||||||
onExport={handleExport}
|
|
||||||
requestData={usageData?.requestTokenData.requestData}
|
requestData={usageData?.requestTokenData.requestData}
|
||||||
xAxisData={usageData?.requestTokenData.xAxisData}
|
xAxisData={usageData?.requestTokenData.xAxisData}
|
||||||
tokenData={usageData?.requestTokenData.tokenData}
|
tokenData={usageData?.requestTokenData.tokenData}
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ const CardWrapperBox = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
interface RequestTokenInnerProps {
|
interface RequestTokenInnerProps {
|
||||||
onExport?: () => void;
|
|
||||||
requestData: {
|
requestData: {
|
||||||
name: string;
|
name: string;
|
||||||
color: string;
|
color: string;
|
||||||
|
|||||||
@@ -1,36 +1,11 @@
|
|||||||
import useSelectRender from '@/components/seal-form/hooks/use-select-render';
|
|
||||||
import { queryModelsList } from '@/pages/llmodels/apis';
|
import { queryModelsList } from '@/pages/llmodels/apis';
|
||||||
import { ListItem as ModelListItem } from '@/pages/llmodels/config/types';
|
import { ListItem as ModelListItem } from '@/pages/llmodels/config/types';
|
||||||
import { queryUsersList } from '@/pages/users/apis';
|
import { queryUsersList } from '@/pages/users/apis';
|
||||||
import { DownloadOutlined } from '@ant-design/icons';
|
|
||||||
import { useIntl } from '@umijs/max';
|
|
||||||
import { Button, DatePicker, Select, Tooltip } from 'antd';
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import styled from 'styled-components';
|
import { DASHBOARD_USAGE_API, queryDashboardUsageData } from '../../apis';
|
||||||
import {
|
|
||||||
DASHBOARD_STATS_API,
|
|
||||||
DASHBOARD_USAGE_API,
|
|
||||||
queryDashboardUsageData
|
|
||||||
} from '../../apis';
|
|
||||||
import { baseColorMap } from '../../config';
|
import { baseColorMap } from '../../config';
|
||||||
import useRangePickerPreset from '../../hooks/use-rangepicker-preset';
|
|
||||||
|
|
||||||
const FilterWrapper = styled.div`
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 0px;
|
|
||||||
.selection {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
.ant-select-selection-overflow-item > span {
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
interface RequestTokenData {
|
interface RequestTokenData {
|
||||||
requestData: {
|
requestData: {
|
||||||
@@ -99,20 +74,8 @@ export default function useUseageData<T>(config: {
|
|||||||
defaultData?: T;
|
defaultData?: T;
|
||||||
disabledDate?: boolean;
|
disabledDate?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const { url, defaultData, disabledDate = false } = config || {};
|
const { url, defaultData } = config || {};
|
||||||
const intl = useIntl();
|
|
||||||
const { TagRender } = useSelectRender({
|
|
||||||
maxTagWidth: 100,
|
|
||||||
filled: true,
|
|
||||||
style: {
|
|
||||||
height: 24,
|
|
||||||
lineHeight: '24px'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const { disabledRangeDaysDate, rangePresets } = useRangePickerPreset({
|
|
||||||
range: DefaultDateConfig.maxRange,
|
|
||||||
disabledDate: disabledDate
|
|
||||||
});
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [result, setResult] = useState<{
|
const [result, setResult] = useState<{
|
||||||
start_date?: string;
|
start_date?: string;
|
||||||
@@ -356,69 +319,6 @@ export default function useUseageData<T>(config: {
|
|||||||
fetchUsersList();
|
fetchUsersList();
|
||||||
};
|
};
|
||||||
|
|
||||||
const FilterBar = () => {
|
|
||||||
const filterOptions = (inputValue: any, option: any) => {
|
|
||||||
return option.label?.toLowerCase().includes(inputValue.toLowerCase());
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<FilterWrapper>
|
|
||||||
<div className="selection">
|
|
||||||
<DatePicker.RangePicker
|
|
||||||
maxDate={dayjs()}
|
|
||||||
defaultValue={[
|
|
||||||
dayjs().add(-DefaultDateConfig.defaultRange, 'd'),
|
|
||||||
dayjs()
|
|
||||||
]}
|
|
||||||
disabledDate={disabledRangeDaysDate}
|
|
||||||
presets={rangePresets}
|
|
||||||
allowClear={false}
|
|
||||||
style={{ width: 220 }}
|
|
||||||
value={[dayjs(query.start_date), dayjs(query.end_date)]}
|
|
||||||
onChange={handleDateChange}
|
|
||||||
></DatePicker.RangePicker>
|
|
||||||
<Select
|
|
||||||
allowClear
|
|
||||||
showSearch
|
|
||||||
mode="multiple"
|
|
||||||
options={userList}
|
|
||||||
maxTagCount={1}
|
|
||||||
filterOption={filterOptions}
|
|
||||||
tagRender={TagRender}
|
|
||||||
placeholder={intl.formatMessage({
|
|
||||||
id: 'dashboard.usage.selectuser'
|
|
||||||
})}
|
|
||||||
style={{ maxWidth: 200, minWidth: 160 }}
|
|
||||||
value={query.user_ids}
|
|
||||||
onChange={handleUsersChange}
|
|
||||||
></Select>
|
|
||||||
<Select
|
|
||||||
allowClear
|
|
||||||
showSearch
|
|
||||||
mode="multiple"
|
|
||||||
options={modelList}
|
|
||||||
maxTagCount={1}
|
|
||||||
tagRender={TagRender}
|
|
||||||
filterOption={filterOptions}
|
|
||||||
placeholder={intl.formatMessage({
|
|
||||||
id: 'dashboard.usage.selectmodel'
|
|
||||||
})}
|
|
||||||
value={query.model_ids}
|
|
||||||
style={{ maxWidth: 200, minWidth: 160 }}
|
|
||||||
onChange={handleModelsChange}
|
|
||||||
></Select>
|
|
||||||
{url === DASHBOARD_STATS_API && (
|
|
||||||
<Tooltip title={intl.formatMessage({ id: 'common.button.export' })}>
|
|
||||||
<Button
|
|
||||||
icon={<DownloadOutlined />}
|
|
||||||
onClick={handleExport}
|
|
||||||
></Button>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</FilterWrapper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
usageData,
|
usageData,
|
||||||
result,
|
result,
|
||||||
@@ -430,8 +330,10 @@ export default function useUseageData<T>(config: {
|
|||||||
setQuery,
|
setQuery,
|
||||||
init,
|
init,
|
||||||
setResult,
|
setResult,
|
||||||
FilterBar,
|
|
||||||
handleOnCancel,
|
handleOnCancel,
|
||||||
handleExport
|
handleExport,
|
||||||
|
handleDateChange,
|
||||||
|
handleUsersChange,
|
||||||
|
handleModelsChange
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const SpinWrapper = styled.div`
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
height: 400px;
|
max-height: 400px;
|
||||||
right: 0;
|
right: 0;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -551,7 +551,8 @@ export const useCheckCompatibility = () => {
|
|||||||
const gpuSelector = generateGPUIds(data.values);
|
const gpuSelector = generateGPUIds(data.values);
|
||||||
return await handleDoEvalute({
|
return await handleDoEvalute({
|
||||||
...data.values,
|
...data.values,
|
||||||
...gpuSelector
|
...gpuSelector,
|
||||||
|
replicas: allValues.replicas || 0
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user