فهرست منبع

#12853 Dashboard v3.0
#13715 group management

vbea 2 سال پیش
والد
کامیت
05c9db4964

+ 36 - 0
Strides-Admin/src/api/dashboard.js

@@ -0,0 +1,36 @@
+import {get, post, put, del} from '../http/http'
+
+const dashboard = {
+  getCountries() {
+    return get("base/getCountries")
+  },
+  getProviderByCode(countryCode) {
+    return get("dashboard/providers", {countryCode})
+  },
+  getSummaryCard(params) {
+    return get("dashboard/top-cards", params)
+  },
+  getHourlyUtilization(params) {
+    return get('dashboard/hourly-usages', params)
+  },
+  getStatisticsCard(params) {
+    return get('dashboard/bottom-cards', params)
+  },
+  getRangeTab() {
+    return get('dashboard/getBottomTab')
+  },
+  getBarChart(params) {
+    return get('dashboard/bottom-chart', params)
+  },
+  getRevenueTracking(params) {
+    return get("dashboard/revenue-tracking", params)
+  },
+  getIncidendtsData(params) {
+    return get("dashboard/incidents", params)
+  },
+  getConnectorsData(params) {
+    return get("dashboard/connectors", params)
+  }
+}
+
+export default dashboard

+ 0 - 17
Strides-Admin/src/api/remote-search.js

@@ -1,17 +0,0 @@
-import request from '@/utils/request'
-
-export function searchUser(name) {
-  return request({
-    url: '/vue-element-admin/search/user',
-    method: 'get',
-    params: { name }
-  })
-}
-
-export function transactionList(query) {
-  return request({
-    url: '/vue-element-admin/transaction/list',
-    method: 'get',
-    params: query
-  })
-}

+ 35 - 0
Strides-Admin/src/router/dashboard/DashboardRouterV1.js

@@ -0,0 +1,35 @@
+import Layout from '@/layout'
+
+export default {
+  path: '/dashboard',
+  component: Layout,
+  meta: {
+    title: 'Dashboard',
+    icon: 'dashboard',
+    activeIcon: 'dashboard-active',
+    affix: true
+  },
+  children: [
+    {
+      path: '/dashboard',
+      component: () => import('@/views/dashboard/Dashboard'),
+      name: 'dashboard',
+      meta: {
+        title: 'Dashboard',
+        icon: 'dashboard',
+        breadcrumb: false
+      }
+    },
+    {
+      path: '/maps',
+      component: () => import('@/views/dashboard/components/Maps'),
+      name: 'maps',
+      meta: {
+        title: 'Maps',
+        icon: 'dashboard',
+        activeMenu: '/dashboard'
+      },
+      hidden: true
+    }
+  ]
+}

+ 24 - 0
Strides-Admin/src/router/dashboard/DashboardRouterV2.js

@@ -0,0 +1,24 @@
+import Layout from '@/layout'
+
+export default {
+  path: '/dashboard',
+  component: Layout,
+  meta: {
+    title: 'Dashboard',
+    icon: 'dashboard',
+    activeIcon: 'dashboard-active',
+    affix: true
+  },
+  children: [
+    {
+      path: '/dashboard',
+      component: () => import('@/views/dashboard/index'),
+      name: 'dashboard',
+      meta: {
+        title: 'Dashboard',
+        icon: 'dashboard',
+        breadcrumb: false
+      }
+    }
+  ]
+}

+ 24 - 0
Strides-Admin/src/router/dashboard/DashboardRouterV3.js

@@ -0,0 +1,24 @@
+import Layout from '@/layout'
+
+export default {
+  path: '/dashboard',
+  component: Layout,
+  meta: {
+    title: 'Dashboard',
+    icon: 'dashboard',
+    activeIcon: 'dashboard-active',
+    affix: true
+  },
+  children: [
+    {
+      path: '/dashboard',
+      component: () => import('@/views/dashboard/index2'),
+      name: 'dashboard',
+      meta: {
+        title: 'Dashboard',
+        icon: 'dashboard',
+        breadcrumb: false
+      }
+    }
+  ]
+}

+ 19 - 0
Strides-Admin/src/router/dashboard/index.js

@@ -0,0 +1,19 @@
+import settings from '../../settings'
+import DashboardRouterV1 from './DashboardRouterV1'
+import DashboardRouterV2 from './DashboardRouterV2';
+import DashboardRouterV3 from './DashboardRouterV3';
+
+export default {
+  getDashboardRouter() {
+    switch (settings.dashboardVersion) {
+      case 1:
+        return DashboardRouterV1;
+      case 2:
+        return DashboardRouterV2;
+      case 3:
+        return DashboardRouterV3;
+      default:
+        return DashboardRouterV1;
+    }
+  }
+}

+ 2 - 32
Strides-Admin/src/router/index.js

@@ -1,6 +1,7 @@
 import Vue from 'vue'
 import VueRouter from 'vue-router'
 import Layout from '@/layout'
+import Dashboard from './dashboard/index'
 import SiteRouter from './SiteRouter'
 import ChargeRouter from './ChargeRouter'
 import UserRouter from './UserRouterV2'
@@ -70,38 +71,7 @@ const constantRoutes = [
       }
     ],
   },*/
-  {
-    path: '/dashboard',
-    component: Layout,
-    meta: {
-      title: 'Dashboard',
-      icon: 'dashboard',
-      activeIcon: 'dashboard-active',
-      affix: true
-    },
-    children: [
-      {
-        path: '/dashboard',
-        component: () => import('@/views/dashboard/index'),
-        name: 'dashboard',
-        meta: {
-          title: 'Dashboard',
-          icon: 'dashboard',
-          breadcrumb: false
-        }
-      },
-      {
-        path: '/maps',
-        component: () => import('@/views/dashboard/components/Maps'),
-        name: 'maps',
-        meta: {
-          title: 'Maps',
-          icon: 'dashboard',
-        },
-        hidden: true
-      }
-    ],
-  },
+  Dashboard.getDashboardRouter(),
   SiteRouter,
   ChargeRouter,
   ActivityRouter,

+ 1 - 0
Strides-Admin/src/settings.js

