Skip to content

Commit 6e9eb22

Browse files
committed
ui: Add comprehensive domain deletion confirmation dialog
Implements a confirmation modal for domain deletion that shows detailed impact before proceeding, making it consistent with account deletion
1 parent a208db5 commit 6e9eb22

File tree

3 files changed

+202
-3
lines changed

3 files changed

+202
-3
lines changed

ui/public/locales/en.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,7 @@
857857
"label.endpoint": "Endpoint",
858858
"label.endport": "End port",
859859
"label.enter.account.name": "Enter the account name",
860+
"label.enter.domain.name": "Enter the domain name",
860861
"label.enter.code": "Enter 2FA code to verify",
861862
"label.enter.static.pin": "Enter static PIN to verify",
862863
"label.enter.token": "Enter token",
@@ -2728,6 +2729,9 @@
27282729
"message.delete.account.processing": "Deleting account",
27292730
"message.delete.account.success": "Successfully deleted account",
27302731
"message.delete.account.warning": "Deleting this account will delete all of the instances, volumes and snapshots associated with the account.",
2732+
"message.delete.domain.confirm": "Please confirm that you want to delete this domain by entering the name of the domain below.",
2733+
"message.delete.domain.warning": "Deleting this domain will permanently delete all associated accounts. All active and inactive virtual machines will be powered off and removed. This action cannot be undone.",
2734+
"message.delete.domain.failed": "Delete domain failed",
27312735
"message.delete.acl.processing": "Removing ACL rule...",
27322736
"message.delete.acl.rule": "Remove ACL rule",
27332737
"message.delete.acl.rule.failed": "Failed to remove ACL rule.",
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
<template>
18+
<a-modal
19+
:visible="true"
20+
:title="$t('label.action.delete.domain') + ': ' + domain.name"
21+
:okText="$t('label.delete.domain')"
22+
okType="danger"
23+
:confirmLoading="loading"
24+
:ok-button-props="{ disabled: !canDelete }"
25+
@cancel="emitClose"
26+
@ok="emitConfirm">
27+
28+
<a-alert
29+
type="warning"
30+
show-icon
31+
style="margin-bottom: 16px">
32+
<template #message>
33+
<div v-html="$t('message.delete.domain.warning')"></div>
34+
</template>
35+
</a-alert>
36+
37+
<a-spin v-if="loading" />
38+
39+
<a-table
40+
v-else
41+
size="small"
42+
:columns="columns"
43+
:dataSource="accountVmSummary"
44+
:pagination="false"
45+
rowKey="account" />
46+
47+
<div style="margin-top: 16px">
48+
<a-alert style="margin-bottom: 10px">
49+
<template #message>
50+
<div v-html="$t('message.delete.domain.confirm')"></div>
51+
</template>
52+
</a-alert>
53+
<a-input
54+
v-model:value="confirmText"
55+
:placeholder="$t('label.enter.domain.name')" />
56+
</div>
57+
58+
</a-modal>
59+
</template>
60+
61+
<script>
62+
import { api } from '@/api'
63+
64+
export default {
65+
name: 'DomainDeleteConfirm',
66+
props: {
67+
domain: {
68+
type: Object,
69+
required: true
70+
}
71+
},
72+
data () {
73+
return {
74+
loading: false,
75+
confirmText: '',
76+
accountVmSummary: []
77+
}
78+
},
79+
computed: {
80+
canDelete () {
81+
return this.confirmText.trim() === this.domain.name.trim()
82+
},
83+
columns () {
84+
return [
85+
{ title: this.$t('label.account'), dataIndex: 'account' },
86+
{ title: this.$t('label.total') + ' VMs', dataIndex: 'total' },
87+
{ title: this.$t('label.running') + ' VMs', dataIndex: 'running' },
88+
{ title: this.$t('label.stopped') + ' VMs', dataIndex: 'stopped' }
89+
]
90+
}
91+
},
92+
mounted () {
93+
this.fetchDomainImpact()
94+
},
95+
methods: {
96+
emitClose () {
97+
this.$emit('close')
98+
},
99+
emitConfirm () {
100+
if (this.canDelete) {
101+
this.$emit('confirm')
102+
}
103+
},
104+
async fetchDomainImpact () {
105+
this.loading = true
106+
try {
107+
const accResp = await api('listAccounts', {
108+
domainid: this.domain.id,
109+
listall: true
110+
})
111+
112+
const accounts =
113+
accResp.listaccountsresponse &&
114+
accResp.listaccountsresponse.account
115+
? accResp.listaccountsresponse.account
116+
: []
117+
118+
const vmResp = await api('listVirtualMachines', {
119+
domainid: this.domain.id,
120+
listall: true
121+
})
122+
123+
const vms =
124+
vmResp.listvirtualmachinesresponse &&
125+
vmResp.listvirtualmachinesresponse.virtualmachine
126+
? vmResp.listvirtualmachinesresponse.virtualmachine
127+
: []
128+
129+
this.accountVmSummary = accounts.map(account => {
130+
const accountVms = vms.filter(vm => vm.account === account.name)
131+
const running = accountVms.filter(vm => vm.state === 'Running').length
132+
const stopped = accountVms.length - running
133+
134+
return {
135+
account: account.name,
136+
total: accountVms.length,
137+
running,
138+
stopped
139+
}
140+
})
141+
} catch (e) {
142+
this.$notification.error({
143+
message: this.$t('message.request.failed'),
144+
description: e.response?.headers['x-description'] || this.$t('message.request.failed')
145+
})
146+
} finally {
147+
this.loading = false
148+
}
149+
}
150+
}
151+
}
152+
</script>
153+
154+
<style scoped>
155+
</style>

