fix: replicas, ready_replicas sorting
This commit is contained in:
@@ -8,6 +8,7 @@ type UserSettings = {
|
||||
colorPrimary: string;
|
||||
isDarkTheme: boolean;
|
||||
collapsed: boolean;
|
||||
hideAddResourceModal?: boolean;
|
||||
};
|
||||
|
||||
const defaultSettings: UserSettings = {
|
||||
@@ -15,7 +16,8 @@ const defaultSettings: UserSettings = {
|
||||
mode: 'auto',
|
||||
isDarkTheme: false,
|
||||
colorPrimary: COLOR_PRIMARY,
|
||||
collapsed: false
|
||||
collapsed: false,
|
||||
hideAddResourceModal: false
|
||||
};
|
||||
|
||||
export const getStorageUserSettings = () => {
|
||||
|
||||
@@ -9,6 +9,7 @@ export const clearStorageUserSettings = () => {
|
||||
'userSettings',
|
||||
JSON.stringify({
|
||||
...savedSettings,
|
||||
hideAddResourceModal: false,
|
||||
colorPrimary: undefined
|
||||
})
|
||||
);
|
||||
|
||||
@@ -244,6 +244,7 @@ export default function useTableFetch<T>(
|
||||
setQueryParams((pre: any) => {
|
||||
return {
|
||||
...pre,
|
||||
page: 1,
|
||||
sort_by: sortKeys.join(',')
|
||||
};
|
||||
});
|
||||
|
||||
+23
-10
@@ -28,7 +28,7 @@ import { Button, ConfigProvider, Modal, theme } from 'antd';
|
||||
import 'driver.js/dist/driver.css';
|
||||
import { useAtom } from 'jotai';
|
||||
import 'overlayscrollbars/overlayscrollbars.css';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { PageContainerInner } from '../pages/_components/page-box';
|
||||
import Exception from './Exception';
|
||||
import './Layout.css';
|
||||
@@ -113,12 +113,6 @@ export default (props: any) => {
|
||||
const { initialize: initialize } = useOverlayScroller({
|
||||
defer: false
|
||||
});
|
||||
const {
|
||||
setLoadingStatus,
|
||||
fetchResourceData,
|
||||
NoResourceModal,
|
||||
loadingStatus
|
||||
} = useAddResource();
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
const { themeData, setUserSettings, userSettings } = useUserSettings();
|
||||
const [userInfo] = useAtom(userAtom);
|
||||
@@ -128,6 +122,18 @@ export default (props: any) => {
|
||||
const intl = useIntl();
|
||||
const { clientRoutes } = useAppData();
|
||||
const [version] = useAtom(GPUStackVersionAtom);
|
||||
const requestResourceRef = useRef<boolean>(false);
|
||||
|
||||
const {
|
||||
setLoadingStatus,
|
||||
fetchResourceData,
|
||||
NoResourceModal,
|
||||
loadingStatus
|
||||
} = useAddResource({
|
||||
onCreate() {
|
||||
requestResourceRef.current = false;
|
||||
}
|
||||
});
|
||||
|
||||
const initialInfo = (useModel && useModel('@@initialState')) || {
|
||||
initialState: undefined,
|
||||
@@ -252,15 +258,18 @@ export default (props: any) => {
|
||||
return <SiderMenu {...menuProps}></SiderMenu>;
|
||||
};
|
||||
|
||||
const onPageChange = (route: any) => {
|
||||
const onPageChange = async (route: any) => {
|
||||
const { location } = history;
|
||||
const { pathname } = location;
|
||||
|
||||
if (
|
||||
!CHECK_RESOURCE_PATH.includes(pathname) &&
|
||||
initialState?.currentUser?.is_admin
|
||||
initialState?.currentUser?.is_admin &&
|
||||
!requestResourceRef.current &&
|
||||
!userSettings.hideAddResourceModal
|
||||
) {
|
||||
fetchResourceData();
|
||||
requestResourceRef.current = true;
|
||||
await fetchResourceData();
|
||||
}
|
||||
|
||||
initRouteCacheValue(pathname);
|
||||
@@ -294,6 +303,10 @@ export default (props: any) => {
|
||||
};
|
||||
|
||||
const onCollapse = (value: boolean) => {
|
||||
// only trigger by window resize
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
setUserSettings({
|
||||
...userSettings,
|
||||
collapsed: value
|
||||
|
||||
@@ -72,7 +72,7 @@ Same applies to the <span class="bold-text">/opt/dtk</span> directory.`,
|
||||
'clusters.addworker.detectWorkerIP': 'Auto-detect Worker IP',
|
||||
'clusters.addworker.specifyWorkerAddress': 'Specify Worker External Address',
|
||||
'clusters.addworker.detectWorkerAddress':
|
||||
'Specify Worker External Address (default uses Worker IP)',
|
||||
'Specify Worker External Address (default to Worker IP)',
|
||||
'clusters.addworker.externalIP.tips':
|
||||
'If running in a VPC or private network, please specify the Worker external address reachable by the GPUStack Server.',
|
||||
'clusters.addworker.enterWorkerIP': 'Enter worker IP',
|
||||
@@ -83,10 +83,10 @@ Same applies to the <span class="bold-text">/opt/dtk</span> directory.`,
|
||||
'If you want to customize the model cache directory, you can specify the path to mount it.',
|
||||
'clusters.addworker.configSummary': 'Configuration Summary',
|
||||
'clusters.addworker.gpuVendor': 'GPU Vendor',
|
||||
'clusters.addworker.workerIP':
|
||||
'Worker IP <span class="text-tertiary">{type}</span>',
|
||||
'clusters.addworker.workerIP': 'Worker IP',
|
||||
'clusters.addworker.workerExternalIP': 'Worker External Address',
|
||||
'clusters.addworker.notSpecified': 'Not Specified',
|
||||
'clusters.addworker.autoDetect': 'Auto-detect',
|
||||
'clusters.addworker.autoDetect': 'Auto',
|
||||
'clusters.addworker.extraVolume.holder':
|
||||
'e.g. /data/models (path must start with /)',
|
||||
'clusters.addworker.cacheVolume.holder':
|
||||
|
||||
@@ -265,5 +265,6 @@ export default {
|
||||
'common.filter.status': 'Filter by status',
|
||||
'common.form.rule.selectInput': 'Please select or enter a {name}',
|
||||
'common.tag.experimental': 'Experimental',
|
||||
'common.title.example': 'Example'
|
||||
'common.title.example': 'Example',
|
||||
'common.button.dontshowagain': "Don't show again"
|
||||
};
|
||||
|
||||
@@ -83,10 +83,10 @@ Same applies to the <span class="bold-text">/opt/dtk</span> directory.`,
|
||||
'If you want to customize the model cache directory, you can specify the path to mount it.',
|
||||
'clusters.addworker.configSummary': 'Configuration Summary',
|
||||
'clusters.addworker.gpuVendor': 'GPU Vendor',
|
||||
'clusters.addworker.workerIP':
|
||||
'Worker IP <span class="text-tertiary">{type}</span>',
|
||||
'clusters.addworker.workerIP': 'Worker IP',
|
||||
'clusters.addworker.workerExternalIP': 'Worker External Address',
|
||||
'clusters.addworker.notSpecified': 'Not Specified',
|
||||
'clusters.addworker.autoDetect': 'Auto-detect',
|
||||
'clusters.addworker.autoDetect': 'Auto',
|
||||
'clusters.addworker.extraVolume.holder':
|
||||
'e.g. /data/models (path must start with /)',
|
||||
'clusters.addworker.cacheVolume.holder':
|
||||
@@ -177,9 +177,10 @@ Same applies to the <span class="bold-text">/opt/dtk</span> directory.`,
|
||||
// 61. 'clusters.addworker.extraVolume': 'Additional Volume Mount',
|
||||
// 62. 'clusters.addworker.configSummary': 'Configuration Summary',
|
||||
// 63. 'clusters.addworker.gpuVendor': 'GPU Vendor',
|
||||
// 64. 'clusters.addworker.workerIP': 'Worker IP <span class="text-tertiary">{type}</span>',
|
||||
// 64. 'clusters.addworker.workerIP': 'Worker IP',
|
||||
// 65. 'clusters.addworker.workerExternalIP': 'Worker External Address',
|
||||
// 65. 'clusters.addworker.notSpecified': 'Not Specified',
|
||||
// 66. 'clusters.addworker.autoDetect': 'Auto-detect',
|
||||
// 66. 'clusters.addworker.autoDetect': 'Auto',
|
||||
// 67. 'clusters.addworker.extraVolume.holder': 'e.g. /data/models (path must start with /)'
|
||||
// 68. 'clusters.addworker.vendorNotes.title': 'Notes for {vendor} Device',
|
||||
// 69. 'clusters.button.genToken': 'Need to create a new token? Click <a href="{link}" target="_blank">here</a>.',
|
||||
|
||||
@@ -265,7 +265,8 @@ export default {
|
||||
'common.filter.status': 'Filter by status',
|
||||
'common.form.rule.selectInput': 'Please select or enter a {name}',
|
||||
'common.tag.experimental': 'Experimental',
|
||||
'common.title.example': 'Example'
|
||||
'common.title.example': 'Example',
|
||||
'common.button.dontshowagain': "Don't show again"
|
||||
};
|
||||
|
||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||
@@ -296,5 +297,6 @@ export default {
|
||||
// 25. 'common.filter.status': 'Filter by status'
|
||||
// 26. 'common.form.rule.selectInput': 'Please select or enter a {name}',
|
||||
// 27. 'common.tag.experimental': 'Experimental',
|
||||
// 28. 'common.title.example': 'Example'
|
||||
// 28. 'common.title.example': 'Example',
|
||||
// 29. 'common.button.dontshowagain': "Don't show again"
|
||||
// ========== End of To-Do List ==========
|
||||
|
||||
@@ -83,8 +83,8 @@ export default {
|
||||
'If you want to customize the model cache directory, you can specify the path to mount it.',
|
||||
'clusters.addworker.configSummary': 'Сводка конфигурации',
|
||||
'clusters.addworker.gpuVendor': 'Производитель GPU',
|
||||
'clusters.addworker.workerIP':
|
||||
'IP воркера <span class="text-tertiary">{type}</span>',
|
||||
'clusters.addworker.workerIP': 'IP воркера',
|
||||
'clusters.addworker.workerExternalIP': 'Внешний адрес воркера',
|
||||
'clusters.addworker.notSpecified': 'Не указано',
|
||||
'clusters.addworker.autoDetect': 'Автоопределение',
|
||||
'clusters.addworker.extraVolume.holder':
|
||||
|
||||
@@ -264,9 +264,10 @@ export default {
|
||||
'common.filter.status': 'Фильтровать по статусу',
|
||||
'common.form.rule.selectInput': 'Выберите или введите {name}',
|
||||
'common.tag.experimental': 'Экспериментальный',
|
||||
'common.title.example': 'Пример'
|
||||
'common.title.example': 'Пример',
|
||||
'common.button.dontshowagain': "Don't show again"
|
||||
};
|
||||
|
||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||
|
||||
// 1. 'common.button.dontshowagain': "Don't show again"
|
||||
// ========== End of To-Do List ==========
|
||||
|
||||
@@ -66,13 +66,13 @@ export default {
|
||||
'clusters.addworker.checkEnv': '检查环境',
|
||||
'clusters.addworker.specifyArgs': '指定参数',
|
||||
'clusters.addworker.runCommand': '运行指令',
|
||||
'clusters.addworker.specifyWorkerIP': '指定 Worker IP',
|
||||
'clusters.addworker.detectWorkerIP': '自动检测 Worker IP',
|
||||
'clusters.addworker.specifyWorkerAddress': '指定 Worker 外部地址',
|
||||
'clusters.addworker.specifyWorkerIP': '指定节点 IP',
|
||||
'clusters.addworker.detectWorkerIP': '自动检测节点 IP',
|
||||
'clusters.addworker.specifyWorkerAddress': '指定节点外部地址',
|
||||
'clusters.addworker.detectWorkerAddress':
|
||||
'指定 Worker 外部地址(默认使用Worker IP)',
|
||||
'指定节点外部地址(默认使用节点 IP)',
|
||||
'clusters.addworker.externalIP.tips':
|
||||
'如运行在 VPC 或私有网络时,请指定 GPUStack Server 可达的 Worker 外部地址。',
|
||||
'如运行在 VPC 或私有网络时,请指定 GPUStack Server 可达的节点外部地址。',
|
||||
'clusters.addworker.enterWorkerIP': '输入节点 IP',
|
||||
'clusters.addworker.enterWorkerIP.error': '请输入节点 IP',
|
||||
'clusters.addworker.extraVolume': '额外卷挂载',
|
||||
@@ -81,8 +81,8 @@ export default {
|
||||
'如果你想自定义模型缓存目录,可以指定路径进行挂载。',
|
||||
'clusters.addworker.configSummary': '配置摘要',
|
||||
'clusters.addworker.gpuVendor': 'GPU 厂商',
|
||||
'clusters.addworker.workerIP':
|
||||
'节点 IP <span class="text-tertiary">{type}</span>',
|
||||
'clusters.addworker.workerIP': '节点 IP',
|
||||
'clusters.addworker.workerExternalIP': '节点外部地址',
|
||||
'clusters.addworker.notSpecified': '未指定',
|
||||
'clusters.addworker.autoDetect': '自动检测',
|
||||
'clusters.addworker.extraVolume.holder':
|
||||
|
||||
@@ -257,5 +257,6 @@ export default {
|
||||
'common.filter.status': '按状态筛选',
|
||||
'common.form.rule.selectInput': '请选择或输入{name}',
|
||||
'common.tag.experimental': '实验性',
|
||||
'common.title.example': '示例'
|
||||
'common.title.example': '示例',
|
||||
'common.button.dontshowagain': '不再提示'
|
||||
};
|
||||
|
||||
@@ -126,16 +126,11 @@ const SummaryData: React.FC = () => {
|
||||
|
||||
<DataItem
|
||||
label={
|
||||
<span
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: intl.formatMessage(
|
||||
{ id: 'clusters.addworker.workerIP' },
|
||||
{
|
||||
type: `(${intl.formatMessage({ id: 'clusters.table.ip.internal' })})`
|
||||
}
|
||||
)
|
||||
}}
|
||||
></span>
|
||||
<span>
|
||||
{intl.formatMessage({
|
||||
id: 'clusters.addworker.workerIP'
|
||||
})}
|
||||
</span>
|
||||
}
|
||||
tips={
|
||||
workerIPConfig.enable
|
||||
@@ -151,16 +146,11 @@ const SummaryData: React.FC = () => {
|
||||
|
||||
<DataItem
|
||||
label={
|
||||
<span
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: intl.formatMessage(
|
||||
{ id: 'clusters.addworker.workerIP' },
|
||||
{
|
||||
type: `(${intl.formatMessage({ id: 'clusters.table.ip.external' })})`
|
||||
}
|
||||
)
|
||||
}}
|
||||
></span>
|
||||
<span>
|
||||
{intl.formatMessage({
|
||||
id: 'clusters.addworker.workerExternalIP'
|
||||
})}
|
||||
</span>
|
||||
}
|
||||
tips={
|
||||
externalWorkerIPConfig.enable
|
||||
|
||||
@@ -2,6 +2,7 @@ import { clusterSessionAtom } from '@/atoms/clusters';
|
||||
import IconFont from '@/components/icon-font';
|
||||
import ScrollerModal from '@/components/scroller-modal/index';
|
||||
import { PageAction } from '@/config';
|
||||
import useUserSettings from '@/hooks/use-user-settings';
|
||||
import useClusterList from '@/pages/cluster-management/hooks/use-cluster-list';
|
||||
import { useIntl, useNavigate } from '@umijs/max';
|
||||
import { Button } from 'antd';
|
||||
@@ -53,14 +54,15 @@ const Content = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
export default function useAddResource() {
|
||||
export default function useAddResource(options?: { onCreated?: () => void }) {
|
||||
const { onCreated } = options || {};
|
||||
const intl = useIntl();
|
||||
const navigate = useNavigate();
|
||||
const { setUserSettings, userSettings } = useUserSettings();
|
||||
const [, setClusterSession] = useAtom(clusterSessionAtom);
|
||||
|
||||
const [hideModalTemporarily, setHideModalTemporarily] = useState(false);
|
||||
const { fetchResource, resourceCount, resourceAtom } = useClusterList();
|
||||
|
||||
const [hiddenModal, setHiddenModal] = useState(false);
|
||||
const [loadingStatus, setLoadingStatus] = useState({
|
||||
loading: false,
|
||||
loadend: false
|
||||
@@ -73,7 +75,6 @@ export default function useAddResource() {
|
||||
}, [resourceAtom, loadingStatus]);
|
||||
|
||||
const contentInfo = useMemo(() => {
|
||||
console.log('resourceCount=', resourceCount);
|
||||
if (!resourceCount.cluster_count) {
|
||||
return {
|
||||
title: intl.formatMessage({ id: 'noresult.cluster.title' }),
|
||||
@@ -89,11 +90,12 @@ export default function useAddResource() {
|
||||
}, [resourceCount, intl]);
|
||||
|
||||
const open: boolean = useMemo(() => {
|
||||
return isNoResource && !hiddenModal;
|
||||
}, [isNoResource, hiddenModal]);
|
||||
return isNoResource && !userSettings.hideAddResourceModal;
|
||||
}, [isNoResource, userSettings.hideAddResourceModal]);
|
||||
|
||||
const handleCreate = () => {
|
||||
setHiddenModal(true);
|
||||
setHideModalTemporarily(true);
|
||||
onCreated?.();
|
||||
if (!resourceCount.cluster_count) {
|
||||
setClusterSession({
|
||||
firstAddWorker: false,
|
||||
@@ -116,23 +118,33 @@ export default function useAddResource() {
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
setHiddenModal(true);
|
||||
setUserSettings({
|
||||
...userSettings,
|
||||
hideAddResourceModal: true
|
||||
});
|
||||
};
|
||||
|
||||
const fetchResourceData = async () => {
|
||||
setLoadingStatus({ loading: true, loadend: false });
|
||||
setHiddenModal(false);
|
||||
await fetchResource();
|
||||
const { hasClusters, hasWorkers } = await fetchResource();
|
||||
setLoadingStatus({ loading: false, loadend: true });
|
||||
setUserSettings({
|
||||
...userSettings,
|
||||
hideAddResourceModal: hasClusters && hasWorkers
|
||||
});
|
||||
};
|
||||
|
||||
const NoResourceModal = (
|
||||
<ScrollerModal
|
||||
open={open}
|
||||
open={open && !hideModalTemporarily}
|
||||
footer={null}
|
||||
maskClosable={false}
|
||||
keyboard={false}
|
||||
closeIcon={null}
|
||||
destroyOnHidden={false}
|
||||
style={{
|
||||
top: '25%'
|
||||
}}
|
||||
onCancel={handleCancel}
|
||||
>
|
||||
<Content>
|
||||
@@ -145,7 +157,7 @@ export default function useAddResource() {
|
||||
</div>
|
||||
<div className="btn-wrapper">
|
||||
<Button onClick={handleCancel} type="default" style={{ flex: 1 }}>
|
||||
{intl.formatMessage({ id: 'clusters.create.skipfornow' })}
|
||||
{intl.formatMessage({ id: 'common.button.dontshowagain' })}
|
||||
</Button>
|
||||
<Button onClick={handleCreate} type="primary" style={{ flex: 1 }}>
|
||||
{contentInfo.btnText}
|
||||
|
||||
@@ -105,9 +105,8 @@ const useModelsColumns = ({
|
||||
<QuestionCircleOutlined className="m-l-5" />
|
||||
</Tooltip>
|
||||
),
|
||||
dataIndex: 'ready_replicas',
|
||||
key: 'ready_replicas',
|
||||
dataField: 'replicas',
|
||||
dataIndex: 'replicas',
|
||||
key: 'replicas',
|
||||
align: 'left',
|
||||
sorter: tableSorter(4),
|
||||
span: 4,
|
||||
|
||||
@@ -316,7 +316,14 @@ const Models: React.FC = () => {
|
||||
};
|
||||
|
||||
const handleOnSortChange = (order: TableOrder | Array<TableOrder>) => {
|
||||
const sortKeys = handleMultiSortChange(order);
|
||||
let orderList = Array.isArray(order) ? order : [order];
|
||||
if (orderList[0].columnKey === 'replicas') {
|
||||
orderList.push({
|
||||
columnKey: 'ready_replicas',
|
||||
order: orderList[0].order
|
||||
});
|
||||
}
|
||||
const sortKeys = handleMultiSortChange(orderList);
|
||||
setQueryParams((pre: any) => {
|
||||
return {
|
||||
...pre,
|
||||
|
||||
@@ -142,9 +142,8 @@ const GroundImages: React.FC<MessageProps> = forwardRef((props, ref) => {
|
||||
const params = {
|
||||
..._.omitBy(finalParameters, (value: string) => !value),
|
||||
seed:
|
||||
parameters.seed || parameters.random_seed
|
||||
? generateRandomNumber()
|
||||
: null,
|
||||
parameters.seed ||
|
||||
(parameters.random_seed ? generateRandomNumber() : null),
|
||||
stream: false,
|
||||
prompt: currentPrompt
|
||||
};
|
||||
|
||||
@@ -167,9 +167,8 @@ const GroundImages: React.FC<MessageProps> = forwardRef((props, ref) => {
|
||||
const params = {
|
||||
..._.omitBy(finalParameters, (value: string) => !value),
|
||||
seed:
|
||||
parameters.seed || parameters.random_seed
|
||||
? generateRandomNumber()
|
||||
: null,
|
||||
parameters.seed ||
|
||||
(parameters.random_seed ? generateRandomNumber() : null),
|
||||
stream: false,
|
||||
prompt: currentPrompt
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user