main layout

This commit is contained in:
Julian Krauser 2024-08-23 14:42:32 +02:00
parent 62990170de
commit f1e6e8b8d3
38 changed files with 1353 additions and 20 deletions

View file

@ -0,0 +1,61 @@
<template>
<svg class="checkmark min-w-fit min-h-fit" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52">
<circle class="checkmark__circle" cx="26" cy="26" r="25" fill="none" />
<path class="checkmark__check" fill="none" d="M 11 11 l 30 30 M 11 41 l 30 -30" />
<!-- <path class="checkmark__check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8" /> -->
</svg>
</template>
<style scoped>
.checkmark__circle {
stroke-dasharray: 166;
stroke-dashoffset: 166;
stroke-width: 2;
stroke-miterlimit: 10;
stroke: #ff0000;
fill: none;
animation: stroke 0.6s cubic-bezier(0.65, 0, 0.45, 1) forwards;
}
.checkmark {
width: 20px;
height: 20px;
border-radius: 50%;
display: block;
stroke-width: 5;
stroke: #fff;
stroke-miterlimit: 10;
margin: auto 0;
box-shadow: inset 0px 0px 0px #ff0000;
animation:
fill 0.4s ease-in-out 0.4s forwards,
scale 0.3s ease-in-out 0.9s both;
}
.checkmark__check {
transform-origin: 50% 50%;
stroke-dasharray: 48;
stroke-dashoffset: 48;
animation: stroke 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.8s forwards;
}
@keyframes stroke {
100% {
stroke-dashoffset: 0;
}
}
@keyframes scale {
0%,
100% {
transform: none;
}
50% {
transform: scale3d(1.1, 1.1, 1);
}
}
@keyframes fill {
100% {
box-shadow: inset 0px 0px 0px 30px #ff0000;
}
}
</style>

30
src/components/Footer.vue Normal file
View file

@ -0,0 +1,30 @@
<template>
<footer
v-if="authCheck && routeName == 'admin'"
class="md:hidden flex flex-row h-16 justify-center md:justify-normal p-1 bg-white"
>
<div class="w-full flex flex-row gap-2 h-full align-middle">
<TopLevelLink v-for="item in topLevel" :key="item.key" :link="item" :disableSubLink="true" />
</div>
</footer>
</template>
<script setup lang="ts">
import { mapState } from "pinia";
import { useAuthStore } from "../stores/auth";
import { useNavigationStore } from "../stores/admin/navigation";
import TopLevelLink from "./admin/TopLevelLink.vue";
</script>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
computed: {
...mapState(useAuthStore, ["authCheck"]),
...mapState(useNavigationStore, ["topLevel"]),
routeName() {
return this.$route.name;
},
},
});
</script>

36
src/components/Header.vue Normal file
View file

@ -0,0 +1,36 @@
<template>
<header class="flex flex-row h-16 justify-between p-3 md:px-5 bg-white shadow-sm">
<RouterLink to="/" class="flex flex-row gap-2 align-bottom w-fit">
<img src="/FFW-Logo.svg" alt="LOGO" class="h-full w-auto" />
<h1 v-if="false" class="font-bold text-3xl w-fit whitespace-nowrap">Mitgliederverwaltung</h1>
</RouterLink>
<div class="flex flex-row gap-2 items-center">
<div v-if="authCheck && routeName == 'admin'" class="hidden md:flex flex-row gap-2 h-full align-middle">
<TopLevelLink v-for="item in topLevel" :key="item.key" :link="item" />
</div>
<UserMenu v-if="authCheck" />
</div>
</header>
</template>
<script setup lang="ts">
import { RouterLink } from "vue-router";
import { mapState } from "pinia";
import { useAuthStore } from "../stores/auth";
import { useNavigationStore } from "../stores/admin/navigation";
import TopLevelLink from "./admin/TopLevelLink.vue";
import UserMenu from "./UserMenu.vue";
</script>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
computed: {
...mapState(useAuthStore, ["authCheck"]),
...mapState(useNavigationStore, ["topLevel"]),
routeName() {
return this.$route.name;
},
},
});
</script>

View file

@ -0,0 +1,3 @@
<template>
<div class="w-5 h-5 border-y-2 rounded-full border-gray-700 animate-spin" />
</template>

View file

