1<template> 2 <page-section :section-title="$t('pageInventory.fans')"> 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="fans.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="fans" 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-expandFans" 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 <template #row-details="{ item }"> 55 <b-container fluid> 56 <b-row> 57 <b-col sm="6" xl="4"> 58 <dl> 59 <!-- Name --> 60 <dt>{{ $t('pageInventory.table.name') }}:</dt> 61 <dd>{{ dataFormatter(item.name) }}</dd> 62 </dl> 63 <dl> 64 <!-- Serial number --> 65 <dt>{{ $t('pageInventory.table.serialNumber') }}:</dt> 66 <dd>{{ dataFormatter(item.serialNumber) }}</dd> 67 </dl> 68 <dl> 69 <!-- Part number --> 70 <dt>{{ $t('pageInventory.table.partNumber') }}:</dt> 71 <dd>{{ dataFormatter(item.partNumber) }}</dd> 72 </dl> 73 <dl> 74 <!-- Fan speed --> 75 <dt>{{ $t('pageInventory.table.fanSpeed') }}:</dt> 76 <dd> 77 {{ dataFormatter(item.speed) }} 78 {{ $t('unit.RPM') }} 79 </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 </dl> 88 <dl> 89 <!-- Health Rollup state --> 90 <dt>{{ $t('pageInventory.table.statusHealthRollup') }}:</dt> 91 <dd>{{ dataFormatter(item.healthRollup) }}</dd> 92 </dl> 93 </b-col> 94 </b-row> 95 </b-container> 96 </template> 97 </b-table> 98 </page-section> 99</template> 100 101<script> 102import PageSection from '@/components/Global/PageSection'; 103import IconChevron from '@carbon/icons-vue/es/chevron--down/20'; 104import TableCellCount from '@/components/Global/TableCellCount'; 105 106import StatusIcon from '@/components/Global/StatusIcon'; 107import DataFormatterMixin from '@/components/Mixins/DataFormatterMixin'; 108import TableSortMixin from '@/components/Mixins/TableSortMixin'; 109import Search from '@/components/Global/Search'; 110import SearchFilterMixin, { 111 searchFilter, 112} from '@/components/Mixins/SearchFilterMixin'; 113import TableRowExpandMixin, { 114 expandRowLabel, 115} from '@/components/Mixins/TableRowExpandMixin'; 116 117export default { 118 components: { IconChevron, PageSection, StatusIcon, Search, TableCellCount }, 119 mixins: [ 120 TableRowExpandMixin, 121 DataFormatterMixin, 122 TableSortMixin, 123 SearchFilterMixin, 124 ], 125 data() { 126 return { 127 isBusy: true, 128 fields: [ 129 { 130 key: 'expandRow', 131 label: '', 132 tdClass: 'table-row-expand', 133 sortable: false, 134 }, 135 { 136 key: 'id', 137 label: this.$t('pageInventory.table.id'), 138 formatter: this.dataFormatter, 139 sortable: true, 140 }, 141 { 142 key: 'health', 143 label: this.$t('pageInventory.table.health'), 144 formatter: this.dataFormatter, 145 sortable: true, 146 tdClass: 'text-nowrap', 147 }, 148 { 149 key: 'partNumber', 150 label: this.$t('pageInventory.table.partNumber'), 151 formatter: this.dataFormatter, 152 sortable: true, 153 }, 154 { 155 key: 'serialNumber', 156 label: this.$t('pageInventory.table.serialNumber'), 157 formatter: this.dataFormatter, 158 }, 159 ], 160 searchFilter: searchFilter, 161 searchTotalFilteredRows: 0, 162 expandRowLabel: expandRowLabel, 163 }; 164 }, 165 computed: { 166 filteredRows() { 167 return this.searchFilter 168 ? this.searchTotalFilteredRows 169 : this.fans.length; 170 }, 171 fans() { 172 return this.$store.getters['fan/fans']; 173 }, 174 }, 175 created() { 176 this.$store.dispatch('fan/getFanInfo').finally(() => { 177 // Emit initial data fetch complete to parent component 178 this.$root.$emit('hardware-status-fans-complete'); 179 this.isBusy = false; 180 }); 181 }, 182 methods: { 183 sortCompare(a, b, key) { 184 if (key === 'health') { 185 return this.sortStatus(a, b, key); 186 } 187 }, 188 onFiltered(filteredItems) { 189 this.searchTotalFilteredRows = filteredItems.length; 190 }, 191 }, 192}; 193</script> 194