Skip to content

Commit 6e9f608

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

2 files changed

Lines changed: 174 additions & 6 deletions

File tree

react/src/pages/AdminModelCardListPage.tsx

Lines changed: 169 additions & 6 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,
@@ -13,17 +14,21 @@ import type {
1314
import AdminModelCardSettingModal from '../components/AdminModelCardSettingModal';
1415
import { convertToOrderBy, handleRowSelectionChange } from '../helper';
1516
import { useBAIPaginationOptionStateOnSearchParam } from '../hooks/reactPaginationQueryOptions';
17+
import { useSetBAINotification } from '../hooks/useBAINotification';
1618
import { useBAISettingUserState } from '../hooks/useBAISetting';
1719
import { useCurrentProjectValue } from '../hooks/useCurrentProject';
1820
import { SettingOutlined } from '@ant-design/icons';
19-
import { App, Typography } from 'antd';
21+
import { shapes } from '@dicebear/collection';
22+
import { createAvatar } from '@dicebear/core';
23+
import { App, Checkbox, Tooltip, Typography, theme } from 'antd';
2024
import {
2125
BAIButton,
2226
BAIColumnType,
2327
BAIDeleteConfirmModal,
2428
BAIFetchKeyButton,
2529
BAIFlex,
2630
BAIGraphQLPropertyFilter,
31+
BAILink,
2732
BAINameActionCell,
2833
BAISelectionLabel,
2934
BAITable,
@@ -62,7 +67,9 @@ const AdminModelCardListPage: React.FC = () => {
6267

6368
const { t } = useTranslation();
6469
const { message } = App.useApp();
70+
const { token } = theme.useToken();
6571
const { logger } = useBAILogger();
72+
const { upsertNotification } = useSetBAINotification();
6673
const currentProject = useCurrentProjectValue();
6774
const [columnOverrides, setColumnOverrides] = useBAISettingUserState(
6875
'table_column_overrides.AdminModelCardListPage',
@@ -77,6 +84,7 @@ const AdminModelCardListPage: React.FC = () => {
7784
);
7885
const [deletingModelCard, setDeletingModelCard] =
7986
useState<ModelCardNode | null>(null);
87+
const [alsoDeleteFolder, setAlsoDeleteFolder] = useState(false);
8088
const [isBulkDeleteOpen, setIsBulkDeleteOpen] = useState(false);
8189
const {
8290
baiPaginationOption,
@@ -144,6 +152,13 @@ const AdminModelCardListPage: React.FC = () => {
144152
node {
145153
id
146154
name
155+
vfolderId
156+
vfolder {
157+
id
158+
metadata {
159+
name
160+
}
161+
}
147162
domainName
148163
projectId
149164
accessLevel
@@ -185,6 +200,15 @@ const AdminModelCardListPage: React.FC = () => {
185200
}
186201
`);
187202

203+
const [commitDeleteVFolder] =
204+
useMutation<AdminModelCardListPageDeleteVFolderMutation>(graphql`
205+
mutation AdminModelCardListPageDeleteVFolderMutation($vfolderId: UUID!) {
206+
deleteVfolderV2(vfolderId: $vfolderId) {
207+
id
208+
}
209+
}
210+
`);
211+
188212
const [commitBulkDeleteModelCards, isBulkDeleteInFlight] =
189213
useMutation<AdminModelCardListPageBulkDeleteMutation>(graphql`
190214
mutation AdminModelCardListPageBulkDeleteMutation(
@@ -428,6 +452,51 @@ const AdminModelCardListPage: React.FC = () => {
428452
description={t('adminModelCard.ConfirmDelete', {
429453
name: deletingModelCard?.name,
430454
})}
455+
extraContent={
456+
<BAIFlex direction="column" gap={'xs'}>
457+
{deletingModelCard?.vfolder && (
458+
<BAIFlex align="center" gap={'xs'}>
459+
<img
460+
draggable={false}
461+
onDragStart={(e) => e.preventDefault()}
462+
style={{
463+
borderRadius: '0.25em',
464+
width: '1em',
465+
height: '1em',
466+
borderWidth: 0.5,
467+
borderStyle: 'solid',
468+
borderColor: token.colorBorder,
469+
userSelect: 'none',
470+
flexShrink: 0,
471+
}}
472+
src={createAvatar(shapes, {
473+
seed: deletingModelCard.vfolderId,
474+
shape3: [],
475+
}).toDataUri()}
476+
alt="VFolder Identicon"
477+
/>
478+
<BAILink
479+
to={{
480+
pathname: '/data',
481+
search: new URLSearchParams({
482+
folder: deletingModelCard.vfolderId,
483+
}).toString(),
484+
}}
485+
>
486+
{deletingModelCard.vfolder.metadata.name}
487+
</BAILink>
488+
</BAIFlex>
489+
)}
490+
<Tooltip title={t('adminModelCard.AlsoDeleteModelFolderTooltip')}>
491+
<Checkbox
492+
checked={alsoDeleteFolder}
493+
onChange={(e) => setAlsoDeleteFolder(e.target.checked)}
494+
>
495+
{t('adminModelCard.AlsoDeleteModelFolder')}
496+
</Checkbox>
497+
</Tooltip>
498+
</BAIFlex>
499+
}
431500
onOk={() => {
432501
if (deletingModelCard) {
433502
return new Promise<void>((resolve, reject) => {
@@ -442,10 +511,101 @@ const AdminModelCardListPage: React.FC = () => {
442511
reject();
443512
return;
444513
}
445-
message.success(t('adminModelCard.ModelCardDeleted'));
446-
setDeletingModelCard(null);
447-
updateFetchKey();
448-
resolve();
514+
515+
const vfolderId = deletingModelCard.vfolderId;
516+
const folderTrashSearch = new URLSearchParams({
517+
statusCategory: 'deleted',
518+
filter: `row_id == "${vfolderId}"`,
519+
}).toString();
520+
521+
if (alsoDeleteFolder && vfolderId) {
522+
commitDeleteVFolder({
523+
variables: { vfolderId },
524+
onCompleted: (_vfolderData, vfolderErrors) => {
525+
if (vfolderErrors && vfolderErrors.length > 0) {
526+
logger.error(vfolderErrors[0]);
527+
message.error(
528+
vfolderErrors[0]?.message ||
529+
t('general.ErrorOccurred'),
530+
);
531+
upsertNotification({
532+
type: 'success',
533+
message: t(
534+
'adminModelCard.ModelCardDeletedFolderKept',
535+
),
536+
to: {
537+
pathname: '/data',
538+
search: 'statusCategory=deleted',
539+
},
540+
toText: t('adminModelCard.GoToTrash'),
541+
open: true,
542+
duration: 4,
543+
extraData: null,
544+
});
545+
} else {
546+
upsertNotification({
547+
type: 'success',
548+
message: t(
549+
'adminModelCard.ModelCardAndFolderDeleted',
550+
),
551+
to: {
552+
pathname: '/data',
553+
search: folderTrashSearch,
554+
},
555+
toText: t('adminModelCard.GoToTrash'),
556+
open: true,
557+
duration: 4,
558+
extraData: null,
559+
});
560+
}
561+
setDeletingModelCard(null);
562+
setAlsoDeleteFolder(false);
563+
updateFetchKey();
564+
resolve();
565+
},
566+
onError: (error) => {
567+
logger.error(error);
568+
message.error(
569+
error?.message || t('general.ErrorOccurred'),
570+
);
571+
upsertNotification({
572+
type: 'success',
573+
message: t(
574+
'adminModelCard.ModelCardDeletedFolderKept',
575+
),
576+
to: {
577+
pathname: '/data',
578+
search: 'statusCategory=deleted',
579+
},
580+
toText: t('adminModelCard.GoToTrash'),
581+
open: true,
582+
duration: 4,
583+
extraData: null,
584+
});
585+
setDeletingModelCard(null);
586+
setAlsoDeleteFolder(false);
587+
updateFetchKey();
588+
resolve();
589+
},
590+
});
591+
} else {
592+
upsertNotification({
593+
type: 'success',
594+
message: t('adminModelCard.ModelCardDeletedFolderKept'),
595+
to: {
596+
pathname: '/data',
597+
search: 'statusCategory=deleted',
598+
},
599+
toText: t('adminModelCard.GoToTrash'),
600+
open: true,
601+
duration: 4,
602+
extraData: null,
603+
});
604+
setDeletingModelCard(null);
605+
setAlsoDeleteFolder(false);
606+
updateFetchKey();
607+
resolve();
608+
}
449609
},
450610
onError: (error) => {
451611
logger.error(error);
@@ -456,7 +616,10 @@ const AdminModelCardListPage: React.FC = () => {
456616
});
457617
}
458618
}}
459-
onCancel={() => setDeletingModelCard(null)}
619+
onCancel={() => {
620+
setDeletingModelCard(null);
621+
setAlsoDeleteFolder(false);
622+
}}
460623
/>
461624
<BAIDeleteConfirmModal
462625
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)