Skip to content

Commit ab031bd

Browse files
committed
feat(FR-1296): Improve error message for deleting in-use vfolder
1 parent 72f49f9 commit ab031bd

27 files changed

+280
-29
lines changed

react/src/components/BAIGeneralNotificationItem.tsx

Lines changed: 14 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { NotificationState } from '../hooks/useBAINotification';
2+
import BAINotificationBackgroundItem from './BAINotificationBackgroundItem';
23
import {
34
CheckCircleOutlined,
45
ClockCircleOutlined,
56
CloseCircleOutlined,
67
} from '@ant-design/icons';
7-
import { Button, Card, List, Progress, Typography, theme } from 'antd';
8+
import { Button, Card, List, Typography, theme } from 'antd';
89
import { BAIFlex } from 'backend.ai-ui';
910
import dayjs from 'dayjs';
1011
import _ from 'lodash';
@@ -117,36 +118,23 @@ const BAIGeneralNotificationItem: React.FC<{
117118
marginTop: token.marginSM,
118119
}}
119120
>
120-
<Typography.Text type="secondary" copyable>
121-
{notification.extraDescription}
122-
</Typography.Text>
121+
{_.isString(notification.extraDescription) ? (
122+
<Typography.Text type="secondary" copyable>
123+
{notification.extraDescription}
124+
</Typography.Text>
125+
) : (
126+
notification.extraDescription
127+
)}
123128
</Card>
124129
) : null}
125130

