1<template> 2 <page-section :section-title="$t('pageInventory.powerSupplies')"> 3 <b-row class="align-items-end"> 4 <b-col sm="6" md="5" xl="4"> 5 <search 6 @change-search="onChangeSearchInput" 7 @clear-search="onClearSearchInput" 8 /> 9 </b-col> 10 <b-col sm="6" md="3" xl="2"> 11 <table-cell-count 12 :filtered-items-count="filteredRows" 13 :total-number-of-cells="powerSupplies.length" 14 ></table-cell-count> 15 </b-col> 16 </b-row> 17 <b-table 18 sort-icon-left 19 must-sort 20 hover 21 responsive="md" 22 thead-class="table-light" 23 :sort-by="['health']" 24 show-empty 25 :items="powerSupplies" 26 :fields="fields" 27 :sort-desc="[true]" 28 :filter="searchFilter" 29 :empty-text="$t('global.table.emptyMessage')" 30 :empty-filtered-text="$t('global.table.emptySearchMessage')" 31 :busy="isBusy" 32 @filtered="onFiltered" 33 > 34 <!-- Expand chevron icon --> 35 <template #cell(expandRow)="row"> 36 <b-button 37 variant="link" 38 data-test-id="hardwareStatus-button-expandPowerSupplies" 39 :title="expandRowLabel" 40 class="btn-icon-only" 41 :class="{ collapsed: !row.detailsShowing }" 42 @click="toggleRowDetails(row)" 43 > 44 <icon-chevron /> 45 <span class="visually-hidden">{{ expandRowLabel }}</span> 46 </b-button> 47 </template> 48 49 <!-- Health --> 50 <template #cell(health)="{ value }"> 51 <status-icon :status="statusIcon(value)" /> 52 {{ value }} 53 </template> 54 55 <!-- StatusState --> 56 <template #cell(statusState)="{ value }"> 57 <status-icon :status="statusStateIcon(value)" /> 58 {{ value }} 59 </template> 60 61 <template #row-details="{ item }"> 62 <b-container fluid> 63 <b-row> 64 <b-col sm="6" xl="4"> 65 <dl> 66 <!-- Name --> 67 <dt>{{ $t('pageInventory.table.name') }}:</dt> 68 <dd>{{ dataFormatter(item.name) }}</dd> 69 <!-- Part number --> 70 <dt>{{ $t('pageInventory.table.partNumber') }}:</dt> 71 <dd>{{ dataFormatter(item.partNumber) }}</dd> 72 <!-- Serial number --> 73 <dt>{{ $t('pageInventory.table.serialNumber') }}:</dt> 74 <dd>{{ dataFormatter(item.serialNumber) }}</dd> 75 <!-- Spare part number --> 76 <dt>{{ $t('pageInventory.table.sparePartNumber') }}:</dt> 77 <dd> 78 {{ dataFormatter(item.sparePartNumber) }} 79 </dd> 80 <!-- Model --> 81 <dt>{{ $t('pageInventory.table.model') }}:</dt> 82 <dd>{{ dataFormatter(item.model) }}</dd> 83 </dl> 84 </b-col> 85 <b-col sm="6" xl="4"> 86 <dl> 87 <!-- Status state --> 88 <dt>{{ $t('pageInventory.table.statusState') }}:</dt> 89 <dd>{{ dataFormatter(item.statusState) }}</dd> 90 <!-- Status Health rollup state --> 91 <dt>{{ $t('pageInventory.table.statusHealthRollup') }}:</dt> 92 <dd>{{ dataFormatter(item.statusHealth) }}</dd> 93 <!-- Efficiency percent --> 94 <dt>{{ $t('pageInventory.table.efficiencyPercent') }}:</dt> 95 <dd> 96 {{ dataFormatter(item.efficiencyPercent) }} 97 {{ $t('unit.Percent') }} 98 </dd> 99 <!-- Power input watts --> 100 <dt>{{ $t('pageInventory.table.powerInputWatts') }}:</dt> 101 <dd> 102 {{ dataFormatter(item.powerInputWatts) }} 103 {{ $t('unit.W') }} 104 </dd> 105 </dl> 106 </b-col> 107 </b-row> 108 <div class="section-divider mb-3 mt-3"></div> 109 <b-row> 110 <b-col sm="6" xl="4"> 111 <dl> 112 <!-- Manufacturer --> 113 <dt>{{ $t('pageInventory.table.manufacturer') }}:</dt> 114 <dd>{{ dataFormatter(item.manufacturer) }}</dd> 115 </dl> 116 </b-col> 117 <b-col sm="6" xl="4"> 118 <dl> 119 <!-- Firmware version --> 120 <dt>{{ $t('pageInventory.table.firmwareVersion') }}:</dt> 121 <dd> 122 {{ dataFormatter(item.firmwareVersion) }} 123 </dd> 124 </dl> 125 </b-col> 126 </b-row> 127 </b-container> 128 </template> 129 </b-table> 130 </page-section> 131</template> 132 133<script> 134import PageSection from '@/components/Global/PageSection'; 135import IconChevron from '@carbon/icons-vue/es/chevron--down/20'; 136 137import StatusIcon from '@/components/Global/StatusIcon'; 138import TableCellCount from '@/components/Global/TableCellCount'; 139import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin'; 140import TableSortMixin from '@/components/Mixins/TableSortMixin'; 141import Search from '@/components/Global/Search'; 142import SearchFilterMixin, { 143 searchFilter, 144} from '@/components/Mixins/SearchFilterMixin'; 145import TableRowExpandMixin, { 146 expandRowLabel, 147} from '@/components/Mixins/TableRowExpandMixin'; 148import { useI18n } from 'vue-i18n'; 149import i18n from '@/i18n'; 150 151export default { 152 components: { 153 IconChevron, 154 PageSection, 155 StatusIcon, 156 Search, 157 TableCellCount, 158 }, 159 mixins: [ 160 TableRowExpandMixin, 161 DataFormatterMixin, 162 TableSortMixin, 163 SearchFilterMixin, 164 ], 165 data() { 166 return { 167 $t: useI18n().t, 168 isBusy: true, 169 fields: [ 170 { 171 key: 'expandRow', 172 label: '', 173 tdClass: 'table-row-expand', 174 sortable: false, 175 }, 176 { 177 key: 'id', 178 label: i18n.global.t('pageInventory.table.id'), 179 formatter: this.dataFormatter, 180 sortable: true, 181 }, 182 { 183 key: 'health', 184 label: i18n.global.t('pageInventory.table.health'), 185 formatter: this.dataFormatter, 186 sortable: true, 187 tdClass: 'text-nowrap', 188 }, 189 { 190 key: 'statusState', 191 label: i18n.global.t('pageInventory.table.state'), 192 formatter: this.dataFormatter, 193 tdClass: 'text-nowrap', 194 }, 195 { 196 key: 'locationNumber', 197 label: i18n.global.t('pageInventory.table.locationNumber'), 198 formatter: this.dataFormatter, 199 sortable: true, 200 }, 201 { 202 key: 'identifyLed', 203 label: i18n.global.t('pageInventory.table.identifyLed'), 204 formatter: this.dataFormatter, 205 }, 206 ], 207 searchFilter: searchFilter, 208 searchTotalFilteredRows: 0, 209 expandRowLabel: expandRowLabel, 210 }; 211 }, 212 computed: { 213 filteredRows() { 214 return this.searchFilter 215 ? this.searchTotalFilteredRows 216 : this.powerSupplies.length; 217 }, 218 powerSupplies() { 219 return this.$store.getters['powerSupply/powerSupplies']; 220 }, 221 }, 222 created() { 223 this.$store.dispatch('powerSupply/getAllPowerSupplies').finally(() => { 224 // Emit initial data fetch complete to parent component 225 require('@/eventBus').default.$emit( 226 'hardware-status-power-supplies-complete', 227 ); 228 this.isBusy = false; 229 }); 230 }, 231 methods: { 232 sortCompare(a, b, key) { 233 if (key === 'health') { 234 return this.sortStatus(a, b, key); 235 } else if (key === 'statusState') { 236 return this.sortStatusState(a, b, key); 237 } 238 }, 239 onFiltered(filteredItems) { 240 this.searchTotalFilteredRows = filteredItems.length; 241 }, 242 /** 243 * Returns the icon to use for status state based on the given status. 244 * @param {string} status The status to determine the icon for. 245 * @return {string} The icon for the given status. 246 */ 247 statusStateIcon(status) { 248 switch (status) { 249 case 'Enabled': 250 return 'success'; 251 case 'Absent': 252 return 'warning'; 253 default: 254 return ''; 255 } 256 }, 257 /** 258 * Sorts the status state of two objects based on the provided key. 259 * @param {Object} a The first object to compare. 260 * @param {Object} b The second object to compare. 261 * @param {string} key The key to use for comparison. 262 */ 263 sortStatusState(a, b, key) { 264 const statusState = ['Enabled', 'Absent']; 265 return statusState.indexOf(a[key]) - statusState.indexOf(b[key]); 266 }, 267 }, 268}; 269</script> 270