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