Skip to content

Commit 058570d

Browse files
ImvedanshLocharla, Sandeep
authored andcommitted
UI: Add comprehensive domain deletion confirmation dialog (Feature Request apache#11497) (apache#12380)
1 parent afca74d commit 058570d

3 files changed

Lines changed: 212 additions & 3 deletions

File tree

ui/public/locales/en.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,6 +1024,7 @@
10241024
"label.endpoint": "Endpoint",
10251025
"label.endport": "End port",
10261026
"label.enter.account.name": "Enter the account name",
1027+
"label.enter.domain.name": "Enter the domain name",
10271028
"label.enter.code": "Enter 2FA code to verify",
10281029
"label.enter.static.pin": "Enter static PIN to verify",
10291030
"label.enter.token": "Enter token",
@@ -3306,6 +3307,9 @@
33063307
"message.delete.account.processing": "Deleting account",
33073308
"message.delete.account.success": "Successfully deleted account",
33083309
"message.delete.account.warning": "Deleting this account will delete all of the instances, volumes and snapshots associated with the account.",
3310+
"message.delete.domain.confirm": "Please confirm that you want to delete this domain by entering the name of the domain below.",
3311+
"message.delete.domain.warning": "All associated accounts, users, VMs, and sub-domains will be permanently deleted. This action cannot be undone.",
3312+
"message.delete.domain.failed": "Delete domain failed",
33093313
"message.delete.acl.processing": "Removing ACL rule...",
33103314
"message.delete.acl.rule": "Remove ACL rule",
33113315
"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: 53 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
@@ -316,6 +330,42 @@ export default {
316330
closeAction () {
317331
this.showAction = false
318332
},
333+
confirmDeleteDomain () {
334+
const domain = this.deleteDomainResource
335+
const params = { id: domain.id, cleanup: true }
336+
337+
api('deleteDomain', params).then(json => {
338+
const jobId = json.deletedomainresponse.jobid
339+
340+
this.$pollJob({
341+
jobId,
342+
title: this.$t('label.action.delete.domain'),
343+
description: domain.name,
344+
loadingMessage: `${this.$t('label.action.delete.domain')} ${domain.name}`,
345+
successMessage: `${this.$t('label.action.delete.domain')} ${domain.name}`,
346+
catchMessage: this.$t('error.fetching.async.job.result'),
347+
successMethod: () => {
348+
this.$router.replace({ path: '/domain' })
349+
this.resource = {}
350+
this.treeSelected = {}
351+
this.treeDeletedKey = null
352+
this.treeViewKey += 1
353+
this.$nextTick(() => {
354+
this.fetchData()
355+
})
356+
}
357+
})
358+
}).catch(error => {
359+
this.$notification.error({
360+
message: this.$t('message.request.failed'),
361+
description: error.response?.headers['x-description'] || this.$t('message.request.failed')
362+
})
363+
}).finally(() => {
364+
this.showDeleteConfirm = false
365+
this.deleteDomainResource = null
366+
this.treeDeletedKey = null
367+
})
368+
},
319369
forceRerender () {
320370
this.treeViewKey += 1
321371
}

0 commit comments

Comments
 (0)