Skip to content

Commit 4fe4032

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

5 files changed

Lines changed: 201 additions & 51 deletions

File tree

packages/backend.ai-ui/src/components/BAIDeleteConfirmModal.stories.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,15 @@ const meta: Meta<typeof BAIDeleteConfirmModal> = {
2020
**BAIDeleteConfirmModal** is a unified delete confirmation modal for table row deletion.
2121
2222
## Behavior
23-
- **Single item**: Simple confirm dialog with item name displayed. OK button is immediately enabled.
24-
- **Multiple items (2+)**: Requires typing confirmation text (localized "Delete") before OK is enabled.
25-
- **\`requireConfirmInput\`**: Forces text-input confirmation even for a single item.
23+
- **Single item**: Simple confirm dialog. OK button is immediately enabled.
24+
- **Single item + \`requireConfirmInput\`**: Requires typing the item name. Item list is hidden — the name already appears in the description.
25+
- **Multiple items (2+)**: Shows scrollable item list followed by a confirmation input requiring "Delete" to be typed.
2626
2727
## Key Features
2828
- Accepts \`React.ReactNode\` for item labels (icons, tags, custom rendering)
29-
- Scrollable item list for large selections
29+
- Scrollable item list for multi-item selections
3030
- \`extraContent\` slot for domain-specific additions (checkboxes, warnings)
31-
- Built on \`BAIConfirmModalWithInput\` (multi) and \`BAIModal\` (single)
31+
- Built on \`BAIModal\`
3232
`,
3333
},
3434
},
@@ -74,7 +74,7 @@ export const SingleItemWithInput: Story = {
7474
docs: {
7575
description: {
7676
story:
77-
'Single item with `requireConfirmInput={true}`. User must type the item name to confirm.',
77+
'Single item with `requireConfirmInput={true}`. Item list is hidden (name already appears in description). User must type the item name into the confirmation input to enable the Delete button.',
7878
},
7979
},
8080
},
@@ -106,7 +106,7 @@ export const MultipleItems: Story = {
106106
docs: {
107107
description: {
108108
story:
109-
'Multiple items require typing "Delete" to confirm. Shows scrollable item list.',
109+
'Multiple items require typing "Delete" to confirm. Shows scrollable item list above the confirmation input.',
110110
},
111111
},
112112
},

packages/backend.ai-ui/src/components/BAIDeleteConfirmModal.tsx

Lines changed: 66 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import BAIConfirmModalWithInput from './BAIConfirmModalWithInput';
21
import BAIFlex from './BAIFlex';
32
import BAIModal, { type BAIModalProps } from './BAIModal';
43
import BAIText from './BAIText';
54
import { ExclamationCircleFilled } from '@ant-design/icons';
6-
import { theme, Typography, type InputProps } from 'antd';
7-
import React from 'react';
5+
import { Form, Input, theme, Typography, type InputProps } from 'antd';
6+
import React, { useState } from 'react';
87
import { Trans, useTranslation } from 'react-i18next';
98

