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

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


The following commit(s) were added to refs/heads/4.18 by this push:
     new b2e83271f8b ui: Admin, account and project dashboard improvements 
(#8193)
b2e83271f8b is described below

commit b2e83271f8b71a25890043179511115df8402c8d
Author: Rohit Yadav <[email protected]>
AuthorDate: Wed Nov 8 14:38:05 2023 +0530

    ui: Admin, account and project dashboard improvements (#8193)
    
    Signed-off-by: Rohit Yadav <[email protected]>
---
 ui/public/locales/en.json                    |   1 +
 ui/src/components/widgets/ChartCard.vue      |   1 -
 ui/src/components/widgets/Drawer.vue         |   2 +-
 ui/src/config/router.js                      |  23 +-
 ui/src/core/lazy_lib/components_use.js       |   4 +-
 ui/src/style/dark-mode.less                  |   4 +-
 ui/src/utils/device.js                       |   6 +-
 ui/src/views/dashboard/CapacityDashboard.vue | 532 ++++++++++++++++++++-----
 ui/src/views/dashboard/UsageDashboard.vue    | 569 +++++++++++++++++++++------
 9 files changed, 882 insertions(+), 260 deletions(-)

diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index 55f07357de9..d1f0d84e755 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -2148,6 +2148,7 @@
 "label.volumetotal": "Volume",
 "label.volumetype": "Volume Type",
 "label.vpc": "VPC",
+"label.vpcs": "VPCs",
 "label.vpc.id": "VPC ID",
 "label.vpc.offerings": "VPC offerings",
 "label.vpc.virtual.router": "VPC virtual router",
diff --git a/ui/src/components/widgets/ChartCard.vue 
b/ui/src/components/widgets/ChartCard.vue
index 8d5e413c9db..a41cd69a3f1 100644
--- a/ui/src/components/widgets/ChartCard.vue
+++ b/ui/src/components/widgets/ChartCard.vue
@@ -93,7 +93,6 @@ export default {
   }
 
   &-footer {
-    border-top: 1px solid #e8e8e8;
     padding-top: 9px;
     margin-top: 8px;
   }
diff --git a/ui/src/components/widgets/Drawer.vue 
b/ui/src/components/widgets/Drawer.vue
index 38636522dbd..0a1c535e5f8 100644
--- a/ui/src/components/widgets/Drawer.vue
+++ b/ui/src/components/widgets/Drawer.vue
@@ -134,7 +134,7 @@ export default {
   text-align: center;
   transition: all 0.5s;
   cursor: pointer;
-  top: calc(50% - 45px);
+  top: calc(100% - 45px);
   z-index: 100;
 
   &.left{
diff --git a/ui/src/config/router.js b/ui/src/config/router.js
index e7e8e642877..502a0246edf 100644
--- a/ui/src/config/router.js
+++ b/ui/src/config/router.js
@@ -20,7 +20,7 @@ import { UserLayout, BasicLayout, RouteView } from '@/layouts'
 import AutogenView from '@/views/AutogenView.vue'
 import IFramePlugin from '@/views/plugins/IFramePlugin.vue'
 
-import { shallowRef, defineAsyncComponent } from 'vue'
+import { shallowRef } from 'vue'
 import { vueProps } from '@/vue-app'
 
 import compute from '@/config/section/compute'
@@ -201,26 +201,7 @@ export function asyncRouterMap () {
         name: 'dashboard',
         meta: {
           title: 'label.dashboard',
-          icon: 'DashboardOutlined',
-          tabs: [
-            {
-              name: 'dashboard',
-              component: shallowRef(defineAsyncComponent(() => 
import('@/views/dashboard/UsageDashboardChart')))
-            },
-            {
-              name: 'accounts',
-              show: (record, route, user) => { return record.account === 
user.account || ['Admin', 'DomainAdmin'].includes(user.roletype) },
-              component: shallowRef(defineAsyncComponent(() => 
import('@/views/project/AccountsTab')))
-            },
-            {
-              name: 'limits',
-              params: {
-                projectid: 'id'
-              },
-              show: (record, route, user) => { return 
['Admin'].includes(user.roletype) },
-              component: shallowRef(defineAsyncComponent(() => 
import('@/components/view/ResourceLimitTab.vue')))
-            }
-          ]
+          icon: 'DashboardOutlined'
         },
         component: () => import('@/views/dashboard/Dashboard')
       },
diff --git a/ui/src/core/lazy_lib/components_use.js 
b/ui/src/core/lazy_lib/components_use.js
index 10790d61bc0..3c61c02c08f 100644
--- a/ui/src/core/lazy_lib/components_use.js
+++ b/ui/src/core/lazy_lib/components_use.js
@@ -63,7 +63,8 @@ import {
   Slider,
   AutoComplete,
   Collapse,
-  Space
+  Space,
+  Statistic
 } from 'ant-design-vue'
 import VueClipboard from 'vue3-clipboard'
 import VueCropper from 'vue-cropper'
@@ -127,5 +128,6 @@ export default {
     app.use(Collapse)
     app.use(Descriptions)
     app.use(Space)
+    app.use(Statistic)
   }
 }
diff --git a/ui/src/style/dark-mode.less b/ui/src/style/dark-mode.less
index 1b338ea9ae6..4d8ae25104f 100644
--- a/ui/src/style/dark-mode.less
+++ b/ui/src/style/dark-mode.less
@@ -36,7 +36,7 @@
 .dark-mode {
   background: @dark-bgColor;
 
-  h1, h2, h3, h4, h5, h6 {
+  h1, h2, h3, h4, h5, h6, .ant-statistic-title, .ant-statistic-content {
     color: @dark-text-color-3;
   }
 
@@ -959,4 +959,4 @@
   .button-clear-notification {
     background-color: @dark-secondary-bgColor;
   }
-}
\ No newline at end of file
+}
diff --git a/ui/src/utils/device.js b/ui/src/utils/device.js
index ce6deab7b39..86c7b1e7d84 100644
--- a/ui/src/utils/device.js
+++ b/ui/src/utils/device.js
@@ -43,7 +43,7 @@ export const deviceEnquire = function (callback) {
   }
 
   enquireJs
-    .register('screen and (max-width: 800px)', matchMobile)
-    .register('screen and (min-width: 800px) and (max-width: 1366px)', 
matchTablet)
-    .register('screen and (min-width: 1367px)', matchDesktop)
+    .register('screen and (max-width: 765px)', matchMobile)
+    .register('screen and (min-width: 766px) and (max-width: 1279px)', 
matchTablet)
+    .register('screen and (min-width: 1280px)', matchDesktop)
 }
diff --git a/ui/src/views/dashboard/CapacityDashboard.vue 
b/ui/src/views/dashboard/CapacityDashboard.vue
index 2d7b31ed467..a174e60b709 100644
--- a/ui/src/views/dashboard/CapacityDashboard.vue
+++ b/ui/src/views/dashboard/CapacityDashboard.vue
@@ -16,8 +16,8 @@
 // under the License.
 
 <template>
-  <a-row class="capacity-dashboard" :gutter="12">
-    <a-col :xl="18">
+  <a-row class="capacity-dashboard" :gutter="[12,12]">
+    <a-col :span="24">
       <div class="capacity-dashboard-wrapper">
         <div class="capacity-dashboard-select">
           <a-select
@@ -41,91 +41,283 @@
         <div class="capacity-dashboard-button">
           <a-button
             shape="round"
-            @click="() => { listCapacity(zoneSelected, true); listEvents() }">
+            @click="() => { updateData(zoneSelected); listAlerts(); 
listEvents(); }">
+            <reload-outlined/>
             {{ $t('label.fetch.latest') }}
           </a-button>
         </div>
       </div>
-      <a-row :gutter="12">
-        <a-col
-          :xs="12"
-          :sm="8"
-          :md="6"
-          :style="{ marginBottom: '12px' }"
-          v-for="stat in stats"
-          :key="stat.type">
-          <chart-card :loading="loading">
-            <router-link :to="{ path: '/zone/' + zoneSelected.id }">
-              <div class="capacity-dashboard-chart-card-inner">
-                <h3>{{ $t(ts[stat.name]) }}</h3>
-                <a-progress
-                  type="dashboard"
-                  :status="getStatus(parseFloat(stat.percentused))"
-                  :percent="parseFloat(stat.percentused)"
-                  :format="percent => 
`${parseFloat(stat.percentused).toFixed(2)}%`"
-                  :strokeColor="getStrokeColour(parseFloat(stat.percentused))"
-                  :width="100" />
-              </div>
+    </a-col>
+    <a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ 
span: 8 }">
+      <chart-card :loading="loading" class="dashboard-card">
+        <template #title>
+          <div class="center">
+            <router-link :to="{ path: '/infrasummary' }" 
v-if="!zoneSelected.id">
+              <h3>
+                <bank-outlined />
+                {{ $t('label.infrastructure') }}
+              </h3>
+            </router-link>
+            <router-link :to="{ path: '/zone/' + zoneSelected.id }" v-else>
+              <h3>
+                <global-outlined />
+                {{ $t('label.zone') }}
+              </h3>
+            </router-link>
+          </div>
+        </template>
+        <a-divider style="margin: 0px 0px; border-width: 0px"/>
+        <a-row :gutter="[12, 12]">
+          <a-col :span="12">
+            <router-link :to="{ path: '/pod', query: { zoneid: zoneSelected.id 
} }">
+              <a-statistic
+                :title="$t('label.pods')"
+                :value="data.pods"
+                :value-style="{ color: $config.theme['@primary-color'] }">
+                <template #prefix>
+                  <appstore-outlined/>&nbsp;
+                </template>
+              </a-statistic>
+            </router-link>
+          </a-col>
+          <a-col :span="12">
+            <router-link :to="{ path: '/cluster', query: { zoneid: 
zoneSelected.id } }">
+              <a-statistic
+                :title="$t('label.clusters')"
+                :value="data.clusters"
+                :value-style="{ color: $config.theme['@primary-color'] }">
+                <template #prefix>
+                  <cluster-outlined/>&nbsp;
+                </template>
+              </a-statistic>
+            </router-link>
+          </a-col>
+          <a-col :span="12">
+            <router-link :to="{ path: '/host', query: { zoneid: 
zoneSelected.id } }">
+              <a-statistic
+                :title="$t('label.hosts')"
+                :value="data.totalHosts"
+                :value-style="{ color: $config.theme['@primary-color'] }">
+                <template #prefix>
+                  <database-outlined/>&nbsp;
+                </template>
+              </a-statistic>
+            </router-link>
+          </a-col>
+          <a-col :span="12">
+            <router-link :to="{ path: '/host', query: { zoneid: 
zoneSelected.id, state: 'alert' } }">
+              <a-statistic
+                :title="$t('label.host.alerts')"
+                :value="data.alertHosts"
+                :value-style="{ color: $config.theme['@primary-color'] }">
+                <template #prefix>
+                  <database-outlined/>
+                  <a-badge v-if="data.alertHosts > 0" count="!" 
style="margin-left: -5px" />
+                  <a-badge v-else count="✓" style="margin-left: -5px" 
:number-style="{ backgroundColor: '#52c41a' }" />
+                </template>
+              </a-statistic>
+            </router-link>
+          </a-col>
+          <a-col :span="12">
+            <router-link :to="{ path: '/storagepool', query: { zoneid: 
zoneSelected.id } }">
+              <a-statistic
+                :title="$t('label.primary.storage')"
+                :value="data.pools"
+                :value-style="{ color: $config.theme['@primary-color'] }">
+                <template #prefix>
+                  <hdd-outlined/>&nbsp;
+                </template>
+              </a-statistic>
+            </router-link>
+          </a-col>
+          <a-col :span="12">
+            <router-link :to="{ path: '/systemvm', query: { zoneid: 
zoneSelected.id } }">
+              <a-statistic
+                :title="$t('label.system.vms')"
+                :value="data.systemvms"
+                :value-style="{ color: $config.theme['@primary-color'] }">
+                <template #prefix>
+                  <thunderbolt-outlined/>&nbsp;
+                </template>
+              </a-statistic>
+            </router-link>
+          </a-col>
+          <a-col :span="12">
+            <router-link :to="{ path: '/router', query: { zoneid: 
zoneSelected.id } }">
+              <a-statistic
+                :title="$t('label.virtual.routers')"
+                :value="data.routers"
+                :value-style="{ color: $config.theme['@primary-color'] }">
+                <template #prefix>
+                  <fork-outlined/>&nbsp;
+                </template>
+              </a-statistic>
             </router-link>
