1<template> 2 <div> 3 <header id="page-header"> 4 <a role="link" class="link-skip-nav btn btn-light" href="#main-content"> 5 {{ $t('appHeader.skipToContent') }} 6 </a> 7 8 <b-navbar 9 variant="dark" 10 type="dark" 11 :aria-label="$t('appHeader.applicationHeader')" 12 > 13 <!-- Left aligned nav items --> 14 <b-button 15 id="app-header-trigger" 16 class="nav-trigger" 17 aria-hidden="true" 18 title="Open navigation" 19 type="button" 20 variant="link" 21 :class="{ open: isNavigationOpen }" 22 @click="toggleNavigation" 23 > 24 <icon-close v-if="isNavigationOpen" /> 25 <icon-menu v-if="!isNavigationOpen" /> 26 </b-button> 27 <b-navbar-nav> 28 <img 29 class="header-logo" 30 src="@/assets/images/logo-header.svg" 31 :alt="altLogo" 32 /> 33 </b-navbar-nav> 34 <!-- Right aligned nav items --> 35 <b-navbar-nav class="ml-auto helper-menu"> 36 <b-nav-item 37 to="/health/event-logs" 38 data-test-id="appHeader-container-health" 39 > 40 <status-icon :status="healthStatusIcon" /> 41 {{ $t('appHeader.health') }} 42 </b-nav-item> 43 <b-nav-item 44 to="/control/server-power-operations" 45 data-test-id="appHeader-container-power" 46 > 47 <status-icon :status="hostStatusIcon" /> 48 {{ $t('appHeader.power') }} 49 </b-nav-item> 50 <!-- Using LI elements instead of b-nav-item to support semantic button elements --> 51 <li class="nav-item"> 52 <b-button 53 id="app-header-refresh" 54 variant="link" 55 data-test-id="appHeader-button-refresh" 56 @click="refresh" 57 > 58 <icon-renew /> 59 <span class="responsive-text">{{ $t('appHeader.refresh') }}</span> 60 </b-button> 61 </li> 62 <li class="nav-item"> 63 <b-dropdown 64 id="app-header-user" 65 variant="link" 66 right 67 data-test-id="appHeader-container-user" 68 > 69 <template v-slot:button-content> 70 <icon-avatar /> 71 <span class="responsive-text">{{ username }}</span> 72 </template> 73 <b-dropdown-item 74 to="/profile-settings" 75 data-test-id="appHeader-link-profile" 76 >{{ $t('appHeader.profileSettings') }} 77 </b-dropdown-item> 78 <b-dropdown-item 79 data-test-id="appHeader-link-logout" 80 @click="logout" 81 > 82 {{ $t('appHeader.logOut') }} 83 </b-dropdown-item> 84 </b-dropdown> 85 </li> 86 </b-navbar-nav> 87 </b-navbar> 88 </header> 89 <loading-bar /> 90 </div> 91</template> 92 93<script> 94import IconAvatar from '@carbon/icons-vue/es/user--avatar/20'; 95import IconClose from '@carbon/icons-vue/es/close/20'; 96import IconMenu from '@carbon/icons-vue/es/menu/20'; 97import IconRenew from '@carbon/icons-vue/es/renew/20'; 98import StatusIcon from '../Global/StatusIcon'; 99import LoadingBar from '../Global/LoadingBar'; 100 101export default { 102 name: 'AppHeader', 103 components: { 104 IconAvatar, 105 IconClose, 106 IconMenu, 107 IconRenew, 108 StatusIcon, 109 LoadingBar 110 }, 111 data() { 112 return { 113 isNavigationOpen: false, 114 altLogo: `${process.env.VUE_APP_COMPANY_NAME} logo` 115 }; 116 }, 117 computed: { 118 hostStatus() { 119 return this.$store.getters['global/hostStatus']; 120 }, 121 healthStatus() { 122 return this.$store.getters['eventLog/healthStatus']; 123 }, 124 hostStatusIcon() { 125 switch (this.hostStatus) { 126 case 'on': 127 return 'success'; 128 case 'error': 129 return 'danger'; 130 case 'diagnosticMode': 131 return 'warning'; 132 case 'off': 133 default: 134 return 'secondary'; 135 } 136 }, 137 healthStatusIcon() { 138 switch (this.healthStatus) { 139 case 'OK': 140 return 'success'; 141 case 'Warning': 142 return 'warning'; 143 case 'Critical': 144 return 'danger'; 145 default: 146 return 'secondary'; 147 } 148 }, 149 username() { 150 return this.$store.getters['global/username']; 151 } 152 }, 153 created() { 154 this.getHostInfo(); 155 this.getEvents(); 156 }, 157 mounted() { 158 this.$root.$on( 159 'change:isNavigationOpen', 160 isNavigationOpen => (this.isNavigationOpen = isNavigationOpen) 161 ); 162 }, 163 methods: { 164 getHostInfo() { 165 this.$store.dispatch('global/getHostStatus'); 166 }, 167 getEvents() { 168 this.$store.dispatch('eventLog/getEventLogData'); 169 }, 170 refresh() { 171 this.$emit('refresh'); 172 }, 173 logout() { 174 this.$store.dispatch('authentication/logout'); 175 }, 176 toggleNavigation() { 177 this.$root.$emit('toggle:navigation'); 178 } 179 } 180}; 181</script> 182 183<style lang="scss"> 184.app-header { 185 .link-skip-nav { 186 position: absolute; 187 top: -60px; 188 left: 0.5rem; 189 z-index: $zindex-popover; 190 transition: $duration--moderate-01 $exit-easing--expressive; 191 &:focus { 192 top: 0.5rem; 193 transition-timing-function: $entrance-easing--expressive; 194 } 195 } 196 .navbar-dark { 197 .navbar-text, 198 .nav-link, 199 .btn-link { 200 color: $white !important; 201 fill: currentColor; 202 } 203 } 204 205 .nav-item { 206 fill: theme-color('light'); 207 } 208 209 .navbar { 210 padding: 0; 211 @include media-breakpoint-up($responsive-layout-bp) { 212 height: $header-height; 213 } 214 215 .btn-link { 216 padding: $spacer / 2; 217 } 218 219 .header-logo { 220 width: auto; 221 height: $header-height; 222 padding: $spacer/2 0; 223 } 224 225 .helper-menu { 226 @include media-breakpoint-down(sm) { 227 background-color: gray('800'); 228 width: 100%; 229 justify-content: flex-end; 230 231 .nav-link, 232 .btn { 233 padding: $spacer / 1.125 $spacer / 2; 234 } 235 } 236 237 .responsive-text { 238 @include media-breakpoint-down(xs) { 239 display: none; 240 } 241 } 242 } 243 } 244 245 .navbar-nav { 246 padding: 0 $spacer; 247 } 248 249 .nav-trigger { 250 fill: theme-color('light'); 251 width: $header-height; 252 height: $header-height; 253 transition: none; 254 255 svg { 256 margin: 0; 257 } 258 259 &:hover { 260 fill: theme-color('light'); 261 background-color: theme-color('dark'); 262 } 263 264 &.open { 265 background-color: gray('800'); 266 } 267 268 @include media-breakpoint-up($responsive-layout-bp) { 269 display: none; 270 } 271 } 272 273 .dropdown { 274 .dropdown-menu { 275 margin-top: 0; 276 @include media-breakpoint-up(md) { 277 margin-top: 7px; 278 } 279 } 280 } 281 282 .navbar-expand { 283 @include media-breakpoint-down(sm) { 284 flex-flow: wrap; 285 } 286 } 287} 288</style> 289