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