main layout
This commit is contained in:
parent
62990170de
commit
f1e6e8b8d3
38 changed files with 1353 additions and 20 deletions
61
src/components/FailureXMark.vue
Normal file
61
src/components/FailureXMark.vue
Normal 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
30
src/components/Footer.vue
Normal 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
36
src/components/Header.vue
Normal 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>
|
3
src/components/Spinner.vue
Normal file
3
src/components/Spinner.vue
Normal file
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<div class="w-5 h-5 border-y-2 rounded-full border-gray-700 animate-spin" />
|
||||
</template>
|
60
src/components/SuccessCheckmark.vue
Normal file
60
src/components/SuccessCheckmark.vue
Normal 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>
|
53
src/components/UserMenu.vue
Normal file
53
src/components/UserMenu.vue
Normal 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>
|
38
src/components/admin/RoutingLink.vue
Normal file
38
src/components/admin/RoutingLink.vue
Normal 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
|
41
src/components/admin/TopLevelLink.vue
Normal file
41
src/components/admin/TopLevelLink.vue
Normal 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>
|
Loading…
Add table
Add a link
Reference in a new issue