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