ui/src/views/iam/DomainView.vue

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@
7474
:resource="resource"
7575
:action="action"/>
7676
</div>
77+
<domain-delete-confirm
78+
v-if="showDeleteConfirm"
79+
:domain="deleteDomainResource"
80+
@close="showDeleteConfirm = false"
81+
@confirm="confirmDeleteDomain" />
7782
</div>
7883
</template>
7984

@@ -87,6 +92,7 @@ import ActionButton from '@/components/view/ActionButton'
8792
import TreeView from '@/components/view/TreeView'
8893
import DomainActionForm from '@/views/iam/DomainActionForm'
8994
import ResourceView from '@/components/view/ResourceView'
95+
import DomainDeleteConfirm from '@/components/view/DomainDeleteConfirm'
9096
import eventBus from '@/config/eventBus'
9197
9298
export default {
@@ -96,7 +102,8 @@ export default {
96102
ActionButton,
97103
TreeView,
98104
DomainActionForm,
99-
ResourceView
105+
ResourceView,
106+
DomainDeleteConfirm
100107
},
101108
mixins: [mixinDevice],
102109
data () {
@@ -111,7 +118,9 @@ export default {
111118
action: {},
112119
dataView: false,
113120
domainStore: {},
114-
treeDeletedKey: null
121+
treeDeletedKey: null,
122+
showDeleteConfirm: false,
123+
deleteDomainResource: null
115124
}
116125
},
117126
computed: {
@@ -205,7 +214,12 @@ export default {
205214
})
206215
},
207216
execAction (action) {
208-
this.treeDeletedKey = action.api === 'deleteDomain' ? this.resource.key : null
217+
if (action.api === 'deleteDomain') {
218+
this.deleteDomainResource = this.resource
219+
this.showDeleteConfirm = true
220+
return
221+
}
222+
this.treeDeletedKey = null
209223
this.actionData = []
210224
this.action = action
211225
this.action.params = store.getters.apis[this.action.api].params
@@ -319,6 +333,32 @@ export default {
319333
closeAction () {
320334
this.showAction = false
321335
},
336+
confirmDeleteDomain () {
337+
const params = { id: this.deleteDomainResource.id }
338+
339+
api('deleteDomain', params)
340+
.then(() => {
341+
this.$notification.success({
342+
message: 'Domain Deleted',
343+
description: `Domain ${this.deleteDomainResource.name} has been successfully deleted.`
344+
})
345+
if (this.$route.params.id === this.deleteDomainResource.id) {
346+
this.$router.push({ path: '/domain' })
347+
}
348+
this.fetchData()
349+
})
350+
.catch(error => {
351+
this.$notification.error({
352+
message: 'Failed to delete domain',
353+
description: error.response?.headers['x-description'] || this.$t('message.request.failed')
354+
})
355+
})
356+
.finally(() => {
357+
this.showDeleteConfirm = false
358+
this.deleteDomainResource = null
359+
this.treeDeletedKey = this.deleteDomainResource?.id || null
360+
})
361+
},
322362
forceRerender () {
323363
this.treeViewKey += 1
324364
}

0 commit comments

Comments
 (0)