Skip to content

Commit 16cba6b

Browse files
committed
fix(FR-2622): add delete-folder option and trash notification on model card deletion
1 parent b941120 commit 16cba6b

2 files changed

Lines changed: 95 additions & 7 deletions

File tree

react/src/pages/AdminModelCardListPage.tsx

Lines changed: 90 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55
import type { AdminModelCardListPageBulkDeleteMutation } from '../__generated__/AdminModelCardListPageBulkDeleteMutation.graphql';
66
import type { AdminModelCardListPageDeleteMutation } from '../__generated__/AdminModelCardListPageDeleteMutation.graphql';
7+
import type { AdminModelCardListPageDeleteVFolderMutation } from '../__generated__/AdminModelCardListPageDeleteVFolderMutation.graphql';
78
import type {
89
AdminModelCardListPageQuery,
910
AdminModelCardListPageQuery$data,
@@ -16,7 +17,7 @@ import { useBAIPaginationOptionStateOnSearchParam } from '../hooks/reactPaginati
1617
import { useBAISettingUserState } from '../hooks/useBAISetting';
1718
import { useCurrentProjectValue } from '../hooks/useCurrentProject';
1819
import { SettingOutlined } from '@ant-design/icons';
19-
import { App, Typography } from 'antd';
20+
import { App, Checkbox, Tooltip, Typography } from 'antd';
2021
import {
2122
BAIButton,
2223
BAIColumnType,
@@ -45,6 +46,7 @@ import { parseAsJson, parseAsString, useQueryStates } from 'nuqs';
4546
import React, { useDeferredValue, useState } from 'react';
4647
import { useTranslation } from 'react-i18next';
4748
import { graphql, useLazyLoadQuery, useMutation } from 'react-relay';
49+
import { useNavigate } from 'react-router-dom';
4850

4951
type ModelCardNode = NonNullableNodeOnEdges<
5052
AdminModelCardListPageQuery$data['adminModelCardsV2']
@@ -61,8 +63,9 @@ const AdminModelCardListPage: React.FC = () => {
6163
'use memo';
6264

6365
const { t } = useTranslation();
64-
const { message } = App.useApp();
66+
const { message, notification } = App.useApp();
6567
const { logger } = useBAILogger();
68+
const navigate = useNavigate();
6669
const currentProject = useCurrentProjectValue();
6770
const [columnOverrides, setColumnOverrides] = useBAISettingUserState(
6871
'table_column_overrides.AdminModelCardListPage',
@@ -77,6 +80,7 @@ const AdminModelCardListPage: React.FC = () => {
7780
);
7881
const [deletingModelCard, setDeletingModelCard] =
7982
useState<ModelCardNode | null>(null);
83+
const [alsoDeleteFolder, setAlsoDeleteFolder] = useState(false);
8084
const [isBulkDeleteOpen, setIsBulkDeleteOpen] = useState(false);
8185
const {
8286
baiPaginationOption,
@@ -144,6 +148,7 @@ const AdminModelCardListPage: React.FC = () => {
144148
node {
145149
id
146150
name
151+
vfolderId
147152
domainName
148153
projectId
149154
accessLevel
@@ -185,6 +190,15 @@ const AdminModelCardListPage: React.FC = () => {
185190
}
186191
`);
187192

193+
const [commitDeleteVFolder] =
194+
useMutation<AdminModelCardListPageDeleteVFolderMutation>(graphql`
195+
mutation AdminModelCardListPageDeleteVFolderMutation($vfolderId: UUID!) {
196+
deleteVfolderV2(vfolderId: $vfolderId) {
197+
id
198+
}
199+
}
200+
`);
201+
188202
const [commitBulkDeleteModelCards, isBulkDeleteInFlight] =
189203
useMutation<AdminModelCardListPageBulkDeleteMutation>(graphql`
190204
mutation AdminModelCardListPageBulkDeleteMutation(
@@ -428,6 +442,16 @@ const AdminModelCardListPage: React.FC = () => {
428442
description={t('adminModelCard.ConfirmDelete', {
429443
name: deletingModelCard?.name,
430444
})}
445+
extraContent={
446+
<Tooltip title={t('adminModelCard.AlsoDeleteModelFolderTooltip')}>
447+
<Checkbox
448+
checked={alsoDeleteFolder}
449+
onChange={(e) => setAlsoDeleteFolder(e.target.checked)}
450+
>
451+
{t('adminModelCard.AlsoDeleteModelFolder')}
452+
</Checkbox>
453+
</Tooltip>
454+
}
431455
onOk={() => {
432456
if (deletingModelCard) {
433457
return new Promise<void>((resolve, reject) => {
@@ -442,10 +466,66 @@ const AdminModelCardListPage: React.FC = () => {
442466
reject();
443467
return;
444468
}
445-
message.success(t('adminModelCard.ModelCardDeleted'));
446-
setDeletingModelCard(null);
447-
updateFetchKey();
448-
resolve();
469+
470+
const notificationKey = `model-card-deleted-${Date.now()}`;
471+
const goToTrashBtn = (
472+
<BAIButton
473+
size="small"
474+
onClick={() => {
475+
notification.destroy(notificationKey);
476+
navigate('/data?statusCategory=deleted');
477+
}}
478+
>
479+
{t('adminModelCard.GoToTrash')}
480+
</BAIButton>
481+
);
482+
483+
if (alsoDeleteFolder && deletingModelCard.vfolderId) {
484+
commitDeleteVFolder({
485+
variables: { vfolderId: deletingModelCard.vfolderId },
486+
onCompleted: (_vfolderData, vfolderErrors) => {
487+
if (vfolderErrors && vfolderErrors.length > 0) {
488+
logger.error(vfolderErrors[0]);
489+
message.error(
490+
vfolderErrors[0]?.message ||
491+
t('general.ErrorOccurred'),
492+
);
493+
} else {
494+
notification.success({
495+
key: notificationKey,
496+
message: t(
497+
'adminModelCard.ModelCardAndFolderDeleted',
498+
),
499+
btn: goToTrashBtn,
500+
});
501+
}
502+
setDeletingModelCard(null);
503+
setAlsoDeleteFolder(false);
504+
updateFetchKey();
505+
resolve();
506+
},
507+
onError: (error) => {
508+
logger.error(error);
509+
message.error(
510+
error?.message || t('general.ErrorOccurred'),
511+
);
512+
setDeletingModelCard(null);
513+
setAlsoDeleteFolder(false);
514+
updateFetchKey();
515+
resolve();
516+
},
517+
});
518+
} else {
519+
notification.success({
520+
key: notificationKey,
521+
message: t('adminModelCard.ModelCardDeletedFolderKept'),
522+
btn: goToTrashBtn,
523+
});
524+
setDeletingModelCard(null);
525+
setAlsoDeleteFolder(false);
526+
updateFetchKey();
527+
resolve();
528+
}
449529
},
450530
onError: (error) => {
451531
logger.error(error);
@@ -456,7 +536,10 @@ const AdminModelCardListPage: React.FC = () => {
456536
});
457537
}
458538
}}
459-
onCancel={() => setDeletingModelCard(null)}
539+
onCancel={() => {
540+
setDeletingModelCard(null);
541+
setAlsoDeleteFolder(false);
542+
}}
460543
/>
461544
<BAIDeleteConfirmModal
462545
open={isBulkDeleteOpen}

resources/i18n/en.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
"AccessLevelUpdated": "Access level has been updated.",
1212
"AddFramework": "Add framework",
1313
"AddLabel": "Add label",
14+
"AlsoDeleteModelFolder": "Also delete the associated model folder",
15+
"AlsoDeleteModelFolderTooltip": "The model folder will be moved to trash. You can permanently delete it from Data > Trash.",
1416
"Architecture": "Architecture",
1517
"ArchitectureTooltip": "The model architecture (e.g., Transformer, CNN, RNN).",
1618
"Author": "Author",
@@ -32,12 +34,15 @@
3234
"EnterProjectId": "Enter project ID",
3335
"Framework": "Framework",
3436
"FrameworkTooltip": "Frameworks used by the model (e.g., PyTorch, TensorFlow). Press Enter to add.",
37+
"GoToTrash": "Go to Data > Trash",
3538
"Label": "Label",
3639
"LabelTooltip": "Custom tags for categorizing and filtering models. Press Enter to add.",
3740
"License": "License",
3841
"LicenseTooltip": "The license under which the model is distributed (e.g., MIT, Apache-2.0).",
42+
"ModelCardAndFolderDeleted": "Model card and folder have been moved to trash.",
3943
"ModelCardCreated": "Model card has been created.",
4044
"ModelCardDeleted": "Model card has been deleted.",
45+
"ModelCardDeletedFolderKept": "Model card has been deleted. The model folder was not deleted.",
4146
"ModelCardUpdated": "Model card has been updated.",
4247
"ModelCards": "Model Cards",
4348
"ModelStorageFolder": "Model Storage Folder",

0 commit comments

Comments
 (0)