diff options
author | Yoshie Muranaka <yoshiemuranaka@gmail.com> | 2020-05-28 23:28:29 +0300 |
---|---|---|
committer | Derick Montague <derick.montague@ibm.com> | 2020-06-17 23:36:55 +0300 |
commit | 56ee769fd5a3df93eb275859a712700a7ba54944 (patch) | |
tree | df91c4974e2c0e42eb6966b7c10728a7918c4a95 /src | |
parent | 8cb0d253809582e52bf2c388ac1f95a0713dc3f9 (diff) | |
download | webui-vue-56ee769fd5a3df93eb275859a712700a7ba54944.tar.xz |
Add Hardware status page and system table
Adds ability to see system information in table format with a row
expansion details view. Modified tables styles to add table borders.
Created global mixin for table data formatting:
- Show '--' for undefined or empty string values
- Map Redfish health status options to status-icon values
Signed-off-by: Yoshie Muranaka <yoshiemuranaka@gmail.com>
Change-Id: I5b45c37997647f5a265c1e84eb53f0b51699ee20
Diffstat (limited to 'src')
-rw-r--r-- | src/assets/styles/vendor-overrides/bootstrap/_tables.scss | 63 | ||||
-rw-r--r-- | src/components/AppNavigation/AppNavigation.vue | 2 | ||||
-rw-r--r-- | src/components/Mixins/TableDataFormatter.js | 25 | ||||
-rw-r--r-- | src/locales/en-US.json | 17 | ||||
-rw-r--r-- | src/router/index.js | 8 | ||||
-rw-r--r-- | src/store/index.js | 4 | ||||
-rw-r--r-- | src/store/modules/Health/SystemStore.js | 39 | ||||
-rw-r--r-- | src/views/Health/HardwareStatus/HardwareStatus.vue | 34 | ||||
-rw-r--r-- | src/views/Health/HardwareStatus/HardwareStatusTableStystem.vue | 120 | ||||
-rw-r--r-- | src/views/Health/HardwareStatus/index.js | 2 |
10 files changed, 301 insertions, 13 deletions
diff --git a/src/assets/styles/vendor-overrides/bootstrap/_tables.scss b/src/assets/styles/vendor-overrides/bootstrap/_tables.scss index 2372d257..b20feb0d 100644 --- a/src/assets/styles/vendor-overrides/bootstrap/_tables.scss +++ b/src/assets/styles/vendor-overrides/bootstrap/_tables.scss @@ -1,22 +1,63 @@ -table { +.table { position: relative; z-index: $zindex-dropdown; - .status-icon svg { - width: 1rem; - height: auto; - } -} -.table-light { td { - border-top: none; + border-top: 1px solid $gray-300; border-bottom: 1px solid $gray-300; + &:first-of-type { + border-left: 1px solid $gray-300; + } + &:last-of-type { + border-right: 1px solid $gray-300; + } } -} -.thead-light.thead-light { - th { + // thead-light added for specificiy + .thead-light th { border: none; color: $dark; } + + .status-icon svg { + width: 1rem; + height: auto; + } + + .b-table-has-details { + td { + border-bottom: none; + } + .table-row-expand svg { + transform: rotate(180deg); + } + } + + .b-table-details { + background-color: $light; + td { + padding-left: calc(50px + (#{$table-cell-padding} * 2)); + } + dl { + margin: 0; + } + dt { + display: inline-block; + margin-right: $spacer / 2; + } + dd { + display: inline-block; + } + } + + .table-row-expand { + width: 50px; + .btn { + padding: 0; + width: 50px; + } + svg { + fill: $dark; + } + } }
\ No newline at end of file diff --git a/src/components/AppNavigation/AppNavigation.vue b/src/components/AppNavigation/AppNavigation.vue index 8103558e..09c1eb8b 100644 --- a/src/components/AppNavigation/AppNavigation.vue +++ b/src/components/AppNavigation/AppNavigation.vue @@ -18,7 +18,7 @@ <b-nav-item to="/health/event-logs"> {{ $t('appNavigation.eventLogs') }} </b-nav-item> - <b-nav-item href="javascript:void(0)"> + <b-nav-item to="/health/hardware-status"> {{ $t('appNavigation.hardwareStatus') }} </b-nav-item> <b-nav-item to="/health/sensors"> diff --git a/src/components/Mixins/TableDataFormatter.js b/src/components/Mixins/TableDataFormatter.js new file mode 100644 index 00000000..5dbe40ad --- /dev/null +++ b/src/components/Mixins/TableDataFormatter.js @@ -0,0 +1,25 @@ +const TableDataFormatter = { + methods: { + tableFormatter(value) { + if (value === undefined || value === '') { + return '--'; + } else { + return value; + } + }, + statusIcon(status) { + switch (status) { + case 'OK': + return 'success'; + case 'Warning': + return 'warning'; + case 'Critical': + return 'danger'; + default: + return ''; + } + } + } +}; + +export default TableDataFormatter; diff --git a/src/locales/en-US.json b/src/locales/en-US.json index 8a722e39..8072770f 100644 --- a/src/locales/en-US.json +++ b/src/locales/en-US.json @@ -124,6 +124,23 @@ "successDelete": "Successfully deleted %{count} log. | Successfully deleted %{count} logs." } }, + "pageHardwareStatus": { + "system": "System", + "table": { + "id": "ID", + "health": "Health", + "partNumber": "Part number", + "serialNumber": "Serial number", + "assetTag": "Asset tag", + "description": "Description", + "indicatorLed": "Indicator LED", + "model": "Model", + "powerState": "Power state", + "statusHealthRollup": "Status (Health rollup)", + "statusState": "Status (State)", + "systemType": "System type" + } + }, "pageLdap": { "pageDescription": "Configure LDAP settings and manage role groups", "roleGroups": "Role groups", diff --git a/src/router/index.js b/src/router/index.js index e35e0f59..f67d5ee4 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -32,6 +32,14 @@ const routes = [ } }, { + path: '/health/hardware-status', + name: 'hardware-status', + component: () => import('@/views/Health/HardwareStatus'), + meta: { + title: 'appPageTitle.hardwareStatus' + } + }, + { path: '/health/sensors', name: 'sensors', component: () => import('@/views/Health/Sensors'), diff --git a/src/store/index.js b/src/store/index.js index ad55030a..ea1e9b3b 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -15,6 +15,7 @@ import NetworkSettingStore from './modules/Configuration/NetworkSettingsStore'; import EventLogStore from './modules/Health/EventLogStore'; import SensorsStore from './modules/Health/SensorsStore'; import ServerLedStore from './modules/Control/ServerLedStore'; +import SystemStore from './modules/Health/SystemStore'; import WebSocketPlugin from './plugins/WebSocketPlugin'; @@ -38,7 +39,8 @@ export default new Vuex.Store({ eventLog: EventLogStore, sensors: SensorsStore, sslCertificates: SslCertificatesStore, - serverLed: ServerLedStore + serverLed: ServerLedStore, + system: SystemStore }, plugins: [WebSocketPlugin] }); diff --git a/src/store/modules/Health/SystemStore.js b/src/store/modules/Health/SystemStore.js new file mode 100644 index 00000000..9c4c477d --- /dev/null +++ b/src/store/modules/Health/SystemStore.js @@ -0,0 +1,39 @@ +import api from '@/store/api'; + +const SystemStore = { + namespaced: true, + state: { + systems: [] + }, + getters: { + systems: state => state.systems + }, + mutations: { + setSystemInfo: (state, data) => { + const system = {}; + system.assetTag = data.AssetTag; + system.description = data.Description; + system.health = data.Status.Health; + system.id = data.Id; + system.indicatorLed = data.IndicatorLED; + system.model = data.Model; + system.partNumber = data.PartNumber; + system.powerState = data.PowerState; + system.serialNumber = data.SerialNumber; + system.healthRollup = data.Status.HealthRollup; + system.statusState = data.Status.State; + system.systemType = data.SystemType; + state.systems = [system]; + } + }, + actions: { + async getSystem({ commit }) { + return await api + .get('/redfish/v1/Systems/system') + .then(({ data }) => commit('setSystemInfo', data)) + .catch(error => console.log(error)); + } + } +}; + +export default SystemStore; diff --git a/src/views/Health/HardwareStatus/HardwareStatus.vue b/src/views/Health/HardwareStatus/HardwareStatus.vue new file mode 100644 index 00000000..9f34b534 --- /dev/null +++ b/src/views/Health/HardwareStatus/HardwareStatus.vue @@ -0,0 +1,34 @@ +<template> + <b-container fluid="xl"> + <page-title /> + + <!-- System table --> + <table-system /> + </b-container> +</template> + +<script> +import PageTitle from '@/components/Global/PageTitle'; +import TableSystem from './HardwareStatusTableStystem'; +import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin'; + +export default { + components: { PageTitle, TableSystem }, + mixins: [LoadingBarMixin], + created() { + this.startLoader(); + const systemTablePromise = new Promise(resolve => { + this.$root.$on('hardwareStatus::system::complete', () => resolve()); + }); + // Combine all child component Promises to indicate + // when page data load complete + Promise.all([systemTablePromise]).finally(() => this.endLoader()); + }, + beforeRouteLeave(to, from, next) { + // Hide loader if user navigates away from page + // before requests complete + this.hideLoader(); + next(); + } +}; +</script> diff --git a/src/views/Health/HardwareStatus/HardwareStatusTableStystem.vue b/src/views/Health/HardwareStatus/HardwareStatusTableStystem.vue new file mode 100644 index 00000000..3894eceb --- /dev/null +++ b/src/views/Health/HardwareStatus/HardwareStatusTableStystem.vue @@ -0,0 +1,120 @@ +<template> + <page-section :section-title="$t('pageHardwareStatus.system')"> + <b-table :items="systems" :fields="fields"> + <!-- Expand chevron icon --> + <template v-slot:cell(expandRow)="row"> + <b-button variant="link" @click="row.toggleDetails"> + <icon-chevron /> + </b-button> + </template> + + <!-- Health --> + <template v-slot:cell(health)="{ value }"> + <status-icon :status="statusIcon(value)" /> + {{ value }} + </template> + + <template v-slot:row-details="{ item }"> + <b-container fluid> + <b-row> + <b-col sm="6" xl="4"> + <dl> + <!-- Asset tag --> + <dt>{{ $t('pageHardwareStatus.table.assetTag') }}:</dt> + <dd>{{ tableFormatter(item.assetTag) }}</dd> + <br /> + <!-- Description --> + <dt>{{ $t('pageHardwareStatus.table.description') }}:</dt> + <dd>{{ tableFormatter(item.description) }}</dd> + <br /> + <!-- Indicator LED --> + <dt>{{ $t('pageHardwareStatus.table.indicatorLed') }}:</dt> + <dd>{{ tableFormatter(item.indicatorLed) }}</dd> + <br /> + <!-- Model --> + <dt>{{ $t('pageHardwareStatus.table.model') }}:</dt> + <dd>{{ tableFormatter(item.model) }}</dd> + </dl> + </b-col> + <b-col sm="6" xl="4"> + <dl> + <!-- Power state --> + <dt>{{ $t('pageHardwareStatus.table.powerState') }}:</dt> + <dd>{{ tableFormatter(item.powerState) }}</dd> + <br /> + <!-- Health rollup --> + <dt> + {{ $t('pageHardwareStatus.table.statusHealthRollup') }}: + </dt> + <dd>{{ tableFormatter(item.healthRollup) }}</dd> + <br /> + <!-- Status state --> + <dt>{{ $t('pageHardwareStatus.table.statusState') }}:</dt> + <dd>{{ tableFormatter(item.statusState) }}</dd> + <br /> + <!-- System type --> + <dt>{{ $t('pageHardwareStatus.table.systemType') }}:</dt> + <dd>{{ tableFormatter(item.systemType) }}</dd> + </dl> + </b-col> + </b-row> + </b-container> + </template> + </b-table> + </page-section> +</template> + +<script> +import PageSection from '@/components/Global/PageSection'; +import IconChevron from '@carbon/icons-vue/es/chevron--down/20'; + +import StatusIcon from '@/components/Global/StatusIcon'; +import TableDataFormatter from '@/components/Mixins/TableDataFormatter'; + +export default { + components: { IconChevron, PageSection, StatusIcon }, + mixins: [TableDataFormatter], + data() { + return { + fields: [ + { + key: 'expandRow', + label: '', + tdClass: 'table-row-expand' + }, + { + key: 'id', + label: this.$t('pageHardwareStatus.table.id'), + formatter: this.tableFormatter + }, + { + key: 'health', + label: this.$t('pageHardwareStatus.table.health'), + formatter: this.tableFormatter + }, + { + key: 'partNumber', + label: this.$t('pageHardwareStatus.table.partNumber'), + formatter: this.tableFormatter + }, + { + key: 'serialNumber', + label: this.$t('pageHardwareStatus.table.serialNumber'), + formatter: this.tableFormatter + } + ] + }; + }, + computed: { + systems() { + return this.$store.getters['system/systems']; + } + }, + created() { + this.$store.dispatch('system/getSystem').finally(() => { + // Emit intial data fetch complete to parent component + this.$root.$emit('hardwareStatus::system::complete'); + }); + } +}; +</script> diff --git a/src/views/Health/HardwareStatus/index.js b/src/views/Health/HardwareStatus/index.js new file mode 100644 index 00000000..25d4551c --- /dev/null +++ b/src/views/Health/HardwareStatus/index.js @@ -0,0 +1,2 @@ +import HardwareStatus from './HardwareStatus.vue'; +export default HardwareStatus; |