gpstracker/frontend/src/App.vue

182 lines
3.5 KiB
Vue

<template>
<Login v-if="!user && !loading" />
<div class="app-container" v-else>
<Header :user="user" @logout="doLogout" v-if="user" />
<div class="body">
<div class="loading-container" v-if="loading">
<Loading />
</div>
<div class="view-container" v-else>
<RouterView />
</div>
</div>
<Messages />
</div>
</template>
<script lang="ts">
import { RouterLink, RouterView } from 'vue-router'
import { type Optional } from './models/Types';
import Api from './mixins/Api.vue';
import Dropdowns from './mixins/Dropdowns.vue';
import Header from './components/Header.vue';
import Loading from './elements/Loading.vue';
import Login from './views/Login.vue';
import Messages from './components/Messages.vue'
import User from './models/User';
export default {
mixins: [
Api,
Dropdowns,
],
components: {
Header,
Loading,
Login,
Messages,
RouterLink,
RouterView,
},
data() {
return {
loading: false,
user: null as Optional<User>,
}
},
methods: {
async doLogout() {
await this.logout()
this.user = null
},
},
async mounted() {
this.loading = true
this.installDropdownHandler()
try {
const auth = await this.fetchUser()
const currentRedirect = this.$route.query.redirect
this.user = auth?.user
if (auth) {
if (currentRedirect?.length) {
this.$router.push(currentRedirect as string)
}
} else {
let redirect = '/login'
if (currentRedirect?.length && currentRedirect !== '/login') {
redirect += `?redirect=${currentRedirect}`
} else if (this.$route.path !== '/login' && this.$route.path !== '/logout') {
redirect += `?redirect=${this.$route.path}${this.$route.hash}`
} else {
redirect += '?redirect=/'
}
this.$router.push(redirect)
}
} finally {
this.loading = false
}
},
}
</script>
<style lang="scss">
@use "@/styles/common.scss" as *;
</style>
<style lang="scss" scoped>
@use "@/styles/common.scss" as *;
.app-container {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
background: var(--color-background);
header {
width: 100%;
height: $header-height;
margin-bottom: 0.2rem;
padding: 0.5rem 0;
box-shadow: 0 0 0.5rem 0 rgba(0, 0, 0, 0.5);
nav {
display: flex;
align-items: center;
li {
display: inline-block;
list-style: none;
border-radius: 0.25rem;
&.main {
font-size: 1.25rem;
margin-left: 0.5rem;
:deep(a) {
&:hover {
background: none;
font-size: 1.5rem;
}
}
&:hover {
margin-top: -0.25rem;
}
}
&:not(.main) {
:deep(a) {
color: var(--color-text);
text-decoration: none;
}
}
&.right {
margin-right: 0.5rem;
}
}
.spacer {
flex: 1;
}
}
}
.body {
width: 100%;
height: calc(100% - #{$header-height});
flex: 1;
overflow-y: auto;
position: relative;
}
.loading-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
display: flex;
justify-content: center;
align-items: center;
}
.view-container {
width: 100%;
height: 100%;
}
}
</style>