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