|
2 | 2 | @license |
3 | 3 | Copyright (c) 2015-2026 Lablup Inc. All rights reserved. |
4 | 4 | */ |
| 5 | +import { DeploymentConfigurationSectionDeleteMutation } from '../__generated__/DeploymentConfigurationSectionDeleteMutation.graphql'; |
5 | 6 | import type { |
6 | 7 | DeploymentConfigurationSection_deployment$data, |
7 | 8 | DeploymentConfigurationSection_deployment$key, |
8 | 9 | } from '../__generated__/DeploymentConfigurationSection_deployment.graphql'; |
9 | 10 | import type { DeploymentRevisionDetail_revision$key } from '../__generated__/DeploymentRevisionDetail_revision.graphql'; |
| 11 | +import { useWebUINavigate } from '../hooks'; |
10 | 12 | import DeploymentRevisionDetail from './DeploymentRevisionDetail'; |
11 | 13 | import DeploymentRevisionDetailDrawer from './DeploymentRevisionDetailDrawer'; |
12 | 14 | import DeploymentRevisionHistoryTab from './DeploymentRevisionHistoryTab'; |
13 | 15 | import DeploymentSettingModal from './DeploymentSettingModal'; |
14 | 16 | import DeploymentTagChips from './DeploymentTagChips'; |
15 | 17 | import ErrorBoundaryWithNullFallback from './ErrorBoundaryWithNullFallback'; |
16 | | -import { EditOutlined, LoadingOutlined, PlusOutlined } from '@ant-design/icons'; |
| 18 | +import { |
| 19 | + DeleteFilled, |
| 20 | + EditOutlined, |
| 21 | + LoadingOutlined, |
| 22 | + MoreOutlined, |
| 23 | + PlusOutlined, |
| 24 | +} from '@ant-design/icons'; |
17 | 25 | import { useToggle } from 'ahooks'; |
18 | 26 | import { |
19 | 27 | Alert, |
| 28 | + App, |
20 | 29 | Button, |
21 | 30 | Descriptions, |
| 31 | + Dropdown, |
22 | 32 | Empty, |
23 | 33 | Skeleton, |
| 34 | + Space, |
24 | 35 | Typography, |
25 | 36 | theme, |
26 | 37 | } from 'antd'; |
27 | 38 | import { |
28 | 39 | BAICard, |
| 40 | + BAIConfirmModalWithInput, |
29 | 41 | BAIFetchKeyButton, |
30 | 42 | BAIFlex, |
31 | 43 | BAIId, |
32 | 44 | BAIText, |
33 | 45 | BAIUnmountAfterClose, |
34 | 46 | BooleanTag, |
35 | 47 | filterOutEmpty, |
| 48 | + toLocalId, |
| 49 | + useBAILogger, |
36 | 50 | useInterval, |
37 | 51 | } from 'backend.ai-ui'; |
38 | 52 | import { parseAsStringLiteral, useQueryState } from 'nuqs'; |
39 | 53 | import React, { Suspense, useState } from 'react'; |
40 | 54 | import { useTranslation } from 'react-i18next'; |
41 | | -import { graphql, useFragment } from 'react-relay'; |
| 55 | +import { graphql, useFragment, useMutation } from 'react-relay'; |
| 56 | +import { useLocation } from 'react-router-dom'; |
42 | 57 |
|
43 | 58 | interface DeploymentConfigurationSectionProps { |
44 | 59 | deploymentFrgmt: DeploymentConfigurationSection_deployment$key | null; |
@@ -156,6 +171,10 @@ const DeploymentConfigurationSection: React.FC< |
156 | 171 |
|
157 | 172 | const { t } = useTranslation(); |
158 | 173 | const { token } = theme.useToken(); |
| 174 | + const { message } = App.useApp(); |
| 175 | + const { logger } = useBAILogger(); |
| 176 | + const webuiNavigate = useWebUINavigate(); |
| 177 | + const location = useLocation(); |
159 | 178 |
|
160 | 179 | const deployment = useFragment( |
161 | 180 | graphql` |
@@ -218,6 +237,47 @@ const DeploymentConfigurationSection: React.FC< |
218 | 237 | settingModalOpen, |
219 | 238 | { setLeft: closeSettingModal, setRight: openSettingModal }, |
220 | 239 | ] = useToggle(false); |
| 240 | + const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); |
| 241 | + |
| 242 | + const [commitDeleteMutation, isInFlightDeleteMutation] = |
| 243 | + useMutation<DeploymentConfigurationSectionDeleteMutation>(graphql` |
| 244 | + mutation DeploymentConfigurationSectionDeleteMutation( |
| 245 | + $input: DeleteDeploymentInput! |
| 246 | + ) { |
| 247 | + deleteModelDeployment(input: $input) { |
| 248 | + id |
| 249 | + } |
| 250 | + } |
| 251 | + `); |
| 252 | + |
| 253 | + const deploymentName = deployment?.metadata.name ?? ''; |
| 254 | + const isAdminContext = location.pathname.startsWith('/admin-deployments'); |
| 255 | + const listPath = isAdminContext ? '/admin-deployments' : '/deployments'; |
| 256 | + |
| 257 | + const handleDelete = () => { |
| 258 | + if (!deployment?.id) return; |
| 259 | + commitDeleteMutation({ |
| 260 | + variables: { |
| 261 | + input: { |
| 262 | + id: toLocalId(deployment.id) ?? deployment.id, |
| 263 | + }, |
| 264 | + }, |
| 265 | + onCompleted: (_response, errors) => { |
| 266 | + if (errors && errors.length > 0) { |
| 267 | + logger.error('Failed to delete deployment', errors); |
| 268 | + message.error(t('deployment.FailedToDeleteDeployment')); |
| 269 | + return; |
| 270 | + } |
| 271 | + message.success(t('deployment.DeploymentDeleted')); |
| 272 | + setIsDeleteModalOpen(false); |
| 273 | + webuiNavigate(listPath); |
| 274 | + }, |
| 275 | + onError: (error) => { |
| 276 | + logger.error('Failed to delete deployment', error); |
| 277 | + message.error(t('deployment.FailedToDeleteDeployment')); |
| 278 | + }, |
| 279 | + }); |
| 280 | + }; |
221 | 281 |
|
222 | 282 | const handleShowRevisionDrawer = ( |
223 | 283 | frgmt: DeploymentRevisionDetail_revision$key, |
@@ -249,13 +309,33 @@ const DeploymentConfigurationSection: React.FC< |
249 | 309 | value="" |
250 | 310 | onChange={onRefetch} |
251 | 311 | /> |
252 | | - <Button |
253 | | - icon={<EditOutlined />} |
254 | | - disabled={isDeploymentDestroying} |
255 | | - onClick={openSettingModal} |
256 | | - > |
257 | | - {t('button.Edit')} |
258 | | - </Button> |
| 312 | + <Space.Compact> |
| 313 | + <Button |
| 314 | + icon={<EditOutlined />} |
| 315 | + disabled={isDeploymentDestroying} |
| 316 | + onClick={openSettingModal} |
| 317 | + > |
| 318 | + {t('button.Edit')} |
| 319 | + </Button> |
| 320 | + <Dropdown |
| 321 | + trigger={['click']} |
| 322 | + menu={{ |
| 323 | + items: [ |
| 324 | + { |
| 325 | + key: 'delete', |
| 326 | + label: t('deployment.DeleteDeployment'), |
| 327 | + icon: <DeleteFilled />, |
| 328 | + danger: true, |
| 329 | + disabled: |
| 330 | + isDeploymentDestroying || isInFlightDeleteMutation, |
| 331 | + onClick: () => setIsDeleteModalOpen(true), |
| 332 | + }, |
| 333 | + ], |
| 334 | + }} |
| 335 | + > |
| 336 | + <Button icon={<MoreOutlined />} aria-label={t('button.More')} /> |
| 337 | + </Dropdown> |
| 338 | + </Space.Compact> |
259 | 339 | </BAIFlex> |
260 | 340 | } |
261 | 341 | styles={{ body: { paddingTop: 0 } }} |
@@ -359,6 +439,27 @@ const DeploymentConfigurationSection: React.FC< |
359 | 439 | onClose={() => setDrawerState(null)} |
360 | 440 | /> |
361 | 441 | </BAIUnmountAfterClose> |
| 442 | + <BAIConfirmModalWithInput |
| 443 | + open={isDeleteModalOpen} |
| 444 | + title={t('deployment.DeleteDeployment')} |
| 445 | + content={ |
| 446 | + <BAIFlex direction="column" gap="md" align="stretch"> |
| 447 | + <Alert type="warning" title={t('dialog.warning.CannotBeUndone')} /> |
| 448 | + <BAIFlex> |
| 449 | + <Typography.Text style={{ marginRight: token.marginXXS }}> |
| 450 | + {t('dialog.TypeNameToConfirmDeletion')} |
| 451 | + </Typography.Text> |
| 452 | + (<Typography.Text code>{deploymentName}</Typography.Text>) |
| 453 | + </BAIFlex> |
| 454 | + </BAIFlex> |
| 455 | + } |
| 456 | + confirmText={deploymentName} |
| 457 | + inputProps={{ placeholder: deploymentName }} |
| 458 | + okText={t('button.Delete')} |
| 459 | + okButtonProps={{ loading: isInFlightDeleteMutation }} |
| 460 | + onOk={handleDelete} |
| 461 | + onCancel={() => setIsDeleteModalOpen(false)} |
| 462 | + /> |
362 | 463 | </> |
363 | 464 | ); |
364 | 465 | }; |
|
0 commit comments