From e8e082aa46007d1e132dee8c534900f4363a55aa Mon Sep 17 00:00:00 2001 From: agatha197 Date: Thu, 31 Oct 2024 11:06:20 +0900 Subject: [PATCH] refactor: `MyEnvironmentPage` into two components --- react/src/components/CustomizedImageList.tsx | 337 ++++++++++++++++ react/src/helper/index.tsx | 2 +- react/src/pages/MyEnvironmentPage.tsx | 387 ++----------------- 3 files changed, 373 insertions(+), 353 deletions(-) create mode 100644 react/src/components/CustomizedImageList.tsx diff --git a/react/src/components/CustomizedImageList.tsx b/react/src/components/CustomizedImageList.tsx new file mode 100644 index 0000000000..f8c287d194 --- /dev/null +++ b/react/src/components/CustomizedImageList.tsx @@ -0,0 +1,337 @@ +import Flex from '../components/Flex'; +import { + BaseImageTags, + BaseVersionTags, + ConstraintTags, + LangTags, +} from '../components/ImageTags'; +import TableColumnsSettingModal from '../components/TableColumnsSettingModal'; +import { getImageFullName, localeCompare } from '../helper'; +import { + useBackendAIImageMetaData, + useSuspendedBackendaiClient, + useUpdatableState, +} from '../hooks'; +import { CustomizedImageListForgetAndUntagMutation } from './__generated__/CustomizedImageListForgetAndUntagMutation.graphql'; +import { + CustomizedImageListQuery, + CustomizedImageListQuery$data, +} from './__generated__/CustomizedImageListQuery.graphql'; +import { DeleteOutlined, SettingOutlined } from '@ant-design/icons'; +import { useLocalStorageState } from 'ahooks'; +import { App, Button, Input, Popconfirm, Table, theme, Typography } from 'antd'; +import { AnyObject } from 'antd/es/_util/type'; +import { ColumnsType, ColumnType } from 'antd/es/table'; +import graphql from 'babel-plugin-relay/macro'; +import _ from 'lodash'; +import React, { PropsWithChildren, useState, useTransition } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useLazyLoadQuery, useMutation } from 'react-relay'; + +export type CommittedImage = NonNullable< + CustomizedImageListQuery$data['customized_images'] +>[number]; + +const CustomizedImageList: React.FC = ({ children }) => { + const { t } = useTranslation(); + const { token } = theme.useToken(); + const { message } = App.useApp(); + const baiClient = useSuspendedBackendaiClient(); + const supportExtendedImageInfo = + baiClient?.supports('extended-image-info') ?? false; + + const [isOpenColumnsSetting, setIsOpenColumnsSetting] = useState(false); + const [isRefetchPending, startRefetchTransition] = useTransition(); + const [customizedImageListFetchKey, updateCustomizedImageListFetchKey] = + useUpdatableState('initial-fetch'); + const [inFlightImageId, setInFlightImageId] = useState(); + const [ + , + { + getNamespace, + getImageLang, + getBaseVersion, + getBaseImage, + getConstraints, + }, + ] = useBackendAIImageMetaData(); + + const { customized_images } = useLazyLoadQuery( + graphql` + query CustomizedImageListQuery { + customized_images { + id + name @deprecatedSince(version: "24.09.1.") + humanized_name + tag + registry + architecture + digest + labels { + key + value + } + supported_accelerators + namespace @since(version: "24.09.1.") + } + } + `, + {}, + { + fetchPolicy: + customizedImageListFetchKey === 'initial-fetch' + ? 'store-and-network' + : 'network-only', + fetchKey: customizedImageListFetchKey, + }, + ); + + const [commitForgetAndUntag, isInflightForgetAndUntag] = + useMutation(graphql` + mutation CustomizedImageListForgetAndUntagMutation($id: String!) { + untag_image_from_registry(image_id: $id) { + ok + msg + } + forget_image_by_id(image_id: $id) { + ok + msg + } + } + `); + + const columns: ColumnsType = [ + { + title: t('environment.Registry'), + dataIndex: 'registry', + key: 'registry', + sorter: (a, b) => localeCompare(a?.registry, b?.registry), + }, + { + title: t('environment.Architecture'), + dataIndex: 'architecture', + key: 'architecture', + sorter: (a, b) => localeCompare(a?.architecture, b?.architecture), + }, + ...(supportExtendedImageInfo + ? [ + { + title: t('environment.Namespace'), + key: 'namespace', + dataIndex: 'namespace', + sorter: (a: CommittedImage, b: CommittedImage) => + localeCompare(a?.namespace, b?.namespace), + }, + ] + : [ + { + title: t('environment.Namespace'), + key: 'name', + dataIndex: 'name', + sorter: (a: CommittedImage, b: CommittedImage) => { + const namespaceA = getNamespace(getImageFullName(a) || ''); + const namespaceB = getNamespace(getImageFullName(b) || ''); + return namespaceA && namespaceB + ? namespaceA.localeCompare(namespaceB) + : 0; + }, + render: (text: string, row: CommittedImage) => ( + {getNamespace(getImageFullName(row) || '')} + ), + }, + ]), + { + title: t('environment.Language'), + key: 'lang', + sorter: (a, b) => { + const langA = getImageLang(getImageFullName(a) || ''); + const langB = getImageLang(getImageFullName(b) || ''); + return langA && langB ? langA.localeCompare(langB) : 0; + }, + render: (text, row) => ( + + ), + }, + { + title: t('environment.Version'), + key: 'baseversion', + sorter: (a, b) => { + const baseversionA = getBaseVersion(getImageFullName(a) || ''); + const baseversionB = getBaseVersion(getImageFullName(b) || ''); + return baseversionA && baseversionB + ? baseversionA.localeCompare(baseversionB) + : 0; + }, + render: (text, row) => ( + + ), + }, + { + title: t('environment.Base'), + key: 'baseimage', + sorter: (a, b) => { + const baseimageA = getBaseImage(getImageFullName(a) || ''); + const baseimageB = getBaseImage(getImageFullName(b) || ''); + return baseimageA && baseimageB + ? baseimageA.localeCompare(baseimageB) + : 0; + }, + render: (text, row) => ( + + ), + }, + { + title: t('environment.Constraint'), + key: 'constraint', + sorter: (a, b) => { + const requirementA = + a?.tag && b?.labels + ? getConstraints( + a?.tag, + a?.labels as { key: string; value: string }[], + )[0] || '' + : ''; + const requirementB = + b?.tag && b?.labels + ? getConstraints( + b?.tag, + b?.labels as { key: string; value: string }[], + )[0] || '' + : ''; + if (requirementA === '' && requirementB === '') return 0; + if (requirementA === '') return -1; + if (requirementB === '') return 1; + return requirementA.localeCompare(requirementB); + }, + render: (text, row) => + row?.tag ? ( + + ) : null, + }, + { + title: t('environment.Digest'), + dataIndex: 'digest', + key: 'digest', + sorter: (a, b) => + a?.digest && b?.digest ? a.digest.localeCompare(b.digest) : 0, + }, + { + title: t('general.Control'), + key: 'control', + fixed: 'right', + render: (text, row) => ( + + + { + if (row?.id) { + setInFlightImageId(row.id + customizedImageListFetchKey); + commitForgetAndUntag({ + variables: { + id: row.id, + }, + onCompleted(data, errors) { + if (errors) { + message.error(errors[0]?.message); + return; + } + startRefetchTransition(() => { + updateCustomizedImageListFetchKey(); + }); + message.success( + t('environment.CustomizedImageSuccessfullyDeleted'), + ); + }, + onError(err) { + message.error(err?.message); + }, + }); + } + }} + > +