106 lines
3.6 KiB
Vue
106 lines
3.6 KiB
Vue
<template>
|
|
<div class="flex flex-col gap-2 h-full w-full overflow-y-auto">
|
|
<Spinner v-if="loading == 'loading'" class="mx-auto" />
|
|
<p v-else-if="loading == 'failed'" @click="fetchProtocolPresence" class="cursor-pointer">
|
|
↺ laden fehlgeschlagen
|
|
</p>
|
|
|
|
<MemberSearchSelect
|
|
title="Anwesende suchen"
|
|
:model-value="presence.map((p) => p.memberId)"
|
|
:disabled="!can('create', 'club', 'protocol')"
|
|
@add:difference="
|
|
(id: string) =>
|
|
presence.push({ memberId: id, absent: false, excused: true, protocolId: parseInt(protocolId ?? '') })
|
|
"
|
|
@add:member="(s) => members.push(s)"
|
|
@add:member-by-array="(s) => members.push(...s)"
|
|
@remove:difference="removeSelected"
|
|
/>
|
|
|
|
<br />
|
|
<p>Anwesenheit</p>
|
|
<div class="flex flex-col gap-2 grow overflow-y-auto">
|
|
<div
|
|
v-for="member in sortedPresence"
|
|
:key="member.memberId"
|
|
class="flex flex-row h-fit w-full border border-primary rounded-md bg-primary p-2 text-white justify-between items-center"
|
|
>
|
|
<div class="flex flex-col items-start">
|
|
<p>
|
|
{{ getMember(member.memberId)?.lastname }}, {{ getMember(member.memberId)?.firstname }}
|
|
{{ getMember(member.memberId)?.nameaffix ? `- ${getMember(member.memberId)?.nameaffix}` : "" }}
|
|
</p>
|
|
<div class="flex flex-row gap-4">
|
|
<label class="flex flex-row gap-2 items-center">
|
|
<input type="checkbox" v-model="member.absent" />
|
|
war abwesend
|
|
</label>
|
|
<label v-if="member.absent" class="flex flex-row gap-2 items-center">
|
|
<input type="checkbox" v-model="member.excused" />
|
|
ist entschuldigt
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<TrashIcon
|
|
v-if="can('create', 'club', 'protocol')"
|
|
class="w-5 h-5 p-1 box-content cursor-pointer"
|
|
@click="removeSelected(member.memberId)"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { defineComponent } from "vue";
|
|
import { mapActions, mapState, mapWritableState } from "pinia";
|
|
import Spinner from "@/components/Spinner.vue";
|
|
import { TrashIcon } from "@heroicons/vue/24/outline";
|
|
import { useProtocolPresenceStore } from "@/stores/admin/club/protocol/protocolPresence";
|
|
import { useAbilityStore } from "@/stores/ability";
|
|
import MemberSearchSelect from "@/components/admin/MemberSearchSelect.vue";
|
|
import type { MemberViewModel } from "@/viewmodels/admin/club/member/member.models";
|
|
</script>
|
|
|
|
<script lang="ts">
|
|
export default defineComponent({
|
|
props: {
|
|
protocolId: String,
|
|
},
|
|
data() {
|
|
return {
|
|
query: "" as String,
|
|
members: [] as Array<MemberViewModel>,
|
|
};
|
|
},
|
|
computed: {
|
|
...mapWritableState(useProtocolPresenceStore, ["presence", "loading"]),
|
|
...mapState(useAbilityStore, ["can"]),
|
|
getMember() {
|
|
return (memberId: string) => {
|
|
return this.members.find((m) => memberId == m.id);
|
|
};
|
|
},
|
|
sortedPresence() {
|
|
return this.presence.toSorted((a, b) => {
|
|
const memberA = this.getMember(a.memberId);
|
|
const memberB = this.getMember(b.memberId);
|
|
return `${memberA?.lastname}, ${memberA?.firstname}`.localeCompare(
|
|
`${memberB?.lastname}, ${memberB?.firstname}`
|
|
);
|
|
});
|
|
},
|
|
},
|
|
mounted() {},
|
|
methods: {
|
|
...mapActions(useProtocolPresenceStore, ["fetchProtocolPresence"]),
|
|
removeSelected(id: string) {
|
|
let index = this.presence.findIndex((s) => s.memberId == id);
|
|
if (index != -1) {
|
|
this.presence.splice(index, 1);
|
|
}
|
|
},
|
|
},
|
|
});
|
|
</script>
|