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