-            <template #footer>
-              <div class="center">{{ displayData(stat.name, stat.capacityused) 
}} / {{ displayData(stat.name, stat.capacitytotal) }}</div>
-            </template>
-          </chart-card>
-        </a-col>
-      </a-row>
+          </a-col>
+          <a-col :span="12">
+            <router-link :to="{ path: '/vm', query: { zoneid: zoneSelected.id, 
projectid: '-1' } }">
+              <a-statistic
+                :title="$t('label.instances')"
+                :value="data.instances"
+                :value-style="{ color: $config.theme['@primary-color'] }">
+                <template #prefix>
+                  <cloud-server-outlined/>&nbsp;
+                </template>
+              </a-statistic>
+            </router-link>
+          </a-col>
+        </a-row>
+      </chart-card>
     </a-col>
-
-    <a-col :xl="6" class="dashboard-event">
-      <chart-card :loading="loading">
-        <div style="text-align: center">
-          <a-tooltip placement="bottom" 
class="capacity-dashboard-button-wrapper">
-            <template #title>
-              {{ $t('label.view') + ' ' + $t('label.host.alerts') }}
-            </template>
-            <a-button type="primary" danger shape="circle">
-              <router-link :to="{ name: 'host', query: {'state': 'Alert'} }">
-                <desktop-outlined class="capacity-dashboard-button-icon" />
-              </router-link>
-            </a-button>
-          </a-tooltip>
-          <a-tooltip placement="bottom" 
class="capacity-dashboard-button-wrapper">
-            <template #title>
-              {{ $t('label.view') + ' ' + $t('label.alerts') }}
-            </template>
-            <a-button shape="circle">
-              <router-link :to="{ name: 'alert' }">
-                <flag-outlined class="capacity-dashboard-button-icon" />
-              </router-link>
-            </a-button>
-          </a-tooltip>
-          <a-tooltip placement="bottom" 
class="capacity-dashboard-button-wrapper">
-            <template #title>
-              {{ $t('label.view') + ' ' + $t('label.events') }}
-            </template>
-            <a-button shape="circle">
-              <router-link :to="{ name: 'event' }">
-                <schedule-outlined class="capacity-dashboard-button-icon" />
-              </router-link>
-            </a-button>
-          </a-tooltip>
+    <a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ 
span: 8 }">
+      <chart-card :loading="loading" class="dashboard-card">
+        <template #title>
+          <div class="center">
+            <h3><cloud-outlined /> {{ $t('label.compute') }}</h3>
+          </div>
+        </template>
+        <div>
+          <div v-for="ctype in ['MEMORY', 'CPU', 'CPU_CORE', 'GPU']" 
:key="ctype" >
+            <div v-if="statsMap[ctype]">
+              <div>
+                <strong>{{ $t(ts[ctype]) }}</strong>
+              </div>
+              <a-progress
+              status="active"
+              :percent="statsMap[ctype]?.capacitytotal > 0 ? parseFloat(100.0 
* statsMap[ctype]?.capacityused / statsMap[ctype]?.capacitytotal).toFixed(2) : 
0"
+              :format="p => statsMap[ctype]?.capacitytotal > 0 ? 
parseFloat(100.0 * statsMap[ctype]?.capacityused / 
statsMap[ctype]?.capacitytotal).toFixed(2) + '%' : '0%'"
+              stroke-color="#52c41a"
+              size="small"
+              style="width:95%; float: left"
+              />
+              <br/>
+              <div style="text-align: center">
+                {{ displayData(ctype, statsMap[ctype]?.capacityused) }} {{ 
$t('label.allocated') }} | {{ displayData(ctype, 
statsMap[ctype]?.capacitytotal) }} {{ $t('label.total') }}
+              </div>
+            </div>
+          </div>
+        </div>
+      </chart-card>
+    </a-col>
+    <a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ 
span: 8 }">
+      <chart-card :loading="loading" class="dashboard-card">
+        <template #title>
+          <div class="center">
+            <h3><hdd-outlined /> {{ $t('label.storage') }}</h3>
+          </div>
+        </template>
+        <div>
+          <div v-for="ctype in ['STORAGE', 'STORAGE_ALLOCATED', 
'LOCAL_STORAGE', 'SECONDARY_STORAGE']" :key="ctype" >
+            <div v-if="statsMap[ctype]">
+              <div>
+                <strong>{{ $t(ts[ctype]) }}</strong>
+              </div>
+              <a-progress
+              status="active"
+              :percent="statsMap[ctype]?.capacitytotal > 0 ? parseFloat(100.0 
* statsMap[ctype]?.capacityused / statsMap[ctype]?.capacitytotal).toFixed(2) : 
0"
+              :format="p => statsMap[ctype]?.capacitytotal > 0 ? 
parseFloat(100.0 * statsMap[ctype]?.capacityused / 
statsMap[ctype]?.capacitytotal).toFixed(2) + '%' : '0%'"
+              stroke-color="#52c41a"
+              size="small"
+              style="width:95%; float: left"
+              />
+              <br/>
+              <div style="text-align: center">
+                {{ displayData(ctype, statsMap[ctype]?.capacityused) }} <span 
v-if="ctype !== 'STORAGE'">{{ $t('label.allocated') }}</span><span v-else>{{ 
$t('label.used') }}</span> | {{ displayData(ctype, 
statsMap[ctype]?.capacitytotal) }} {{ $t('label.total') }}
+              </div>
+            </div>
+          </div>
         </div>
