Show time correctly in the backup schedule UI (#12012)
diff --git a/ui/src/views/compute/backup/BackupSchedule.vue b/ui/src/views/compute/backup/BackupSchedule.vue
index 627bcd8..b3e894e 100644
--- a/ui/src/views/compute/backup/BackupSchedule.vue
+++ b/ui/src/views/compute/backup/BackupSchedule.vue
@@ -99,7 +99,7 @@
default: false
},
dataSource: {
- type: Object,
+ type: Array,
required: true
},
deleteFn: {
@@ -128,6 +128,7 @@
dataIndex: 'intervaltype'
},
{
+ key: 'time',
title: this.$t('label.time'),
dataIndex: 'schedule'
},
diff --git a/ui/src/views/compute/backup/FormSchedule.vue b/ui/src/views/compute/backup/FormSchedule.vue
index 1833449..7d951de 100644
--- a/ui/src/views/compute/backup/FormSchedule.vue
+++ b/ui/src/views/compute/backup/FormSchedule.vue
@@ -35,16 +35,16 @@
v-model:value="form.intervaltype"
button-style="solid"
@change="handleChangeIntervalType">
- <a-radio-button value="hourly">
+ <a-radio-button value="hourly" :disabled="isIntervalDisabled('hourly')">
{{ $t('label.hourly') }}
</a-radio-button>
- <a-radio-button value="daily">
+ <a-radio-button value="daily" :disabled="isIntervalDisabled('daily')">
{{ $t('label.daily') }}
</a-radio-button>
- <a-radio-button value="weekly">
+ <a-radio-button value="weekly" :disabled="isIntervalDisabled('weekly')">
{{ $t('label.weekly') }}
</a-radio-button>
- <a-radio-button value="monthly">
+ <a-radio-button value="monthly" :disabled="isIntervalDisabled('monthly')">
{{ $t('label.monthly') }}
</a-radio-button>
</a-radio-group>
@@ -54,6 +54,7 @@
<a-form-item :label="$t('label.time')" ref="time" name="time">
<a-input-number
style="width: 100%"
+ :disabled="isIntervalDisabled(form.intervaltype)"
v-model:value="form.time"
:placeholder="$t('label.minute.past.hour')"
:min="1"
@@ -70,6 +71,7 @@
<a-time-picker
use12Hours
format="h:mm A"
+ :disabled="isIntervalDisabled(form.intervaltype)"
v-model:value="form.timeSelect"
style="width: 100%;" />
</a-form-item>
@@ -79,6 +81,7 @@
<a-select
v-model:value="form['day-of-week']"
showSearch
+ :disabled="isIntervalDisabled(form.intervaltype)"
optionFilterProp="label"
:filterOption="(input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
@@ -94,6 +97,7 @@
<a-select
v-model:value="form['day-of-month']"
showSearch
+ :disabled="isIntervalDisabled(form.intervaltype)"
optionFilterProp="label"
:filterOption="(input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
@@ -180,6 +184,10 @@
type: Boolean,
default: false
},
+ dataSource: {
+ type: Array,
+ required: true
+ },
resource: {
type: Object,
required: true
@@ -210,6 +218,38 @@
this.fetchTimeZone()
this.fetchBackupOffering()
},
+ mounted () {
+ if (this.form.intervaltype && this.isIntervalDisabled(this.form.intervaltype)) {
+ const nextAvailable = this.getNextAvailableIntervalType(this.form.intervaltype)
+ if (nextAvailable) {
+ this.form.intervaltype = nextAvailable
+ this.handleChangeIntervalType()
+ }
+ }
+ },
+ watch: {
+ dataSource: {
+ handler () {
+ if (this.form.intervaltype && this.getNextAvailableIntervalType && this.isIntervalDisabled(this.form.intervaltype)) {
+ const nextAvailable = this.getNextAvailableIntervalType(this.form.intervaltype)
+ if (nextAvailable) {
+ this.form.intervaltype = nextAvailable
+ this.handleChangeIntervalType()
+ }
+ }
+ },
+ deep: true
+ },
+ 'form.intervaltype' (newVal) {
+ if (newVal && this.getNextAvailableIntervalType && this.isIntervalDisabled(newVal)) {
+ const nextAvailable = this.getNextAvailableIntervalType(newVal)
+ if (nextAvailable) {
+ this.form.intervaltype = nextAvailable
+ this.handleChangeIntervalType()
+ }
+ }
+ }
+ },
inject: ['refreshSchedule', 'closeSchedule'],
computed: {
isQuiesceVmSupported () {
@@ -274,19 +314,39 @@
})
}
},
- handleChangeIntervalType (e) {
- switch (this.form.intervaltype) {
- case 'weekly':
- this.fetchDayOfWeek()
- break
- case 'monthly':
- this.intervalValue = 'MONTHLY'
- this.fetchDayOfMonth()
- break
- default:
- break
+ handleChangeIntervalType () {
+ if (this.form.intervaltype === 'weekly') {
+ this.fetchDayOfWeek()
+ } else if (this.form.intervaltype === 'monthly') {
+ this.fetchDayOfMonth()
}
},
+ getNextAvailableIntervalType (currentIntervalType) {
+ const intervalTypes = ['hourly', 'daily', 'weekly', 'monthly']
+ const currentIndex = intervalTypes.indexOf(currentIntervalType ? currentIntervalType.toLowerCase() : '')
+ const startIndex = currentIndex >= 0 ? currentIndex : -1
+
+ for (let i = 1; i <= intervalTypes.length; i++) {
+ const nextIndex = (startIndex + i) % intervalTypes.length
+ const nextIntervalType = intervalTypes[nextIndex]
+
+ if (!this.isIntervalDisabled(nextIntervalType)) {
+ return nextIntervalType
+ }
+ }
+ return null
+ },
+ isIntervalDisabled (intervalType) {
+ intervalType = intervalType.toUpperCase()
+ if (this.dataSource?.length === 0) {
+ return false
+ }
+ const dataSource = this.dataSource.filter(item => item.intervaltype === intervalType)
+ if (dataSource && dataSource.length > 0) {
+ return true
+ }
+ return false
+ },
handleSubmit (e) {
if (this.actionLoading) return
this.formRef.value.validate().then(() => {
@@ -294,7 +354,7 @@
const values = this.handleRemoveFields(formRaw)
const params = {}
params.virtualmachineid = this.resource.id
- params.intervaltype = values.intervaltype
+ params.intervaltype = values.intervaltype.toUpperCase()
params.maxbackups = values.maxbackups
params.timezone = values.timezone
if (values.quiescevm) {
diff --git a/ui/src/views/compute/wizard/DeployInstanceBackupSelection.vue b/ui/src/views/compute/wizard/DeployInstanceBackupSelection.vue
index 6f6c0b5..d35ed00 100644
--- a/ui/src/views/compute/wizard/DeployInstanceBackupSelection.vue
+++ b/ui/src/views/compute/wizard/DeployInstanceBackupSelection.vue
@@ -55,6 +55,7 @@
@cancel="closeModals">
<form-schedule
:resource="addFormResource"
+ :dataSource="dataSource"
:submitFn="handleAddBackupSchedule" />
</a-modal>
</div>
@@ -90,7 +91,8 @@
return {
backupOffering: null,
showAddBackupSchedule: false,
- localBackupOfferingId: this.backupOfferingId
+ localBackupOfferingId: this.backupOfferingId,
+ dataSource: []
}
},
provide () {
diff --git a/ui/src/views/storage/FormSchedule.vue b/ui/src/views/storage/FormSchedule.vue
index acc12e8..433e399 100644
--- a/ui/src/views/storage/FormSchedule.vue
+++ b/ui/src/views/storage/FormSchedule.vue
@@ -38,16 +38,16 @@
v-model:value="form.intervaltype"
buttonStyle="solid"
@change="handleChangeIntervalType">
- <a-radio-button value="hourly" :disabled="handleVisibleInterval(0)">
+ <a-radio-button value="hourly" :disabled="isIntervalDisabled('hourly')">
{{ $t('label.hourly') }}
</a-radio-button>
- <a-radio-button value="daily" :disabled="handleVisibleInterval(1)">
+ <a-radio-button value="daily" :disabled="isIntervalDisabled('daily')">
{{ $t('label.daily') }}
</a-radio-button>
- <a-radio-button value="weekly" :disabled="handleVisibleInterval(2)">
+ <a-radio-button value="weekly" :disabled="isIntervalDisabled('weekly')">
{{ $t('label.weekly') }}
</a-radio-button>
- <a-radio-button value="monthly" :disabled="handleVisibleInterval(3)">
+ <a-radio-button value="monthly" :disabled="isIntervalDisabled('monthly')">
{{ $t('label.monthly') }}
</a-radio-button>
</a-radio-group>
@@ -60,6 +60,7 @@
:title="$t('label.minute.past.hour')">
<a-input-number
style="width: 100%"
+ :disabled="isIntervalDisabled(form.intervaltype)"
v-model:value="form.time"
:min="1"
:max="59"
@@ -76,6 +77,7 @@
<a-time-picker
use12Hours
format="h:mm A"
+ :disabled="isIntervalDisabled(form.intervaltype)"
v-model:value="form.timeSelect"
style="width: 100%;" />
</a-form-item>
@@ -85,6 +87,7 @@
<a-select
v-model:value="form['day-of-week']"
showSearch
+ :disabled="isIntervalDisabled(form.intervaltype)"
optionFilterProp="label"
:filterOption="(input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
@@ -100,6 +103,7 @@
<a-select
v-model:value="form['day-of-month']"
showSearch
+ :disabled="isIntervalDisabled(form.intervaltype)"
optionFilterProp="value"
:filterOption="(input, option) => {
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
@@ -311,6 +315,15 @@
this.volumeId = this.resource.id
this.fetchTimeZone()
},
+ mounted () {
+ if (this.form.intervaltype && this.isIntervalDisabled(this.form.intervaltype)) {
+ const nextAvailable = this.getNextAvailableIntervalType(this.form.intervaltype)
+ if (nextAvailable) {
+ this.form.intervaltype = nextAvailable
+ this.handleChangeIntervalType()
+ }
+ }
+ },
computed: {
formattedAdditionalZoneMessage () {
return `${this.$t('message.snapshot.additional.zones').replace('%x', this.resource.zonename)}`
@@ -319,6 +332,20 @@
return isAdmin()
}
},
+ watch: {
+ dataSource: {
+ handler () {
+ if (this.form.intervaltype && this.getNextAvailableIntervalType && this.isIntervalDisabled(this.form.intervaltype)) {
+ const nextAvailable = this.getNextAvailableIntervalType(this.form.intervaltype)
+ if (nextAvailable) {
+ this.form.intervaltype = nextAvailable
+ this.handleChangeIntervalType()
+ }
+ }
+ },
+ deep: true
+ }
+ },
methods: {
initForm () {
this.formRef = ref()
@@ -404,28 +431,46 @@
}
},
handleChangeIntervalType () {
- switch (this.form.intervaltype) {
+ if (this.form.intervaltype === 'weekly') {
+ this.fetchDayOfWeek()
+ } else if (this.form.intervaltype === 'monthly') {
+ this.fetchDayOfMonth()
+ }
+ this.intervalValue = this.getIntervalValue(this.formintervaltype)
+ },
+ getIntervalValue (intervalType) {
+ switch (intervalType) {
case 'hourly':
- this.intervalValue = 0
- break
+ return 0
case 'daily':
- this.intervalValue = 1
- break
+ return 1
case 'weekly':
- this.intervalValue = 2
- this.fetchDayOfWeek()
- break
+ return 2
case 'monthly':
- this.intervalValue = 3
- this.fetchDayOfMonth()
- break
+ return 3
}
},
- handleVisibleInterval (intervalType) {
+ getNextAvailableIntervalType (currentIntervalType) {
+ const intervalTypes = ['hourly', 'daily', 'weekly', 'monthly']
+ const currentIndex = intervalTypes.indexOf(currentIntervalType)
+ const startIndex = currentIndex >= 0 ? currentIndex : -1
+
+ for (let i = 1; i <= intervalTypes.length; i++) {
+ const nextIndex = (startIndex + i) % intervalTypes.length
+ const nextIntervalType = intervalTypes[nextIndex]
+
+ if (!this.isIntervalDisabled(nextIntervalType)) {
+ return nextIntervalType
+ }
+ }
+ return null
+ },
+ isIntervalDisabled (intervalType) {
+ const intervalValue = this.getIntervalValue(intervalType)
if (this.dataSource.length === 0) {
return false
}
- const dataSource = this.dataSource.filter(item => item.intervaltype === intervalType)
+ const dataSource = this.dataSource.filter(item => item.intervaltype === intervalValue)
if (dataSource && dataSource.length > 0) {
return true
}