Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 31 additions & 66 deletions ui/admin/src/layouts/AppLayout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -82,73 +82,28 @@
</template>
</v-list>
</v-navigation-drawer>
<v-app-bar
<AppBarContent
:theme
class="bg-v-theme-surface border-b-thin"
data-test="app-bar"
flat
floating
show-menu-toggle
show-support
@toggle-menu="drawer = !drawer"
@support-click="openShellhubHelp"
>
<v-app-bar-nav-icon
class="hidden-lg-and-up"
aria-label="Toggle Menu"
@click.stop="drawer = !drawer"
/>

<Namespace :is-admin-context="true" />

<v-spacer />

<v-menu anchor="bottom">
<template #activator="{ props }">
<v-chip
color="primary"
v-bind="props"
class="mr-8"
>
<UserIcon
size="1.5rem"
:email="currentUser"
class="mr-2"
data-test="user-icon"
/>
{{ currentUser || "ADMIN" }}
<v-icon
right
icon="mdi-chevron-down"
/>
</v-chip>
</template>
<v-list class="bg-v-theme-surface">
<v-list-item
v-for="item in menu"
:key="item.title"
:value="item"
:data-test="item.title"
@click="triggerClick(item)"
>
<div class="d-flex align-center">
<div><v-icon :icon="item.icon" /></div>
<v-list-item-title>{{ item.title }}</v-list-item-title>
</div>
</v-list-item>

<v-divider />

<v-list-item>
<v-switch
label="Dark Mode"
:model-value="isDarkMode"
data-test="dark-mode-switch"
color="primary"
inset
hide-details
@change="toggleDarkMode"
/>
</v-list-item>
</v-list>
</v-menu>
</v-app-bar>
<template #left>
<Namespace :is-admin-context="true" />
</template>

<template #right>
<UserMenu
:user-email="currentUser"
:display-name="displayName"
:menu-items="menu"
:is-dark-mode="isDarkMode"
@select="handleUserMenuSelect"
@toggle-dark-mode="toggleDarkMode"
/>
</template>
</AppBarContent>

<Snackbar />

Expand Down Expand Up @@ -189,7 +144,8 @@ import useLayoutStore from "@/store/modules/layout";
import useAuthStore from "@admin/store/modules/auth";
import useSpinnerStore from "@/store/modules/spinner";
import Snackbar from "@/components/Snackbar/Snackbar.vue";
import UserIcon from "@/components/User/UserIcon.vue";
import AppBarContent from "@/components/AppBar/AppBarContent.vue";
import UserMenu from "@/components/AppBar/UserMenu.vue";
import Namespace from "@/components/Namespace/Namespace.vue";
import Logo from "@/assets/logo-inverted.png";
import { createNewAdminClient } from "@/api/http";
Expand Down Expand Up @@ -227,6 +183,7 @@ const expiredLicense = computed(() => licenseStore.isExpired);

const hasSpinner = computed(() => spinnerStore.status);
const currentUser = computed(() => authStore.currentUser);
const displayName = computed(() => currentUser.value || "ADMIN");
const currentRoute = computed(() => router.currentRoute);
const theme = computed(() => layoutStore.theme);
const isDarkMode = ref(theme.value === "dark");
Expand Down Expand Up @@ -258,11 +215,19 @@ const triggerClick = async (item: MenuItem) => {
}
};

const handleUserMenuSelect = (item: unknown) => {
void triggerClick(item as MenuItem);
};

const toggleDarkMode = () => {
isDarkMode.value = !isDarkMode.value;
layoutStore.setTheme(isDarkMode.value ? "dark" : "light");
};

const openShellhubHelp = () => {
window.open("https://github.com/shellhub-io/shellhub/issues/new/choose", "_blank");
};

