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

Pearl1594 pushed a commit to branch fix-clone-offering
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit a8e2be3f656660dd6129d72686d3c729e25c791d
Author: Pearl1594 <[email protected]>
AuthorDate: Fri Sep 4 19:05:27 2026 -0400

    UI: fix clone offering forms silently reverting user edits on services
---
 ui/src/views/offering/CloneNetworkOffering.vue | 11 +++--
 ui/src/views/offering/CloneVpcOffering.vue     | 56 +++++++++++++++++++++-----
 2 files changed, 52 insertions(+), 15 deletions(-)

diff --git a/ui/src/views/offering/CloneNetworkOffering.vue 
b/ui/src/views/offering/CloneNetworkOffering.vue
index 0cd5e733b24..4b4343a823d 100644
--- a/ui/src/views/offering/CloneNetworkOffering.vue
+++ b/ui/src/views/offering/CloneNetworkOffering.vue
@@ -1228,12 +1228,7 @@ export default {
     },
     handleSupportedServiceChange (service, checked, provider) {
       if (checked) {
-        const correctProvider = this.serviceProviderMap[service]
-        if (correctProvider && provider !== correctProvider) {
-          this.selectedServiceProviderMap[service] = correctProvider
-        } else {
-          this.selectedServiceProviderMap[service] = provider
-        }
+        this.selectedServiceProviderMap[service] = provider
       } else {
         delete this.selectedServiceProviderMap[service]
       }
@@ -1294,6 +1289,10 @@ export default {
           params.guestiptype = values.guestiptype
         }
 
+        if (values.forvpc === true) {
+          params.forvpc = true
+        }
+
         // Use composable for service capability params
         if (this.selectedServiceProviderMap != null) {
           buildServiceCapabilityParams(params, values, 
this.selectedServiceProviderMap, this.registeredServicePackages)
diff --git a/ui/src/views/offering/CloneVpcOffering.vue 
b/ui/src/views/offering/CloneVpcOffering.vue
index 6adbfafad61..ab83d7db32d 100644
--- a/ui/src/views/offering/CloneVpcOffering.vue
+++ b/ui/src/views/offering/CloneVpcOffering.vue
@@ -405,7 +405,7 @@ export default {
       this.fetchZoneData()
       this.fetchIpv6NetworkOfferingConfiguration()
       this.fetchRoutedNetworkConfiguration()
-      this.fetchSupportedServiceData()
+      this.fetchSupportedServiceData(true)
     },
     isAdmin () {
       return isAdmin()
@@ -455,7 +455,7 @@ export default {
         this.zoneLoading = false
       })
     },
-    fetchSupportedServiceData () {
+    fetchSupportedServiceData (isInitialLoad = false) {
       this.supportedServiceLoading = true
       getAPI('listSupportedNetworkServices', {}).then(json => {
         const networkServices = 
json.listsupportednetworkservicesresponse.networkservice || []
@@ -491,7 +491,11 @@ export default {
         this.supportedServiceLoading = false
 
         this.$nextTick(() => {
-          this.populateFormFromResource()
+          if (isInitialLoad) {
+            this.populateFormFromResource()
+          } else {
+            this.syncServiceSelectionsForCurrentMode()
+          }
         })
       })
     },
@@ -656,6 +660,45 @@ export default {
         this.form.nsxsupportlb = Boolean(this.serviceProviderMap.Lb)
       }
     },
+    syncServiceSelectionsForCurrentMode () {
+      const updatedServices = this.supportedServices.map(svc => {
+        const serviceCopy = { ...svc, provider: [...svc.provider] }
+        const providerName = this.selectedServiceProviderMap[serviceCopy.name]
+
+        if (providerName) {
+          const providerIndex = serviceCopy.provider.findIndex(p => p.name === 
providerName)
+          if (providerIndex > 0) {
+            const targetProvider = serviceCopy.provider[providerIndex]
+            serviceCopy.provider.splice(providerIndex, 1)
+            serviceCopy.provider.unshift(targetProvider)
+          }
+          serviceCopy.defaultChecked = true
+          serviceCopy.selectedProvider = providerName
+        } else {
+          serviceCopy.defaultChecked = false
+          serviceCopy.selectedProvider = null
+        }
+        return serviceCopy
+      })
+      this.supportedServices = updatedServices
+
+      const availableNames = new Set(updatedServices.map(svc => svc.name))
+      Object.keys(this.selectedServiceProviderMap).forEach(name => {
+        if (!availableNames.has(name)) {
+          delete this.selectedServiceProviderMap[name]
+        }
+      })
+
+      this.connectivityServiceChecked = 
Boolean(this.selectedServiceProviderMap.Connectivity)
+      this.sourceNatServiceChecked = 
Boolean(this.selectedServiceProviderMap.SourceNat)
+
+      this.$nextTick(() => {
+        this.servicesReady = true
+        this.$nextTick(() => {
+          this.checkVpcVirtualRouterForServices()
+        })
+      })
+    },
     async handleProviderChange (value) {
       this.provider = value
       if (this.provider === 'NSX') {
@@ -682,12 +725,7 @@ export default {
     },
     handleSupportedServiceChange (service, checked, provider) {
       if (checked) {
-        const correctProvider = this.serviceProviderMap[service]
-        if (correctProvider && provider !== correctProvider) {
-          this.selectedServiceProviderMap[service] = correctProvider
-        } else {
-          this.selectedServiceProviderMap[service] = provider
-        }
+        this.selectedServiceProviderMap[service] = provider
       } else {
         delete this.selectedServiceProviderMap[service]
       }

Reply via email to