Merge pull request 'feature/#29-Membership-statistics' (#48) from feature/#29-Membership-statistics into main

Reviewed-on: #48
This commit is contained in:
Julian Krauser 2025-01-22 08:00:59 +00:00
commit 03b35906b2
5 changed files with 76 additions and 3 deletions

View file

@ -1,5 +1,9 @@
import { defineStore } from "pinia";
import type { CreateMemberViewModel, UpdateMemberViewModel } from "@/viewmodels/admin/club/member/member.models";
import type {
CreateMemberViewModel,
MemberStatisticsViewModel,
UpdateMemberViewModel,
} from "@/viewmodels/admin/club/member/member.models";
import { http } from "@/serverCom";
import type { AxiosResponse } from "axios";
import type { MemberViewModel } from "@/viewmodels/admin/club/member/member.models";
@ -12,6 +16,7 @@ export const useMemberStore = defineStore("member", {
loading: "loading" as "loading" | "fetched" | "failed",
activeMember: null as number | null,
activeMemberObj: null as MemberViewModel | null,
activeMemberStatistics: null as MemberStatisticsViewModel | null,
loadingActive: "loading" as "loading" | "fetched" | "failed",
};
},
@ -70,6 +75,17 @@ export const useMemberStore = defineStore("member", {
fetchMemberById(id: number) {
return http.get(`/admin/member/${id}`);
},
fetchMemberStatisticsByActiveId() {
http
.get(`/admin/member/${this.activeMember}/statistics`)
.then((res) => {
this.activeMemberStatistics = res.data;
})
.catch((err) => {});
},
fetchMemberStatisticsById(id: number) {
return http.get(`/admin/member/${id}/statistics`);
},
async createMember(member: CreateMemberViewModel): Promise<AxiosResponse<any, any>> {
const result = await http.post(`/admin/member`, {
salutation: member.salutation,

View file

@ -6,6 +6,7 @@ import type { MemberViewModel } from "@/viewmodels/admin/club/member/member.mode
import { useMemberStore } from "./member";
import type {
CreateMembershipViewModel,
MembershipStatisticsViewModel,
MembershipViewModel,
UpdateMembershipViewModel,
} from "@/viewmodels/admin/club/member/membership.models";
@ -14,6 +15,7 @@ export const useMembershipStore = defineStore("membership", {
state: () => {
return {
memberships: [] as Array<MembershipViewModel>,
membershipStatistics: [] as Array<MembershipStatisticsViewModel>,
loading: "loading" as "loading" | "fetched" | "failed",
};
},
@ -31,6 +33,15 @@ export const useMembershipStore = defineStore("membership", {
this.loading = "failed";
});
},
fetchMembershipStatisticsForMember() {
const memberId = useMemberStore().activeMember;
http
.get(`/admin/member/${memberId}/memberships/statistics`)
.then((result) => {
this.membershipStatistics = result.data;
})
.catch((err) => {});
},
fetchMembershipById(id: number) {
const memberId = useMemberStore().activeMember;
return http.get(`/admin/member/${memberId}/membership/${id}`);

View file

@ -17,6 +17,18 @@ export interface MemberViewModel {
preferredCommunication?: Array<CommunicationViewModel>;
}
export interface MemberStatisticsViewModel {
id: number;
salutation: Salutation;
firstname: string;
lastname: string;
nameaffix: string;
birthdate: Date;
todayAge: number;
ageThisYear: number;
exactAge: string;
}
export interface CreateMemberViewModel {
salutation: Salutation;
firstname: string;

View file

@ -1,3 +1,5 @@
import type { Salutation } from "../../../../enums/salutation";
export interface MembershipViewModel {
id: number;
start: Date;
@ -7,6 +9,19 @@ export interface MembershipViewModel {
statusId: number;
}
export interface MembershipStatisticsViewModel {
durationInDays: number;
durationInYears: string;
status: string;
statusId: number;
memberId: number;
memberSalutation: Salutation;
memberFirstname: string;
memberLastname: string;
memberNameaffix: string;
memberBirthdate: Date;
}
export interface CreateMembershipViewModel {
start: Date;
statusId: number;

View file

@ -25,6 +25,20 @@
<label for="birthdate">Geburtsdatum</label>
<input type="date" id="birthdate" :value="activeMemberObj.birthdate" readonly />
</div>
<div v-if="membershipStatistics.length != 0">
<p>Statistiken zur Mitgliedschaft</p>
<div class="flex flex-col h-fit w-full border border-primary rounded-md">
<div
v-for="stat in membershipStatistics"
class="bg-primary p-2 text-white flex flex-row justify-between items-center"
>
<p>
{{ stat.status }} für gesamt {{ stat.durationInDays }} Tage
<span class="whitespace-nowrap"> ~> {{ stat.durationInYears.replace("_", "") }} Jahre</span>
</p>
</div>
</div>
</div>
<div v-if="activeMemberObj.firstMembershipEntry">
<p>Erster Eintrag Mitgliedschaft</p>
<div class="flex flex-col h-fit w-full border border-primary rounded-md">
@ -125,6 +139,7 @@ import { defineComponent } from "vue";
import { mapActions, mapState } from "pinia";
import Spinner from "@/components/Spinner.vue";
import { useMemberStore } from "@/stores/admin/club/member/member";
import { useMembershipStore } from "@/stores/admin/club/member/membership";
</script>
<script lang="ts">
@ -133,13 +148,17 @@ export default defineComponent({
memberId: String,
},
computed: {
...mapState(useMemberStore, ["activeMemberObj", "loadingActive"]),
...mapState(useMemberStore, ["activeMemberObj", "activeMemberStatistics", "loadingActive"]),
...mapState(useMembershipStore, ["membershipStatistics"]),
},
mounted() {
this.fetchMemberByActiveId();
this.fetchMemberStatisticsByActiveId();
this.fetchMembershipStatisticsForMember();
},
methods: {
...mapActions(useMemberStore, ["fetchMemberByActiveId"]),
...mapActions(useMemberStore, ["fetchMemberByActiveId", "fetchMemberStatisticsByActiveId"]),
...mapActions(useMembershipStore, ["fetchMembershipStatisticsForMember"]),
},
});
</script>