126131
<BAIFlex direction="row" align="center" justify="end" gap={'sm'}>
127-
{notification.backgroundTask &&
128-
_.isNumber(notification.backgroundTask.percent) ? (
129-
<Progress
130-
size="small"
131-
showInfo={false}
132-
percent={notification.backgroundTask.percent}
133-
strokeColor={
134-
notification.backgroundTask.status === 'rejected'
135-
? token.colorTextDisabled
136-
: undefined
137-
}
138-
style={{
139-
margin: 0,
140-
opacity:
141-
notification.backgroundTask.status === 'resolved' &&
142-
showDate
143-
? 0
144-
: 1,
145-
}}
146-
147-
// status={item.progressStatus}
132+
{notification.backgroundTask && (
133+
<BAINotificationBackgroundItem
134+
backgroundTask={notification.backgroundTask}
135+
showDate={showDate}
148136
/>
149-
) : null}
137+
)}
150138
{showDate ? (
151139
<BAIFlex>
152140
<Typography.Text type="secondary">

react/src/components/BAINodeNotificationItem.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { NotificationState } from '../hooks/useBAINotification';
22
import BAIComputeSessionNodeNotificationItem from './BAIComputeSessionNodeNotificationItem';
3+
import BAIVirtualFolderNodeNotificationItem from './BAIVirtualFolderNodeNotificationItem';
34
import React from 'react';
45
import { graphql, useRefetchableFragment } from 'react-relay';
56
import { BAINodeNotificationItemFragment$key } from 'src/__generated__/BAINodeNotificationItemFragment.graphql';
@@ -18,6 +19,8 @@ const nodeFragmentOperation = graphql`
1819
... on VirtualFolderNode {
1920
__typename
2021
status
22+
...BAIVirtualFolderNodeNotificationItemFragment
23+
@alias(as: "virtualFolderNodeFrgmt")
2124
}
2225
}
2326
`;
@@ -39,6 +42,13 @@ const BAINodeNotificationItem: React.FC<{
3942
/>
4043
);
4144
} else if (node?.__typename === 'VirtualFolderNode') {
45+
return (
46+
<BAIVirtualFolderNodeNotificationItem
47+
notification={notification}
48+
virtualFolderNodeFrgmt={node.virtualFolderNodeFrgmt || null}
49+
showDate={showDate}
50+
/>
51+
);
4252
} else {
4353
// console.warn('Unknown node type in BAINodeNotificationItem:', node);
4454
return null;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Progress, theme } from 'antd';
2+
import _ from 'lodash';
3+
import { NotificationState } from 'src/hooks/useBAINotification';
4+
5+
interface BAINotificationBackgroundItemProps {
6+
backgroundTask: NotificationState['backgroundTask'];
7+
showDate?: boolean;
8+
}
9+
10+
const BAINotificationBackgroundItem: React.FC<
11+
BAINotificationBackgroundItemProps
12+
> = ({ backgroundTask, showDate }) => {
13+
const { token } = theme.useToken();
14+
15+
return _.isNumber(backgroundTask?.percent) ? (
16+
<Progress
17+
size="small"
18+
showInfo={false}
19+
percent={backgroundTask.percent}
20+
strokeColor={
21+
backgroundTask.status === 'rejected'
22+
? token.colorTextDisabled
23+
: undefined
24+
}
25+
style={{
26+
margin: 0,
27+
opacity: backgroundTask.status === 'resolved' && showDate ? 0 : 1,
28+
}}
29+
/>
30+
) : null;
31+
};
32+
33+
export default BAINotificationBackgroundItem;
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import BAINotificationBackgroundItem from './BAINotificationBackgroundItem';
2+
import { useToggle } from 'ahooks';
3+
import { Card, List, theme, Typography } from 'antd';
4+
import { BAIFlex, BAILink, BAINotificationItem, BAIText } from 'backend.ai-ui';
5+
import dayjs from 'dayjs';
6+
import _ from 'lodash';
7+
import { useTranslation } from 'react-i18next';
8+
import { graphql, useFragment } from 'react-relay';
9+
import { useNavigate } from 'react-router-dom';
10+
import {
11+
NotificationState,
12+
useSetBAINotification,
13+
} from 'src/hooks/useBAINotification';
14+
15+
interface BAIVirtualFolderNodeNotificationItemProps {
16+
notification: NotificationState;
17+
virtualFolderNodeFrgmt: any;
18+
showDate?: boolean;
19+
}
20+
21+
const BAIVirtualFolderNodeNotificationItem: React.FC<
22+
BAIVirtualFolderNodeNotificationItemProps
23+
> = ({ notification, virtualFolderNodeFrgmt, showDate }) => {
24+
const navigate = useNavigate();
25+
const { t } = useTranslation();
26+
const { token } = theme.useToken();
27+
const { closeNotification } = useSetBAINotification();
28+
const [showExtraDescription, { toggle: toggleShowExtraDescription }] =
29+
useToggle(false);
30+
31+
const node = useFragment(
32+
graphql`
33+
fragment BAIVirtualFolderNodeNotificationItemFragment on VirtualFolderNode {
34+
row_id
35+
id
36+
name
37+
status
38+
}
39+
`,
40+
virtualFolderNodeFrgmt,
41+
);
42+
43+
// Implementation goes here
44+
return (
45+
node && (
46+
<BAINotificationItem
47+
title={
48+
<BAIText ellipsis>
49+
{t('general.Folder')}:&nbsp;
50+
<BAILink
51+
style={{
52+
fontWeight: 'normal',
53+
}}
54+
title={node.name || ''}
55+
onClick={() => {
56+
navigate(
57+
`/data${node.row_id ? `?${new URLSearchParams({ folder: node.row_id }).toString()}` : ''}`,
58+
);
59+
closeNotification(notification.key);
60+
}}
61+
>
62+
{node.name}
63+
</BAILink>
64+
</BAIText>
65+
}
66+
description={
67+
<List.Item>
68+
<BAIFlex direction="column" align="stretch" gap={'xxs'}>
69+
<BAIFlex
70+
direction="row"
71+
align="end"
72+
gap={'xxs'}
73+
justify="between"
74+
>
75+
{_.isString(notification.description) ? (
76+
<Typography.Paragraph>
77+
{_.truncate(notification.description, { length: 300 })}
78+
</Typography.Paragraph>
79+
) : (
80+
notification.description
81+
)}
82+
83+
{notification.extraDescription && !notification?.onCancel ? (
84+
<BAIFlex>
85+
<Typography.Link
86+
onClick={() => {
87+
toggleShowExtraDescription();
88+
}}
89+
>
90+
{t('notification.SeeDetail')}
91+
</Typography.Link>
92+
</BAIFlex>
93+
) : null}
94+
</BAIFlex>
95+
96+
{notification.extraDescription && showExtraDescription ? (
97+
<Card
98+
size="small"
99+
style={{
100+
maxHeight: '300px',
101+
overflow: 'auto',
102+
overflowX: 'hidden',
103+
marginTop: token.marginSM,
104+
}}
105+
>
106+
{_.isString(notification.extraDescription) ? (
107+
<Typography.Text type="secondary" copyable>
108+
{notification.extraDescription}
109+
</Typography.Text>
110+
) : (
111+
notification.extraDescription
112+
)}
113+
</Card>
114+
) : null}
115+
116+
{notification.backgroundTask && (
117+
<BAINotificationBackgroundItem
118+
backgroundTask={notification.backgroundTask}
119+
showDate={showDate}
120+
/>
121+
)}
122+
</BAIFlex>
123+
</List.Item>
124+
}
125+
footer={
126+
showDate ? dayjs(notification.created).format('lll') : undefined
127+
}
128+
/>
129+
)
130+
);
131+
};
132+
133+
export default BAIVirtualFolderNodeNotificationItem;

react/src/components/VFolderNodes.tsx

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import _ from 'lodash';
4343
import React, { useState } from 'react';
4444
import { useTranslation } from 'react-i18next';
4545
import { graphql, useFragment } from 'react-relay';
46+
import { useNavigate } from 'react-router-dom';
4647

4748
export const statusTagColor = {
4849
// mountable
@@ -84,6 +85,7 @@ const VFolderNodes: React.FC<VFolderNodesProps> = ({
8485
const { upsertNotification } = useSetBAINotification();
8586
const { generateFolderPath } = useFolderExplorerOpener();
8687
const { getErrorMessage } = useErrorMessageResolver();
88+
const navigate = useNavigate();
8789

8890
const [deletingVFolder, setDeletingVFolder] =
8991
useState<VFolderNodeInList | null>(null);
@@ -107,6 +109,7 @@ const VFolderNodes: React.FC<VFolderNodesProps> = ({
107109
...VFolderNodeIdenticonFragment
108110
...SharedFolderPermissionInfoModalFragment
109111
...BAIVFolderDeleteButtonFragment
112+
...BAINodeNotificationItemFragment
110113
}
111114
`,
112115
vfoldersFrgmt,
@@ -274,6 +277,8 @@ const VFolderNodes: React.FC<VFolderNodesProps> = ({
274277
},
275278
onError: (error) => {
276279
upsertNotification({
280+
key: `vfolder-error-${vfolder?.id}`,
281+
node: vfolder,
277282
description: getErrorMessage(error),
278283
open: true,
279284
});
@@ -297,10 +302,46 @@ const VFolderNodes: React.FC<VFolderNodesProps> = ({
297302
}),
298303
);
299304
},
300-
onError: (error) => {
305+
onError: async (error) => {
306+
const matchString = error?.message.match(
307+
/sessions\(ids: (\[.*?\])\)/,
308+
)?.[1];
309+
const occupiedSession = await JSON.parse(
310+
matchString?.replace(/'/g, '"') || '[]',
311+
);
301312
upsertNotification({
302-
description: getErrorMessage(error),
303313
open: true,
314+
key: `vfolder-error-${vfolder?.id}`,
315+
node: vfolder,
316+
description: getErrorMessage(error).replace(
317+
/\(ids[\s\S]*$/,
318+
'',
319+
),
320+
extraDescription: occupiedSession ? (
321+
<BAIFlex direction="column" align="stretch">
322+
<Typography.Text
323+
style={{
324+
color: token.colorTextDescription,
325+
}}
326+
>
327+
{t('data.folders.MountedSessions')}
328+
</Typography.Text>
329+
{_.map(occupiedSession, (sessionId) => (
330+
<BAILink
331+
style={{
332+
fontWeight: 'normal',
333+
}}
334+
onClick={() => {
335+
navigate(
336+
`/session${`?${new URLSearchParams({ sessionDetail: sessionId }).toString()}`}`,
337+
);
338+
}}
339+
>
340+
{sessionId}
341+
</BAILink>
342+
))}
343+
</BAIFlex>
344+
) : null,
304345
});
305346
},
306347
});
@@ -427,6 +468,8 @@ const VFolderNodes: React.FC<VFolderNodesProps> = ({
427468
},
428469
onError: (error) => {
429470
upsertNotification({
471+
key: `vfolder-error-${deletingVFolder?.id}`,
472+
...(deletingVFolder && { node: deletingVFolder }),
430473
description: getErrorMessage(error),
431474
open: true,
432475
});

react/src/hooks/useBAINotification.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export interface NotificationState
6464
renderDataMessage?: (message?: string) => React.ReactNode;
6565
promise?: Promise<unknown> | null;
6666
};
67-
extraDescription?: string | null;
67+
extraDescription?: ReactNode | null;
6868
onCancel?: (() => void) | null;
6969
skipDesktopNotification?: boolean;
7070
extraData: any;
@@ -279,6 +279,8 @@ export const useBAINotificationEffect = () => {
279279
* @returns An object containing functions for manipulating notifications.
280280
*/
281281
export const useSetBAINotification = () => {
282+
'use memo';
283+
282284
// Don't use _notifications carefully when you need to mutate it.
283285
const setNotifications = useSetAtom(notificationListState);
284286
const [desktopNotification] = useBAISettingUserState('desktop_notification');

resources/i18n/de.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,7 @@
402402
"MaxSize": "Maximale Größe",
403403
"ModifyPermissions": "Berechtigungen ändern",
404404
"MountPermission": "Erlaubnis montieren",
405+
"MountedSessions": "Gemountete Sitzungen",
405406
"MoveToTrash": "Ziehen Sie zu Müllbehälter",
406407
"MoveToTrashDescription": "Sind Sie sicher, dass Sie \"{{folderName}}\" in den Müll verschieben möchten?",
407408
"MoveToTrashMultipleDescription": "Sind Sie sicher, dass Sie {{folderLength}} Ordner in Müll bin verschieben möchten?",
@@ -690,6 +691,7 @@
690691
"Enabled": "aktiviert",
691692
"ErrorOccurred": "Etwas lief schief. \nBitte versuchen Sie es später erneut.",
692693
"ExtendLoginSession": "Eine Anmeldesitzung verlängern",
694+
"Folder": "Ordner",
693695
"Folders": "Ordner",
694696
"General": "Allgemein",
695697
"Image": "Bild",

0 commit comments

Comments
 (0)