-        <template #footer>
-          <div class="capacity-dashboard-footer">
-            <a-timeline>
-              <a-timeline-item
-                v-for="event in events"
-                :key="event.id"
-                :color="getEventColour(event)">
-                <span :style="{ color: '#999' }"><small>{{ 
$toLocaleDate(event.created) }}</small></span><br/>
-                <span :style="{ color: '#666' }"><small><router-link :to="{ 
path: '/event/' + event.id }">{{ event.type }}</router-link></small></span><br/>
-                <resource-label :resourceType="event.resourcetype" 
:resourceId="event.resourceid" :resourceName="event.resourcename" />
-                <span :style="{ color: '#aaa' }">({{ event.username }}) {{ 
event.description }}</span>
-              </a-timeline-item>
-            </a-timeline>
+      </chart-card>
+    </a-col>
+    <a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ 
span: 8 }">
+      <chart-card :loading="loading" class="dashboard-card">
+        <template #title>
+          <div class="center">
+            <h3><apartment-outlined /> {{ $t('label.network') }}</h3>
           </div>
         </template>
+        <div>
+          <div v-for="ctype in ['VLAN', 'VIRTUAL_NETWORK_PUBLIC_IP', 
'VIRTUAL_NETWORK_IPV6_SUBNET', 'DIRECT_ATTACHED_PUBLIC_IP', 'PRIVATE_IP']" 
:key="ctype" >
+            <div v-if="statsMap[ctype]">
+              <div>
+                <strong>{{ $t(ts[ctype]) }}</strong>
+              </div>
+              <a-progress
+              status="active"
+              :percent="statsMap[ctype]?.capacitytotal > 0 ? parseFloat(100.0 
* statsMap[ctype]?.capacityused / statsMap[ctype]?.capacitytotal).toFixed(2) : 
0"
+              :format="p => statsMap[ctype]?.capacitytotal > 0 ? 
parseFloat(100.0 * statsMap[ctype]?.capacityused / 
statsMap[ctype]?.capacitytotal).toFixed(2) + '%' : '0%'"
+              stroke-color="#52c41a"
+              size="small"
+              style="width:95%; float: left"
+              />
+              <br/>
+              <div style="text-align: center">
+                {{ displayData(ctype, statsMap[ctype]?.capacityused) }} {{ 
$t('label.allocated') }} | {{ displayData(ctype, 
statsMap[ctype]?.capacitytotal) }} {{ $t('label.total') }}
+              </div>
+            </div>
+          </div>
+        </div>
       </chart-card>
     </a-col>
