Skip to content

Commit

Permalink
Merge pull request #102 from harvester/mergify/bp/release-harvester-v…
Browse files Browse the repository at this point in the history
…1.5/pr-94

feat: add confirmation pop-up for VM stop and pause actions (backport #94)
  • Loading branch information
houhoucoop authored Jan 21, 2025
2 parents f59c81d + cf6d446 commit 8d338b5
Show file tree
Hide file tree
Showing 3 changed files with 225 additions and 16 deletions.
173 changes: 173 additions & 0 deletions pkg/harvester/dialog/ConfirmExecutionDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
<script>
import { mapState } from 'vuex';
import { exceptionToErrorsArray } from '@shell/utils/error';
import { alternateLabel } from '@shell/utils/platform';
import AsyncButton from '@shell/components/AsyncButton';
import { Banner } from '@components/Banner';
import { Card } from '@components/Card';
import { escapeHtml } from '@shell/utils/string';
/**
* @name ConfirmExecutionDialog
* @description Dialog component to confirm the related resources before executing the action.
*/
export default {
name: 'ConfirmExecutionDialog',
emits: ['close'],
components: {
AsyncButton,
Banner,
Card,
},
props: {
/**
* @property resources to be deleted.
* @type {Resource[]} Array of the resource model's instance
*/
resources: {
type: Array,
required: true
}
},
data() {
return { errors: [] };
},
computed: {
...mapState('action-menu', ['modalData']),
warningMessageKey() {
return this.modalData.warningMessageKey;
},
names() {
return this.resources.map((obj) => obj.nameDisplay).slice(0, 5);
},
resourceNames() {
return this.names.reduce((res, name, i) => {
if (i >= 5) {
return res;
}
res += `<b>${ escapeHtml(name) }</b>`;
if (i === this.names.length - 1) {
res += this.plusMore;
} else {
res += i === this.resources.length - 2 ? ' and ' : ', ';
}
return res;
}, '');
},
plusMore() {
const remaining = this.resources.length - this.names.length;
return this.t('dialog.confirmExecution.andOthers', { count: remaining });
},
type() {
const types = new Set(this.resources.reduce((array, each) => {
array.push(each.type);
return array;
}, []));
if (types.size > 1) {
return this.t('generic.resource', { count: this.resources.length });
}
const schema = this.resources[0]?.schema;
if ( !schema ) {
return `resource${ this.resources.length === 1 ? '' : 's' }`;
}
return this.$store.getters['type-map/labelFor'](schema, this.resources.length);
},
protip() {
return this.t('dialog.confirmExecution.protip', { alternateLabel });
},
},
methods: {
escapeHtml,
close() {
this.errors = [];
this.$emit('close');
},
async apply(buttonDone) {
try {
for (const resource of this.resources) {
await resource.doActionGrowl(this.modalData.action, {});
}
buttonDone(true);
this.close();
} catch (e) {
this.errors = exceptionToErrorsArray(e);
buttonDone(false);
}
}
}
};
</script>
<template>
<Card :show-highlight-border="false">
<template #title>
<h4 class="text-default-text">
{{ t('dialog.confirmExecution.title') }}
</h4>
</template>
<template #body>
<div class="pl-10 pr-10">
<span
v-clean-html="t(warningMessageKey, { type, names: resourceNames }, true)"
></span>
<div class="text-info mt-20">
{{ protip }}
</div>
<Banner
v-for="(error, i) in errors"
:key="i"
/>
</div>
</template>
<template #actions>
<div class="actions">
<button
class="btn role-secondary"
@click="close"
>
{{ t('generic.cancel') }}
</button>
<AsyncButton
mode="apply"
class="btn bg-primary ml-10"
:disabled="applyDisabled"
@click="apply"
/>
</div>
</template>
</Card>
</template>
<style lang='scss' scoped>
.modal-container {
max-width: 400px;
}
.actions {
width: 100%;
text-align: right;
}
</style>
25 changes: 20 additions & 5 deletions pkg/harvester/l10n/en-us.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,21 @@ asyncButton:
success: Restarted
waiting: Restarting&hellip;

dialog:
confirmExecution:
title: Are you sure?
andOthers: |-
{count, plural,
=0 {}
=1 { and <b>one other </b>}
other { and <b>{count} other </b>}
}
protip: "Tip: Hold the {alternateLabel} key while clicking action to bypass this confirmation"
stop:
message: "Are you sure you want to continue stop the {type} {names}?"
pause:
message: "Are you sure you want to continue pause the {type} {names}?"

harvester:
productLabel: 'Harvester'
modal:
Expand Down Expand Up @@ -861,14 +876,14 @@ harvester:
doc: Read the <a href="{url}" target="_blank">documentation</a> before starting the upgrade process. Ensure that you complete procedures that are relevant to your environment and the version you are upgrading to.
tip: Unmet system requirements and incorrectly performed procedures may cause complete upgrade failure and other issues that require manual workarounds.
moreNotes: For more details about the release notes, please visit -

schedule:
label: Virtual Machine Schedules
createTitle: Create Schedule
createButtonText: Create Schedule
scheduleType: Virtual Machine Schedule Type
cron: Cron Schedule
detail:
detail:
namespace: Namespace
sourceVM: Source Virtual Machine
tabs:
Expand All @@ -878,12 +893,12 @@ harvester:
message:
noSetting:
suffix: before creating a backup schedule
retain:
retain:
label: Retain
count: Count
tooltip: Number of up-to-date VM backups to retain. Maximum to 250, minimum to 2.
tooltip: Number of up-to-date VM backups to retain. Maximum to 250, minimum to 2.
maxFailure:
label: Max Failure
label: Max Failure
count: Count
tooltip: Max number of consecutive failed backups that could be tolerated. If reach this threshold, Harvester controller will suspend the schedule job. This value should less than retain count
virtualMachine:
Expand Down
43 changes: 32 additions & 11 deletions pkg/harvester/models/kubevirt.io.virtualmachine.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,13 @@ export default class VirtVm extends HarvesterResource {

return [
{
action: 'stopVM',
enabled: !!this.actions?.stop,
icon: 'icon icon-close',
label: this.t('harvester.action.stop'),
bulkable: true
action: 'stopVM',
altAction: 'altStopVM',
enabled: !!this.actions?.stop,
icon: 'icon icon-close',
label: this.t('harvester.action.stop'),
bulkable: true,
bulkAction: 'stopVM',
},
{
action: 'forceStop',
Expand All @@ -116,10 +118,11 @@ export default class VirtVm extends HarvesterResource {
bulkable: true
},
{
action: 'pauseVM',
enabled: !!this.actions?.pause,
icon: 'icon icon-pause',
label: this.t('harvester.action.pause')
action: 'pauseVM',
altAction: 'altPauseVM',
enabled: !!this.actions?.pause,
icon: 'icon icon-pause',
label: this.t('harvester.action.pause')
},
{
action: 'unpauseVM',
Expand Down Expand Up @@ -402,7 +405,16 @@ export default class VirtVm extends HarvesterResource {
return node?.id;
}

pauseVM() {
pauseVM(resources = this) {
this.$dispatch('promptModal', {
resources,
action: 'pause',
warningMessageKey: 'dialog.confirmExecution.pause.message',
component: 'ConfirmExecutionDialog'
});
}

altPauseVM() {
this.doActionGrowl('pause', {});
}

Expand All @@ -417,7 +429,16 @@ export default class VirtVm extends HarvesterResource {
this.doActionGrowl('unpause', {});
}

stopVM() {
stopVM(resources = this) {
this.$dispatch('promptModal', {
resources,
action: 'stop',
warningMessageKey: 'dialog.confirmExecution.stop.message',
component: 'ConfirmExecutionDialog'
});
}

altStopVM() {
this.doActionGrowl('stop', {});
}

Expand Down

0 comments on commit 8d338b5

Please sign in to comment.