initial
This commit is contained in:
131
apps/client/layouts/auth.vue
Normal file
131
apps/client/layouts/auth.vue
Normal file
@@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<div class="auth-layout">
|
||||
<header class="auth-header">
|
||||
<NuxtLink to="/projects">
|
||||
<img
|
||||
src="/logo.svg"
|
||||
alt="logo"
|
||||
draggable="false"
|
||||
>
|
||||
</NuxtLink>
|
||||
|
||||
<UiButton
|
||||
v-if="headerAction"
|
||||
class="ml-a"
|
||||
type="ghost"
|
||||
:href="headerAction.link"
|
||||
size="large"
|
||||
>
|
||||
{{ headerAction.name }}
|
||||
</UiButton>
|
||||
|
||||
<LangSwitcher />
|
||||
</header>
|
||||
|
||||
<main class="auth-main">
|
||||
<slot />
|
||||
</main>
|
||||
|
||||
<footer class="auth-footer">
|
||||
<div class="auth-footer__left">
|
||||
<UiButton
|
||||
type="link"
|
||||
color="secondary"
|
||||
href="#"
|
||||
>
|
||||
{{ $t('privacy_policy') }}
|
||||
</UiButton>
|
||||
</div>
|
||||
|
||||
<div class="auth-footer__middle">
|
||||
<span class="auth-footer__copyright">
|
||||
{{ $t('copyright', { year }) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="auth-footer__right">
|
||||
<UiButton
|
||||
icon="circle-question"
|
||||
type="link"
|
||||
color="secondary"
|
||||
href="#"
|
||||
>
|
||||
{{ $t('support') }}
|
||||
</UiButton>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const route = useRoute()
|
||||
const { t } = useI18n({ useScope: 'global' })
|
||||
const year = computed(() => new Date().getFullYear())
|
||||
|
||||
const headerAction = computed(() => {
|
||||
switch (route.name) {
|
||||
case 'login':
|
||||
return {
|
||||
name: t('register'),
|
||||
link: '/register',
|
||||
}
|
||||
case 'register':
|
||||
return {
|
||||
name: t('login'),
|
||||
link: '/login',
|
||||
}
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.auth-layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.auth-header {
|
||||
display: flex;
|
||||
padding: 32px 56px 0 56px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.auth-main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-block: 30px;
|
||||
}
|
||||
|
||||
.auth-footer {
|
||||
padding: 24px 56px 32px;
|
||||
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
> * {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&__left {
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
&__middle {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__right {
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
&__copyright {
|
||||
color: $clr-grey-400;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
117
apps/client/layouts/default.vue
Normal file
117
apps/client/layouts/default.vue
Normal file
@@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<div class="default-layout">
|
||||
<NavigationSidebar>
|
||||
<template #logo>
|
||||
<NavigationLogo />
|
||||
</template>
|
||||
|
||||
<template #default>
|
||||
<NavigationItem
|
||||
v-for="item in navItems"
|
||||
:key="item.to"
|
||||
:to="item.to"
|
||||
:icon="item.icon"
|
||||
:title="item.title"
|
||||
:matcher="item.matcher ?? defaultMatcher(item.to)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #bottom>
|
||||
<NavigationItem
|
||||
icon="info"
|
||||
title="Help"
|
||||
to="/_"
|
||||
:matcher="() => ['/help'].includes(route.fullPath)"
|
||||
/>
|
||||
</template>
|
||||
</NavigationSidebar>
|
||||
|
||||
<main
|
||||
class="default-layout__main"
|
||||
:class="{
|
||||
center: shouldCenterContent,
|
||||
narrow: isAnySidebarOpened,
|
||||
}"
|
||||
>
|
||||
<div class="container">
|
||||
<slot />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<ModalsContainer />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ModalsContainer, useVfm } from 'vue-final-modal'
|
||||
|
||||
const { t } = useI18n()
|
||||
const route = useRoute()
|
||||
const { openedModals } = useVfm()
|
||||
|
||||
const defaultMatcher = link => () => route.fullPath === link
|
||||
|
||||
const navItems = computed(() => [
|
||||
{
|
||||
icon: 'merchant',
|
||||
title: t('projects'),
|
||||
to: '/projects',
|
||||
matcher: () =>
|
||||
route.fullPath === '/projects'
|
||||
|| route.fullPath.startsWith('/projects')
|
||||
|| route.meta.alias?.startsWith('/projects'),
|
||||
},
|
||||
])
|
||||
|
||||
const shouldCenterContent = computed(() => route.meta.centerContent)
|
||||
const isAnySidebarOpened = computed(() => openedModals.some(modal => modal.value.modalId.startsWith('sidebar') && modal.value.overlayVisible.value))
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.default-layout {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 120px 1fr;
|
||||
|
||||
grid-area: sidebar;
|
||||
overflow-y: hidden;
|
||||
z-index: 1;
|
||||
|
||||
&__main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
min-height: 100%;
|
||||
padding: 0 35px 32px;
|
||||
transition: padding-right .2s ease-in-out;
|
||||
|
||||
&.center {
|
||||
padding-top: 32px;
|
||||
|
||||
> .container {
|
||||
margin-block: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&.narrow {
|
||||
padding-right: calc(353px + 35px);
|
||||
}
|
||||
|
||||
&:not(.center) {
|
||||
> .container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
> .container {
|
||||
width: 100%;
|
||||
max-width: 1250px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
5
apps/client/layouts/empty.vue
Normal file
5
apps/client/layouts/empty.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div class="empty-layout">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user