Skip to content

Commit 3885cfa

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

2 files changed

Lines changed: 109 additions & 7 deletions

File tree

react/src/pages/AdminModelCardListPage.tsx

Lines changed: 104 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,80 @@ 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+
notification.success({
494+
key: notificationKey,
495+
message: t(
496+
'adminModelCard.ModelCardDeletedFolderKept',
497+
),
498+
btn: goToTrashBtn,
499+
});
500+
} else {
501+
notification.success({
502+
key: notificationKey,
503+
message: t(
504+
'adminModelCard.ModelCardAndFolderDeleted',
505+
),
506+
btn: goToTrashBtn,
507+
});
508+
}
509+
setDeletingModelCard(null);
510+
setAlsoDeleteFolder(false);
511+
updateFetchKey();
512+
resolve();
513+
},
514+
onError: (error) => {
515+
logger.error(error);
516+
message.error(
517+
error?.message || t('general.ErrorOccurred'),
518+
);
519+
notification.success({
520+
key: notificationKey,
521+
message: t(
522+
'adminModelCard.ModelCardDeletedFolderKept',
523+
),
524+
btn: goToTrashBtn,
525+
});
526+
setDeletingModelCard(null);
527+
setAlsoDeleteFolder(false);
528+
updateFetchKey();
529+
resolve();
530+
},
531+
});
532+
} else {
533+
notification.success({
534+
key: notificationKey,
535+
message: t('adminModelCard.ModelCardDeletedFolderKept'),
536+
btn: goToTrashBtn,
537+
});
538+
setDeletingModelCard(null);
539+
setAlsoDeleteFolder(false);
540+
updateFetchKey();
541+
resolve();
542+
}
449543
},
450544
onError: (error) => {
451545
logger.error(error);
@@ -456,7 +550,10 @@ const AdminModelCardListPage: React.FC = () => {
456550
});
457551
}
458552
}}
459-
onCancel={() => setDeletingModelCard(null)}
553+
onCancel={() => {
554+
setDeletingModelCard(null);
555+
setAlsoDeleteFolder(false);
556+
}}
460557
/>
461558
<BAIDeleteConfirmModal
462559
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)