fix: playground formref is invalid
This commit is contained in:
@@ -54,3 +54,10 @@ export const allRegionInstanceTypeListAtom = atom<
|
|||||||
>([]);
|
>([]);
|
||||||
|
|
||||||
export const fromClusterCreationAtom = atom(false);
|
export const fromClusterCreationAtom = atom(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* for temporary cluster session storage during creation/editing
|
||||||
|
*/
|
||||||
|
export const clusterSessionAtom = atom<{
|
||||||
|
firstAddWorker: boolean;
|
||||||
|
} | null>(null);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { createFromIconfontCN } from '@ant-design/icons';
|
|||||||
// import './iconfont/iconfont.js';
|
// import './iconfont/iconfont.js';
|
||||||
|
|
||||||
const IconFont = createFromIconfontCN({
|
const IconFont = createFromIconfontCN({
|
||||||
scriptUrl: '//at.alicdn.com/t/c/font_4613488_ga8perwfeom.js'
|
scriptUrl: '//at.alicdn.com/t/c/font_4613488_9h4e8ttfsau.js'
|
||||||
});
|
});
|
||||||
|
|
||||||
export default IconFont;
|
export default IconFont;
|
||||||
|
|||||||
@@ -2,15 +2,40 @@ import { Segmented, type SegmentedProps } from 'antd';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
interface ThemeType {
|
||||||
|
color: string;
|
||||||
|
itemColorHover: string;
|
||||||
|
itemSelectedColor: string;
|
||||||
|
thumbBgColor: string;
|
||||||
|
fontWeight: number;
|
||||||
|
}
|
||||||
interface SegmentLineProps extends SegmentedProps {
|
interface SegmentLineProps extends SegmentedProps {
|
||||||
height?: number;
|
height?: number;
|
||||||
|
theme: 'dark' | 'light';
|
||||||
}
|
}
|
||||||
|
|
||||||
const SegmentedQwrapper = styled.div<{ $height: number }>`
|
const darkTheme: ThemeType = {
|
||||||
|
color: 'var(--color-white-tertiary)',
|
||||||
|
itemColorHover: 'var(--color-white-secondary)',
|
||||||
|
itemSelectedColor: 'var(--color-white-primary)',
|
||||||
|
thumbBgColor: 'var(--color-white-primary)',
|
||||||
|
fontWeight: 400
|
||||||
|
};
|
||||||
|
|
||||||
|
const lightTheme: ThemeType = {
|
||||||
|
color: 'var(--ant-segmented-item-color)',
|
||||||
|
itemColorHover: 'var(--ant-segmented-item-hover-color)',
|
||||||
|
itemSelectedColor: 'var(--ant-segmented-item-selected-color)',
|
||||||
|
thumbBgColor: 'var(--ant-color-primary)',
|
||||||
|
fontWeight: 500
|
||||||
|
};
|
||||||
|
|
||||||
|
const SegmentedQwrapper = styled.div<{ $height: number; $theme: ThemeType }>`
|
||||||
.ant-segmented.segment-line {
|
.ant-segmented.segment-line {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
color: var(--color-white-tertiary);
|
color: ${(props) => props.$theme.color};
|
||||||
|
font-weight: ${(props) => props.$theme.fontWeight};
|
||||||
border: none;
|
border: none;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
height: ${(props) => props.$height}px;
|
height: ${(props) => props.$height}px;
|
||||||
@@ -21,14 +46,14 @@ const SegmentedQwrapper = styled.div<{ $height: number }>`
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
.ant-segmented-item:hover {
|
.ant-segmented-item:hover {
|
||||||
color: var(--color-white-secondary);
|
color: ${(props) => props.$theme.itemColorHover};
|
||||||
}
|
}
|
||||||
.ant-segmented-thumb {
|
.ant-segmented-thumb {
|
||||||
height: 2px;
|
height: 2px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
top: unset;
|
top: unset;
|
||||||
background-color: var(--color-white-primary) !important;
|
background-color: ${(props) => props.$theme.thumbBgColor} !important;
|
||||||
}
|
}
|
||||||
.ant-segmented-item-label {
|
.ant-segmented-item-label {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -42,7 +67,7 @@ const SegmentedQwrapper = styled.div<{ $height: number }>`
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
&::after {
|
&::after {
|
||||||
background-color: var(--color-white-primary) !important;
|
background-color: ${(props) => props.$theme.thumbBgColor} !important;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 0px;
|
height: 0px;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
@@ -53,7 +78,8 @@ const SegmentedQwrapper = styled.div<{ $height: number }>`
|
|||||||
}
|
}
|
||||||
.ant-segmented-item-selected {
|
.ant-segmented-item-selected {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
color: var(--color-white-primary);
|
box-shadow: none;
|
||||||
|
color: ${(props) => props.$theme.itemSelectedColor};
|
||||||
&::after {
|
&::after {
|
||||||
height: 2px;
|
height: 2px;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@@ -68,11 +94,15 @@ const SegmentLine: React.FC<SegmentLineProps> = (props) => {
|
|||||||
size = 'small',
|
size = 'small',
|
||||||
options = [],
|
options = [],
|
||||||
className = 'segment-line',
|
className = 'segment-line',
|
||||||
|
theme = 'dark',
|
||||||
...rest
|
...rest
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SegmentedQwrapper $height={height}>
|
<SegmentedQwrapper
|
||||||
|
$height={height}
|
||||||
|
$theme={theme === 'dark' ? darkTheme : lightTheme}
|
||||||
|
>
|
||||||
<Segmented
|
<Segmented
|
||||||
{...rest}
|
{...rest}
|
||||||
size={size}
|
size={size}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
.ant-pro-footer-bar {
|
.ant-pro-footer-bar {
|
||||||
padding-inline: 0;
|
padding-inline: 0;
|
||||||
border-block-start: 1px solid rgba(5, 5, 5, 6%);
|
border-block-start: 1px solid rgba(5, 5, 5, 6%);
|
||||||
background-color: #f5f5f5;
|
// background-color: #f5f5f5;
|
||||||
border-radius: 4px 4px 0 0;
|
border-radius: 4px 4px 0 0;
|
||||||
|
|
||||||
.ant-pro-footer-bar-left {
|
.ant-pro-footer-bar-left {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import IconFont from '@/components/icon-font';
|
import IconFont from '@/components/icon-font';
|
||||||
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 SegmentLine from '@/components/segment-line';
|
||||||
import { PageAction } from '@/config';
|
import { PageAction } from '@/config';
|
||||||
import { PageActionType } from '@/config/types';
|
import { PageActionType } from '@/config/types';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
@@ -33,7 +34,8 @@ const SegmentedInner = styled(Segmented)`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const SegmentedHeader = styled.div`
|
const SegmentedHeader = styled.div`
|
||||||
padding: 16px 24px;
|
margin: 16px 24px;
|
||||||
|
border-bottom: 1px solid var(--ant-color-split);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface AddModalProps {
|
interface AddModalProps {
|
||||||
@@ -203,7 +205,8 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
|||||||
footer={false}
|
footer={false}
|
||||||
>
|
>
|
||||||
<SegmentedHeader>
|
<SegmentedHeader>
|
||||||
<SegmentedInner
|
<SegmentLine
|
||||||
|
theme="light"
|
||||||
onChange={(value) => setActiveKey(value as string)}
|
onChange={(value) => setActiveKey(value as string)}
|
||||||
options={[
|
options={[
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { expandKeysAtom } from '@/atoms/clusters';
|
import { clusterSessionAtom, expandKeysAtom } from '@/atoms/clusters';
|
||||||
import DeleteModal from '@/components/delete-modal';
|
import DeleteModal from '@/components/delete-modal';
|
||||||
import IconFont from '@/components/icon-font';
|
import IconFont from '@/components/icon-font';
|
||||||
import { FilterBar } from '@/components/page-tools';
|
import { FilterBar } from '@/components/page-tools';
|
||||||
@@ -10,7 +10,7 @@ import useExpandedRowKeys from '@/hooks/use-expanded-row-keys';
|
|||||||
import useTableFetch from '@/hooks/use-table-fetch';
|
import useTableFetch from '@/hooks/use-table-fetch';
|
||||||
import useWatchList from '@/hooks/use-watch-list';
|
import useWatchList from '@/hooks/use-watch-list';
|
||||||
import AddWorker from '@/pages/cluster-management/components/add-worker';
|
import AddWorker from '@/pages/cluster-management/components/add-worker';
|
||||||
import { useIntl, useNavigate, useSearchParams } from '@umijs/max';
|
import { useIntl, useNavigate } from '@umijs/max';
|
||||||
import { useMemoizedFn } from 'ahooks';
|
import { useMemoizedFn } from 'ahooks';
|
||||||
import { Button, message } from 'antd';
|
import { Button, message } from 'antd';
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
@@ -32,7 +32,11 @@ import AddCluster from './components/add-cluster';
|
|||||||
import AddPool from './components/add-pool';
|
import AddPool from './components/add-pool';
|
||||||
import PoolRows from './components/pool-rows';
|
import PoolRows from './components/pool-rows';
|
||||||
import RegisterCluster from './components/register-cluster';
|
import RegisterCluster from './components/register-cluster';
|
||||||
import { ProviderType, ProviderValueMap } from './config';
|
import {
|
||||||
|
ClusterStatusValueMap,
|
||||||
|
ProviderType,
|
||||||
|
ProviderValueMap
|
||||||
|
} from './config';
|
||||||
import {
|
import {
|
||||||
ClusterListItem,
|
ClusterListItem,
|
||||||
ClusterFormData as FormData,
|
ClusterFormData as FormData,
|
||||||
@@ -61,15 +65,10 @@ const Clusters: React.FC = () => {
|
|||||||
contentForDelete: 'menu.clusterManagement.clusters'
|
contentForDelete: 'menu.clusterManagement.clusters'
|
||||||
});
|
});
|
||||||
const { watchDataList: allWorkerPoolList } = useWatchList(WORKER_POOLS_API);
|
const { watchDataList: allWorkerPoolList } = useWatchList(WORKER_POOLS_API);
|
||||||
const [expandAtom, setExpandAtom] = useAtom(expandKeysAtom);
|
const [expandAtom] = useAtom(expandKeysAtom);
|
||||||
const {
|
const [clusterSession, setClusterSession] = useAtom(clusterSessionAtom);
|
||||||
handleExpandChange,
|
const { handleExpandChange, handleExpandAll, expandedRowKeys } =
|
||||||
handleExpandAll,
|
useExpandedRowKeys(expandAtom);
|
||||||
updateExpandedRowKeys,
|
|
||||||
removeExpandedRowKey,
|
|
||||||
expandedRowKeys
|
|
||||||
} = useExpandedRowKeys(expandAtom);
|
|
||||||
const [searchParams, setSearchParams] = useSearchParams();
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const [registerClusterStatus, setRegisterClusterStatus] = useState<{
|
const [registerClusterStatus, setRegisterClusterStatus] = useState<{
|
||||||
@@ -309,6 +308,28 @@ const Clusters: React.FC = () => {
|
|||||||
fetchCredentialList();
|
fetchCredentialList();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add worker from workers page redirect
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
clusterSession?.firstAddWorker &&
|
||||||
|
dataSource.loadend &&
|
||||||
|
dataSource.dataList?.length > 0
|
||||||
|
) {
|
||||||
|
const targetCluster = dataSource.dataList.find(
|
||||||
|
(cluster) =>
|
||||||
|
cluster.provider === ProviderValueMap.Docker &&
|
||||||
|
cluster.state === ClusterStatusValueMap.Ready
|
||||||
|
);
|
||||||
|
if (targetCluster) {
|
||||||
|
handleAddWorker(targetCluster);
|
||||||
|
// reset session
|
||||||
|
setClusterSession(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [clusterSession, dataSource.loadend, dataSource.dataList]);
|
||||||
|
|
||||||
const renderChildren = (
|
const renderChildren = (
|
||||||
list: any,
|
list: any,
|
||||||
options: { parent?: any; [key: string]: any }
|
options: { parent?: any; [key: string]: any }
|
||||||
|
|||||||
@@ -29,15 +29,14 @@ const NotesWrapper = styled.ol`
|
|||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
line-height: 1.25;
|
line-height: 1.25;
|
||||||
|
counter-reset: main 2 sub 0;
|
||||||
li {
|
li {
|
||||||
margin-left: 12px !important;
|
counter-increment: sub;
|
||||||
|
margin-left: 20px !important;
|
||||||
|
&::marker {
|
||||||
|
content: counters(main, '.') '.' counter(sub) ' ';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`;
|
|
||||||
|
|
||||||
const Title = styled.div`
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Line = styled.div`
|
const Line = styled.div`
|
||||||
|
|||||||
@@ -1,390 +0,0 @@
|
|||||||
import DropdownButtons from '@/components/drop-down-buttons';
|
|
||||||
import GaugeChart from '@/components/echarts/gauge';
|
|
||||||
import IconFont from '@/components/icon-font';
|
|
||||||
import StatusTag from '@/components/status-tag';
|
|
||||||
import ThemeTag from '@/components/tags-wrapper/theme-tag';
|
|
||||||
import Card from '@/components/templates/card';
|
|
||||||
import { PageAction } from '@/config';
|
|
||||||
import { Card as ACard, Col, Collapse, Row } from 'antd';
|
|
||||||
import React, { useMemo } from 'react';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import { queryClusterToken } from '../apis';
|
|
||||||
import {
|
|
||||||
ClusterStatus,
|
|
||||||
ClusterStatusLabelMap,
|
|
||||||
ProviderLabelMap,
|
|
||||||
ProviderValueMap,
|
|
||||||
clusterActionList
|
|
||||||
} from '../config';
|
|
||||||
import { ClusterListItem as ListItem, NodePoolListItem } from '../config/types';
|
|
||||||
import AddPool from './add-pool';
|
|
||||||
import RegisterCluster from './register-cluster';
|
|
||||||
import WorkerPools from './worker-pools';
|
|
||||||
|
|
||||||
const Content = styled.div`
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
.chart-wrapper {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const CardWrapper = styled(ACard)`
|
|
||||||
text-align: center;
|
|
||||||
box-shadow: none !important;
|
|
||||||
flex: 1;
|
|
||||||
.ant-card {
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
.ant-card-body {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: center;
|
|
||||||
padding: 6px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
font-weight: 400;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
font-size: var(--font-size-middle);
|
|
||||||
color: var(--ant-color-text-secondary);
|
|
||||||
}
|
|
||||||
.value {
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--ant-color-text);
|
|
||||||
font-size: var(--font-size-large);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const CardBox = styled.div`
|
|
||||||
display: flex;
|
|
||||||
gap: 16px;
|
|
||||||
flex: 1;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Inner = styled.div`
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
cursor: default;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
margin-bottom: 12px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding-bottom: 8px;
|
|
||||||
|
|
||||||
.text {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
font-size: var(--font-size-middle);
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--ant-color-text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const CollapseWrapper = styled(Collapse)`
|
|
||||||
width: 100%;
|
|
||||||
border: none;
|
|
||||||
border-radius: 0;
|
|
||||||
border-top: 1px solid var(--ant-color-split);
|
|
||||||
background-color: var(--ant-color-fill-quaternary);
|
|
||||||
.ant-collapse-content {
|
|
||||||
border-top: 1px solid var(--ant-color-split);
|
|
||||||
}
|
|
||||||
.ant-collapse-header {
|
|
||||||
font-size: var(--font-size-middle);
|
|
||||||
padding-block: 10px !important;
|
|
||||||
.ant-collapse-expand-icon {
|
|
||||||
margin-left: 0 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
table .ant-table-thead tr {
|
|
||||||
background-color: transparent !important;
|
|
||||||
th {
|
|
||||||
border-bottom: 1px solid var(--ant-color-split) !important;
|
|
||||||
font-weight: 500 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const SubTitle = styled.div`
|
|
||||||
font-size: var(--font-size-middle);
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--ant-color-text);
|
|
||||||
margin-block: 24px 16px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
interface CardProps {
|
|
||||||
data: ListItem;
|
|
||||||
onSelect?: (key: string, row: ListItem) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const gaugeConfig = {
|
|
||||||
radius: '100%',
|
|
||||||
progress: {
|
|
||||||
show: true,
|
|
||||||
roundCap: false,
|
|
||||||
width: 8
|
|
||||||
},
|
|
||||||
axisLine: {
|
|
||||||
roundCap: false,
|
|
||||||
lineStyle: {
|
|
||||||
width: 8,
|
|
||||||
color: [
|
|
||||||
[0.5, 'rgba(84, 204, 152, 80%)'],
|
|
||||||
[0.8, 'rgba(250, 173, 20, 80%)'],
|
|
||||||
[1, 'rgba(255, 77, 79, 80%)']
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const CardItem: React.FC<CardProps> = (props) => {
|
|
||||||
const chartHeight = 160;
|
|
||||||
const { data, onSelect } = props;
|
|
||||||
const [show, setShow] = React.useState(false);
|
|
||||||
const [addPoolStatus, setAddPoolStatus] = React.useState({
|
|
||||||
open: false,
|
|
||||||
action: PageAction.CREATE,
|
|
||||||
title: '',
|
|
||||||
provider: ProviderValueMap.DigitalOcean
|
|
||||||
});
|
|
||||||
const [registerClusterStatus, setRegisterClusterStatus] = React.useState<{
|
|
||||||
open: boolean;
|
|
||||||
registrationInfo: {
|
|
||||||
token: string;
|
|
||||||
image: string;
|
|
||||||
server_url: string;
|
|
||||||
cluster_id: number;
|
|
||||||
};
|
|
||||||
}>({
|
|
||||||
open: false,
|
|
||||||
registrationInfo: {
|
|
||||||
token: '',
|
|
||||||
image: '',
|
|
||||||
server_url: '',
|
|
||||||
cluster_id: 0
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleRegisterCluster = async () => {
|
|
||||||
try {
|
|
||||||
const info = await queryClusterToken({ id: data.id });
|
|
||||||
setRegisterClusterStatus({
|
|
||||||
open: true,
|
|
||||||
registrationInfo: {
|
|
||||||
...info,
|
|
||||||
cluster_id: data.id
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (error) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// cluster action handler
|
|
||||||
const handleOnSelect = (key: string) => {
|
|
||||||
if (key === 'addPool') {
|
|
||||||
setAddPoolStatus({
|
|
||||||
open: true,
|
|
||||||
action: PageAction.CREATE,
|
|
||||||
title: 'Add Worker Pool',
|
|
||||||
provider: data.provider
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key === 'register_cluster') {
|
|
||||||
handleRegisterCluster();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onSelect?.(key, data);
|
|
||||||
};
|
|
||||||
|
|
||||||
// pool action handler
|
|
||||||
const handleOnAction = (action: string, record: NodePoolListItem) => {
|
|
||||||
if (action === 'edit') {
|
|
||||||
setAddPoolStatus({
|
|
||||||
open: true,
|
|
||||||
action: PageAction.CREATE,
|
|
||||||
title: 'Edit Worker Pool',
|
|
||||||
provider: data.provider
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const actions = useMemo(() => {
|
|
||||||
return clusterActionList.filter((item) => {
|
|
||||||
if (item.provider) {
|
|
||||||
return item.provider === data.provider;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}, [data.provider]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card
|
|
||||||
height={'auto'}
|
|
||||||
clickable={false}
|
|
||||||
ghost
|
|
||||||
footerHolder={
|
|
||||||
<CollapseWrapper
|
|
||||||
onChange={() => setShow(!show)}
|
|
||||||
expandIconPosition="end"
|
|
||||||
expandIcon={({ isActive }) => (
|
|
||||||
<IconFont type="icon-down" rotate={isActive ? 0 : -90} />
|
|
||||||
)}
|
|
||||||
items={[
|
|
||||||
{
|
|
||||||
key: '1',
|
|
||||||
label: 'More Information',
|
|
||||||
children: (
|
|
||||||
<>
|
|
||||||
<div className="chart-wrapper">
|
|
||||||
<Row gutter={16} style={{ width: '100%' }}>
|
|
||||||
<Col span={6}>
|
|
||||||
<GaugeChart
|
|
||||||
title="GPU Utilization"
|
|
||||||
value={85}
|
|
||||||
height={chartHeight}
|
|
||||||
gaugeConfig={gaugeConfig}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
<Col span={6}>
|
|
||||||
<GaugeChart
|
|
||||||
title="CPU Utilization"
|
|
||||||
value={50}
|
|
||||||
height={chartHeight}
|
|
||||||
gaugeConfig={gaugeConfig}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
<Col span={6}>
|
|
||||||
<GaugeChart
|
|
||||||
title="RAM Utilization"
|
|
||||||
value={70}
|
|
||||||
height={chartHeight}
|
|
||||||
gaugeConfig={gaugeConfig}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
<Col span={6}>
|
|
||||||
<GaugeChart
|
|
||||||
title="VRAM Utilization"
|
|
||||||
value={60}
|
|
||||||
height={chartHeight}
|
|
||||||
gaugeConfig={gaugeConfig}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</div>
|
|
||||||
{data.provider === ProviderValueMap.DigitalOcean && (
|
|
||||||
<>
|
|
||||||
<SubTitle>Worker Pools</SubTitle>
|
|
||||||
<WorkerPools
|
|
||||||
provider={data.provider}
|
|
||||||
workerPools={data.worker_pools}
|
|
||||||
height={show ? 'auto' : 0}
|
|
||||||
onAction={handleOnAction}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
></CollapseWrapper>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Inner>
|
|
||||||
<div className="title">
|
|
||||||
<span className="flex-center gap-8">
|
|
||||||
<span className="text">{data.name}</span>
|
|
||||||
<ThemeTag color="purple">
|
|
||||||
{ProviderLabelMap[data.provider]}
|
|
||||||
</ThemeTag>
|
|
||||||
<StatusTag
|
|
||||||
statusValue={{
|
|
||||||
status: ClusterStatus[data.state],
|
|
||||||
text: ClusterStatusLabelMap[data.state]
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
<DropdownButtons
|
|
||||||
items={actions}
|
|
||||||
onSelect={handleOnSelect}
|
|
||||||
></DropdownButtons>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<Content>
|
|
||||||
<CardBox>
|
|
||||||
<CardWrapper bordered={false}>
|
|
||||||
<div className="label">Workers</div>
|
|
||||||
<div className="value">
|
|
||||||
{data.ready_workers} / {data.workers}
|
|
||||||
</div>
|
|
||||||
</CardWrapper>
|
|
||||||
<CardWrapper bordered={false}>
|
|
||||||
<div className="label">GPUs</div>
|
|
||||||
<div className="value">{data.gpus}</div>
|
|
||||||
</CardWrapper>
|
|
||||||
<CardWrapper bordered={false}>
|
|
||||||
<div className="label">Deployments</div>
|
|
||||||
<div className="value">{data.models}</div>
|
|
||||||
</CardWrapper>
|
|
||||||
</CardBox>
|
|
||||||
</Content>
|
|
||||||
</Inner>
|
|
||||||
<AddPool
|
|
||||||
provider={addPoolStatus.provider}
|
|
||||||
open={addPoolStatus.open}
|
|
||||||
action={addPoolStatus.action}
|
|
||||||
title={addPoolStatus.title}
|
|
||||||
onCancel={() => {
|
|
||||||
setAddPoolStatus({
|
|
||||||
open: false,
|
|
||||||
action: PageAction.CREATE,
|
|
||||||
title: '',
|
|
||||||
provider: 'digitalocean'
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
onOk={() => {
|
|
||||||
setAddPoolStatus({
|
|
||||||
open: false,
|
|
||||||
action: addPoolStatus.action,
|
|
||||||
title: '',
|
|
||||||
provider: 'digitalocean'
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
></AddPool>
|
|
||||||
<RegisterCluster
|
|
||||||
title="Register Cluster"
|
|
||||||
open={registerClusterStatus.open}
|
|
||||||
registrationInfo={registerClusterStatus.registrationInfo}
|
|
||||||
onCancel={() => {
|
|
||||||
setRegisterClusterStatus({
|
|
||||||
open: false,
|
|
||||||
registrationInfo: {
|
|
||||||
token: '',
|
|
||||||
image: '',
|
|
||||||
server_url: '',
|
|
||||||
cluster_id: 0
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
></RegisterCluster>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CardItem;
|
|
||||||
@@ -13,19 +13,6 @@ const Wrapper = styled.div`
|
|||||||
max-width: 300px !important;
|
max-width: 300px !important;
|
||||||
color: var(--ant-color-text-description) !important;
|
color: var(--ant-color-text-description) !important;
|
||||||
}
|
}
|
||||||
.ant-steps-item-content > .ant-steps-item-title {
|
|
||||||
// font-weight: 600;
|
|
||||||
}
|
|
||||||
.ant-steps-item {
|
|
||||||
.ant-steps-item-container {
|
|
||||||
// .ant-steps-item-icon {
|
|
||||||
// margin-inline-start: 0 !important;
|
|
||||||
// }
|
|
||||||
// .ant-steps-item-tail {
|
|
||||||
// margin-inline-start: 0 !important;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ClusterSteps: React.FC<{
|
const ClusterSteps: React.FC<{
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ const Credentials: React.FC = () => {
|
|||||||
deleteAPI: deleteCredential,
|
deleteAPI: deleteCredential,
|
||||||
contentForDelete: 'menu.clusterManagement.credentials'
|
contentForDelete: 'menu.clusterManagement.credentials'
|
||||||
});
|
});
|
||||||
const [isFromCluster] = useAtom(fromClusterCreationAtom);
|
const [isFromCluster, setIsFromCluster] = useAtom(fromClusterCreationAtom);
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [openModalStatus, setOpenModalStatus] = useState<{
|
const [openModalStatus, setOpenModalStatus] = useState<{
|
||||||
@@ -110,6 +110,7 @@ const Credentials: React.FC = () => {
|
|||||||
navigate(-1);
|
navigate(-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
setIsFromCluster(false);
|
||||||
fetchData();
|
fetchData();
|
||||||
setOpenModalStatus({ ...openModalStatus, open: false });
|
setOpenModalStatus({ ...openModalStatus, open: false });
|
||||||
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
||||||
|
|||||||
@@ -454,6 +454,7 @@ const AddModal: FC<AddModalProps> = (props) => {
|
|||||||
form.current?.form?.setFieldsValue({
|
form.current?.form?.setFieldsValue({
|
||||||
...props.initialValues
|
...props.initialValues
|
||||||
});
|
});
|
||||||
|
console.log('handleOnOpen - modelFiles:', props.initialValues);
|
||||||
handleOnValuesChange?.({
|
handleOnValuesChange?.({
|
||||||
changedValues: {},
|
changedValues: {},
|
||||||
allValues: {
|
allValues: {
|
||||||
@@ -530,7 +531,6 @@ const AddModal: FC<AddModalProps> = (props) => {
|
|||||||
closeIcon={false}
|
closeIcon={false}
|
||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
keyboard={false}
|
keyboard={false}
|
||||||
zIndex={2000}
|
|
||||||
styles={{
|
styles={{
|
||||||
body: {
|
body: {
|
||||||
height: 'calc(100vh - 57px)',
|
height: 'calc(100vh - 57px)',
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import IconFont from '@/components/icon-font';
|
||||||
|
import SegmentLine from '@/components/segment-line';
|
||||||
import { PageAction } from '@/config';
|
import { PageAction } from '@/config';
|
||||||
import { PageActionType } from '@/config/types';
|
import { PageActionType } from '@/config/types';
|
||||||
import CollapsePanel from '@/pages/_components/collapse-panel';
|
import CollapsePanel from '@/pages/_components/collapse-panel';
|
||||||
@@ -52,7 +54,8 @@ const SegmentedHeader = styled.div<{ $top?: number }>`
|
|||||||
position: sticky;
|
position: sticky;
|
||||||
top: ${(props) => props.$top || 0}px;
|
top: ${(props) => props.$top || 0}px;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
padding-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
border-bottom: 1px solid var(--ant-color-split);
|
||||||
background-color: var(--ant-color-bg-elevated);
|
background-color: var(--ant-color-bg-elevated);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -112,21 +115,25 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
|||||||
{
|
{
|
||||||
value: TABKeysMap.BASIC,
|
value: TABKeysMap.BASIC,
|
||||||
label: intl.formatMessage({ id: 'common.title.basicInfo' }),
|
label: intl.formatMessage({ id: 'common.title.basicInfo' }),
|
||||||
|
icon: <IconFont type="icon-basic" />,
|
||||||
field: 'name'
|
field: 'name'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: TABKeysMap.SCHEDULING,
|
value: TABKeysMap.SCHEDULING,
|
||||||
label: intl.formatMessage({ id: 'models.form.scheduling' }),
|
label: intl.formatMessage({ id: 'models.form.scheduling' }),
|
||||||
|
icon: <IconFont type="icon-model" />,
|
||||||
field: 'scheduleType'
|
field: 'scheduleType'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: TABKeysMap.PERFORMANCE,
|
value: TABKeysMap.PERFORMANCE,
|
||||||
label: intl.formatMessage({ id: 'models.form.performance' }),
|
label: intl.formatMessage({ id: 'models.form.performance' }),
|
||||||
|
icon: <IconFont type="icon-speed" />,
|
||||||
field: 'extended_kv_cache.enabled'
|
field: 'extended_kv_cache.enabled'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: TABKeysMap.ADVANCED,
|
value: TABKeysMap.ADVANCED,
|
||||||
label: intl.formatMessage({ id: 'resources.form.advanced' }),
|
label: intl.formatMessage({ id: 'resources.form.advanced' }),
|
||||||
|
icon: <IconFont type="icon-settings" />,
|
||||||
field: 'categories'
|
field: 'categories'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -404,7 +411,8 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SegmentedHeader $top={segmentedTop.top}>
|
<SegmentedHeader $top={segmentedTop.top}>
|
||||||
<SegmentedInner
|
<SegmentLine
|
||||||
|
theme={'light'}
|
||||||
defaultValue="basic"
|
defaultValue="basic"
|
||||||
value={target}
|
value={target}
|
||||||
onChange={handleTargetChange}
|
onChange={handleTargetChange}
|
||||||
|
|||||||
@@ -52,13 +52,13 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
|
|||||||
handleAddNewMessage,
|
handleAddNewMessage,
|
||||||
handleClear,
|
handleClear,
|
||||||
setMessageList,
|
setMessageList,
|
||||||
formRef,
|
|
||||||
tokenResult,
|
tokenResult,
|
||||||
messageList,
|
messageList,
|
||||||
loading
|
loading
|
||||||
} = useChatCompletion(scroller);
|
} = useChatCompletion(scroller);
|
||||||
const {
|
const {
|
||||||
handleOnValuesChange,
|
handleOnValuesChange,
|
||||||
|
formRef,
|
||||||
paramsRef,
|
paramsRef,
|
||||||
paramsConfig,
|
paramsConfig,
|
||||||
initialValues,
|
initialValues,
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ export default function useChatCompletion(
|
|||||||
const currentMessageRef = useRef<any>(null);
|
const currentMessageRef = useRef<any>(null);
|
||||||
const messageListLengthCache = useRef<number>(0);
|
const messageListLengthCache = useRef<number>(0);
|
||||||
const reasonContentRef = useRef('');
|
const reasonContentRef = useRef('');
|
||||||
const formRef = useRef<any>(null);
|
|
||||||
|
|
||||||
const setMessageId = () => {
|
const setMessageId = () => {
|
||||||
messageId.current = messageId.current + 1;
|
messageId.current = messageId.current + 1;
|
||||||
@@ -217,7 +216,6 @@ export default function useChatCompletion(
|
|||||||
loading,
|
loading,
|
||||||
tokenResult,
|
tokenResult,
|
||||||
messageList,
|
messageList,
|
||||||
formRef,
|
|
||||||
setMessageId,
|
setMessageId,
|
||||||
setMessageList,
|
setMessageList,
|
||||||
handleClear,
|
handleClear,
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ export const useInitLLmMeta = (
|
|||||||
defaultValues = {},
|
defaultValues = {},
|
||||||
defaultParamsConfig = []
|
defaultParamsConfig = []
|
||||||
} = options;
|
} = options;
|
||||||
|
const formRef = useRef<any>(null);
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const [modelMeta, setModelMeta] = useState<any>({});
|
const [modelMeta, setModelMeta] = useState<any>({});
|
||||||
const [initialValues, setInitialValues] = useState<any>({
|
const [initialValues, setInitialValues] = useState<any>({
|
||||||
@@ -194,6 +195,7 @@ export const useInitLLmMeta = (
|
|||||||
setInitialValues,
|
setInitialValues,
|
||||||
setParams,
|
setParams,
|
||||||
setParamsConfig,
|
setParamsConfig,
|
||||||
|
formRef,
|
||||||
paramsConfig,
|
paramsConfig,
|
||||||
initialValues,
|
initialValues,
|
||||||
parameters,
|
parameters,
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import classNames from 'classnames';
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
import styled from 'styled-components';
|
|
||||||
import { PageContainerInner } from '../_components/page-box';
|
import { PageContainerInner } from '../_components/page-box';
|
||||||
import { queryModelsList } from './apis';
|
import { queryModelsList } from './apis';
|
||||||
import GroundLeft from './components/ground-left';
|
import GroundLeft from './components/ground-left';
|
||||||
@@ -19,14 +18,6 @@ import MultipleChat from './components/multiple-chat';
|
|||||||
import ViewCodeButtons from './components/view-code-buttons';
|
import ViewCodeButtons from './components/view-code-buttons';
|
||||||
import './style/play-ground.less';
|
import './style/play-ground.less';
|
||||||
|
|
||||||
const Header = styled.div`
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-top: 16px;
|
|
||||||
padding-inline: 32px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Playground: React.FC = () => {
|
const Playground: React.FC = () => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { size } = useWindowResize();
|
const { size } = useWindowResize();
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { clusterSessionAtom } from '@/atoms/clusters';
|
||||||
import DeleteModal from '@/components/delete-modal';
|
import DeleteModal from '@/components/delete-modal';
|
||||||
import IconFont from '@/components/icon-font';
|
import IconFont from '@/components/icon-font';
|
||||||
import { FilterBar } from '@/components/page-tools';
|
import { FilterBar } from '@/components/page-tools';
|
||||||
@@ -7,6 +8,7 @@ import { queryClusterList } from '@/pages/cluster-management/apis';
|
|||||||
import { useIntl, useNavigate } from '@umijs/max';
|
import { useIntl, useNavigate } from '@umijs/max';
|
||||||
import { useMemoizedFn } from 'ahooks';
|
import { useMemoizedFn } from 'ahooks';
|
||||||
import { Button, ConfigProvider, Table, message } from 'antd';
|
import { Button, ConfigProvider, Table, message } from 'antd';
|
||||||
|
import { useAtom } from 'jotai';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import NoResult from '../../_components/no-result';
|
import NoResult from '../../_components/no-result';
|
||||||
import TerminalTabs from '../../_components/terminal-tabs';
|
import TerminalTabs from '../../_components/terminal-tabs';
|
||||||
@@ -44,6 +46,7 @@ const Workers: React.FC = () => {
|
|||||||
watch: true,
|
watch: true,
|
||||||
API: WORKERS_API
|
API: WORKERS_API
|
||||||
});
|
});
|
||||||
|
const [, setClusterSession] = useAtom(clusterSessionAtom);
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
@@ -178,6 +181,7 @@ const Workers: React.FC = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const handleAddWorker = () => {
|
const handleAddWorker = () => {
|
||||||
|
setClusterSession({ firstAddWorker: true });
|
||||||
navigate('/cluster-management/clusters/list');
|
navigate('/cluster-management/clusters/list');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user