xref: /openbmc/webui-vue/src/components/AppHeader/AppHeader.vue (revision 61859097d8a129de1d8292f3ebde5e9228d82838)
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 IconAvatar from '@carbon/icons-vue/es/user--avatar/20';
98import IconClose from '@carbon/icons-vue/es/close/20';
99import IconMenu from '@carbon/icons-vue/es/menu/20';
100import IconRenew from '@carbon/icons-vue/es/renew/20';
101import StatusIcon from '@/components/Global/StatusIcon';
102import LoadingBar from '@/components/Global/LoadingBar';
103
104export default {
105  name: 'AppHeader',
106  components: {
107    IconAvatar,
108    IconClose,
109    IconMenu,
110    IconRenew,
111    StatusIcon,
112    LoadingBar
113  },
114  data() {
115    return {
116      isNavigationOpen: false,
117      altLogo: `${process.env.VUE_APP_COMPANY_NAME} logo`
118    };
119  },
120  computed: {
121    hostStatus() {
122      return this.$store.getters['global/hostStatus'];
123    },
124    healthStatus() {
125      return this.$store.getters['eventLog/healthStatus'];
126    },
127    hostStatusIcon() {
128      switch (this.hostStatus) {
129        case 'on':
130          return 'success';
131        case 'error':
132          return 'danger';
133        case 'diagnosticMode':
134          return 'warning';
135        case 'off':
136        default:
137          return 'secondary';
138      }
139    },
140    healthStatusIcon() {
141      switch (this.healthStatus) {
142        case 'OK':
143          return 'success';
144        case 'Warning':
145          return 'warning';
146        case 'Critical':
147          return 'danger';
148        default:
149          return 'secondary';
150      }
151    },
152    username() {
153      return this.$store.getters['global/username'];
154    }
155  },
156  created() {
157    this.getHostInfo();
158    this.getEvents();
159  },
160  mounted() {
161    this.$root.$on(
162      'change:isNavigationOpen',
163      isNavigationOpen => (this.isNavigationOpen = isNavigationOpen)
164    );
165  },
166  methods: {
167    getHostInfo() {
168      this.$store.dispatch('global/getHostStatus');
169    },
170    getEvents() {
171      this.$store.dispatch('eventLog/getEventLogData');
172    },
173    refresh() {
174      this.$emit('refresh');
175    },
176    logout() {
177      this.$store.dispatch('authentication/logout');
178    },
179    toggleNavigation() {
180      this.$root.$emit('toggle:navigation');
181    }
182  }
183};
184</script>
185
186<style lang="scss">
187.app-header {
188  .link-skip-nav {
189    position: absolute;
190    top: -60px;
191    left: 0.5rem;
192    z-index: $zindex-popover;
193    transition: $duration--moderate-01 $exit-easing--expressive;
194    &:focus {
195      top: 0.5rem;
196      transition-timing-function: $entrance-easing--expressive;
197    }
198  }
199  .navbar-dark {
200    .navbar-text,
201    .nav-link,
202    .btn-link {
203      color: theme-color('light') !important;
204      fill: currentColor;
205    }
206  }
207
208  .nav-item {
209    fill: theme-color('light');
210  }
211
212  .navbar {
213    padding: 0;
214    background-color: $navbar-color;
215    @include media-breakpoint-up($responsive-layout-bp) {
216      height: $header-height;
217    }
218
219    .btn-link {
220      padding: $spacer / 2;
221    }
222
223    .header-logo {
224      width: auto;
225      height: $header-height;
226      padding: $spacer/2 0;
227    }
228
229    .helper-menu {
230      @include media-breakpoint-down(sm) {
231        background-color: gray('800');
232        width: 100%;
233        justify-content: flex-end;
234
235        .nav-link,
236        .btn {
237          padding: $spacer / 1.125 $spacer / 2;
238        }
239      }
240
241      .responsive-text {
242        @include media-breakpoint-down(xs) {
243          display: none;
244        }
245      }
246    }
247  }
248
249  .navbar-nav {
250    padding: 0 $spacer;
251  }
252
253  .nav-trigger {
254    fill: theme-color('light');
255    width: $header-height;
256    height: $header-height;
257    transition: none;
258
259    svg {
260      margin: 0;
261    }
262
263    &:hover {
264      fill: theme-color('light');
265      background-color: theme-color('dark');
266    }
267
268    &.open {
269      background-color: gray('800');
270    }
271
272    @include media-breakpoint-up($responsive-layout-bp) {
273      display: none;
274    }
275  }
276
277  .dropdown {
278    .dropdown-menu {
279      margin-top: 0;
280      @include media-breakpoint-up(md) {
281        margin-top: 7px;
282      }
283    }
284  }
285
286  .navbar-expand {
287    @include media-breakpoint-down(sm) {
288      flex-flow: wrap;
289    }
290  }
291}
292</style>
293