Files
gpustack-ui/src/pages/cluster-management/components/detail/cluster-basic.tsx
T

172 lines
4.6 KiB
TypeScript

import { StarFilled } from '@ant-design/icons';
import { CardWrapper, IconFont, StatusTag } from '@gpustack/core-ui';
import { useIntl } from '@umijs/max';
import type { DescriptionsProps } from 'antd';
import { Descriptions, Tooltip } from 'antd';
import dayjs from 'dayjs';
import React, { useEffect } from 'react';
import styled from 'styled-components';
import {
ClusterStatus,
ClusterStatusLabelMap,
ProviderLabelMap
} from '../../config';
import providers from '../../config/providers';
import { useClusterDetail } from '../../services/use-cluster-detail';
const Container = styled.div`
display: flex;
height: 168px;
.left {
padding: 16px 24px;
width: 124px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 20px;
.img {
width: 72px;
height: 72px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
background-color: var(--ant-blue-1);
.anticon {
font-size: 36px;
}
}
}
.right {
flex: 1;
padding-block: 16px;
padding-inline: 0 24px;
}
`;
const Title = styled.div`
display: flex;
align-items: center;
gap: 8px;
`;
const Resources = styled.div`
display: flex;
gap: 24px;
align-items: center;
line-height: 22px;
margin-top: 16px;
.item {
display: flex;
align-items: center;
gap: 8px;
.anticon {
color: var(--ant-color-text-quaternary);
font-size: 16px;
}
.value {
font-weight: 500;
}
.label {
color: var(--ant-color-text-secondary);
}
}
`;
const ClusterBasic: React.FC<{ clusterId: number }> = ({ clusterId }) => {
const intl = useIntl();
const { clusterDetail, fetchClusterDetail } = useClusterDetail();
const items: DescriptionsProps['items'] = [
{
key: 'provider',
label: intl.formatMessage({ id: 'clusters.table.provider' }),
children: ProviderLabelMap[clusterDetail.provider || '']
},
{
key: 'created_at',
label: intl.formatMessage({ id: 'common.table.createTime' }),
children: dayjs(clusterDetail.created_at).format('YYYY-MM-DD HH:mm:ss')
}
];
useEffect(() => {
if (clusterId) {
fetchClusterDetail({ id: clusterId });
}
}, [clusterId]);
return (
<CardWrapper style={{ padding: 0 }}>
<Container>
<div className="left">
<div className="img">
{
providers.find((item) => item.value === clusterDetail?.provider)
?.icon
}
</div>
<div className="status">
<StatusTag
statusValue={{
status: ClusterStatus[clusterDetail?.state || ''],
text: ClusterStatusLabelMap[clusterDetail?.state || '']
}}
/>
</div>
</div>
<div className="right">
<Descriptions
title={
<Title>
<span>{clusterDetail.name}</span>
{clusterDetail.is_default && (
<Tooltip
title={intl.formatMessage({
id: 'clusters.form.setDefault.tips'
})}
>
<StarFilled
style={{ color: 'var(--ant-gold-4)', marginLeft: 4 }}
/>
</Tooltip>
)}
</Title>
}
layout="vertical"
items={items}
/>
<Resources>
<div className="item">
<IconFont type="icon-resources-filled"></IconFont>
<span className="value">
{clusterDetail.ready_workers}/{clusterDetail.workers}
</span>
<span className="label">
{intl.formatMessage({ id: 'resources.nodes' })}
</span>
</div>
<div className="item">
<IconFont type="icon-rocket-launch-fill"></IconFont>
<span className="value">{clusterDetail.models}</span>
<span className="label">
{intl.formatMessage({ id: 'clusters.table.deployments' })}
</span>
</div>
<div className="item">
<IconFont type="icon-gpu"></IconFont>
<span className="value">{clusterDetail.gpus}</span>
<span className="label">
{intl.formatMessage({ id: 'menu.resources.gpus' })}
</span>
</div>
</Resources>
</div>
</Container>
</CardWrapper>
);
};
export default ClusterBasic;