@@ -13,6 +13,7 @@ module.exports = {
   company: 'Strides YTL Pte. Ltd.',
   defaultCountry: 'SG',
   defaultCalling: '65',
+  dashboardVersion: 2,
   /**
    * @type {string}
    * @description API url base service

+ 2 - 1
Strides-Admin/src/views/article/detail.vue

@@ -74,7 +74,8 @@
                 :http-request="file => uploadImages(file, index)"
                 accept=".jpg,.jpeg,.png,.gif,.JPG,.JPEG"
                 v-loading="item.loading"
-                v-for="(item, index) in images">
+                v-for="(item, index) in images"
+                :key="index">
                 <div class="uploader-image" v-if="item.articleImagePath">
                   <el-image
                     :src="$imageSrc(item.articleImagePath)"

+ 6 - 3
Strides-Admin/src/views/campaign/detail.vue

@@ -78,7 +78,8 @@
                 :http-request="file => uploadImages(file, index)"
                 accept=".jpg,.jpeg,.png,.gif,.JPG,.JPEG"
                 v-loading="item.loading"
-                v-for="(item, index) in images">
+                v-for="(item, index) in images"
+                :key="index">
                 <div class="uploader-image" v-if="item.articleImagePath">
                   <el-image
                     :src="$imageSrc(item.articleImagePath)"
@@ -349,7 +350,9 @@ export default {
           this.form = info;
           this.$nextTick(() => {
             this.form.discounts.forEach((item, index) => {
-              this.changeUserType(index, item.userType)
+              setTimeout(() => {
+                this.changeUserType(index, item.userType)
+              }, 300 * index)
             })
           })
           this.addImageObj();
@@ -503,7 +506,7 @@ export default {
         this.form.discounts[dsIndex].groupPk = "";
       }
       if (dsType) {
-        if (this.groupOptions[dsType]) {
+        if (this.groupOptions[dsType] && this.groupOptions[dsType].length > 0) {
           this.form.discounts[dsIndex].groupList = this.groupOptions[dsType];
         } else {
           this.groupOptions[dsType] = []

+ 14 - 0
Strides-Admin/src/views/company/index.vue

@@ -121,6 +121,9 @@
         <template slot="header">
           Eligible Sites<br/><span style="white-space: nowrap;">(Discount)</span>
         </template>
+        <template slot-scope="{row}">
+          {{getEligibleCount(row.eligibleSiteCount)}}
+        </template>
       </el-table-column>
       <el-table-column
         align="center"
@@ -311,6 +314,17 @@ export default {
           message: error
         })
       })
+    },
+    getEligibleCount(src) {
+      if (src && src.indexOf("/") > 0) {
+        let arr = src.split("/");
+        let cur = Number(arr[0]);
+        let tol = Number(arr[1]);
+        if (cur > tol) cur = tol;
+        return cur + "/" + tol;
+      } else {
+        return src;
+      }
     }
   }
 }

+ 12 - 4
Strides-Admin/src/views/dashboard/chart/LineChart.vue

@@ -1,5 +1,9 @@
 <template>
-  <div id="lineChart" ref="chartBox"/>
+  <div 
+    :id='"lineChart"+index'
+    :key="index"
+    ref="chartBox"
+    class="lineChart"/>
 </template>
 
 <script>
@@ -9,6 +13,10 @@ import * as echarts from 'echarts'
 
 export default {
   props: {
+    index: {
+      type: Number|String,
+      default: 0
+    },
     chartData: {
       type: Object,
       default: () => ({
@@ -40,7 +48,7 @@ export default {
       this.initChart()
     })
     const erd = elementResizeDetectorMaker()
-    erd.listenTo(document.getElementById('lineChart'), element => {
+    erd.listenTo(document.getElementById('lineChart'+this.index), element => {
       const s = this.resize + 1;
       this.resize = s;
       setTimeout(() => {
@@ -58,7 +66,7 @@ export default {
       }
     },
     initChart() {
-      this.chart = echarts.init(document.getElementById('lineChart'), 'macarons')
+      this.chart = echarts.init(document.getElementById('lineChart'+this.index), 'macarons')
       this.setOptions()
     },
     setOptions() {
@@ -140,7 +148,7 @@ export default {
 </script>
 
 <style lang="scss" scoped>
-#lineChart {
+.lineChart {
   width: 100%;
   height: 22vh;
 }

+ 283 - 0
Strides-Admin/src/views/dashboard/components/Connectors.vue

@@ -0,0 +1,283 @@
+<template>
+  <div class="connectors-container" v-loading="changeLoading">
+    <div class="card-group">
+      <div class="card-item-view" :style="'max-width:' + size + 'px;'">
+        <div class="card-title">Available</div>
+        <div class="card-value avaliable">{{ connectorData.available.total || 0 }}</div>
+      </div>
+      <div class="card-item-view" :style="'max-width:' + size + 'px;'">
+        <div class="card-title">In Use</div>
+        <div class="card-value in-use">{{ connectorData.inUse.total || 0 }}</div>
+      </div>
+      <div class="card-item-view" :style="'max-width:' + size + 'px;'">
+        <div class="card-title">Reserved</div>
+        <div class="card-value reserved">{{ connectorData.reserved.total || 0 }}</div>
+      </div>
+      <div class="card-item-view" :style="'max-width:' + size + 'px;'">
+        <div class="card-title">Faulted</div>
+        <div class="card-value faulted">{{ connectorData.faulted.total || 0 }}</div>
+      </div>
+      <div class="card-item-view" :style="'max-width:' + size + 'px;'">
+        <div class="card-title">Unavailable</div>
+        <div class="card-value unavailable">{{ connectorData.unavailable.total || 0 }}</div>
+      </div>
+    </div>
+    <div class="card-group card-list-group">
+      <div class="card-item-view" :style="'max-width:' + size + 'px;'">
+        <div class="title-underline">
+          <span>STATION</span>
+          <span>CONNECTOR</span>
+        </div>
+        <div v-if="connectorData.available.items">
+          <div
+            class="details-row"
+            v-for="(item, index) in connectorData.available.items"
+            :key="index">
+            <span>{{item.stationId}}</span>
+            <b>{{item.connector}}</b>
+          </div>
+        </div>
+        <el-skeleton
+          v-else
+          :rows="10"
+          animated
+          style="width: 100%; flex: 1"/>
+      </div>
+      <div class="card-item-view" :style="'max-width:' + size + 'px;'">
+        <div class="title-underline">
+          <span>STATION</span>
+          <span>CONNECTOR</span>
+        </div>
+        <div v-if="connectorData.inUse.items">
+          <div
+            class="details-row"
+            v-for="(item, index) in connectorData.inUse.items"
+            :key="index">
+            <span>{{item.stationId}}</span>
+            <b>{{item.connector}}</b>
+          </div>
+        </div>
+        <el-skeleton
+          v-else
+          :rows="10"
+          animated
+          style="width: 100%; flex: 1"/>
+      </div>
+      <div class="card-item-view" :style="'max-width:' + size + 'px;'">
+        <div class="title-underline">
+          <span>STATION</span>
+          <span>CONNECTOR</span>
+        </div>
+        <div v-if="connectorData.reserved.items">
+          <div
+            class="details-row"
+            v-for="(item, index) in connectorData.reserved.items"
+            :key="index">
+            <span>{{item.stationId}}</span>
+            <b>{{item.connector}}</b>
+          </div>
+        </div>
+        <el-skeleton
+          v-else
+          :rows="10"
+          animated
+          style="width: 100%; flex: 1"/>
+      </div>
+      <div class="card-item-view" :style="'max-width:' + size + 'px;'">
+        <div class="title-underline">
+          <span>STATION</span>
+          <span>CONNECTOR</span>
+        </div>
+        <div v-if="connectorData.faulted.items">
+          <div
+            class="details-row"
+            v-for="(item, index) in connectorData.faulted.items"
+            :key="index">
+            <span>{{item.stationId}}</span>
+            <b>{{item.connector}}</b>
+          </div>
+        </div>
+        <el-skeleton
+          v-else
+          :rows="10"
+          animated
+          style="width: 100%; flex: 1"/>
+      </div>
+      <div class="card-item-view" :style="'max-width:' + size + 'px;'">
+        <div class="title-underline">
+          <span>STATION ID</span>
+          <span>CONNECTOR</span>
+        </div>
+        <div v-if="connectorData.unavailable.items">
+          <div
+            class="details-row"
+            v-for="(item, index) in connectorData.unavailable.items"
+            :key="index">
+            <span>{{item.stationId}}</span>
+            <b>{{item.connector}}</b>
+          </div>
+        </div>
+        <el-skeleton
+          v-else
+          :rows="10"
+          animated
+          style="width: 100%; flex: 1"/>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import api from '../../../api/dashboard';
+export default {
+  name: "Connectors",
+  props: {
+    filters: {
+      type: Object,
+      default: () => {}
+    },
+    size: {
+      type: String | Number,
+      default: ""
+    }
+  },
+  data() {
+    return {
+      changeLoading: false,
+      connectorData: {
+        inUse: {},
+        faulted: {},
+        reserved: {},
+        available: {},
+        unavailable: {}
+      }
+    };
+  },
+  watch: {
+    filters: {
+      deep: true,
+      handler(val) {
+        this.getData()
+      }
+    }
+  },
+  mounted() {
+    this.getData()
+  },
+  methods: {
+    init() {
+      this.connectorData = {
+        inUse: {},
+        faulted: {},
+        reserved: {},
+        available: {},
+        unavailable: {}
+      }
+    },
+    getData() {
+      this.changeLoading = true;
+      api.getConnectorsData(this.filters).then(res => {
+        this.changeLoading = false
+        if (res.data) {
+          this.connectorData = res.data
+        } else {
+          this.init()
+        }
+      }).catch(err => {
+        this.changeLoading = false
+        this.$message({
+          type: 'error',
+          message: err
+        })
+        this.init()
+      })
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+  .connectors-container {
+    margin: 5px -5px 0;
+    min-height:  calc(100vh - 130px);
+  }
+
+  .card-group {
+    display: flex;
+    flex-wrap: wrap;
+    flex-flow: wrap;
+    & > .card-item-view {
+      margin: 5px;
+      min-width: 240px;
+    }
+  }
+
+  .card-item-view {
+    flex: 1;
+    padding: 10px 20px;
+    border-radius: 6px;
+    background-color: white;
+  }
+  
+  .card-list-group .card-item-view {
+    min-height: 65vh;
+  }
+  
+  .card-title {
+    color: #333333;
+    font-size: 16px;
+    font-weight: bold;
+    text-align: center;
+  }
+  
+  .card-value {
+    color: #333;
+    font-size: 42px;
+    line-height: 63px;
+    text-align: center;
+    padding: 10px 0;
+    font-family: sans-serif;
+    &.avaliable {
+      color: #1ABD00;
+    }
+    &.in-use {
+      color: #236EDE;
+    }
+    &.reserved {
+      color: #FDA802;
+    }
+    &.faulted {
+      color: #EF3340;
+    }
+    &.unavaliable {
+      color: #000000;
+    }
+  }
+  
+  .title-underline {
+    width: 100%;
+    color: #333333;
+    font-size: 14px;
+    line-height: 21px;
+    display: flex;
+    align-items: center;
+    font-weight: bold;
+    white-space: nowrap;
+    padding-bottom: 20px;
+    font-family: sans-serif;
+    text-decoration: underline;
+    justify-content: space-between;
+  }
+  
+  .details-row {
+    width: 100%;
+    color: #333333;
+    font-size: 14px;
+    line-height: 21px;
+    display: flex;
+    align-items: center;
+    padding-bottom: 10px;
+    font-family: sans-serif;
+    justify-content: space-between;
+  }
+</style>

+ 272 - 0
Strides-Admin/src/views/dashboard/components/Incidents.vue

@@ -0,0 +1,272 @@
+<template>
+  <div class="incidents-container" v-loading="changeLoading">
+    <div class="card-group">
+      <div class="card-item-view" :style="'max-width:' + size + 'px;'" v-if="incidentData.bootNotification">
+        <div class="card-title">Boot Notification</div>
+        <div class="card-value">{{ incidentData.bootNotification.total || 0 }}</div>
+      </div>
+      <div class="card-item-view" :style="'max-width:' + size + 'px;'" v-if="incidentData.connectionEstablished">
+        <div class="card-title">Connection Established</div>
+        <div class="card-value">{{ incidentData.connectionEstablished.total || 0 }}</div>
+      </div>
+      <div class="card-item-view" :style="'max-width:' + size + 'px;'" v-if="incidentData.disconnected">
+        <div class="card-title">Disconnected</div>
+        <div class="card-value error">{{ incidentData.disconnected.total || 0 }}</div>
+      </div>
+      <div class="card-item-view" :style="'max-width:' + size + 'px;'" v-if="incidentData.statusFailure">
+        <div class="card-title">Status Failure</div>
+        <div class="card-value error">{{ incidentData.statusFailure.total || 0 }}</div>
+      </div>
+      <div class="card-item-view" :style="'max-width:' + size + 'px;'" v-if="incidentData.idlingFeeCharge">
+        <div class="card-title">Idling Fee Charge</div>
+        <div class="card-value">{{ incidentData.idlingFeeCharge.total || 0 }}</div>
+      </div>
+    </div>
+    <div class="card-group card-list-group">
+      <div class="card-item-view" :style="'max-width:' + size + 'px;'">
+        <div class="title-underline">
+          <span>STATION ID</span>
+          <span>COUNT</span>
+        </div>
+        <div v-if="incidentData.bootNotification.items">
+          <div
+            class="details-row"
+            v-for="(item, index) in incidentData.bootNotification.items"
+            :key="index">
+            <span>{{item.stationId}}</span>
+            <b>{{item.count}}</b>
+          </div>
+        </div>
+        <el-skeleton
+          v-else
+          :rows="10"
+          animated
+          style="width: 100%; flex: 1"/>
+      </div>
+      <div class="card-item-view" :style="'max-width:' + size + 'px;'">
+        <div class="title-underline">
+          <span>STATION ID</span>
+          <span>COUNT</span>
+        </div>
+        <div v-if="incidentData.connectionEstablished.items">
+          <div
+            class="details-row"
+            v-for="(item, index) in incidentData.connectionEstablished.items"
+            :key="index">
+            <span>{{item.stationId}}</span>
+            <b>{{item.count}}</b>
+          </div>
+        </div>
+        <el-skeleton
+          v-else
+          :rows="10"
+          animated
+          style="width: 100%; flex: 1"/>
+      </div>
+      <div class="card-item-view" :style="'max-width:' + size + 'px;'">
+        <div class="title-underline">
+          <span>STATION ID</span>
+          <span>COUNT</span>
+        </div>
+        <div v-if="incidentData.disconnected.items">
+          <div
+            class="details-row"
+            v-for="(item, index) in incidentData.disconnected.items"
+            :key="index">
+            <span>{{item.stationId}}</span>
+            <b>{{item.count}}</b>
+          </div>
+        </div>
+        <el-skeleton
+          v-else
+          :rows="10"
+          animated
+          style="width: 100%; flex: 1"/>
+      </div>
+      <div class="card-item-view" :style="'max-width:' + size + 'px;'">
+        <div class="title-underline">
+          <span>STATION ID</span>
+          <span>COUNT</span>
+        </div>
+        <div v-if="incidentData.statusFailure.items">
+          <div
+            class="details-row"
+            v-for="(item, index) in incidentData.statusFailure.items"
+            :key="index">
+            <span>{{item.stationId}}</span>
+            <b>{{item.count}}</b>
+          </div>
+        </div>
+        <el-skeleton
+          v-else
+          :rows="10"
+          animated
+          style="width: 100%; flex: 1"/>
+      </div>
+      <div class="card-item-view" :style="'max-width:' + size + 'px;'">
+        <div class="title-underline">
+          <span>STATION ID</span>
+          <span>COUNT</span>
+        </div>
+        <div v-if="incidentData.idlingFeeCharge.items">
+          <div
+            class="details-row"
+            v-for="(item, index) in incidentData.idlingFeeCharge.items"
+            :key="index">
+            <span>{{item.stationId}}</span>
+            <b>{{item.count}}</b>
+          </div>
+        </div>
+        <el-skeleton
+          v-else
+          :rows="10"
+          animated
+          style="width: 100%; flex: 1"/>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import api from '../../../api/dashboard';
+export default {
+  name: "Incidents",
+  props: {
+    filters: {
+      type: Object,
+      default: () => {}
+    },
+    size: {
+      type: String | Number,
+      default: ""
+    }
+  },
+  data() {
+    return {
+      changeLoading: false,
+      incidentData: {
+        disconnected: {},
+        statusFailure: {},
+        idlingFeeCharge: {},
+        bootNotification: {},
+        connectionEstablished: {}
+      }
+    };
+  },
+  watch: {
+    filters: {
+      deep: true,
+      handler(val) {
+        this.getData()
+      }
+    }
+  },
+  mounted() {
+    this.getData()
+  },
+  methods: {
+    init() {
+      this.incidentData = {
+        disconnected: {},
+        statusFailure: {},
+        idlingFeeCharge: {},
+        bootNotification: {},
+        connectionEstablished: {}
+      }
+    },
+    getData() {
+      this.changeLoading = true;
+      api.getIncidendtsData(this.filters).then(res => {
+        this.changeLoading = false
+        if (res.data) {
+          this.incidentData = res.data
+        } else {
+          this.init()
+        }
+      }).catch(err => {
+        this.changeLoading = false
+        this.$message({
+          type: 'error',
+          message: err
+        })
+        this.init()
+      })
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+  @import '../../../styles/element-ui.scss';
+  .incidents-container {
+    margin: 5px -5px 0;
+    min-height:  calc(100vh - 130px);
+  }
+  
+  .card-group {
+    display: flex;
+    flex-wrap: wrap;
+    flex-flow: wrap;
+    & > .card-item-view {
+      margin: 5px;
+      min-width: 240px;
+    }
+  }
+
+  .card-item-view {
+    flex: 1;
+    padding: 10px 20px;
+    border-radius: 6px;
+    background-color: white;
+  }
+  
+  .card-list-group .card-item-view {
+    min-height: 65vh;
+  }
+  
+  .card-title {
+    color: #333333;
+    font-size: 16px;
+    font-weight: bold;
+    text-align: center;
+  }
+  
+  .card-value {
+    color: $--color-accent;
+    font-size: 42px;
+    line-height: 63px;
+    text-align: center;
+    padding: 10px 0;
+    font-family: sans-serif;
+    &.error {
+      color: #FF3500;
+    }
+  }
+  
+  .title-underline {
+    width: 100%;
+    color: #333333;
+    font-size: 14px;
+    line-height: 21px;
+    display: flex;
+    align-items: center;
+    font-weight: bold;
+    white-space: nowrap;
+    padding-bottom: 20px;
+    font-family: sans-serif;
+    text-decoration: underline;
+    justify-content: space-between;
+  }
+  
+  .details-row {
+    width: 100%;
+    color: #333333;
+    font-size: 14px;
+    line-height: 21px;
+    display: flex;
+    align-items: center;
+    padding-bottom: 10px;
+    font-family: sans-serif;
+    justify-content: space-between;
+  }
+</style>

+ 14 - 10
Strides-Admin/src/views/dashboard/components/Maps.vue

@@ -22,23 +22,23 @@
       </el-form>
     </div>
     <div class="card-group">
-      <div class="maps-card-view">
+      <div class="maps-card-view" :style="'max-width:' + size + 'px;'">
         <div class="card-title">Available</div>
         <div class="avaliable-card-value">{{ statistics.available || 0 }}</div>
       </div>
-      <div class="maps-card-view">
+      <div class="maps-card-view" :style="'max-width:' + size + 'px;'">
         <div class="card-title">In Use</div>
         <div class="in-user-card-value">{{ statistics.inUse || 0}}</div>
       </div>
-      <div class="maps-card-view">
+      <div class="maps-card-view" :style="'max-width:' + size + 'px;'">
         <div class="card-title">Reserved</div>
         <div class="reserved-card-value">{{ statistics.reserved || 0}}</div>
       </div>
-      <div class="maps-card-view">
+      <div class="maps-card-view" :style="'max-width:' + size + 'px;'">
         <div class="card-title">Faulted</div>
         <div class="faulted-card-value">{{ statistics.faulted || 0 }}</div>
       </div>
-      <div class="maps-card-view">
+      <div class="maps-card-view" :style="'max-width:' + size + 'px;'">
         <div class="card-title">Unavailable</div>
         <div class="unavaliable-card-value">{{ statistics.unavailable }}</div>
       </div>
@@ -61,6 +61,10 @@ export default {
     provider: {
       type: String|Number,
       default: ""
+    },
+    size: {
+      type: String | Number,
+      default: ""
     }
   },
   data() {
@@ -292,21 +296,20 @@ export default {
     display: flex;
     flex-wrap: wrap;
     flex-flow: wrap;
-    align-items: flex-start;
+    margin: 0 -5px 5px;
   }
 
   .maps-card-view {
     flex: 1;
     display: flex;
-    min-width: 200px;
+    min-width: 240px;
     max-width: 320px;
     flex-direction: column;
     background-color: white;
     justify-content: center;
     align-items: center;
-    padding: 10px 10px;
-    margin-right: 10px;
-    margin-bottom: 10px;
+    padding: 10px;
+    margin: 5px;
     border-radius: 6px;
   }
   
@@ -335,6 +338,7 @@ export default {
   }
 
   %card-value {
+    padding: 10px 0;
     font-family: sans-serif;
     font-style: normal;
     font-weight: 500;

+ 604 - 0
Strides-Admin/src/views/dashboard/components/SummaryV2.vue

@@ -0,0 +1,604 @@
+<template>
+  <div v-loading="changeLoading">
+    <div class="summary-view flexr">
+      <div class="summary-card" :style="'max-width:' + size + 'px;'">
+        <div class="summary-title">Total Sites</div>
+        <div class="summary-max-value">{{summaryData.totalSites}}</div>
+      </div>
+      <div class="summary-card" :style="'max-width:' + size + 'px;'">
+        <div class="summary-title">Total Stations</div>
+        <div class="summary-max-value">{{summaryData.totalStations}}</div>
+      </div>
+      <div class="summary-card" :style="'max-width:' + size + 'px;'">
+        <div class="summary-title">Total Connectors</div>
+        <div class="summary-max-value">{{summaryData.totalConnectors}}</div>
+      </div>
+      <div class="summary-card" :style="'max-width:' + size + 'px;'">
+        <div class="summary-title">Total No. Of Top Ups</div>
+        <div class="summary-max-value">{{summaryData.totalNoOfTopUps}}</div>
+      </div>
+      <div class="summary-card" :style="'max-width:' + size + 'px;'">
+        <div class="summary-title">Total Consumption (kWh)</div>
+        <div class="summary-max-value">{{summaryData.totalConsumption}}</div>
+      </div>
+    </div>
+    <div class="charts-card">
+      <div class="flex">
+        <div class="chart-title hourly">Hourly Utilization(kWh)</div>
+      </div>
+      <LineChart
+        index="hourly"
+        :chartData="hourlyData"/>
+    </div>
+    <div class="summary-view flexr">
+      <StatisticCard
+        title="Total Transactions"
+        :value="statisticsData.totalTransCard.totalTrans"
+        v-if="statisticsData.totalTransCard">
+        <div class="scard-item-row">
+          <div class="scard-item-title">Public Users</div>
+          <div class="scard-item-value">{{statisticsData.totalTransCard.totalPublic}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Fleet Users</div>
+          <div class="scard-item-value">{{statisticsData.totalTransCard.totalFleet}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Partners</div>
+          <div class="scard-item-value">{{statisticsData.totalTransCard.totalPartner}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Users with Memberships</div>
+          <div class="scard-item-value">{{statisticsData.totalTransCard.totalMember}}</div>
+        </div>
+      </StatisticCard>
+      <StatisticCard
+        title="Total Users"
+        :value="statisticsData.totalUsersCard.totalUsers"
+        v-if="statisticsData.totalUsersCard">
+        <div class="scard-item-row">
+          <div class="scard-item-title">Public Users</div>
+          <div class="scard-item-value">{{statisticsData.totalUsersCard.totalPublic}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Fleet Users</div>
+          <div class="scard-item-value">{{statisticsData.totalUsersCard.totalFleet}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Partners</div>
+          <div class="scard-item-value">{{statisticsData.totalUsersCard.totalPartner}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Users with Memberships</div>
+          <div class="scard-item-value">{{statisticsData.totalUsersCard.totalMember}}</div>
+        </div>
+      </StatisticCard>
+      <StatisticCard
+        title="Total Revenue"
+        :value="statisticsData.totalRevenue.totalRevenue"
+        v-if="statisticsData.totalRevenue">
+        <div class="scard-item-row">
+          <div class="scard-item-title">Charging Revenue</div>
+          <div class="scard-item-value">{{statisticsData.totalRevenue.totalChargingRevenue}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Idle Fee Revenue</div>
+          <div class="scard-item-value">{{statisticsData.totalRevenue.totalIdleFeeRevenue}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Reservation Revenue</div>
+          <div class="scard-item-value">{{statisticsData.totalRevenue.totalReservationRevenue}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Discounts Given</div>
+          <div class="scard-item-value">{{statisticsData.totalRevenue.totalDiscountsGiven}}</div>
+        </div>
+      </StatisticCard>
+      <StatisticCard
+        title="Today’s Consumption"
+        :value="statisticsData.totalTodayConsumption.totalTodayConsumption"
+        unit="kWh"
+        v-if="statisticsData.totalTodayConsumption">
+        <div class="scard-item-row">
+          <div class="scard-item-title">Total Delivered</div>
+          <div class="scard-item-value">{{statisticsData.totalTodayConsumption.totalTotalDelivered}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Highest Delivered</div>
+          <div class="scard-item-value">{{statisticsData.totalTodayConsumption.totalHighestDelivered}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Average Delivered</div>
+          <div class="scard-item-value">{{statisticsData.totalTodayConsumption.totalAverageDelivered}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Lowest Delivered</div>
+          <div class="scard-item-value">{{statisticsData.totalTodayConsumption.totalLowestDelivered}}</div>
+        </div>
+      </StatisticCard>
+      <StatisticCard
+        title="Today’s Incidents"
+        :value="statisticsData.totalTodayIncidents.totalTodayIncidents"
+        v-if="statisticsData.totalTodayIncidents">
+        <div class="scard-item-row">
+          <div class="scard-item-title">Disconnected</div>
+          <div class="scard-item-value">{{statisticsData.totalTodayIncidents.totalDisconnected}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Connection Established</div>
+          <div class="scard-item-value">{{statisticsData.totalTodayIncidents.totalConnectionEstablished}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Boot Notification</div>
+          <div class="scard-item-value">{{statisticsData.totalTodayIncidents.totalBootNotification}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Status Failure</div>
+          <div class="scard-item-value">{{statisticsData.totalTodayIncidents.totalStatusFailure}}</div>
+        </div>
+      </StatisticCard>
+      <StatisticCard
+        title="Today’s Revenue"
+        :value="statisticsData.totalTodayRevenue.totalTodayRevenue"
+        v-if="statisticsData.totalTodayRevenue">
+        <div class="scard-item-row">
+          <div class="scard-item-title">Transactions Completed</div>
+          <div class="scard-item-value">{{statisticsData.totalTodayRevenue.totalTransCompleted}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Transactions Ongoing</div>
+          <div class="scard-item-value">{{statisticsData.totalTodayRevenue.totalTransOngoing}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Reservations</div>
+          <div class="scard-item-value">{{statisticsData.totalTodayRevenue.totalReservations}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Idle Charges</div>
+          <div class="scard-item-value">{{statisticsData.totalTodayRevenue.totalIdleCharges}}</div>
+        </div>
+      </StatisticCard>
+      <StatisticCard
+        title="Credit Expenditure"
+        :value="statisticsData.totalCreditExpenditure.totalCreditExpenditure"
+        v-if="statisticsData.totalCreditExpenditure">
+        <div class="scard-item-row">
+          <div class="scard-item-title">Wallet Expenditure</div>
+          <div class="scard-item-value">{{statisticsData.totalCreditExpenditure.totalWalletExpenditure}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Fleet Credit Expenditure</div>
+          <div class="scard-item-value">{{statisticsData.totalCreditExpenditure.totalFleetCreditExpenditure}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Monthly Fleet Credit Limit</div>
+          <div class="scard-item-value">{{statisticsData.totalCreditExpenditure.totalMonthlyFleetCredit}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Discounts Given</div>
+          <div class="scard-item-value">{{statisticsData.totalCreditExpenditure.totalDiscountsGiven}}</div>
+        </div>
+      </StatisticCard>
+      <StatisticCard
+        title="Connectors In Use"
+        :value="statisticsData.totalConnectorsInUse.totalConnectorsInUse"
+        v-if="statisticsData.totalConnectorsInUse">
+        <div class="scard-item-row">
+          <div class="scard-item-title">Available</div>
+          <div class="scard-item-value">{{statisticsData.totalConnectorsInUse.totalAvailable}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Faulted</div>
+          <div class="scard-item-value">{{statisticsData.totalConnectorsInUse.totalFaulted}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Reserved</div>
+          <div class="scard-item-value">{{statisticsData.totalConnectorsInUse.totalReserved}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Finishing</div>
+          <div class="scard-item-value">{{statisticsData.totalConnectorsInUse.totalFinishing}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Unavailable</div>
+          <div class="scard-item-value">{{statisticsData.totalConnectorsInUse.totalUnavailable}}</div>
+        </div>
+      </StatisticCard>
+      <StatisticCard
+        title="Total Wallet Credit"
+        :value="statisticsData.totalWalletCredit.totalWalletCredit"
+        v-if="statisticsData.totalWalletCredit">
+        <div class="scard-item-row">
+          <div class="scard-item-title">Credits Purchase</div>
+          <div class="scard-item-value">{{statisticsData.totalWalletCredit.totalCreditsPurchase}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Manual Top Up</div>
+          <div class="scard-item-value">{{statisticsData.totalWalletCredit.totalManualTopUp}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Refunds</div>
+          <div class="scard-item-value">{{statisticsData.totalWalletCredit.totalRefunds}}</div>
+        </div>
+        <div class="scard-item-row">
+          <div class="scard-item-title">Credits Unused</div>
+          <div class="scard-item-value">{{statisticsData.totalWalletCredit.totalCreditsUnused}}</div>
+        </div>
+      </StatisticCard>
+    </div>
+    <div class="charts-card">
+      <div class="radio-group">
+        <el-radio-group
+          v-model="dateTabIndex"
+          size="small"
+          @change="changeBarDateTab">
+          <el-radio-button
+            v-for="(item,index) in dateRangeTab"
+            :key="index"
+            :label="item.value">
+            {{item.name}}
+          </el-radio-button>
+        </el-radio-group>
+      </div>
+      <el-row
+        class="barGroup"
+        v-loading="barLoading">
+        <el-col
+          class="barView"
+          :sm="12"
+          :md="8"
+          v-if="barChartData.utilization">
+          <BarChart 
+            index="0"
+            :chartData="barChartData.utilization"
+            unit="%"
+            title="Utilization Rate so far"/>
+        </el-col>
+        <el-col
+          class="barView"
+          :sm="12"
+          :md="8"
+          v-if="barChartData.consumption">
+          <BarChart
+            index="1"
+            :chartData="barChartData.consumption"
+            unit=" kWh"
+            title="Consumption so far"/>
+        </el-col>
+        <el-col
+          class="barView"
+          :sm="12"
+          :md="8"
+          v-if="barChartData.revenue">
+          <BarChart
+            index="2"
+            :chartData="barChartData.revenue"
+            :unit="barChartData.currency + ' ' + barChartData.currencySymbol"
+            :leftUnit="true"
+            title="Revenue so far"/>
+        </el-col>
+      </el-row>
+    </div>
+    <div class="charts-card">
+      <div class="flexcr">
+        <div class="chart-title flex1">Revenue Tracking</div>
+        <el-radio-group
+          v-model="dateTrackIndex"
+          size="small"
+          @change="changeTrackDateTab">
+          <el-radio-button
+            v-for="(item,index) in dateRangeTab"
+            :key="index"
+            :label="item.value">
+            {{item.name}}
+          </el-radio-button>
+        </el-radio-group>
+      </div>
+      <div style="padding: 10px 0;" v-loading="trackLoading">
+        <LineChart
+          index="tracking"
+          :chartData="revenueTracking"/>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import api from '../../../api/dashboard'
+import BarChart from '../chart/BarChart.vue'
+import LineChart from '../chart/LineChart.vue'
+import CircleChart from '../chart/CircleChart.vue'
+import StatisticCard from '../fragment/StatisticCard.vue'
+export default {
+  name: "SummaryV2",
+  props: {
+    filters: {
+      type: Object,
+      default: () => {}
+    },
+    size: {
+      type: String | Number,
+      default: ""
+    }
+  },
+  components: {
+    BarChart, LineChart, CircleChart, StatisticCard
+  },
+  data() {
+    return {
+      changeLoading: false,
+      barLoading: true,
+      trackLoading: true,
+      hourlyData: {
+        xdata: [],
+        ydata: []
+      },
+      summaryData: {
+        totalSites: 0,
+        totalStations: 0,
+        totalConnectors: 0,
+        totalNoOfTopUps: 0,
+        totalConsumption: 0
+      },
+      statisticsData: {
+        /*totalRevenue: {},
+        totalUsersCard: {},
+        totalTransCard: {},
+        totalTodayRevenue: {},
+        totalWalletCredit: {},
+        totalTodayIncidents: {},
+        totalConnectorsInUse: {},
+        totalTodayConsumption: {},
+        totalCreditExpenditure: {}*/
+      },
+      dateTabIndex: 0,
+      dateRangeTab: [],
+      barChartData: [],
+      dateTrackIndex: 0,
+      revenueTracking: {
+        xdata: [],
+        ydata: []
+      }
+    };
+  },
+  watch: {
+    filters: {
+      deep: true,
+      handler(val) {
+        this.getData()
+      }
+    }
+  },
+  mounted() {
+    this.getData();
+  },
+  methods: {
+    getData() {
+      this.changeLoading = true
+      this.getSummaryData();
+      this.getStatisticsData();
+      this.getRangeTabList();
+      this.getLineChart();
+    },
+    getSummaryData() {
+      api.getSummaryCard(this.filters).then(res => {
+        if (res.data) {
+          this.summaryData = res.data
+        }
+      }).catch(err => {
+        this.changeLoading = false
+        this.$message({
+          type: 'error',
+          message: err
+        })
+        this.summaryData = {}
+      })
+    },
+    getLineChart() {
+      api.getHourlyUtilization(this.filters).then(res => {
+        if (res.data.xdata && res.data.ydata) {
+          this.hourlyData = res.data
+        } else {
+          this.hourlyData = undefined
+        }
+      }).catch(err => {
+        this.changeLoading = false
+        this.$message({
+          type: 'error',
+          message: err
+        })
+        this.hourlyData = undefined
+      }).finally(() => {
+        
+      })
+    },
+    getStatisticsData() {
+      api.getStatisticsCard(this.filters).then(res => {
+        if (res.data) {
+          this.statisticsData = res.data
+        } else {
+          this.statisticsData = {};
+        }
+      }).catch(err => {
+        this.changeLoading = false
+        this.$message({
+          type: 'error',
+          message: err
+        })
+        this.statisticsData = {}
+      })
+    },
+    getRangeTabList() {
+      if (this.dateRangeTab.length == 0) {
+        api.getRangeTab().then(res => {
+          if (res.data) {
+            this.dateRangeTab = res.data
+            if (res.data.length > 0) {
+              this.dateTabIndex = res.data[0].value
+              this.dateTrackIndex = res.data[0].value
+              this.getBarChartData()
+              this.getRevenueTrackingData()
+            }
+          } else {
+            this.dateRangeTab = [];
+          }
+        }).catch(err => {
+          this.changeLoading = false
+          this.$message({
+            type: 'error',
+            message: err
+          })
+          this.dateRangeTab = [];
+        })
+      } else {
+        this.getBarChartData()
+        this.getRevenueTrackingData()
+      }
+    },
+    getBarChartData() {
+      api.getBarChart({
+        tabType: this.dateTabIndex,
+        ...this.filters
+      }).then(res => {
+        if (res.data) {
+          this.barChartData = res.data
+        } else {
+          this.barChartData = []
+        }
+      }).catch(err => {
+        this.$message({
+          type: 'error',
+          message: err
+        })
+        this.barChartData = []
+      }).finally(() => {
+        this.barLoading = false
+      })
+    },
+    getRevenueTrackingData() {
+      api.getRevenueTracking({
+        tabType: this.dateTrackIndex,
+        ...this.filters
+      }).then(res => {
+        if (res.data) {
+          this.revenueTracking = res.data
+        } else {
+          this.revenueTracking = {}
+        }
+      }).catch(err => {
+        this.$message({
+          type: 'error',
+          message: err
+        })
+        this.revenueTracking = {}
+      }).finally(() => {
+        this.trackLoading = false;
+        this.changeLoading = false
+      })
+    },
+    changeBarDateTab() {
+      this.barLoading = true;
+      setTimeout(() => {
+        this.getBarChartData()
+      }, 300);
+    },
+    changeTrackDateTab() {
+      this.trackLoading = true;
+      setTimeout(() => {
+        this.getRevenueTrackingData()
+      }, 300);
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+  @import '../../../styles/element-ui.scss';
+ .summary-view {
+    margin: 5px -5px 0;
+  }
+  .summary-view > div {
+    margin: 5px;
+  }
+  .summary-view > div.summary-view {
+    margin: 0;
+  }
+  .summary-view > .summary-card {
+    margin: 5px;
+    min-width: 240px;
+  }
+  .summary-card {
+    flex: 1;
+    padding: 10px 20px;
+    border-radius: 6px;
+    background-color: white;
+  }
+  .summary-title {
+    color: #333333;
+    font-size: 16px;
+    font-weight: bold;
+    text-align: center;
+  }
+  .summary-max-value {
+    color: $--color-accent;
+    font-size: 42px;
+    line-height: 63px;
+    text-align: center;
+    padding: 10px 0;
+  }
+  .charts-card {
+    display: flex;
+    overflow: hidden;
+    padding: 10px 20px;
+    margin-top: 5px;
+    margin-bottom: 10px;
+    border-radius: 6px;
+    flex-direction: column;
+    background-color: white;
+  }
+  .chart-title {
+    color: #333;
+    font-size: 32px;
+    padding-top: 5px;
+    padding-bottom: 15px;
+    &.hourly {
+      color: #555;
+      font-size: 20px;
+      padding-left: 30px;
+      transform: scaleX(1.3);
+      -webkit-font-smoothing: antialiased;
+    }
+  }
+  .radio-group {
+    padding-top: 5px;
+    text-align: center;
+  }
+  .barGroup {
+    margin-top: 15px;
+    padding-top: 10px;
+    border-top: 1px solid #F0F5FC;
+  }
+  .barView {
+    padding-top: 10px;
+    padding-bottom: 10px;
+  }
+  @media screen and (min-width: 768px) {
+    .barView + .barView {
+      border-left: 10px solid #F0F5FC;
+    }
+    .barView + .barView + .barView {
+      border-left: none;
+    }
+  }
+  @media screen and (min-width: 992px) {
+    .barView + .barView + .barView {
+      border-left: 10px solid #F0F5FC;
+    }
+  }
+  @media screen and (max-width: 768px) {
+    .summary-card {
+      min-width: 40vw;
+      max-width: calc(50% - 10px);
+    }
+  }
+</style>

+ 150 - 0
Strides-Admin/src/views/dashboard/fragment/StatisticCard.vue

@@ -0,0 +1,150 @@
+<template>
+  <div class="scard-layout">
+    <div class="scard-left">
+      <div class="title">{{title}}</div>
+      <div :style='"height: " + (scalePixel * 2) + "px;"'></div>
+      <div
+        class="total-value"
+        :style='"zoom: " + scaleText + ";"'
+        :title="value">{{value || "0"}}</div>
+      <div class="unit-text">{{unit}}</div>
+    </div>
+    <div class="scard-right">
+      <slot></slot>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "StatisticCard",
+  props: {
+    title: {
+      type: String,
+      default: ""
+    },
+    value: {
+      type: String|Number,
+      default: 0
+    },
+    unit: String
+  },
+  data() {
+    return {
+      scaleText: 1,
+      scalePixel: 0
+    };
+  },
+  watch: {
+    value: {
+      deep: true,
+      handler(val) {
+        this.getValueTextSize()
+      }
+    }
+  },
+  mounted() {
+    this.getValueTextSize()
+  },
+  methods: {
+    getValueTextSize() {
+      this.scaleText = 1;
+      if (this.value) {
+        const length = ("" + this.value).length;
+        if (length > 6) {
+          this.scalePixel = (length - 6) % 2 + 1;
+          this.scaleText -= this.scalePixel * 0.12;
+        }
+      }
+    }
+  }
+}
+</script>
+
+<style scoped="scoped" lang="scss">
+  @import '../../../styles/element-ui.scss';
+  .scard-layout {
+    flex: 1;
+    display: flex;
+    min-width: 30vw;
+    max-width: 33vw;
+    padding: 10px 20px;
+    border-radius: 6px;
+    background-color: white;
+  }
+  .scard-left {
+    flex: 2;
+    overflow: hidden;
+    padding: 5px 15px 5px 0;
+    text-align: center;
+    border-right: 1px solid #333;
+  }
+  .title {
+    color: #333;
+    text-align: center;
+    font-size: 16px;
+    font-weight: bold;
+    position: relative;
+  }
+  .total-value {
+    color: $--color-accent;
+    font-size: 42px;
+    text-align: center;
+    white-space: nowrap;
+    overflow: hidden;
+    padding: 15px 0 5px;
+    text-overflow: ellipsis;
+  }
+  .unit-text {
+    color: #333;
+    font-size: 14px;
+  }
+  .scard-right {
+    flex: 3;
+    display: flex;
+    flex-direction: column;
+    justify-content: space-around;
+    padding: 2px 0px 2px 10px;
+  }
+  .scard-item-row {
+    display: flex;
+    align-items: center;
+  }
+  .scard-item-title {
+    flex: 1;
+    color: #333;
+    font-size: 14px;
+    padding-right: 5px;
+  }
+  .scard-item-value {
+    color: $--color-accent;
+    font-size: 14px;
+  }
+  @media screen and (max-width: 1500px) {
+    .scard-layout {
+      min-width: 400px;
+      max-width: calc(50% - 10px);
+    }
+    .total-value {
+      font-size: 40px;
+    }
+  }
+  @media screen and (max-width: 900px) {
+    .scard-layout {
+      min-width: 45vw;
+      max-width: calc(50% - 10px);
+    }
+    .total-value {
+      font-size: 35px;
+    }
+  }
+  @media screen and (max-width: 600px) {
+    .scard-layout {
+      min-width: 90vw;
+      max-width: 100%;
+    }
+    .total-value {
+      font-size: 35px;
+    }
+  }
+</style>

+ 229 - 0
Strides-Admin/src/views/dashboard/index2.vue

@@ -0,0 +1,229 @@
+<template>
+  <div class="dashboard-container" v-loading="loading">
+    <div class="filter-view" id="dashboard-container-v3">
+      <el-select
+        class="sp-filter"
+        v-model="filters.countryCode"
+        @change="changeCountry"
+        placeholder="Country">
+        <el-option 
+          v-for="(item, index) in options.country"
+          :key="index"
+          :value="item.countryCode"
+          :label="item.countryName"/>
+      </el-select>
+      <el-select
+        class="sp-filter"
+        v-model="filters.providerPk"
+        @change="changeProvider"
+        placeholder="Service Provider">
+        <el-option 
+          v-for="(item, index) in options.provider"
+          :key="index"
+          :value="item.value"
+          :label="item.name"/>
+      </el-select>
+      <div class="radio-group">
+        <el-radio-group
+          v-model="filterTab"
+          @change="changeTab">
+          <el-radio-button
+            v-for="(item,index) in filterTabs"
+            :key="index"
+            :label="item.value">
+            {{item.name}}
+          </el-radio-button>
+        </el-radio-group>
+      </div>
+    </div>
+    <Summary
+      :filters="filters"
+      v-show="filterTab=='summary'"
+      :size="size"/>
+    <Incidents
+      :filters="filters"
+      v-show="filterTab=='incident'"
+      :size="size"/>
+    <Connectors
+      :filters="filters"
+      v-show="filterTab=='connector'"
+      :size="size"/>
+    <Maps
+      provider="all"
+      v-if="filterTab=='maps'"
+      :size="size"/>
+  </div>
+</template>
+
+<script>
+import api from '../../api/dashboard';
+import Summary from './components/SummaryV2';
+import Incidents from './components/Incidents';
+import Connectors from './components/Connectors';
+import Maps from './components/Maps';
+import setting from '../../settings.js';
+import elementResizeDetectorMaker from 'element-resize-detector';
+export default {
+  data() {
+    return {
+      loading: false,
+      filters: {
+        providerPk: "",
+        countryCode: setting.defaultCountry
+      },
+      filterTab: "summary",
+      filterTabs: [{
+        name: "Summary",
+        value: "summary"
+      },{
+        name: "Incidents",
+        value: "incident"
+      },{
+        name: "Connectors",
+        value: "connector"
+      },{
+        name: "Map",
+        value: "maps"
+      }],
+      options: {
+        country: [],
+        provider: []
+      },
+      size: 0,
+      resize: 0,
+      columns: 0,
+      width: 1920,
+      minWidth: 250,
+      maxWidth: 1920,
+      lastColumns: 0,
+    }
+  },
+  components: {
+    Summary, Incidents, Connectors, Maps
+  },
+  created() {
+    this.loading = true;
+    this.getContryOptions();
+  },
+  mounted() {
+    this.init();
+  },
+  methods: {
+    init() {
+      const maker = elementResizeDetectorMaker()
+      maker.listenTo(document.getElementById('dashboard-container-v3'), element => {
+        const s = this.resize + 1;
+        this.resize = s;
+        setTimeout(() => {
+          this.onResize(s);
+        }, 100);
+      })
+    },
+    onResize(p) {
+      if (p && p == this.resize) {
+        const layout = document.getElementById("dashboard-container-v3");
+        const width = layout.clientWidth;
+        if (width < this.maxWidth) {
+          this.width = width;
+          this.columns = parseInt(this.width / this.minWidth);
+          if (this.columns > 5) this.columns = 5;
+          this.size = parseInt(this.width / this.columns) - 9;
+        } else {
+          this.width = (this.maxWidth + 0);
+          this.columns = 5;
+          this.size = parseInt(this.width / this.columns) - 9;
+        }
+      }
+    },
+    getContryOptions() {
+      api.getCountries().then(res => {
+        this.loading = false
+        if (res.data) {
+          this.options.country = res.data
+          if (this.filters.countryCode) {
+            this.getProvider();
+          }
+        }
+      }).catch(err => {
+        this.loading = false
+        this.$message({
+          type: 'error',
+          message: err
+        })
+      })
+    },
+    getProvider() {
+      api.getProviderByCode(this.filters.countryCode).then(res => {
+        if (res.data) {
+          this.options.provider = res.data
+          if (res.data.length > 0) {
+            this.filters.providerPk = res.data[0].value
+          }
+        }
+      }).catch(err => {
+        this.loading = false
+        this.$message({
+          type: 'error',
+          message: err
+        })
+      })
+    },
+    changeCountry() {
+      this.filters.providerPk = "";
+      this.getProvider();
+    },
+    changeProvider() {
+      
+    },
+    changeTab() {
+      
+    },
+    changeSize(size) {
+      this.cardSize = size;
+    }
+  }
+}
+</script>
+
+<style scoped lang='scss'>
+  /*@import '../../styles/variables.scss';*/
+  @import '../../styles/element-ui.scss';
+  .dashboard-container {
+    padding: 10px 20px;
+    /*min-height: $mainAppMinHeight;*/
+    background-color: #F0F5FC;
+    .sp-filter {
+      flex: 1;
+      min-width: 200px;
+      max-width: 240px;
+    }
+    
+    .radio-group {
+      margin: 0;
+      padding-top: 0px;
+      user-select: none;
+      
+      ::v-deep  .el-radio-button {
+        padding: 5px;
+        min-width: 150px;
+        box-shadow: none !important;
+        
+        .el-radio-button__inner {
+          width: 100%;
+          border-radius: 4px;
+          border: 1px solid $--color-primary;
+        }
+      }
+    }
+  }
+  @media screen and (max-width: 768px) {
+    .dashboard-container {
+      padding: 10px;
+    }
+  }
+  @media screen and (max-width: 520px) {
+    .dashboard-container {
+      padding: 10px 5px;
+    }
+  }
+</style>