Skip to content

Commit cb0e1f7

Browse files
committed
feat(FR-1499): implement delete and restore artifacts
1 parent 4dfe10a commit cb0e1f7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+657
-99
lines changed

packages/backend.ai-ui/src/components/fragments/BAIArtifactTable.tsx

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
filterOutNullAndUndefined,
99
toLocalId,
1010
} from '../../helper';
11+
import { BAIRestoreIcon, BAITrashBinIcon } from '../../icons';
1112
import BAIFlex from '../BAIFlex';
1213
import BAILink from '../BAILink';
1314
import BAIText from '../BAIText';
@@ -16,7 +17,7 @@ import BAIArtifactRevisionDownloadButton from './BAIArtifactRevisionDownloadButt
1617
import BAIArtifactStatusTag from './BAIArtifactStatusTag';
1718
import BAIArtifactTypeTag from './BAIArtifactTypeTag';
1819
import { SyncOutlined } from '@ant-design/icons';
19-
import { TableColumnsType, theme, Typography } from 'antd';
20+
import { Button, TableColumnsType, theme, Typography } from 'antd';
2021
import dayjs from 'dayjs';
2122
import relativeTime from 'dayjs/plugin/relativeTime';
2223
import _ from 'lodash';
@@ -77,11 +78,15 @@ export interface BAIArtifactTableProps
7778
extends Omit<BAITableProps<Artifact>, 'dataSource' | 'columns' | 'rowKey'> {
7879
artifactFragment: BAIArtifactTableArtifactFragment$key;
7980
onClickPull: (artifactId: string, revisionId: string) => void;
81+
onClickDelete: (artifactId: string) => void;
82+
onClickRestore: (artifactId: string) => void;
8083
}
8184

