<template> <MainTemplate> <template #topBar> <div class="flex flex-row items-center justify-between pt-5 pb-3 px-7"> <h1 class="font-bold text-xl h-8">Mitglieder</h1> </div> </template> <template #diffMain> <div class="flex flex-col w-full h-full gap-2 justify-center px-7"> <div class="flex flex-col w-full grow gap-2 pr-2 overflow-y-scroll"> <MemberListItem v-for="member in members" :key="member.id" :member="member" /> </div> <div class="flex flex-row w-full justify-between select-none"> <p class="text-sm font-normal text-gray-500"> Elemente <span class="font-semibold text-gray-900">{{ showingText }}</span> von <span class="font-semibold text-gray-900">{{ entryCount }}</span> </p> <ul class="flex flex-row text-sm h-8"> <li class="flex h-8 w-8 items-center justify-center text-gray-500 bg-white border border-gray-300 first:rounded-s-lg last:rounded-e-lg" :class="[currentPage > 0 ? 'cursor-pointer hover:bg-gray-100 hover:text-gray-700' : 'opacity-50']" @click="loadPage(currentPage - 1)" > <ChevronLeftIcon class="h-4" /> </li> <li v-for="page in displayedPagesNumbers" :key="page" class="flex h-8 w-8 items-center justify-center text-gray-500 bg-white border border-gray-300 hover:bg-gray-100 hover:text-gray-700 first:rounded-s-lg last:rounded-e-lg" :class="[currentPage == page ? 'font-bold border-primary' : '', page != '.' ? ' cursor-pointer' : '']" @click="loadPage(page)" > {{ typeof page == "number" ? page + 1 : "..." }} </li> <li class="flex h-8 w-8 items-center justify-center text-gray-500 bg-white border border-gray-300 first:rounded-s-lg last:rounded-e-lg" :class="[ currentPage + 1 < countOfPages ? 'cursor-pointer hover:bg-gray-100 hover:text-gray-700' : 'opacity-50', ]" @click="loadPage(currentPage + 1)" > <ChevronRightIcon class="h-4" /> </li> </ul> </div> <div class="flex flex-row gap-4"> <button primary class="!w-fit" @click="openCreateModal">Mitglied erstellen</button> </div> </div> </template> </MainTemplate> </template> <script setup lang="ts"> import { defineAsyncComponent, defineComponent, markRaw } from "vue"; import { mapActions, mapState } from "pinia"; import MainTemplate from "@/templates/Main.vue"; import { ChevronRightIcon, ChevronLeftIcon } from "@heroicons/vue/20/solid"; import { useMemberStore } from "@/stores/admin/member"; import MemberListItem from "@/components/admin/club/member/MemberListItem.vue"; import { useModalStore } from "@/stores/modal"; </script> <script lang="ts"> export default defineComponent({ data() { return { currentPage: 0, maxEntriesPerPage: 25, }; }, computed: { ...mapState(useMemberStore, ["members", "totalCount"]), entryCount() { return this.totalCount ?? this.members.length; }, showingStart() { return this.currentPage * this.maxEntriesPerPage; }, showingEnd() { let max = this.currentPage * this.maxEntriesPerPage + this.maxEntriesPerPage; if (max > this.entryCount) max = this.entryCount; return max; }, showingText() { return `${this.entryCount != 0 ? this.showingStart + 1 : 0} - ${this.showingEnd}`; }, countOfPages() { return Math.ceil(this.entryCount / this.maxEntriesPerPage); }, displayedPagesNumbers(): Array<number | "."> { //indicate if "." or page number gets pushed let stateOfPush = false; return [...new Array(this.countOfPages)].reduce((acc, curr, index) => { if ( // always display first 2 pages index <= 1 || // always display last 2 pages index >= this.countOfPages - 2 || // always display 1 pages around current page (this.currentPage - 1 <= index && index <= this.currentPage + 1) ) { acc.push(index); stateOfPush = false; return acc; } // abort if placeholder already added to array if (stateOfPush == true) return acc; // show placeholder if pagenumber is not actively rendered acc.push("."); stateOfPush = true; return acc; }, []); }, visibleRows() { return this.filterData(this.members, this.showingStart, this.showingEnd); }, }, mounted() { this.fetchMembers(0, this.maxEntriesPerPage, true); }, methods: { ...mapActions(useMemberStore, ["fetchMembers"]), ...mapActions(useModalStore, ["openModal"]), loadPage(newPage: number | ".") { if (newPage == ".") return; if (newPage < 0 || newPage >= this.countOfPages) return; let pageStart = newPage * this.maxEntriesPerPage; let pageEnd = newPage * this.maxEntriesPerPage + this.maxEntriesPerPage; if (pageEnd > this.entryCount) pageEnd = this.entryCount; let loadedElementCount = this.filterData(this.members, pageStart, pageEnd).length; if (loadedElementCount < this.maxEntriesPerPage) this.fetchMembers(pageStart, this.maxEntriesPerPage); this.currentPage = newPage; }, filterData(array: Array<any>, start: number, end: number): Array<any> { return array.filter((elem, index) => (elem?.tab_pos ?? index) >= start && (elem?.tab_pos ?? index) < end); }, openCreateModal() { this.openModal( markRaw(defineAsyncComponent(() => import("@/components/admin/club/member/CreateMemberModal.vue"))) ); }, }, }); </script>