diff --git a/packages/backend.ai-ui/src/components/fragments/BAIActivateArtifactsModal.tsx b/packages/backend.ai-ui/src/components/fragments/BAIActivateArtifactsModal.tsx new file mode 100644 index 0000000000..5c426f9249 --- /dev/null +++ b/packages/backend.ai-ui/src/components/fragments/BAIActivateArtifactsModal.tsx @@ -0,0 +1,113 @@ +import { BAIActivateArtifactsModalArtifactsFragment$key } from '../../__generated__/BAIActivateArtifactsModalArtifactsFragment.graphql'; +import { BAIActivateArtifactsModalArtifactsFragmentRestoreArtifactsMutation } from '../../__generated__/BAIActivateArtifactsModalArtifactsFragmentRestoreArtifactsMutation.graphql'; +import { toLocalId } from '../../helper'; +import BAIUnmountAfterClose from '../BAIUnmountAfterClose'; +import { App, Modal, ModalProps, Typography } from 'antd'; +import { useTranslation } from 'react-i18next'; +import { graphql, useFragment, useMutation } from 'react-relay'; + +export type BAIActivateArtifactsModalArtifactsFragmentKey = + BAIActivateArtifactsModalArtifactsFragment$key; + +export interface BAIActivateArtifactsModalProps extends ModalProps { + selectedArtifactsFragment: BAIActivateArtifactsModalArtifactsFragmentKey; +} + +const BAIActivateArtifactsModal = ({ + selectedArtifactsFragment, + onOk, + onCancel, + ...props +}: BAIActivateArtifactsModalProps) => { + const { t } = useTranslation(); + const { message } = App.useApp(); + + const selectedArtifacts = + useFragment( + graphql` + fragment BAIActivateArtifactsModalArtifactsFragment on Artifact + @relay(plural: true) { + id + name + } + `, + selectedArtifactsFragment, + ); + + const [restoreArtifacts, isInflightRestoreArtifacts] = + useMutation( + graphql` + mutation BAIActivateArtifactsModalArtifactsFragmentRestoreArtifactsMutation( + $input: RestoreArtifactsInput! + ) { + restoreArtifacts(input: $input) { + artifacts { + id + availability + } + } + } + `, + ); + + return ( + + { + restoreArtifacts({ + variables: { + input: { + artifactIds: selectedArtifacts.map((a) => toLocalId(a.id)), + }, + }, + onCompleted: (res, errors) => { + if (errors && errors.length > 0) { + errors.forEach((err) => + message.error( + err.message ?? + t( + 'comp:BAIActivateArtifactsModal.FailedToActivateArtifacts', + ), + ), + ); + return; + } + message.success( + t('comp:BAIActivateArtifactsModal.SuccessfullyActivated'), + ); + onOk?.(e); + }, + onError: (err) => { + message.error( + err.message ?? + t('comp:BAIActivateArtifactsModal.FailedToActivateArtifacts'), + ); + }, + }); + }} + onCancel={(e) => { + onCancel?.(e); + }} + okText={t('comp:BAIActivateArtifactsModal.Activate')} + okButtonProps={{ loading: isInflightRestoreArtifacts }} + > + + {selectedArtifacts.length === 1 + ? t( + 'comp:BAIActivateArtifactsModal.AreYouSureYouWantToActivateOne', + { name: selectedArtifacts[0].name }, + ) + : t( + 'comp:BAIActivateArtifactsModal.AreYouSureYouWantToActivateSome', + { count: selectedArtifacts.length }, + )} + + + + ); +}; + +export default BAIActivateArtifactsModal; diff --git a/packages/backend.ai-ui/src/components/fragments/BAIArtifactTable.tsx b/packages/backend.ai-ui/src/components/fragments/BAIArtifactTable.tsx index 125bad7a5c..6b562c9146 100644 --- a/packages/backend.ai-ui/src/components/fragments/BAIArtifactTable.tsx +++ b/packages/backend.ai-ui/src/components/fragments/BAIArtifactTable.tsx @@ -16,11 +16,11 @@ import BAIArtifactRevisionDownloadButton from './BAIArtifactRevisionDownloadButt import BAIArtifactStatusTag from './BAIArtifactStatusTag'; import BAIArtifactTypeTag from './BAIArtifactTypeTag'; import { SyncOutlined } from '@ant-design/icons'; -import { TableColumnsType, theme, Typography } from 'antd'; +import { Button, TableColumnsType, theme, Typography } from 'antd'; import dayjs from 'dayjs'; import relativeTime from 'dayjs/plugin/relativeTime'; import _ from 'lodash'; -import { Package, Container, Brain } from 'lucide-react'; +import { Package, Container, Brain, BanIcon, UndoIcon } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { graphql, useFragment } from 'react-relay'; @@ -77,11 +77,15 @@ export interface BAIArtifactTableProps extends Omit, 'dataSource' | 'columns' | 'rowKey'> { artifactFragment: BAIArtifactTableArtifactFragment$key; onClickPull: (artifactId: string, revisionId: string) => void; + onClickDelete: (artifactId: string) => void; + onClickRestore: (artifactId: string) => void; } const BAIArtifactTable = ({ artifactFragment, onClickPull, + onClickDelete, + onClickRestore, ...tableProps }: BAIArtifactTableProps) => { const { token } = theme.useToken(); @@ -97,6 +101,7 @@ const BAIArtifactTable = ({ description updatedAt scannedAt + availability ...BAIArtifactTypeTagFragment latestVersion: revisions( first: 1 @@ -125,9 +130,9 @@ const BAIArtifactTable = ({ key: 'name', render: (name: string, record: Artifact) => { return ( - + - + {name} @@ -143,7 +148,42 @@ const BAIArtifactTable = ({ ); }, - width: '30%', + }, + { + title: t('comp:BAIArtifactTable.Controls'), + key: 'controls', + render: (record: Artifact) => { + const availability = record.availability; + if (availability === 'ALIVE') { + return ( +