Merge pull request #744 from utchoang/feature/fix-public-vpc-network

diff --git a/src/config/section/network.js b/src/config/section/network.js
index a22bd95..150aa46 100644
--- a/src/config/section/network.js
+++ b/src/config/section/network.js
@@ -244,6 +244,7 @@
       resourceType: 'PublicIpAddress',
       columns: ['ipaddress', 'state', 'associatednetworkname', 'virtualmachinename', 'allocated', 'account', 'zonename'],
       details: ['ipaddress', 'id', 'associatednetworkname', 'virtualmachinename', 'networkid', 'issourcenat', 'isstaticnat', 'virtualmachinename', 'vmipaddress', 'vlan', 'allocated', 'account', 'zonename'],
+      component: () => import('@/views/network/PublicIpResource.vue'),
       tabs: [{
         name: 'details',
         component: () => import('@/components/view/DetailsTab.vue')
diff --git a/src/views/AutogenView.vue b/src/views/AutogenView.vue
index c8742b4..f607ea5 100644
--- a/src/views/AutogenView.vue
+++ b/src/views/AutogenView.vue
@@ -65,7 +65,9 @@
         <a-col
           :span="device === 'mobile' ? 24 : 12"
           :style="device === 'mobile' ? { float: 'right', 'margin-top': '12px', 'margin-bottom': '-6px', display: 'table' } : { float: 'right', display: 'table', 'margin-bottom': '-6px' }" >
+          <slot name="action" v-if="dataView && $route.path.startsWith('/publicip')"></slot>
           <action-button
+            v-else
             :style="dataView ? { float: device === 'mobile' ? 'left' : 'right' } : { 'margin-right': '10px', display: 'inline-flex' }"
             :loading="loading"
             :actions="actions"
@@ -290,7 +292,7 @@
     </div>
 
     <div v-if="dataView">
-      <slot v-if="$route.path.startsWith('/quotasummary')"></slot>
+      <slot name="resource" v-if="$route.path.startsWith('/quotasummary') || $route.path.startsWith('/publicip')"></slot>
       <resource-view
         v-else
         :resource="resource"
@@ -403,6 +405,9 @@
     eventBus.$on('async-job-complete', () => {
       this.fetchData()
     })
+    eventBus.$on('exec-action', (action, isGroupAction) => {
+      this.execAction(action, isGroupAction)
+    })
   },
   mounted () {
     if (this.device === 'desktop') {
diff --git a/src/views/network/PublicIpResource.vue b/src/views/network/PublicIpResource.vue
new file mode 100644
index 0000000..0831f18
--- /dev/null
+++ b/src/views/network/PublicIpResource.vue
@@ -0,0 +1,169 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+<template>
+  <div>
+    <autogen-view @change-resource="changeResource">
+      <div slot="action">
+        <action-button
+          :style="{ float: device === 'mobile' ? 'left' : 'right' }"
+          :loading="loading"
+          :actions="actions"
+          :selectedRowKeys="selectedRowKeys"
+          :dataView="true"
+          :resource="resource"
+          @exec-action="(action) => execAction(action, action.groupAction && !dataView)" />
+      </div>
+      <div slot="resource">
+        <resource-view
+          v-if="isPublicIpAddress && 'id' in resource"
+          :loading="loading"
+          :resource="resource"
+          :tabs="tabs" />
+      </div>
+    </autogen-view>
+  </div>
+</template>
+
+<script>
+import { api } from '@api'
+import { mixinDevice } from '@/utils/mixin.js'
+import eventBus from '@/config/eventBus'
+import AutogenView from '@/views/AutogenView.vue'
+import ResourceView from '@/components/view/ResourceView'
+import ActionButton from '@/components/view/ActionButton'
+
+export default {
+  name: 'PublicIpResource',
+  components: {
+    AutogenView,
+    ResourceView,
+    ActionButton
+  },
+  data () {
+    return {
+      loading: false,
+      selectedRowKeys: [],
+      actions: [],
+      resource: {},
+      tabs: [{
+        name: 'details',
+        component: () => import('@/components/view/DetailsTab.vue')
+      }]
+    }
+  },
+  mixins: [mixinDevice],
+  provide: function () {
+    return {
+      parentFetchData: this.fetchData(),
+      parentToggleLoading: this.toggleLoading
+    }
+  },
+  computed: {
+    isPublicIpAddress () {
+      return this.$route.path.startsWith('/publicip') && this.$route.path.includes('/publicip/')
+    }
+  },
+  watch: {
+    resource () {
+      if ('id' in this.resource) {
+        this.fetchData()
+      }
+    }
+  },
+  mounted () {
+    if ('id' in this.resource) {
+      this.fetchData()
+    }
+  },
+  methods: {
+    async fetchData () {
+      if (Object.keys(this.resource).length === 0) {
+        return
+      }
+
+      this.loading = true
+      this.portFWRuleCount = await this.fetchPortFWRule()
+
+      if (this.portFWRuleCount > 0) {
+        this.tabs = this.$route.meta.tabs.filter(tab => tab.name !== 'loadbalancing')
+      } else {
+        this.loadBalancerRuleCount = await this.fetchLoadBalancerRule()
+
+        if (this.loadBalancerRuleCount > 0) {
+          this.tabs = this.$route.meta.tabs.filter(tab => tab.name !== 'portforwarding')
+          this.loading = false
+        } else {
+          this.tabs = this.$route.meta.tabs
+        }
+      }
+
+      await this.fetchAction()
+      this.loading = false
+    },
+    fetchAction () {
+      this.actions = []
+      if (this.$route.meta.actions) {
+        this.actions = this.$route.meta.actions
+      }
+
+      if (this.portFWRuleCount > 0 || this.loadBalancerRuleCount > 0) {
+        this.actions = this.actions.filter(action => action.api !== 'enableStaticNat')
+      }
+    },
+    fetchPortFWRule () {
+      return new Promise((resolve, reject) => {
+        api('listPortForwardingRules', {
+          listAll: true,
+          ipaddressid: this.resource.id,
+          page: 1,
+          pagesize: 1
+        }).then(json => {
+          const portFWRuleCount = json.listportforwardingrulesresponse.count || 0
+          resolve(portFWRuleCount)
+        }).catch(e => {
+          reject(e)
+        })
+      })
+    },
+    fetchLoadBalancerRule () {
+      return new Promise((resolve, reject) => {
+        api('listLoadBalancerRules', {
+          listAll: true,
+          publicipid: this.resource.id,
+          page: 1,
+          pagesize: 1
+        }).then(json => {
+          const loadBalancerRuleCount = json.listloadbalancerrulesresponse.count || 0
+          resolve(loadBalancerRuleCount)
+        }).catch(e => {
+          reject(e)
+        })
+      })
+    },
+    changeResource (resource) {
+      this.resource = resource
+    },
+    toggleLoading () {
+      this.loading = !this.loading
+    },
+    execAction (action, isGroupAction) {
+      eventBus.$emit('exec-action', action, isGroupAction)
+    }
+  }
+}
+</script>