22 @license
33 Copyright (c) 2015-2026 Lablup Inc. All rights reserved.
44 */
5+ import { DeploymentConfigurationSectionDeleteMutation } from '../__generated__/DeploymentConfigurationSectionDeleteMutation.graphql' ;
56import { DeploymentConfigurationSectionQuery } from '../__generated__/DeploymentConfigurationSectionQuery.graphql' ;
67import type { DeploymentRevisionDetail_revision$key } from '../__generated__/DeploymentRevisionDetail_revision.graphql' ;
8+ import { useWebUINavigate } from '../hooks' ;
79import DeploymentRevisionDetail from './DeploymentRevisionDetail' ;
810import DeploymentRevisionDetailDrawer from './DeploymentRevisionDetailDrawer' ;
911import DeploymentRevisionHistoryTab from './DeploymentRevisionHistoryTab' ;
1012import DeploymentSettingModal from './DeploymentSettingModal' ;
1113import DeploymentTagChips from './DeploymentTagChips' ;
1214import ErrorBoundaryWithNullFallback from './ErrorBoundaryWithNullFallback' ;
13- import { EditOutlined , LoadingOutlined , PlusOutlined } from '@ant-design/icons' ;
15+ import {
16+ DeleteFilled ,
17+ EditOutlined ,
18+ LoadingOutlined ,
19+ MoreOutlined ,
20+ PlusOutlined ,
21+ } from '@ant-design/icons' ;
1422import { useToggle } from 'ahooks' ;
1523import {
1624 Alert ,
25+ App ,
1726 Button ,
1827 Descriptions ,
28+ Dropdown ,
1929 Empty ,
2030 Skeleton ,
2131 Typography ,
2232 theme ,
2333} from 'antd' ;
2434import {
2535 BAICard ,
36+ BAIConfirmModalWithInput ,
2637 BAIFetchKeyButton ,
2738 BAIFlex ,
2839 BAIId ,
@@ -31,12 +42,15 @@ import {
3142 BooleanTag ,
3243 INITIAL_FETCH_KEY ,
3344 filterOutEmpty ,
45+ toLocalId ,
46+ useBAILogger ,
3447 useInterval ,
3548} from 'backend.ai-ui' ;
3649import { parseAsStringLiteral , useQueryState } from 'nuqs' ;
3750import React , { Suspense , useState } from 'react' ;
3851import { useTranslation } from 'react-i18next' ;
39- import { graphql , useLazyLoadQuery } from 'react-relay' ;
52+ import { graphql , useLazyLoadQuery , useMutation } from 'react-relay' ;
53+ import { useLocation } from 'react-router-dom' ;
4054
4155interface DeploymentConfigurationSectionProps {
4256 deploymentId : string ;
@@ -262,6 +276,10 @@ const DeploymentConfigurationCards: React.FC<{
262276 'use memo' ;
263277 const { t } = useTranslation ( ) ;
264278 const { token } = theme . useToken ( ) ;
279+ const { message } = App . useApp ( ) ;
280+ const { logger } = useBAILogger ( ) ;
281+ const webuiNavigate = useWebUINavigate ( ) ;
282+ const location = useLocation ( ) ;
265283
266284 const [ activeRevisionTab , setActiveRevisionTab ] = useQueryState (
267285 'revisionTab' ,
@@ -278,6 +296,18 @@ const DeploymentConfigurationCards: React.FC<{
278296 settingModalOpen ,
279297 { setLeft : closeSettingModal , setRight : openSettingModal } ,
280298 ] = useToggle ( false ) ;
299+ const [ isDeleteModalOpen , setIsDeleteModalOpen ] = useState ( false ) ;
300+
301+ const [ commitDeleteMutation , isInFlightDeleteMutation ] =
302+ useMutation < DeploymentConfigurationSectionDeleteMutation > ( graphql `
303+ mutation DeploymentConfigurationSectionDeleteMutation(
304+ $input: DeleteDeploymentInput!
305+ ) {
306+ deleteModelDeployment(input: $input) {
307+ id
308+ }
309+ }
310+ ` ) ;
281311
282312 const { deployment } = useLazyLoadQuery < DeploymentConfigurationSectionQuery > (
283313 graphql `
@@ -337,6 +367,35 @@ const DeploymentConfigurationCards: React.FC<{
337367 // the deploying revision matches the current revision.
338368 useInterval ( onRefetch , isDeployingDifferentRevision ? 5000 : null ) ;
339369
370+ const deploymentName = deployment ?. metadata . name ?? '' ;
371+ const isAdminContext = location . pathname . startsWith ( '/admin-deployments' ) ;
372+ const listPath = isAdminContext ? '/admin-deployments' : '/deployments' ;
373+
374+ const handleDelete = ( ) => {
375+ if ( ! deployment ?. id ) return ;
376+ commitDeleteMutation ( {
377+ variables : {
378+ input : {
379+ id : toLocalId ( deployment . id ) ?? deployment . id ,
380+ } ,
381+ } ,
382+ onCompleted : ( _response , errors ) => {
383+ if ( errors && errors . length > 0 ) {
384+ logger . error ( 'Failed to delete deployment' , errors ) ;
385+ message . error ( t ( 'deployment.FailedToDeleteDeployment' ) ) ;
386+ return ;
387+ }
388+ message . success ( t ( 'deployment.DeploymentDeleted' ) ) ;
389+ setIsDeleteModalOpen ( false ) ;
390+ webuiNavigate ( listPath ) ;
391+ } ,
392+ onError : ( error ) => {
393+ logger . error ( 'Failed to delete deployment' , error ) ;
394+ message . error ( t ( 'deployment.FailedToDeleteDeployment' ) ) ;
395+ } ,
396+ } ) ;
397+ } ;
398+
340399 return (
341400 < >
342401 < BAICard
@@ -351,6 +410,24 @@ const DeploymentConfigurationCards: React.FC<{
351410 >
352411 { t ( 'button.Edit' ) }
353412 </ Button >
413+ < Dropdown
414+ trigger = { [ 'click' ] }
415+ menu = { {
416+ items : [
417+ {
418+ key : 'delete' ,
419+ label : t ( 'deployment.DeleteDeployment' ) ,
420+ icon : < DeleteFilled /> ,
421+ danger : true ,
422+ disabled :
423+ isDeploymentDestroying || isInFlightDeleteMutation ,
424+ onClick : ( ) => setIsDeleteModalOpen ( true ) ,
425+ } ,
426+ ] ,
427+ } }
428+ >
429+ < Button icon = { < MoreOutlined /> } aria-label = { t ( 'button.More' ) } />
430+ </ Dropdown >
354431 </ BAIFlex >
355432 }
356433 styles = { { body : { paddingTop : 0 } } }
@@ -445,6 +522,27 @@ const DeploymentConfigurationCards: React.FC<{
445522 if ( success ) onRefetch ( ) ;
446523 } }
447524 />
525+ < BAIConfirmModalWithInput
526+ open = { isDeleteModalOpen }
527+ title = { t ( 'deployment.DeleteDeployment' ) }
528+ content = {
529+ < BAIFlex direction = "column" gap = "md" align = "stretch" >
530+ < Alert type = "warning" title = { t ( 'dialog.warning.CannotBeUndone' ) } />
531+ < BAIFlex >
532+ < Typography . Text style = { { marginRight : token . marginXXS } } >
533+ { t ( 'dialog.TypeNameToConfirmDeletion' ) }
534+ </ Typography . Text >
535+ (< Typography . Text code > { deploymentName } </ Typography . Text > )
536+ </ BAIFlex >
537+ </ BAIFlex >
538+ }
539+ confirmText = { deploymentName }
540+ inputProps = { { placeholder : deploymentName } }
541+ okText = { t ( 'button.Delete' ) }
542+ okButtonProps = { { loading : isInFlightDeleteMutation } }
543+ onOk = { handleDelete }
544+ onCancel = { ( ) => setIsDeleteModalOpen ( false ) }
545+ />
448546 </ >
449547 ) ;
450548} ;
0 commit comments