refactor: credential table columns

This commit is contained in:
jialin
2025-09-16 11:26:17 +08:00
parent 43936e21d9
commit 0c1057d554
34 changed files with 648 additions and 503 deletions
+2 -1
View File
@@ -31,7 +31,7 @@
"@xterm/xterm": "^5.5.0", "@xterm/xterm": "^5.5.0",
"ahooks": "^3.8.5", "ahooks": "^3.8.5",
"ansi-to-html": "^0.7.2", "ansi-to-html": "^0.7.2",
"antd": "^5.21.6", "antd": "^5.25.4",
"antd-style": "^3.6.2", "antd-style": "^3.6.2",
"axios": "^1.8.2", "axios": "^1.8.2",
"classnames": "^2.5.1", "classnames": "^2.5.1",
@@ -73,6 +73,7 @@
"remark-math": "^6.0.0", "remark-math": "^6.0.0",
"simplebar-react": "^3.2.6", "simplebar-react": "^3.2.6",
"styled-components": "^6.1.15", "styled-components": "^6.1.15",
"tinycolor2": "^1.6.0",
"umi-presets-pro": "^2.0.3", "umi-presets-pro": "^2.0.3",
"wavesurfer.js": "^7.8.8", "wavesurfer.js": "^7.8.8",
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz" "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
+344 -304
View File
File diff suppressed because it is too large Load Diff
+19 -12
View File
@@ -4,6 +4,7 @@ import { Button, Dropdown, Tooltip, type MenuProps } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
import _ from 'lodash'; import _ from 'lodash';
import React from 'react'; import React from 'react';
import styled from 'styled-components';
import './index.less'; import './index.less';
type Trigger = 'click' | 'hover'; type Trigger = 'click' | 'hover';
@@ -19,6 +20,17 @@ interface DropdownButtonsProps {
onSelect: (val: any, item?: any) => void; onSelect: (val: any, item?: any) => void;
} }
const DropdownWrapper = styled.div`
display: flex;
flex-direction: column;
background-color: var(--ant-color-bg-elevated);
padding: 5px;
align-items: flex-start;
border-radius: var(--border-radius-base);
box-shadow: var(--ant-box-shadow-secondary);
min-width: 160px;
`;
const DropdownButtons: React.FC<DropdownButtonsProps> = ({ const DropdownButtons: React.FC<DropdownButtonsProps> = ({
items, items,
size = 'middle', size = 'middle',
@@ -66,16 +78,7 @@ const DropdownButtons: React.FC<DropdownButtonsProps> = ({
type="primary" type="primary"
dropdownRender={(menus: any) => { dropdownRender={(menus: any) => {
return ( return (
<div <DropdownWrapper>
className="flex flex-column "
style={{
backgroundColor: 'var(--ant-color-bg-elevated)',
padding: 5,
alignItems: 'flex-start',
borderRadius: 'var(--border-radius-base)',
boxShadow: 'var(--ant-box-shadow-secondary)'
}}
>
{_.map(_.tail(items), (item: any) => { {_.map(_.tail(items), (item: any) => {
return ( return (
<Button <Button
@@ -86,13 +89,17 @@ const DropdownButtons: React.FC<DropdownButtonsProps> = ({
key={item.key} key={item.key}
disabled={item.disabled} disabled={item.disabled}
onClick={() => handleMenuClick(item)} onClick={() => handleMenuClick(item)}
style={{ width: '100%', justifyContent: 'flex-start' }} style={{
width: '100%',
justifyContent: 'flex-start',
paddingInline: 10
}}
> >
{intl.formatMessage({ id: item.label })} {intl.formatMessage({ id: item.label })}
</Button> </Button>
); );
})} })}
</div> </DropdownWrapper>
); );
}} }}
buttonsRender={([leftButton, rightButton]) => [ buttonsRender={([leftButton, rightButton]) => [
+20
View File
@@ -1,10 +1,13 @@
import Chart from '@/components/echarts/chart'; import Chart from '@/components/echarts/chart';
import useChartConfig from '@/components/echarts/config'; import useChartConfig from '@/components/echarts/config';
import EmptyData from '@/components/empty-data'; import EmptyData from '@/components/empty-data';
import { genColors } from '@/utils';
import _ from 'lodash'; import _ from 'lodash';
import React, { memo, useMemo } from 'react'; import React, { memo, useMemo } from 'react';
import echarts from '.';
import { ChartProps } from './types'; import { ChartProps } from './types';
const LinearGradient = echarts.graphic.LinearGradient;
const LineChart: React.FC<ChartProps> = (props) => { const LineChart: React.FC<ChartProps> = (props) => {
const { const {
seriesData, seriesData,
@@ -81,6 +84,11 @@ const LineChart: React.FC<ChartProps> = (props) => {
const dataOptions = useMemo((): any => { const dataOptions = useMemo((): any => {
const data = _.map(seriesData, (item: any) => { const data = _.map(seriesData, (item: any) => {
const colors = genColors({
color: item.color,
alpha1: 0.5,
alpha2: 0.1
});
return { return {
...item, ...item,
...lineItemConfig, ...lineItemConfig,
@@ -93,6 +101,18 @@ const LineChart: React.FC<ChartProps> = (props) => {
...lineItemConfig.lineStyle, ...lineItemConfig.lineStyle,
color: item.color color: item.color
} }
// areaStyle: {
// color: new LinearGradient(0, 0, 0, 1, [
// {
// offset: 0,
// color: colors[0]
// },
// {
// offset: 1,
// color: colors[1]
// }
// ])
// }
}; };
}); });
return { return {
+21
View File
@@ -1,10 +1,14 @@
import Chart from '@/components/echarts/chart'; import Chart from '@/components/echarts/chart';
import useChartConfig from '@/components/echarts/config'; import useChartConfig from '@/components/echarts/config';
import EmptyData from '@/components/empty-data'; import EmptyData from '@/components/empty-data';
import { genColors } from '@/utils';
import _ from 'lodash'; import _ from 'lodash';
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import echarts from '.';
import { ChartProps } from './types'; import { ChartProps } from './types';
const LinearGradient = echarts.graphic.LinearGradient;
const MixLineBarChart: React.FC< const MixLineBarChart: React.FC<
ChartProps & { ChartProps & {
chartData: { chartData: {
@@ -78,6 +82,11 @@ const MixLineBarChart: React.FC<
const dataOptions = useMemo((): any => { const dataOptions = useMemo((): any => {
const linedata = _.map(lineSeriesData, (item: any) => { const linedata = _.map(lineSeriesData, (item: any) => {
const colors = genColors({
color: item.color,
alpha1: 0.5,
alpha2: 0.1
});
return { return {
...item, ...item,
...lineItemConfig, ...lineItemConfig,
@@ -91,6 +100,18 @@ const MixLineBarChart: React.FC<
...lineItemConfig.lineStyle, ...lineItemConfig.lineStyle,
color: item.color color: item.color
} }
// areaStyle: {
// color: new LinearGradient(0, 0, 0, 1, [
// {
// offset: 0,
// color: colors[0]
// },
// {
// offset: 1,
// color: colors[1]
// }
// ])
// }
}; };
}); });
+1
View File
@@ -45,6 +45,7 @@ const ScrollerModal = (props: ModalProps & { maxContentHeight?: number }) => {
return ( return (
<Modal <Modal
{...props} {...props}
destroyOnClose={true}
styles={{ styles={{
content: { content: {
padding: 0 padding: 0
+4 -4
View File
@@ -14,9 +14,9 @@ type WatchConfig =
| { watch: true; API: string; polling?: false | undefined } | { watch: true; API: string; polling?: false | undefined }
| { polling: true; watch: false | undefined; API?: string }; | { polling: true; watch: false | undefined; API?: string };
export default function useTableFetch<ListItem>( export default function useTableFetch<T>(
options: { options: {
fetchAPI: (params: any) => Promise<Global.PageResponse<ListItem>>; fetchAPI: (params: any) => Promise<Global.PageResponse<T>>;
deleteAPI?: (id: number, params?: any) => Promise<any>; deleteAPI?: (id: number, params?: any) => Promise<any>;
contentForDelete?: string; contentForDelete?: string;
defaultData?: any[]; defaultData?: any[];
@@ -47,7 +47,7 @@ export default function useTableFetch<ListItem>(
}); });
const [dataSource, setDataSource] = useState<{ const [dataSource, setDataSource] = useState<{
dataList: ListItem[]; dataList: T[];
loading: boolean; loading: boolean;
loadend: boolean; loadend: boolean;
total: number; total: number;
@@ -217,7 +217,7 @@ export default function useTableFetch<ListItem>(
const handleNameChange = debounceUpdateFilter; const handleNameChange = debounceUpdateFilter;
const handleDelete = ( const handleDelete = (
row: ListItem & { name: string; id: number }, row: T & { name: string; id: number },
options?: any options?: any
) => { ) => {
modalRef.current?.show({ modalRef.current?.show({
+2 -1
View File
@@ -10,5 +10,6 @@ export default {
'clusters.edit.cluster': 'Edit {cluster}', 'clusters.edit.cluster': 'Edit {cluster}',
'clusters.provider.custom': 'Custom', 'clusters.provider.custom': 'Custom',
'clusters.button.register': 'Register Cluster', 'clusters.button.register': 'Register Cluster',
'clusters.button.addNodePool': 'Add Node Pool' 'clusters.button.addNodePool': 'Add Node Pool',
'clusters.button.add.credential': 'Add {provider} Credential'
}; };
+2 -1
View File
@@ -91,5 +91,6 @@ export default {
'resources.register.download': 'resources.register.download':
'Download and install the <a href={url} target="_blank">installer</a>. Only supported: {versions}.', 'Download and install the <a href={url} target="_blank">installer</a>. Only supported: {versions}.',
'resource.register.maos.support': 'Apple Silicon (M series), macOS 14+', 'resource.register.maos.support': 'Apple Silicon (M series), macOS 14+',
'resource.register.windows.support': 'win 10, win 11' 'resource.register.windows.support': 'win 10, win 11',
'resources.model.instance': 'Model Instance'
}; };
+4 -2
View File
@@ -10,7 +10,8 @@ export default {
'clusters.edit.cluster': 'Edit {cluster}', 'clusters.edit.cluster': 'Edit {cluster}',
'clusters.provider.custom': 'Custom', 'clusters.provider.custom': 'Custom',
'clusters.button.register': 'Register Cluster', 'clusters.button.register': 'Register Cluster',
'clusters.button.addNodePool': 'Add Node Pool' 'clusters.button.addNodePool': 'Add Node Pool',
'clusters.button.add.credential': 'Add {provider} Credential'
}; };
// ========== To-Do: Translate Keys (Remove After Translation) ========== // ========== To-Do: Translate Keys (Remove After Translation) ==========
@@ -25,5 +26,6 @@ export default {
// 9. 'clusters.edit.cluster': 'Edit {cluster}', // 9. 'clusters.edit.cluster': 'Edit {cluster}',
// 10. 'clusters.provider.custom': 'Custom', // 10. 'clusters.provider.custom': 'Custom',
// 11. 'clusters.button.register': 'Register Cluster', // 11. 'clusters.button.register': 'Register Cluster',
// 12. 'clusters.button.addNodePool': 'Add Node Pool' // 12. 'clusters.button.addNodePool': 'Add Node Pool',
// 13. 'clusters.button.add.credential': 'Add {provider} Credential'
// ========== End of To-Do List ========== // ========== End of To-Do List ==========
+3 -1
View File
@@ -92,7 +92,8 @@ export default {
'resources.register.download': 'resources.register.download':
'Download and install the <a href={url} target="_blank">installer</a>. Only supported: {versions}.', 'Download and install the <a href={url} target="_blank">installer</a>. Only supported: {versions}.',
'resource.register.maos.support': 'Apple Silicon (M series), macOS 14+', 'resource.register.maos.support': 'Apple Silicon (M series), macOS 14+',
'resource.register.windows.support': 'win 10, win 11' 'resource.register.windows.support': 'win 10, win 11',
'resources.model.instance': 'Model Instance'
}; };
// ========== To-Do: Translate Keys (Remove After Translation) ========== // ========== To-Do: Translate Keys (Remove After Translation) ==========
@@ -108,4 +109,5 @@ export default {
// 10. 'resource.register.maos.support': 'Apple Silicon (M series), macOS 14+', // 10. 'resource.register.maos.support': 'Apple Silicon (M series), macOS 14+',
// 11. 'resource.register.windows.support': 'win 10, win 11', // 11. 'resource.register.windows.support': 'win 10, win 11',
// 12. 'resources.filter.status': 'Filter by Status', // 12. 'resources.filter.status': 'Filter by Status',
// 13. 'resources.model.instance': 'Model Instance'
// ========== End of To-Do List ========== // ========== End of To-Do List ==========
+4 -2
View File
@@ -10,7 +10,8 @@ export default {
'clusters.edit.cluster': 'Edit {cluster}', 'clusters.edit.cluster': 'Edit {cluster}',
'clusters.provider.custom': 'Custom', 'clusters.provider.custom': 'Custom',
'clusters.button.register': 'Register Cluster', 'clusters.button.register': 'Register Cluster',
'clusters.button.addNodePool': 'Add Node Pool' 'clusters.button.addNodePool': 'Add Node Pool',
'clusters.button.add.credential': 'Add {provider} Credential'
}; };
// ========== To-Do: Translate Keys (Remove After Translation) ========== // ========== To-Do: Translate Keys (Remove After Translation) ==========
@@ -25,5 +26,6 @@ export default {
// 9. 'clusters.edit.cluster': 'Edit {cluster}', // 9. 'clusters.edit.cluster': 'Edit {cluster}',
// 10. 'clusters.provider.custom': 'Custom', // 10. 'clusters.provider.custom': 'Custom',
// 11. 'clusters.button.register': 'Register Cluster', // 11. 'clusters.button.register': 'Register Cluster',
// 12. 'clusters.button.addNodePool': 'Add Node Pool' // 12. 'clusters.button.addNodePool': 'Add Node Pool',
// 13. 'clusters.button.add.credential': 'Add {provider} Credential'
// ========== End of To-Do List ========== // ========== End of To-Do List ==========
+2 -1
View File
@@ -90,7 +90,8 @@ export default {
'resources.register.download': 'resources.register.download':
'Скачайте и установите <a href={url} target="_blank">инсталлятор</a>. Поддерживаемые версии: {versions}.', 'Скачайте и установите <a href={url} target="_blank">инсталлятор</a>. Поддерживаемые версии: {versions}.',
'resource.register.maos.support': 'Apple Silicon (серия M), macOS 14+', 'resource.register.maos.support': 'Apple Silicon (серия M), macOS 14+',
'resource.register.windows.support': 'Windows 10, Windows 11' 'resource.register.windows.support': 'Windows 10, Windows 11',
'resources.model.instance': 'Модель экземпляра'
}; };
// ========== To-Do: Translate Keys (Remove After Translation) ========== // ========== To-Do: Translate Keys (Remove After Translation) ==========
+2 -1
View File
@@ -10,5 +10,6 @@ export default {
'clusters.edit.cluster': '编辑 {cluster}', 'clusters.edit.cluster': '编辑 {cluster}',
'clusters.provider.custom': '自定义', 'clusters.provider.custom': '自定义',
'clusters.button.register': '注册集群', 'clusters.button.register': '注册集群',
'clusters.button.addNodePool': '添加节点池' 'clusters.button.addNodePool': '添加节点池',
'clusters.button.add.credential': '添加 {provider} 凭证'
}; };
+2 -1
View File
@@ -89,5 +89,6 @@ export default {
'resources.register.download': 'resources.register.download':
'下载并安装<a href={url} target="_blank">安装包</a>,仅支持 {versions}。', '下载并安装<a href={url} target="_blank">安装包</a>,仅支持 {versions}。',
'resource.register.maos.support': 'M 芯片,macOS 14+', 'resource.register.maos.support': 'M 芯片,macOS 14+',
'resource.register.windows.support': 'win 10, win 11' 'resource.register.windows.support': 'win 10, win 11',
'resources.model.instance': '模型实例'
}; };
+1 -1
View File
@@ -42,7 +42,7 @@ const Credentials: React.FC = () => {
} = useTableFetch<ListItem>({ } = useTableFetch<ListItem>({
fetchAPI: queryClusterList, fetchAPI: queryClusterList,
deleteAPI: deleteCluster, deleteAPI: deleteCluster,
contentForDelete: 'users.table.user' contentForDelete: 'menu.clusterManagement.clusters'
}); });
const intl = useIntl(); const intl = useIntl();
@@ -13,7 +13,7 @@ import {
ClusterListItem as ListItem ClusterListItem as ListItem
} from '../config/types'; } from '../config/types';
import CloudProvider from './cloud-provider-form'; import CloudProvider from './cloud-provider-form';
import RegisterClusterInner from './resiter-cluster-inner'; import RegisterClusterInner from './register-cluster-inner';
type AddModalProps = { type AddModalProps = {
title: string; title: string;
@@ -4,8 +4,8 @@ import SealInput from '@/components/seal-form/seal-input';
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';
import { Button, Form } from 'antd'; import { Form } from 'antd';
import React from 'react'; import React, { useEffect } from 'react';
import { ProviderValueMap } from '../config'; import { ProviderValueMap } from '../config';
import { import {
CredentialFormData as FormData, CredentialFormData as FormData,
@@ -41,32 +41,32 @@ const AddModal: React.FC<AddModalProps> = ({
onOk(data); onOk(data);
}; };
const handleCancel = () => {
form.resetFields();
onCancel();
};
useEffect(() => {
if (currentData) {
form.setFieldsValue(currentData);
}
}, [currentData]);
return ( return (
<ScrollerModal <ScrollerModal
title={title} title={title}
open={open} open={open}
centered={true} centered={true}
onOk={handleSumit} onOk={handleSumit}
onCancel={onCancel}
destroyOnClose={true} destroyOnClose={true}
closeIcon={false} closeIcon={false}
maskClosable={false} maskClosable={false}
keyboard={false} keyboard={false}
width={600} width={600}
footer={ footer={
<ModalFooter <ModalFooter onOk={handleSumit} onCancel={handleCancel}></ModalFooter>
onOk={handleSumit}
onCancel={onCancel}
description={<Button>Validation Test</Button>}
></ModalFooter>
} }
> >
<Form <Form form={form} onFinish={handleOk} preserve={false}>
form={form}
onFinish={handleOk}
preserve={false}
initialValues={currentData}
>
<Form.Item<FormData> <Form.Item<FormData>
name="name" name="name"
rules={[ rules={[
@@ -88,7 +88,7 @@ const AddModal: React.FC<AddModalProps> = ({
</Form.Item> </Form.Item>
{provider === ProviderValueMap.DigitalOcean && ( {provider === ProviderValueMap.DigitalOcean && (
<> <>
<Form.Item<FormData> {/* <Form.Item<FormData>
name="key" name="key"
rules={[ rules={[
{ {
@@ -103,20 +103,18 @@ const AddModal: React.FC<AddModalProps> = ({
label="Access Key" label="Access Key"
required={action === PageAction.CREATE} required={action === PageAction.CREATE}
></SealInput.Password> ></SealInput.Password>
</Form.Item> </Form.Item> */}
<Form.Item<FormData> <Form.Item<FormData>
name="secret" name="secret"
rules={[ rules={[
{ {
required: action === PageAction.CREATE, required: action === PageAction.CREATE,
message: intl.formatMessage({ message: 'Access Token is required'
id: 'users.form.rule.password'
})
} }
]} ]}
> >
<SealInput.Password <SealInput.Password
label="Access Secret" label="Access Token"
required={action === PageAction.CREATE} required={action === PageAction.CREATE}
></SealInput.Password> ></SealInput.Password>
</Form.Item> </Form.Item>
@@ -25,6 +25,12 @@ const metricsMap = {
intl: false, intl: false,
color: 'rgba(114, 46, 209,.8)' color: 'rgba(114, 46, 209,.8)'
}, },
allocated: {
label: 'Allocated',
type: 'Allocated',
intl: false,
color: 'rgba(250, 173, 20,.8)'
},
gpu: { gpu: {
label: 'GPU', label: 'GPU',
type: 'GPU', type: 'GPU',
@@ -186,7 +192,7 @@ const ClusterDetail: React.FC<ClusterDetailProps> = ({ data }) => {
</Row> </Row>
</div> </div>
<SubTitle>System Load</SubTitle> <SubTitle>System Load</SubTitle>
<Row style={{ marginBottom: 20 }} gutter={20}> <Row style={{ marginBottom: 16 }} gutter={16}>
<Col span={12}> <Col span={12}>
<Card height={CardHeight} clickable={false} ghost> <Card height={CardHeight} clickable={false} ghost>
<TrendChart <TrendChart
@@ -208,14 +214,28 @@ const ClusterDetail: React.FC<ClusterDetailProps> = ({ data }) => {
</Card> </Card>
</Col> </Col>
</Row> </Row>
<Card height={CardHeight} clickable={false} ghost> <Row gutter={16}>
<TrendChart <Col span={12}>
data={detailContent?.history} <Card height={CardHeight} clickable={false} ghost>
metrics={['cpu', 'gpu']} <TrendChart
metricsMap={metricsMap} data={detailContent?.history}
title="CPU & GPU" metrics={['cpu']}
></TrendChart> metricsMap={metricsMap}
</Card> title="CPU"
></TrendChart>
</Card>
</Col>
<Col span={12}>
<Card height={CardHeight} clickable={false} ghost>
<TrendChart
data={detailContent?.history}
metrics={['gpu']}
metricsMap={metricsMap}
title="GPU"
></TrendChart>
</Card>
</Col>
</Row>
{data?.provider === ProviderValueMap.DigitalOcean && ( {data?.provider === ProviderValueMap.DigitalOcean && (
<> <>
<SubTitle>Worker Pools</SubTitle> <SubTitle>Worker Pools</SubTitle>
@@ -1,6 +1,6 @@
import ScrollerModal from '@/components/scroller-modal/index'; import ScrollerModal from '@/components/scroller-modal/index';
import React from 'react'; import React from 'react';
import RegisterClusterInner from './resiter-cluster-inner'; import RegisterClusterInner from './register-cluster-inner';
type AddModalProps = { type AddModalProps = {
title: string; title: string;
@@ -38,6 +38,7 @@ const TrendChart: React.FC<TrendChartProps> = ({
: itemConfig.label; : itemConfig.label;
legendData.push(name); legendData.push(name);
const itemDataList = _.get(data, item, []); const itemDataList = _.get(data, item, []);
console.log('itemConfig:', itemConfig.color);
return { return {
name: name, name: name,
color: itemConfig.color, color: itemConfig.color,
+17 -1
View File
@@ -65,6 +65,22 @@ export const addActions = [
} }
]; ];
export const credentialActionList = [
{
key: 'edit',
label: 'common.button.edit',
icon: icons.EditOutlined
},
{
key: 'delete',
props: {
danger: true
},
label: 'common.button.delete',
icon: icons.DeleteOutlined
}
];
export const clusterActionList = [ export const clusterActionList = [
{ {
key: 'edit', key: 'edit',
@@ -74,7 +90,7 @@ export const clusterActionList = [
{ {
key: 'details', key: 'details',
label: 'common.button.detail', label: 'common.button.detail',
icon: icons.DetailInfo icon: icons.FileTextOutlined
}, },
{ {
key: 'add_worker', key: 'add_worker',
+29 -125
View File
@@ -1,20 +1,13 @@
import AutoTooltip from '@/components/auto-tooltip';
import DeleteModal from '@/components/delete-modal'; import DeleteModal from '@/components/delete-modal';
import DropdownButtons from '@/components/drop-down-buttons';
import IconFont from '@/components/icon-font'; import IconFont from '@/components/icon-font';
import { FilterBar } from '@/components/page-tools'; import { FilterBar } from '@/components/page-tools';
import { PageAction } from '@/config'; import { PageAction } from '@/config';
import type { PageActionType } from '@/config/types'; import type { PageActionType } from '@/config/types';
import useTableFetch from '@/hooks/use-table-fetch'; import useTableFetch from '@/hooks/use-table-fetch';
import {
DeleteOutlined,
EditOutlined,
KubernetesOutlined
} from '@ant-design/icons';
import { PageContainer } from '@ant-design/pro-components'; import { PageContainer } from '@ant-design/pro-components';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { useMemoizedFn } from 'ahooks';
import { ConfigProvider, Empty, Table, message } from 'antd'; import { ConfigProvider, Empty, Table, message } from 'antd';
import dayjs from 'dayjs';
import { useState } from 'react'; import { useState } from 'react';
import { import {
createCredential, createCredential,
@@ -23,43 +16,20 @@ import {
updateCredential updateCredential
} from './apis'; } from './apis';
import AddModal from './components/add-credential'; import AddModal from './components/add-credential';
import { ProviderValueMap } from './config'; import { ProviderLabelMap, ProviderValueMap } from './config';
import { import {
CredentialFormData as FormData, CredentialFormData as FormData,
CredentialListItem as ListItem CredentialListItem as ListItem
} from './config/types'; } from './config/types';
const { Column } = Table; import useCredentialColumns from './hooks/use-credential-columns';
const ActionList = [
{
key: 'edit',
label: 'common.button.edit',
icon: <EditOutlined></EditOutlined>
},
{
key: 'delete',
props: {
danger: true
},
label: 'common.button.delete',
icon: <DeleteOutlined></DeleteOutlined>
}
];
const addActions = [ const addActions = [
{ {
label: 'Digital Ocean', label: 'Digital Ocean',
locale: false, locale: false,
value: 'digital_ocean', key: ProviderValueMap.DigitalOcean,
key: 'digital_ocean', value: ProviderValueMap.DigitalOcean,
icon: <IconFont type="icon-digitalocean" /> icon: <IconFont type="icon-digitalocean" />
},
{
label: 'Kubernetes',
locale: false,
value: 'kubernetes',
key: 'kubernetes',
icon: <KubernetesOutlined className="size-16" />
} }
]; ];
@@ -80,7 +50,7 @@ const Credentials: React.FC = () => {
} = useTableFetch<ListItem>({ } = useTableFetch<ListItem>({
fetchAPI: queryCredentialList, fetchAPI: queryCredentialList,
deleteAPI: deleteCredential, deleteAPI: deleteCredential,
contentForDelete: 'users.table.user' contentForDelete: 'menu.clusterManagement.credentials'
}); });
const intl = useIntl(); const intl = useIntl();
@@ -98,12 +68,17 @@ const Credentials: React.FC = () => {
currentData: undefined currentData: undefined
}); });
const handleAddCredential = () => { const handleAddCredential = (item: { key: string; label: string }) => {
setOpenModalStatus({ setOpenModalStatus({
provider: ProviderValueMap.DigitalOcean, provider: item.key,
open: true, open: true,
action: PageAction.CREATE, action: PageAction.CREATE,
title: intl.formatMessage({ id: 'clusters.button.addCredential' }), title: intl.formatMessage(
{ id: 'clusters.button.add.credential' },
{
provider: ProviderLabelMap[item.key] || item.key
}
),
currentData: undefined currentData: undefined
}); });
}; };
@@ -132,8 +107,11 @@ const Credentials: React.FC = () => {
}; };
const handleModalCancel = () => { const handleModalCancel = () => {
console.log('handleModalCancel'); setOpenModalStatus({
setOpenModalStatus({ ...openModalStatus, open: false }); ...openModalStatus,
open: false,
currentData: undefined
});
}; };
const handleEditUser = (row: ListItem) => { const handleEditUser = (row: ListItem) => {
@@ -142,20 +120,20 @@ const Credentials: React.FC = () => {
open: true, open: true,
action: PageAction.EDIT, action: PageAction.EDIT,
title: intl.formatMessage( title: intl.formatMessage(
{ id: 'common.buton.edit.item' }, { id: 'common.button.edit.item' },
{ name: row.name } { name: row.name }
), ),
currentData: row currentData: row
}); });
}; };
const handleSelect = (val: any, row: ListItem) => { const handleSelect = useMemoizedFn((val: any, row: ListItem) => {
if (val === 'edit') { if (val === 'edit') {
handleEditUser(row); handleEditUser(row);
} else if (val === 'delete') { } else if (val === 'delete') {
handleDelete({ ...row, name: row.name }); handleDelete({ ...row, name: row.name });
} }
}; });
const renderEmpty = (type?: string) => { const renderEmpty = (type?: string) => {
if (type !== 'Table') return; if (type !== 'Table') return;
@@ -169,6 +147,8 @@ const Credentials: React.FC = () => {
return <div></div>; return <div></div>;
}; };
const columns = useCredentialColumns(sortOrder, handleSelect);
return ( return (
<> <>
<PageContainer <PageContainer
@@ -185,6 +165,8 @@ const Credentials: React.FC = () => {
extra={[]} extra={[]}
> >
<FilterBar <FilterBar
actionItems={addActions}
actionType="dropdown"
showSelect={false} showSelect={false}
showPrimaryButton={true} showPrimaryButton={true}
marginBottom={22} marginBottom={22}
@@ -202,6 +184,8 @@ const Credentials: React.FC = () => {
<ConfigProvider renderEmpty={renderEmpty}> <ConfigProvider renderEmpty={renderEmpty}>
<Table <Table
tableLayout="fixed"
columns={columns}
dataSource={dataSource.dataList} dataSource={dataSource.dataList}
rowSelection={rowSelection} rowSelection={rowSelection}
loading={dataSource.loading} loading={dataSource.loading}
@@ -215,87 +199,7 @@ const Credentials: React.FC = () => {
hideOnSinglePage: queryParams.perPage === 10, hideOnSinglePage: queryParams.perPage === 10,
onChange: handlePageChange onChange: handlePageChange
}} }}
> ></Table>
<Column
title={intl.formatMessage({ id: 'common.table.name' })}
dataIndex="name"
key="name"
ellipsis={{
showTitle: false
}}
render={(text, record) => {
return (
<AutoTooltip ghost minWidth={20}>
{text}
</AutoTooltip>
);
}}
/>
<Column
title={intl.formatMessage({ id: 'clusters.table.provider' })}
dataIndex="provider"
key="provider"
ellipsis={{
showTitle: false
}}
render={(text, record) => {
return (
<AutoTooltip ghost minWidth={20}>
{text}
</AutoTooltip>
);
}}
/>
<Column
title={intl.formatMessage({ id: 'common.table.createTime' })}
dataIndex="created_at"
key="createTime"
defaultSortOrder="descend"
sortOrder={sortOrder}
showSorterTooltip={false}
sorter={false}
ellipsis={{
showTitle: false
}}
render={(text, record) => {
return (
<AutoTooltip ghost minWidth={20}>
{dayjs(text).format('YYYY-MM-DD HH:mm:ss')}
</AutoTooltip>
);
}}
/>
<Column
title={intl.formatMessage({ id: 'common.table.description' })}
dataIndex="description"
key="description"
ellipsis={{
showTitle: false
}}
render={(text, record) => {
return (
<AutoTooltip ghost minWidth={20}>
{text}
</AutoTooltip>
);
}}
/>
<Column
title={intl.formatMessage({ id: 'common.table.operation' })}
key="operation"
ellipsis={{
showTitle: false
}}
render={(text, record: ListItem) => {
return (
<DropdownButtons
items={ActionList}
onSelect={(val) => handleSelect(val, record)}
></DropdownButtons>
);
}}
/>
</Table>
</ConfigProvider> </ConfigProvider>
</PageContainer> </PageContainer>
<AddModal <AddModal
@@ -0,0 +1,77 @@
// columns.ts
import AutoTooltip from '@/components/auto-tooltip';
import DropdownButtons from '@/components/drop-down-buttons';
import { useIntl } from '@umijs/max';
import { ColumnsType } from 'antd/es/table';
import type { SortOrder } from 'antd/es/table/interface';
import dayjs from 'dayjs';
import { useMemo } from 'react';
import { ProviderLabelMap, credentialActionList } from '../config';
import { CredentialListItem as ListItem } from '../config/types';
const useCredentialColumns = (
sortOrder: SortOrder,
handleSelect: (val: string, record: ListItem) => void
): ColumnsType<ListItem> => {
const intl = useIntl();
return useMemo(() => {
return [
{
title: intl.formatMessage({ id: 'common.table.name' }),
dataIndex: 'name',
render: (text: string) => (
<AutoTooltip ghost minWidth={20}>
{text}
</AutoTooltip>
)
},
{
title: intl.formatMessage({ id: 'clusters.table.provider' }),
dataIndex: 'provider',
render: (value: string) => <span>{ProviderLabelMap[value]}</span>
},
{
title: intl.formatMessage({ id: 'common.table.createTime' }),
dataIndex: 'created_at',
showSorterTooltip: false,
defaultSortOrder: 'descend',
sortOrder: sortOrder,
sorter: false,
ellipsis: {
showTitle: false
},
render: (value: string) => (
<span>{dayjs(value).format('YYYY-MM-DD HH:mm:ss')}</span>
)
},
{
title: intl.formatMessage({ id: 'common.table.description' }),
dataIndex: 'description',
ellipsis: {
showTitle: false
},
render: (value: string) => (
<AutoTooltip ghost minWidth={20}>
{value}
</AutoTooltip>
)
},
{
title: intl.formatMessage({ id: 'common.table.operation' }),
dataIndex: 'operations',
ellipsis: {
showTitle: false
},
render: (value: string, record: ListItem) => (
<DropdownButtons
items={credentialActionList}
onSelect={(val) => handleSelect(val, record)}
></DropdownButtons>
)
}
];
}, [handleSelect, sortOrder]);
};
export default useCredentialColumns;
+5 -5
View File
@@ -46,11 +46,11 @@ export const ActionList: ActionItem[] = [
key: 'edit', key: 'edit',
icon: icons.EditOutlined icon: icons.EditOutlined
}, },
{ // {
label: 'common.button.detail', // label: 'common.button.detail',
key: 'details', // key: 'details',
icon: icons.FileTextOutlined // icon: icons.FileTextOutlined
}, // },
{ {
label: 'models.openinplayground', label: 'models.openinplayground',
key: 'chat', key: 'chat',
+4 -4
View File
@@ -194,6 +194,10 @@ const LoginForm = () => {
return SSOAuth.options.oidc || SSOAuth.options.saml; return SSOAuth.options.oidc || SSOAuth.options.saml;
}, [SSOAuth.options]); }, [SSOAuth.options]);
const isThirdPartyAuthHandling = useMemo(() => {
return loading && !authError;
}, [loading, authError]);
const renderLoginButtons = () => { const renderLoginButtons = () => {
// do not render login buttons if using password login or no third-party login // do not render login buttons if using password login or no third-party login
if (!hasThirdPartyLogin || isPassword) return null; if (!hasThirdPartyLogin || isPassword) return null;
@@ -229,10 +233,6 @@ const LoginForm = () => {
); );
}; };
const isThirdPartyAuthHandling = useMemo(() => {
return loading && !authError;
}, [loading, authError]);
return ( return (
<div> <div>
{contextHolder} {contextHolder}
@@ -67,4 +67,4 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
); );
}; };
export default React.memo(AddWorker); export default AddWorker;
@@ -111,4 +111,4 @@ const UpdateLabels: React.FC<ViewModalProps> = (props) => {
); );
}; };
export default React.memo(UpdateLabels); export default UpdateLabels;
@@ -63,6 +63,9 @@ const useGPUColumns = (props: {
{ {
title: intl.formatMessage({ id: 'clusters.title' }), title: intl.formatMessage({ id: 'clusters.title' }),
dataIndex: 'cluster_id', dataIndex: 'cluster_id',
ellipsis: {
showTitle: false
},
render: (text: number, record: GPUDeviceItem) => ( render: (text: number, record: GPUDeviceItem) => (
<AutoTooltip ghost> <AutoTooltip ghost>
{clusterList.find((item) => item.value === text)?.label} {clusterList.find((item) => item.value === text)?.label}
@@ -70,8 +73,11 @@ const useGPUColumns = (props: {
) )
}, },
{ {
title: intl.formatMessage({ id: 'resources.table.workername' }), title: 'Worker',
dataIndex: 'worker_name', dataIndex: 'worker_name',
ellipsis: {
showTitle: false
},
render: (text: string, record: GPUDeviceItem) => ( render: (text: string, record: GPUDeviceItem) => (
<AutoTooltip ghost>{text}</AutoTooltip> <AutoTooltip ghost>{text}</AutoTooltip>
) )
@@ -294,7 +294,7 @@ const useInstanceColumns = (props: {
return useMemo(() => { return useMemo(() => {
return [ return [
{ {
title: intl.formatMessage({ id: 'common.table.name' }), title: intl.formatMessage({ id: 'resources.model.instance' }),
dataIndex: 'name', dataIndex: 'name',
width: 240, width: 240,
render: (text: string, record: ListItem) => ( render: (text: string, record: ListItem) => (
@@ -9,6 +9,7 @@ import {
CodeOutlined, CodeOutlined,
DeleteOutlined, DeleteOutlined,
EditOutlined, EditOutlined,
FileTextOutlined,
InfoCircleOutlined InfoCircleOutlined
} from '@ant-design/icons'; } from '@ant-design/icons';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
@@ -31,7 +32,7 @@ const ActionList = [
{ {
label: 'common.button.detail', label: 'common.button.detail',
key: 'details', key: 'details',
icon: <IconFont type="icon-detail-info" /> icon: <FileTextOutlined></FileTextOutlined>
}, },
{ {
label: 'common.button.logs', label: 'common.button.logs',
+20
View File
@@ -1,4 +1,5 @@
import _ from 'lodash'; import _ from 'lodash';
import tinycolor from 'tinycolor2';
export const isNotEmptyValue = (value: any) => { export const isNotEmptyValue = (value: any) => {
if (Array.isArray(value)) { if (Array.isArray(value)) {
@@ -219,3 +220,22 @@ export const base64ToFile = (base64String: string, fileName: string) => {
export const isOnline = () => { export const isOnline = () => {
return window.navigator.onLine; return window.navigator.onLine;
}; };
export const genColors = ({
color,
alpha1,
alpha2
}: {
color: string;
alpha1?: number;
alpha2?: number;
}) => {
const base = tinycolor(color);
const alpha_start = alpha1 || base.getAlpha();
const alpha_end = alpha2 || base.getAlpha();
return [
base.setAlpha(alpha_start).toRgbString(),
base.setAlpha(alpha_end).toRgbString()
];
};
+1
View File
@@ -26,5 +26,6 @@ declare module 'vibrant';
declare module 'node-vibrant'; declare module 'node-vibrant';
declare module 'lamejs'; declare module 'lamejs';
declare module 'file-saver'; declare module 'file-saver';
declare module 'tinycolor2';
declare const REACT_APP_ENV: 'test' | 'dev' | 'pre' | false; declare const REACT_APP_ENV: 'test' | 'dev' | 'pre' | false;