diff options
Diffstat (limited to 'src/components/_ibs/AppHeader')
-rw-r--r-- | src/components/_ibs/AppHeader/AppHeader.vue | 395 | ||||
-rw-r--r-- | src/components/_ibs/AppHeader/index.js | 2 |
2 files changed, 397 insertions, 0 deletions
diff --git a/src/components/_ibs/AppHeader/AppHeader.vue b/src/components/_ibs/AppHeader/AppHeader.vue new file mode 100644 index 00000000..1c5dbd3a --- /dev/null +++ b/src/components/_ibs/AppHeader/AppHeader.vue @@ -0,0 +1,395 @@ +<template> + <div> + <header id="page-header"> + <a + class="link-skip-nav btn btn-light" + href="#main-content" + @click="setFocus" + > + {{ $t('appHeader.skipToContent') }} + </a> + + <b-navbar type="dark" :aria-label="$t('appHeader.applicationHeader')"> + <!-- Left aligned nav items --> + <b-button + id="app-header-trigger" + class="nav-trigger" + aria-hidden="true" + type="button" + variant="link" + :class="{ open: isNavigationOpen }" + @click="toggleNavigation" + > + <icon-close + v-if="isNavigationOpen" + :title="$t('appHeader.titleHideNavigation')" + /> + <icon-menu + v-if="!isNavigationOpen" + :title="$t('appHeader.titleShowNavigation')" + /> + </b-button> + <b-navbar-nav> + <b-navbar-brand + class="mr-0" + to="/" + data-test-id="appHeader-container-overview" + > + <img + class="header-logo" + src="@/assets/images/_ibs/logo-header.svg" + :alt="altLogo" + /> + </b-navbar-brand> + <div v-if="isNavTagPresent" :key="routerKey" class="pl-2 nav-tags"> + <span>|</span> + <span class="pl-3 asset-tag">{{ assetTag }}</span> + <span class="pl-3">{{ modelType }}</span> + <span class="pl-3">{{ serialNumber }}</span> + </div> + </b-navbar-nav> + <!-- Right aligned nav items --> + <b-navbar-nav class="ml-auto helper-menu"> + <b-nav-item + to="/logs/event-logs" + data-test-id="appHeader-container-health" + > + <status-icon :status="healthStatusIcon" /> + {{ $t('appHeader.health') }} + </b-nav-item> + <b-nav-item + to="/operations/server-power-operations" + data-test-id="appHeader-container-power" + > + <status-icon :status="serverStatusIcon" /> + {{ $t('appHeader.power') }} + </b-nav-item> + <!-- Using LI elements instead of b-nav-item to support semantic button elements --> + <li class="nav-item"> + <b-button + id="app-header-refresh" + variant="link" + data-test-id="appHeader-button-refresh" + @click="refresh" + > + <icon-renew :title="$t('appHeader.titleRefresh')" /> + <span class="responsive-text">{{ $t('appHeader.refresh') }}</span> + </b-button> + </li> + <li class="nav-item"> + <b-dropdown + id="app-header-user" + variant="link" + right + data-test-id="appHeader-container-user" + > + <template #button-content> + <icon-avatar :title="$t('appHeader.titleProfile')" /> + <span class="responsive-text">{{ username }}</span> + </template> + <b-dropdown-item + to="/profile-settings" + data-test-id="appHeader-link-profile" + >{{ $t('appHeader.profileSettings') }} + </b-dropdown-item> + <b-dropdown-item + data-test-id="appHeader-link-logout" + @click="logout" + > + {{ $t('appHeader.logOut') }} + </b-dropdown-item> + </b-dropdown> + </li> + </b-navbar-nav> + </b-navbar> + </header> + <loading-bar /> + </div> +</template> + +<script> +import BVToastMixin from '@/components/Mixins/BVToastMixin'; +import IconAvatar from '@carbon/icons-vue/es/user--avatar/20'; +import IconClose from '@carbon/icons-vue/es/close/20'; +import IconMenu from '@carbon/icons-vue/es/menu/20'; +import IconRenew from '@carbon/icons-vue/es/renew/20'; +import StatusIcon from '@/components/Global/StatusIcon'; +import LoadingBar from '@/components/Global/LoadingBar'; + +export default { + name: 'AppHeader', + components: { + IconAvatar, + IconClose, + IconMenu, + IconRenew, + StatusIcon, + LoadingBar, + }, + mixins: [BVToastMixin], + props: { + routerKey: { + type: Number, + default: 0, + }, + }, + data() { + return { + isNavigationOpen: false, + altLogo: process.env.VUE_APP_COMPANY_NAME || 'Built on OpenBMC', + }; + }, + computed: { + isNavTagPresent() { + return this.assetTag || this.modelType || this.serialNumber; + }, + assetTag() { + return this.$store.getters['global/assetTag']; + }, + modelType() { + return this.$store.getters['global/modelType']; + }, + serialNumber() { + return this.$store.getters['global/serialNumber']; + }, + isAuthorized() { + return this.$store.getters['global/isAuthorized']; + }, + serverStatus() { + return this.$store.getters['global/serverStatus']; + }, + healthStatus() { + return this.$store.getters['eventLog/healthStatus']; + }, + serverStatusIcon() { + switch (this.serverStatus) { + case 'on': + return 'success'; + case 'error': + return 'danger'; + case 'diagnosticMode': + return 'warning'; + case 'off': + default: + return 'secondary'; + } + }, + healthStatusIcon() { + switch (this.healthStatus) { + case 'OK': + return 'success'; + case 'Warning': + return 'warning'; + case 'Critical': + return 'danger'; + default: + return 'secondary'; + } + }, + username() { + return this.$store.getters['global/username']; + }, + }, + watch: { + isAuthorized(value) { + if (value === false) { + this.errorToast(this.$t('global.toast.unAuthDescription'), { + title: this.$t('global.toast.unAuthTitle'), + }); + } + }, + }, + created() { + // Reset auth state to check if user is authenticated based + // on available browser cookies + this.$store.dispatch('authentication/resetStoreState'); + this.getSystemInfo(); + this.getEvents(); + }, + mounted() { + this.$root.$on( + 'change-is-navigation-open', + (isNavigationOpen) => (this.isNavigationOpen = isNavigationOpen) + ); + }, + methods: { + getSystemInfo() { + this.$store.dispatch('global/getSystemInfo'); + }, + getEvents() { + this.$store.dispatch('eventLog/getEventLogData'); + }, + refresh() { + this.$emit('refresh'); + }, + logout() { + this.$store.dispatch('authentication/logout'); + }, + toggleNavigation() { + this.$root.$emit('toggle-navigation'); + }, + setFocus(event) { + event.preventDefault(); + this.$root.$emit('skip-navigation'); + }, + }, +}; +</script> + +<style lang="scss"> +@mixin focus-box-shadow($padding-color: $navbar-color, $outline-color: $white) { + box-shadow: inset 0 0 0 3px $padding-color, inset 0 0 0 5px $outline-color; +} +.app-header { + .link-skip-nav { + position: absolute; + top: -60px; + left: 0.5rem; + z-index: $zindex-popover; + transition: $duration--moderate-01 $exit-easing--expressive; + &:focus { + top: 0.5rem; + transition-timing-function: $entrance-easing--expressive; + } + } + .navbar-text, + .nav-link, + .btn-link { + color: color('white') !important; + fill: currentColor; + padding: 0.68rem 1rem !important; + + &:hover { + background-color: theme-color-level(light, 10); + } + &:active { + background-color: theme-color-level(light, 9); + } + &:focus { + @include focus-box-shadow; + outline: 0; + } + } + + .nav-item { + fill: theme-color('light'); + overflow-x: hidden; + white-space: nowrap; + + .nav-link { + margin-top: -2px; + } + } + + .nav-item:last-of-type { + overflow-x: visible; + } + + .navbar { + padding: 0; + background-color: $navbar-color; + @include media-breakpoint-up($responsive-layout-bp) { + height: $header-height; + } + + .helper-menu { + @include media-breakpoint-down(sm) { + background-color: $navbar-color; + width: 100%; + justify-content: flex-end; + + .nav-link, + .btn { + padding: $spacer / 1.125 $spacer / 2; + } + + .nav-link:focus, + .btn:focus { + @include focus-box-shadow($gray-800); + } + } + + .responsive-text { + @include media-breakpoint-down(xs) { + @include sr-only; + } + } + } + } + + .navbar-nav { + @include media-breakpoint-up($responsive-layout-bp) { + padding: 0 $spacer; + } + align-items: center; + + .navbar-brand, + .nav-link { + transition: $focus-transition; + } + .nav-tags { + color: theme-color-level(light, 3); + @include media-breakpoint-down(xs) { + @include sr-only; + } + .asset-tag { + @include media-breakpoint-down($responsive-layout-bp) { + @include sr-only; + } + } + } + } + + .nav-trigger { + fill: theme-color('light'); + width: $header-height * 1.5; + height: $header-height; + transition: none; + display: inline-flex; + flex: 0 0 20px; + align-items: center; + + svg { + margin: 0; + } + + &:hover { + fill: theme-color('light'); + background-color: theme-color-level(light, 10); + } + + &.open { + background-color: gray('800'); + } + + @include media-breakpoint-up($responsive-layout-bp) { + display: none; + } + } + + .dropdown-menu { + margin-top: 0; + + @include media-breakpoint-only(md) { + margin-top: 4px; + } + } + + .navbar-expand { + flex-flow: row wrap; + @include media-breakpoint-down(sm) { + flex-flow: wrap; + } + } +} + +.navbar-brand { + padding: $spacer/2; + height: $header-height; + line-height: 1; + &:focus { + box-shadow: inset 0 0 0 3px $navbar-color, inset 0 0 0 5px color('white'); + outline: 0; + } +} +</style> diff --git a/src/components/_ibs/AppHeader/index.js b/src/components/_ibs/AppHeader/index.js new file mode 100644 index 00000000..e180e80f --- /dev/null +++ b/src/components/_ibs/AppHeader/index.js @@ -0,0 +1,2 @@ +import AppHeader from './AppHeader'; +export default AppHeader; |