This commit is contained in:
Nadar
2026-03-17 13:24:22 +03:00
commit 82e5ac9d81
554 changed files with 29637 additions and 0 deletions

View 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>

View 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>

View File

@@ -0,0 +1,5 @@
<template>
<div class="empty-layout">
<slot />
</div>
</template>