109
const { Text } = Typography;
@@ -39,7 +38,7 @@ export interface BAIDeleteConfirmModalProps extends Omit<
3938
inputLabel?: React.ReactNode;
4039
/** Additional props for the confirmation Input. */
4140
inputProps?: InputProps;
42-
/** Content rendered between the item list and the input field (e.g. checkboxes). */
41+
/** Content rendered between the input field and "cannot be undone" text (e.g. checkboxes). */
4342
extraContent?: React.ReactNode;
4443
/** Max height (px) of the scrollable item list. Default: 200. Set 0 for no limit. */
4544
itemListMaxHeight?: number;
@@ -71,6 +70,7 @@ const BAIDeleteConfirmModal: React.FC<BAIDeleteConfirmModalProps> = ({
7170

7271
const { t } = useTranslation();
7372
const { token } = theme.useToken();
73+
const [typedText, setTypedText] = useState('');
7474

7575
const needsInput = items.length > 1 || requireConfirmInput;
7676

@@ -101,6 +101,17 @@ const BAIDeleteConfirmModal: React.FC<BAIDeleteConfirmModalProps> = ({
101101
/>
102102
);
103103

104+
const modalTitle = (
105+
<BAIFlex direction="column" justify="start" align="start">
106+
<Text strong>
107+
<ExclamationCircleFilled
108+
style={{ color: token.colorWarning, marginRight: 5 }}
109+
/>
110+
{resolvedTitle}
111+
</Text>
112+
</BAIFlex>
113+
);
114+
104115
const itemListContent =
105116
items.length > 0 ? (
106117
<div
@@ -125,49 +136,60 @@ const BAIDeleteConfirmModal: React.FC<BAIDeleteConfirmModalProps> = ({
125136
</div>
126137
) : null;
127138

128-
const bodyContent = (
129-
<BAIFlex direction="column" align="stretch" gap="xs">
130-
<Text>{resolvedDescription}</Text>
131-
{itemListContent}
132-
<Text type="danger">
133-
{t('comp:BAIDeleteConfirmModal.CannotBeUndone')}
134-
</Text>
135-
{extraContent}
136-
</BAIFlex>
137-
);
138-
139139
if (needsInput) {
140140
return (
141-
<BAIConfirmModalWithInput
142-
{...restModalProps}
141+
<BAIModal
143142
destroyOnHidden
144-
title={resolvedTitle}
145-
confirmText={resolvedConfirmText}
146-
content={bodyContent}
147-
inputLabel={resolvedInputLabel}
148-
inputProps={inputProps}
143+
{...restModalProps}
144+
title={modalTitle}
149145
okText={resolvedOkText}
150-
okButtonProps={okButtonProps}
151-
onOk={onOk}
152-
onCancel={onCancel}
153-
/>
146+
okButtonProps={{
147+
danger: true,
148+
disabled: typedText !== resolvedConfirmText,
149+
...okButtonProps,
150+
}}
151+
onOk={(e) => {
152+
setTypedText('');
153+
onOk?.(e);
154+
}}
155+
onCancel={(e) => {
156+
setTypedText('');
157+
onCancel?.(e);
158+
}}
159+
>
160+
<BAIFlex direction="column" align="stretch" gap="xs">
161+
<Text>{resolvedDescription}</Text>
162+
{items.length > 1 && itemListContent}
163+
<Form layout="vertical" preserve={false}>
164+
<Form.Item
165+
label={resolvedInputLabel}
166+
style={{ marginBottom: 0 }}
167+
required
168+
>
169+
<Input
170+
autoFocus
171+
autoComplete="off"
172+
allowClear
173+
value={typedText}
174+
onChange={(e) => setTypedText(e.target.value)}
175+
{...inputProps}
176+
/>
177+
</Form.Item>
178+
</Form>
179+
<Text type="danger">
180+
{t('comp:BAIDeleteConfirmModal.CannotBeUndone')}
181+
</Text>
182+
{extraContent}
183+
</BAIFlex>
184+
</BAIModal>
154185
);
155186
}
156187

157188
return (
158189
<BAIModal
159190
{...restModalProps}
160191
destroyOnHidden
161-
title={
162-
<BAIFlex direction="column" justify="start" align="start">
163-
<Text strong>
164-
<ExclamationCircleFilled
165-
style={{ color: token.colorWarning, marginRight: 5 }}
166-
/>
167-
{resolvedTitle}
168-
</Text>
169-
</BAIFlex>
170-
}
192+
title={modalTitle}
171193
okText={resolvedOkText}
172194
okButtonProps={{
173195
danger: true,
@@ -177,7 +199,14 @@ const BAIDeleteConfirmModal: React.FC<BAIDeleteConfirmModalProps> = ({
177199
onOk={onOk}
178200
onCancel={onCancel}
179201
>
180-
{bodyContent}
202+
<BAIFlex direction="column" align="stretch" gap="xs">
203+
<Text>{resolvedDescription}</Text>
204+
{itemListContent}
205+
<Text type="danger">
206+
{t('comp:BAIDeleteConfirmModal.CannotBeUndone')}
207+
</Text>
208+
{extraContent}
209+
</BAIFlex>
181210
</BAIModal>
182211
);
183212
};

packages/backend.ai-ui/src/locale/ko.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@
9696
"CannotBeUndone": "이 작업은 되돌릴 수 없습니다.",
9797
"DeleteItem": "삭제",
9898
"DeleteNItems": "{{count}}개 항목 삭제",
99-
"TypeToConfirm": "<code>{{confirmText}}</code>을(를) 입력하여 확인하세요."
99+
"TypeToConfirm": "삭제를 위해 <code>{{confirmText}}</code>을(를) 입력하세요."
100100
},
101101
"comp:BAIDeploymentSelect": {
102102
"SelectDeployment": "배포를 선택해주세요"

0 commit comments

Comments
 (0)