const items = reactive([
{
icon: "mdi-view-dashboard",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

exports[`AppLayout > Renders the component 1`] = `
"<v-navigation-drawer-stub data-v-b779e8e7="" class="bg-v-theme-surface" disableresizewatcher="false" disableroutewatcher="false" expandonhover="true" floating="false" modelvalue="true" permanent="false" railwidth="56" scrim="true" temporary="false" persistent="false" touchless="false" width="256" location="start" sticky="false" border="false" order="0" absolute="false" tile="false" retainfocus="false" capturefocus="false" tag="nav"></v-navigation-drawer-stub>
<v-app-bar-stub data-v-b779e8e7="" theme="dark" class="bg-v-theme-surface border-b-thin" modelvalue="true" location="top" absolute="false" collapse="false" collapseposition="start" density="default" extensionheight="48" flat="true" floating="true" height="64" border="false" tile="false" tag="header" order="0" scrollthreshold="300" data-test="app-bar"></v-app-bar-stub>
<app-bar-content-stub data-v-b779e8e7="" showmenutoggle="true" showsupport="true" theme="dark"></app-bar-content-stub>
<snackbar-stub data-v-b779e8e7=""></snackbar-stub>
<v-main-stub data-v-b779e8e7="" scrollable="false" tag="main" data-test="main"></v-main-stub>
<v-overlay-stub data-v-b779e8e7="" class="align-center justify-center w-100 h-100" _disableglobalstack="false" absolute="false" attach="false" closeonback="true" contained="true" disabled="false" noclickanimation="false" modelvalue="false" persistent="false" scrim="false" zindex="2000" activatorprops="[object Object]" openonhover="false" closeoncontentclick="false" eager="false" locationstrategy="static" location="bottom" origin="auto" sticktotarget="false" viewportmargin="12" scrollstrategy="block" retainfocus="false" capturefocus="false" transition="fade-transition" data-test="overlay"></v-overlay-stub>"
Expand Down
6 changes: 6 additions & 0 deletions ui/admin/tests/unit/layouts/AppLayout/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,10 @@ describe("AppLayout", () => {
const vuetify = createVuetify();
const wrapper = shallowMount(AppLayout, { global: { plugins: [vuetify, routes, SnackbarPlugin] } });
it("Renders the component", () => { expect(wrapper.html()).toMatchSnapshot(); });
it("Passes AppBarContent flags", () => {
const appBarContent = wrapper.findComponent({ name: "AppBarContent" });
expect(appBarContent.exists()).toBe(true);
expect(appBarContent.props("showMenuToggle")).toBe(true);
expect(appBarContent.props("showSupport")).toBe(true);
});
});
158 changes: 25 additions & 133 deletions ui/src/components/AppBar/AppBar.vue
Original file line number Diff line number Diff line change
@@ -1,24 +1,17 @@
<template>
<PaywallChat v-model="chatSupportPaywall" />
<v-app-bar
flat
floating
class="bg-v-theme-surface border-b-thin"
data-test="app-bar"
<AppBarContent
show-menu-toggle
show-support
@toggle-menu="showNavigationDrawer = !showNavigationDrawer"
@support-click="openShellhubHelp()"
>
<v-app-bar-nav-icon
class="hidden-lg-and-up"
aria-label="Toggle Menu"
data-test="menu-toggle"
@click.stop="showNavigationDrawer = !showNavigationDrawer"
/>

<div class="d-flex align-center hidden-md-and-down">
<template #left>
<Namespace data-test="namespace-selector" />

<v-breadcrumbs
:items="breadcrumbItems"
class="pa-0 mx-4"
class="pa-0 mx-4 hidden-xs"
data-test="breadcrumbs"
>
<template #prepend>
Expand All @@ -36,29 +29,9 @@
/>
</template>
</v-breadcrumbs>
</div>

<v-spacer />

<div class="d-flex align-center ga-4 mr-4">
<v-tooltip
location="bottom"
class="text-center"
>
<template #activator="{ props }">
<v-btn
v-bind="props"
size="medium"
color="primary"
aria-label="community-help-icon"
icon="mdi-help-circle"
data-test="support-btn"
@click="openShellhubHelp()"
/>
</template>
<span>Need assistance? Click here for support.</span>
</v-tooltip>
</template>

<template #right>
<DevicesDropdown
v-if="hasNamespaces"
v-model="showDevicesDrawer"
Expand All @@ -71,102 +44,16 @@
@update:model-value="showDevicesDrawer = false"
/>

<v-menu
scrim
location="bottom end"
:offset="4"
>
<template #activator="{ props }">
<v-btn
v-bind="props"
size="medium"
color="primary"
icon
data-test="user-menu-btn"
>
<UserIcon
size="1.5rem"
:email="userEmail"
data-test="user-icon"
/>
</v-btn>
</template>

<v-card
:width="$vuetify.display.thresholds.sm / 2"
border
>
<v-list class="bg-v-theme-surface pa-0">
<!-- User Profile Header -->
<div class="pa-6 text-center">
<UserIcon
size="4rem"
:email="userEmail"
class="mb-4"
data-test="user-icon-large"
/>
<div class="text-h6 font-weight-medium mb-1">
{{ currentUser || userEmail }}
</div>
<div
v-if="currentUser"
class="text-body-2 text-medium-emphasis"
>
{{ userEmail }}
</div>
</div>

<v-divider />

<!-- Menu Items -->
<div>
<v-list-item
v-for="item in menu"
:key="item.title"
:value="item"
:data-test="item.title"
:prepend-icon="item.icon"
@click="triggerClick(item)"
>
<v-list-item-title class="font-weight-medium">
{{ item.title }}
</v-list-item-title>
</v-list-item>
</div>

<v-divider />

<!-- Dark Mode Toggle -->
<v-list-item
@click="toggleDarkMode"
>
<template #prepend>
<v-icon
:icon="isDarkMode ? 'mdi-brightness-6' : 'mdi-brightness-6'"
size="small"
/>
</template>
<v-list-item-title class="font-weight-medium">
{{ isDarkMode ? 'Dark Mode' : 'Light Mode' }}
</v-list-item-title>
<template #append>
<v-switch
:model-value="isDarkMode"
data-test="dark-mode-switch"
color="primary"
density="comfortable"
false-icon="mdi-weather-sunny"
true-icon="mdi-weather-night"
hide-details
readonly
/>
</template>
</v-list-item>
</v-list>
</v-card>
</v-menu>
</div>
</v-app-bar>
<UserMenu
:user-email="userEmail"
:display-name="currentUser"
:menu-items="menu"
:is-dark-mode="isDarkMode"
@select="handleUserMenuSelect"
@toggle-dark-mode="toggleDarkMode"
/>
</template>
</AppBarContent>
</template>

<script setup lang="ts">
Expand All @@ -177,7 +64,8 @@ import {
import { useRouter, useRoute, RouteLocationRaw, RouteLocation } from "vue-router";
import { useChatWoot } from "@productdevbook/chatwoot/vue";
import handleError from "@/utils/handleError";
import UserIcon from "../User/UserIcon.vue";
import AppBarContent from "@/components/AppBar/AppBarContent.vue";
import UserMenu from "@/components/AppBar/UserMenu.vue";
import DevicesDropdown from "./DevicesDropdown.vue";
import InvitationsMenu from "@/components/Invitations/InvitationsMenu.vue";
import PaywallChat from "../User/PaywallChat.vue";
Expand Down Expand Up @@ -249,6 +137,10 @@ const triggerClick = async (item: MenuItem) => {
}
};

const handleUserMenuSelect = (item: unknown) => {
void triggerClick(item as MenuItem);
};

const logout = async () => {
try {
await router.push({ name: "Login" });
Expand Down
Loading