This commit is contained in:
Julian Krauser 2024-11-24 15:31:08 +01:00
parent 03710dfdae
commit 9a1ff79f66
43 changed files with 264 additions and 92 deletions

View file

@ -1,6 +1,10 @@
<template> <template>
<div class="relative w-full md:max-w-md"> <div class="relative w-full md:max-w-md">
<TrashIcon class="absolute top-3 right-3 w-5 h-5 cursor-pointer" @click="deleteCalendar" /> <TrashIcon
v-if="can('delete', 'club', 'calendar')"
class="absolute top-3 right-3 w-5 h-5 cursor-pointer"
@click="deleteCalendar"
/>
<div class="flex flex-col items-center"> <div class="flex flex-col items-center">
<p class="text-xl font-medium">Termintyp erstellen</p> <p class="text-xl font-medium">Termintyp erstellen</p>
</div> </div>
@ -191,6 +195,7 @@ import { useCalendarTypeStore } from "@/stores/admin/calendarType";
import type { CalendarTypeViewModel } from "@/viewmodels/admin/calendarType.models"; import type { CalendarTypeViewModel } from "@/viewmodels/admin/calendarType.models";
import cloneDeep from "lodash.clonedeep"; import cloneDeep from "lodash.clonedeep";
import isEqual from "lodash.isEqual"; import isEqual from "lodash.isEqual";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -208,6 +213,7 @@ export default defineComponent({
...mapState(useModalStore, ["data"]), ...mapState(useModalStore, ["data"]),
...mapState(useCalendarStore, ["calendars"]), ...mapState(useCalendarStore, ["calendars"]),
...mapState(useCalendarTypeStore, ["calendarTypes"]), ...mapState(useCalendarTypeStore, ["calendarTypes"]),
...mapState(useAbilityStore, ["can"]),
canSaveOrReset(): boolean { canSaveOrReset(): boolean {
return isEqual(this.origin, this.calendar); return isEqual(this.origin, this.calendar);
}, },

View file

@ -2,8 +2,8 @@
<div class="flex flex-col h-fit w-full border border-primary rounded-md"> <div class="flex flex-col h-fit w-full border border-primary rounded-md">
<div class="bg-primary p-2 text-white flex flex-row gap-2 justify-between items-center"> <div class="bg-primary p-2 text-white flex flex-row gap-2 justify-between items-center">
<p class="grow">{{ award.award }}</p> <p class="grow">{{ award.award }}</p>
<PencilIcon class="w-5 h-5 cursor-pointer" @click="openEditModal" /> <PencilIcon v-if="can('update', 'club', 'member')" class="w-5 h-5 cursor-pointer" @click="openEditModal" />
<TrashIcon class="w-5 h-5 cursor-pointer" @click="openDeleteModal" /> <TrashIcon v-if="can('delete', 'club', 'member')" class="w-5 h-5 cursor-pointer" @click="openDeleteModal" />
</div> </div>
<div class="p-2"> <div class="p-2">
<p>erhalten am: {{ award.date }}</p> <p>erhalten am: {{ award.date }}</p>
@ -19,6 +19,7 @@ import { mapState, mapActions } from "pinia";
import type { MemberAwardViewModel } from "@/viewmodels/admin/memberAward.models"; import type { MemberAwardViewModel } from "@/viewmodels/admin/memberAward.models";
import { PencilIcon, TrashIcon } from "@heroicons/vue/24/outline"; import { PencilIcon, TrashIcon } from "@heroicons/vue/24/outline";
import { useModalStore } from "@/stores/modal"; import { useModalStore } from "@/stores/modal";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -29,6 +30,9 @@ export default defineComponent({
default: {}, default: {},
}, },
}, },
computed: {
...mapState(useAbilityStore, ["can"]),
},
methods: { methods: {
...mapActions(useModalStore, ["openModal"]), ...mapActions(useModalStore, ["openModal"]),
openEditModal() { openEditModal() {

View file

@ -3,8 +3,8 @@
<div class="bg-primary p-2 text-white flex flex-row gap-2 justify-between items-center"> <div class="bg-primary p-2 text-white flex flex-row gap-2 justify-between items-center">
<EnvelopeIcon class="h-5 w-5 pr-1 box-content" v-if="communication.isNewsletterMain" /> <EnvelopeIcon class="h-5 w-5 pr-1 box-content" v-if="communication.isNewsletterMain" />
<p class="grow">{{ communication.type.type }} {{ communication.preferred ? "(bevorzugt)" : "" }}</p> <p class="grow">{{ communication.type.type }} {{ communication.preferred ? "(bevorzugt)" : "" }}</p>
<PencilIcon class="w-5 h-5 cursor-pointer" @click="openEditModal" /> <PencilIcon v-if="can('update', 'club', 'member')" class="w-5 h-5 cursor-pointer" @click="openEditModal" />
<TrashIcon class="w-5 h-5 cursor-pointer" @click="openDeleteModal" /> <TrashIcon v-if="can('delete', 'club', 'member')" class="w-5 h-5 cursor-pointer" @click="openDeleteModal" />
</div> </div>
<div class="p-2"> <div class="p-2">
<p v-for="field in communication.type.fields" :key="field">{{ field }}: {{ communication[field] || "--" }}</p> <p v-for="field in communication.type.fields" :key="field">{{ field }}: {{ communication[field] || "--" }}</p>
@ -18,6 +18,7 @@ import { mapState, mapActions } from "pinia";
import type { CommunicationViewModel } from "@/viewmodels/admin/communication.models"; import type { CommunicationViewModel } from "@/viewmodels/admin/communication.models";
import { EnvelopeIcon, PencilIcon, TrashIcon } from "@heroicons/vue/24/outline"; import { EnvelopeIcon, PencilIcon, TrashIcon } from "@heroicons/vue/24/outline";
import { useModalStore } from "@/stores/modal"; import { useModalStore } from "@/stores/modal";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -28,6 +29,9 @@ export default defineComponent({
default: {}, default: {},
}, },
}, },
computed: {
...mapState(useAbilityStore, ["can"]),
},
methods: { methods: {
...mapActions(useModalStore, ["openModal"]), ...mapActions(useModalStore, ["openModal"]),
openEditModal() { openEditModal() {

View file

@ -2,8 +2,8 @@
<div class="flex flex-col h-fit w-full border border-primary rounded-md"> <div class="flex flex-col h-fit w-full border border-primary rounded-md">
<div class="bg-primary p-2 text-white flex flex-row gap-2 justify-between items-center"> <div class="bg-primary p-2 text-white flex flex-row gap-2 justify-between items-center">
<p class="grow">{{ position.executivePosition }} von {{ position.start }} bis {{ position.end ?? "heute" }}</p> <p class="grow">{{ position.executivePosition }} von {{ position.start }} bis {{ position.end ?? "heute" }}</p>
<PencilIcon class="w-5 h-5 cursor-pointer" @click="openEditModal" /> <PencilIcon v-if="can('update', 'club', 'member')" class="w-5 h-5 cursor-pointer" @click="openEditModal" />
<TrashIcon class="w-5 h-5 cursor-pointer" @click="openDeleteModal" /> <TrashIcon v-if="can('delete', 'club', 'member')" class="w-5 h-5 cursor-pointer" @click="openDeleteModal" />
</div> </div>
<div v-if="position.note" class="p-2"> <div v-if="position.note" class="p-2">
<p v-if="position.note">Notiz: {{ position.note }}</p> <p v-if="position.note">Notiz: {{ position.note }}</p>
@ -17,6 +17,7 @@ import { mapState, mapActions } from "pinia";
import type { MemberExecutivePositionViewModel } from "@/viewmodels/admin/memberExecutivePosition.models"; import type { MemberExecutivePositionViewModel } from "@/viewmodels/admin/memberExecutivePosition.models";
import { PencilIcon, TrashIcon } from "@heroicons/vue/24/outline"; import { PencilIcon, TrashIcon } from "@heroicons/vue/24/outline";
import { useModalStore } from "@/stores/modal"; import { useModalStore } from "@/stores/modal";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -27,6 +28,9 @@ export default defineComponent({
default: {}, default: {},
}, },
}, },
computed: {
...mapState(useAbilityStore, ["can"]),
},
methods: { methods: {
...mapActions(useModalStore, ["openModal"]), ...mapActions(useModalStore, ["openModal"]),
openEditModal() { openEditModal() {

View file

@ -4,8 +4,8 @@
<p class="grow"> <p class="grow">
{{ qualification.qualification }} von {{ qualification.start }} bis {{ qualification.end ?? "heute" }} {{ qualification.qualification }} von {{ qualification.start }} bis {{ qualification.end ?? "heute" }}
</p> </p>
<PencilIcon class="w-5 h-5 cursor-pointer" @click="openEditModal" /> <PencilIcon v-if="can('update', 'club', 'member')" class="w-5 h-5 cursor-pointer" @click="openEditModal" />
<TrashIcon class="w-5 h-5 cursor-pointer" @click="openDeleteModal" /> <TrashIcon v-if="can('delete', 'club', 'member')" class="w-5 h-5 cursor-pointer" @click="openDeleteModal" />
</div> </div>
<div v-if="qualification.note || qualification.terminationReason" class="p-2"> <div v-if="qualification.note || qualification.terminationReason" class="p-2">
<p v-if="qualification.note">Notiz: {{ qualification.note }}</p> <p v-if="qualification.note">Notiz: {{ qualification.note }}</p>
@ -20,6 +20,7 @@ import { mapState, mapActions } from "pinia";
import type { MemberQualificationViewModel } from "@/viewmodels/admin/memberQualification.models"; import type { MemberQualificationViewModel } from "@/viewmodels/admin/memberQualification.models";
import { PencilIcon, TrashIcon } from "@heroicons/vue/24/outline"; import { PencilIcon, TrashIcon } from "@heroicons/vue/24/outline";
import { useModalStore } from "@/stores/modal"; import { useModalStore } from "@/stores/modal";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -30,6 +31,9 @@ export default defineComponent({
default: {}, default: {},
}, },
}, },
computed: {
...mapState(useAbilityStore, ["can"]),
},
methods: { methods: {
...mapActions(useModalStore, ["openModal"]), ...mapActions(useModalStore, ["openModal"]),
openEditModal() { openEditModal() {

View file

@ -5,8 +5,8 @@
{{ membership.start }} bis {{ membership.end ?? "heute" }}: {{ membership.start }} bis {{ membership.end ?? "heute" }}:
{{ membership.status }} {{ membership.status }}
</p> </p>
<PencilIcon class="w-5 h-5 cursor-pointer" @click="openEditModal" /> <PencilIcon v-if="can('update', 'club', 'member')" class="w-5 h-5 cursor-pointer" @click="openEditModal" />
<TrashIcon class="w-5 h-5 cursor-pointer" @click="openDeleteModal" /> <TrashIcon v-if="can('delete', 'club', 'member')" class="w-5 h-5 cursor-pointer" @click="openDeleteModal" />
</div> </div>
<div v-if="membership.terminationReason || membership.internalId" class="p-2"> <div v-if="membership.terminationReason || membership.internalId" class="p-2">
<p v-if="membership.internalId">Interne ID: {{ membership.internalId }}</p> <p v-if="membership.internalId">Interne ID: {{ membership.internalId }}</p>
@ -21,6 +21,7 @@ import { mapState, mapActions } from "pinia";
import type { MembershipViewModel } from "@/viewmodels/admin/membership.models"; import type { MembershipViewModel } from "@/viewmodels/admin/membership.models";
import { PencilIcon, TrashIcon } from "@heroicons/vue/24/outline"; import { PencilIcon, TrashIcon } from "@heroicons/vue/24/outline";
import { useModalStore } from "@/stores/modal"; import { useModalStore } from "@/stores/modal";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -31,6 +32,9 @@ export default defineComponent({
default: {}, default: {},
}, },
}, },
computed: {
...mapState(useAbilityStore, ["can"]),
},
methods: { methods: {
...mapActions(useModalStore, ["openModal"]), ...mapActions(useModalStore, ["openModal"]),
openEditModal() { openEditModal() {

View file

@ -15,7 +15,7 @@
> >
<PencilIcon class="w-5 h-5 p-1 box-content cursor-pointer" /> <PencilIcon class="w-5 h-5 p-1 box-content cursor-pointer" />
</RouterLink> </RouterLink>
<div v-if="can('delete', 'user', 'user')" @click="openDeleteModal"> <div v-if="can('delete', 'user', 'role')" @click="openDeleteModal">
<TrashIcon class="w-5 h-5 p-1 box-content cursor-pointer" /> <TrashIcon class="w-5 h-5 p-1 box-content cursor-pointer" />
</div> </div>
</div> </div>

View file

@ -45,7 +45,8 @@ import { useModalStore } from "@/stores/modal";
import Spinner from "@/components/Spinner.vue"; import Spinner from "@/components/Spinner.vue";
import SuccessCheckmark from "@/components/SuccessCheckmark.vue"; import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
import FailureXMark from "@/components/FailureXMark.vue"; import FailureXMark from "@/components/FailureXMark.vue";
import { useUserStore } from "@/stores/admin/user"; import { useInviteStore } from "@/stores/admin/invite";
import type { CreateInviteViewModel } from "@/viewmodels/admin/invite.models";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -66,16 +67,17 @@ export default defineComponent({
}, },
methods: { methods: {
...mapActions(useModalStore, ["closeModal"]), ...mapActions(useModalStore, ["closeModal"]),
...mapActions(useInviteStore, ["createInvite"]),
invite(e: any) { invite(e: any) {
let formData = e.target.elements; let formData = e.target.elements;
this.status = "loading"; this.status = "loading";
this.$http let createInvite: CreateInviteViewModel = {
.post(`/admin/invite`, { username: formData.username.value,
username: formData.username.value, mail: formData.mail.value,
mail: formData.mail.value, firstname: formData.firstname.value,
firstname: formData.firstname.value, lastname: formData.lastname.value,
lastname: formData.lastname.value, };
}) this.createInvite(createInvite)
.then((result) => { .then((result) => {
this.status = { status: "success" }; this.status = { status: "success" };
}) })

View file

@ -13,7 +13,7 @@
<UserGroupIcon class="w-5 h-5 p-1 box-content cursor-pointer" /> <UserGroupIcon class="w-5 h-5 p-1 box-content cursor-pointer" />
</RouterLink> </RouterLink>
<RouterLink <RouterLink
v-if="can('admin', 'user') && false" v-if="can('admin', 'user', 'user')"
:to="{ name: 'admin-user-user-permission', params: { id: user.id } }" :to="{ name: 'admin-user-user-permission', params: { id: user.id } }"
> >
<WrenchScrewdriverIcon class="w-5 h-5 p-1 box-content cursor-pointer" /> <WrenchScrewdriverIcon class="w-5 h-5 p-1 box-content cursor-pointer" />

View file

@ -41,20 +41,20 @@ const router = createRouter({
], ],
}, },
{ {
path: "/setup", path: "/reset",
name: "setup", name: "reset",
component: () => import("@/views/RouterView.vue"), component: () => import("@/views/RouterView.vue"),
beforeEnter: [isSetup],
children: [ children: [
{ {
path: "", path: "",
name: "setup-create", name: "reset-start",
component: () => import("@/views/setup/Setup.vue"), component: () => import("@/views/reset/Start.vue"),
}, },
{ {
path: "verify", path: "reset",
name: "setup-verify", name: "reset-reset",
component: () => import("@/views/setup/Verify.vue"), component: () => import("@/views/reset/Reset.vue"),
props: (route) => ({ mail: route.query.mail, token: route.query.token }), props: (route) => ({ mail: route.query.mail, token: route.query.token }),
}, },
], ],
@ -107,56 +107,56 @@ const router = createRouter({
{ {
path: "", path: "",
name: "admin-club-member", name: "admin-club-member",
component: () => import("@/views/admin/members/Member.vue"), component: () => import("@/views/admin/club/members/Member.vue"),
beforeEnter: [resetMemberStores], beforeEnter: [resetMemberStores],
}, },
{ {
path: ":memberId", path: ":memberId",
name: "admin-club-member-routing", name: "admin-club-member-routing",
component: () => import("@/views/admin/members/MemberRouting.vue"), component: () => import("@/views/admin/club/members/MemberRouting.vue"),
beforeEnter: [setMemberId], beforeEnter: [setMemberId],
props: true, props: true,
children: [ children: [
{ {
path: "overview", path: "overview",
name: "admin-club-member-overview", name: "admin-club-member-overview",
component: () => import("@/views/admin/members/MemberOverview.vue"), component: () => import("@/views/admin/club/members/MemberOverview.vue"),
props: true, props: true,
}, },
{ {
path: "membership", path: "membership",
name: "admin-club-member-membership", name: "admin-club-member-membership",
component: () => import("@/views/admin/members/Membership.vue"), component: () => import("@/views/admin/club/members/Membership.vue"),
props: true, props: true,
}, },
{ {
path: "communication", path: "communication",
name: "admin-club-member-communication", name: "admin-club-member-communication",
component: () => import("@/views/admin/members/MemberCommunication.vue"), component: () => import("@/views/admin/club/members/MemberCommunication.vue"),
props: true, props: true,
}, },
{ {
path: "awards", path: "awards",
name: "admin-club-member-awards", name: "admin-club-member-awards",
component: () => import("@/views/admin/members/MemberAwards.vue"), component: () => import("@/views/admin/club/members/MemberAwards.vue"),
props: true, props: true,
}, },
{ {
path: "qualifications", path: "qualifications",
name: "admin-club-member-qualifications", name: "admin-club-member-qualifications",
component: () => import("@/views/admin/members/MemberQualifications.vue"), component: () => import("@/views/admin/club/members/MemberQualifications.vue"),
props: true, props: true,
}, },
{ {
path: "positions", path: "positions",
name: "admin-club-member-positions", name: "admin-club-member-positions",
component: () => import("@/views/admin/members/MemberExecutivePositions.vue"), component: () => import("@/views/admin/club/members/MemberExecutivePositions.vue"),
props: true, props: true,
}, },
{ {
path: "edit", path: "edit",
name: "admin-club-member-edit", name: "admin-club-member-edit",
component: () => import("@/views/admin/members/MemberEdit.vue"), component: () => import("@/views/admin/club/members/MemberEdit.vue"),
meta: { type: "update", section: "club", module: "member" }, meta: { type: "update", section: "club", module: "member" },
beforeEnter: [abilityAndNavUpdate], beforeEnter: [abilityAndNavUpdate],
props: true, props: true,
@ -175,7 +175,7 @@ const router = createRouter({
{ {
path: "newsletter", path: "newsletter",
name: "admin-club-newsletter", name: "admin-club-newsletter",
component: () => import("@/views/admin/members/Overview.vue"), component: () => import("@/views/admin/ViewSelect.vue"),
meta: { type: "read", section: "club", module: "newsletter" }, meta: { type: "read", section: "club", module: "newsletter" },
beforeEnter: [abilityAndNavUpdate], beforeEnter: [abilityAndNavUpdate],
}, },

View file

@ -30,7 +30,7 @@ export const useCalendarStore = defineStore("calendar", {
fetchCalendars() { fetchCalendars() {
this.loading = "loading"; this.loading = "loading";
http http
.get("/admin/calendar/items") .get("/admin/calendar")
.then((result) => { .then((result) => {
this.calendars = result.data; this.calendars = result.data;
this.loading = "fetched"; this.loading = "fetched";
@ -40,10 +40,10 @@ export const useCalendarStore = defineStore("calendar", {
}); });
}, },
fetchCalendarById(id: string): Promise<AxiosResponse<any, any>> { fetchCalendarById(id: string): Promise<AxiosResponse<any, any>> {
return http.get(`/admin/calendar/item/${id}`); return http.get(`/admin/calendar/${id}`);
}, },
async createCalendar(calendar: CreateCalendarViewModel): Promise<AxiosResponse<any, any>> { async createCalendar(calendar: CreateCalendarViewModel): Promise<AxiosResponse<any, any>> {
const result = await http.post(`/admin/calendar/item`, { const result = await http.post(`/admin/calendar`, {
starttime: calendar.starttime, starttime: calendar.starttime,
endtime: calendar.endtime, endtime: calendar.endtime,
title: calendar.title, title: calendar.title,
@ -56,7 +56,7 @@ export const useCalendarStore = defineStore("calendar", {
return result; return result;
}, },
async updateCalendar(calendar: UpdateCalendarViewModel): Promise<AxiosResponse<any, any>> { async updateCalendar(calendar: UpdateCalendarViewModel): Promise<AxiosResponse<any, any>> {
const result = await http.patch(`/admin/calendar/item/${calendar.id}`, { const result = await http.patch(`/admin/calendar/${calendar.id}`, {
starttime: calendar.starttime, starttime: calendar.starttime,
endtime: calendar.endtime, endtime: calendar.endtime,
title: calendar.title, title: calendar.title,
@ -69,7 +69,7 @@ export const useCalendarStore = defineStore("calendar", {
return result; return result;
}, },
async deleteCalendar(calendar: number): Promise<AxiosResponse<any, any>> { async deleteCalendar(calendar: number): Promise<AxiosResponse<any, any>> {
const result = await http.delete(`/admin/calendar/item/${calendar}`); const result = await http.delete(`/admin/calendar/${calendar}`);
this.fetchCalendars(); this.fetchCalendars();
return result; return result;
}, },

View file

@ -19,7 +19,7 @@ export const useCalendarTypeStore = defineStore("calendarType", {
fetchCalendarTypes() { fetchCalendarTypes() {
this.loading = "loading"; this.loading = "loading";
http http
.get("/admin/calendar/types") .get("/admin/calendartype")
.then((result) => { .then((result) => {
this.calendarTypes = result.data; this.calendarTypes = result.data;
this.loading = "fetched"; this.loading = "fetched";
@ -29,10 +29,10 @@ export const useCalendarTypeStore = defineStore("calendarType", {
}); });
}, },
fetchCalendarTypeById(id: number): Promise<AxiosResponse<any, any>> { fetchCalendarTypeById(id: number): Promise<AxiosResponse<any, any>> {
return http.get(`/admin/calendar/type/${id}`); return http.get(`/admin/calendartype/${id}`);
}, },
async createCalendarType(calendarType: CreateCalendarTypeViewModel): Promise<AxiosResponse<any, any>> { async createCalendarType(calendarType: CreateCalendarTypeViewModel): Promise<AxiosResponse<any, any>> {
const result = await http.post(`/admin/calendar/type`, { const result = await http.post(`/admin/calendartype`, {
type: calendarType.type, type: calendarType.type,
nscdr: calendarType.nscdr, nscdr: calendarType.nscdr,
color: calendarType.color, color: calendarType.color,
@ -41,7 +41,7 @@ export const useCalendarTypeStore = defineStore("calendarType", {
return result; return result;
}, },
async updateActiveCalendarType(calendarType: UpdateCalendarTypeViewModel): Promise<AxiosResponse<any, any>> { async updateActiveCalendarType(calendarType: UpdateCalendarTypeViewModel): Promise<AxiosResponse<any, any>> {
const result = await http.patch(`/admin/calendar/type/${calendarType.id}`, { const result = await http.patch(`/admin/calendartype/${calendarType.id}`, {
type: calendarType.type, type: calendarType.type,
nscdr: calendarType.nscdr, nscdr: calendarType.nscdr,
color: calendarType.color, color: calendarType.color,
@ -50,7 +50,7 @@ export const useCalendarTypeStore = defineStore("calendarType", {
return result; return result;
}, },
async deleteCalendarType(calendarType: number): Promise<AxiosResponse<any, any>> { async deleteCalendarType(calendarType: number): Promise<AxiosResponse<any, any>> {
const result = await http.delete(`/admin/calendar/type/${calendarType}`); const result = await http.delete(`/admin/calendartype/${calendarType}`);
this.fetchCalendarTypes(); this.fetchCalendarTypes();
return result; return result;
}, },

View file

@ -1,5 +1,5 @@
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import type { InviteViewModel } from "@/viewmodels/admin/invite.models"; import type { CreateInviteViewModel, InviteViewModel } from "@/viewmodels/admin/invite.models";
import { http } from "@/serverCom"; import { http } from "@/serverCom";
import type { PermissionObject } from "@/types/permissionTypes"; import type { PermissionObject } from "@/types/permissionTypes";
import type { AxiosResponse } from "axios"; import type { AxiosResponse } from "axios";
@ -24,11 +24,18 @@ export const useInviteStore = defineStore("invite", {
this.loading = "failed"; this.loading = "failed";
}); });
}, },
deleteInvite(mail: string): Promise<AxiosResponse<any, any>> { createInvite(createInvite: CreateInviteViewModel): Promise<AxiosResponse<any, any>> {
return http.delete(`/admin/invite/${mail}`).then((result) => { return http.post(`/admin/invite`, {
this.fetchInvites(); username: createInvite.username,
return result; mail: createInvite.mail,
firstname: createInvite.firstname,
lastname: createInvite.lastname,
}); });
}, },
async deleteInvite(mail: string): Promise<AxiosResponse<any, any>> {
const result = await http.delete(`/admin/invite/${mail}`);
this.fetchInvites();
return result;
},
}, },
}); });

View file

@ -1,5 +1,11 @@
export interface InviteViewModel { export interface InviteViewModel {
id: number; username: string;
mail: string;
firstname: string;
lastname: string;
}
export interface CreateInviteViewModel {
username: string; username: string;
mail: string; mail: string;
firstname: string; firstname: string;

View file

@ -23,7 +23,8 @@ import deLocale from "@fullcalendar/core/locales/de";
import dayGridPlugin from "@fullcalendar/daygrid"; import dayGridPlugin from "@fullcalendar/daygrid";
import timeGridPlugin from "@fullcalendar/timegrid"; import timeGridPlugin from "@fullcalendar/timegrid";
import interactionPlugin from "@fullcalendar/interaction"; import interactionPlugin from "@fullcalendar/interaction";
import { useCalendarStore } from "../../../../stores/admin/calendar"; import { useCalendarStore } from "@/stores/admin/calendar";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -33,6 +34,7 @@ export default defineComponent({
}, },
computed: { computed: {
...mapState(useCalendarStore, ["formattedItems"]), ...mapState(useCalendarStore, ["formattedItems"]),
...mapState(useAbilityStore, ["can"]),
calendarOptions() { calendarOptions() {
return { return {
timeZone: "local", timeZone: "local",
@ -68,7 +70,7 @@ export default defineComponent({
...mapActions(useModalStore, ["openModal"]), ...mapActions(useModalStore, ["openModal"]),
...mapActions(useCalendarStore, ["fetchCalendars"]), ...mapActions(useCalendarStore, ["fetchCalendars"]),
select(e: any) { select(e: any) {
console.log(e); if (!this.can("create", "club", "calendar")) return;
this.openModal( this.openModal(
markRaw(defineAsyncComponent(() => import("@/components/admin/club/calendar/CreateCalendarModal.vue"))), markRaw(defineAsyncComponent(() => import("@/components/admin/club/calendar/CreateCalendarModal.vue"))),
{ {
@ -79,6 +81,7 @@ export default defineComponent({
); );
}, },
eventClick(e: any) { eventClick(e: any) {
if (!this.can("update", "club", "calendar")) return;
this.openModal( this.openModal(
markRaw(defineAsyncComponent(() => import("@/components/admin/club/calendar/UpdateCalendarModal.vue"))), markRaw(defineAsyncComponent(() => import("@/components/admin/club/calendar/UpdateCalendarModal.vue"))),
e.event.id e.event.id

View file

@ -20,7 +20,9 @@
</Pagination> </Pagination>
<div class="flex flex-row gap-4"> <div class="flex flex-row gap-4">
<button primary class="!w-fit" @click="openCreateModal">Mitglied erstellen</button> <button v-if="can('create', 'club', 'member')" primary class="!w-fit" @click="openCreateModal">
Mitglied erstellen
</button>
</div> </div>
</div> </div>
</template> </template>
@ -35,8 +37,9 @@ import { ChevronRightIcon, ChevronLeftIcon } from "@heroicons/vue/20/solid";
import { useMemberStore } from "@/stores/admin/member"; import { useMemberStore } from "@/stores/admin/member";
import MemberListItem from "@/components/admin/club/member/MemberListItem.vue"; import MemberListItem from "@/components/admin/club/member/MemberListItem.vue";
import { useModalStore } from "@/stores/modal"; import { useModalStore } from "@/stores/modal";
import Pagination from "../../../components/Pagination.vue"; import Pagination from "@/components/Pagination.vue";
import type { MemberViewModel } from "../../../viewmodels/admin/member.models"; import type { MemberViewModel } from "@/viewmodels/admin/member.models";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -49,6 +52,7 @@ export default defineComponent({
}, },
computed: { computed: {
...mapState(useMemberStore, ["members", "totalCount", "loading"]), ...mapState(useMemberStore, ["members", "totalCount", "loading"]),
...mapState(useAbilityStore, ["can"]),
entryCount() { entryCount() {
return this.totalCount ?? this.members.length; return this.totalCount ?? this.members.length;
}, },

View file

@ -7,7 +7,9 @@
<p v-else-if="loading == 'failed'" @click="fetchItem" class="cursor-pointer">&#8634; laden fehlgeschlagen</p> <p v-else-if="loading == 'failed'" @click="fetchItem" class="cursor-pointer">&#8634; laden fehlgeschlagen</p>
</div> </div>
<div class="flex flex-row gap-4"> <div class="flex flex-row gap-4">
<button primary class="!w-fit" @click="openCreateModal">Auszeichnung hinzufügen</button> <button v-if="can('create', 'club', 'member')" primary class="!w-fit" @click="openCreateModal">
Auszeichnung hinzufügen
</button>
</div> </div>
</template> </template>
@ -18,6 +20,7 @@ import Spinner from "@/components/Spinner.vue";
import { useMemberAwardStore } from "@/stores/admin/memberAward"; import { useMemberAwardStore } from "@/stores/admin/memberAward";
import MemberAwardListItem from "@/components/admin/club/member/MemberAwardListItem.vue"; import MemberAwardListItem from "@/components/admin/club/member/MemberAwardListItem.vue";
import { useModalStore } from "@/stores/modal"; import { useModalStore } from "@/stores/modal";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -27,6 +30,7 @@ export default defineComponent({
}, },
computed: { computed: {
...mapState(useMemberAwardStore, ["memberAwards", "loading"]), ...mapState(useMemberAwardStore, ["memberAwards", "loading"]),
...mapState(useAbilityStore, ["can"]),
}, },
mounted() { mounted() {
this.fetchItem(); this.fetchItem();

View file

@ -11,7 +11,9 @@
<p v-else-if="loading == 'failed'" @click="fetchItem" class="cursor-pointer">&#8634; laden fehlgeschlagen</p> <p v-else-if="loading == 'failed'" @click="fetchItem" class="cursor-pointer">&#8634; laden fehlgeschlagen</p>
</div> </div>
<div class="flex flex-row gap-4"> <div class="flex flex-row gap-4">
<button primary class="!w-fit" @click="openCreateModal">Kommunikation hinzufügen</button> <button v-if="can('create', 'club', 'member')" primary class="!w-fit" @click="openCreateModal">
Kommunikation hinzufügen
</button>
</div> </div>
</template> </template>
@ -25,6 +27,7 @@ import type { CommunicationViewModel } from "@/viewmodels/admin/communication.mo
import { useCommunicationStore } from "@/stores/admin/communication"; import { useCommunicationStore } from "@/stores/admin/communication";
import MemberCommunicationListItem from "@/components/admin/club/member/MemberCommunicationListItem.vue"; import MemberCommunicationListItem from "@/components/admin/club/member/MemberCommunicationListItem.vue";
import { useModalStore } from "@/stores/modal"; import { useModalStore } from "@/stores/modal";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -34,6 +37,7 @@ export default defineComponent({
}, },
computed: { computed: {
...mapState(useCommunicationStore, ["communications", "loading"]), ...mapState(useCommunicationStore, ["communications", "loading"]),
...mapState(useAbilityStore, ["can"]),
}, },
mounted() { mounted() {
this.fetchItem(); this.fetchItem();

View file

@ -11,7 +11,9 @@
<p v-else-if="loading == 'failed'" @click="fetchItem" class="cursor-pointer">&#8634; laden fehlgeschlagen</p> <p v-else-if="loading == 'failed'" @click="fetchItem" class="cursor-pointer">&#8634; laden fehlgeschlagen</p>
</div> </div>
<div class="flex flex-row gap-4"> <div class="flex flex-row gap-4">
<button primary class="!w-fit" @click="openCreateModal">Vereinsamt hinzufügen</button> <button v-if="can('create', 'club', 'member')" primary class="!w-fit" @click="openCreateModal">
Vereinsamt hinzufügen
</button>
</div> </div>
</template> </template>
@ -22,6 +24,7 @@ import Spinner from "@/components/Spinner.vue";
import { useMemberExecutivePositionStore } from "@/stores/admin/memberExecutivePosition"; import { useMemberExecutivePositionStore } from "@/stores/admin/memberExecutivePosition";
import MemberExecutivePositionListItem from "@/components/admin/club/member/MemberExecutivePositionListItem.vue"; import MemberExecutivePositionListItem from "@/components/admin/club/member/MemberExecutivePositionListItem.vue";
import { useModalStore } from "@/stores/modal"; import { useModalStore } from "@/stores/modal";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -31,6 +34,7 @@ export default defineComponent({
}, },
computed: { computed: {
...mapState(useMemberExecutivePositionStore, ["memberExecutivePositions", "loading"]), ...mapState(useMemberExecutivePositionStore, ["memberExecutivePositions", "loading"]),
...mapState(useAbilityStore, ["can"]),
}, },
mounted() { mounted() {
this.fetchItem(); this.fetchItem();

View file

@ -11,7 +11,9 @@
<p v-else-if="loading == 'failed'" @click="fetchItem" class="cursor-pointer">&#8634; laden fehlgeschlagen</p> <p v-else-if="loading == 'failed'" @click="fetchItem" class="cursor-pointer">&#8634; laden fehlgeschlagen</p>
</div> </div>
<div class="flex flex-row gap-4"> <div class="flex flex-row gap-4">
<button primary class="!w-fit" @click="openCreateModal">Qualifikation hinzufügen</button> <button v-if="can('create', 'club', 'member')" primary class="!w-fit" @click="openCreateModal">
Qualifikation hinzufügen
</button>
</div> </div>
</template> </template>
@ -22,6 +24,7 @@ import Spinner from "@/components/Spinner.vue";
import { useMemberQualificationStore } from "@/stores/admin/memberQualification"; import { useMemberQualificationStore } from "@/stores/admin/memberQualification";
import MemberQualificationListItem from "@/components/admin/club/member/MemberQualificationListItem.vue"; import MemberQualificationListItem from "@/components/admin/club/member/MemberQualificationListItem.vue";
import { useModalStore } from "@/stores/modal"; import { useModalStore } from "@/stores/modal";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -31,6 +34,7 @@ export default defineComponent({
}, },
computed: { computed: {
...mapState(useMemberQualificationStore, ["memberQualifications", "loading"]), ...mapState(useMemberQualificationStore, ["memberQualifications", "loading"]),
...mapState(useAbilityStore, ["can"]),
}, },
mounted() { mounted() {
this.fetchItem(); this.fetchItem();

View file

@ -9,10 +9,10 @@
{{ activeMemberObj?.lastname }}, {{ activeMemberObj?.firstname }} {{ activeMemberObj?.lastname }}, {{ activeMemberObj?.firstname }}
{{ activeMemberObj?.nameaffix ? `- ${activeMemberObj?.nameaffix}` : "" }} {{ activeMemberObj?.nameaffix ? `- ${activeMemberObj?.nameaffix}` : "" }}
</h1> </h1>
<RouterLink :to="{ name: 'admin-club-member-edit' }"> <RouterLink v-if="can('update', 'club', 'member')" :to="{ name: 'admin-club-member-edit' }">
<PencilIcon class="w-5 h-5" /> <PencilIcon class="w-5 h-5" />
</RouterLink> </RouterLink>
<TrashIcon class="w-5 h-5 cursor-pointer" @click="openDeleteModal" /> <TrashIcon v-if="can('delete', 'club', 'member')" class="w-5 h-5 cursor-pointer" @click="openDeleteModal" />
</div> </div>
</template> </template>
<template #diffMain> <template #diffMain>
@ -51,6 +51,7 @@ import { RouterLink, RouterView } from "vue-router";
import { useMemberStore } from "@/stores/admin/member"; import { useMemberStore } from "@/stores/admin/member";
import { PencilIcon, TrashIcon } from "@heroicons/vue/24/outline"; import { PencilIcon, TrashIcon } from "@heroicons/vue/24/outline";
import { useModalStore } from "@/stores/modal"; import { useModalStore } from "@/stores/modal";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -72,6 +73,7 @@ export default defineComponent({
}, },
computed: { computed: {
...mapState(useMemberStore, ["activeMemberObj"]), ...mapState(useMemberStore, ["activeMemberObj"]),
...mapState(useAbilityStore, ["can"]),
}, },
mounted() { mounted() {
this.fetchMemberByActiveId(); this.fetchMemberByActiveId();

View file

@ -7,7 +7,9 @@
<p v-else-if="loading == 'failed'" @click="fetchItem" class="cursor-pointer">&#8634; laden fehlgeschlagen</p> <p v-else-if="loading == 'failed'" @click="fetchItem" class="cursor-pointer">&#8634; laden fehlgeschlagen</p>
</div> </div>
<div class="flex flex-row gap-4"> <div class="flex flex-row gap-4">
<button primary class="!w-fit" @click="openCreateModal">Mitgliedschaft hinzufügen</button> <button v-if="can('create', 'club', 'member')" primary class="!w-fit" @click="openCreateModal">
Mitgliedschaft hinzufügen
</button>
</div> </div>
</template> </template>
@ -18,6 +20,7 @@ import Spinner from "@/components/Spinner.vue";
import { useMembershipStore } from "@/stores/admin/membership"; import { useMembershipStore } from "@/stores/admin/membership";
import { useModalStore } from "@/stores/modal"; import { useModalStore } from "@/stores/modal";
import MembershipListItem from "@/components/admin/club/member/MembershipListItem.vue"; import MembershipListItem from "@/components/admin/club/member/MembershipListItem.vue";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -27,6 +30,7 @@ export default defineComponent({
}, },
computed: { computed: {
...mapState(useMembershipStore, ["memberships", "loading"]), ...mapState(useMembershipStore, ["memberships", "loading"]),
...mapState(useAbilityStore, ["can"]),
}, },
mounted() { mounted() {
this.fetchItem(); this.fetchItem();

View file

@ -20,7 +20,9 @@
</Pagination> </Pagination>
<div class="flex flex-row gap-4"> <div class="flex flex-row gap-4">
<button primary class="!w-fit" @click="openCreateModal">Protokoll erstellen</button> <button v-if="can('create', 'club', 'protocol')" primary class="!w-fit" @click="openCreateModal">
Protokoll erstellen
</button>
</div> </div>
</div> </div>
</template> </template>
@ -35,8 +37,9 @@ import { ChevronRightIcon, ChevronLeftIcon } from "@heroicons/vue/20/solid";
import { useProtocolStore } from "@/stores/admin/protocol"; import { useProtocolStore } from "@/stores/admin/protocol";
import ProtocolListItem from "@/components/admin/club/protocol/ProtocolListItem.vue"; import ProtocolListItem from "@/components/admin/club/protocol/ProtocolListItem.vue";
import { useModalStore } from "@/stores/modal"; import { useModalStore } from "@/stores/modal";
import Pagination from "../../../../components/Pagination.vue"; import Pagination from "@/components/Pagination.vue";
import type { ProtocolViewModel } from "../../../../viewmodels/admin/protocol.models"; import type { ProtocolViewModel } from "@/viewmodels/admin/protocol.models";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -49,6 +52,7 @@ export default defineComponent({
}, },
computed: { computed: {
...mapState(useProtocolStore, ["protocols", "totalCount", "loading"]), ...mapState(useProtocolStore, ["protocols", "totalCount", "loading"]),
...mapState(useAbilityStore, ["can"]),
}, },
mounted() { mounted() {
this.fetchProtocols(0, this.maxEntriesPerPage, true); this.fetchProtocols(0, this.maxEntriesPerPage, true);

View file

@ -27,6 +27,7 @@
autocomplete="off" autocomplete="off"
v-model="item.topic" v-model="item.topic"
@keyup.prevent @keyup.prevent
:disabled="!can('create', 'club', 'protocol')"
/> />
</summary> </summary>
<QuillEditor <QuillEditor
@ -37,11 +38,15 @@
contentType="html" contentType="html"
:toolbar="toolbarOptions" :toolbar="toolbarOptions"
v-model:content="item.context" v-model:content="item.context"
:enable="can('create', 'club', 'protocol')"
:style="!can('create', 'club', 'protocol') ? 'opacity: 75%; background: rgb(243 244 246)' : ''"
/> />
</details> </details>
</div> </div>
<button primary class="!w-fit" @click="createProtocolAgenda">Eintrag hinzufügen</button> <button v-if="can('create', 'club', 'protocol')" primary class="!w-fit" @click="createProtocolAgenda">
Eintrag hinzufügen
</button>
</div> </div>
</template> </template>
@ -54,6 +59,7 @@ import "@vueup/vue-quill/dist/vue-quill.snow.css";
import { toolbarOptions } from "@/helpers/quillConfig"; import { toolbarOptions } from "@/helpers/quillConfig";
import { useProtocolAgendaStore } from "@/stores/admin/protocolAgenda"; import { useProtocolAgendaStore } from "@/stores/admin/protocolAgenda";
import type { ProtocolAgendaViewModel } from "@/viewmodels/admin/protocolAgenda.models"; import type { ProtocolAgendaViewModel } from "@/viewmodels/admin/protocolAgenda.models";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -63,6 +69,7 @@ export default defineComponent({
}, },
computed: { computed: {
...mapWritableState(useProtocolAgendaStore, ["agenda", "loading"]), ...mapWritableState(useProtocolAgendaStore, ["agenda", "loading"]),
...mapState(useAbilityStore, ["can"]),
}, },
mounted() { mounted() {
this.fetchProtocolAgenda(); this.fetchProtocolAgenda();

View file

@ -27,6 +27,7 @@
autocomplete="off" autocomplete="off"
v-model="item.topic" v-model="item.topic"
@keyup.prevent @keyup.prevent
:disabled="!can('create', 'club', 'protocol')"
/> />
</summary> </summary>
<QuillEditor <QuillEditor
@ -37,11 +38,15 @@
contentType="html" contentType="html"
:toolbar="toolbarOptions" :toolbar="toolbarOptions"
v-model:content="item.context" v-model:content="item.context"
:enable="can('create', 'club', 'protocol')"
:style="!can('create', 'club', 'protocol') ? 'opacity: 75%; background: rgb(243 244 246)' : ''"
/> />
</details> </details>
</div> </div>
<button primary class="!w-fit" @click="createProtocolDecision">Eintrag hinzufügen</button> <button v-if="can('create', 'club', 'protocol')" primary class="!w-fit" @click="createProtocolDecision">
Eintrag hinzufügen
</button>
</div> </div>
</template> </template>
@ -53,7 +58,8 @@ import { useProtocolStore } from "@/stores/admin/protocol";
import { QuillEditor } from "@vueup/vue-quill"; import { QuillEditor } from "@vueup/vue-quill";
import "@vueup/vue-quill/dist/vue-quill.snow.css"; import "@vueup/vue-quill/dist/vue-quill.snow.css";
import { toolbarOptions } from "@/helpers/quillConfig"; import { toolbarOptions } from "@/helpers/quillConfig";
import { useProtocolDecisionStore } from "../../../../stores/admin/protocolDecision"; import { useProtocolDecisionStore } from "@/stores/admin/protocolDecision";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -63,6 +69,7 @@ export default defineComponent({
}, },
computed: { computed: {
...mapWritableState(useProtocolDecisionStore, ["decision", "loading"]), ...mapWritableState(useProtocolDecisionStore, ["decision", "loading"]),
...mapState(useAbilityStore, ["can"]),
}, },
mounted() { mounted() {
this.fetchProtocolDecision(); this.fetchProtocolDecision();

View file

@ -3,20 +3,37 @@
<div v-if="activeProtocolObj != null" class="flex flex-col gap-2 w-full"> <div v-if="activeProtocolObj != null" class="flex flex-col gap-2 w-full">
<div class="w-full"> <div class="w-full">
<label for="title">Titel</label> <label for="title">Titel</label>
<input type="text" id="title" v-model="activeProtocolObj.title" /> <input
type="text"
id="title"
v-model="activeProtocolObj.title"
:disabled="!can('create', 'club', 'protocol')"
/>
</div> </div>
<div class="w-full"> <div class="w-full">
<label for="date">Datum</label> <label for="date">Datum</label>
<input type="date" id="date" v-model="activeProtocolObj.date" /> <input type="date" id="date" v-model="activeProtocolObj.date" :disabled="!can('create', 'club', 'protocol')" />
</div> </div>
<div class="flex flex-row gap-2 w-full"> <div class="flex flex-row gap-2 w-full">
<div class="w-full"> <div class="w-full">
<label for="starttime">Startzeit</label> <label for="starttime">Startzeit</label>
<input type="time" id="starttime" step="1" v-model="activeProtocolObj.starttime" /> <input
type="time"
id="starttime"
step="1"
v-model="activeProtocolObj.starttime"
:disabled="!can('create', 'club', 'protocol')"
/>
</div> </div>
<div class="w-full"> <div class="w-full">
<label for="endtime">Endzeit</label> <label for="endtime">Endzeit</label>
<input type="time" id="endtime" step="1" v-model="activeProtocolObj.endtime" /> <input
type="time"
id="endtime"
step="1"
v-model="activeProtocolObj.endtime"
:disabled="!can('create', 'club', 'protocol')"
/>
</div> </div>
</div> </div>
<div class="flex flex-col h-1/2"> <div class="flex flex-col h-1/2">
@ -29,6 +46,8 @@
contentType="html" contentType="html"
:toolbar="toolbarOptions" :toolbar="toolbarOptions"
v-model:content="activeProtocolObj.summary" v-model:content="activeProtocolObj.summary"
:enable="can('create', 'club', 'protocol')"
:style="!can('create', 'club', 'protocol') ? 'opacity: 75%; background: rgb(243 244 246)' : ''"
/> />
</div> </div>
</div> </div>
@ -48,6 +67,7 @@ import { useProtocolStore } from "@/stores/admin/protocol";
import { QuillEditor } from "@vueup/vue-quill"; import { QuillEditor } from "@vueup/vue-quill";
import "@vueup/vue-quill/dist/vue-quill.snow.css"; import "@vueup/vue-quill/dist/vue-quill.snow.css";
import { toolbarOptions } from "@/helpers/quillConfig"; import { toolbarOptions } from "@/helpers/quillConfig";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -57,6 +77,7 @@ export default defineComponent({
}, },
computed: { computed: {
...mapWritableState(useProtocolStore, ["loadingActive", "activeProtocolObj"]), ...mapWritableState(useProtocolStore, ["loadingActive", "activeProtocolObj"]),
...mapState(useAbilityStore, ["can"]),
}, },
mounted() { mounted() {
this.fetchProtocolByActiveId(); this.fetchProtocolByActiveId();

View file

@ -6,7 +6,7 @@
</p> </p>
<div class="w-full"> <div class="w-full">
<Combobox v-model="presence" multiple> <Combobox v-model="presence" :disabled="!can('create', 'club', 'protocol')" multiple>
<ComboboxLabel>Anwesende suchen</ComboboxLabel> <ComboboxLabel>Anwesende suchen</ComboboxLabel>
<div class="relative mt-1"> <div class="relative mt-1">
<ComboboxInput <ComboboxInput
@ -71,7 +71,11 @@
class="flex flex-row h-fit w-full border border-primary rounded-md bg-primary p-2 text-white justify-between items-center" class="flex flex-row h-fit w-full border border-primary rounded-md bg-primary p-2 text-white justify-between items-center"
> >
<p>{{ member.lastname }}, {{ member.firstname }} {{ member.nameaffix ? `- ${member.nameaffix}` : "" }}</p> <p>{{ member.lastname }}, {{ member.firstname }} {{ member.nameaffix ? `- ${member.nameaffix}` : "" }}</p>
<TrashIcon class="w-5 h-5 p-1 box-content cursor-pointer" @click="removeSelected(member.id)" /> <TrashIcon
v-if="can('create', 'club', 'protocol')"
class="w-5 h-5 p-1 box-content cursor-pointer"
@click="removeSelected(member.id)"
/>
</div> </div>
</div> </div>
</div> </div>
@ -96,6 +100,7 @@ import { useProtocolStore } from "@/stores/admin/protocol";
import { useMemberStore } from "@/stores/admin/member"; import { useMemberStore } from "@/stores/admin/member";
import type { MemberViewModel } from "@/viewmodels/admin/member.models"; import type { MemberViewModel } from "@/viewmodels/admin/member.models";
import { useProtocolPresenceStore } from "@/stores/admin/protocolPresence"; import { useProtocolPresenceStore } from "@/stores/admin/protocolPresence";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -111,6 +116,7 @@ export default defineComponent({
computed: { computed: {
...mapWritableState(useProtocolPresenceStore, ["presence", "loading"]), ...mapWritableState(useProtocolPresenceStore, ["presence", "loading"]),
...mapState(useMemberStore, ["members"]), ...mapState(useMemberStore, ["members"]),
...mapState(useAbilityStore, ["can"]),
filtered(): Array<MemberViewModel> { filtered(): Array<MemberViewModel> {
return this.query === "" return this.query === ""
? this.members ? this.members

View file

@ -30,7 +30,13 @@
</div> </div>
<div class="flex flex-row justify-start gap-2"> <div class="flex flex-row justify-start gap-2">
<button primary class="!w-fit" :disabled="printing != undefined" @click="createProtocolPrintout"> <button
v-if="can('create', 'club', 'protocol')"
primary
class="!w-fit"
:disabled="printing != undefined"
@click="createProtocolPrintout"
>
Ausdruck erstellen Ausdruck erstellen
</button> </button>
<Spinner v-if="printing == 'loading'" class="my-auto" /> <Spinner v-if="printing == 'loading'" class="my-auto" />
@ -49,6 +55,7 @@ import FailureXMark from "@/components/FailureXMark.vue";
import { useProtocolPrintoutStore } from "@/stores/admin/protocolPrintout"; import { useProtocolPrintoutStore } from "@/stores/admin/protocolPrintout";
import { ArrowDownTrayIcon, ViewfinderCircleIcon } from "@heroicons/vue/24/outline"; import { ArrowDownTrayIcon, ViewfinderCircleIcon } from "@heroicons/vue/24/outline";
import { useModalStore } from "@/stores/modal"; import { useModalStore } from "@/stores/modal";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -58,6 +65,7 @@ export default defineComponent({
}, },
computed: { computed: {
...mapState(useProtocolPrintoutStore, ["printout", "loading", "printing"]), ...mapState(useProtocolPrintoutStore, ["printout", "loading", "printing"]),
...mapState(useAbilityStore, ["can"]),
}, },
mounted() { mounted() {
this.fetchProtocolPrintout(); this.fetchProtocolPrintout();

View file

@ -14,7 +14,6 @@
} }
" "
/> />
<!-- <TrashIcon class="w-5 h-5 cursor-pointer" @click="openDeleteModal" /> -->
</div> </div>
</template> </template>
<template #diffMain> <template #diffMain>

View file

@ -27,6 +27,7 @@
autocomplete="off" autocomplete="off"
v-model="item.topic" v-model="item.topic"
@keyup.prevent @keyup.prevent
:disabled="!can('create', 'club', 'protocol')"
/> />
</summary> </summary>
<QuillEditor <QuillEditor
@ -37,6 +38,8 @@
contentType="html" contentType="html"
:toolbar="toolbarOptions" :toolbar="toolbarOptions"
v-model:content="item.context" v-model:content="item.context"
:enable="can('create', 'club', 'protocol')"
:style="!can('create', 'club', 'protocol') ? 'opacity: 75%; background: rgb(243 244 246)' : ''"
/> />
<div class="px-2 pb-2"> <div class="px-2 pb-2">
<p>Ergebnis:</p> <p>Ergebnis:</p>
@ -58,7 +61,9 @@
</details> </details>
</div> </div>
<button primary class="!w-fit" @click="createProtocolVoting">Abstimmung hinzufügen</button> <button v-if="can('create', 'club', 'protocol')" primary class="!w-fit" @click="createProtocolVoting">
Abstimmung hinzufügen
</button>
</div> </div>
</template> </template>
@ -70,7 +75,8 @@ import { useProtocolStore } from "@/stores/admin/protocol";
import { QuillEditor } from "@vueup/vue-quill"; import { QuillEditor } from "@vueup/vue-quill";
import "@vueup/vue-quill/dist/vue-quill.snow.css"; import "@vueup/vue-quill/dist/vue-quill.snow.css";
import { toolbarOptions } from "@/helpers/quillConfig"; import { toolbarOptions } from "@/helpers/quillConfig";
import { useProtocolVotingStore } from "../../../../stores/admin/protocolVoting"; import { useProtocolVotingStore } from "@/stores/admin/protocolVoting";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
@ -80,6 +86,7 @@ export default defineComponent({
}, },
computed: { computed: {
...mapState(useProtocolVotingStore, ["voting", "loading"]), ...mapState(useProtocolVotingStore, ["voting", "loading"]),
...mapState(useAbilityStore, ["can"]),
}, },
mounted() { mounted() {
this.fetchProtocolVoting(); this.fetchProtocolVoting();

View file

@ -11,7 +11,9 @@
<AwardListItem v-for="award in awards" :key="award.id" :award="award" /> <AwardListItem v-for="award in awards" :key="award.id" :award="award" />
</div> </div>
<div class="flex flex-row gap-4"> <div class="flex flex-row gap-4">
<button primary class="!w-fit" @click="openCreateModal">Auszeichnung erstellen</button> <button v-if="can('create', 'settings', 'award')" primary class="!w-fit" @click="openCreateModal">
Auszeichnung erstellen
</button>
</div> </div>
</div> </div>
</template> </template>
@ -25,12 +27,14 @@ import MainTemplate from "@/templates/Main.vue";
import { useAwardStore } from "@/stores/admin/award"; import { useAwardStore } from "@/stores/admin/award";
import AwardListItem from "@/components/admin/settings/award/AwardListItem.vue"; import AwardListItem from "@/components/admin/settings/award/AwardListItem.vue";
import { useModalStore } from "@/stores/modal"; import { useModalStore } from "@/stores/modal";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
export default defineComponent({ export default defineComponent({
computed: { computed: {
...mapState(useAwardStore, ["awards"]), ...mapState(useAwardStore, ["awards"]),
...mapState(useAbilityStore, ["can"]),
}, },
mounted() { mounted() {
this.fetchAwards(); this.fetchAwards();

View file

@ -15,7 +15,9 @@
/> />
</div> </div>
<div class="flex flex-row gap-4"> <div class="flex flex-row gap-4">
<button primary class="!w-fit" @click="openCreateModal">Termintyp erstellen</button> <button v-if="can('create', 'settings', 'calendar_type')" primary class="!w-fit" @click="openCreateModal">
Termintyp erstellen
</button>
</div> </div>
</div> </div>
</template> </template>
@ -29,12 +31,14 @@ import MainTemplate from "@/templates/Main.vue";
import { useCalendarTypeStore } from "@/stores/admin/calendarType"; import { useCalendarTypeStore } from "@/stores/admin/calendarType";
import CalendarTypeListItem from "@/components/admin/settings/calendarType/CalendarTypeListItem.vue"; import CalendarTypeListItem from "@/components/admin/settings/calendarType/CalendarTypeListItem.vue";
import { useModalStore } from "@/stores/modal"; import { useModalStore } from "@/stores/modal";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
export default defineComponent({ export default defineComponent({
computed: { computed: {
...mapState(useCalendarTypeStore, ["calendarTypes"]), ...mapState(useCalendarTypeStore, ["calendarTypes"]),
...mapState(useAbilityStore, ["can"]),
}, },
mounted() { mounted() {
this.fetchCalendarTypes(); this.fetchCalendarTypes();

View file

@ -15,7 +15,14 @@
/> />
</div> </div>
<div class="flex flex-row gap-4"> <div class="flex flex-row gap-4">
<button primary class="!w-fit" @click="openCreateModal">Kommunikationsart erstellen</button> <button
v-if="can('create', 'settings', 'communication_type')"
primary
class="!w-fit"
@click="openCreateModal"
>
Kommunikationsart erstellen
</button>
</div> </div>
</div> </div>
</template> </template>
@ -29,12 +36,14 @@ import MainTemplate from "@/templates/Main.vue";
import { useCommunicationTypeStore } from "@/stores/admin/communicationType"; import { useCommunicationTypeStore } from "@/stores/admin/communicationType";
import CommunicationTypeListItem from "@/components/admin/settings/communicationType/CommunicationTypeListItem.vue"; import CommunicationTypeListItem from "@/components/admin/settings/communicationType/CommunicationTypeListItem.vue";
import { useModalStore } from "@/stores/modal"; import { useModalStore } from "@/stores/modal";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
export default defineComponent({ export default defineComponent({
computed: { computed: {
...mapState(useCommunicationTypeStore, ["communicationTypes"]), ...mapState(useCommunicationTypeStore, ["communicationTypes"]),
...mapState(useAbilityStore, ["can"]),
}, },
mounted() { mounted() {
this.fetchCommunicationTypes(); this.fetchCommunicationTypes();

View file

@ -15,7 +15,14 @@
/> />
</div> </div>
<div class="flex flex-row gap-4"> <div class="flex flex-row gap-4">
<button primary class="!w-fit" @click="openCreateModal">Vereinsämt erstellen</button> <button
v-if="can('create', 'settings', 'executive_position')"
primary
class="!w-fit"
@click="openCreateModal"
>
Vereinsämt erstellen
</button>
</div> </div>
</div> </div>
</template> </template>
@ -29,12 +36,14 @@ import MainTemplate from "@/templates/Main.vue";
import { useExecutivePositionStore } from "@/stores/admin/executivePosition"; import { useExecutivePositionStore } from "@/stores/admin/executivePosition";
import ExecutivePositionListItem from "@/components/admin/settings/executivePosition/ExecutivePositionListItem.vue"; import ExecutivePositionListItem from "@/components/admin/settings/executivePosition/ExecutivePositionListItem.vue";
import { useModalStore } from "@/stores/modal"; import { useModalStore } from "@/stores/modal";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
export default defineComponent({ export default defineComponent({
computed: { computed: {
...mapState(useExecutivePositionStore, ["executivePositions"]), ...mapState(useExecutivePositionStore, ["executivePositions"]),
...mapState(useAbilityStore, ["can"]),
}, },
mounted() { mounted() {
this.fetchExecutivePositions(); this.fetchExecutivePositions();

View file

@ -11,7 +11,9 @@
<MembershipStatusListItem v-for="status in membershipStatus" :key="status.id" :membershipStatus="status" /> <MembershipStatusListItem v-for="status in membershipStatus" :key="status.id" :membershipStatus="status" />
</div> </div>
<div class="flex flex-row gap-4"> <div class="flex flex-row gap-4">
<button primary class="!w-fit" @click="openCreateModal">Mitgliedsstatus erstellen</button> <button v-if="can('create', 'settings', 'membership_status')" primary class="!w-fit" @click="openCreateModal">
Mitgliedsstatus erstellen
</button>
</div> </div>
</div> </div>
</template> </template>
@ -25,12 +27,14 @@ import MainTemplate from "@/templates/Main.vue";
import { useMembershipStatusStore } from "@/stores/admin/membershipStatus"; import { useMembershipStatusStore } from "@/stores/admin/membershipStatus";
import MembershipStatusListItem from "@/components/admin/settings/membershipStatus/MembershipStatusListItem.vue"; import MembershipStatusListItem from "@/components/admin/settings/membershipStatus/MembershipStatusListItem.vue";
import { useModalStore } from "@/stores/modal"; import { useModalStore } from "@/stores/modal";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
export default defineComponent({ export default defineComponent({
computed: { computed: {
...mapState(useMembershipStatusStore, ["membershipStatus"]), ...mapState(useMembershipStatusStore, ["membershipStatus"]),
...mapState(useAbilityStore, ["can"]),
}, },
mounted() { mounted() {
this.fetchMembershipStatus(); this.fetchMembershipStatus();

View file

@ -15,7 +15,9 @@
/> />
</div> </div>
<div class="flex flex-row gap-4"> <div class="flex flex-row gap-4">
<button primary class="!w-fit" @click="openCreateModal">Qualifikation erstellen</button> <button v-if="can('create', 'settings', 'qualification')" primary class="!w-fit" @click="openCreateModal">
Qualifikation erstellen
</button>
</div> </div>
</div> </div>
</template> </template>
@ -29,12 +31,14 @@ import MainTemplate from "@/templates/Main.vue";
import { useQualificationStore } from "@/stores/admin/qualification"; import { useQualificationStore } from "@/stores/admin/qualification";
import QualificationListItem from "@/components/admin/settings/qualification/QualificationListItem.vue"; import QualificationListItem from "@/components/admin/settings/qualification/QualificationListItem.vue";
import { useModalStore } from "@/stores/modal"; import { useModalStore } from "@/stores/modal";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
export default defineComponent({ export default defineComponent({
computed: { computed: {
...mapState(useQualificationStore, ["qualifications"]), ...mapState(useQualificationStore, ["qualifications"]),
...mapState(useAbilityStore, ["can"]),
}, },
mounted() { mounted() {
this.fetchQualifications(); this.fetchQualifications();

View file

@ -10,7 +10,7 @@
</template> </template>
<template #diffMain> <template #diffMain>
<div class="flex flex-col gap-2 grow overflow-y-scroll px-7"> <div class="flex flex-col gap-2 grow overflow-y-scroll px-7">
<InviteListItem v-for="invite in invites" :key="invite.id" :invite="invite" /> <InviteListItem v-for="invite in invites" :key="invite.username" :invite="invite" />
</div> </div>
</template> </template>
</MainTemplate> </MainTemplate>

View file

@ -11,7 +11,9 @@
<RoleListItem v-for="role in roles" :key="role.id" :role="role" /> <RoleListItem v-for="role in roles" :key="role.id" :role="role" />
</div> </div>
<div class="flex flex-row gap-4"> <div class="flex flex-row gap-4">
<button primary class="!w-fit" @click="openCreateModal">Rolle erstellen</button> <button v-if="can('create', 'user', 'role')" primary class="!w-fit" @click="openCreateModal">
Rolle erstellen
</button>
</div> </div>
</div> </div>
</template> </template>
@ -25,12 +27,14 @@ import MainTemplate from "@/templates/Main.vue";
import { useRoleStore } from "@/stores/admin/role"; import { useRoleStore } from "@/stores/admin/role";
import RoleListItem from "@/components/admin/user/role/RoleListItem.vue"; import RoleListItem from "@/components/admin/user/role/RoleListItem.vue";
import { useModalStore } from "@/stores/modal"; import { useModalStore } from "@/stores/modal";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
export default defineComponent({ export default defineComponent({
computed: { computed: {
...mapState(useRoleStore, ["roles"]), ...mapState(useRoleStore, ["roles"]),
...mapState(useAbilityStore, ["can"]),
}, },
mounted() { mounted() {
this.fetchRoles(); this.fetchRoles();

View file

@ -11,7 +11,9 @@
<UserListItem v-for="user in users" :key="user.id" :user="user" /> <UserListItem v-for="user in users" :key="user.id" :user="user" />
</div> </div>
<div class="flex flex-row gap-4"> <div class="flex flex-row gap-4">
<button primary class="!w-fit" @click="inviteUser">Nutzer einladen</button> <button v-if="can('create', 'user', 'user')" primary class="!w-fit" @click="inviteUser">
Nutzer einladen
</button>
<RouterLink button primary-outline :to="{ name: 'admin-user-user-invites' }" class="!w-fit"> <RouterLink button primary-outline :to="{ name: 'admin-user-user-invites' }" class="!w-fit">
offene Einladungen offene Einladungen
</RouterLink> </RouterLink>
@ -29,12 +31,14 @@ import MainTemplate from "@/templates/Main.vue";
import { useUserStore } from "@/stores/admin/user"; import { useUserStore } from "@/stores/admin/user";
import { useModalStore } from "@/stores/modal"; import { useModalStore } from "@/stores/modal";
import UserListItem from "@/components/admin/user/user/UserListItem.vue"; import UserListItem from "@/components/admin/user/user/UserListItem.vue";
import { useAbilityStore } from "@/stores/ability";
</script> </script>
<script lang="ts"> <script lang="ts">
export default defineComponent({ export default defineComponent({
computed: { computed: {
...mapState(useUserStore, ["users"]), ...mapState(useUserStore, ["users"]),
...mapState(useAbilityStore, ["can"]),
}, },
mounted() { mounted() {
this.fetchUsers(); this.fetchUsers();

View file

@ -9,6 +9,7 @@
</div> </div>
</template> </template>
<template #main> <template #main>
<p>Hinweis: Berechtigungen von Nutzer und Rolle sind ergänzend.</p>
<Spinner v-if="loading == 'loading'" class="mx-auto" /> <Spinner v-if="loading == 'loading'" class="mx-auto" />
<p v-else-if="loading == 'failed'">laden fehlgeschlagen</p> <p v-else-if="loading == 'failed'">laden fehlgeschlagen</p>
<Permission <Permission