Skip to content

Commit c44153a

Browse files
committed
refactor(FR-2479): standardize confirmation UX with BAIConfirmModalWithInput and Popconfirm
1 parent 4892c49 commit c44153a

30 files changed

Lines changed: 341 additions & 295 deletions

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@
386386
"modal": {
387387
"DeleteForeverDesc": "경고: 선택한 항목은 완전히 삭제되며 복원할 수 없습니다.",
388388
"ItemSelectedWithCount": "{{count}}개 선택됨.",
389-
"PleaseTypeToConfirm": "{{ confirmText }}를 입력하십시오."
389+
"PleaseTypeToConfirm": "{{ confirmText }}를 입력해주세요."
390390
},
391391
"validation": {
392392
"LetterNumber-_dot": "문자, 숫자, '-', '_', '.'만 입력할 수 있습니다.",

react/src/components/EndpointList.tsx

Lines changed: 66 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,20 @@ import {
1818
DeleteOutlined,
1919
SettingOutlined,
2020
} from '@ant-design/icons';
21-
import { Typography, theme, App, TablePaginationConfig } from 'antd';
21+
import { Typography, theme, App, TablePaginationConfig, Alert } from 'antd';
2222
import type { ColumnType } from 'antd/lib/table';
2323
import {
2424
filterOutEmpty,
2525
filterOutNullAndUndefined,
2626
BAITable,
2727
BAITableProps,
2828
BAINameActionCell,
29+
BAIConfirmModalWithInput,
30+
BAIFlex,
2931
} from 'backend.ai-ui';
3032
import dayjs from 'dayjs';
3133
import * as _ from 'lodash-es';
32-
import React from 'react';
34+
import React, { useState } from 'react';
3335
import { useTranslation } from 'react-i18next';
3436
import { graphql, useFragment } from 'react-relay';
3537

@@ -73,10 +75,13 @@ const EndpointList: React.FC<EndpointListProps> = ({
7375
}) => {
7476
const { t } = useTranslation();
7577
const { token } = theme.useToken();
76-
const { message, modal } = App.useApp();
78+
const { message } = App.useApp();
7779
const [currentUser] = useCurrentUserInfo();
7880
const baiClient = useSuspendedBackendaiClient();
7981
const webuiNavigate = useWebUINavigate();
82+
const [deletingEndpoint, setDeletingEndpoint] = useState<Endpoint | null>(
83+
null,
84+
);
8085

8186
const endpoints = useFragment(
8287
graphql`
@@ -144,46 +149,7 @@ const EndpointList: React.FC<EndpointListProps> = ({
144149
type: 'danger',
145150
disabled: isEndpointInDestroyingCategory(row),
146151
onClick: () => {
147-
modal.confirm({
148-
title: t('dialog.ask.DoYouWantToDeleteSomething', {
149-
name: row.name,
150-
}),
151-
content: t('dialog.warning.CannotBeUndone'),
152-
okText: t('button.Delete'),
153-
okButtonProps: {
154-
danger: true,
155-
type: 'primary',
156-
},
157-
onOk: () => {
158-
if (row.endpoint_id) {
159-
return new Promise<void>((resolve) => {
160-
terminateModelServiceMutation.mutate(row.endpoint_id!, {
161-
onSuccess: (res) => {
162-
onDeleted?.(row);
163-
if (res.success) {
164-
message.success(
165-
t('modelService.ServiceTerminated', {
166-
name: row?.name,
167-
}),
168-
);
169-
} else {
170-
message.error(
171-
t('modelService.FailedToTerminateService'),
172-
);
173-
}
174-
resolve();
175-
},
176-
onError: () => {
177-
message.error(
178-
t('modelService.FailedToTerminateService'),
179-
);
180-
resolve();
181-
},
182-
});
183-
});
184-
}
185-
},
186-
});
152+
setDeletingEndpoint(row);
187153
},
188154
},
189155
]}
@@ -267,16 +233,63 @@ const EndpointList: React.FC<EndpointListProps> = ({
267233
]);
268234

269235
return (
270-
<BAITable
271-
size="small"
272-
loading={loading}
273-
scroll={{ x: 'max-content' }}
274-
rowKey={'endpoint_id'}
275-
dataSource={filterOutNullAndUndefined(endpoints)}
276-
columns={columns}
277-
pagination={pagination}
278-
{...tableProps}
279-
/>
236+
<>
237+
<BAITable
238+
size="small"
239+
loading={loading}
240+
scroll={{ x: 'max-content' }}
241+
rowKey={'endpoint_id'}
242+
dataSource={filterOutNullAndUndefined(endpoints)}
243+
columns={columns}
244+
pagination={pagination}
245+
{...tableProps}
246+
/>
247+
<BAIConfirmModalWithInput
248+
open={!!deletingEndpoint}
249+
title={t('dialog.ask.DoYouWantToDeleteSomething', {
250+
name: deletingEndpoint?.name,
251+
})}
252+
content={
253+
<BAIFlex direction="column" gap="md" align="stretch">
254+
<Alert type="warning" title={t('dialog.warning.CannotBeUndone')} />
255+
<BAIFlex>
256+
<Typography.Text style={{ marginRight: token.marginXXS }}>
257+
{t('dialog.TypeNameToConfirmDeletion')}
258+
</Typography.Text>
259+
(<Typography.Text code>{deletingEndpoint?.name}</Typography.Text>)
260+
</BAIFlex>
261+
</BAIFlex>
262+
}
263+
confirmText={deletingEndpoint?.name ?? ''}
264+
inputProps={{ placeholder: deletingEndpoint?.name ?? '' }}
265+
okText={t('button.Delete')}
266+
okButtonProps={{ loading: terminateModelServiceMutation.isPending }}
267+
onOk={() => {
268+
if (deletingEndpoint?.endpoint_id) {
269+
terminateModelServiceMutation.mutate(deletingEndpoint.endpoint_id, {
270+
onSuccess: (res) => {
271+
onDeleted?.(deletingEndpoint);
272+
if (res.success) {
273+
message.success(
274+
t('modelService.ServiceTerminated', {
275+
name: deletingEndpoint?.name,
276+
}),
277+
);
278+
} else {
279+
message.error(t('modelService.FailedToTerminateService'));
280+
}
281+
setDeletingEndpoint(null);
282+
},
283+
onError: () => {
284+
message.error(t('modelService.FailedToTerminateService'));
285+
setDeletingEndpoint(null);
286+
},
287+
});
288+
}
289+
}}
290+
onCancel={() => setDeletingEndpoint(null)}
291+
/>
292+
</>
280293
);
281294
};
282295

react/src/components/ManageAppsModal.tsx

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,17 @@ import {
1111
Form,
1212
message,
1313
Typography,
14-
App,
1514
FormInstance,
1615
theme,
1716
} from 'antd';
18-
import { BAIFlex, BAIModal, BAIModalProps } from 'backend.ai-ui';
17+
import {
18+
BAIFlex,
19+
BAIModal,
20+
BAIModalProps,
21+
BAIConfirmModalWithInput,
22+
} from 'backend.ai-ui';
1923
import * as _ from 'lodash-es';
20-
import React from 'react';
24+
import React, { useState } from 'react';
2125
import { Trans, useTranslation } from 'react-i18next';
2226
import { graphql, useFragment, useMutation } from 'react-relay';
2327

@@ -37,7 +41,10 @@ const ManageAppsModal: React.FC<ManageAppsModalProps> = ({
3741
}) => {
3842
const { t } = useTranslation();
3943
const formRef = React.useRef<FormInstance>(null);
40-
const app = App.useApp();
44+
const [isReinstallConfirmOpen, setIsReinstallConfirmOpen] = useState(false);
45+
const [pendingCommitRequest, setPendingCommitRequest] = useState<
46+
(() => void) | null
47+
>(null);
4148

4249
const { token } = theme.useToken();
4350

@@ -157,21 +164,8 @@ const ManageAppsModal: React.FC<ManageAppsModalProps> = ({
157164
});
158165

159166
if (image?.installed) {
160-
app.modal.confirm({
161-
title: 'Image reinstallation required',
162-
content: (
163-
<>
164-
<Trans
165-
i18nKey={
166-
'environment.ModifyImageResourceLimitReinstallRequired'
167-
}
168-
/>
169-
</>
170-
),
171-
onOk: commitRequest,
172-
getContainer: () => document.body,
173-
closable: true,
174-
});
167+
setPendingCommitRequest(() => commitRequest);
168+
setIsReinstallConfirmOpen(true);
175169
} else {
176170
commitRequest();
177171
}
@@ -329,6 +323,25 @@ const ManageAppsModal: React.FC<ManageAppsModalProps> = ({
329323
</Form.List>
330324
</BAIFlex>
331325
</Form>
326+
<BAIConfirmModalWithInput
327+
open={isReinstallConfirmOpen}
328+
title={t('environment.ImageReinstallationRequired')}
329+
content={
330+
<Trans
331+
i18nKey={'environment.ModifyImageResourceLimitReinstallRequired'}
332+
/>
333+
}
334+
confirmText={image?.name ?? image?.namespace ?? ''}
335+
onOk={() => {
336+
setIsReinstallConfirmOpen(false);
337+
pendingCommitRequest?.();
338+
setPendingCommitRequest(null);
339+
}}
340+
onCancel={() => {
341+
setIsReinstallConfirmOpen(false);
342+
setPendingCommitRequest(null);
343+
}}
344+
/>
332345
</BAIModal>
333346
);
334347
};

react/src/components/ManageImageResourceLimitModal.tsx

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,16 @@ import {
88
} from '../__generated__/ManageImageResourceLimitModalMutation.graphql';
99
import { ManageImageResourceLimitModal_image$key } from '../__generated__/ManageImageResourceLimitModal_image.graphql';
1010
import { compareNumberWithUnits } from '../helper';
11-
import {
12-
App,
13-
Form,
14-
type FormInstance,
15-
message,
16-
InputNumber,
17-
Row,
18-
Col,
19-
} from 'antd';
11+
import { Form, type FormInstance, message, InputNumber, Row, Col } from 'antd';
2012
import {
2113
useResourceSlotsDetails,
2214
BAIModal,
2315
BAIModalProps,
2416
BAIDynamicUnitInputNumber,
17+
BAIConfirmModalWithInput,
2518
} from 'backend.ai-ui';
2619
import * as _ from 'lodash-es';
27-
import React, { useRef, Fragment } from 'react';
20+
import React, { useRef, useState, Fragment } from 'react';
2821
import { Trans, useTranslation } from 'react-i18next';
2922
import { graphql, useFragment, useMutation } from 'react-relay';
3023

@@ -46,8 +39,11 @@ const ManageImageResourceLimitModal: React.FC<
4639

4740
const { t } = useTranslation();
4841
const formRef = useRef<FormInstance>(null);
49-
const app = App.useApp();
5042
const { mergedResourceSlots } = useResourceSlotsDetails();
43+
const [isReinstallConfirmOpen, setIsReinstallConfirmOpen] = useState(false);
44+
const [pendingCommitRequest, setPendingCommitRequest] = useState<
45+
(() => void) | null
46+
>(null);
5147

5248
const image = useFragment(
5349
graphql`
@@ -129,17 +125,8 @@ const ManageImageResourceLimitModal: React.FC<
129125
});
130126

131127
if (image?.installed) {
132-
app.modal.confirm({
133-
title: t('environment.ImageReinstallationRequired'),
134-
content: (
135-
<Trans
136-
i18nKey={'environment.ModifyImageResourceLimitReinstallRequired'}
137-
/>
138-
),
139-
onOk: commitRequest,
140-
getContainer: () => document.body,
141-
closable: true,
142-
});
128+
setPendingCommitRequest(() => commitRequest);
129+
setIsReinstallConfirmOpen(true);
143130
} else {
144131
commitRequest();
145132
}
@@ -245,6 +232,25 @@ const ManageImageResourceLimitModal: React.FC<
245232
)}
246233
</Row>
247234
</Form>
235+
<BAIConfirmModalWithInput
236+
open={isReinstallConfirmOpen}
237+
title={t('environment.ImageReinstallationRequired')}
238+
content={
239+
<Trans
240+
i18nKey={'environment.ModifyImageResourceLimitReinstallRequired'}
241+
/>
242+
}
243+
confirmText={image?.name ?? image?.namespace ?? ''}
244+
onOk={() => {
245+
setIsReinstallConfirmOpen(false);
246+
pendingCommitRequest?.();
247+
setPendingCommitRequest(null);
248+
}}
249+
onCancel={() => {
250+
setIsReinstallConfirmOpen(false);
251+
setPendingCommitRequest(null);
252+
}}
253+
/>
248254
</BAIModal>
249255
);
250256
};

0 commit comments

Comments
 (0)