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

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


The following commit(s) were added to refs/heads/main by this push:
     new 5c97979d458 ui: show related settings when (main) config value is 
changed (#13965)
5c97979d458 is described below

commit 5c97979d458f23f608f120011d727474a4f9abf6
Author: Abhishek Kumar <[email protected]>
AuthorDate: Tue Sep 8 17:35:54 2026 +0530

    ui: show related settings when (main) config value is changed (#13965)
    
    Signed-off-by: Abhishek Kumar <[email protected]>
---
 ui/public/locales/en.json                   |   2 +
 ui/src/views/setting/ConfigurationValue.vue | 122 +++++++++++++++++++++++++++-
 2 files changed, 121 insertions(+), 3 deletions(-)

diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index 83f7d182285..19034d4c4db 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -2203,6 +2203,7 @@
 "label.reinstall.vm": "Reinstall Instance",
 "label.reject": "Reject",
 "label.related": "Related",
+"label.related.settings": "Related Settings",
 "label.relationaloperator": "Operator",
 "label.release": "Release",
 "label.release.account": "Release from Account",
@@ -3938,6 +3939,7 @@
 "message.read.admin.guide.scaling.up": "Please read the dynamic scaling 
section in the admin guide before scaling up.",
 "message.recover.vm": "Please confirm that you would like to recover this 
Instance.",
 "message.reinstall.vm": "NOTE: Proceed with caution. This will cause the 
Instance to be reinstalled from the Template; data on the root disk will be 
lost. Extra data volumes, if any, will not be touched.",
+"message.related.settings.changed": "'%x' has been changed. Would you like to 
review or update any related settings below?",
 "message.release.ip.failed": "Failed to release IP",
 "message.releasing.dedicated.cluster": "Releasing dedicated Cluster...",
 "message.releasing.dedicated.host": "Releasing dedicated host...",
diff --git a/ui/src/views/setting/ConfigurationValue.vue 
b/ui/src/views/setting/ConfigurationValue.vue
index 0979c3c03cb..ea146df8cb3 100644
--- a/ui/src/views/setting/ConfigurationValue.vue
+++ b/ui/src/views/setting/ConfigurationValue.vue
@@ -190,10 +190,41 @@
           :disabled="(!('resetConfiguration' in $store.getters.apis) || 
configDisabled || valueLoading || configrecord.value === 
configrecord.defaultvalue)" />
       </span>
     </a-list-item>
+    <a-modal
+      v-if="!suppressRelatedPrompt"
+      v-model:visible="relatedModalVisible"
+      :title="$t('label.related.settings')"
+      :footer="null"
+      :maskClosable="false"
+      :width="'60vw'"
+      @cancel="closeRelatedModal">
+      <p>{{ $t('message.related.settings.changed').replace('%x', 
relatedSourceConfigName) }}</p>
+      <a-table
+        size="small"
+        :showHeader="false"
+        :pagination="false"
+        :loading="relatedLoading"
+        :columns="relatedColumns"
+        :dataSource="relatedConfigs"
+        :rowKey="record => record.name">
+        <template #bodyCell="{ column, record }">
+          <template v-if="column.key === 'name'">
+            <b>{{ record.displaytext }}</b> {{ ' (' + record.name + ')' }} 
<br/> {{ record.description }}
+          </template>
+          <template v-if="column.key === 'value'">
+            <ConfigurationValue
+              :configrecord="record"
+              :resource="resource"
+              :suppressRelatedPrompt="true"
+              @refresh="handleRelatedConfigRefresh" />
+          </template>
+        </template>
+      </a-table>
+    </a-modal>
   </a-list>
 </template>
 <script>
-import { postAPI } from '@/api'
+import { getAPI, postAPI } from '@/api'
 import TooltipButton from '@/components/widgets/TooltipButton'
 
 export default {
@@ -221,6 +252,10 @@ export default {
     resource: {
       type: Object,
       required: false
+    },
+    suppressRelatedPrompt: {
+      type: Boolean,
+      default: false
     }
   },
   data () {
@@ -229,7 +264,24 @@ export default {
       scopeKey: '',
       actualValue: null,
       editableValue: null,
-      editableValueKey: null
+      editableValueKey: null,
+      relatedModalVisible: false,
+      relatedLoading: false,
+      relatedConfigs: [],
+      relatedSourceConfigName: '',
+      relatedColumns: [
+        {
+          title: 'name',
+          dataIndex: 'name',
+          key: 'name'
+        },
+        {
+          title: 'value',
+          dataIndex: 'value',
+          key: 'value',
+          width: '35%'
+        }
+      ]
     }
   },
   created () {
@@ -259,8 +311,12 @@ export default {
   },
   watch: {
     configrecord: {
-      handler () {
+      handler (newRecord) {
         this.setConfigData()
+        if (newRecord && newRecord.type === 'Boolean' && 
!this.suppressRelatedPrompt &&
+            this.$route.meta.name === 'globalsetting' && 
this.isConfigNowActive(newRecord)) {
+          this.fetchRelatedConfigurations(newRecord)
+        }
       },
       deep: true
     }
@@ -319,6 +375,66 @@ export default {
         this.$emit('refresh', configrecord.name, configRecordEntry)
       })
     },
+    isConfigNowActive (configrecord) {
+      const name = configrecord.name || ''
+      if (name.endsWith('.disabled')) {
+        return configrecord.value === 'false'
+      }
+      return configrecord.value === 'true'
+    },
+    getRelatedConfigPrefix (configrecord) {
+      const name = configrecord.name || ''
+      const segments = name.split('.')
+      if (segments.length < 2) {
+        return null
+      }
+      let prefixSegments = segments.slice(0, -1)
+      if ((name.endsWith('.service.enabled') || 
name.endsWith('.service.disabled')) && prefixSegments.length > 1) {
+        prefixSegments = prefixSegments.slice(0, -1)
+      }
+      return prefixSegments.join('.')
+    },
+    fetchRelatedConfigurations (configrecord) {
+      const prefix = this.getRelatedConfigPrefix(configrecord)
+      if (!prefix) {
+        return
+      }
+      this.relatedLoading = true
+      const params = {
+        [this.scopeKey]: this.$route.params?.id,
+        keyword: prefix,
+        pagesize: -1,
+        listAll: true
+      }
+      if (this.scopeKey === 'domainid' && !params[this.scopeKey]) {
+        params[this.scopeKey] = this.resource?.id
+      }
+      getAPI('listConfigurations', params).then(json => {
+        const list = json?.listconfigurationsresponse?.configuration || []
+        this.relatedConfigs = list.filter(c => c.name !== configrecord.name && 
c.name.startsWith(prefix + '.'))
+        if (this.relatedConfigs.length > 0) {
+          this.relatedSourceConfigName = configrecord.name
+          this.relatedModalVisible = true
+        }
+      }).catch(error => {
+        console.error(error)
+      }).finally(() => {
+        this.relatedLoading = false
+      })
+    },
+    handleRelatedConfigRefresh (name, updatedRecord) {
+      if (!name || !updatedRecord) return
+      const index = this.relatedConfigs.findIndex(item => item.name === name)
+      if (index !== -1) {
+        this.relatedConfigs.splice(index, 1, updatedRecord)
+      }
+      this.$emit('refresh', name, updatedRecord)
+    },
+    closeRelatedModal () {
+      this.relatedModalVisible = false
+      this.relatedConfigs = []
+      this.relatedSourceConfigName = ''
+    },
     resetConfigurationValue (configrecord) {
       let configRecordEntry = this.configrecord
       this.valueLoading = true

Reply via email to