feat: add cluster detail
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
.cardWrapper {
|
.cardWrapper {
|
||||||
border-radius: var(--border-radius-small);
|
border-radius: var(--border-radius-small);
|
||||||
background-color: var(--color-white-1);
|
background-color: var(--ant-color-bg-container);
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
padding: 10px 16px;
|
padding: 10px 16px;
|
||||||
border: 1px solid var(--ant-color-border);
|
border: 1px solid var(--ant-color-border);
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ export interface CollapsibleContainerProps {
|
|||||||
className?: string;
|
className?: string;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
styles?: {
|
styles?: {
|
||||||
|
root?: React.CSSProperties;
|
||||||
body?: React.CSSProperties;
|
body?: React.CSSProperties;
|
||||||
header?: React.CSSProperties;
|
header?: React.CSSProperties;
|
||||||
content?: React.CSSProperties;
|
content?: React.CSSProperties;
|
||||||
@@ -191,6 +192,9 @@ export default function CollapsibleContainer({
|
|||||||
className={classNames(className, { collapsible, disabled, isOpen })}
|
className={classNames(className, { collapsible, disabled, isOpen })}
|
||||||
variant={variant}
|
variant={variant}
|
||||||
styles={{
|
styles={{
|
||||||
|
root: {
|
||||||
|
...cardStyles?.root
|
||||||
|
},
|
||||||
body: {
|
body: {
|
||||||
padding: 0,
|
padding: 0,
|
||||||
...cardStyles?.body
|
...cardStyles?.body
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ interface ActionItem {
|
|||||||
icon: React.ReactNode;
|
icon: React.ReactNode;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FilterBarProps {
|
interface FilterBarProps {
|
||||||
handleInputChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
handleInputChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||||
handleSelectChange?: (value: any) => void;
|
handleSelectChange?: (value: any) => void;
|
||||||
@@ -76,7 +77,7 @@ interface FilterBarProps {
|
|||||||
showDeleteButton?: boolean;
|
showDeleteButton?: boolean;
|
||||||
right?: React.ReactNode;
|
right?: React.ReactNode;
|
||||||
left?: React.ReactNode;
|
left?: React.ReactNode;
|
||||||
width?: {
|
widths?: {
|
||||||
input?: number;
|
input?: number;
|
||||||
select?: number;
|
select?: number;
|
||||||
};
|
};
|
||||||
@@ -102,7 +103,7 @@ export const FilterBar: React.FC<FilterBarProps> = (props) => {
|
|||||||
selectHolder,
|
selectHolder,
|
||||||
right,
|
right,
|
||||||
left,
|
left,
|
||||||
width
|
widths
|
||||||
} = props;
|
} = props;
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
@@ -181,7 +182,7 @@ export const FilterBar: React.FC<FilterBarProps> = (props) => {
|
|||||||
id: 'common.filter.name'
|
id: 'common.filter.name'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
style={{ width: width?.input || 230 }}
|
style={{ width: widths?.input || 230 }}
|
||||||
allowClear
|
allowClear
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
></Input>
|
></Input>
|
||||||
@@ -190,7 +191,7 @@ export const FilterBar: React.FC<FilterBarProps> = (props) => {
|
|||||||
allowClear
|
allowClear
|
||||||
showSearch={false}
|
showSearch={false}
|
||||||
placeholder={selectHolder}
|
placeholder={selectHolder}
|
||||||
style={{ width: width?.select || 230 }}
|
style={{ width: widths?.select || 230 }}
|
||||||
size="large"
|
size="large"
|
||||||
onChange={handleSelectChange}
|
onChange={handleSelectChange}
|
||||||
options={selectOptions}
|
options={selectOptions}
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ export default {
|
|||||||
borderRadiusSM: 2,
|
borderRadiusSM: 2,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
motion: true,
|
motion: true,
|
||||||
colorFill: '#000',
|
colorFill: '#0A0A0A',
|
||||||
colorBgBase: '#000'
|
colorBgBase: '#0A0A0A'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export default {
|
|||||||
darkMode: false,
|
darkMode: false,
|
||||||
fontFamily:
|
fontFamily:
|
||||||
"Helvetica Neue, -apple-system, BlinkMacSystemFont, Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'",
|
"Helvetica Neue, -apple-system, BlinkMacSystemFont, Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'",
|
||||||
colorText: 'rgba(0,0,0,1)',
|
colorText: '#1F1F1F',
|
||||||
colorPrimary: COLOR_PRIMARY,
|
colorPrimary: COLOR_PRIMARY,
|
||||||
colorSuccess: '#54cc98',
|
colorSuccess: '#54cc98',
|
||||||
colorBorder: '#d3d0d9',
|
colorBorder: '#d3d0d9',
|
||||||
|
|||||||
+1
-1
@@ -103,7 +103,7 @@ html[data-theme='realDark'] {
|
|||||||
--color-spotlight-bg: #3e3e3e;
|
--color-spotlight-bg: #3e3e3e;
|
||||||
--color-esc-hint-bg: rgba(150, 150, 150, 75%);
|
--color-esc-hint-bg: rgba(150, 150, 150, 75%);
|
||||||
|
|
||||||
background: #141414;
|
background: #292929;
|
||||||
|
|
||||||
.ant-result-image {
|
.ant-result-image {
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ export default {
|
|||||||
'{count} new worker has been added to the cluster.',
|
'{count} new worker has been added to the cluster.',
|
||||||
'clusters.addworker.message.success_multiple':
|
'clusters.addworker.message.success_multiple':
|
||||||
'{count} new workers have been added to the cluster.',
|
'{count} new workers have been added to the cluster.',
|
||||||
'clusters.create.serverUrl': 'Server URL',
|
'clusters.create.serverUrl': 'GPUStack Server URL',
|
||||||
'clusters.create.workerConfig': 'Worker Configuration',
|
'clusters.create.workerConfig': 'Worker Configuration',
|
||||||
'clusters.addworker.containerName': 'Worker Container Name',
|
'clusters.addworker.containerName': 'Worker Container Name',
|
||||||
'clusters.addworker.containerName.tips':
|
'clusters.addworker.containerName.tips':
|
||||||
@@ -113,7 +113,7 @@ export default {
|
|||||||
'clusters.table.ip.internal': 'Internal',
|
'clusters.table.ip.internal': 'Internal',
|
||||||
'clusters.table.ip.external': 'External',
|
'clusters.table.ip.external': 'External',
|
||||||
'clusters.form.serverUrl.tips':
|
'clusters.form.serverUrl.tips':
|
||||||
'Specify the server URL accessible from your cloud provider.',
|
'Specify an externally accessible GPUStack service URL if the worker cannot access GPUStack directly.',
|
||||||
'clusters.form.setDefault': 'Set as Default',
|
'clusters.form.setDefault': 'Set as Default',
|
||||||
'clusters.form.setDefault.tips': 'Default for deployment.',
|
'clusters.form.setDefault.tips': 'Default for deployment.',
|
||||||
'clusters.addworker.noClusters': 'No available Docker clusters found',
|
'clusters.addworker.noClusters': 'No available Docker clusters found',
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ export default {
|
|||||||
'{count} new worker has been added to the cluster.',
|
'{count} new worker has been added to the cluster.',
|
||||||
'clusters.addworker.message.success_multiple':
|
'clusters.addworker.message.success_multiple':
|
||||||
'{count} new workers have been added to the cluster.',
|
'{count} new workers have been added to the cluster.',
|
||||||
'clusters.create.serverUrl': 'Server URL',
|
'clusters.create.serverUrl': 'GPUStack Server URL',
|
||||||
'clusters.create.workerConfig': 'Worker Configuration',
|
'clusters.create.workerConfig': 'Worker Configuration',
|
||||||
'clusters.addworker.containerName': 'Worker Container Name',
|
'clusters.addworker.containerName': 'Worker Container Name',
|
||||||
'clusters.addworker.containerName.tips':
|
'clusters.addworker.containerName.tips':
|
||||||
@@ -113,7 +113,7 @@ export default {
|
|||||||
'clusters.table.ip.internal': 'Internal',
|
'clusters.table.ip.internal': 'Internal',
|
||||||
'clusters.table.ip.external': 'External',
|
'clusters.table.ip.external': 'External',
|
||||||
'clusters.form.serverUrl.tips':
|
'clusters.form.serverUrl.tips':
|
||||||
'Specify the server URL accessible from your cloud provider.',
|
'Specify an externally accessible GPUStack service URL if the worker cannot access GPUStack directly.',
|
||||||
'clusters.form.setDefault': 'Set as Default',
|
'clusters.form.setDefault': 'Set as Default',
|
||||||
'clusters.form.setDefault.tips': 'Default for deployment.',
|
'clusters.form.setDefault.tips': 'Default for deployment.',
|
||||||
'clusters.addworker.noClusters': 'No available Docker clusters found',
|
'clusters.addworker.noClusters': 'No available Docker clusters found',
|
||||||
@@ -205,7 +205,7 @@ export default {
|
|||||||
// 73. 'clusters.addworker.cacheVolume.holder': 'e.g. /data/cache (path must start with /)',
|
// 73. 'clusters.addworker.cacheVolume.holder': 'e.g. /data/cache (path must start with /)',
|
||||||
// 74. 'clusters.addworker.message.success_single': '{count} new worker has been added to the cluster.',
|
// 74. 'clusters.addworker.message.success_single': '{count} new worker has been added to the cluster.',
|
||||||
// 75. 'clusters.addworker.message.success_multiple': '{count} new workers have been added to the cluster.',
|
// 75. 'clusters.addworker.message.success_multiple': '{count} new workers have been added to the cluster.',
|
||||||
// 76. 'clusters.create.serverUrl': 'Server URL',
|
// 76. 'clusters.create.serverUrl': 'GPUStack Server URL',
|
||||||
// 77. 'clusters.create.workerConfig': 'Worker Configuration'
|
// 77. 'clusters.create.workerConfig': 'Worker Configuration'
|
||||||
// 78. 'clusters.addworker.containerName': 'Worker Container Name',
|
// 78. 'clusters.addworker.containerName': 'Worker Container Name',
|
||||||
// 79. 'clusters.addworker.containerName.tips':'Specify a name for the worker container.',
|
// 79. 'clusters.addworker.containerName.tips':'Specify a name for the worker container.',
|
||||||
@@ -213,7 +213,7 @@ export default {
|
|||||||
// 78. 'clusters.addworker.dataVolume.tips': 'Specify a data storage path for GPUStack.',
|
// 78. 'clusters.addworker.dataVolume.tips': 'Specify a data storage path for GPUStack.',
|
||||||
// 79. 'clusters.table.ip.internal': 'Internal',
|
// 79. 'clusters.table.ip.internal': 'Internal',
|
||||||
// 80. 'clusters.table.ip.external': 'External',
|
// 80. 'clusters.table.ip.external': 'External',
|
||||||
// 81. 'clusters.form.serverUrl.tips': 'Specify the server URL accessible from your cloud provider.',
|
// 81. 'clusters.form.serverUrl.tips': 'Specify an externally accessible GPUStack service URL if the worker cannot access GPUStack directly.',
|
||||||
// 82. 'clusters.addworker.externalIP.tips': 'Specify an external IP if the worker is in a VPC or private network.',
|
// 82. 'clusters.addworker.externalIP.tips': 'Specify an external IP if the worker is in a VPC or private network.',
|
||||||
// 83. 'clusters.form.setDefault': 'Set as Default',
|
// 83. 'clusters.form.setDefault': 'Set as Default',
|
||||||
// 84. 'clusters.form.setDefault.tips': 'Default for deployment',
|
// 84. 'clusters.form.setDefault.tips': 'Default for deployment',
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ export default {
|
|||||||
'{count} новый воркер был добавлен в кластер.',
|
'{count} новый воркер был добавлен в кластер.',
|
||||||
'clusters.addworker.message.success_multiple':
|
'clusters.addworker.message.success_multiple':
|
||||||
'{count} новых воркеров были добавлены в кластер.',
|
'{count} новых воркеров были добавлены в кластер.',
|
||||||
'clusters.create.serverUrl': 'URL сервера',
|
'clusters.create.serverUrl': 'URL сервера GPUStack',
|
||||||
'clusters.create.workerConfig': 'Конфигурация воркера',
|
'clusters.create.workerConfig': 'Конфигурация воркера',
|
||||||
'clusters.addworker.containerName': 'Имя контейнера воркера',
|
'clusters.addworker.containerName': 'Имя контейнера воркера',
|
||||||
'clusters.addworker.containerName.tips':
|
'clusters.addworker.containerName.tips':
|
||||||
@@ -113,7 +113,7 @@ export default {
|
|||||||
'clusters.table.ip.internal': 'Внутренний',
|
'clusters.table.ip.internal': 'Внутренний',
|
||||||
'clusters.table.ip.external': 'Внешний',
|
'clusters.table.ip.external': 'Внешний',
|
||||||
'clusters.form.serverUrl.tips':
|
'clusters.form.serverUrl.tips':
|
||||||
'Укажите URL сервера, доступный из вашего облачного провайдера.',
|
'Если рабочий узел не может напрямую получить доступ к GPUStack, укажите внешний URL службы GPUStack.',
|
||||||
'clusters.form.setDefault': 'Установить по умолчанию',
|
'clusters.form.setDefault': 'Установить по умолчанию',
|
||||||
'clusters.form.setDefault.tips':
|
'clusters.form.setDefault.tips':
|
||||||
'Использовать по умолчанию для развертывания.',
|
'Использовать по умолчанию для развертывания.',
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ export default {
|
|||||||
'已将 {count} 个新节点添加到集群中。',
|
'已将 {count} 个新节点添加到集群中。',
|
||||||
'clusters.addworker.message.success_multiple':
|
'clusters.addworker.message.success_multiple':
|
||||||
'已将 {count} 个新节点添加到集群中。',
|
'已将 {count} 个新节点添加到集群中。',
|
||||||
'clusters.create.serverUrl': '服务器地址',
|
'clusters.create.serverUrl': 'GPUStack 服务地址',
|
||||||
'clusters.create.workerConfig': '节点配置',
|
'clusters.create.workerConfig': '节点配置',
|
||||||
'clusters.addworker.containerName': '节点容器名称',
|
'clusters.addworker.containerName': '节点容器名称',
|
||||||
'clusters.addworker.containerName.tips': '为节点容器指定一个名称。',
|
'clusters.addworker.containerName.tips': '为节点容器指定一个名称。',
|
||||||
@@ -110,7 +110,8 @@ export default {
|
|||||||
'clusters.addworker.dataVolume.tips': '为 GPUStack 指定数据存储路径。',
|
'clusters.addworker.dataVolume.tips': '为 GPUStack 指定数据存储路径。',
|
||||||
'clusters.table.ip.internal': '内',
|
'clusters.table.ip.internal': '内',
|
||||||
'clusters.table.ip.external': '外',
|
'clusters.table.ip.external': '外',
|
||||||
'clusters.form.serverUrl.tips': '指定可从您的云服务提供商访问的服务器地址。',
|
'clusters.form.serverUrl.tips':
|
||||||
|
'如果节点无法直接访问 GPUStack,则指定一个可访问的外部 GPUStack 服务地址。',
|
||||||
'clusters.form.setDefault': '设为默认',
|
'clusters.form.setDefault': '设为默认',
|
||||||
'clusters.form.setDefault.tips': '部署时的默认集群。',
|
'clusters.form.setDefault.tips': '部署时的默认集群。',
|
||||||
'clusters.addworker.noClusters': '无可用的 Docker 集群',
|
'clusters.addworker.noClusters': '无可用的 Docker 集群',
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import ModalFooter from '@/components/modal-footer';
|
import ModalFooter from '@/components/modal-footer';
|
||||||
import GSDrawer from '@/components/scroller-modal/gs-drawer';
|
import GSDrawer from '@/components/scroller-modal/gs-drawer';
|
||||||
import ColumnWrapper from '@/pages/_components/column-wrapper';
|
import ColumnWrapper from '@/pages/_components/column-wrapper';
|
||||||
|
import { Tag } from 'antd';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
const ModalFooterStyle = {
|
const ModalFooterStyle = {
|
||||||
padding: '16px 24px 8px',
|
padding: '16px 24px 8px',
|
||||||
@@ -9,12 +11,14 @@ const ModalFooterStyle = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type AddModalProps = {
|
type AddModalProps = {
|
||||||
title: string;
|
title: React.ReactNode;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onCancel: () => void;
|
onCancel?: () => void;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
onSubmit: () => void;
|
onSubmit?: () => void;
|
||||||
width?: number;
|
width?: number | string;
|
||||||
|
footer?: React.ReactNode;
|
||||||
|
subTitle?: React.ReactNode;
|
||||||
};
|
};
|
||||||
const FormDrawer: React.FC<AddModalProps> = ({
|
const FormDrawer: React.FC<AddModalProps> = ({
|
||||||
title,
|
title,
|
||||||
@@ -22,11 +26,32 @@ const FormDrawer: React.FC<AddModalProps> = ({
|
|||||||
onCancel,
|
onCancel,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
children,
|
children,
|
||||||
width = 600
|
width = 600,
|
||||||
|
subTitle,
|
||||||
|
footer
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<GSDrawer
|
<GSDrawer
|
||||||
title={title}
|
title={
|
||||||
|
<>
|
||||||
|
{title}
|
||||||
|
{subTitle && (
|
||||||
|
<Tag
|
||||||
|
variant="outlined"
|
||||||
|
style={{
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: 400,
|
||||||
|
marginLeft: 8,
|
||||||
|
borderRadius: 4,
|
||||||
|
borderColor: 'var(--ant-color-border-secondary)',
|
||||||
|
color: 'var(--ant-color-text-secondary)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{subTitle}
|
||||||
|
</Tag>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
}
|
||||||
open={open}
|
open={open}
|
||||||
onClose={onCancel}
|
onClose={onCancel}
|
||||||
destroyOnHidden={true}
|
destroyOnHidden={true}
|
||||||
@@ -43,11 +68,13 @@ const FormDrawer: React.FC<AddModalProps> = ({
|
|||||||
container: { paddingBlock: 0 }
|
container: { paddingBlock: 0 }
|
||||||
}}
|
}}
|
||||||
footer={
|
footer={
|
||||||
<ModalFooter
|
footer ?? (
|
||||||
onOk={onSubmit}
|
<ModalFooter
|
||||||
onCancel={onCancel}
|
onOk={onSubmit}
|
||||||
style={ModalFooterStyle}
|
onCancel={onCancel}
|
||||||
></ModalFooter>
|
style={ModalFooterStyle}
|
||||||
|
></ModalFooter>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ const APIKeys: React.FC = () => {
|
|||||||
handleClickPrimary={handleAddKey}
|
handleClickPrimary={handleAddKey}
|
||||||
handleInputChange={handleNameChange}
|
handleInputChange={handleNameChange}
|
||||||
rowSelection={rowSelection}
|
rowSelection={rowSelection}
|
||||||
width={{ input: 300 }}
|
widths={{ input: 300 }}
|
||||||
></FilterBar>
|
></FilterBar>
|
||||||
<ConfigProvider renderEmpty={renderEmpty}>
|
<ConfigProvider renderEmpty={renderEmpty}>
|
||||||
<Table
|
<Table
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ const BackendList = () => {
|
|||||||
<FilterBar
|
<FilterBar
|
||||||
marginBottom={22}
|
marginBottom={22}
|
||||||
marginTop={30}
|
marginTop={30}
|
||||||
width={{
|
widths={{
|
||||||
input: 300
|
input: 300
|
||||||
}}
|
}}
|
||||||
inputHolder={intl.formatMessage({ id: 'common.filter.name' })}
|
inputHolder={intl.formatMessage({ id: 'common.filter.name' })}
|
||||||
|
|||||||
@@ -171,7 +171,6 @@ const ClusterCreate: React.FC<{
|
|||||||
if (result) {
|
if (result) {
|
||||||
await callback?.(result as ClusterFormData);
|
await callback?.(result as ClusterFormData);
|
||||||
const step = steps[currentStep];
|
const step = steps[currentStep];
|
||||||
step.beforeNext?.();
|
|
||||||
setCurrentStep((prev) => Math.min(prev + 1, steps.length - 1));
|
setCurrentStep((prev) => Math.min(prev + 1, steps.length - 1));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import WorkerList from '@/pages/resources/components/workers';
|
||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import FormDrawer from '../_components/form-drawer';
|
||||||
|
import { ClusterListItem } from './config/types';
|
||||||
|
|
||||||
|
const Content = styled.div`
|
||||||
|
.ant-pagination {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 8px;
|
||||||
|
right: 24px;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
.page-tools {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface ClusterWorkersModalProps {
|
||||||
|
cluster: ClusterListItem;
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ClusterWorkersModal: React.FC<ClusterWorkersModalProps> = ({
|
||||||
|
open,
|
||||||
|
onClose,
|
||||||
|
cluster
|
||||||
|
}) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
return (
|
||||||
|
<FormDrawer
|
||||||
|
title={`${intl.formatMessage({ id: 'clusters.title' })}: ${cluster.name}`}
|
||||||
|
open={open}
|
||||||
|
onCancel={onClose}
|
||||||
|
footer={
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: 56
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
}
|
||||||
|
width={'calc(100vw - 220px)'}
|
||||||
|
>
|
||||||
|
<Content>
|
||||||
|
<WorkerList
|
||||||
|
cluster={cluster}
|
||||||
|
showAddButton={false}
|
||||||
|
showSelect={false}
|
||||||
|
widths={{ input: 360 }}
|
||||||
|
sourceType="cluster"
|
||||||
|
/>
|
||||||
|
</Content>
|
||||||
|
</FormDrawer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ClusterWorkersModal;
|
||||||
@@ -31,6 +31,7 @@ import {
|
|||||||
WORKER_POOLS_API
|
WORKER_POOLS_API
|
||||||
} from './apis';
|
} from './apis';
|
||||||
import ClusterModal from './cluster-modal';
|
import ClusterModal from './cluster-modal';
|
||||||
|
import ClusterWorkersModal from './cluster-workers-modal';
|
||||||
import AddCluster from './components/add-cluster';
|
import AddCluster from './components/add-cluster';
|
||||||
import AddPool from './components/add-pool';
|
import AddPool from './components/add-pool';
|
||||||
import {
|
import {
|
||||||
@@ -48,6 +49,7 @@ import {
|
|||||||
} from './config/types';
|
} from './config/types';
|
||||||
import useAddWorker from './hooks/use-add-worker';
|
import useAddWorker from './hooks/use-add-worker';
|
||||||
import useClusterColumns from './hooks/use-cluster-columns';
|
import useClusterColumns from './hooks/use-cluster-columns';
|
||||||
|
import { useClusterWorkers } from './hooks/use-cluster-workers';
|
||||||
import useCreateCluster from './hooks/use-create-cluster';
|
import useCreateCluster from './hooks/use-create-cluster';
|
||||||
|
|
||||||
const Clusters: React.FC = () => {
|
const Clusters: React.FC = () => {
|
||||||
@@ -82,6 +84,11 @@ const Clusters: React.FC = () => {
|
|||||||
useCreateCluster({
|
useCreateCluster({
|
||||||
refresh: handleSearch
|
refresh: handleSearch
|
||||||
});
|
});
|
||||||
|
const {
|
||||||
|
clusterWorkersModalStatus,
|
||||||
|
openClusterWorkersModal,
|
||||||
|
closeClusterWorkersModal
|
||||||
|
} = useClusterWorkers();
|
||||||
|
|
||||||
const [openAddModal, setOpenAddModal] = useState<{
|
const [openAddModal, setOpenAddModal] = useState<{
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -311,7 +318,7 @@ const Clusters: React.FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const columns = useClusterColumns(handleSelect);
|
const columns = useClusterColumns(handleSelect, openClusterWorkersModal);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -320,7 +327,7 @@ const Clusters: React.FC = () => {
|
|||||||
showSelect={false}
|
showSelect={false}
|
||||||
marginBottom={22}
|
marginBottom={22}
|
||||||
marginTop={30}
|
marginTop={30}
|
||||||
width={{ input: 300 }}
|
widths={{ input: 300 }}
|
||||||
buttonText={intl.formatMessage({ id: 'clusters.button.add' })}
|
buttonText={intl.formatMessage({ id: 'clusters.button.add' })}
|
||||||
rowSelection={rowSelection}
|
rowSelection={rowSelection}
|
||||||
handleInputChange={handleNameChange}
|
handleInputChange={handleNameChange}
|
||||||
@@ -416,6 +423,11 @@ const Clusters: React.FC = () => {
|
|||||||
open={clusterModalStatus.open}
|
open={clusterModalStatus.open}
|
||||||
onClose={closeClusterModal}
|
onClose={closeClusterModal}
|
||||||
></ClusterModal>
|
></ClusterModal>
|
||||||
|
<ClusterWorkersModal
|
||||||
|
open={clusterWorkersModalStatus.open}
|
||||||
|
onClose={closeClusterWorkersModal}
|
||||||
|
cluster={clusterWorkersModalStatus.cluster}
|
||||||
|
></ClusterWorkersModal>
|
||||||
{AddWorkerModal}
|
{AddWorkerModal}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ const Footer = styled.div`
|
|||||||
type AddWorkerProps = {
|
type AddWorkerProps = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
provider: ProviderType;
|
provider: ProviderType;
|
||||||
title: string;
|
title: React.ReactNode;
|
||||||
clusterList?: Global.BaseOption<number, ClusterListItem>[];
|
clusterList?: Global.BaseOption<number, ClusterListItem>[];
|
||||||
clusterLoading?: boolean;
|
clusterLoading?: boolean;
|
||||||
stepList: StepName[];
|
stepList: StepName[];
|
||||||
|
|||||||
@@ -310,6 +310,11 @@ const PoolForm: React.FC<AddModalProps> = forwardRef((props, ref) => {
|
|||||||
title={title}
|
title={title}
|
||||||
collapsible={collapsible}
|
collapsible={collapsible}
|
||||||
onToggle={onToggle}
|
onToggle={onToggle}
|
||||||
|
styles={{
|
||||||
|
root: {
|
||||||
|
background: 'unset'
|
||||||
|
}
|
||||||
|
}}
|
||||||
deleteBtn={
|
deleteBtn={
|
||||||
showDelete && (
|
showDelete && (
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ const WorkerPools = () => {
|
|||||||
showSelect={false}
|
showSelect={false}
|
||||||
marginBottom={22}
|
marginBottom={22}
|
||||||
marginTop={22}
|
marginTop={22}
|
||||||
width={{ input: 300 }}
|
widths={{ input: 300 }}
|
||||||
buttonText={intl.formatMessage({ id: 'clusters.button.addNodePool' })}
|
buttonText={intl.formatMessage({ id: 'clusters.button.addNodePool' })}
|
||||||
rowSelection={rowSelection}
|
rowSelection={rowSelection}
|
||||||
handleInputChange={handleNameChange}
|
handleInputChange={handleNameChange}
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ const Credentials: React.FC = () => {
|
|||||||
handleInputChange={handleNameChange}
|
handleInputChange={handleNameChange}
|
||||||
handleClickPrimary={handleAddCredential}
|
handleClickPrimary={handleAddCredential}
|
||||||
rowSelection={rowSelection}
|
rowSelection={rowSelection}
|
||||||
width={{ input: 300 }}
|
widths={{ input: 300 }}
|
||||||
></FilterBar>
|
></FilterBar>
|
||||||
|
|
||||||
<ConfigProvider renderEmpty={renderEmpty}>
|
<ConfigProvider renderEmpty={renderEmpty}>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import AddWorker from '@/pages/cluster-management/components/add-worker';
|
import AddWorker from '@/pages/cluster-management/components/add-worker';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { message } from 'antd';
|
import { message, Tag } from 'antd';
|
||||||
import { useMemo, useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import { StepName } from '../components/add-worker/config';
|
import { StepName } from '../components/add-worker/config';
|
||||||
import {
|
import {
|
||||||
ClusterStatusValueMap,
|
ClusterStatusValueMap,
|
||||||
@@ -20,7 +20,7 @@ const useAddWorker = (props: {
|
|||||||
const [openAddWorker, setOpenAddWorker] = useState<{
|
const [openAddWorker, setOpenAddWorker] = useState<{
|
||||||
open: boolean;
|
open: boolean;
|
||||||
provider: ProviderType;
|
provider: ProviderType;
|
||||||
title: string;
|
title: React.ReactNode;
|
||||||
cluster_id: number | null;
|
cluster_id: number | null;
|
||||||
}>({
|
}>({
|
||||||
open: false,
|
open: false,
|
||||||
@@ -56,7 +56,26 @@ const useAddWorker = (props: {
|
|||||||
|
|
||||||
setOpenAddWorker({
|
setOpenAddWorker({
|
||||||
open: true,
|
open: true,
|
||||||
title: title,
|
title: (
|
||||||
|
<span>
|
||||||
|
{title}
|
||||||
|
{row.name && (
|
||||||
|
<Tag
|
||||||
|
variant="outlined"
|
||||||
|
style={{
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: 400,
|
||||||
|
marginLeft: 8,
|
||||||
|
borderRadius: 4,
|
||||||
|
borderColor: 'var(--ant-color-border-secondary)',
|
||||||
|
color: 'var(--ant-color-text-secondary)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{intl.formatMessage({ id: 'clusters.title' })}: {row.name}
|
||||||
|
</Tag>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
provider: row.provider as ProviderType,
|
provider: row.provider as ProviderType,
|
||||||
cluster_id: clusterId
|
cluster_id: clusterId
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import StatusTag from '@/components/status-tag';
|
|||||||
import { tableSorter } from '@/config/settings';
|
import { tableSorter } from '@/config/settings';
|
||||||
import { StarFilled } from '@ant-design/icons';
|
import { StarFilled } from '@ant-design/icons';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Tooltip } from 'antd';
|
import { Tooltip, Typography } from 'antd';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
@@ -27,7 +27,8 @@ const setActionsItems = (row: ClusterListItem) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const useClusterColumns = (
|
const useClusterColumns = (
|
||||||
handleSelect: (val: string, record: ClusterListItem) => void
|
handleSelect: (val: string, record: ClusterListItem) => void,
|
||||||
|
onViewWorkers: (record: ClusterListItem) => void
|
||||||
): SealColumnProps[] => {
|
): SealColumnProps[] => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
@@ -40,7 +41,15 @@ const useClusterColumns = (
|
|||||||
span: 3,
|
span: 3,
|
||||||
render: (text: string, record: ClusterListItem) => (
|
render: (text: string, record: ClusterListItem) => (
|
||||||
<>
|
<>
|
||||||
<AutoTooltip ghost>{text}</AutoTooltip>
|
<AutoTooltip ghost title={text}>
|
||||||
|
{record.workers > 0 ? (
|
||||||
|
<Typography.Link onClick={() => onViewWorkers(record)}>
|
||||||
|
{record.name}
|
||||||
|
</Typography.Link>
|
||||||
|
) : (
|
||||||
|
text
|
||||||
|
)}
|
||||||
|
</AutoTooltip>
|
||||||
{record.is_default && (
|
{record.is_default && (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={intl.formatMessage({
|
title={intl.formatMessage({
|
||||||
@@ -127,7 +136,7 @@ const useClusterColumns = (
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}, [handleSelect]);
|
}, [handleSelect, onViewWorkers]);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useClusterColumns;
|
export default useClusterColumns;
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { ClusterListItem } from '../config/types';
|
||||||
|
|
||||||
|
export const useClusterWorkers = () => {
|
||||||
|
const [clusterWorkersModalStatus, setClusterWorkersModalStatus] = useState<{
|
||||||
|
open: boolean;
|
||||||
|
cluster: ClusterListItem;
|
||||||
|
}>({
|
||||||
|
open: false,
|
||||||
|
cluster: {} as ClusterListItem
|
||||||
|
});
|
||||||
|
|
||||||
|
const openClusterWorkersModal = (cluster: ClusterListItem) => {
|
||||||
|
setClusterWorkersModalStatus({
|
||||||
|
open: true,
|
||||||
|
cluster
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeClusterWorkersModal = () => {
|
||||||
|
setClusterWorkersModalStatus({
|
||||||
|
open: false,
|
||||||
|
cluster: {} as ClusterListItem
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
clusterWorkersModalStatus,
|
||||||
|
openClusterWorkersModal,
|
||||||
|
closeClusterWorkersModal
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -3,7 +3,6 @@ import DropdownButtons from '@/components/drop-down-buttons';
|
|||||||
import { SealColumnProps } from '@/components/seal-table/types';
|
import { SealColumnProps } from '@/components/seal-table/types';
|
||||||
import { DeleteOutlined, EditOutlined } from '@ant-design/icons';
|
import { DeleteOutlined, EditOutlined } from '@ant-design/icons';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import type { SortOrder } from 'antd/es/table/interface';
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
@@ -29,7 +28,7 @@ const actionItems = [
|
|||||||
|
|
||||||
const usePoolsColumns = (
|
const usePoolsColumns = (
|
||||||
handleSelect: (val: string, record: ListItem) => void,
|
handleSelect: (val: string, record: ListItem) => void,
|
||||||
sortOrder?: SortOrder
|
sortOrder?: string[]
|
||||||
): SealColumnProps[] => {
|
): SealColumnProps[] => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
@@ -83,7 +82,6 @@ const usePoolsColumns = (
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
SHOWTITLE
|
|
||||||
ghost
|
ghost
|
||||||
minWidth={20}
|
minWidth={20}
|
||||||
>
|
>
|
||||||
@@ -162,7 +160,6 @@ const usePoolsColumns = (
|
|||||||
key: 'created_at',
|
key: 'created_at',
|
||||||
span: 4,
|
span: 4,
|
||||||
showSorterTootip: false,
|
showSorterTootip: false,
|
||||||
sortOrder: sortOrder,
|
|
||||||
sorter: false,
|
sorter: false,
|
||||||
ellipsis: {
|
ellipsis: {
|
||||||
showTitle: false
|
showTitle: false
|
||||||
@@ -192,7 +189,7 @@ const usePoolsColumns = (
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}, [sortOrder, handleSelect]);
|
}, [handleSelect]);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default usePoolsColumns;
|
export default usePoolsColumns;
|
||||||
|
|||||||
@@ -14,11 +14,7 @@ const renderCardItem = (data: {
|
|||||||
}) => {
|
}) => {
|
||||||
const { label, value, bgColor } = data;
|
const { label, value, bgColor } = data;
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card variant="borderless" className={styles['card-body']}>
|
||||||
variant="borderless"
|
|
||||||
style={{ background: 'var(--color-white-1)' }}
|
|
||||||
className={styles['card-body']}
|
|
||||||
>
|
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<div className="label text-secondary">{label}</div>
|
<div className="label text-secondary">{label}</div>
|
||||||
<div className="value font-600 font-size-16">{value}</div>
|
<div className="value font-600 font-size-16">{value}</div>
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ const Catalog: React.FC = () => {
|
|||||||
handleInputChange={handleNameChange}
|
handleInputChange={handleNameChange}
|
||||||
selectOptions={categoryOptions}
|
selectOptions={categoryOptions}
|
||||||
buttonIcon={<SearchOutlined />}
|
buttonIcon={<SearchOutlined />}
|
||||||
width={{ input: 230, select: 200 }}
|
widths={{ input: 230, select: 200 }}
|
||||||
></FilterBar>
|
></FilterBar>
|
||||||
<ScrollerContext.Provider
|
<ScrollerContext.Provider
|
||||||
value={{
|
value={{
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { getRequestId } from '@/atoms/models';
|
import { getRequestId } from '@/atoms/models';
|
||||||
import BaseSelect from '@/components/seal-form/base/select';
|
import BaseSelect from '@/components/seal-form/base/select';
|
||||||
import SimpleOverlay from '@/components/simple-overlay';
|
import SimpleOverlay from '@/components/simple-overlay';
|
||||||
import { createAxiosToken } from '@/hooks/use-chunk-request';
|
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Empty, Spin } from 'antd';
|
import { Empty, Spin } from 'antd';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
@@ -14,13 +13,8 @@ import React, {
|
|||||||
} from 'react';
|
} from 'react';
|
||||||
import 'simplebar-react/dist/simplebar.min.css';
|
import 'simplebar-react/dist/simplebar.min.css';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import {
|
import { queryHuggingfaceModelFiles, queryModelScopeModelFiles } from '../apis';
|
||||||
evaluationsModelSpec,
|
|
||||||
queryHuggingfaceModelFiles,
|
|
||||||
queryModelScopeModelFiles
|
|
||||||
} from '../apis';
|
|
||||||
import { modelSourceMap } from '../config';
|
import { modelSourceMap } from '../config';
|
||||||
import { backendOptionsMap } from '../config/backend-parameters';
|
|
||||||
import '../style/hf-model-file.less';
|
import '../style/hf-model-file.less';
|
||||||
import FileSkeleton from './file-skeleton';
|
import FileSkeleton from './file-skeleton';
|
||||||
import ModelFileItem from './model-file-item';
|
import ModelFileItem from './model-file-item';
|
||||||
@@ -226,70 +220,6 @@ const HFModelFile: React.FC<HFModelFileProps> = forwardRef((props, ref) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getEvaluateResults = async (repoList: any[]) => {
|
|
||||||
try {
|
|
||||||
checkTokenRef.current?.cancel?.();
|
|
||||||
checkTokenRef.current = createAxiosToken();
|
|
||||||
const evaluations = await evaluationsModelSpec(
|
|
||||||
{
|
|
||||||
model_specs: repoList
|
|
||||||
},
|
|
||||||
{
|
|
||||||
token: checkTokenRef.current.token
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return evaluations.results || [];
|
|
||||||
} catch (error) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleEvaluate = async (list: any[]) => {
|
|
||||||
if (isDownload) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const evaluateFileList = list.map((item: any) => {
|
|
||||||
// TODO: llamabox is remove from backend options, user should select a available backend
|
|
||||||
return {
|
|
||||||
backend: backendOptionsMap.llamaBox,
|
|
||||||
source: modelSource,
|
|
||||||
...(modelSource === modelSourceMap.huggingface_value
|
|
||||||
? {
|
|
||||||
huggingface_repo_id: props.selectedModel.name,
|
|
||||||
huggingface_filename: item.fakeName
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
model_scope_model_id: props.selectedModel.name,
|
|
||||||
model_scope_file_path: item.fakeName
|
|
||||||
})
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
setIsEvaluating(true);
|
|
||||||
const evaluationList = await getEvaluateResults(evaluateFileList);
|
|
||||||
|
|
||||||
const resultList = _.map(list, (item: any, index: number) => {
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
evaluateResult: evaluationList[index]
|
|
||||||
};
|
|
||||||
});
|
|
||||||
const currentItem = _.find(
|
|
||||||
resultList,
|
|
||||||
(item: any) => item.path === currentPathRef.current
|
|
||||||
);
|
|
||||||
|
|
||||||
if (currentItem) {
|
|
||||||
onSelectFileAfterEvaluate?.(currentItem);
|
|
||||||
}
|
|
||||||
setDataSource({ fileList: resultList, loading: false });
|
|
||||||
setIsEvaluating(false);
|
|
||||||
} catch (error) {
|
|
||||||
setIsEvaluating(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleFetchModelFiles = async () => {
|
const handleFetchModelFiles = async () => {
|
||||||
if (!props.selectedModel.name) {
|
if (!props.selectedModel.name) {
|
||||||
setDataSource({ fileList: [], loading: false });
|
setDataSource({ fileList: [], loading: false });
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ const GPUList: React.FC = () => {
|
|||||||
handleSelectChange={handleClusterChange}
|
handleSelectChange={handleClusterChange}
|
||||||
selectOptions={clusterList}
|
selectOptions={clusterList}
|
||||||
showSelect
|
showSelect
|
||||||
width={{ input: 200 }}
|
widths={{ input: 200 }}
|
||||||
></FilterBar>
|
></FilterBar>
|
||||||
<ConfigProvider renderEmpty={renderEmpty}>
|
<ConfigProvider renderEmpty={renderEmpty}>
|
||||||
<Table
|
<Table
|
||||||
@@ -110,6 +110,7 @@ const GPUList: React.FC = () => {
|
|||||||
dataSource={dataSource.dataList}
|
dataSource={dataSource.dataList}
|
||||||
loading={dataSource.loading}
|
loading={dataSource.loading}
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
|
scroll={{ x: true }}
|
||||||
onChange={handleTableChange}
|
onChange={handleTableChange}
|
||||||
pagination={{
|
pagination={{
|
||||||
showSizeChanger: true,
|
showSizeChanger: true,
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ const WorkerDetailContent: React.FC<{ worker_id: number | undefined }> = ({
|
|||||||
selectHolder={intl.formatMessage({ id: 'resources.filter.status' })}
|
selectHolder={intl.formatMessage({ id: 'resources.filter.status' })}
|
||||||
showSelect={true}
|
showSelect={true}
|
||||||
selectOptions={statusList}
|
selectOptions={statusList}
|
||||||
width={{ input: 300 }}
|
widths={{ input: 300 }}
|
||||||
></FilterBar>
|
></FilterBar>
|
||||||
<ConfigProvider renderEmpty={renderEmpty}>
|
<ConfigProvider renderEmpty={renderEmpty}>
|
||||||
<Table
|
<Table
|
||||||
|
|||||||
@@ -26,7 +26,19 @@ import useWorkerMaintenance from '../hooks/use-worker-maintenance';
|
|||||||
import UpdateLabels from './update-labels';
|
import UpdateLabels from './update-labels';
|
||||||
import WorkerDetailModal from './worker-detail-modal';
|
import WorkerDetailModal from './worker-detail-modal';
|
||||||
|
|
||||||
const Workers: React.FC = () => {
|
const Workers: React.FC<{
|
||||||
|
cluster: ClusterListItem;
|
||||||
|
showSelect?: boolean;
|
||||||
|
showAddButton?: boolean;
|
||||||
|
widths?: { input: number };
|
||||||
|
sourceType?: string;
|
||||||
|
}> = ({
|
||||||
|
cluster,
|
||||||
|
showSelect = true,
|
||||||
|
showAddButton = true,
|
||||||
|
widths = { input: 200 },
|
||||||
|
sourceType = 'resources'
|
||||||
|
}) => {
|
||||||
const {
|
const {
|
||||||
dataSource,
|
dataSource,
|
||||||
rowSelection,
|
rowSelection,
|
||||||
@@ -48,7 +60,10 @@ const Workers: React.FC = () => {
|
|||||||
events: ['UPDATE', 'DELETE', 'INSERT'],
|
events: ['UPDATE', 'DELETE', 'INSERT'],
|
||||||
contentForDelete: 'resources.worker',
|
contentForDelete: 'resources.worker',
|
||||||
watch: true,
|
watch: true,
|
||||||
API: WORKERS_API
|
API: WORKERS_API,
|
||||||
|
defaultQueryParams: {
|
||||||
|
cluster_id: cluster?.id || undefined
|
||||||
|
}
|
||||||
});
|
});
|
||||||
const { TerminalPanel, terminals, handleAddTerminal } = useTerminalTabs();
|
const { TerminalPanel, terminals, handleAddTerminal } = useTerminalTabs();
|
||||||
const { MaintenanceModal, handleStopMaintenance, setOpenStatus } =
|
const { MaintenanceModal, handleStopMaintenance, setOpenStatus } =
|
||||||
@@ -233,7 +248,8 @@ const Workers: React.FC = () => {
|
|||||||
loadend: dataSource.loadend,
|
loadend: dataSource.loadend,
|
||||||
firstLoad: extraStatus.firstLoad,
|
firstLoad: extraStatus.firstLoad,
|
||||||
sortOrder,
|
sortOrder,
|
||||||
handleSelect
|
handleSelect,
|
||||||
|
sourceType
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -245,7 +261,7 @@ const Workers: React.FC = () => {
|
|||||||
<>
|
<>
|
||||||
<PageBox>
|
<PageBox>
|
||||||
<FilterBar
|
<FilterBar
|
||||||
showSelect={true}
|
showSelect={showSelect}
|
||||||
selectHolder={intl.formatMessage({ id: 'clusters.filterBy.cluster' })}
|
selectHolder={intl.formatMessage({ id: 'clusters.filterBy.cluster' })}
|
||||||
marginBottom={22}
|
marginBottom={22}
|
||||||
marginTop={30}
|
marginTop={30}
|
||||||
@@ -253,11 +269,11 @@ const Workers: React.FC = () => {
|
|||||||
handleDeleteByBatch={handleDeleteBatch}
|
handleDeleteByBatch={handleDeleteBatch}
|
||||||
handleSearch={handleSearch}
|
handleSearch={handleSearch}
|
||||||
handleSelectChange={handleClusterChange}
|
handleSelectChange={handleClusterChange}
|
||||||
handleClickPrimary={handleOnAddWorker}
|
handleClickPrimary={showAddButton ? handleOnAddWorker : undefined}
|
||||||
handleInputChange={handleNameChange}
|
handleInputChange={handleNameChange}
|
||||||
rowSelection={rowSelection}
|
rowSelection={rowSelection}
|
||||||
selectOptions={clusterData.list}
|
selectOptions={clusterData.list}
|
||||||
width={{ input: 200 }}
|
widths={widths}
|
||||||
></FilterBar>
|
></FilterBar>
|
||||||
<ConfigProvider renderEmpty={renderEmpty}>
|
<ConfigProvider renderEmpty={renderEmpty}>
|
||||||
<Table
|
<Table
|
||||||
@@ -269,6 +285,7 @@ const Workers: React.FC = () => {
|
|||||||
dataSource={dataSource.dataList}
|
dataSource={dataSource.dataList}
|
||||||
loading={dataSource.loading}
|
loading={dataSource.loading}
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
|
scroll={{ x: true }}
|
||||||
onChange={handleTableChange}
|
onChange={handleTableChange}
|
||||||
rowSelection={rowSelection}
|
rowSelection={rowSelection}
|
||||||
pagination={{
|
pagination={{
|
||||||
|
|||||||
@@ -232,6 +232,7 @@ const useWorkerColumns = ({
|
|||||||
loadend,
|
loadend,
|
||||||
firstLoad,
|
firstLoad,
|
||||||
sortOrder,
|
sortOrder,
|
||||||
|
sourceType,
|
||||||
handleSelect
|
handleSelect
|
||||||
}: {
|
}: {
|
||||||
clusterData: {
|
clusterData: {
|
||||||
@@ -241,6 +242,7 @@ const useWorkerColumns = ({
|
|||||||
loadend: boolean;
|
loadend: boolean;
|
||||||
firstLoad: boolean;
|
firstLoad: boolean;
|
||||||
sortOrder: string[];
|
sortOrder: string[];
|
||||||
|
sourceType: string;
|
||||||
handleSelect: (action: string, record: ListItem) => void;
|
handleSelect: (action: string, record: ListItem) => void;
|
||||||
}): ColumnsType<ListItem> => {
|
}): ColumnsType<ListItem> => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
@@ -272,7 +274,10 @@ const useWorkerColumns = ({
|
|||||||
return useMemo<ColumnsType<ListItem>>(
|
return useMemo<ColumnsType<ListItem>>(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
title: intl.formatMessage({ id: 'common.table.name' }),
|
title:
|
||||||
|
sourceType === 'resources'
|
||||||
|
? intl.formatMessage({ id: 'common.table.name' })
|
||||||
|
: intl.formatMessage({ id: 'resources.table.workername' }),
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
width: 100,
|
width: 100,
|
||||||
sorter: tableSorter(1),
|
sorter: tableSorter(1),
|
||||||
@@ -291,6 +296,7 @@ const useWorkerColumns = ({
|
|||||||
{
|
{
|
||||||
title: intl.formatMessage({ id: 'clusters.title' }),
|
title: intl.formatMessage({ id: 'clusters.title' }),
|
||||||
dataIndex: 'cluster_id',
|
dataIndex: 'cluster_id',
|
||||||
|
hidden: sourceType === 'cluster',
|
||||||
render: (id: number) => (
|
render: (id: number) => (
|
||||||
<AutoTooltip ghost maxWidth={240}>
|
<AutoTooltip ghost maxWidth={240}>
|
||||||
{_.get(clusterData.data, id, '')}
|
{_.get(clusterData.data, id, '')}
|
||||||
@@ -402,7 +408,7 @@ const useWorkerColumns = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[intl, sortOrder, clusterData, loadend, firstLoad, handleSelect]
|
[intl, sourceType, sortOrder, clusterData, loadend, firstLoad, handleSelect]
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ const Users: React.FC = () => {
|
|||||||
handleClickPrimary={handleAddUser}
|
handleClickPrimary={handleAddUser}
|
||||||
handleInputChange={handleNameChange}
|
handleInputChange={handleNameChange}
|
||||||
rowSelection={rowSelection}
|
rowSelection={rowSelection}
|
||||||
width={{ input: 300 }}
|
widths={{ input: 300 }}
|
||||||
></FilterBar>
|
></FilterBar>
|
||||||
<ConfigProvider renderEmpty={renderEmpty}>
|
<ConfigProvider renderEmpty={renderEmpty}>
|
||||||
<Table
|
<Table
|
||||||
|
|||||||
Reference in New Issue
Block a user