8285
const BAIArtifactTable = ({
8386
artifactFragment,
8487
onClickPull,
88+
onClickDelete,
89+
onClickRestore,
8590
...tableProps
8691
}: BAIArtifactTableProps) => {
8792
const { token } = theme.useToken();
@@ -97,6 +102,7 @@ const BAIArtifactTable = ({
97102
description
98103
updatedAt
99104
scannedAt
105+
availability
100106
...BAIArtifactTypeTagFragment
101107
latestVersion: revisions(
102108
first: 1
@@ -125,9 +131,9 @@ const BAIArtifactTable = ({
125131
key: 'name',
126132
render: (name: string, record: Artifact) => {
127133
return (
128-
<BAIFlex direction="column" align="start">
134+
<BAIFlex direction="column" align="start" wrap="wrap">
129135
<BAIFlex gap={'xs'}>
130-
<BAILink to={'/reservoir/' + toLocalId(record.id)}>
136+
<BAILink to={'/reservoir/' + toLocalId(record.id)} style={{}}>
131137
{name}
132138
</BAILink>
133139
<BAIArtifactTypeTag artifactTypeFrgmt={record} />
@@ -143,7 +149,42 @@ const BAIArtifactTable = ({
143149
</BAIFlex>
144150
);
145151
},
146-
width: '30%',
152+
},
153+
{
154+
title: t('comp:BAIArtifactTable.Controls'),
155+
key: 'controls',
156+
render: (record: Artifact) => {
157+
const availability = record.availability;
158+
if (availability === 'ALIVE') {
159+
return (
160+
<Button
161+
title={t('comp:BAIArtifactTable.Delete')}
162+
size="small"
163+
type="text"
164+
style={{
165+
color: token.colorError,
166+
background: token.colorErrorBg,
167+
}}
168+
icon={<BAITrashBinIcon />}
169+
onClick={() => onClickDelete(record.id)}
170+
/>
171+
);
172+
} else if (availability === 'DELETED') {
173+
return (
174+
<Button
175+
title={t('comp:BAIArtifactTable.Restore')}
176+
size="small"
177+
type="text"
178+
icon={<BAIRestoreIcon />}
179+
style={{
180+
color: token.colorInfo,
181+
background: token.colorInfoBg,
182+
}}
183+
onClick={() => onClickRestore(record.id)}
184+
/>
185+
);
186+
}
187+
},
147188
},
148189
{
149190
title: t('comp:BAIArtifactRevisionTable.LatestVersion'),
@@ -171,7 +212,6 @@ const BAIArtifactTable = ({
171212
</BAIFlex>
172213
);
173214
},
174-
width: '25%',
175215
},
176216
{
177217
title: t('comp:BAIArtifactRevisionTable.Size'),
@@ -187,7 +227,6 @@ const BAIArtifactTable = ({
187227
</BAIText>
188228
);
189229
},
190-
width: '15%',
191230
},
192231
{
193232
title: t('comp:BAIArtifactTable.Scanned'),
@@ -203,7 +242,6 @@ const BAIArtifactTable = ({
203242
</Typography.Text>
204243
);
205244
},
206-
width: '15%',
207245
},
208246
{
209247
title: t('comp:BAIArtifactRevisionTable.Updated'),
@@ -219,7 +257,6 @@ const BAIArtifactTable = ({
219257
</Typography.Text>
220258
);
221259
},
222-
width: '15%',
223260
},
224261
];
225262

packages/backend.ai-ui/src/components/fragments/BAIDeleteArtifactModal.tsx renamed to packages/backend.ai-ui/src/components/fragments/BAIDeleteArtifactRevisionsModal.tsx

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { BAIDeleteArtifactModalArtifactFragment$key } from '../../__generated__/BAIDeleteArtifactModalArtifactFragment.graphql';
1+
import { BAIDeleteArtifactRevisionsModalArtifactFragment$key } from '../../__generated__/BAIDeleteArtifactRevisionsModalArtifactFragment.graphql';
22
import {
3-
BAIDeleteArtifactModalArtifactRevisionFragment$data,
4-
BAIDeleteArtifactModalArtifactRevisionFragment$key,
5-
} from '../../__generated__/BAIDeleteArtifactModalArtifactRevisionFragment.graphql';
6-
import { BAIDeleteArtifactModalCleanupVersionMutation } from '../../__generated__/BAIDeleteArtifactModalCleanupVersionMutation.graphql';
3+
BAIDeleteArtifactRevisionsModalArtifactRevisionFragment$data,
4+
BAIDeleteArtifactRevisionsModalArtifactRevisionFragment$key,
5+
} from '../../__generated__/BAIDeleteArtifactRevisionsModalArtifactRevisionFragment.graphql';
6+
import { BAIDeleteArtifactRevisionsModalCleanupVersionMutation } from '../../__generated__/BAIDeleteArtifactRevisionsModalCleanupVersionMutation.graphql';
77
import {
88
convertToDecimalUnit,
99
filterOutEmpty,
@@ -22,34 +22,34 @@ import { useTranslation } from 'react-i18next';
2222
import { graphql, useFragment, useMutation } from 'react-relay';
2323

2424
type ArtifactRevision =
25-
NonNullable<BAIDeleteArtifactModalArtifactRevisionFragment$data>[number];
25+
NonNullable<BAIDeleteArtifactRevisionsModalArtifactRevisionFragment$data>[number];
2626

27-
export type BAIDeleteArtifactModalArtifactFragmentKey =
28-
BAIDeleteArtifactModalArtifactFragment$key;
27+
export type BAIDeleteArtifactRevisionsModalArtifactFragmentKey =
28+
BAIDeleteArtifactRevisionsModalArtifactFragment$key;
2929

30-
export type BAIDeleteArtifactModalArtifactRevisionFragmentKey =
31-
BAIDeleteArtifactModalArtifactRevisionFragment$key;
30+
export type BAIDeleteArtifactRevisionsModalArtifactRevisionFragmentKey =
31+
BAIDeleteArtifactRevisionsModalArtifactRevisionFragment$key;
3232

33-
export interface BAIDeleteArtifactModalProps
33+
export interface BAIDeleteArtifactRevisionsModalProps
3434
extends Omit<ModalProps, 'onOk' | 'onCancel'> {
35-
selectedArtifactFrgmt: BAIDeleteArtifactModalArtifactFragment$key | null;
36-
selectedArtifactRevisionFrgmt: BAIDeleteArtifactModalArtifactRevisionFragment$key;
35+
selectedArtifactFrgmt: BAIDeleteArtifactRevisionsModalArtifactFragment$key | null;
36+
selectedArtifactRevisionFrgmt: BAIDeleteArtifactRevisionsModalArtifactRevisionFragment$key;
3737
onOk: (e: React.MouseEvent<HTMLElement>) => void;
3838
onCancel: (e: React.MouseEvent<HTMLElement>) => void;
3939
}
4040

41-
const BAIDeleteArtifactModal = ({
41+
const BAIDeleteArtifactRevisionsModal = ({
4242
selectedArtifactFrgmt,
4343
selectedArtifactRevisionFrgmt,
4444
onOk,
4545
onCancel,
4646
...modalProps
47-
}: BAIDeleteArtifactModalProps) => {
47+
}: BAIDeleteArtifactRevisionsModalProps) => {
4848
const { t } = useTranslation();
4949

5050
const [cleanupVersion, isInflightCleanupVersion] =
51-
useMutation<BAIDeleteArtifactModalCleanupVersionMutation>(graphql`
52-
mutation BAIDeleteArtifactModalCleanupVersionMutation(
51+
useMutation<BAIDeleteArtifactRevisionsModalCleanupVersionMutation>(graphql`
52+
mutation BAIDeleteArtifactRevisionsModalCleanupVersionMutation(
5353
$input: CleanupArtifactRevisionsInput!
5454
) {
5555
cleanupArtifactRevisions(input: $input) {
@@ -64,9 +64,9 @@ const BAIDeleteArtifactModal = ({
6464
}
6565
`);
6666
const selectedArtifact =
67-
useFragment<BAIDeleteArtifactModalArtifactFragment$key>(
67+
useFragment<BAIDeleteArtifactRevisionsModalArtifactFragment$key>(
6868
graphql`
69-
fragment BAIDeleteArtifactModalArtifactFragment on Artifact {
69+
fragment BAIDeleteArtifactRevisionsModalArtifactFragment on Artifact {
7070
id
7171
...BAIArtifactDescriptionsFragment
7272
}
@@ -75,9 +75,9 @@ const BAIDeleteArtifactModal = ({
7575
);
7676

7777
const selectedArtifactRevision =
78-
useFragment<BAIDeleteArtifactModalArtifactRevisionFragment$key>(
78+
useFragment<BAIDeleteArtifactRevisionsModalArtifactRevisionFragment$key>(
7979
graphql`
80-
fragment BAIDeleteArtifactModalArtifactRevisionFragment on ArtifactRevision
80+
fragment BAIDeleteArtifactRevisionsModalArtifactRevisionFragment on ArtifactRevision
8181
@relay(plural: true) {
8282
id
8383
version
@@ -202,4 +202,4 @@ const BAIDeleteArtifactModal = ({
202202
);
203203
};
204204

205-
export default BAIDeleteArtifactModal;
205+
export default BAIDeleteArtifactRevisionsModal;
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { BAIDeleteArtifactsModalArtifactsFragment$key } from '../../__generated__/BAIDeleteArtifactsModalArtifactsFragment.graphql';
2+
import { BAIDeleteArtifactsModalDeleteArtifactsMutation } from '../../__generated__/BAIDeleteArtifactsModalDeleteArtifactsMutation.graphql';
3+
import { toLocalId } from '../../helper';
4+
import BAIUnmountAfterClose from '../BAIUnmountAfterClose';
5+
import { App, Modal, ModalProps, Typography } from 'antd';
6+
import { useTranslation } from 'react-i18next';
7+
import { graphql, useFragment, useMutation } from 'react-relay';
8+
9+
export type BAIDeleteArtifactsModalArtifactsFragmentKey =
10+
BAIDeleteArtifactsModalArtifactsFragment$key;
11+
12+
export interface BAIDeleteArtifactsModalProps extends ModalProps {
13+
selectedArtifactsFragment: BAIDeleteArtifactsModalArtifactsFragmentKey;
14+
}
15+
16+
const BAIDeleteArtifactsModal = ({
17+
selectedArtifactsFragment,
18+
onOk,
19+
onCancel,
20+
...modalProps
21+
}: BAIDeleteArtifactsModalProps) => {
22+
const { t } = useTranslation();
23+
const { message } = App.useApp();
24+
const selectedArtifacts = useFragment(
25+
graphql`
26+
fragment BAIDeleteArtifactsModalArtifactsFragment on Artifact
27+
@relay(plural: true) {
28+
id
29+
name
30+
}
31+
`,
32+
selectedArtifactsFragment,
33+
);
34+
35+
const [deleteArtifacts, isInflightDeleteArtifacts] =
36+
useMutation<BAIDeleteArtifactsModalDeleteArtifactsMutation>(graphql`
37+
mutation BAIDeleteArtifactsModalDeleteArtifactsMutation(
38+
$input: DeleteArtifactsInput!
39+
) {
40+
deleteArtifacts(input: $input) {
41+
artifacts {
42+
id
43+
availability
44+
}
45+
}
46+
}
47+
`);
48+
49+
return (
50+
<BAIUnmountAfterClose>
51+
<Modal
52+
title="Delete Artifacts"
53+
centered
54+
okText="Delete"
55+
cancelText={t('general.button.Cancel')}
56+
onOk={(e) => {
57+
deleteArtifacts({
58+
variables: {
59+
input: {
60+
artifactIds: selectedArtifacts.map((a) => toLocalId(a.id)),
61+
},
62+
},
63+
onCompleted: (res, errors) => {
64+
if (errors && errors.length > 0) {
65+
errors.forEach((err) =>
66+
message.error(err.message ?? 'Failed to delete artifacts'),
67+
);
68+
return;
69+
}
70+
message.success('Successfully deleted artifacts');
71+
onOk?.(e);
72+
},
73+
onError: (err) => {
74+
message.error(err.message ?? 'Failed to delete artifacts');
75+
},
76+
});
77+
}}
78+
onCancel={(e) => {
79+
onCancel?.(e);
80+
}}
81+
okButtonProps={{ danger: true, loading: isInflightDeleteArtifacts }}
82+
{...modalProps}
83+
>
84+
<Typography.Text>
85+
{selectedArtifacts.length === 1
86+
? `Are you sure you want to delete ${selectedArtifacts[0].name}?`
87+
: `Are you sure you want to delete ${selectedArtifacts.length} artifacts?`}
88+
</Typography.Text>
89+
</Modal>
90+
</BAIUnmountAfterClose>
91+
);
92+
};
93+
94+
export default BAIDeleteArtifactsModal;

0 commit comments

Comments
 (0)