refactor: table filters ux
This commit is contained in:
@@ -11,7 +11,7 @@ export type { OverlayScrollerOptions };
|
|||||||
|
|
||||||
export const OverlayScroller: React.FC<
|
export const OverlayScroller: React.FC<
|
||||||
OverlayScrollerOptions & {
|
OverlayScrollerOptions & {
|
||||||
maxHeight?: number;
|
maxHeight?: number | string;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
styles?: {
|
styles?: {
|
||||||
wrapper?: React.CSSProperties;
|
wrapper?: React.CSSProperties;
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
.wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.count {
|
||||||
|
display: flex;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
background-color: var(--ant-color-bg-text-active);
|
||||||
|
color: var(--ant-color-text-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttonWrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.close-btn {
|
||||||
|
border-radius: 50%;
|
||||||
|
color: var(--ant-color-text-quaternary);
|
||||||
|
display: none;
|
||||||
|
font-size: 16px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--ant-color-text-tertiary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.count {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import DropDownActions from '@/components/drop-down-actions';
|
import DropDownActions from '@/components/drop-down-actions';
|
||||||
import {
|
import {
|
||||||
|
CloseCircleFilled,
|
||||||
DeleteOutlined,
|
DeleteOutlined,
|
||||||
DownOutlined,
|
DownOutlined,
|
||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
@@ -9,7 +10,9 @@ import {
|
|||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Button, Input, Space } from 'antd';
|
import { Button, Input, Space } from 'antd';
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
|
import IconFont from '../icon-font';
|
||||||
import BaseSelect from '../seal-form/base/select';
|
import BaseSelect from '../seal-form/base/select';
|
||||||
|
import filtersButtonCss from './filters-button.less';
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
|
||||||
type PageToolsProps = {
|
type PageToolsProps = {
|
||||||
@@ -20,6 +23,49 @@ type PageToolsProps = {
|
|||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const FiltersButton = ({
|
||||||
|
onClick,
|
||||||
|
onClear,
|
||||||
|
count
|
||||||
|
}: {
|
||||||
|
onClick: () => void;
|
||||||
|
onClear: () => void;
|
||||||
|
count?: number;
|
||||||
|
}) => {
|
||||||
|
const handleClear = (e: React.MouseEvent) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onClear();
|
||||||
|
};
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={filtersButtonCss.buttonWrapper}>
|
||||||
|
<Button
|
||||||
|
onClick={onClick}
|
||||||
|
style={{
|
||||||
|
color: 'var(--ant-color-text-tertiary)'
|
||||||
|
}}
|
||||||
|
icon={
|
||||||
|
<IconFont type="icon-filter-list" style={{ fontSize: 14 }}></IconFont>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<span className={filtersButtonCss.wrapper}>
|
||||||
|
<span>{intl.formatMessage({ id: 'common.filter.label' })}</span>
|
||||||
|
{!!count && <span className={filtersButtonCss.count}>{count}</span>}
|
||||||
|
{!!count && (
|
||||||
|
<span
|
||||||
|
className={filtersButtonCss['close-btn']}
|
||||||
|
onClick={handleClear}
|
||||||
|
>
|
||||||
|
<CloseCircleFilled />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const PageTools: React.FC<PageToolsProps> = (props) => {
|
const PageTools: React.FC<PageToolsProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
left,
|
left,
|
||||||
@@ -77,6 +123,12 @@ interface FilterBarProps {
|
|||||||
showDeleteButton?: boolean;
|
showDeleteButton?: boolean;
|
||||||
right?: React.ReactNode;
|
right?: React.ReactNode;
|
||||||
left?: React.ReactNode;
|
left?: React.ReactNode;
|
||||||
|
filtersButtonProps?: {
|
||||||
|
show: boolean;
|
||||||
|
count: number;
|
||||||
|
onClick: () => void;
|
||||||
|
onClear: () => void;
|
||||||
|
};
|
||||||
widths?: {
|
widths?: {
|
||||||
input?: number;
|
input?: number;
|
||||||
select?: number;
|
select?: number;
|
||||||
@@ -103,13 +155,21 @@ export const FilterBar: React.FC<FilterBarProps> = (props) => {
|
|||||||
selectHolder,
|
selectHolder,
|
||||||
right,
|
right,
|
||||||
left,
|
left,
|
||||||
widths
|
widths,
|
||||||
|
filtersButtonProps
|
||||||
} = props;
|
} = props;
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const renderLeft = () => {
|
const renderLeft = () => {
|
||||||
return (
|
return (
|
||||||
<Space>
|
<Space>
|
||||||
|
{filtersButtonProps?.show && (
|
||||||
|
<FiltersButton
|
||||||
|
onClear={filtersButtonProps.onClear}
|
||||||
|
onClick={filtersButtonProps.onClick}
|
||||||
|
count={filtersButtonProps.count}
|
||||||
|
></FiltersButton>
|
||||||
|
)}
|
||||||
<Input
|
<Input
|
||||||
prefix={
|
prefix={
|
||||||
<SearchOutlined
|
<SearchOutlined
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { SealColumnProps, SealTableProps } from './types';
|
|||||||
import useSorter from './use-sorter';
|
import useSorter from './use-sorter';
|
||||||
|
|
||||||
const Wrapper = styled.div<{ $token: any }>`
|
const Wrapper = styled.div<{ $token: any }>`
|
||||||
|
width: 100%;
|
||||||
--ant-table-cell-padding-inline: ${(props) =>
|
--ant-table-cell-padding-inline: ${(props) =>
|
||||||
props.$token.cellPaddingInline}px;
|
props.$token.cellPaddingInline}px;
|
||||||
--ant-table-cell-padding-block: ${(props) => props.$token.cellPaddingBlock}px;
|
--ant-table-cell-padding-block: ${(props) => props.$token.cellPaddingBlock}px;
|
||||||
|
|||||||
@@ -281,5 +281,6 @@ export default {
|
|||||||
'Not enabled yet. Will be enabled after deployment. ',
|
'Not enabled yet. Will be enabled after deployment. ',
|
||||||
'models.table.instance.benchmark': 'Run Benchmark',
|
'models.table.instance.benchmark': 'Run Benchmark',
|
||||||
'models.table.modelView': 'Model List',
|
'models.table.modelView': 'Model List',
|
||||||
'models.table.instanceView': 'Instance List'
|
'models.table.instanceView': 'Instance List',
|
||||||
|
'models.table.category': 'Category'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -281,7 +281,8 @@ export default {
|
|||||||
'Not enabled yet. Will be enabled after deployment. ',
|
'Not enabled yet. Will be enabled after deployment. ',
|
||||||
'models.table.instance.benchmark': 'Run Benchmark',
|
'models.table.instance.benchmark': 'Run Benchmark',
|
||||||
'models.table.modelView': 'Model List',
|
'models.table.modelView': 'Model List',
|
||||||
'models.table.instanceView': 'Instance List'
|
'models.table.instanceView': 'Instance List',
|
||||||
|
'models.table.category': 'Category'
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
@@ -383,5 +384,6 @@ export default {
|
|||||||
// 77. 'models.form.enableModelRoute': 'Enable Model Route',
|
// 77. 'models.form.enableModelRoute': 'Enable Model Route',
|
||||||
// 78. 'models.form.enableModelRoute.tips': 'Enable Model Route',
|
// 78. 'models.form.enableModelRoute.tips': 'Enable Model Route',
|
||||||
// 79. 'models.table.modelView': 'Model View',
|
// 79. 'models.table.modelView': 'Model View',
|
||||||
// 80. 'models.table.instanceView': 'Instance View'
|
// 80. 'models.table.instanceView': 'Instance View',
|
||||||
|
// 81. 'models.table.category': 'Category'
|
||||||
// ========== End of To-Do List ==========
|
// ========== End of To-Do List ==========
|
||||||
|
|||||||
@@ -285,7 +285,8 @@ export default {
|
|||||||
'Not enabled yet. Will be enabled after deployment. ',
|
'Not enabled yet. Will be enabled after deployment. ',
|
||||||
'models.table.instance.benchmark': 'Run Benchmark',
|
'models.table.instance.benchmark': 'Run Benchmark',
|
||||||
'models.table.modelView': 'Model List',
|
'models.table.modelView': 'Model List',
|
||||||
'models.table.instanceView': 'Instance List'
|
'models.table.instanceView': 'Instance List',
|
||||||
|
'models.table.category': 'Category'
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
@@ -297,5 +298,6 @@ export default {
|
|||||||
// 4. 'models.form.backend.vllm': 'Built-in support for NVIDIA, AMD, Ascend, Hygon, Moore Threads, Iluvatar, MetaX, T-Head PPU devices.',
|
// 4. 'models.form.backend.vllm': 'Built-in support for NVIDIA, AMD, Ascend, Hygon, Moore Threads, Iluvatar, MetaX, T-Head PPU devices.',
|
||||||
// 5. 'models.form.backend.sglang': 'Built-in support for NVIDIA, AMD, Ascend, Moore Threads, MetaX, T-Head PPU devices.',
|
// 5. 'models.form.backend.sglang': 'Built-in support for NVIDIA, AMD, Ascend, Moore Threads, MetaX, T-Head PPU devices.',
|
||||||
// 6. 'models.table.modelView': 'Model List',
|
// 6. 'models.table.modelView': 'Model List',
|
||||||
// 7. 'models.table.instanceView': 'Instance List'
|
// 7. 'models.table.instanceView': 'Instance List',
|
||||||
|
// 8. 'models.table.category': 'Category'
|
||||||
// ========== End of To-Do List ==========
|
// ========== End of To-Do List ==========
|
||||||
|
|||||||
@@ -281,10 +281,12 @@ export default {
|
|||||||
'Henüz etkinleştirilmedi. Dağıtımdan sonra etkinleştirilecektir. ',
|
'Henüz etkinleştirilmedi. Dağıtımdan sonra etkinleştirilecektir. ',
|
||||||
'models.table.instance.benchmark': 'Kıyaslama Çalıştır',
|
'models.table.instance.benchmark': 'Kıyaslama Çalıştır',
|
||||||
'models.table.modelView': 'Model List',
|
'models.table.modelView': 'Model List',
|
||||||
'models.table.instanceView': 'Instance List'
|
'models.table.instanceView': 'Instance List',
|
||||||
|
'models.table.category': 'Category'
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
// 1. 'models.table.modelView': 'Model List',
|
// 1. 'models.table.modelView': 'Model List',
|
||||||
// 2. 'models.table.instanceView': 'Instance List',
|
// 2. 'models.table.instanceView': 'Instance List',
|
||||||
|
// 3. 'models.table.category': 'Category'
|
||||||
// ========== End of To-Do List ==========
|
// ========== End of To-Do List ==========
|
||||||
|
|||||||
@@ -265,5 +265,6 @@ export default {
|
|||||||
'models.form.backend.helperText': '该社区后端暂未启用,部署后将自动启用',
|
'models.form.backend.helperText': '该社区后端暂未启用,部署后将自动启用',
|
||||||
'models.table.instance.benchmark': '运行基准测试',
|
'models.table.instance.benchmark': '运行基准测试',
|
||||||
'models.table.modelView': '模型列表',
|
'models.table.modelView': '模型列表',
|
||||||
'models.table.instanceView': '实例列表'
|
'models.table.instanceView': '实例列表',
|
||||||
|
'models.table.category': '类别'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,238 +0,0 @@
|
|||||||
import IconFont from '@/components/icon-font';
|
|
||||||
import OverlayScroller from '@/components/overlay-scroller';
|
|
||||||
import { CloseCircleFilled } from '@ant-design/icons';
|
|
||||||
import { useIntl } from '@umijs/max';
|
|
||||||
import { Button, Divider, Form, Popover } from 'antd';
|
|
||||||
import React, { useState } from 'react';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
const Container = styled.div`
|
|
||||||
padding: 12px 8px;
|
|
||||||
.title {
|
|
||||||
font-weight: 500;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
.btn-wrapper {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding-top: 24px;
|
|
||||||
padding-right: 8px;
|
|
||||||
}
|
|
||||||
.buttons {
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Filters = styled.span`
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
font-size: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
.count {
|
|
||||||
display: flex;
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
border-radius: 50%;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 500;
|
|
||||||
background-color: var(--ant-color-bg-text-active);
|
|
||||||
color: var(--ant-color-text-secondary);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const ButtonWrapper = styled.div`
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
position: relative;
|
|
||||||
.close-btn {
|
|
||||||
border-radius: 50%;
|
|
||||||
color: var(--ant-color-text-quaternary);
|
|
||||||
&:hover {
|
|
||||||
color: var(--ant-color-text-tertiary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
.close-btn {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const FilterForm: React.FC<
|
|
||||||
React.PropsWithChildren & {
|
|
||||||
width?: number;
|
|
||||||
contentHeight?: number;
|
|
||||||
initialValues?: any;
|
|
||||||
hasFilters?: boolean;
|
|
||||||
placement?: 'bottomLeft' | 'bottomRight' | 'topLeft' | 'topRight';
|
|
||||||
onClose?: () => void;
|
|
||||||
onValuesChange?: (ChangeValues: any, allValues: any) => void;
|
|
||||||
}
|
|
||||||
> = ({
|
|
||||||
children,
|
|
||||||
width = 300,
|
|
||||||
contentHeight = 400,
|
|
||||||
initialValues = {},
|
|
||||||
placement = 'bottomLeft',
|
|
||||||
onClose,
|
|
||||||
onValuesChange
|
|
||||||
}) => {
|
|
||||||
const intl = useIntl();
|
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
const [hasFilters, setHasFilters] = useState(false);
|
|
||||||
const [form] = Form.useForm();
|
|
||||||
|
|
||||||
const handleToggle = () => {
|
|
||||||
setOpen(!open);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOpenChange = (isOpen: boolean) => {
|
|
||||||
setOpen(isOpen);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOnReset = () => {
|
|
||||||
form.resetFields(Object.keys(initialValues));
|
|
||||||
setOpen(false);
|
|
||||||
setHasFilters(false);
|
|
||||||
form.resetFields();
|
|
||||||
onValuesChange?.({}, form.getFieldsValue());
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOnClose = () => {
|
|
||||||
setOpen(false);
|
|
||||||
onClose?.();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOnValuesChange = (changedValues: any, allValues: any) => {
|
|
||||||
onValuesChange?.(changedValues, allValues);
|
|
||||||
const hasActiveFilters = Object.values(allValues).some(
|
|
||||||
(value) => value !== undefined && value !== null && value !== ''
|
|
||||||
);
|
|
||||||
setHasFilters(hasActiveFilters);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOnClear = () => {
|
|
||||||
handleOnReset();
|
|
||||||
};
|
|
||||||
|
|
||||||
const filtersCount = Object.values(form.getFieldsValue()).filter(
|
|
||||||
(value) => value !== undefined && value !== null && value !== ''
|
|
||||||
).length;
|
|
||||||
|
|
||||||
const renderFooter = () => {
|
|
||||||
return (
|
|
||||||
<div className="btn-wrapper">
|
|
||||||
<Button size="middle" onClick={handleOnReset}>
|
|
||||||
{intl.formatMessage({ id: 'common.button.reset' })}
|
|
||||||
</Button>
|
|
||||||
<div className="buttons">
|
|
||||||
<Button size="middle" type="primary" onClick={handleOnClose}>
|
|
||||||
{intl.formatMessage({ id: 'common.button.close' })}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Popover
|
|
||||||
open={open}
|
|
||||||
trigger={['click', 'hover']}
|
|
||||||
arrow={false}
|
|
||||||
placement={placement}
|
|
||||||
content={
|
|
||||||
<Container>
|
|
||||||
<OverlayScroller
|
|
||||||
maxHeight={contentHeight}
|
|
||||||
styles={{
|
|
||||||
wrapper: {
|
|
||||||
paddingInline: 8
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Form
|
|
||||||
onValuesChange={handleOnValuesChange}
|
|
||||||
initialValues={initialValues}
|
|
||||||
form={form}
|
|
||||||
layout="vertical"
|
|
||||||
styles={{
|
|
||||||
label: {
|
|
||||||
lineHeight: 1,
|
|
||||||
height: 'auto',
|
|
||||||
marginBottom: 8,
|
|
||||||
fontWeight: 500
|
|
||||||
},
|
|
||||||
content: {
|
|
||||||
minHeight: 0
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</Form>
|
|
||||||
</OverlayScroller>
|
|
||||||
</Container>
|
|
||||||
}
|
|
||||||
onOpenChange={handleOpenChange}
|
|
||||||
styles={{
|
|
||||||
container: {
|
|
||||||
padding: 8,
|
|
||||||
border: '1px solid var(--ant-color-split)'
|
|
||||||
},
|
|
||||||
root: {
|
|
||||||
width: width
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ButtonWrapper>
|
|
||||||
<Divider orientation="vertical" style={{ height: 16 }} />
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
type="text"
|
|
||||||
variant="filled"
|
|
||||||
color="default"
|
|
||||||
shape="round"
|
|
||||||
>
|
|
||||||
<Filters>
|
|
||||||
<span
|
|
||||||
style={{
|
|
||||||
fontSize: 12,
|
|
||||||
color: 'var(--ant-color-text-secondary)',
|
|
||||||
fontWeight: 400
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
More Filters
|
|
||||||
</span>
|
|
||||||
{filtersCount > 0 ? (
|
|
||||||
<>
|
|
||||||
<span className="count">{filtersCount}</span>
|
|
||||||
<span className="close-btn">
|
|
||||||
<CloseCircleFilled
|
|
||||||
style={{ fontSize: 16 }}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
handleOnClear();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<IconFont
|
|
||||||
type="icon-filter-list"
|
|
||||||
style={{ fontSize: 14 }}
|
|
||||||
></IconFont>
|
|
||||||
)}
|
|
||||||
</Filters>
|
|
||||||
</Button>
|
|
||||||
</ButtonWrapper>
|
|
||||||
</Popover>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default FilterForm;
|
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
import OverlayScroller from '@/components/overlay-scroller';
|
||||||
|
import { CloseOutlined } from '@ant-design/icons';
|
||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { Button, Form } from 'antd';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import React, { forwardRef, useImperativeHandle } from 'react';
|
||||||
|
import filterFormCss from '../styles/filter-form.less';
|
||||||
|
|
||||||
|
const FilterForm: React.FC<
|
||||||
|
React.PropsWithChildren & {
|
||||||
|
ref?: any;
|
||||||
|
width?: number;
|
||||||
|
contentHeight?: number | string;
|
||||||
|
initialValues?: any;
|
||||||
|
hasFilters?: boolean;
|
||||||
|
open?: boolean;
|
||||||
|
onClear?: () => void;
|
||||||
|
onClose?: () => void;
|
||||||
|
onValuesChange?: (ChangeValues: any, allValues: any) => void;
|
||||||
|
styles?: {
|
||||||
|
container?: React.CSSProperties;
|
||||||
|
wrapper?: React.CSSProperties;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
> = forwardRef(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
children,
|
||||||
|
open,
|
||||||
|
width = 300,
|
||||||
|
contentHeight = 400,
|
||||||
|
initialValues = {},
|
||||||
|
styles,
|
||||||
|
onClose,
|
||||||
|
onClear,
|
||||||
|
onValuesChange
|
||||||
|
},
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
|
const handleOnReset = () => {
|
||||||
|
form.resetFields(Object.keys(initialValues));
|
||||||
|
form.resetFields();
|
||||||
|
onValuesChange?.({}, form.getFieldsValue());
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOnClose = () => {
|
||||||
|
onClose?.();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOnValuesChange = (changedValues: any, allValues: any) => {
|
||||||
|
onValuesChange?.(changedValues, allValues);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOnClear = () => {
|
||||||
|
handleOnReset();
|
||||||
|
};
|
||||||
|
|
||||||
|
const filtersCount = Object.values(form.getFieldsValue()).filter(
|
||||||
|
(value) => value !== undefined && value !== null && value !== ''
|
||||||
|
).length;
|
||||||
|
|
||||||
|
const renderFooter = () => {
|
||||||
|
return (
|
||||||
|
<div className={filterFormCss['btn-wrapper']}>
|
||||||
|
<Button size="middle" onClick={handleOnReset}>
|
||||||
|
{intl.formatMessage({ id: 'common.button.reset' })}
|
||||||
|
</Button>
|
||||||
|
<div className={filterFormCss.buttons}>
|
||||||
|
<Button size="middle" type="primary" onClick={handleOnClose}>
|
||||||
|
{intl.formatMessage({ id: 'common.button.close' })}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
form,
|
||||||
|
reset: handleOnReset,
|
||||||
|
getValues: () => form.getFieldsValue(),
|
||||||
|
setValues: (values: any) => form.setFieldsValue(values)
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classNames(filterFormCss.wrapper, {
|
||||||
|
[filterFormCss.show]: open
|
||||||
|
})}
|
||||||
|
style={{
|
||||||
|
width: open ? width : 0,
|
||||||
|
height: contentHeight,
|
||||||
|
...styles?.wrapper
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{ width: width, ...styles?.container }}
|
||||||
|
className={filterFormCss.container}
|
||||||
|
>
|
||||||
|
<div className={filterFormCss.title}>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontWeight: 500,
|
||||||
|
color: 'var(--ant-color-text-tertiary)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{intl.formatMessage({ id: 'common.filter.label' })}
|
||||||
|
</span>
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
icon={<CloseOutlined />}
|
||||||
|
onClick={handleOnClose}
|
||||||
|
type="text"
|
||||||
|
style={{
|
||||||
|
color: 'var(--ant-color-text-secondary)'
|
||||||
|
}}
|
||||||
|
></Button>
|
||||||
|
</div>
|
||||||
|
<OverlayScroller
|
||||||
|
maxHeight={contentHeight}
|
||||||
|
styles={{
|
||||||
|
wrapper: {
|
||||||
|
paddingInline: 8
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Form
|
||||||
|
onValuesChange={handleOnValuesChange}
|
||||||
|
initialValues={initialValues}
|
||||||
|
form={form}
|
||||||
|
layout="vertical"
|
||||||
|
styles={{
|
||||||
|
label: {
|
||||||
|
lineHeight: 1,
|
||||||
|
height: 'auto',
|
||||||
|
marginBottom: 8,
|
||||||
|
fontWeight: 500
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
minHeight: 0
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Form>
|
||||||
|
</OverlayScroller>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export default FilterForm;
|
||||||
@@ -74,8 +74,11 @@ export const PageContainerInner: React.FC<
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const PageBox: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
const PageBox: React.FC<{
|
||||||
return <div>{children}</div>;
|
children: React.ReactNode;
|
||||||
|
style?: React.CSSProperties;
|
||||||
|
}> = ({ children, style }) => {
|
||||||
|
return <div style={style}>{children}</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PageBox;
|
export default PageBox;
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
.wrapper {
|
||||||
|
flex-shrink: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 0;
|
||||||
|
transition: all var(--ant-motion-duration-slow) var(--ant-motion-ease-in-out);
|
||||||
|
border-color: var(--ant-color-split);
|
||||||
|
|
||||||
|
&.show {
|
||||||
|
width: 232px;
|
||||||
|
border-right: 1px solid var(--ant-color-split);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding: 0 16px;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
margin-top: 8px;
|
||||||
|
padding-inline: 8px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--ant-color-text-tertiary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 24px;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -233,7 +233,7 @@ const BackendList = () => {
|
|||||||
marginBottom={22}
|
marginBottom={22}
|
||||||
marginTop={30}
|
marginTop={30}
|
||||||
widths={{
|
widths={{
|
||||||
input: 300
|
input: 230
|
||||||
}}
|
}}
|
||||||
actionItems={addActions}
|
actionItems={addActions}
|
||||||
actionType="dropdown"
|
actionType="dropdown"
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import BaseSelect from '@/components/seal-form/base/select';
|
import { FiltersButton } from '@/components/page-tools';
|
||||||
import { modelCategoriesMap } from '@/pages/llmodels/config';
|
import { modelCategoriesMap } from '@/pages/llmodels/config';
|
||||||
import { SearchOutlined, SyncOutlined } from '@ant-design/icons';
|
import { SearchOutlined, SyncOutlined } from '@ant-design/icons';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Button, Input, Space } from 'antd';
|
import { Button, Input, Space } from 'antd';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { profileOptions } from '../config';
|
|
||||||
|
|
||||||
export interface RightActionsProps {
|
export interface RightActionsProps {
|
||||||
handleInputChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
handleInputChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||||
@@ -13,12 +12,18 @@ export interface RightActionsProps {
|
|||||||
handleQueryChange: (value: any, option?: any) => void;
|
handleQueryChange: (value: any, option?: any) => void;
|
||||||
modelList?: Global.BaseOption<number, { categories: string[] }>[];
|
modelList?: Global.BaseOption<number, { categories: string[] }>[];
|
||||||
datasetList?: Global.BaseOption<string | number>[];
|
datasetList?: Global.BaseOption<string | number>[];
|
||||||
|
toggleFilters: () => void;
|
||||||
|
count?: number;
|
||||||
|
onClear: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const RightActions: React.FC<RightActionsProps> = ({
|
const RightActions: React.FC<RightActionsProps> = ({
|
||||||
handleInputChange,
|
handleInputChange,
|
||||||
handleSearch,
|
handleSearch,
|
||||||
handleQueryChange,
|
handleQueryChange,
|
||||||
|
toggleFilters,
|
||||||
|
count,
|
||||||
|
onClear,
|
||||||
modelList,
|
modelList,
|
||||||
datasetList
|
datasetList
|
||||||
}) => {
|
}) => {
|
||||||
@@ -44,6 +49,11 @@ const RightActions: React.FC<RightActionsProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Space>
|
<Space>
|
||||||
|
<FiltersButton
|
||||||
|
onClick={toggleFilters}
|
||||||
|
count={count}
|
||||||
|
onClear={onClear}
|
||||||
|
></FiltersButton>
|
||||||
<Input
|
<Input
|
||||||
prefix={
|
prefix={
|
||||||
<SearchOutlined
|
<SearchOutlined
|
||||||
@@ -53,11 +63,11 @@ const RightActions: React.FC<RightActionsProps> = ({
|
|||||||
placeholder={intl.formatMessage({
|
placeholder={intl.formatMessage({
|
||||||
id: 'common.filter.name'
|
id: 'common.filter.name'
|
||||||
})}
|
})}
|
||||||
style={{ width: 200 }}
|
style={{ width: 300 }}
|
||||||
allowClear
|
allowClear
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
></Input>
|
></Input>
|
||||||
<Input
|
{/* <Input
|
||||||
prefix={
|
prefix={
|
||||||
<SearchOutlined
|
<SearchOutlined
|
||||||
style={{ color: 'var(--ant-color-text-placeholder)' }}
|
style={{ color: 'var(--ant-color-text-placeholder)' }}
|
||||||
@@ -107,7 +117,7 @@ const RightActions: React.FC<RightActionsProps> = ({
|
|||||||
page: 1
|
page: 1
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
></BaseSelect>
|
></BaseSelect> */}
|
||||||
<Button
|
<Button
|
||||||
type="text"
|
type="text"
|
||||||
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
||||||
|
|||||||
@@ -0,0 +1,138 @@
|
|||||||
|
import BaseSelect from '@/components/seal-form/base/select';
|
||||||
|
import FilterForm from '@/pages/_components/filter-form';
|
||||||
|
import { modelCategoriesMap } from '@/pages/llmodels/config';
|
||||||
|
import { SearchOutlined } from '@ant-design/icons';
|
||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { Form, Input } from 'antd';
|
||||||
|
import { forwardRef, useImperativeHandle, useRef } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { profileOptions } from '../config';
|
||||||
|
|
||||||
|
const Content = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
`;
|
||||||
|
const Label = styled.span`
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 13px;
|
||||||
|
margin-block: 12px 8px;
|
||||||
|
color: var(--ant-color-text-tertiary);
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface FilterFormContentProps {
|
||||||
|
clusterList?: Global.BaseOption<number>[];
|
||||||
|
initialValues?: any;
|
||||||
|
open?: boolean;
|
||||||
|
ref?: any;
|
||||||
|
modelList?: Global.BaseOption<number, { categories: string[] }>[];
|
||||||
|
onClose?: () => void;
|
||||||
|
onClear?: () => void;
|
||||||
|
onValuesChange: (values: any) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FilterFormContent: React.FC<FilterFormContentProps> = forwardRef(
|
||||||
|
(
|
||||||
|
{ initialValues, onClose, onClear, onValuesChange, open, modelList },
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const filterRef = useRef<any>(null);
|
||||||
|
|
||||||
|
const handleOnValuesChange = (changedValues: any, allValues: any) => {
|
||||||
|
onValuesChange?.(allValues);
|
||||||
|
};
|
||||||
|
|
||||||
|
const modelOptions = modelList
|
||||||
|
?.filter((item) => {
|
||||||
|
return item.categories?.includes(modelCategoriesMap.llm);
|
||||||
|
})
|
||||||
|
.map((item) => ({
|
||||||
|
label: item.label,
|
||||||
|
value: item.label
|
||||||
|
}));
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
reset: () => {
|
||||||
|
filterRef.current?.reset();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FilterForm
|
||||||
|
ref={filterRef}
|
||||||
|
width={232}
|
||||||
|
contentHeight={'calc(100vh - 122px)'}
|
||||||
|
open={open}
|
||||||
|
onClose={onClose}
|
||||||
|
onClear={onClear}
|
||||||
|
onValuesChange={handleOnValuesChange}
|
||||||
|
initialValues={initialValues}
|
||||||
|
styles={{
|
||||||
|
container: {
|
||||||
|
paddingInline: '0 8px',
|
||||||
|
marginLeft: '-8px'
|
||||||
|
},
|
||||||
|
wrapper: {
|
||||||
|
marginRight: open ? 24 : 0,
|
||||||
|
marginTop: '-24px'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Content>
|
||||||
|
<Label style={{ marginTop: 0 }}>GPU</Label>
|
||||||
|
<Form.Item name="gpu_summary" noStyle>
|
||||||
|
<Input
|
||||||
|
prefix={
|
||||||
|
<SearchOutlined
|
||||||
|
style={{ color: 'var(--ant-color-text-placeholder)' }}
|
||||||
|
></SearchOutlined>
|
||||||
|
}
|
||||||
|
placeholder={intl.formatMessage({
|
||||||
|
id: 'benchmark.table.filter.bygpu'
|
||||||
|
})}
|
||||||
|
allowClear
|
||||||
|
></Input>
|
||||||
|
</Form.Item>
|
||||||
|
<Label>{intl.formatMessage({ id: 'benchmark.form.profile' })}</Label>
|
||||||
|
<Form.Item noStyle name="profile">
|
||||||
|
<BaseSelect
|
||||||
|
allowClear
|
||||||
|
placeholder={intl.formatMessage({
|
||||||
|
id: 'benchmark.table.filter.byProfile'
|
||||||
|
})}
|
||||||
|
options={[
|
||||||
|
...profileOptions,
|
||||||
|
{
|
||||||
|
label: 'backend.custom',
|
||||||
|
locale: true,
|
||||||
|
value: 'Custom'
|
||||||
|
}
|
||||||
|
].map((item) => ({
|
||||||
|
label: item.locale
|
||||||
|
? intl.formatMessage({ id: item.label })
|
||||||
|
: item.label,
|
||||||
|
value: item.value
|
||||||
|
}))}
|
||||||
|
></BaseSelect>
|
||||||
|
</Form.Item>
|
||||||
|
<Label>{intl.formatMessage({ id: 'benchmark.table.model' })}</Label>
|
||||||
|
<Form.Item noStyle name="model_name">
|
||||||
|
{/* <PillButtonGroup
|
||||||
|
options={modelCategories.filter((item) => item.value)}
|
||||||
|
></PillButtonGroup> */}
|
||||||
|
<BaseSelect
|
||||||
|
allowClear
|
||||||
|
placeholder={intl.formatMessage({
|
||||||
|
id: 'benchmark.table.filter.bymodel'
|
||||||
|
})}
|
||||||
|
options={modelOptions}
|
||||||
|
></BaseSelect>
|
||||||
|
</Form.Item>
|
||||||
|
</Content>
|
||||||
|
</FilterForm>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export default FilterFormContent;
|
||||||
@@ -7,10 +7,10 @@ import useTableFetch from '@/hooks/use-table-fetch';
|
|||||||
import { useBenchmarkTargetInstance } from '@/pages/llmodels/hooks/use-run-benchmark';
|
import { useBenchmarkTargetInstance } from '@/pages/llmodels/hooks/use-run-benchmark';
|
||||||
import { useQueryModelList } from '@/pages/llmodels/services/use-query-model-list';
|
import { useQueryModelList } from '@/pages/llmodels/services/use-query-model-list';
|
||||||
import { useIntl, useNavigate } from '@umijs/max';
|
import { useIntl, useNavigate } from '@umijs/max';
|
||||||
import { useMemoizedFn } from 'ahooks';
|
import { useMemoizedFn, useToggle } from 'ahooks';
|
||||||
import { ConfigProvider, Table, message } from 'antd';
|
import { ConfigProvider, Table, message } from 'antd';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { useEffect } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import NoResult from '../_components/no-result';
|
import NoResult from '../_components/no-result';
|
||||||
import PageBox from '../_components/page-box';
|
import PageBox from '../_components/page-box';
|
||||||
import { useQueryClusterList } from '../cluster-management/services/use-query-cluster-list';
|
import { useQueryClusterList } from '../cluster-management/services/use-query-cluster-list';
|
||||||
@@ -26,6 +26,7 @@ import LeftActions from './components/left-actions';
|
|||||||
import RightActions from './components/right-actions';
|
import RightActions from './components/right-actions';
|
||||||
import ViewLogsModal from './components/view-logs-modal';
|
import ViewLogsModal from './components/view-logs-modal';
|
||||||
import { FormData, BenchmarkListItem as ListItem } from './config/types';
|
import { FormData, BenchmarkListItem as ListItem } from './config/types';
|
||||||
|
import Filters from './filters';
|
||||||
import useBenchmarkColumns from './hooks/use-benchmark-columns';
|
import useBenchmarkColumns from './hooks/use-benchmark-columns';
|
||||||
import useColumnSettings from './hooks/use-column-settings';
|
import useColumnSettings from './hooks/use-column-settings';
|
||||||
import useCreateBenchmark from './hooks/use-create-benchmark';
|
import useCreateBenchmark from './hooks/use-create-benchmark';
|
||||||
@@ -86,6 +87,9 @@ const Benchmark: React.FC = () => {
|
|||||||
clusterList,
|
clusterList,
|
||||||
profileOptions: profilesOptions
|
profileOptions: profilesOptions
|
||||||
});
|
});
|
||||||
|
const [filtersVisible, { toggle: toggleFilters }] = useToggle();
|
||||||
|
const filterRef = useRef<any>(null);
|
||||||
|
const [filterValues, setFilterValues] = useState<any>({});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchModelList({ page: -1 });
|
fetchModelList({ page: -1 });
|
||||||
@@ -205,9 +209,38 @@ const Benchmark: React.FC = () => {
|
|||||||
exportData(rowSelection.selectedRowKeys);
|
exportData(rowSelection.selectedRowKeys);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleOnFilterChange = (filters: any) => {
|
||||||
|
handleQueryChange({
|
||||||
|
page: 1,
|
||||||
|
...filters
|
||||||
|
});
|
||||||
|
|
||||||
|
setFilterValues(filters);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOnClearFilters = () => {
|
||||||
|
filterRef.current?.reset();
|
||||||
|
};
|
||||||
|
|
||||||
|
const filtersCount = Object.values(filterValues).filter(
|
||||||
|
(value) => value !== undefined && value !== null && value !== ''
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div
|
||||||
<PageBox>
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'flex-start'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Filters
|
||||||
|
ref={filterRef}
|
||||||
|
open={filtersVisible}
|
||||||
|
onValuesChange={handleOnFilterChange}
|
||||||
|
onClose={toggleFilters}
|
||||||
|
onClear={handleOnClearFilters}
|
||||||
|
></Filters>
|
||||||
|
<PageBox style={{ flex: 1 }}>
|
||||||
<FilterBar
|
<FilterBar
|
||||||
showSelect={false}
|
showSelect={false}
|
||||||
marginBottom={22}
|
marginBottom={22}
|
||||||
@@ -223,6 +256,9 @@ const Benchmark: React.FC = () => {
|
|||||||
handleSearch={handleSearch}
|
handleSearch={handleSearch}
|
||||||
handleQueryChange={handleQueryChange}
|
handleQueryChange={handleQueryChange}
|
||||||
handleInputChange={handleNameChange}
|
handleInputChange={handleNameChange}
|
||||||
|
count={filtersCount.length}
|
||||||
|
toggleFilters={toggleFilters}
|
||||||
|
onClear={handleOnClearFilters}
|
||||||
></LeftActions>
|
></LeftActions>
|
||||||
}
|
}
|
||||||
right={
|
right={
|
||||||
@@ -284,7 +320,7 @@ const Benchmark: React.FC = () => {
|
|||||||
onCancel={closeViewLogsModal}
|
onCancel={closeViewLogsModal}
|
||||||
></ViewLogsModal>
|
></ViewLogsModal>
|
||||||
<DeleteModal ref={modalRef}></DeleteModal>
|
<DeleteModal ref={modalRef}></DeleteModal>
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
|
import { FiltersButton } from '@/components/page-tools/index';
|
||||||
import { SearchOutlined, SyncOutlined } from '@ant-design/icons';
|
import { SearchOutlined, SyncOutlined } from '@ant-design/icons';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Button, Input, Space } from 'antd';
|
import { Button, Input, Space } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Filters from '../filters';
|
|
||||||
import useFilterStatus from '../hooks/use-filter-status';
|
|
||||||
|
|
||||||
interface LeftFiltersProps {
|
interface LeftFiltersProps {
|
||||||
handleNameChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
handleNameChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||||
@@ -13,6 +12,9 @@ interface LeftFiltersProps {
|
|||||||
handleCategoryChange: (value: string) => void;
|
handleCategoryChange: (value: string) => void;
|
||||||
onFilterChange: (allValues: any) => void;
|
onFilterChange: (allValues: any) => void;
|
||||||
clusterList: Global.BaseOption<number>[];
|
clusterList: Global.BaseOption<number>[];
|
||||||
|
toggleFilters: () => void;
|
||||||
|
onClear: () => void;
|
||||||
|
count?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const LeftFilters: React.FC<LeftFiltersProps> = (props) => {
|
const LeftFilters: React.FC<LeftFiltersProps> = (props) => {
|
||||||
@@ -23,13 +25,20 @@ const LeftFilters: React.FC<LeftFiltersProps> = (props) => {
|
|||||||
handleSearch,
|
handleSearch,
|
||||||
handleCategoryChange,
|
handleCategoryChange,
|
||||||
onFilterChange,
|
onFilterChange,
|
||||||
clusterList
|
clusterList,
|
||||||
|
toggleFilters,
|
||||||
|
onClear,
|
||||||
|
count
|
||||||
} = props;
|
} = props;
|
||||||
const { labelRender, optionRender, statusOptions } = useFilterStatus();
|
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Space>
|
<Space>
|
||||||
|
<FiltersButton
|
||||||
|
onClick={toggleFilters}
|
||||||
|
count={count}
|
||||||
|
onClear={onClear}
|
||||||
|
></FiltersButton>
|
||||||
<Input
|
<Input
|
||||||
prefix={
|
prefix={
|
||||||
<SearchOutlined
|
<SearchOutlined
|
||||||
@@ -37,54 +46,11 @@ const LeftFilters: React.FC<LeftFiltersProps> = (props) => {
|
|||||||
></SearchOutlined>
|
></SearchOutlined>
|
||||||
}
|
}
|
||||||
placeholder={intl.formatMessage({ id: 'common.filter.name' })}
|
placeholder={intl.formatMessage({ id: 'common.filter.name' })}
|
||||||
style={{ width: 400 }}
|
style={{ width: 300 }}
|
||||||
size="large"
|
size="large"
|
||||||
allowClear
|
allowClear
|
||||||
suffix={
|
|
||||||
<Filters
|
|
||||||
clusterList={clusterList}
|
|
||||||
onValuesChange={onFilterChange}
|
|
||||||
></Filters>
|
|
||||||
}
|
|
||||||
onChange={handleNameChange}
|
onChange={handleNameChange}
|
||||||
></Input>
|
></Input>
|
||||||
|
|
||||||
{/* <BaseSelect
|
|
||||||
allowClear
|
|
||||||
showSearch={false}
|
|
||||||
placeholder={intl.formatMessage({
|
|
||||||
id: 'models.filter.category'
|
|
||||||
})}
|
|
||||||
style={{ width: 160 }}
|
|
||||||
size="large"
|
|
||||||
maxTagCount={1}
|
|
||||||
onChange={handleCategoryChange}
|
|
||||||
options={modelCategories.filter((item) => item.value)}
|
|
||||||
></BaseSelect>
|
|
||||||
<BaseSelect
|
|
||||||
allowClear
|
|
||||||
showSearch={false}
|
|
||||||
placeholder={intl.formatMessage({
|
|
||||||
id: 'clusters.filterBy.cluster'
|
|
||||||
})}
|
|
||||||
style={{ width: 160 }}
|
|
||||||
size="large"
|
|
||||||
maxTagCount={1}
|
|
||||||
onChange={handleClusterChange}
|
|
||||||
options={clusterList}
|
|
||||||
></BaseSelect>
|
|
||||||
<BaseSelect
|
|
||||||
allowClear
|
|
||||||
showSearch={false}
|
|
||||||
placeholder={intl.formatMessage({ id: 'common.filter.status' })}
|
|
||||||
style={{ width: 180 }}
|
|
||||||
size="large"
|
|
||||||
maxTagCount={1}
|
|
||||||
optionRender={optionRender}
|
|
||||||
labelRender={labelRender}
|
|
||||||
options={statusOptions}
|
|
||||||
onChange={handleStatusChange}
|
|
||||||
></BaseSelect> */}
|
|
||||||
<Button
|
<Button
|
||||||
type="text"
|
type="text"
|
||||||
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import useBodyScroll from '@/hooks/use-body-scroll';
|
|||||||
import useExpandedRowKeys from '@/hooks/use-expanded-row-keys';
|
import useExpandedRowKeys from '@/hooks/use-expanded-row-keys';
|
||||||
import useTableRowSelection from '@/hooks/use-table-row-selection';
|
import useTableRowSelection from '@/hooks/use-table-row-selection';
|
||||||
import useWatchList from '@/hooks/use-watch-list';
|
import useWatchList from '@/hooks/use-watch-list';
|
||||||
import PageBox from '@/pages/_components/page-box';
|
|
||||||
import useNoResourceResult from '@/pages/llmodels/hooks/use-no-resource-result';
|
import useNoResourceResult from '@/pages/llmodels/hooks/use-no-resource-result';
|
||||||
import { MODEL_ROUTE_TARGETS } from '@/pages/model-routes/apis';
|
import { MODEL_ROUTE_TARGETS } from '@/pages/model-routes/apis';
|
||||||
import { TargetStatusValueMap } from '@/pages/model-routes/config';
|
import { TargetStatusValueMap } from '@/pages/model-routes/config';
|
||||||
@@ -21,7 +20,7 @@ import useGranfanaLink from '@/pages/resources/hooks/use-grafana-link';
|
|||||||
import { handleBatchRequest } from '@/utils';
|
import { handleBatchRequest } from '@/utils';
|
||||||
import { DownOutlined } from '@ant-design/icons';
|
import { DownOutlined } from '@ant-design/icons';
|
||||||
import { useIntl, useNavigate, useSearchParams } from '@umijs/max';
|
import { useIntl, useNavigate, useSearchParams } from '@umijs/max';
|
||||||
import { useMemoizedFn } from 'ahooks';
|
import { useMemoizedFn, useToggle } from 'ahooks';
|
||||||
import { Button, Space, message } from 'antd';
|
import { Button, Space, message } from 'antd';
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
@@ -53,6 +52,7 @@ import {
|
|||||||
ModelInstanceListItem,
|
ModelInstanceListItem,
|
||||||
SourceType
|
SourceType
|
||||||
} from '../config/types';
|
} from '../config/types';
|
||||||
|
import Filters from '../filters';
|
||||||
import useEditDeployment from '../hooks/use-edit-deployment';
|
import useEditDeployment from '../hooks/use-edit-deployment';
|
||||||
import useModelsColumns from '../hooks/use-models-columns';
|
import useModelsColumns from '../hooks/use-models-columns';
|
||||||
import useViewInstanceLogs from '../hooks/use-view-instance-logs';
|
import useViewInstanceLogs from '../hooks/use-view-instance-logs';
|
||||||
@@ -77,7 +77,7 @@ interface ModelsProps {
|
|||||||
onTableSort?: (order: TableOrder | Array<TableOrder>) => void;
|
onTableSort?: (order: TableOrder | Array<TableOrder>) => void;
|
||||||
onStatusChange: (value?: any) => void;
|
onStatusChange: (value?: any) => void;
|
||||||
onDeleteInstanceFromCache?: (instanceId: number) => void;
|
onDeleteInstanceFromCache?: (instanceId: number) => void;
|
||||||
onFilterChange?: (filters: any) => void;
|
onFilterChange: (filters: any) => void;
|
||||||
sortOrder: string[];
|
sortOrder: string[];
|
||||||
queryParams: {
|
queryParams: {
|
||||||
page: number;
|
page: number;
|
||||||
@@ -90,6 +90,7 @@ interface ModelsProps {
|
|||||||
loading: boolean;
|
loading: boolean;
|
||||||
loadend: boolean;
|
loadend: boolean;
|
||||||
total: number;
|
total: number;
|
||||||
|
filterValues?: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getFormattedData = (record: any, extraData = {}) => ({
|
const getFormattedData = (record: any, extraData = {}) => ({
|
||||||
@@ -128,7 +129,8 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
queryParams,
|
queryParams,
|
||||||
loading,
|
loading,
|
||||||
loadend,
|
loadend,
|
||||||
total
|
total,
|
||||||
|
filterValues = {}
|
||||||
}) => {
|
}) => {
|
||||||
const { generateFormValues, clusterList, workerList } =
|
const { generateFormValues, clusterList, workerList } =
|
||||||
useDeploymentsContext();
|
useDeploymentsContext();
|
||||||
@@ -177,6 +179,8 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
source: modelSourceMap.huggingface_value as SourceType
|
source: modelSourceMap.huggingface_value as SourceType
|
||||||
});
|
});
|
||||||
const modalRef = useRef<any>(null);
|
const modalRef = useRef<any>(null);
|
||||||
|
const [filtersVisible, { toggle: toggleFilters }] = useToggle();
|
||||||
|
const filterRef = useRef<any>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (deleteIds?.length) {
|
if (deleteIds?.length) {
|
||||||
@@ -540,6 +544,14 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleOnClearFilters = () => {
|
||||||
|
filterRef.current?.reset();
|
||||||
|
};
|
||||||
|
|
||||||
|
const filtersCount = Object.values(filterValues).filter(
|
||||||
|
(value) => value !== undefined && value !== null && value !== ''
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (modelsSession.source && loadend) {
|
if (modelsSession.source && loadend) {
|
||||||
handleClickDropdown({
|
handleClickDropdown({
|
||||||
@@ -552,13 +564,29 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
}, [loadend]);
|
}, [loadend]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div
|
||||||
<PageBox>
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'flex-start'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Filters
|
||||||
|
ref={filterRef}
|
||||||
|
open={filtersVisible}
|
||||||
|
onValuesChange={onFilterChange}
|
||||||
|
clusterList={clusterList}
|
||||||
|
onClose={toggleFilters}
|
||||||
|
onClear={handleOnClearFilters}
|
||||||
|
></Filters>
|
||||||
|
<div style={{ flex: 1, padding: '24px' }}>
|
||||||
<PageTools
|
<PageTools
|
||||||
marginBottom={22}
|
marginBottom={22}
|
||||||
marginTop={0}
|
marginTop={0}
|
||||||
left={
|
left={
|
||||||
<LeftFilters
|
<LeftFilters
|
||||||
|
count={filtersCount.length}
|
||||||
|
toggleFilters={toggleFilters}
|
||||||
|
onClear={handleOnClearFilters}
|
||||||
handleNameChange={handleNameChange}
|
handleNameChange={handleNameChange}
|
||||||
handleClusterChange={handleClusterChange}
|
handleClusterChange={handleClusterChange}
|
||||||
handleCategoryChange={handleCategoryChange}
|
handleCategoryChange={handleCategoryChange}
|
||||||
@@ -603,7 +631,6 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
</Space>
|
</Space>
|
||||||
}
|
}
|
||||||
></PageTools>
|
></PageTools>
|
||||||
|
|
||||||
<SealTable
|
<SealTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
sortDirections={TABLE_SORT_DIRECTIONS}
|
sortDirections={TABLE_SORT_DIRECTIONS}
|
||||||
@@ -636,7 +663,7 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
onChange: handlePageChange
|
onChange: handlePageChange
|
||||||
}}
|
}}
|
||||||
></SealTable>
|
></SealTable>
|
||||||
</PageBox>
|
</div>
|
||||||
<UpdateModelModal
|
<UpdateModelModal
|
||||||
open={openEditModalStatus.open}
|
open={openEditModalStatus.open}
|
||||||
action={openEditModalStatus.action}
|
action={openEditModalStatus.action}
|
||||||
@@ -668,7 +695,7 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
onCancel={handleLogModalCancel}
|
onCancel={handleLogModalCancel}
|
||||||
></ViewLogsModal>
|
></ViewLogsModal>
|
||||||
<DeleteModal ref={modalRef}></DeleteModal>
|
<DeleteModal ref={modalRef}></DeleteModal>
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -87,6 +87,8 @@ const Models = forwardRef((props, ref) => {
|
|||||||
setDataList: setModelInstances
|
setDataList: setModelInstances
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [filterValues, setFilterValues] = useState<any>({});
|
||||||
|
|
||||||
const getAllModelInstances = useMemoizedFn(async () => {
|
const getAllModelInstances = useMemoizedFn(async () => {
|
||||||
try {
|
try {
|
||||||
instancesToken.current?.cancel?.();
|
instancesToken.current?.cancel?.();
|
||||||
@@ -368,6 +370,8 @@ const Models = forwardRef((props, ref) => {
|
|||||||
search: queryParams.search,
|
search: queryParams.search,
|
||||||
...filters
|
...filters
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setFilterValues(filters);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDeleteInstanceFromCache = (id: number) => {
|
const handleDeleteInstanceFromCache = (id: number) => {
|
||||||
@@ -464,6 +468,7 @@ const Models = forwardRef((props, ref) => {
|
|||||||
loadend={dataSource.loadend}
|
loadend={dataSource.loadend}
|
||||||
total={dataSource.total}
|
total={dataSource.total}
|
||||||
deleteIds={dataSource.deletedIds}
|
deleteIds={dataSource.deletedIds}
|
||||||
|
filterValues={filterValues}
|
||||||
></TableList>
|
></TableList>
|
||||||
</TableContext.Provider>
|
</TableContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import BaseSelect from '@/components/seal-form/base/select';
|
import BaseSelect from '@/components/seal-form/base/select';
|
||||||
import FilterForm from '@/pages/_components/filter-form';
|
import FilterForm from '@/pages/_components/filter-form';
|
||||||
import PillButtonGroup from '@/pages/_components/pill-button-group';
|
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Form } from 'antd';
|
import { Form } from 'antd';
|
||||||
|
import { forwardRef, useImperativeHandle, useRef } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { modelCategories } from '../config';
|
import { modelCategories } from '../config';
|
||||||
import useFilterStatus from '../hooks/use-filter-status';
|
import useFilterStatus from '../hooks/use-filter-status';
|
||||||
@@ -10,98 +10,136 @@ import useFilterStatus from '../hooks/use-filter-status';
|
|||||||
const Content = styled.div`
|
const Content = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
// gap: 16px;
|
|
||||||
`;
|
`;
|
||||||
const Label = styled.span`
|
const Label = styled.span`
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 14px;
|
font-size: 13px;
|
||||||
margin-block: 16px;
|
margin-block: 12px 8px;
|
||||||
color: var(--ant-color-text-tertiary);
|
color: var(--ant-color-text-tertiary);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface FilterFormContentProps {
|
interface FilterFormContentProps {
|
||||||
clusterList: Global.BaseOption<number>[];
|
clusterList: Global.BaseOption<number>[];
|
||||||
initialValues?: any;
|
initialValues?: any;
|
||||||
onValuesChange?: (values: any) => void;
|
open?: boolean;
|
||||||
|
ref?: any;
|
||||||
|
onClose?: () => void;
|
||||||
|
onClear: () => void;
|
||||||
|
onValuesChange: (values: any) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FilterFormContent: React.FC<FilterFormContentProps> = ({
|
const FilterFormContent: React.FC<FilterFormContentProps> = forwardRef(
|
||||||
clusterList,
|
(
|
||||||
initialValues,
|
{ clusterList, initialValues, onClose, onClear, onValuesChange, open },
|
||||||
onValuesChange
|
ref
|
||||||
}) => {
|
) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { labelRender, optionRender, statusOptions } = useFilterStatus();
|
const { labelRender, optionRender, statusOptions } = useFilterStatus();
|
||||||
|
const filterRef = useRef<any>(null);
|
||||||
|
|
||||||
const handleOnValuesChange = (changedValues: any, allValues: any) => {
|
const handleOnValuesChange = (changedValues: any, allValues: any) => {
|
||||||
onValuesChange?.(allValues);
|
onValuesChange?.(allValues);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
useImperativeHandle(ref, () => ({
|
||||||
<FilterForm
|
reset: () => {
|
||||||
width={430}
|
filterRef.current?.reset();
|
||||||
onValuesChange={handleOnValuesChange}
|
}
|
||||||
initialValues={initialValues}
|
}));
|
||||||
>
|
|
||||||
<Content>
|
return (
|
||||||
<Label style={{ marginTop: 0 }}>
|
<FilterForm
|
||||||
{intl.formatMessage({
|
ref={filterRef}
|
||||||
id: 'clusters.filterBy.cluster'
|
width={232}
|
||||||
})}
|
contentHeight={'calc(100vh - 74px)'}
|
||||||
</Label>
|
open={open}
|
||||||
<Form.Item
|
onClose={onClose}
|
||||||
noStyle
|
onClear={onClear}
|
||||||
name="cluster_id"
|
onValuesChange={handleOnValuesChange}
|
||||||
label={intl.formatMessage({
|
initialValues={initialValues}
|
||||||
id: 'clusters.filterBy.cluster'
|
styles={{
|
||||||
})}
|
wrapper: {
|
||||||
>
|
marginTop: 0
|
||||||
<BaseSelect
|
}
|
||||||
allowClear
|
}}
|
||||||
showSearch={false}
|
>
|
||||||
placeholder={intl.formatMessage({
|
<Content>
|
||||||
|
<Label style={{ marginTop: 0 }}>
|
||||||
|
{intl.formatMessage({
|
||||||
|
id: 'clusters.title'
|
||||||
|
})}
|
||||||
|
</Label>
|
||||||
|
<Form.Item
|
||||||
|
noStyle
|
||||||
|
name="cluster_id"
|
||||||
|
label={intl.formatMessage({
|
||||||
id: 'clusters.filterBy.cluster'
|
id: 'clusters.filterBy.cluster'
|
||||||
})}
|
})}
|
||||||
size="large"
|
>
|
||||||
maxTagCount={1}
|
<BaseSelect
|
||||||
options={clusterList}
|
allowClear
|
||||||
></BaseSelect>
|
showSearch={false}
|
||||||
</Form.Item>
|
placeholder={intl.formatMessage({
|
||||||
<Label>
|
id: 'clusters.filterBy.cluster'
|
||||||
{intl.formatMessage({
|
})}
|
||||||
id: 'models.filter.category'
|
size="large"
|
||||||
})}
|
maxTagCount={1}
|
||||||
</Label>
|
options={clusterList}
|
||||||
<Form.Item
|
></BaseSelect>
|
||||||
noStyle
|
</Form.Item>
|
||||||
name="categories"
|
<Label>{intl.formatMessage({ id: 'models.table.category' })}</Label>
|
||||||
label={intl.formatMessage({
|
<Form.Item
|
||||||
id: 'models.filter.category'
|
noStyle
|
||||||
})}
|
name="categories"
|
||||||
>
|
label={intl.formatMessage({
|
||||||
<PillButtonGroup
|
id: 'models.filter.category'
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{/* <PillButtonGroup
|
||||||
options={modelCategories.filter((item) => item.value)}
|
options={modelCategories.filter((item) => item.value)}
|
||||||
></PillButtonGroup>
|
></PillButtonGroup> */}
|
||||||
</Form.Item>
|
<BaseSelect
|
||||||
|
allowClear
|
||||||
|
showSearch={false}
|
||||||
|
placeholder={intl.formatMessage({
|
||||||
|
id: 'models.filter.category'
|
||||||
|
})}
|
||||||
|
size="large"
|
||||||
|
maxTagCount={1}
|
||||||
|
// onChange={handleCategoryChange}
|
||||||
|
options={modelCategories.filter((item) => item.value)}
|
||||||
|
></BaseSelect>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
<Label>
|
<Label>
|
||||||
{intl.formatMessage({
|
{intl.formatMessage({
|
||||||
id: 'common.filter.status'
|
id: 'models.table.status'
|
||||||
})}
|
})}
|
||||||
</Label>
|
</Label>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
noStyle
|
noStyle
|
||||||
name="state"
|
name="state"
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: 'common.filter.status'
|
id: 'models.table.status'
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<PillButtonGroup options={statusOptions}></PillButtonGroup>
|
{/* <PillButtonGroup options={statusOptions}></PillButtonGroup> */}
|
||||||
</Form.Item>
|
<BaseSelect
|
||||||
</Content>
|
allowClear
|
||||||
</FilterForm>
|
showSearch={false}
|
||||||
);
|
placeholder={intl.formatMessage({ id: 'common.filter.status' })}
|
||||||
};
|
size="large"
|
||||||
|
maxTagCount={1}
|
||||||
|
optionRender={optionRender}
|
||||||
|
labelRender={labelRender}
|
||||||
|
options={statusOptions}
|
||||||
|
></BaseSelect>
|
||||||
|
</Form.Item>
|
||||||
|
</Content>
|
||||||
|
</FilterForm>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export default FilterFormContent;
|
export default FilterFormContent;
|
||||||
|
|||||||
@@ -105,7 +105,14 @@ const LLModels: React.FC = () => {
|
|||||||
}, [activeKey]);
|
}, [activeKey]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContainerInner leftContent={title}>
|
<PageContainerInner
|
||||||
|
leftContent={title}
|
||||||
|
styles={{
|
||||||
|
containerWrapper: {
|
||||||
|
padding: 0
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
<DeploymentsContext.Provider
|
<DeploymentsContext.Provider
|
||||||
value={{
|
value={{
|
||||||
generateFormValues,
|
generateFormValues,
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ const InstanceView = forwardRef((props, ref) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div style={{ padding: 24 }}>
|
||||||
<PageBox>
|
<PageBox>
|
||||||
<FilterBar
|
<FilterBar
|
||||||
showSelect={false}
|
showSelect={false}
|
||||||
@@ -186,7 +186,7 @@ const InstanceView = forwardRef((props, ref) => {
|
|||||||
onCancel={closeViewLogsModal}
|
onCancel={closeViewLogsModal}
|
||||||
></ViewLogsModal>
|
></ViewLogsModal>
|
||||||
<DeleteModal ref={modalRef}></DeleteModal>
|
<DeleteModal ref={modalRef}></DeleteModal>
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user