diff options
author | Sandeepa Singh <sandeepa.singh@ibm.com> | 2021-07-06 13:59:10 +0300 |
---|---|---|
committer | Derick Montague <derick.montague@ibm.com> | 2021-08-10 22:20:42 +0300 |
commit | 7affc529b7fba41193c4d48764707e9961cdd22d (patch) | |
tree | d198c2026a40faf07d622492297aed488b17fccb /src/views/HardwareStatus | |
parent | 828dda9b187684902710bb11621eca27bf0c6eec (diff) | |
download | webui-vue-7affc529b7fba41193c4d48764707e9961cdd22d.tar.xz |
IA update: Update health section
This is the second update to information architecture changes and
has the following changes:
- Health section is updated to hardware status section
- Hardware status page is updated to inventory and LEDs page
- Route for sensors page has been updated
Signed-off-by: Sandeepa Singh <sandeepa.singh@ibm.com>
Change-Id: Ia1ba3a15a243a00f59a2ec646132436eb355a999
Diffstat (limited to 'src/views/HardwareStatus')
11 files changed, 1885 insertions, 0 deletions
diff --git a/src/views/HardwareStatus/Inventory/Inventory.vue b/src/views/HardwareStatus/Inventory/Inventory.vue new file mode 100644 index 00000000..d8b46b25 --- /dev/null +++ b/src/views/HardwareStatus/Inventory/Inventory.vue @@ -0,0 +1,180 @@ +<template> + <b-container fluid="xl"> + <page-title /> + + <!-- Quicklinks section --> + <page-section :section-title="$t('pageInventory.quicklinkTitle')"> + <b-row class="w-75"> + <b-col v-for="column in quicklinkColumns" :key="column.id" xl="4"> + <div v-for="item in column" :key="item.id"> + <b-link + :href="item.href" + :data-ref="item.dataRef" + @click.prevent="scrollToOffset" + > + <jump-link /> {{ item.linkText }} + </b-link> + </div> + </b-col> + </b-row> + </page-section> + + <!-- System table --> + <table-system ref="system" /> + + <!-- BMC manager table --> + <table-bmc-manager ref="bmc" /> + + <!-- Chassis table --> + <table-chassis ref="chassis" /> + + <!-- DIMM slot table --> + <table-dimm-slot ref="dimms" /> + + <!-- Fans table --> + <table-fans ref="fans" /> + + <!-- Power supplies table --> + <table-power-supplies ref="powerSupply" /> + + <!-- Processors table --> + <table-processors ref="processors" /> + </b-container> +</template> + +<script> +import PageTitle from '@/components/Global/PageTitle'; +import TableSystem from './InventoryTableSystem'; +import TablePowerSupplies from './InventoryTablePowerSupplies'; +import TableDimmSlot from './InventoryTableDimmSlot'; +import TableFans from './InventoryTableFans'; +import TableBmcManager from './InventoryTableBmcManager'; +import TableChassis from './InventoryTableChassis'; +import TableProcessors from './InventoryTableProcessors'; +import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin'; +import PageSection from '@/components/Global/PageSection'; +import JumpLink16 from '@carbon/icons-vue/es/jump-link/16'; + +import JumpLinkMixin from '@/components/Mixins/JumpLinkMixin'; + +import { chunk } from 'lodash'; + +export default { + components: { + PageTitle, + TableDimmSlot, + TablePowerSupplies, + TableSystem, + TableFans, + TableBmcManager, + TableChassis, + TableProcessors, + PageSection, + JumpLink: JumpLink16, + }, + mixins: [LoadingBarMixin, JumpLinkMixin], + beforeRouteLeave(to, from, next) { + // Hide loader if user navigates away from page + // before requests complete + this.hideLoader(); + next(); + }, + data() { + return { + links: [ + { + id: 'system', + dataRef: 'system', + href: '#system', + linkText: this.$t('pageInventory.system'), + }, + { + id: 'bmc', + dataRef: 'bmc', + href: '#bmc', + linkText: this.$t('pageInventory.bmcManager'), + }, + { + id: 'chassis', + dataRef: 'chassis', + href: '#chassis', + linkText: this.$t('pageInventory.chassis'), + }, + { + id: 'dimms', + dataRef: 'dimms', + href: '#dimms', + linkText: this.$t('pageInventory.dimmSlot'), + }, + { + id: 'fans', + dataRef: 'fans', + href: '#fans', + linkText: this.$t('pageInventory.fans'), + }, + { + id: 'powerSupply', + dataRef: 'powerSupply', + href: '#powerSupply', + linkText: this.$t('pageInventory.powerSupplies'), + }, + { + id: 'processors', + dataRef: 'processors', + href: '#processors', + linkText: this.$t('pageInventory.processors'), + }, + { + id: 'system', + dataRef: 'system', + href: '#system', + linkText: this.$t('pageInventory.system'), + }, + ], + }; + }, + computed: { + quicklinkColumns() { + // Chunk links array to 3 array's to display 3 items per column + return chunk(this.links, 3); + }, + }, + created() { + this.startLoader(); + const systemTablePromise = new Promise((resolve) => { + this.$root.$on('hardware-status-system-complete', () => resolve()); + }); + const bmcManagerTablePromise = new Promise((resolve) => { + this.$root.$on('hardware-status-bmc-manager-complete', () => resolve()); + }); + const chassisTablePromise = new Promise((resolve) => { + this.$root.$on('hardware-status-chassis-complete', () => resolve()); + }); + const dimmSlotTablePromise = new Promise((resolve) => { + this.$root.$on('hardware-status-dimm-slot-complete', () => resolve()); + }); + const fansTablePromise = new Promise((resolve) => { + this.$root.$on('hardware-status-fans-complete', () => resolve()); + }); + const powerSuppliesTablePromise = new Promise((resolve) => { + this.$root.$on('hardware-status-power-supplies-complete', () => + resolve() + ); + }); + const processorsTablePromise = new Promise((resolve) => { + this.$root.$on('hardware-status-processors-complete', () => resolve()); + }); + // Combine all child component Promises to indicate + // when page data load complete + Promise.all([ + systemTablePromise, + bmcManagerTablePromise, + chassisTablePromise, + dimmSlotTablePromise, + fansTablePromise, + powerSuppliesTablePromise, + processorsTablePromise, + ]).finally(() => this.endLoader()); + }, +}; +</script> diff --git a/src/views/HardwareStatus/Inventory/InventoryTableBmcManager.vue b/src/views/HardwareStatus/Inventory/InventoryTableBmcManager.vue new file mode 100644 index 00000000..6533fd90 --- /dev/null +++ b/src/views/HardwareStatus/Inventory/InventoryTableBmcManager.vue @@ -0,0 +1,248 @@ +<template> + <page-section :section-title="$t('pageInventory.bmcManager')"> + <b-table + responsive="md" + hover + :items="items" + :fields="fields" + show-empty + :empty-text="$t('global.table.emptyMessage')" + > + <!-- Expand chevron icon --> + <template #cell(expandRow)="row"> + <b-button + variant="link" + data-test-id="hardwareStatus-button-expandBmc" + :title="expandRowLabel" + class="btn-icon-only" + @click="toggleRowDetails(row)" + > + <icon-chevron /> + <span class="sr-only">{{ expandRowLabel }}</span> + </b-button> + </template> + + <!-- Health --> + <template #cell(health)="{ value }"> + <status-icon :status="statusIcon(value)" /> + {{ value }} + </template> + + <!-- Toggle identify LED --> + <template #cell(identifyLed)="row"> + <b-form-checkbox + v-if="hasIdentifyLed(row.item.identifyLed)" + v-model="row.item.identifyLed" + name="switch" + switch + @change="toggleIdentifyLedValue(row.item)" + > + <span v-if="row.item.identifyLed"> + {{ $t('global.status.on') }} + </span> + <span v-else> {{ $t('global.status.off') }} </span> + </b-form-checkbox> + <div v-else>--</div> + </template> + + <template #row-details="{ item }"> + <b-container fluid> + <b-row> + <b-col class="mt-2" sm="6" xl="6"> + <dl> + <!-- Name --> + <dt>{{ $t('pageInventory.table.name') }}:</dt> + <dd>{{ tableFormatter(item.name) }}</dd> + <!-- Part number --> + <dt>{{ $t('pageInventory.table.partNumber') }}:</dt> + <dd>{{ tableFormatter(item.partNumber) }}</dd> + <!-- Serial number --> + <dt>{{ $t('pageInventory.table.serialNumber') }}:</dt> + <dd>{{ tableFormatter(item.serialNumber) }}</dd> + <!-- Spare part number --> + <dt>{{ $t('pageInventory.table.sparePartNumber') }}:</dt> + <dd>{{ tableFormatter(item.sparePartNumber) }}</dd> + <!-- Model --> + <dt>{{ $t('pageInventory.table.model') }}:</dt> + <dd>{{ tableFormatter(item.model) }}</dd> + <!-- UUID --> + <dt>{{ $t('pageInventory.table.uuid') }}:</dt> + <dd>{{ tableFormatter(item.uuid) }}</dd> + <!-- Service entry point UUID --> + <dt>{{ $t('pageInventory.table.serviceEntryPointUuid') }}:</dt> + <dd>{{ tableFormatter(item.serviceEntryPointUuid) }}</dd> + </dl> + </b-col> + <b-col class="mt-2" sm="6" xl="6"> + <dl> + <!-- Status state --> + <dt>{{ $t('pageInventory.table.statusState') }}:</dt> + <dd>{{ tableFormatter(item.statusState) }}</dd> + <!-- Power state --> + <dt>{{ $t('pageInventory.table.power') }}:</dt> + <dd>{{ tableFormatter(item.powerState) }}</dd> + <!-- Health rollup --> + <dt>{{ $t('pageInventory.table.healthRollup') }}:</dt> + <dd>{{ tableFormatter(item.healthRollup) }}</dd> + <!-- BMC date and time --> + <dt>{{ $t('pageInventory.table.bmcDateTime') }}:</dt> + <dd> + {{ item.dateTime | formatDate }} + {{ item.dateTime | formatTime }} + </dd> + <!-- Reset date and time --> + <dt>{{ $t('pageInventory.table.lastResetTime') }}:</dt> + <dd> + {{ item.lastResetTime | formatDate }} + {{ item.lastResetTime | formatTime }} + </dd> + </dl> + </b-col> + </b-row> + <div class="section-divider mb-3 mt-3"></div> + <b-row> + <b-col class="mt-2" sm="6" xl="6"> + <dl> + <!-- Manufacturer --> + <dt>{{ $t('pageInventory.table.manufacturer') }}:</dt> + <dd>{{ tableFormatter(item.manufacturer) }}</dd> + <!-- Description --> + <dt>{{ $t('pageInventory.table.description') }}:</dt> + <dd>{{ tableFormatter(item.description) }}</dd> + <!-- Manager type --> + <dt>{{ $t('pageInventory.table.managerType') }}:</dt> + <dd>{{ tableFormatter(item.managerType) }}</dd> + </dl> + </b-col> + <b-col class="mt-2" sm="6" xl="6"> + <!-- Firmware Version --> + <dl> + <dt>{{ $t('pageHardwareStatus.table.firmwareVersion') }}:</dt> + <dd>{{ item.firmwareVersion }}</dd> + </dl> + <!-- Graphical console --> + <p class="mt-1 mb-2 h6 float-none m-0"> + {{ $t('pageHardwareStatus.table.graphicalConsole') }} + </p> + <dl class="ml-4"> + <dt> + {{ $t('pageHardwareStatus.table.connectTypesSupported') }}: + </dt> + <dt>{{ $t('pageInventory.table.connectTypesSupported') }}:</dt> + <dd> + {{ tableFormatterArray(item.graphicalConsoleConnectTypes) }} + </dd> + <dt>{{ $t('pageInventory.table.maxConcurrentSessions') }}:</dt> + <dd> + {{ tableFormatter(item.graphicalConsoleMaxSessions) }} + </dd> + <dt>{{ $t('pageInventory.table.serviceEnabled') }}:</dt> + <dd> + {{ tableFormatter(item.graphicalConsoleEnabled) }} + </dd> + </dl> + <!-- Serial console --> + <p class="mt-1 mb-2 h6 float-none m-0"> + {{ $t('pageHardwareStatus.table.serialConsole') }} + </p> + <dl class="ml-4"> + <dt> + {{ $t('pageHardwareStatus.table.connectTypesSupported') }}: + </dt> + <dt>{{ $t('pageInventory.table.connectTypesSupported') }}:</dt> + <dd> + {{ tableFormatterArray(item.serialConsoleConnectTypes) }} + </dd> + <dt>{{ $t('pageInventory.table.maxConcurrentSessions') }}:</dt> + <dd>{{ tableFormatter(item.serialConsoleMaxSessions) }}</dd> + <dt>{{ $t('pageInventory.table.serviceEnabled') }}:</dt> + <dd>{{ tableFormatter(item.serialConsoleEnabled) }}</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 BVToastMixin from '@/components/Mixins/BVToastMixin'; +import TableRowExpandMixin, { + expandRowLabel, +} from '@/components/Mixins/TableRowExpandMixin'; +import TableDataFormatterMixin from '@/components/Mixins/TableDataFormatterMixin'; + +export default { + components: { IconChevron, PageSection, StatusIcon }, + mixins: [BVToastMixin, TableRowExpandMixin, TableDataFormatterMixin], + data() { + return { + fields: [ + { + key: 'expandRow', + label: '', + tdClass: 'table-row-expand', + }, + { + key: 'id', + label: this.$t('pageInventory.table.id'), + formatter: this.tableFormatter, + }, + { + key: 'health', + label: this.$t('pageInventory.table.health'), + formatter: this.tableFormatter, + }, + { + key: 'locationNumber', + label: this.$t('pageInventory.table.locationNumber'), + formatter: this.tableFormatter, + }, + { + key: 'identifyLed', + label: this.$t('pageInventory.table.identifyLed'), + formatter: this.tableFormatter, + }, + ], + expandRowLabel: expandRowLabel, + }; + }, + computed: { + bmc() { + return this.$store.getters['bmc/bmc']; + }, + items() { + if (this.bmc) { + return [this.bmc]; + } else { + return []; + } + }, + }, + created() { + this.$store.dispatch('bmc/getBmcInfo').finally(() => { + // Emit initial data fetch complete to parent component + this.$root.$emit('hardware-status-bmc-manager-complete'); + }); + }, + methods: { + toggleIdentifyLedValue(row) { + this.$store + .dispatch('bmc/updateIdentifyLedValue', { + uri: row.uri, + identifyLed: row.identifyLed, + }) + .catch(({ message }) => this.errorToast(message)); + }, + // TO DO: remove hasIdentifyLed method once the following story is merged: + // https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/43179 + hasIdentifyLed(identifyLed) { + return typeof identifyLed === 'boolean'; + }, + }, +}; +</script> diff --git a/src/views/HardwareStatus/Inventory/InventoryTableChassis.vue b/src/views/HardwareStatus/Inventory/InventoryTableChassis.vue new file mode 100644 index 00000000..0d535027 --- /dev/null +++ b/src/views/HardwareStatus/Inventory/InventoryTableChassis.vue @@ -0,0 +1,188 @@ +<template> + <page-section :section-title="$t('pageInventory.chassis')"> + <b-table + responsive="md" + hover + :items="chassis" + :fields="fields" + show-empty + :empty-text="$t('global.table.emptyMessage')" + > + <!-- Expand chevron icon --> + <template #cell(expandRow)="row"> + <b-button + variant="link" + data-test-id="hardwareStatus-button-expandChassis" + :title="expandRowLabel" + class="btn-icon-only" + @click="toggleRowDetails(row)" + > + <icon-chevron /> + <span class="sr-only">{{ expandRowLabel }}</span> + </b-button> + </template> + + <!-- Health --> + <template #cell(health)="{ value }"> + <status-icon :status="statusIcon(value)" /> + {{ value }} + </template> + <!-- Toggle identify LED --> + <template #cell(identifyLed)="row"> + <b-form-checkbox + v-if="hasIdentifyLed(row.item.identifyLed)" + v-model="row.item.identifyLed" + name="switch" + switch + @change="toggleIdentifyLedValue(row.item)" + > + <span v-if="row.item.identifyLed"> + {{ $t('global.status.on') }} + </span> + <span v-else> {{ $t('global.status.off') }} </span> + </b-form-checkbox> + <div v-else>--</div> + </template> + <template #row-details="{ item }"> + <b-container fluid> + <b-row> + <b-col class="mt-2" sm="6" xl="6"> + <dl> + <!-- Name --> + <dt>{{ $t('pageInventory.table.name') }}:</dt> + <dd>{{ tableFormatter(item.name) }}</dd> + <!-- Part number --> + <dt>{{ $t('pageInventory.table.partNumber') }}:</dt> + <dd>{{ tableFormatter(item.partNumber) }}</dd> + <!-- Serial Number --> + <dt>{{ $t('pageInventory.table.serialNumber') }}:</dt> + <dd>{{ tableFormatter(item.serialNumber) }}</dd> + <!-- Model --> + <dt>{{ $t('pageInventory.table.model') }}:</dt> + <dd class="mb-2"> + {{ tableFormatter(item.model) }} + </dd> + <!-- Asset tag --> + <dt>{{ $t('pageInventory.table.assetTag') }}:</dt> + <dd class="mb-2"> + {{ tableFormatter(item.assetTag) }} + </dd> + </dl> + </b-col> + <b-col class="mt-2" sm="6" xl="6"> + <dl> + <!-- Status state --> + <dt>{{ $t('pageInventory.table.statusState') }}:</dt> + <dd>{{ tableFormatter(item.statusState) }}</dd> + <!-- Power state --> + <dt>{{ $t('pageInventory.table.power') }}:</dt> + <dd>{{ tableFormatter(item.power) }}</dd> + <!-- Health rollup --> + <dt>{{ $t('pageInventory.table.healthRollup') }}:</dt> + <dd>{{ tableFormatter(item.healthRollup) }}</dd> + </dl> + </b-col> + </b-row> + <div class="section-divider mb-3 mt-3"></div> + <b-row> + <b-col class="mt-2" sm="6" xl="6"> + <dl> + <!-- Manufacturer --> + <dt>{{ $t('pageInventory.table.manufacturer') }}:</dt> + <dd>{{ tableFormatter(item.manufacturer) }}</dd> + <!-- Chassis Type --> + <dt>{{ $t('pageInventory.table.chassisType') }}:</dt> + <dd>{{ tableFormatter(item.chassisType) }}</dd> + </dl> + </b-col> + <b-col class="mt-2" sm="6" xl="6"> + <dl> + <!-- Min power --> + <dt>{{ $t('pageInventory.table.minPowerWatts') }}:</dt> + <dd>{{ tableFormatter(item.minPowerWatts) }}</dd> + <!-- Max power --> + <dt>{{ $t('pageInventory.table.maxPowerWatts') }}:</dt> + <dd>{{ tableFormatter(item.maxPowerWatts) }}</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 BVToastMixin from '@/components/Mixins/BVToastMixin'; +import StatusIcon from '@/components/Global/StatusIcon'; + +import TableRowExpandMixin, { + expandRowLabel, +} from '@/components/Mixins/TableRowExpandMixin'; +import TableDataFormatterMixin from '@/components/Mixins/TableDataFormatterMixin'; + +export default { + components: { IconChevron, PageSection, StatusIcon }, + mixins: [BVToastMixin, TableRowExpandMixin, TableDataFormatterMixin], + data() { + return { + fields: [ + { + key: 'expandRow', + label: '', + tdClass: 'table-row-expand', + }, + { + key: 'id', + label: this.$t('pageInventory.table.id'), + formatter: this.tableFormatter, + }, + { + key: 'health', + label: this.$t('pageInventory.table.health'), + formatter: this.tableFormatter, + tdClass: 'text-nowrap', + }, + { + key: 'locationNumber', + label: this.$t('pageInventory.table.locationNumber'), + formatter: this.tableFormatter, + }, + { + key: 'identifyLed', + label: this.$t('pageInventory.table.identifyLed'), + formatter: this.tableFormatter, + }, + ], + expandRowLabel: expandRowLabel, + }; + }, + computed: { + chassis() { + return this.$store.getters['chassis/chassis']; + }, + }, + created() { + this.$store.dispatch('chassis/getChassisInfo').finally(() => { + // Emit initial data fetch complete to parent component + this.$root.$emit('hardware-status-chassis-complete'); + }); + }, + methods: { + toggleIdentifyLedValue(row) { + this.$store + .dispatch('chassis/updateIdentifyLedValue', { + uri: row.uri, + identifyLed: row.identifyLed, + }) + .catch(({ message }) => this.errorToast(message)); + }, + // TO DO: Remove this method when the LocationIndicatorActive is added from backend. + hasIdentifyLed(identifyLed) { + return typeof identifyLed === 'boolean'; + }, + }, +}; +</script> diff --git a/src/views/HardwareStatus/Inventory/InventoryTableDimmSlot.vue b/src/views/HardwareStatus/Inventory/InventoryTableDimmSlot.vue new file mode 100644 index 00000000..ed370b53 --- /dev/null +++ b/src/views/HardwareStatus/Inventory/InventoryTableDimmSlot.vue @@ -0,0 +1,162 @@ +<template> + <page-section :section-title="$t('pageInventory.dimmSlot')"> + <b-row class="align-items-end"> + <b-col sm="6" md="5" xl="4"> + <search + @change-search="onChangeSearchInput" + @clear-search="onClearSearchInput" + /> + </b-col> + <b-col sm="6" md="3" xl="2"> + <table-cell-count + :filtered-items-count="filteredRows" + :total-number-of-cells="dimms.length" + ></table-cell-count> + </b-col> + </b-row> + <b-table + sort-icon-left + no-sort-reset + hover + sort-by="health" + responsive="md" + show-empty + :items="dimms" + :fields="fields" + :sort-desc="true" + :sort-compare="sortCompare" + :filter="searchFilter" + :empty-text="$t('global.table.emptyMessage')" + :empty-filtered-text="$t('global.table.emptySearchMessage')" + @filtered="onFiltered" + > + <!-- Expand chevron icon --> + <template #cell(expandRow)="row"> + <b-button + variant="link" + data-test-id="hardwareStatus-button-expandDimms" + :title="expandRowLabel" + class="btn-icon-only" + @click="toggleRowDetails(row)" + > + <icon-chevron /> + <span class="sr-only">{{ expandRowLabel }}</span> + </b-button> + </template> + + <!-- Health --> + <template #cell(health)="{ value }"> + <status-icon :status="statusIcon(value)" /> + {{ value }} + </template> + + <template #row-details="{ item }"> + <b-container fluid> + <b-row> + <b-col sm="6" xl="4"> + <dl> + <!-- Status state --> + <dt>{{ $t('pageInventory.table.statusState') }}:</dt> + <dd>{{ tableFormatter(item.statusState) }}</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 TableCellCount from '@/components/Global/TableCellCount'; + +import TableDataFormatterMixin from '@/components/Mixins/TableDataFormatterMixin'; +import TableSortMixin from '@/components/Mixins/TableSortMixin'; +import Search from '@/components/Global/Search'; +import SearchFilterMixin, { + searchFilter, +} from '@/components/Mixins/SearchFilterMixin'; +import TableRowExpandMixin, { + expandRowLabel, +} from '@/components/Mixins/TableRowExpandMixin'; + +export default { + components: { IconChevron, PageSection, StatusIcon, Search, TableCellCount }, + mixins: [ + TableRowExpandMixin, + TableDataFormatterMixin, + TableSortMixin, + SearchFilterMixin, + ], + data() { + return { + fields: [ + { + key: 'expandRow', + label: '', + tdClass: 'table-row-expand', + sortable: false, + }, + { + key: 'id', + label: this.$t('pageInventory.table.id'), + formatter: this.tableFormatter, + sortable: true, + }, + { + key: 'health', + label: this.$t('pageInventory.table.health'), + formatter: this.tableFormatter, + sortable: true, + tdClass: 'text-nowrap', + }, + { + key: 'partNumber', + label: this.$t('pageInventory.table.partNumber'), + formatter: this.tableFormatter, + sortable: true, + }, + { + key: 'serialNumber', + label: this.$t('pageInventory.table.serialNumber'), + formatter: this.tableFormatter, + sortable: true, + }, + ], + searchFilter: searchFilter, + searchTotalFilteredRows: 0, + expandRowLabel: expandRowLabel, + }; + }, + computed: { + filteredRows() { + return this.searchFilter + ? this.searchTotalFilteredRows + : this.dimms.length; + }, + dimms() { + return this.$store.getters['memory/dimms']; + }, + }, + created() { + this.$store.dispatch('memory/getDimms').finally(() => { + // Emit initial data fetch complete to parent component + this.$root.$emit('hardware-status-dimm-slot-complete'); + }); + }, + methods: { + sortCompare(a, b, key) { + if (key === 'health') { + return this.sortStatus(a, b, key); + } + }, + onFiltered(filteredItems) { + this.searchTotalFilteredRows = filteredItems.length; + }, + }, +}; +</script> diff --git a/src/views/HardwareStatus/Inventory/InventoryTableFans.vue b/src/views/HardwareStatus/Inventory/InventoryTableFans.vue new file mode 100644 index 00000000..8f1003fe --- /dev/null +++ b/src/views/HardwareStatus/Inventory/InventoryTableFans.vue @@ -0,0 +1,187 @@ +<template> + <page-section :section-title="$t('pageInventory.fans')"> + <b-row class="align-items-end"> + <b-col sm="6" md="5" xl="4"> + <search + @change-search="onChangeSearchInput" + @clear-search="onClearSearchInput" + /> + </b-col> + <b-col sm="6" md="3" xl="2"> + <table-cell-count + :filtered-items-count="filteredRows" + :total-number-of-cells="fans.length" + ></table-cell-count> + </b-col> + </b-row> + <b-table + sort-icon-left + no-sort-reset + hover + responsive="md" + sort-by="health" + show-empty + :items="fans" + :fields="fields" + :sort-desc="true" + :sort-compare="sortCompare" + :filter="searchFilter" + :empty-text="$t('global.table.emptyMessage')" + :empty-filtered-text="$t('global.table.emptySearchMessage')" + @filtered="onFiltered" + > + <!-- Expand chevron icon --> + <template #cell(expandRow)="row"> + <b-button + variant="link" + data-test-id="hardwareStatus-button-expandFans" + :title="expandRowLabel" + class="btn-icon-only" + @click="toggleRowDetails(row)" + > + <icon-chevron /> + <span class="sr-only">{{ expandRowLabel }}</span> + </b-button> + </template> + + <!-- Health --> + <template #cell(health)="{ value }"> + <status-icon :status="statusIcon(value)" /> + {{ value }} + </template> + + <template #row-details="{ item }"> + <b-container fluid> + <b-row> + <b-col sm="6" xl="4"> + <dl> + <!-- Name --> + <dt>{{ $t('pageInventory.table.name') }}:</dt> + <dd>{{ tableFormatter(item.name) }}</dd> + </dl> + <dl> + <!-- Serial number --> + <dt>{{ $t('pageInventory.table.serialNumber') }}:</dt> + <dd>{{ tableFormatter(item.serialNumber) }}</dd> + </dl> + <dl> + <!-- Part number --> + <dt>{{ $t('pageInventory.table.partNumber') }}:</dt> + <dd>{{ tableFormatter(item.partNumber) }}</dd> + </dl> + <dl> + <!-- Fan speed --> + <dt>{{ $t('pageInventory.table.fanSpeed') }}:</dt> + <dd>{{ tableFormatter(item.speed) }}</dd> + </dl> + </b-col> + <b-col sm="6" xl="4"> + <dl> + <!-- Status state --> + <dt>{{ $t('pageInventory.table.statusState') }}:</dt> + <dd>{{ tableFormatter(item.statusState) }}</dd> + </dl> + <dl> + <!-- Health Rollup state --> + <dt>{{ $t('pageInventory.table.statusHealthRollup') }}:</dt> + <dd>{{ tableFormatter(item.healthRollup) }}</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 TableCellCount from '@/components/Global/TableCellCount'; + +import StatusIcon from '@/components/Global/StatusIcon'; +import TableDataFormatterMixin from '@/components/Mixins/TableDataFormatterMixin'; +import TableSortMixin from '@/components/Mixins/TableSortMixin'; +import Search from '@/components/Global/Search'; +import SearchFilterMixin, { + searchFilter, +} from '@/components/Mixins/SearchFilterMixin'; +import TableRowExpandMixin, { + expandRowLabel, +} from '@/components/Mixins/TableRowExpandMixin'; + +export default { + components: { IconChevron, PageSection, StatusIcon, Search, TableCellCount }, + mixins: [ + TableRowExpandMixin, + TableDataFormatterMixin, + TableSortMixin, + SearchFilterMixin, + ], + data() { + return { + fields: [ + { + key: 'expandRow', + label: '', + tdClass: 'table-row-expand', + sortable: false, + }, + { + key: 'id', + label: this.$t('pageInventory.table.id'), + formatter: this.tableFormatter, + sortable: true, + }, + { + key: 'health', + label: this.$t('pageInventory.table.health'), + formatter: this.tableFormatter, + sortable: true, + tdClass: 'text-nowrap', + }, + { + key: 'partNumber', + label: this.$t('pageInventory.table.partNumber'), + formatter: this.tableFormatter, + sortable: true, + }, + { + key: 'serialNumber', + label: this.$t('pageInventory.table.serialNumber'), + formatter: this.tableFormatter, + }, + ], + searchFilter: searchFilter, + searchTotalFilteredRows: 0, + expandRowLabel: expandRowLabel, + }; + }, + computed: { + filteredRows() { + return this.searchFilter + ? this.searchTotalFilteredRows + : this.fans.length; + }, + fans() { + return this.$store.getters['fan/fans']; + }, + }, + created() { + this.$store.dispatch('fan/getFanInfo').finally(() => { + // Emit initial data fetch complete to parent component + this.$root.$emit('hardware-status-fans-complete'); + }); + }, + methods: { + sortCompare(a, b, key) { + if (key === 'health') { + return this.sortStatus(a, b, key); + } + }, + onFiltered(filteredItems) { + this.searchTotalFilteredRows = filteredItems.length; + }, + }, +}; +</script> diff --git a/src/views/HardwareStatus/Inventory/InventoryTablePowerSupplies.vue b/src/views/HardwareStatus/Inventory/InventoryTablePowerSupplies.vue new file mode 100644 index 00000000..1ef5aa71 --- /dev/null +++ b/src/views/HardwareStatus/Inventory/InventoryTablePowerSupplies.vue @@ -0,0 +1,205 @@ +<template> + <page-section :section-title="$t('pageInventory.powerSupplies')"> + <b-row class="align-items-end"> + <b-col sm="6" md="5" xl="4"> + <search + @change-search="onChangeSearchInput" + @clear-search="onClearSearchInput" + /> + </b-col> + <b-col sm="6" md="3" xl="2"> + <table-cell-count + :filtered-items-count="filteredRows" + :total-number-of-cells="powerSupplies.length" + ></table-cell-count> + </b-col> + </b-row> + <b-table + sort-icon-left + no-sort-reset + hover + responsive="md" + sort-by="health" + show-empty + :items="powerSupplies" + :fields="fields" + :sort-desc="true" + :sort-compare="sortCompare" + :filter="searchFilter" + :empty-text="$t('global.table.emptyMessage')" + :empty-filtered-text="$t('global.table.emptySearchMessage')" + @filtered="onFiltered" + > + <!-- Expand chevron icon --> + <template #cell(expandRow)="row"> + <b-button + variant="link" + data-test-id="hardwareStatus-button-expandPowerSupplies" + :title="expandRowLabel" + class="btn-icon-only" + @click="toggleRowDetails(row)" + > + <icon-chevron /> + <span class="sr-only">{{ expandRowLabel }}</span> + </b-button> + </template> + + <!-- Health --> + <template #cell(health)="{ value }"> + <status-icon :status="statusIcon(value)" /> + {{ value }} + </template> + + <template #row-details="{ item }"> + <b-container fluid> + <b-row> + <b-col sm="6" xl="4"> + <dl> + <!-- Name --> + <dt>{{ $t('pageInventory.table.name') }}:</dt> + <dd>{{ tableFormatter(item.name) }}</dd> + <!-- Part number --> + <dt>{{ $t('pageInventory.table.partNumber') }}:</dt> + <dd>{{ tableFormatter(item.partNumber) }}</dd> + <!-- Serial number --> + <dt>{{ $t('pageInventory.table.serialNumber') }}:</dt> + <dd>{{ tableFormatter(item.serialNumber) }}</dd> + <!-- Spare part number --> + <dt>{{ $t('pageInventory.table.sparePartNumber') }}:</dt> + <dd>{{ tableFormatter(item.sparePartNumber) }}</dd> + <!-- Model --> + <dt>{{ $t('pageInventory.table.model') }}:</dt> + <dd>{{ tableFormatter(item.model) }}</dd> + </dl> + </b-col> + <b-col sm="6" xl="4"> + <dl> + <!-- Status state --> + <dt>{{ $t('pageInventory.table.statusState') }}:</dt> + <dd>{{ tableFormatter(item.statusState) }}</dd> + <!-- Status Health rollup state --> + <dt>{{ $t('pageInventory.table.statusHealthRollup') }}:</dt> + <dd>{{ tableFormatter(item.statusHealth) }}</dd> + <!-- Efficiency percent --> + <dt>{{ $t('pageInventory.table.efficiencyPercent') }}:</dt> + <dd>{{ tableFormatter(item.efficiencyPercent) }}</dd> + <!-- Power input watts --> + <dt>{{ $t('pageInventory.table.powerInputWatts') }}:</dt> + <dd>{{ tableFormatter(item.powerInputWatts) }}</dd> + </dl> + </b-col> + </b-row> + <div class="section-divider mb-3 mt-3"></div> + <b-row> + <b-col sm="6" xl="4"> + <dl> + <!-- Manufacturer --> + <dt>{{ $t('pageInventory.table.manufacturer') }}:</dt> + <dd>{{ tableFormatter(item.manufacturer) }}</dd> + </dl> + </b-col> + <b-col sm="6" xl="4"> + <dl> + <!-- Firmware version --> + <dt>{{ $t('pageInventory.table.firmwareVersion') }}:</dt> + <dd>{{ tableFormatter(item.firmwareVersion) }}</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 TableCellCount from '@/components/Global/TableCellCount'; +import TableDataFormatterMixin from '@/components/Mixins/TableDataFormatterMixin'; +import TableSortMixin from '@/components/Mixins/TableSortMixin'; +import Search from '@/components/Global/Search'; +import SearchFilterMixin, { + searchFilter, +} from '@/components/Mixins/SearchFilterMixin'; +import TableRowExpandMixin, { + expandRowLabel, +} from '@/components/Mixins/TableRowExpandMixin'; + +export default { + components: { IconChevron, PageSection, StatusIcon, Search, TableCellCount }, + mixins: [ + TableRowExpandMixin, + TableDataFormatterMixin, + TableSortMixin, + SearchFilterMixin, + ], + data() { + return { + fields: [ + { + key: 'expandRow', + label: '', + tdClass: 'table-row-expand', + sortable: false, + }, + { + key: 'id', + label: this.$t('pageInventory.table.id'), + formatter: this.tableFormatter, + sortable: true, + }, + { + key: 'health', + label: this.$t('pageInventory.table.health'), + formatter: this.tableFormatter, + sortable: true, + tdClass: 'text-nowrap', + }, + { + key: 'locationNumber', + label: this.$t('pageInventory.table.locationNumber'), + formatter: this.tableFormatter, + sortable: true, + }, + { + key: 'identifyLed', + label: this.$t('pageInventory.table.identifyLed'), + formatter: this.tableFormatter, + }, + ], + searchFilter: searchFilter, + searchTotalFilteredRows: 0, + expandRowLabel: expandRowLabel, + }; + }, + computed: { + filteredRows() { + return this.searchFilter + ? this.searchTotalFilteredRows + : this.powerSupplies.length; + }, + powerSupplies() { + return this.$store.getters['powerSupply/powerSupplies']; + }, + }, + created() { + this.$store.dispatch('powerSupply/getAllPowerSupplies').finally(() => { + // Emit initial data fetch complete to parent component + this.$root.$emit('hardware-status-power-supplies-complete'); + }); + }, + methods: { + sortCompare(a, b, key) { + if (key === 'health') { + return this.sortStatus(a, b, key); + } + }, + onFiltered(filteredItems) { + this.searchTotalFilteredRows = filteredItems.length; + }, + }, +}; +</script> diff --git a/src/views/HardwareStatus/Inventory/InventoryTableProcessors.vue b/src/views/HardwareStatus/Inventory/InventoryTableProcessors.vue new file mode 100644 index 00000000..85f12723 --- /dev/null +++ b/src/views/HardwareStatus/Inventory/InventoryTableProcessors.vue @@ -0,0 +1,248 @@ +<template> + <page-section :section-title="$t('pageInventory.processors')"> + <!-- Search --> + <b-row class="align-items-end"> + <b-col sm="6" md="5" xl="4"> + <search + @change-search="onChangeSearchInput" + @clear-search="onClearSearchInput" + /> + </b-col> + <b-col sm="6" md="3" xl="2"> + <table-cell-count + :filtered-items-count="filteredRows" + :total-number-of-cells="processors.length" + ></table-cell-count> + </b-col> + </b-row> + <b-table + sort-icon-left + no-sort-reset + hover + responsive="md" + show-empty + :items="processors" + :fields="fields" + :sort-desc="true" + :filter="searchFilter" + :empty-text="$t('global.table.emptyMessage')" + :empty-filtered-text="$t('global.table.emptySearchMessage')" + @filtered="onFiltered" + > + <!-- Expand button --> + <template #cell(expandRow)="row"> + <b-button + variant="link" + data-test-id="hardwareStatus-button-expandProcessors" + :title="expandRowLabel" + class="btn-icon-only" + @click="toggleRowDetails(row)" + > + <icon-chevron /> + <span class="sr-only">{{ expandRowLabel }}</span> + </b-button> + </template> + <!-- Health --> + <template #cell(health)="{ value }"> + <status-icon :status="statusIcon(value)" /> + {{ value }} + </template> + + <!-- Toggle identify LED --> + <template #cell(identifyLed)="row"> + <b-form-checkbox + v-if="hasIdentifyLed(row.item.identifyLed)" + v-model="row.item.identifyLed" + name="switch" + switch + @change="toggleIdentifyLedValue(row.item)" + > + <span v-if="row.item.identifyLed"> + {{ $t('global.status.on') }} + </span> + <span v-else> {{ $t('global.status.off') }} </span> + </b-form-checkbox> + <div v-else>--</div> + </template> + + <template #row-details="{ item }"> + <b-container fluid> + <b-row> + <b-col class="mt-2" sm="6" xl="6"> + <dl> + <!-- Name --> + <dt>{{ $t('pageInventory.table.name') }}:</dt> + <dd>{{ tableFormatter(item.name) }}</dd> + <!-- Part Number --> + <dt>{{ $t('pageInventory.table.partNumber') }}:</dt> + <dd>{{ tableFormatter(item.partNumber) }}</dd> + <!-- Serial Number --> + <dt>{{ $t('pageInventory.table.serialNumber') }}:</dt> + <dd>{{ tableFormatter(item.serialNumber) }}</dd> + <!-- Spare Part Number --> + <dt>{{ $t('pageInventory.table.sparePartNumber') }}:</dt> + <dd>{{ tableFormatter(item.sparePartNumber) }}</dd> + <!-- Model --> + <dt>{{ $t('pageInventory.table.model') }}:</dt> + <dd>{{ tableFormatter(item.model) }}</dd> + <!-- Asset Tag --> + <dt>{{ $t('pageInventory.table.assetTag') }}:</dt> + <dd>{{ tableFormatter(item.assetTag) }}</dd> + </dl> + </b-col> + <b-col class="mt-2" sm="6" xl="6"> + <dl> + <!-- Status state --> + <dt>{{ $t('pageInventory.table.statusState') }}:</dt> + <dd>{{ tableFormatter(item.statusState) }}</dd> + <!-- Health Rollup --> + <dt>{{ $t('pageInventory.table.healthRollup') }}:</dt> + <dd>{{ tableFormatter(item.healthRollup) }}</dd> + </dl> + </b-col> + </b-row> + <div class="section-divider mb-3 mt-3"></div> + <b-row> + <b-col class="mt-1" sm="6" xl="6"> + <dl> + <!-- Manufacturer --> + <dt>{{ $t('pageInventory.table.manufacturer') }}:</dt> + <dd>{{ tableFormatter(item.manufacturer) }}</dd> + <!-- Processor Type --> + <dt>{{ $t('pageInventory.table.processorType') }}:</dt> + <dd>{{ tableFormatter(item.processorType) }}</dd> + <!-- Processor Architecture --> + <dt>{{ $t('pageInventory.table.processorArchitecture') }}:</dt> + <dd>{{ tableFormatter(item.processorArchitecture) }}</dd> + <!-- Instruction Set --> + <dt>{{ $t('pageInventory.table.instructionSet') }}:</dt> + <dd>{{ tableFormatter(item.instructionSet) }}</dd> + <!-- Version --> + <dt>{{ $t('pageInventory.table.version') }}:</dt> + <dd>{{ tableFormatter(item.version) }}</dd> + </dl> + </b-col> + <b-col class="mt-1" sm="6" xl="6"> + <dl> + <!-- Min Speed MHz --> + <dt>{{ $t('pageInventory.table.minSpeedMHz') }}:</dt> + <dd>{{ tableFormatter(item.minSpeedMHz) }}</dd> + <!-- Max Speed MHz --> + <dt>{{ $t('pageInventory.table.maxSpeedMHz') }}:</dt> + <dd>{{ tableFormatter(item.maxSpeedMHz) }}</dd> + <!-- Total Cores --> + <dt>{{ $t('pageInventory.table.totalCores') }}:</dt> + <dd>{{ tableFormatter(item.totalCores) }}</dd> + <!-- Total Threads --> + <dt>{{ $t('pageInventory.table.totalThreads') }}:</dt> + <dd>{{ tableFormatter(item.totalThreads) }}</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 TableCellCount from '@/components/Global/TableCellCount'; +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import TableSortMixin from '@/components/Mixins/TableSortMixin'; +import TableDataFormatterMixin from '@/components/Mixins/TableDataFormatterMixin'; +import Search from '@/components/Global/Search'; +import SearchFilterMixin, { + searchFilter, +} from '@/components/Mixins/SearchFilterMixin'; +import TableRowExpandMixin, { + expandRowLabel, +} from '@/components/Mixins/TableRowExpandMixin'; + +export default { + components: { IconChevron, PageSection, StatusIcon, Search, TableCellCount }, + mixins: [ + BVToastMixin, + TableRowExpandMixin, + TableDataFormatterMixin, + TableSortMixin, + SearchFilterMixin, + ], + data() { + return { + fields: [ + { + key: 'expandRow', + label: '', + tdClass: 'table-row-expand', + sortable: false, + }, + { + key: 'id', + label: this.$t('pageInventory.table.id'), + formatter: this.tableFormatter, + sortable: true, + }, + { + key: 'health', + label: this.$t('pageInventory.table.health'), + formatter: this.tableFormatter, + sortable: true, + tdClass: 'text-nowrap', + }, + { + key: 'locationNumber', + label: this.$t('pageInventory.table.locationNumber'), + formatter: this.tableFormatter, + sortable: true, + }, + { + key: 'identifyLed', + label: this.$t('pageInventory.table.identifyLed'), + formatter: this.tableFormatter, + sortable: false, + }, + ], + searchFilter: searchFilter, + searchTotalFilteredRows: 0, + expandRowLabel: expandRowLabel, + }; + }, + computed: { + filteredRows() { + return this.searchFilter + ? this.searchTotalFilteredRows + : this.processors.length; + }, + processors() { + return this.$store.getters['processors/processors']; + }, + }, + created() { + this.$store.dispatch('processors/getProcessorsInfo').finally(() => { + // Emit initial data fetch complete to parent component + this.$root.$emit('hardware-status-processors-complete'); + }); + }, + methods: { + onFiltered(filteredItems) { + this.searchTotalFilteredRows = filteredItems.length; + }, + toggleIdentifyLedValue(row) { + this.$store + .dispatch('processors/updateIdentifyLedValue', { + uri: row.uri, + identifyLed: row.identifyLed, + }) + .catch(({ message }) => this.errorToast(message)); + }, + // TO DO: remove hasIdentifyLed when the following is merged: + // https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/37045 + hasIdentifyLed(identifyLed) { + return typeof identifyLed === 'boolean'; + }, + }, +}; +</script> diff --git a/src/views/HardwareStatus/Inventory/InventoryTableSystem.vue b/src/views/HardwareStatus/Inventory/InventoryTableSystem.vue new file mode 100644 index 00000000..54129d1f --- /dev/null +++ b/src/views/HardwareStatus/Inventory/InventoryTableSystem.vue @@ -0,0 +1,210 @@ +<template> + <page-section :section-title="$t('pageInventory.system')"> + <b-table + responsive="md" + hover + show-empty + :items="systems" + :fields="fields" + :empty-text="$t('global.table.emptyMessage')" + > + <!-- Expand chevron icon --> + <template #cell(expandRow)="row"> + <b-button + variant="link" + data-test-id="hardwareStatus-button-expandSystem" + :title="expandRowLabel" + class="btn-icon-only" + @click="toggleRowDetails(row)" + > + <icon-chevron /> + <span class="sr-only">{{ expandRowLabel }}</span> + </b-button> + </template> + + <!-- Health --> + <template #cell(health)="{ value }"> + <status-icon :status="statusIcon(value)" /> + {{ value }} + </template> + + <template #cell(locationIndicatorActive)="{ item }"> + <b-form-checkbox + id="identifyLedSwitch" + v-model="item.locationIndicatorActive" + data-test-id="hardwareStatus-toggle-identifyLed" + switch + @change="toggleIdentifyLedSwitch" + > + </b-form-checkbox> + </template> + + <template #row-details="{ item }"> + <b-container fluid> + <b-row> + <b-col class="mt-2" sm="6"> + <dl> + <!-- Serial number --> + <dt>{{ $t('pageInventory.table.serialNumber') }}:</dt> + <dd>{{ tableFormatter(item.serialNumber) }}</dd> + <!-- Model --> + <dt>{{ $t('pageInventory.table.model') }}:</dt> + <dd>{{ tableFormatter(item.model) }}</dd> + <!-- Asset tag --> + <dt>{{ $t('pageInventory.table.assetTag') }}:</dt> + <dd class="mb-2"> + {{ tableFormatter(item.assetTag) }} + </dd> + </dl> + </b-col> + <b-col class="mt-2" sm="6"> + <dl> + <!-- Status state --> + <dt>{{ $t('pageInventory.table.statusState') }}:</dt> + <dd>{{ tableFormatter(item.statusState) }}</dd> + <!-- Power state --> + <dt>{{ $t('pageInventory.table.power') }}:</dt> + <dd>{{ tableFormatter(item.powerState) }}</dd> + <!-- Health rollup --> + <dt>{{ $t('pageInventory.table.healthRollup') }}:</dt> + <dd>{{ tableFormatter(item.healthRollup) }}</dd> + </dl> + </b-col> + </b-row> + <div class="section-divider mb-3 mt-3"></div> + <b-row> + <b-col class="mt-1" sm="6"> + <dl> + <!-- Manufacturer --> + <dt>{{ $t('pageInventory.table.manufacturer') }}:</dt> + <dd>{{ tableFormatter(item.assetTag) }}</dd> + <!-- Description --> + <dt>{{ $t('pageInventory.table.description') }}:</dt> + <dd>{{ tableFormatter(item.description) }}</dd> + <!-- Sub Model --> + <dt>{{ $t('pageInventory.table.subModel') }}:</dt> + <dd> + {{ tableFormatter(item.subModel) }} + </dd> + <!-- System Type --> + <dt>{{ $t('pageInventory.table.systemType') }}:</dt> + <dd> + {{ tableFormatter(item.systemType) }} + </dd> + </dl> + </b-col> + <b-col sm="6"> + <dl> + <!-- Memory Summary --> + <dt class="mt-1 mb-2 font-weight-bold float-none"> + {{ $t('pageInventory.table.memorySummary') }} + </dt> + <!-- Status state --> + <dt>{{ $t('pageInventory.table.statusState') }}:</dt> + <dd>{{ tableFormatter(item.memorySummaryState) }}</dd> + <!-- Health --> + <dt>{{ $t('pageInventory.table.health') }}:</dt> + <dd>{{ tableFormatter(item.memorySummaryHealth) }}</dd> + <!-- Health Roll --> + <dt>{{ $t('pageInventory.table.healthRollup') }}:</dt> + <dd>{{ tableFormatter(item.memorySummaryHealthRoll) }}</dd> + + <!-- Processor Summary --> + <dt class="mt-1 mb-2 font-weight-bold float-none"> + {{ $t('pageInventory.table.processorSummary') }} + </dt> + <!-- Status state --> + <dt>{{ $t('pageInventory.table.statusState') }}:</dt> + <dd>{{ tableFormatter(item.processorSummaryState) }}</dd> + <!-- Health --> + <dt>{{ $t('pageInventory.table.health') }}:</dt> + <dd>{{ tableFormatter(item.processorSummaryHealth) }}</dd> + <!-- Health Rollup --> + <dt>{{ $t('pageInventory.table.healthRollup') }}:</dt> + <dd>{{ tableFormatter(item.processorSummaryHealthRoll) }}</dd> + <!-- Count --> + <dt>{{ $t('pageInventory.table.count') }}:</dt> + <dd>{{ tableFormatter(item.processorSummaryCount) }}</dd> + </dl> + </b-col> + </b-row> + </b-container> + </template> + </b-table> + </page-section> +</template> + +<script> +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import PageSection from '@/components/Global/PageSection'; +import IconChevron from '@carbon/icons-vue/es/chevron--down/20'; + +import StatusIcon from '@/components/Global/StatusIcon'; + +import TableRowExpandMixin, { + expandRowLabel, +} from '@/components/Mixins/TableRowExpandMixin'; +import TableDataFormatterMixin from '@/components/Mixins/TableDataFormatterMixin'; + +export default { + components: { IconChevron, PageSection, StatusIcon }, + mixins: [BVToastMixin, TableRowExpandMixin, TableDataFormatterMixin], + data() { + return { + fields: [ + { + key: 'expandRow', + label: '', + tdClass: 'table-row-expand', + }, + { + key: 'id', + label: this.$t('pageInventory.table.id'), + formatter: this.tableFormatter, + }, + { + key: 'hardwareType', + label: this.$t('pageInventory.table.hardwareType'), + formatter: this.tableFormatter, + tdClass: 'text-nowrap', + }, + { + key: 'health', + label: this.$t('pageInventory.table.health'), + formatter: this.tableFormatter, + tdClass: 'text-nowrap', + }, + { + key: 'locationNumber', + label: this.$t('pageInventory.table.locationNumber'), + formatter: this.tableFormatter, + }, + { + key: 'locationIndicatorActive', + label: this.$t('pageInventory.table.identifyLed'), + formatter: this.tableFormatter, + }, + ], + expandRowLabel: expandRowLabel, + }; + }, + computed: { + systems() { + return this.$store.getters['system/systems']; + }, + }, + created() { + this.$store.dispatch('system/getSystem').finally(() => { + // Emit initial data fetch complete to parent component + this.$root.$emit('hardware-status-system-complete'); + }); + }, + methods: { + toggleIdentifyLedSwitch(state) { + this.$store + .dispatch('system/changeIdentifyLedState', state) + .catch(({ message }) => this.errorToast(message)); + }, + }, +}; +</script> diff --git a/src/views/HardwareStatus/Inventory/index.js b/src/views/HardwareStatus/Inventory/index.js new file mode 100644 index 00000000..c9fde8d2 --- /dev/null +++ b/src/views/HardwareStatus/Inventory/index.js @@ -0,0 +1,2 @@ +import Inventory from './Inventory.vue'; +export default Inventory; diff --git a/src/views/HardwareStatus/Sensors/Sensors.vue b/src/views/HardwareStatus/Sensors/Sensors.vue new file mode 100644 index 00000000..c69532a6 --- /dev/null +++ b/src/views/HardwareStatus/Sensors/Sensors.vue @@ -0,0 +1,253 @@ +<template> + <b-container fluid="xl"> + <page-title /> + <b-row class="align-items-end"> + <b-col sm="6" md="5" xl="4"> + <search + :placeholder="$t('pageSensors.searchForSensors')" + data-test-id="sensors-input-searchForSensors" + @change-search="onChangeSearchInput" + @clear-search="onClearSearchInput" + /> + </b-col> + <b-col sm="3" md="3" xl="2"> + <table-cell-count + :filtered-items-count="filteredRows" + :total-number-of-cells="allSensors.length" + ></table-cell-count> + </b-col> + <b-col sm="3" md="4" xl="6" class="text-right"> + <table-filter :filters="tableFilters" @filter-change="onFilterChange" /> + </b-col> + </b-row> + <b-row> + <b-col xl="12"> + <table-toolbar + ref="toolbar" + :selected-items-count="selectedRows.length" + @clear-selected="clearSelectedRows($refs.table)" + > + <template #toolbar-buttons> + <table-toolbar-export + :data="selectedRows" + :file-name="exportFileNameByDate()" + /> + </template> + </table-toolbar> + <b-table + ref="table" + responsive="md" + selectable + no-select-on-click + sort-icon-left + hover + no-sort-reset + sticky-header="75vh" + sort-by="status" + show-empty + :no-border-collapse="true" + :items="filteredSensors" + :fields="fields" + :sort-desc="true" + :sort-compare="sortCompare" + :filter="searchFilter" + :empty-text="$t('global.table.emptyMessage')" + :empty-filtered-text="$t('global.table.emptySearchMessage')" + @filtered="onFiltered" + @row-selected="onRowSelected($event, filteredSensors.length)" + > + <!-- Checkbox column --> + <template #head(checkbox)> + <b-form-checkbox + v-model="tableHeaderCheckboxModel" + :indeterminate="tableHeaderCheckboxIndeterminate" + @change="onChangeHeaderCheckbox($refs.table)" + > + <span class="sr-only">{{ $t('global.table.selectAll') }}</span> + </b-form-checkbox> + </template> + <template #cell(checkbox)="row"> + <b-form-checkbox + v-model="row.rowSelected" + @change="toggleSelectRow($refs.table, row.index)" + > + <span class="sr-only">{{ $t('global.table.selectItem') }}</span> + </b-form-checkbox> + </template> + + <template #cell(status)="{ value }"> + <status-icon :status="statusIcon(value)" /> {{ value }} + </template> + <template #cell(currentValue)="data"> + {{ data.value }} {{ data.item.units }} + </template> + <template #cell(lowerCaution)="data"> + {{ data.value }} {{ data.item.units }} + </template> + <template #cell(upperCaution)="data"> + {{ data.value }} {{ data.item.units }} + </template> + <template #cell(lowerCritical)="data"> + {{ data.value }} {{ data.item.units }} + </template> + <template #cell(upperCritical)="data"> + {{ data.value }} {{ data.item.units }} + </template> + </b-table> + </b-col> + </b-row> + </b-container> +</template> + +<script> +import PageTitle from '@/components/Global/PageTitle'; +import Search from '@/components/Global/Search'; +import StatusIcon from '@/components/Global/StatusIcon'; +import TableFilter from '@/components/Global/TableFilter'; +import TableToolbar from '@/components/Global/TableToolbar'; +import TableToolbarExport from '@/components/Global/TableToolbarExport'; +import TableCellCount from '@/components/Global/TableCellCount'; + +import BVTableSelectableMixin, { + selectedRows, + tableHeaderCheckboxModel, + tableHeaderCheckboxIndeterminate, +} from '@/components/Mixins/BVTableSelectableMixin'; +import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin'; +import TableFilterMixin from '@/components/Mixins/TableFilterMixin'; +import TableDataFormatterMixin from '@/components/Mixins/TableDataFormatterMixin'; +import TableSortMixin from '@/components/Mixins/TableSortMixin'; +import SearchFilterMixin, { + searchFilter, +} from '@/components/Mixins/SearchFilterMixin'; + +export default { + name: 'Sensors', + components: { + PageTitle, + Search, + StatusIcon, + TableCellCount, + TableFilter, + TableToolbar, + TableToolbarExport, + }, + mixins: [ + TableFilterMixin, + BVTableSelectableMixin, + LoadingBarMixin, + TableDataFormatterMixin, + TableSortMixin, + SearchFilterMixin, + ], + beforeRouteLeave(to, from, next) { + this.hideLoader(); + next(); + }, + data() { + return { + fields: [ + { + key: 'checkbox', + sortable: false, + label: '', + }, + { + key: 'name', + sortable: true, + label: this.$t('pageSensors.table.name'), + }, + { + key: 'status', + sortable: true, + label: this.$t('pageSensors.table.status'), + tdClass: 'text-nowrap', + }, + { + key: 'lowerCritical', + formatter: this.tableFormatter, + label: this.$t('pageSensors.table.lowerCritical'), + }, + { + key: 'lowerCaution', + formatter: this.tableFormatter, + label: this.$t('pageSensors.table.lowerWarning'), + }, + + { + key: 'currentValue', + formatter: this.tableFormatter, + label: this.$t('pageSensors.table.currentValue'), + }, + { + key: 'upperCaution', + formatter: this.tableFormatter, + label: this.$t('pageSensors.table.upperWarning'), + }, + { + key: 'upperCritical', + formatter: this.tableFormatter, + label: this.$t('pageSensors.table.upperCritical'), + }, + ], + tableFilters: [ + { + key: 'status', + label: this.$t('pageSensors.table.status'), + values: ['OK', 'Warning', 'Critical'], + }, + ], + activeFilters: [], + searchFilter: searchFilter, + searchTotalFilteredRows: 0, + selectedRows: selectedRows, + tableHeaderCheckboxModel: tableHeaderCheckboxModel, + tableHeaderCheckboxIndeterminate: tableHeaderCheckboxIndeterminate, + }; + }, + computed: { + allSensors() { + return this.$store.getters['sensors/sensors']; + }, + filteredRows() { + return this.searchFilter + ? this.searchTotalFilteredRows + : this.filteredSensors.length; + }, + filteredSensors() { + return this.getFilteredTableData(this.allSensors, this.activeFilters); + }, + }, + created() { + this.startLoader(); + this.$store + .dispatch('sensors/getAllSensors') + .finally(() => this.endLoader()); + }, + methods: { + sortCompare(a, b, key) { + if (key === 'status') { + return this.sortStatus(a, b, key); + } + }, + onFilterChange({ activeFilters }) { + this.activeFilters = activeFilters; + }, + onFiltered(filteredItems) { + this.searchTotalFilteredRows = filteredItems.length; + }, + onChangeSearchInput(event) { + this.searchFilter = event; + }, + exportFileNameByDate() { + // Create export file name based on date + let date = new Date(); + date = + date.toISOString().slice(0, 10) + + '_' + + date.toString().split(':').join('-').split(' ')[4]; + return this.$t('pageSensors.exportFilePrefix') + date; + }, + }, +}; +</script> diff --git a/src/views/HardwareStatus/Sensors/index.js b/src/views/HardwareStatus/Sensors/index.js new file mode 100644 index 00000000..fc71b611 --- /dev/null +++ b/src/views/HardwareStatus/Sensors/index.js @@ -0,0 +1,2 @@ +import Sensors from './Sensors.vue'; +export default Sensors; |