+    <a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ 
span: 8 }">
+      <router-link :to="{ path: '/alert' }">
+      <a-card :loading="loading" :bordered="false" class="dashboard-card 
dashboard-event">
+        <div class="center" style="margin-top: -8px">
+          <h3>
+            <flag-outlined />
+            {{ $t('label.alerts') }}
+          </h3>
+        </div>
+        <a-divider style="margin: 6px 0px; border-width: 0px"/>
+        <a-timeline>
+          <a-timeline-item
+            v-for="alert in alerts"
+            :key="alert.id"
+            color="red">
+            <span :style="{ color: '#999' }"><small>{{ 
$toLocaleDate(alert.sent) }}</small></span>&nbsp;
+            <span :style="{ color: '#666' }"><small><router-link :to="{ path: 
'/alert/' + alert.id }">{{ alert.name }}</router-link></small></span><br/>
+            <span :style="{ color: '#aaa' }">{{ alert.description }}</span>
+          </a-timeline-item>
+        </a-timeline>
+        <router-link :to="{ path: '/alert' }">
+          <a-button>
+            {{ $t('label.view') }} {{ $t('label.alerts') }}
+          </a-button>
+        </router-link>
+      </a-card>
+      </router-link>
+    </a-col>
+    <a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ 
span: 8 }">
+      <router-link :to="{ path: '/event' }">
+      <a-card :loading="loading" :bordered="false" class="dashboard-card 
dashboard-event">
+        <div class="center" style="margin-top: -8px">
+          <h3>
+            <schedule-outlined />
+            {{ $t('label.events') }}
+          </h3>
+        </div>
+        <a-divider style="margin: 6px 0px; border-width: 0px"/>
+        <a-timeline>
+          <a-timeline-item
+            v-for="event in events"
+            :key="event.id"
+            :color="getEventColour(event)">
+            <span :style="{ color: '#999' }"><small>{{ 
$toLocaleDate(event.created) }}</small></span>&nbsp;
+            <span :style="{ color: '#666' }"><small><router-link :to="{ path: 
'/event/' + event.id }">{{ event.type }}</router-link></small></span><br/>
+            <span>
+              <resource-label :resourceType="event.resourcetype" 
:resourceId="event.resourceid" :resourceName="event.resourcename" />
+            </span>
+            <span :style="{ color: '#aaa' }">({{ event.username }}) {{ 
event.description }}</span>
+          </a-timeline-item>
+        </a-timeline>
+        <router-link :to="{ path: '/event' }">
+          <a-button>
+            {{ $t('label.view') }} {{ $t('label.events') }}
+          </a-button>
+        </router-link>
+      </a-card>
+      </router-link>
+    </a-col>
   </a-row>
 </template>
 
@@ -135,21 +327,35 @@ import { api } from '@/api'
 import ChartCard from '@/components/widgets/ChartCard'
 import ResourceIcon from '@/components/view/ResourceIcon'
 import ResourceLabel from '@/components/widgets/ResourceLabel'
