src/cockpit/App.vue
<template lang="pug">
#app(:class="darkMode ? 'dark_mode' : ''")
n-config-provider(:theme="darkTheme")
.main_menu.dark_mode()
n-menu.navBarLeftMenu(
mode="horizontal"
v-model:value="activeIndex"
:options="leftMenuOptions"
)
br.navBarNewLine
n-menu.navBarRightMenu(
mode="horizontal"
v-model:value="activeIndex"
:options="rightMenuOptions"
)
n-config-provider(:theme="darkMode ? darkTheme : lightTheme")
CPWA
n-divider(class="navBarBorder")
n-loading-bar-provider
n-dialog-provider
n-message-provider
n-notification-provider
router-view#content.content
#footerMargin
#footer
</template>
<script lang="ts">
import General from "./shared/General";
import Routing from "./shared/Routing";
import CPWA from "./components/CPWA.vue";
import { defineComponent, h, HTMLAttributes } from "vue";
import {
lightTheme,
darkTheme,
MenuInst,
MenuOption,
NConfigProvider,
NDialogProvider,
NDivider,
NLoadingBarProvider,
NMenu,
NMessageProvider,
NNotificationProvider,
NSwitch,
NIcon,
} from "naive-ui";
import { MoonOutline, SunnyOutline } from "@vicons/ionicons5";
import { BuiltInGlobalTheme } from "naive-ui/lib/themes/interface";
import Routes from "./routes";
import router from "./router";
import { getLocal, setLocal } from "./shared/Storage";
import { MenuMixedOption } from "naive-ui/lib/menu/src/interface";
interface Data {
activeIndex: string;
authed: boolean;
confirmed: boolean;
darkTheme: BuiltInGlobalTheme;
lightTheme: BuiltInGlobalTheme;
darkMode: boolean;
menuRef: MenuInst | null;
leftMenuOptions: MenuOption[];
rightMenuOptions: MenuOption[];
}
const options: MenuOption[] = [
menuItem("Home", "/"),
{
label: "Trip \u25BE",
key: "trip",
children: [
menuItem("Search", Routes.trip_Search),
menuItem("New", Routes.trip_New),
],
},
];
const loggedOutOptions: MenuOption[] = [
menuItem("Login", Routes.Login),
menuItem("Register", Routes.Register),
];
const loggedInOptions: MenuMixedOption[] = [
{
label: "My Account \u25BE",
key: "my_account",
children: [
menuItem(
"View Profile",
Routes.User + "/" + General.getCurrentUsername(),
),
// menuItem("Saved Trips", Routes.saved_Trips),
{
key: "logged_in_menu_divider",
type: "divider",
},
menuItem("Logout", Routes.Logout, { id: "menubar_logout" }),
],
},
];
function menuItem(
label: string,
url: string,
p: HTMLAttributes | undefined = undefined,
): MenuOption {
return {
label: () => h("a", { href: url }, label),
key: url,
props: p,
};
}
export default defineComponent({
components: {
CPWA,
NConfigProvider,
NDialogProvider,
NDivider,
NLoadingBarProvider,
NMenu,
NMessageProvider,
NNotificationProvider,
NSwitch,
},
data(): Data {
return {
activeIndex: "/",
authed: false,
confirmed: false,
darkTheme: darkTheme,
lightTheme: lightTheme,
darkMode: false,
menuRef: null,
leftMenuOptions: options,
rightMenuOptions: [],
};
},
watch: {
$route: {
handler: function (): void {
this.onLoad();
},
immediate: true,
deep: false,
},
},
async beforeMount(): Promise<void> {
await General.genCurrentUser();
this.onLoad();
},
methods: {
onLoad(): void {
this.setAuthed();
this.setActiveIndex();
this.setTheme();
},
setAuthed(): void {
this.$data.authed = General.authSession();
this.$data.confirmed = General.confirmed();
},
setActiveIndex(): void {
let path = window.location.pathname;
if (path.length < 2) {
this.$data.activeIndex = "/";
return;
}
if (path.substring(path.length - 1).localeCompare("/") === 0) {
path = path.slice(0, -1);
}
const paths = path.split("/");
if (!isNaN(Number(paths[paths.length - 1]))) {
path = path.slice(0, -1 * paths[paths.length - 1].length - 1);
}
this.$data.activeIndex = path;
},
async setTheme(): Promise<void> {
this.$data.darkMode = getLocal("theme") === "dark";
const themeSwtich: MenuOption[] = [
{
label: () =>
h(
NSwitch,
{
"onUpdate:value": (value: boolean) => {
setLocal("theme", value ? "dark" : "light");
// Text in CTripPreviewCard color is broken if update in place.
// this.$data.darkMode = value;
router.go(0);
},
defaultValue: this.$data.darkMode,
},
{
"checked-icon": () =>
h(NIcon, null, {
default: () => h(MoonOutline),
}),
checked: () => "Dark mode",
"unchecked-icon": () =>
h(NIcon, null, {
default: () => h(SunnyOutline),
}),
unchecked: () => "Light mode",
},
),
key: "theme_toggle",
},
];
let options: MenuOption[];
if (this.$data.authed) {
options = loggedInOptions;
} else {
options = loggedOutOptions;
}
this.$data.rightMenuOptions = themeSwtich.concat(options);
},
handleSelect(key: string): void {
const path = key;
if (path === this.$data.activeIndex || path === null) {
return;
}
Routing.genRedirectTo(`${path}`);
},
},
});
</script>
<style>
@import "./shared/general.css";
#app {
position: relative;
min-height: 100vh;
text-align: center;
}
.navBarNewLine {
display: none;
}
.main_menu {
width: 100%;
text-align: left;
}
#app .navBarBorder {
margin: 0;
}
.navBarRightMenu {
float: right;
}
@media screen and (max-width: 450px) {
.main_menu {
height: 85px;
text-align: center;
}
.navBarNewLine {
display: unset;
}
.navBarRightMenu {
float: unset;
width: unset;
}
}
@media screen and (max-width: 350px) {
.navBarRightMenu .n-switch__unchecked,
.navBarRightMenu .n-switch__checked,
.navBarRightMenu .n-switch__rail-placeholder {
display: none;
}
}
#app .login_button,
#app .myaccount_button,
#app .register_button {
float: right;
}
#footer {
display: inline-block;
position: absolute;
left: 0px;
bottom: 0px;
height: 40px;
width: 100%;
background-color: #333;
/* color: white; */
}
#footerMargin {
height: 50px;
}
#footerMessage {
margin: 0px;
margin-top: 10px;
}
#footerMessage a {
color: #42b983;
text-decoration: none;
}
#footerMessage a:hover {
text-decoration: underline;
}
#menubar_logout {
background-color: darkred;
border-radius: 10px;
margin: 5px;
}
#menubar_logout:hover {
background-color: red;
}
</style>