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