+import Status from '@/components/widgets/Status'
 
 export default {
   name: 'CapacityDashboard',
   components: {
     ChartCard,
     ResourceIcon,
-    ResourceLabel
+    ResourceLabel,
+    Status
   },
   data () {
     return {
       loading: true,
+      tabKey: 'alerts',
+      alerts: [],
       events: [],
       zones: [],
       zoneSelected: {},
-      stats: [],
+      statsMap: {},
+      data: {
+        pods: 0,
+        clusters: 0,
+        totalHosts: 0,
+        alertHosts: 0,
+        pools: 0,
+        instances: 0,
+        systemvms: 0,
+        routers: 0
+      },
       ts: {
         CPU: 'label.cpu',
         CPU_CORE: 'label.cpunumber',
@@ -159,8 +365,8 @@ export default {
         MEMORY: 'label.memory',
         PRIVATE_IP: 'label.management.ips',
         SECONDARY_STORAGE: 'label.secondary.storage',
-        STORAGE: 'label.storage',
-        STORAGE_ALLOCATED: 'label.primary.storage',
+        STORAGE: 'label.primary.storage.used',
+        STORAGE_ALLOCATED: 'label.primary.storage.allocated',
         VIRTUAL_NETWORK_PUBLIC_IP: 'label.public.ips',
         VLAN: 'label.vlan',
         VIRTUAL_NETWORK_IPV6_SUBNET: 'label.ipv6.subnets'
@@ -196,13 +402,10 @@ export default {
       }
       return 'normal'
     },
-    getStrokeColour (value) {
-      if (value >= 80) {
-        return this.$config.theme['@graph-exception-color'] || 'red'
-      }
-      return this.$config.theme['@graph-normal-color'] || 'primary'
-    },
     displayData (dataType, value) {
+      if (!value) {
+        value = 0
+      }
       switch (dataType) {
         case 'CPU':
           value = parseFloat(value / 1000.0, 10).toFixed(2) + ' GHz'
@@ -214,9 +417,9 @@ export default {
         case 'LOCAL_STORAGE':
           value = parseFloat(value / (1024 * 1024 * 1024.0), 10).toFixed(2)
           if (value >= 1024.0) {
-            value = parseFloat(value / 1024.0).toFixed(2) + ' TB'
+            value = parseFloat(value / 1024.0).toFixed(2) + ' TiB'
           } else {
-            value = value + ' GB'
+            value = value + ' GiB'
           }
           break
       }
@@ -224,26 +427,134 @@ export default {
     },
     fetchData () {
       this.listZones()
+      this.listAlerts()
       this.listEvents()
     },
-    listCapacity (zone, latest = false) {
+    listCapacity (zone, latest = false, additive = false) {
+      this.loading = true
+      api('listCapacity', { zoneid: zone.id, fetchlatest: latest }).then(json 
=> {
+        this.loading = false
+        let stats = []
+        if (json && json.listcapacityresponse && 
json.listcapacityresponse.capacity) {
+          stats = json.listcapacityresponse.capacity
+        }
+        for (const stat of stats) {
+          if (additive) {
+            for (const [key, value] of Object.entries(stat)) {
+              if (stat.name in this.statsMap) {
+                if (key in this.statsMap[stat.name]) {
+                  this.statsMap[stat.name][key] += value
+                } else {
+                  this.statsMap[stat.name][key] = value
+                }
+              } else {
+                this.statsMap[stat.name] = { key: value }
+              }
+            }
+          } else {
+            this.statsMap[stat.name] = stat
+          }
+        }
+      })
+    },
+    updateData (zone) {
+      if (!zone.id) {
+        this.statsMap = {}
+        for (const zone of this.zones.slice(1)) {
+          this.listCapacity(zone, true, true)
+        }
+      } else {
+        this.statsMap = {}
+        this.listCapacity(this.zoneSelected, true)
+      }
+
+      this.data = {
+        pods: 0,
+        clusters: 0,
+        totalHosts: 0,
+        alertHosts: 0,
+        pools: 0,
+        instances: 0,
+        systemvms: 0,
+        routers: 0
+      }
+      this.loading = true
+      api('listPods', { zoneid: zone.id }).then(json => {
+        this.loading = false
+        this.data.pods = json?.listpodsresponse?.count
+        if (!this.data.pods) {
+          this.data.pods = 0
+        }
+      })
+      api('listClusters', { zoneid: zone.id }).then(json => {
+        this.loading = false
+        this.data.clusters = json?.listclustersresponse?.count
+        if (!this.data.clusters) {
+          this.data.clusters = 0
+        }
+      })
+      api('listHosts', { zoneid: zone.id, listall: true, details: 'min', type: 
'routing', page: 1, pagesize: 1 }).then(json => {
+        this.loading = false
+        this.data.totalHosts = json?.listhostsresponse?.count
+        if (!this.data.totalHosts) {
+          this.data.totalHosts = 0
+        }
+      })
+      api('listHosts', { zoneid: zone.id, listall: true, details: 'min', type: 
'routing', state: 'alert', page: 1, pagesize: 1 }).then(json => {
+        this.loading = false
+        this.data.alertHosts = json?.listhostsresponse?.count
+        if (!this.data.alertHosts) {
+          this.data.alertHosts = 0
+        }
+      })
+      api('listStoragePools', { zoneid: zone.id }).then(json => {
+        this.loading = false
+        this.data.pools = json?.liststoragepoolsresponse?.count
+        if (!this.data.pools) {
+          this.data.pools = 0
+        }
+      })
+      api('listSystemVms', { zoneid: zone.id }).then(json => {
+        this.loading = false
+        this.data.systemvms = json?.listsystemvmsresponse?.count
+        if (!this.data.systemvms) {
+          this.data.systemvms = 0
+        }
+      })
+      api('listRouters', { zoneid: zone.id, listall: true }).then(json => {
+        this.loading = false
+        this.data.routers = json?.listroutersresponse?.count
+        if (!this.data.routers) {
+          this.data.routers = 0
+        }
+      })
+      api('listVirtualMachines', { zoneid: zone.id, listall: true, projectid: 
'-1', details: 'min', page: 1, pagesize: 1 }).then(json => {
+        this.loading = false
+        this.data.instances = json?.listvirtualmachinesresponse?.count
+        if (!this.data.instances) {
+          this.data.instances = 0
+        }
+      })
+    },
+    listAlerts () {
       const params = {
-        zoneid: zone.id,
-        fetchlatest: latest
+        page: 1,
+        pagesize: 8,
+        listall: true
       }
       this.loading = true
-      api('listCapacity', params).then(json => {
-        this.stats = []
+      api('listAlerts', params).then(json => {
+        this.alerts = []
         this.loading = false
-        if (json && json.listcapacityresponse && 
json.listcapacityresponse.capacity) {
-          this.stats = json.listcapacityresponse.capacity
+        if (json && json.listalertsresponse && json.listalertsresponse.alert) {
+          this.alerts = json.listalertsresponse.alert
         }
       })
     },
     listEvents () {
       const params = {
         page: 1,
-        pagesize: 6,
+        pagesize: 8,
         listall: true
       }
       this.loading = true
@@ -269,18 +580,19 @@ export default {
         if (json && json.listzonesresponse && json.listzonesresponse.zone) {
           this.zones = json.listzonesresponse.zone
           if (this.zones.length > 0) {
+            this.zones.splice(0, 0, { name: this.$t('label.all.zone') })
             this.zoneSelected = this.zones[0]
-            this.listCapacity(this.zones[0])
+            this.updateData(this.zones[0])
           }
         }
       })
     },
     changeZone (index) {
       this.zoneSelected = this.zones[index]
-      this.listCapacity(this.zoneSelected)
+      this.updateData(this.zoneSelected)
     },
     filterZone (input, option) {
-      return 
option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+      return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
     }
   }
 }
@@ -290,7 +602,6 @@ export default {
 .capacity-dashboard {
   &-wrapper {
     display: flex;
-    margin-bottom: 12px;
   }
 
   &-chart-card-inner {
@@ -313,7 +624,7 @@ export default {
 
   &-button {
     width: auto;
-    padding-left: 12px;
+    padding-left: 8px;
   }
 
   &-button-icon {
@@ -321,21 +632,28 @@ export default {
     padding: 2px;
   }
 
-  &-footer {
+  &-title {
     padding-top: 12px;
     padding-left: 3px;
     white-space: normal;
   }
 }
 
+.dashboard-card {
+  width: 100%;
+  min-height: 370px;
+}
+
+.dashboard-event {
+  width: 100%;
+  overflow-x:hidden;
+  overflow-y: auto;
+  max-height: 370px;
+}
+
 .center {
   display: block;
   text-align: center;
 }
 
-@media (max-width: 1200px) {
-  .dashboard-event {
-    width: 100%;
-  }
-}
 </style>
diff --git a/ui/src/views/dashboard/UsageDashboard.vue 
b/ui/src/views/dashboard/UsageDashboard.vue
index a6983300cb9..9f7d9883dab 100644
--- a/ui/src/views/dashboard/UsageDashboard.vue
+++ b/ui/src/views/dashboard/UsageDashboard.vue
@@ -16,82 +16,303 @@
 // under the License.
 
 <template>
-  <a-row class="usage-dashboard" :gutter="12">
-    <a-col :xl="16" style="padding-left: 0; padding-right: 0;">
-      <a-row>
-        <a-card style="width: 100%">
-          <a-tabs
-            v-if="showProject"
-            :animated="false"
-            @change="onTabChange">
-            <template v-for="tab in $route.meta.tabs" :key="tab.name">
-              <a-tab-pane
-                v-if="'show' in tab ? tab.show(project, $route, 
$store.getters.userInfo) : true"
-                :tab="$t('label.' + tab.name)"
-                :key="tab.name">
-                <keep-alive>
-                  <component
-                    :is="tab.component"
-                    :resource="project"
-                    :loading="loading"
-                    :bordered="false"
-                    :stats="stats" />
-                </keep-alive>
-              </a-tab-pane>
-            </template>
-          </a-tabs>
-          <a-row :gutter="24" v-else>
-            <a-col
-              class="usage-dashboard-chart-tile"
-              :xs="12"
-              :md="8"
-              v-for="stat in stats"
-              :key="stat.type">
-              <a-card
-                class="usage-dashboard-chart-card"
-                :bordered="false"
-                :loading="loading"
-                :style="stat.bgcolor ? { 'background': stat.bgcolor } : {}">
-                <router-link v-if="stat.path" :to="{ path: stat.path, query: 
stat.query }">
-                  <div
-                    class="usage-dashboard-chart-card-inner">
-                    <h3>{{ stat.name }}</h3>
-                    <h2>
-                      <render-icon :icon="stat.icon" />
-                      {{ stat.count == undefined ? 0 : stat.count }}
-                    </h2>
-                  </div>
-                </router-link>
-              </a-card>
-            </a-col>
-          </a-row>
-        </a-card>
-      </a-row>
+  <a-row class="capacity-dashboard" :gutter="[12,12]">
+    <a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ 
span: 8 }">
+      <chart-card :loading="loading" class="dashboard-card">
+        <template #title>
+          <div class="center">
+            <h3>
+              <dashboard-outlined /> {{ $t('label.resources') }}
+              <span style="float: right" v-if="showProject">
+                <a-dropdown>
+                  <template #overlay>
+                    <a-menu>
+                      <a-menu-item>
+                        <router-link :to="{ path: '/project/' + project.id }">
+                          <project-outlined/>
+                            {{ $t('label.view') }} {{  $t('label.project') }}
+                        </router-link>
+                      </a-menu-item>
+                      <a-menu-item v-if="showProject && 
['Admin'].includes($store.getters.userInfo.roletype)">
+                        <router-link :to="{ path: '/project/' + project.id, 
query: { tab: 'limits.configure' } }">
+                          <setting-outlined/>
+                          {{ $t('label.configure') }} {{ $t('label.project') 
}} {{ $t('label.limits') }}
+                        </router-link>
+                      </a-menu-item>
+                    </a-menu>
+                  </template>
+                  <a-button size="small" type="text">
+                    <more-outlined />
+                  </a-button>
+                </a-dropdown>
+              </span>
+            </h3>
+          </div>
+        </template>
+        <a-divider style="margin: 6px 0px; border-width: 0px"/>
+        <a-row :gutter="[10, 10]">
+          <a-col :span="12">
+            <router-link :to="{ path: '/vm' }">
+              <a-statistic
+                :title="$t('label.instances')"
+                :value="data.instances"
+                :value-style="{ color: $config.theme['@primary-color'] }">
+                <template #prefix>
+                  <cloud-server-outlined/>&nbsp;
+                </template>
+              </a-statistic>
+            </router-link>
+          </a-col>
+          <a-col :span="12">
+            <router-link :to="{ path: '/kubernetes' }">
+              <a-statistic
+                :title="$t('label.kubernetes.cluster')"
+                :value="data.kubernetes"
+                :value-style="{ color: $config.theme['@primary-color'] }">
+                <template #prefix>
+                  <cluster-outlined/>&nbsp;
+                </template>
+              </a-statistic>
+            </router-link>
+          </a-col>
+          <a-col :span="12">
+            <router-link :to="{ path: '/volume' }">
+              <a-statistic
+                :title="$t('label.volumes')"
+                :value="data.volumes"
+                :value-style="{ color: $config.theme['@primary-color'] }">
+                <template #prefix>
+                  <hdd-outlined/>&nbsp;
+                </template>
+              </a-statistic>
+            </router-link>
+          </a-col>
+          <a-col :span="12">
+            <router-link :to="{ path: '/snapshot' }">
+              <a-statistic
+                :title="$t('label.snapshots')"
+                :value="data.snapshots"
+                :value-style="{ color: $config.theme['@primary-color'] }">
+                <template #prefix>
+                  <build-outlined/>&nbsp;
+                </template>
+              </a-statistic>
+            </router-link>
+          </a-col>
+          <a-col :span="12">
+            <router-link :to="{ path: '/guestnetwork' }">
+              <a-statistic
+                :title="$t('label.guest.networks')"
+                :value="data.networks"
+                :value-style="{ color: $config.theme['@primary-color'] }">
+                <template #prefix>
+                  <apartment-outlined/>&nbsp;
+                </template>
+              </a-statistic>
+            </router-link>
+          </a-col>
+          <a-col :span="12">
+            <router-link :to="{ path: '/vpc' }">
+              <a-statistic
+                :title="$t('label.vpcs')"
+                :value="data.vpcs"
+                :value-style="{ color: $config.theme['@primary-color'] }">
+                <template #prefix>
+                  <deployment-unit-outlined/>&nbsp;
+                </template>
+              </a-statistic>
+            </router-link>
+          </a-col>
+          <a-col :span="12">
+            <router-link :to="{ path: '/publicip' }">
+              <a-statistic
+                :title="$t('label.public.ips')"
+                :value="data.ips"
+                :value-style="{ color: $config.theme['@primary-color'] }">
+                <template #prefix>
+                  <environment-outlined/>&nbsp;
+                </template>
+              </a-statistic>
+            </router-link>
+          </a-col>
+          <a-col :span="12">
+            <router-link :to="{ path: '/template', query: { templatefilter: 
'self', filter: 'self' } }">
+              <a-statistic
+                :title="$t('label.templates')"
+                :value="data.templates"
+                :value-style="{ color: $config.theme['@primary-color'] }">
+                <template #prefix>
+                  <picture-outlined/>&nbsp;
+                </template>
+              </a-statistic>
+            </router-link>
+          </a-col>
+        </a-row>
+      </chart-card>
     </a-col>
-    <a-col :xl="8">
-      <chart-card :loading="loading" >
-        <div class="usage-dashboard-chart-card-inner">
-          <a-button>
-            <router-link :to="{ name: 'event' }">
-              {{ $t('label.view') + ' ' + $t('label.events') }}
+    <a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ 
span: 8 }">
+      <chart-card :loading="loading" class="dashboard-card">
+        <template #title>
+          <div class="center">
+            <h3>
+              <cloud-outlined /> {{ $t('label.compute') }}
+            </h3>
+          </div>
+        </template>
+        <a-divider style="margin: 6px 0px; border-width: 0px"/>
+        <a-row>
+          <a-col :span="12">
+            <router-link :to="{ path: '/vm', query: { state: 'running', 
filter: 'running' } }">
+              <a-statistic
+                :title="$t('label.running') + ' ' + $t('label.instances')"
+                :value="data.running"
+                :value-style="{ color: $config.theme['@primary-color'] }">
+                <template #prefix>
+                  <status class="status" text="Running"/>
+                </template>
+              </a-statistic>
             </router-link>
-          </a-button>
+          </a-col>
+          <a-col :span="12">
+            <router-link :to="{ path: '/vm', query: { state: 'stopped', 
filter: 'stopped' } }">
+              <a-statistic
+                :title="$t('label.stopped') + ' ' + $t('label.instances')"
+                :value="data.stopped"
+                :value-style="{ color: $config.theme['@primary-color'] }">
+                <template #prefix>
+                  <status class="status" text="Stopped"/>
+                </template>
+              </a-statistic>
+            </router-link>
+          </a-col>
+        </a-row>
+        <a-divider style="margin: 1px 0px; border-width: 0px;"/>
+        <div
+          v-for="usageType in ['vm', 'cpu', 'memory', 'project']"
+          :key="usageType">
+          <div v-if="usageType + 'total' in entity">
+            <div>
+              <strong>
+                {{ $t(getLabel(usageType)) }}
+              </strong>
+              <span style="float: right">
+              {{ getValue(usageType, entity[usageType + 'total']) }} {{ 
$t('label.used') }}
+              </span>
+            </div>
+            <a-progress
+            status="active"
+            :percent="parseFloat(getPercentUsed(entity[usageType + 'total'], 
entity[usageType + 'limit']))"
+            :format="p => resource[item + 'limit'] !== '-1' && resource[item + 
'limit'] !== 'Unlimited' ? p.toFixed(0) + '%' : ''"
+            stroke-color="#52c41a"
+            size="small"
+            />
+            <br/>
+            <div style="text-align: center">
+              {{ entity[usageType + 'available'] === 'Unlimited' ? 
$t('label.unlimited') : getValue(usageType, entity[usageType + 'available']) }} 
{{ $t('label.available') }}
+              {{ entity[usageType + 'limit'] === 'Unlimited' ? '' : (' | ' + 
getValue(usageType, entity[usageType + 'limit']) + ' ' + $t('label.limit')) }}
+            </div>
+          </div>
+        </div>
+      </chart-card>
+    </a-col>
+    <a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ 
span: 8 }">
+      <chart-card :loading="loading" class="dashboard-card">
+        <template #title>
+          <div class="center">
+            <h3><hdd-outlined /> {{ $t('label.storage') }}</h3>
+          </div>
+        </template>
+        <a-divider style="margin: 6px 0px; border-width: 0px"/>
+        <div
+          v-for="usageType in ['volume', 'snapshot', 'template', 
'primarystorage', 'secondarystorage']"
+          :key="usageType">
+          <div>
+            <div>
+              <strong>
+                {{ $t(getLabel(usageType)) }}
+              </strong>
+              <span style="float: right">
+              {{ getValue(usageType, entity[usageType + 'total']) }} {{ 
$t('label.used') }}
+              </span>
+            </div>
+            <a-progress
+            status="active"
+            :percent="parseFloat(getPercentUsed(entity[usageType + 'total'], 
entity[usageType + 'limit']))"
+            :format="p => resource[item + 'limit'] !== '-1' && resource[item + 
'limit'] !== 'Unlimited' ? p.toFixed(0) + '%' : ''"
+            stroke-color="#52c41a"
+            size="small"
+            />
+            <br/>
+            <div style="text-align: center">
+              {{ entity[usageType + 'available'] === 'Unlimited' ? 
$t('label.unlimited') : getValue(usageType, entity[usageType + 'available']) }} 
{{ $t('label.available') }}
+              {{ entity[usageType + 'limit'] === 'Unlimited' ? '' : (' | ' + 
getValue(usageType, entity[usageType + 'limit']) + ' ' + $t('label.limit')) }}
+            </div>
+          </div>
         </div>