@ -0,0 +1,60 @@
<template>
<svg class="checkmark min-w-fit min-h-fit" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52">
<circle class="checkmark__circle" cx="26" cy="26" r="25" fill="none" />
<path class="checkmark__check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8" />
</svg>
</template>
<style scoped>
.checkmark__circle {
stroke-dasharray: 166;
stroke-dashoffset: 166;
stroke-width: 2;
stroke-miterlimit: 10;
stroke: #7ac142;
fill: none;
animation: stroke 0.6s cubic-bezier(0.65, 0, 0.45, 1) forwards;
}
.checkmark {
width: 20px;
height: 20px;
border-radius: 50%;
display: block;
stroke-width: 5;
stroke: #fff;
stroke-miterlimit: 10;
margin: auto 0;
box-shadow: inset 0px 0px 0px #7ac142;
animation:
fill 0.4s ease-in-out 0.4s forwards,
scale 0.3s ease-in-out 0.9s both;
}
.checkmark__check {
transform-origin: 50% 50%;
stroke-dasharray: 48;
stroke-dashoffset: 48;
animation: stroke 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.8s forwards;
}
@keyframes stroke {
100% {
stroke-dashoffset: 0;
}
}
@keyframes scale {
0%,
100% {
transform: none;
}
50% {
transform: scale3d(1.1, 1.1, 1);
}
}
@keyframes fill {
100% {
box-shadow: inset 0px 0px 0px 30px #7ac142;
}
}
</style>

View file

@ -0,0 +1,53 @@
<template>
<Menu as="div" class="relative inline-block text-left self-center">
<MenuButton class="cursor-pointer flex flex-row gap-2 p-1 w-fit h-fit box-content self-center">
<UserIcon class="text-gray-500 h-6 w-6 cursor-pointer" />
</MenuButton>
<transition
enter-active-class="transition duration-100 ease-out"
enter-from-class="transform scale-95 opacity-0"
enter-to-class="transform scale-100 opacity-100"
leave-active-class="transition duration-75 ease-in"
leave-from-class="transform scale-100 opacity-100"
leave-to-class="transform scale-95 opacity-0"
>
<MenuItems
class="absolute right-0 mt-2 w-56 z-10 origin-top-right divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"
>
<div class="px-3 py-1 pt-2">
<p class="text-xs">Angemeldet als</p>
<p class="font-bold leading-4 text-base">{{ firstname + " " + lastname }}</p>
</div>
<div class="px-1 py-1 w-full flex flex-col gap-2">
<MenuItem v-slot="{ close }">
<RouterLink to="/account">
<button button primary @click="close">Mein Account</button>
</RouterLink>
</MenuItem>
<MenuItem>
<button primary-outline @click="logoutAccount">ausloggen</button>
</MenuItem>
</div>
</MenuItems>
</transition>
</Menu>
</template>
<script setup lang="ts">
import { Menu, MenuButton, MenuItems, MenuItem } from "@headlessui/vue";
import { mapState, mapActions } from "pinia";
import { UserIcon } from "@heroicons/vue/outline";
import { useAccountStore } from "@/stores/account";
</script>
<script lang="ts">
export default {
computed: {
...mapState(useAccountStore, ["firstname", "lastname"]),
},
methods: {
...mapActions(useAccountStore, ["logoutAccount"]),
},
};
</script>

View file

@ -0,0 +1,38 @@
<template>
<div
v-if="link"
class="cursor-pointer w-full px-2 py-3"
:class="
activeLink?.key == link.key
? 'rounded-r-lg bg-red-200 border-l-4 border-l-primary'
: 'pl-3 hover:bg-red-200 rounded-lg'
"
@click="setLink(link.key)"
>
{{ link.title }}
</div>
</template>
<script setup lang="ts">
import { mapState, mapActions } from "pinia";
import { useNavigationStore, type navigationLinkModel } from "@/stores/admin/navigation";
</script>
<script lang="ts">
import { defineComponent, type PropType } from "vue";
export default defineComponent({
props: {
link: {
type: Object as PropType<navigationLinkModel>,
default: null,
},
},
computed: {
...mapState(useNavigationStore, ["activeLink"]),
},
methods: {
...mapActions(useNavigationStore, ["setLink"]),
},
});
</script>
@/stores/contest/viewManager

View file

@ -0,0 +1,41 @@
<template>
<div
v-if="link"
class="cursor-pointer w-full flex flex-col md:flex-row items-center md:gap-2 justify-center p-1 md:rounded-full md:px-3 font-medium text-center text-base self-center"
:class="
activeNavigation == link.key
? 'text-primary md:bg-primary md:text-white'
: 'text-gray-700 hover:text-accent md:hover:bg-accent md:hover:text-white'
"
@click="setTopLevel(link.key, disableSubLink)"
>
{{ link.title }}
</div>
</template>
<script setup lang="ts">
import { mapState, mapActions } from "pinia";
import { useNavigationStore, type topLevelNavigationModel } from "@/stores/admin/navigation";
</script>
<script lang="ts">
import { defineComponent, type PropType } from "vue";
export default defineComponent({
props: {
link: {
type: Object as PropType<topLevelNavigationModel>,
default: null,
},
disableSubLink: {
type: Boolean,
default: false,
},
},
computed: {
...mapState(useNavigationStore, ["activeNavigation"]),
},
methods: {
...mapActions(useNavigationStore, ["setTopLevel"]),
},
});
</script>