style: no resource modal
This commit is contained in:
@@ -792,3 +792,7 @@ body {
|
||||
.ant-page-header .ant-page-header-heading-extra {
|
||||
margin-block: 0;
|
||||
}
|
||||
|
||||
.ant-pro-footer-bar {
|
||||
padding-inline: 0 !important;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ export default {
|
||||
'clusters.create.configBasic': '基本配置',
|
||||
'clusters.create.execCommand': '执行命令',
|
||||
'clusters.create.supportedGpu': '支持的 GPU',
|
||||
'clusters.create.skipfornow': '暂时跳过',
|
||||
'clusters.create.skipfornow': '稍后再说',
|
||||
'clusters.create.noImages': '无可用的镜像',
|
||||
'clusters.create.noInstanceTypes': '无可用的实例类型',
|
||||
'clusters.create.noRegions': '无可用的区域',
|
||||
|
||||
@@ -146,6 +146,7 @@ const AddWorkerSteps: React.FC<AddWorkerProps> = (props) => {
|
||||
)}
|
||||
{addedCount > 0 && (
|
||||
<Alert
|
||||
style={{ width: 'max-content' }}
|
||||
message={intl.formatMessage(
|
||||
{
|
||||
id: 'clusters.addworker.message.success'
|
||||
|
||||
@@ -11,10 +11,16 @@ import AddWorkerStep from './add-worker-step';
|
||||
import { StepName } from './config';
|
||||
|
||||
const Footer = styled.div`
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding-top: 12px;
|
||||
.tips {
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
}
|
||||
`;
|
||||
|
||||
type AddWorkerProps = {
|
||||
@@ -113,7 +119,7 @@ const AddWorker: React.FC<AddWorkerProps> = (props) => {
|
||||
maxContentHeight={'max(calc(100vh - 200px), 600px)'}
|
||||
footer={
|
||||
<Footer>
|
||||
<span>
|
||||
<span className="tips">
|
||||
{addedCount > 0 && (
|
||||
<Alert
|
||||
message={intl.formatMessage(
|
||||
|
||||
@@ -23,9 +23,11 @@ const SwitchSetting: React.FC<{
|
||||
tips?: React.ReactNode;
|
||||
errorMessage?: React.ReactNode;
|
||||
extra?: React.ReactNode;
|
||||
showSwitch?: boolean;
|
||||
onInputChange?: (value: string) => void;
|
||||
onChange: (checked: boolean) => void;
|
||||
}> = ({
|
||||
showSwitch = true,
|
||||
label,
|
||||
checked,
|
||||
onChange,
|
||||
@@ -42,7 +44,7 @@ const SwitchSetting: React.FC<{
|
||||
<span style={{ color: 'var(--ant-color-text)', fontWeight: 500 }}>
|
||||
<span>{label}</span>
|
||||
</span>
|
||||
<Switch checked={checked} onChange={onChange}></Switch>
|
||||
{showSwitch && <Switch checked={checked} onChange={onChange}></Switch>}
|
||||
</ButtonWrapper>
|
||||
{tips && (
|
||||
<Tips
|
||||
@@ -97,12 +99,12 @@ const SpecifyArguments = () => {
|
||||
};
|
||||
|
||||
const containerNameConfig = summary.get('containerNameConfig') || {
|
||||
enable: false,
|
||||
enable: true,
|
||||
name: ''
|
||||
};
|
||||
|
||||
const gpustackDataVolumeConfig = summary.get('gpustackDataVolumeConfig') || {
|
||||
enable: false,
|
||||
enable: true,
|
||||
path: ''
|
||||
};
|
||||
|
||||
@@ -179,12 +181,12 @@ const SpecifyArguments = () => {
|
||||
});
|
||||
|
||||
updateField('containerNameConfig', {
|
||||
enable: false,
|
||||
enable: true,
|
||||
name: ''
|
||||
});
|
||||
|
||||
updateField('gpustackDataVolumeConfig', {
|
||||
enable: false,
|
||||
enable: true,
|
||||
path: ''
|
||||
});
|
||||
}, []);
|
||||
@@ -272,56 +274,23 @@ const SpecifyArguments = () => {
|
||||
)
|
||||
}
|
||||
></SwitchSetting>
|
||||
{/* container name config */}
|
||||
{/* cache directory config */}
|
||||
<SwitchSetting
|
||||
label={intl.formatMessage({ id: 'clusters.addworker.containerName' })}
|
||||
label={intl.formatMessage({ id: 'clusters.addworker.cacheVolume' })}
|
||||
tips={intl.formatMessage({
|
||||
id: 'clusters.addworker.containerName.tips'
|
||||
id: 'clusters.addworker.cacheVolume.tips'
|
||||
})}
|
||||
placeholder={intl.formatMessage(
|
||||
{
|
||||
id: 'common.help.default'
|
||||
},
|
||||
{ content: 'gpustack-worker' }
|
||||
)}
|
||||
checked={containerNameConfig.enable}
|
||||
value={containerNameConfig.name}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'clusters.addworker.cacheVolume.holder'
|
||||
})}
|
||||
checked={cacheDirConfig.enable}
|
||||
value={cacheDirConfig.path}
|
||||
onChange={(checked) =>
|
||||
updateField('containerNameConfig', {
|
||||
...containerNameConfig,
|
||||
enable: checked
|
||||
})
|
||||
setCacheDirConfig({ ...cacheDirConfig, enable: checked })
|
||||
}
|
||||
onInputChange={(value) =>
|
||||
updateField('containerNameConfig', {
|
||||
...containerNameConfig,
|
||||
name: value
|
||||
})
|
||||
}
|
||||
></SwitchSetting>
|
||||
{/* gpustack data volume config */}
|
||||
<SwitchSetting
|
||||
label={intl.formatMessage({ id: 'clusters.addworker.dataVolume' })}
|
||||
tips={intl.formatMessage({
|
||||
id: 'clusters.addworker.dataVolume.tips'
|
||||
})}
|
||||
placeholder={intl.formatMessage(
|
||||
{
|
||||
id: 'common.help.default'
|
||||
},
|
||||
{ content: 'gpustack-data' }
|
||||
)}
|
||||
checked={gpustackDataVolumeConfig.enable}
|
||||
value={gpustackDataVolumeConfig.path}
|
||||
onChange={(checked) =>
|
||||
updateField('gpustackDataVolumeConfig', {
|
||||
...gpustackDataVolumeConfig,
|
||||
enable: checked
|
||||
})
|
||||
}
|
||||
onInputChange={(value) =>
|
||||
updateField('gpustackDataVolumeConfig', {
|
||||
...gpustackDataVolumeConfig,
|
||||
setCacheDirConfig({
|
||||
...cacheDirConfig,
|
||||
path: value
|
||||
})
|
||||
}
|
||||
@@ -347,27 +316,57 @@ const SpecifyArguments = () => {
|
||||
})
|
||||
}
|
||||
></SwitchSetting>
|
||||
{/* cache directory config */}
|
||||
|
||||
{/* gpustack data volume config */}
|
||||
<SwitchSetting
|
||||
label={intl.formatMessage({ id: 'clusters.addworker.cacheVolume' })}
|
||||
tips={intl.formatMessage({
|
||||
id: 'clusters.addworker.cacheVolume.tips'
|
||||
})}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'clusters.addworker.cacheVolume.holder'
|
||||
})}
|
||||
checked={cacheDirConfig.enable}
|
||||
value={cacheDirConfig.path}
|
||||
showSwitch={false}
|
||||
label={intl.formatMessage({ id: 'clusters.addworker.dataVolume' })}
|
||||
placeholder={intl.formatMessage(
|
||||
{
|
||||
id: 'common.help.default'
|
||||
},
|
||||
{ content: 'gpustack-data' }
|
||||
)}
|
||||
checked={gpustackDataVolumeConfig.enable}
|
||||
value={gpustackDataVolumeConfig.path}
|
||||
onChange={(checked) =>
|
||||
setCacheDirConfig({ ...cacheDirConfig, enable: checked })
|
||||
updateField('gpustackDataVolumeConfig', {
|
||||
...gpustackDataVolumeConfig,
|
||||
enable: checked
|
||||
})
|
||||
}
|
||||
onInputChange={(value) =>
|
||||
setCacheDirConfig({
|
||||
...cacheDirConfig,
|
||||
updateField('gpustackDataVolumeConfig', {
|
||||
...gpustackDataVolumeConfig,
|
||||
path: value
|
||||
})
|
||||
}
|
||||
></SwitchSetting>
|
||||
{/* container name config */}
|
||||
<SwitchSetting
|
||||
showSwitch={false}
|
||||
label={intl.formatMessage({ id: 'clusters.addworker.containerName' })}
|
||||
placeholder={intl.formatMessage(
|
||||
{
|
||||
id: 'common.help.default'
|
||||
},
|
||||
{ content: 'gpustack-worker' }
|
||||
)}
|
||||
checked={containerNameConfig.enable}
|
||||
value={containerNameConfig.name}
|
||||
onChange={(checked) =>
|
||||
updateField('containerNameConfig', {
|
||||
...containerNameConfig,
|
||||
enable: checked
|
||||
})
|
||||
}
|
||||
onInputChange={(value) =>
|
||||
updateField('containerNameConfig', {
|
||||
...containerNameConfig,
|
||||
name: value
|
||||
})
|
||||
}
|
||||
></SwitchSetting>
|
||||
</div>
|
||||
</StepCollapse>
|
||||
);
|
||||
|
||||
@@ -99,16 +99,6 @@ const SummaryData: React.FC = () => {
|
||||
path: ''
|
||||
};
|
||||
|
||||
const containerNameConfig = summary.get('containerNameConfig') || {
|
||||
enable: false,
|
||||
name: ''
|
||||
};
|
||||
|
||||
const gpustackDataVolumeConfig = summary.get('gpustackDataVolumeConfig') || {
|
||||
enable: false,
|
||||
path: ''
|
||||
};
|
||||
|
||||
return (
|
||||
<ConfigWrapper>
|
||||
<Title>
|
||||
@@ -143,18 +133,6 @@ const SummaryData: React.FC = () => {
|
||||
required={true}
|
||||
></DataItem>
|
||||
|
||||
<DataItem
|
||||
label={intl.formatMessage({ id: 'clusters.addworker.containerName' })}
|
||||
enable={containerNameConfig.enable}
|
||||
value={containerNameConfig.name}
|
||||
></DataItem>
|
||||
|
||||
<DataItem
|
||||
label={intl.formatMessage({ id: 'clusters.addworker.dataVolume' })}
|
||||
enable={gpustackDataVolumeConfig.enable}
|
||||
value={gpustackDataVolumeConfig.path}
|
||||
></DataItem>
|
||||
|
||||
<DataItem
|
||||
label={intl.formatMessage({ id: 'clusters.addworker.extraVolume' })}
|
||||
enable={modelDirConfig.enable}
|
||||
|
||||
@@ -9,6 +9,7 @@ const Title = styled.span`
|
||||
justify-content: flex-end;
|
||||
font-weight: 700;
|
||||
font-size: 16px;
|
||||
padding-inline: 24px;
|
||||
.text {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
import IconFont from '@/components/icon-font';
|
||||
import ScrollerModal from '@/components/scroller-modal/index';
|
||||
import useClusterList from '@/pages/cluster-management/hooks/use-cluster-list';
|
||||
import { useIntl, useNavigate } from '@umijs/max';
|
||||
import { Button } from 'antd';
|
||||
import { useMemo, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Content = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 32px auto 0px;
|
||||
.title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.tips,
|
||||
.sub-title {
|
||||
color: var(--ant-color-text-tertiary);
|
||||
}
|
||||
.icon {
|
||||
font-size: 32px;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 32px;
|
||||
margin-bottom: 16px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: var(--ant-color-primary);
|
||||
background-color: var(--ant-color-primary-bg);
|
||||
}
|
||||
.desc {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.btn-wrapper {
|
||||
width: 100%;
|
||||
padding: 0 16px;
|
||||
margin-top: 32px;
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
`;
|
||||
|
||||
export default function useAddResource() {
|
||||
const intl = useIntl();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { fetchAll, clusterList, workerList, clustersAtom, workersAtom } =
|
||||
useClusterList();
|
||||
|
||||
const [hiddenModal, setHiddenModal] = useState(false);
|
||||
const [loadingStatus, setLoadingStatus] = useState({
|
||||
loading: false,
|
||||
loadend: false
|
||||
});
|
||||
|
||||
const isNoResource = useMemo(() => {
|
||||
const noResource = workerList.length === 0 || clusterList.length === 0;
|
||||
return noResource && !loadingStatus.loading && loadingStatus.loadend;
|
||||
}, [workersAtom.length, clustersAtom.length, loadingStatus]);
|
||||
|
||||
const contentInfo = useMemo(() => {
|
||||
if (clusterList.length === 0) {
|
||||
return {
|
||||
title: intl.formatMessage({ id: 'noresult.cluster.title' }),
|
||||
subTitle: intl.formatMessage({ id: 'noresult.resources.cluster' }),
|
||||
btnText: intl.formatMessage({ id: 'noresult.resources.gotocluster' })
|
||||
};
|
||||
}
|
||||
return {
|
||||
title: intl.formatMessage({ id: 'noresult.workers.title' }),
|
||||
subTitle: intl.formatMessage({ id: 'noresult.resources.worker' }),
|
||||
btnText: intl.formatMessage({ id: 'noresult.workers.button.add' })
|
||||
};
|
||||
}, [clusterList.length, workerList.length, intl]);
|
||||
|
||||
const open: boolean = useMemo(() => {
|
||||
return isNoResource && !hiddenModal;
|
||||
}, [isNoResource, hiddenModal]);
|
||||
|
||||
const handleCreate = () => {};
|
||||
|
||||
const handleCancel = () => {
|
||||
setHiddenModal(true);
|
||||
};
|
||||
|
||||
const Modal = (
|
||||
<ScrollerModal
|
||||
open={open}
|
||||
footer={null}
|
||||
maskClosable={false}
|
||||
closeIcon={null}
|
||||
onCancel={handleCancel}
|
||||
>
|
||||
<Content>
|
||||
<div className="icon">
|
||||
<IconFont type="icon-dashboard" />
|
||||
</div>
|
||||
<div className="title">{contentInfo.title}</div>
|
||||
<div className="desc">
|
||||
<div className="sub-title">{contentInfo.subTitle}</div>
|
||||
</div>
|
||||
<div className="btn-wrapper">
|
||||
<Button onClick={handleCancel} type="default" style={{ flex: 1 }}>
|
||||
{intl.formatMessage({ id: 'clusters.create.skipfornow' })}
|
||||
</Button>
|
||||
<Button onClick={handleCreate} type="primary" style={{ flex: 1 }}>
|
||||
{contentInfo.btnText}
|
||||
</Button>
|
||||
</div>
|
||||
</Content>
|
||||
</ScrollerModal>
|
||||
);
|
||||
|
||||
return {
|
||||
open,
|
||||
contentInfo,
|
||||
loadingStatus,
|
||||
clusterList,
|
||||
Modal,
|
||||
handleCreate,
|
||||
setLoadingStatus,
|
||||
handleCancel,
|
||||
fetchAll
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,3 @@
|
||||
import useClusterList from '@/pages/cluster-management/hooks/use-cluster-list';
|
||||
import useNoResourceResult from '@/pages/llmodels/hooks/use-no-resource-result';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { Spin } from 'antd';
|
||||
import { useEffect, useState } from 'react';
|
||||
@@ -9,36 +6,12 @@ import { queryDashboardData } from './apis';
|
||||
import DashboardInner from './components/dahboard-inner';
|
||||
import DashboardContext from './config/dashboard-context';
|
||||
import { DashboardProps } from './config/types';
|
||||
import useAddResource from './hooks/use-add-resource';
|
||||
|
||||
const Dashboard: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const [loadingStatus, setLoadingStatus] = useState({
|
||||
loading: false,
|
||||
loadend: false
|
||||
});
|
||||
const { setLoadingStatus, fetchAll, Modal, loadingStatus, clusterList } =
|
||||
useAddResource();
|
||||
const [data, setData] = useState<DashboardProps>({} as DashboardProps);
|
||||
const { fetchAll, clusterList, workerList, clustersAtom, workersAtom } =
|
||||
useClusterList();
|
||||
|
||||
const { noResourceResult } = useNoResourceResult({
|
||||
loadend: true,
|
||||
loading: false,
|
||||
dataSource: clusterList.length > 0 ? workerList : [],
|
||||
queryParams: {},
|
||||
iconType: 'icon-dashboard',
|
||||
title:
|
||||
clusterList.length > 0
|
||||
? intl.formatMessage({ id: 'noresult.workers.title' })
|
||||
: intl.formatMessage({ id: 'noresult.cluster.title' }),
|
||||
noClusters: !clusterList.length,
|
||||
noWorkers: workerList.length === 0 && clusterList.length > 0,
|
||||
defaultContent: {
|
||||
subTitle: intl.formatMessage({ id: 'noresult.workers.subTitle' }),
|
||||
noFoundText: intl.formatMessage({ id: 'noresult.workers.nofound' }),
|
||||
buttonText: intl.formatMessage({ id: 'noresult.workers.button.add' }),
|
||||
onClick: () => {}
|
||||
}
|
||||
});
|
||||
|
||||
const fetchDashboardData = useMemoizedFn(async () => {
|
||||
try {
|
||||
@@ -80,13 +53,10 @@ const Dashboard: React.FC = () => {
|
||||
>
|
||||
<PageBox>
|
||||
<Spin spinning={loadingStatus.loading} style={{ minHeight: 300 }}>
|
||||
{workersAtom.length > 0 && clustersAtom.length > 0 ? (
|
||||
<DashboardInner />
|
||||
) : loadingStatus.loading || !loadingStatus.loadend ? null : (
|
||||
noResourceResult
|
||||
)}
|
||||
<DashboardInner />
|
||||
</Spin>
|
||||
</PageBox>
|
||||
{Modal}
|
||||
</DashboardContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -111,7 +111,8 @@ const useNoResourceResult = (props: {
|
||||
></NoResult>
|
||||
);
|
||||
return {
|
||||
noResourceResult
|
||||
noResourceResult,
|
||||
handleCreate: handleClick
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user