-        <template #footer>
-          <div class="usage-dashboard-chart-footer">
-            <a-timeline>
-              <a-timeline-item
-                v-for="event in events"
-                :key="event.id"
-                :color="getEventColour(event)">
-                <span :style="{ color: '#999' }"><small>{{ 
$toLocaleDate(event.created) }}</small></span><br/>
-                <span :style="{ color: '#666' }"><small><router-link :to="{ 
path: '/event/' + event.id }">{{ event.type }}</router-link></small></span><br/>
-                <resource-label :resourceType="event.resourcetype" 
:resourceId="event.resourceid" :resourceName="event.resourcename" />
-                <span :style="{ color: '#aaa' }">({{ event.username }}) {{ 
event.description }}</span>
-              </a-timeline-item>
-            </a-timeline>
+      </chart-card>
+    </a-col>
+    <a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ 
span: 8 }" class="dashboard-card">
+      <chart-card :loading="loading" class="dashboard-card">
+        <template #title>
+          <div class="center">
+            <h3><apartment-outlined /> {{ $t('label.network') }}</h3>
           </div>
         </template>
+        <a-divider style="margin: 6px 0px; border-width: 0px"/>
+        <div
+          v-for="usageType in ['ip', 'network', 'vpc']"
+          :key="usageType">
+          <div>
+            <div>
+              <strong>
+                {{ $t(getLabel(usageType)) }}
+              </strong>
+              <span style="float: right">
+              {{ getValue(usageType, entity[usageType + 'total']) }} {{ 
$t('label.used') }}
+              </span>
+            </div>
+            <a-progress
+            status="active"
+            :percent="parseFloat(getPercentUsed(entity[usageType + 'total'], 
entity[usageType + 'limit']))"
+            :format="p => resource[item + 'limit'] !== '-1' && resource[item + 
'limit'] !== 'Unlimited' ? p.toFixed(0) + '%' : ''"
+            stroke-color="#52c41a"
+            size="small"
+            />
+            <br/>
+            <div style="text-align: center">
+              {{ entity[usageType + 'available'] === 'Unlimited' ? 
$t('label.unlimited') : getValue(usageType, entity[usageType + 'available']) }} 
{{ $t('label.available') }}
+              {{ entity[usageType + 'limit'] === 'Unlimited' ? '' : (' | ' + 
getValue(usageType, entity[usageType + 'limit']) + ' ' + $t('label.limit')) }}
+            </div>
+          </div>
+        </div>
+      </chart-card>
+    </a-col>
+    <a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ 
span: 8 }">
+      <chart-card :loading="loading" class="dashboard-card dashboard-event">
+        <template #title>
+          <div class="center">
+            <h3><schedule-outlined /> {{ $t('label.events') }}</h3>
+          </div>
+        </template>
+        <a-divider style="margin: 6px 0px; border-width: 0px"/>
+        <a-timeline>
+          <a-timeline-item
+            v-for="event in events"
+            :key="event.id"
+            :color="getEventColour(event)">
+            <span :style="{ color: '#999' }"><small>{{ 
$toLocaleDate(event.created) }}</small></span>&nbsp;
+            <span :style="{ color: '#666' }"><small><router-link :to="{ path: 
'/event/' + event.id }">{{ event.type }}</router-link></small></span><br/>
+            <span>
+              <resource-label :resourceType="event.resourcetype" 
:resourceId="event.resourceid" :resourceName="event.resourcename" />
+            </span>
+            <span :style="{ color: '#aaa' }">({{ event.username }}) {{ 
event.description }}</span>
+          </a-timeline-item>
+        </a-timeline>
+        <router-link :to="{ path: '/event' }">
+          <a-button>
+            {{ $t('label.view') }} {{ $t('label.events') }}
+          </a-button>
+        </router-link>
       </chart-card>
     </a-col>
   </a-row>
