This is an automated email from the ASF dual-hosted git repository.

dahn pushed a commit to branch 4.22
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/4.22 by this push:
     new e9900aba23a Show time correctly in the backup schedule UI  (#12012)
e9900aba23a is described below

commit e9900aba23a089020722e89b438445e6d11128d7
Author: Abhisar Sinha <[email protected]>
AuthorDate: Wed Dec 17 19:17:28 2025 +0530

    Show time correctly in the backup schedule UI  (#12012)
---
 ui/src/views/compute/backup/BackupSchedule.vue     |  3 +-
 ui/src/views/compute/backup/FormSchedule.vue       | 92 ++++++++++++++++++----
 .../wizard/DeployInstanceBackupSelection.vue       |  4 +-
 ui/src/views/storage/FormSchedule.vue              | 79 +++++++++++++++----
 4 files changed, 143 insertions(+), 35 deletions(-)

diff --git a/ui/src/views/compute/backup/BackupSchedule.vue 
b/ui/src/views/compute/backup/BackupSchedule.vue
index 627bcd8cfbf..b3e894ec982 100644
--- a/ui/src/views/compute/backup/BackupSchedule.vue
+++ b/ui/src/views/compute/backup/BackupSchedule.vue
@@ -99,7 +99,7 @@ export default {
       default: false
     },
     dataSource: {
-      type: Object,
+      type: Array,
       required: true
     },
     deleteFn: {
@@ -128,6 +128,7 @@ export default {
           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 1833449c3cc..7d951deaf20 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 @@ export default {
       type: Boolean,
       default: false
     },
+    dataSource: {
+      type: Array,
+      required: true
+    },
     resource: {
       type: Object,
       required: true
@@ -210,6 +218,38 @@ export default {
     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,18 +314,38 @@ export default {
         })
       }
     },
-    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
@@ -294,7 +354,7 @@ export default {
         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 6f6c0b58d1d..d35ed003827 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 @@ export default {
     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 acc12e8158b..433e399d2a8 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 @@ export default {
     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 @@ export default {
       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 @@ export default {
       }
     },
     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
+      }
+    },
+    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
     },
-    handleVisibleInterval (intervalType) {
+    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
       }

Reply via email to