@@ -104,13 +325,15 @@ import store from '@/store'
 import ChartCard from '@/components/widgets/ChartCard'
 import UsageDashboardChart from '@/views/dashboard/UsageDashboardChart'
 import ResourceLabel from '@/components/widgets/ResourceLabel'
+import Status from '@/components/widgets/Status'
 
 export default {
   name: 'UsageDashboard',
   components: {
     ChartCard,
     UsageDashboardChart,
-    ResourceLabel
+    ResourceLabel,
+    Status
   },
   props: {
     resource: {
@@ -129,9 +352,29 @@ export default {
       loading: false,
       showAction: false,
       showAddAccount: false,
+      project: {},
+      account: {},
       events: [],
-      stats: [],
-      project: {}
+      data: {
+        running: 0,
+        stopped: 0,
+        instances: 0,
+        kubernetes: 0,
+        volumes: 0,
+        snapshots: 0,
+        networks: 0,
+        vpcs: 0,
+        ips: 0,
+        templates: 0
+      }
+    }
+  },
+  computed: {
+    entity: function () {
+      if (this.showProject) {
+        return this.project
+      }
+      return this.account
     }
   },
   created () {
@@ -158,6 +401,9 @@ export default {
       deep: true,
       handler (newData, oldData) {
         this.project = newData
+        if (newData.id) {
+          this.fetchData()
+        }
       }
     },
     '$i18n.global.locale' (to, from) {
@@ -168,61 +414,95 @@ export default {
   },
   methods: {
     fetchData () {
-      this.stats = [{}, {}, {}, {}, {}, {}]
-      api('listVirtualMachines', { state: 'Running', listall: true 
}).then(json => {
-        var count = 0
-        if (json && json.listvirtualmachinesresponse) {
-          count = json.listvirtualmachinesresponse.count
+      if (store.getters.project.id) {
+        this.listProject()
+      } else {
+        this.listAccount()
+      }
+      this.updateData()
+    },
+    listAccount () {
+      this.loading = true
+      api('listAccounts', { id: this.$store.getters.userInfo.accountid 
}).then(json => {
+        this.loading = false
+        if (json && json.listaccountsresponse && 
json.listaccountsresponse.account) {
+          this.account = json.listaccountsresponse.account[0]
         }
-        var tileColor = this.$config.theme['@dashboard-tile-runningvms-bg'] || 
'#dfe9cc'
-        this.stats.splice(0, 1, { name: this.$t('label.running.vms'), count: 
count, icon: 'desktop-outlined', bgcolor: tileColor, path: '/vm', query: { 
state: 'running', filter: 'running' } })
       })
-      api('listVirtualMachines', { state: 'Stopped', listall: true 
}).then(json => {
-        var count = 0
-        if (json && json.listvirtualmachinesresponse) {
-          count = json.listvirtualmachinesresponse.count
+    },
+    listProject () {
+      this.loading = true
+      api('listProjects', { id: store.getters.project.id }).then(json => {
+        this.loading = false
+        if (json && json.listprojectsresponse && 
json.listprojectsresponse.project) {
+          this.project = json.listprojectsresponse.project[0]
         }
-        var tileColor = this.$config.theme['@dashboard-tile-stoppedvms-bg'] || 
'#edcbce'
-        this.stats.splice(1, 1, { name: this.$t('label.stopped.vms'), count: 
count, icon: 'poweroff-outlined', bgcolor: tileColor, path: '/vm', query: { 
state: 'stopped', filter: 'stopped' } })
       })
-      api('listVirtualMachines', { listall: true }).then(json => {
-        var count = 0
-        if (json && json.listvirtualmachinesresponse) {
-          count = json.listvirtualmachinesresponse.count
-        }
-        var tileColor = this.$config.theme['@dashboard-tile-totalvms-bg'] || 
'#ffffff'
-        this.stats.splice(2, 1, { name: this.$t('label.total.vms'), count: 
count, icon: 'number-outlined', bgcolor: tileColor, path: '/vm' })
+    },
+    updateData () {
+      this.data = {
+        running: 0,
+        stopped: 0,
+        instances: 0,
+        kubernetes: 0,
+        volumes: 0,
+        snapshots: 0,
+        networks: 0,
+        vpcs: 0,
+        ips: 0,
+        templates: 0
+      }
+      this.listInstances()
+      this.listEvents()
+      this.loading = true
+      api('listKubernetesClusters', { listall: true, page: 1, pagesize: 1 
}).then(json => {
+        this.loading = false
+        this.data.kubernetes = json?.listkubernetesclustersresponse?.count
       })
-      api('listVolumes', { listall: true }).then(json => {
-        var count = 0
-        if (json && json.listvolumesresponse) {
-          count = json.listvolumesresponse.count
-        }
-        var tileColor = this.$config.theme['@dashboard-tile-totalvolumes-bg'] 
|| '#ffffff'
-        this.stats.splice(3, 1, { name: this.$t('label.total.volume'), count: 
count, icon: 'database-outlined', bgcolor: tileColor, path: '/volume' })
+      api('listVolumes', { listall: true, page: 1, pagesize: 1 }).then(json => 
{
+        this.loading = false
+        this.data.volumes = json?.listvolumesresponse?.count
       })
-      api('listNetworks', { listall: true }).then(json => {
-        var count = 0
-        if (json && json.listnetworksresponse) {
-          count = json.listnetworksresponse.count
-        }
-        var tileColor = this.$config.theme['@dashboard-tile-totalnetworks-bg'] 
|| '#ffffff'
-        this.stats.splice(4, 1, { name: this.$t('label.total.network'), count: 
count, icon: 'apartment-outlined', bgcolor: tileColor, path: '/guestnetwork' })
+      api('listSnapshots', { listall: true, page: 1, pagesize: 1 }).then(json 
=> {
+        this.loading = false
+        this.data.snapshots = json?.listsnapshotsresponse?.count
       })
-      api('listPublicIpAddresses', { listall: true }).then(json => {
-        var count = 0
-        if (json && json.listpublicipaddressesresponse) {
-          count = json.listpublicipaddressesresponse.count
-        }
-        var tileColor = this.$config.theme['@dashboard-tile-totalips-bg'] || 
'#ffffff'
-        this.stats.splice(5, 1, { name: this.$t('label.public.ip.addresses'), 
count: count, icon: 'environment-outlined', bgcolor: tileColor, path: 
'/publicip' })
+      api('listNetworks', { listall: true, page: 1, pagesize: 1 }).then(json 
=> {
+        this.loading = false
+        this.data.networks = json?.listnetworksresponse?.count
+      })
+      api('listVPCs', { listall: true, page: 1, pagesize: 1 }).then(json => {
+        this.loading = false
+        this.data.vpcs = json?.listvpcsresponse?.count
+      })
+      api('listPublicIpAddresses', { listall: true, page: 1, pagesize: 1 
}).then(json => {
+        this.loading = false
+        this.data.ips = json?.listpublicipaddressesresponse?.count
+      })
+      api('listTemplates', { templatefilter: 'self', listall: true, page: 1, 
pagesize: 1 }).then(json => {
+        this.loading = false
+        this.data.templates = json?.listtemplatesresponse?.count
+      })
+    },
+    listInstances (zone) {
+      this.loading = true
+      api('listVirtualMachines', { listall: true, details: 'min', page: 1, 
pagesize: 1 }).then(json => {
+        this.loading = false
+        this.data.instances = json?.listvirtualmachinesresponse?.count
+      })
+      api('listVirtualMachines', { listall: true, details: 'min', state: 
'running', page: 1, pagesize: 1 }).then(json => {
+        this.loading = false
+        this.data.running = json?.listvirtualmachinesresponse?.count
+      })
+      api('listVirtualMachines', { listall: true, details: 'min', state: 
'stopped', page: 1, pagesize: 1 }).then(json => {
+        this.loading = false
+        this.data.stopped = json?.listvirtualmachinesresponse?.count
       })
-      this.listEvents()
     },
     listEvents () {
       const params = {
         page: 1,
-        pagesize: 6,
+        pagesize: 8,
         listall: true
       }
       this.loading = true
@@ -234,6 +514,37 @@ export default {
         }
       })
     },
+    getLabel (usageType) {
+      switch (usageType) {
+        case 'vm':
+          return 'label.instances'
+        case 'cpu':
+          return 'label.cpunumber'
+        case 'memory':
+          return 'label.memory'
+        case 'primarystorage':
+          return 'label.primary.storage'
+        case 'secondarystorage':
+          return 'label.secondary.storage'
+        case 'ip':
+          return 'label.public.ips'
+      }
+      return 'label.' + usageType + 's'
+    },
+    getValue (usageType, value) {
+      switch (usageType) {
+        case 'memory':
+          return parseFloat(value / 1024.0).toFixed(2) + ' GiB'
+        case 'primarystorage':
+          return parseFloat(value).toFixed(2) + ' GiB'
+        case 'secondarystorage':
+          return parseFloat(value).toFixed(2) + ' GiB'
+      }
+      return value
+    },
+    getPercentUsed (total, limit) {
+      return (limit === 'Unlimited') ? 0 : (total / limit) * 100
+    },
     getEventColour (event) {
       if (event.level === 'ERROR') {
         return 'red'
@@ -242,13 +553,6 @@ export default {
         return 'green'
       }
       return 'blue'
-    },
-    onTabChange (key) {
-      this.showAddAccount = false
-
-      if (key !== 'Dashboard') {
-        this.showAddAccount = true
-      }
     }
   }
 }
@@ -276,6 +580,23 @@ export default {
     }
   }
 
+  .dashboard-card {
+    width: 100%;
+    min-height: 420px;
+  }
+
+  .dashboard-event {
+    width: 100%;
+    overflow-x:hidden;
+    overflow-y: scroll;
+    max-height: 420px;
+  }
+
+  .center {
+    display: block;
+    text-align: center;
+  }
+
   @media (max-width: 1200px) {
     .ant-col-xl-8 {
       width: 100%;

Reply via email to