Merge pull request 'patches v1.3.3' (#75) from develop into main
Reviewed-on: #75
This commit is contained in:
commit
8ad91a3097
19 changed files with 357 additions and 40 deletions
|
@ -9,7 +9,6 @@
|
||||||
<a ref="download" button primary class="!w-fit">download</a>
|
<a ref="download" button primary class="!w-fit">download</a>
|
||||||
<button primary-outline class="!w-fit" @click="closeModal">schließen</button>
|
<button primary-outline class="!w-fit" @click="closeModal">schließen</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -31,25 +30,29 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(useModalStore, ["data"]),
|
...mapState(useModalStore, ["data"]),
|
||||||
|
...mapState(useMemberStore, ["activeMemberObj"]),
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.fetchItem();
|
this.fetchItem();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(useModalStore, ["closeModal"]),
|
...mapActions(useModalStore, ["closeModal"]),
|
||||||
...mapActions(useMemberStore, ["printMemberList"]),
|
...mapActions(useMemberStore, ["printMemberByActiveId"]),
|
||||||
fetchItem() {
|
fetchItem() {
|
||||||
this.status = "loading";
|
this.status = "loading";
|
||||||
this.printMemberList()
|
this.printMemberByActiveId()
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.status = { status: "success" };
|
this.status = { status: "success" };
|
||||||
const blob = new Blob([response.data], { type: "application/pdf" });
|
const blob = new Blob([response.data], { type: "application/pdf" });
|
||||||
(this.$refs.viewer as HTMLIFrameElement).src = window.URL.createObjectURL(blob);
|
(this.$refs.viewer as HTMLIFrameElement).src = window.URL.createObjectURL(blob);
|
||||||
|
|
||||||
const fileURL = window.URL.createObjectURL(new Blob([response.data]));
|
const fileURL = window.URL.createObjectURL(new Blob([response.data]));
|
||||||
const fileLink = (this.$refs.download as HTMLAnchorElement)
|
const fileLink = this.$refs.download as HTMLAnchorElement;
|
||||||
fileLink.href = fileURL;
|
fileLink.href = fileURL;
|
||||||
fileLink.setAttribute("download", "Mitgliederliste.pdf");
|
fileLink.setAttribute(
|
||||||
|
"download",
|
||||||
|
`Mitglied-Ausdruck ${this.activeMemberObj?.firstname}_${this.activeMemberObj?.lastname}.pdf`
|
||||||
|
);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.status = { status: "failed" };
|
this.status = { status: "failed" };
|
|
@ -42,10 +42,11 @@
|
||||||
<div class="flex flex-row gap-2 items-center">
|
<div class="flex flex-row gap-2 items-center">
|
||||||
<p class="whitespace-nowrap">Höhe [mm]:</p>
|
<p class="whitespace-nowrap">Höhe [mm]:</p>
|
||||||
<input
|
<input
|
||||||
|
ref="headerHeight"
|
||||||
id="headerHeight"
|
id="headerHeight"
|
||||||
type="number"
|
type="number"
|
||||||
:min="15"
|
:min="15"
|
||||||
v-model="templateUsage.headerHeight"
|
:value="templateUsage.headerHeight"
|
||||||
class="!w-24"
|
class="!w-24"
|
||||||
placeholder="15"
|
placeholder="15"
|
||||||
/>
|
/>
|
||||||
|
@ -71,10 +72,11 @@
|
||||||
<div class="flex flex-row gap-2 items-center">
|
<div class="flex flex-row gap-2 items-center">
|
||||||
<p class="whitespace-nowrap">Höhe [mm]:</p>
|
<p class="whitespace-nowrap">Höhe [mm]:</p>
|
||||||
<input
|
<input
|
||||||
|
ref="footerHeight"
|
||||||
id="footerHeight"
|
id="footerHeight"
|
||||||
type="number"
|
type="number"
|
||||||
:min="15"
|
:min="15"
|
||||||
v-model="templateUsage.footerHeight"
|
:value="templateUsage.footerHeight"
|
||||||
class="!w-24"
|
class="!w-24"
|
||||||
placeholder="15"
|
placeholder="15"
|
||||||
/>
|
/>
|
||||||
|
@ -134,8 +136,8 @@ export default defineComponent({
|
||||||
const headerId = fromData.header.value === "def" ? null : fromData.header.value;
|
const headerId = fromData.header.value === "def" ? null : fromData.header.value;
|
||||||
const bodyId = fromData.body.value === "def" ? null : fromData.body.value;
|
const bodyId = fromData.body.value === "def" ? null : fromData.body.value;
|
||||||
const footerId = fromData.footer.value === "def" ? null : fromData.footer.value;
|
const footerId = fromData.footer.value === "def" ? null : fromData.footer.value;
|
||||||
const headerHeight = fromData.footer.value === "" ? null : parseInt(fromData.headerHeight.value);
|
const headerHeight = fromData.headerHeight.value === "" ? null : parseInt(fromData.headerHeight.value);
|
||||||
const footerHeight = fromData.footer.value === "" ? null : parseInt(fromData.footerHeight.value);
|
const footerHeight = fromData.footerHeight.value === "" ? null : parseInt(fromData.footerHeight.value);
|
||||||
|
|
||||||
this.status = "loading";
|
this.status = "loading";
|
||||||
this.updateTemplateUsage({
|
this.updateTemplateUsage({
|
||||||
|
@ -160,6 +162,8 @@ export default defineComponent({
|
||||||
(this.$refs.header as HTMLSelectElement).value = String(this.templateUsage.header?.id ?? "def");
|
(this.$refs.header as HTMLSelectElement).value = String(this.templateUsage.header?.id ?? "def");
|
||||||
(this.$refs.body as HTMLSelectElement).value = String(this.templateUsage.body?.id ?? "def");
|
(this.$refs.body as HTMLSelectElement).value = String(this.templateUsage.body?.id ?? "def");
|
||||||
(this.$refs.footer as HTMLSelectElement).value = String(this.templateUsage.footer?.id ?? "def");
|
(this.$refs.footer as HTMLSelectElement).value = String(this.templateUsage.footer?.id ?? "def");
|
||||||
|
(this.$refs.headerHeight as HTMLInputElement).value = (this.templateUsage.headerHeight ?? "").toString();
|
||||||
|
(this.$refs.footerHeight as HTMLInputElement).value = (this.templateUsage.footerHeight ?? "").toString();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -295,6 +295,13 @@ const router = createRouter({
|
||||||
meta: { type: "read", section: "club", module: "query" },
|
meta: { type: "read", section: "club", module: "query" },
|
||||||
beforeEnter: [abilityAndNavUpdate],
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "listprint",
|
||||||
|
name: "admin-club-listprint",
|
||||||
|
component: () => import("@/views/admin/club/listprint/ListPrint.vue"),
|
||||||
|
meta: { type: "read", section: "club", module: "listprint" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
40
src/stores/admin/club/listprint.ts
Normal file
40
src/stores/admin/club/listprint.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { http } from "@/serverCom";
|
||||||
|
|
||||||
|
export const useListPrintStore = defineStore("listprint", {
|
||||||
|
actions: {
|
||||||
|
async printList({
|
||||||
|
title,
|
||||||
|
queryStore,
|
||||||
|
headerId,
|
||||||
|
bodyId,
|
||||||
|
footerId,
|
||||||
|
headerHeight,
|
||||||
|
footerHeight,
|
||||||
|
}: {
|
||||||
|
title: string;
|
||||||
|
queryStore: string;
|
||||||
|
headerId?: string;
|
||||||
|
bodyId?: string;
|
||||||
|
footerId?: string;
|
||||||
|
headerHeight?: number;
|
||||||
|
footerHeight?: number;
|
||||||
|
}) {
|
||||||
|
return http.post(
|
||||||
|
`/admin/listprint`,
|
||||||
|
{
|
||||||
|
title,
|
||||||
|
queryStore,
|
||||||
|
headerId,
|
||||||
|
bodyId,
|
||||||
|
footerId,
|
||||||
|
headerHeight,
|
||||||
|
footerHeight,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
responseType: "blob",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
|
@ -87,6 +87,11 @@ export const useMemberStore = defineStore("member", {
|
||||||
})
|
})
|
||||||
.catch((err) => {});
|
.catch((err) => {});
|
||||||
},
|
},
|
||||||
|
async printMemberByActiveId() {
|
||||||
|
return http.get(`/admin/member/${this.activeMember}/print`, {
|
||||||
|
responseType: "blob",
|
||||||
|
});
|
||||||
|
},
|
||||||
fetchMemberStatisticsById(id: string) {
|
fetchMemberStatisticsById(id: string) {
|
||||||
return http.get(`/admin/member/${id}/statistics`);
|
return http.get(`/admin/member/${id}/statistics`);
|
||||||
},
|
},
|
||||||
|
@ -119,10 +124,5 @@ export const useMemberStore = defineStore("member", {
|
||||||
this.fetchMembers();
|
this.fetchMembers();
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
async printMemberList() {
|
|
||||||
return http.get(`/admin/member/print/namelist`, {
|
|
||||||
responseType: "blob",
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -54,6 +54,7 @@ export const useProtocolAgendaStore = defineStore("protocolAgenda", {
|
||||||
id: Number(res.data),
|
id: Number(res.data),
|
||||||
topic: "",
|
topic: "",
|
||||||
context: "",
|
context: "",
|
||||||
|
sort: this.agenda.length,
|
||||||
protocolId: Number(protocolId),
|
protocolId: Number(protocolId),
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
|
@ -55,6 +55,7 @@ export const useProtocolDecisionStore = defineStore("protocolDecision", {
|
||||||
id: Number(res.data),
|
id: Number(res.data),
|
||||||
topic: "",
|
topic: "",
|
||||||
context: "",
|
context: "",
|
||||||
|
sort: this.decision.length,
|
||||||
protocolId: Number(protocolId),
|
protocolId: Number(protocolId),
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
|
@ -58,6 +58,7 @@ export const useProtocolVotingStore = defineStore("protocolVoting", {
|
||||||
favour: 0,
|
favour: 0,
|
||||||
abstain: 0,
|
abstain: 0,
|
||||||
against: 0,
|
against: 0,
|
||||||
|
sort: this.voting.length,
|
||||||
protocolId: Number(protocolId),
|
protocolId: Number(protocolId),
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
|
@ -92,6 +92,7 @@ export const useNavigationStore = defineStore("navigation", {
|
||||||
...(abilityStore.can("read", "club", "protocol") ? [{ key: "protocol", title: "Protokolle" }] : []),
|
...(abilityStore.can("read", "club", "protocol") ? [{ key: "protocol", title: "Protokolle" }] : []),
|
||||||
...(abilityStore.can("read", "club", "newsletter") ? [{ key: "newsletter", title: "Newsletter" }] : []),
|
...(abilityStore.can("read", "club", "newsletter") ? [{ key: "newsletter", title: "Newsletter" }] : []),
|
||||||
...(abilityStore.can("read", "club", "query") ? [{ key: "query_builder", title: "Query Builder" }] : []),
|
...(abilityStore.can("read", "club", "query") ? [{ key: "query_builder", title: "Query Builder" }] : []),
|
||||||
|
...(abilityStore.can("read", "club", "listprint") ? [{ key: "listprint", title: "Liste Drucken" }] : []),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
configuration: {
|
configuration: {
|
||||||
|
|
|
@ -6,6 +6,7 @@ export type PermissionModule =
|
||||||
| "newsletter"
|
| "newsletter"
|
||||||
| "newsletter_config"
|
| "newsletter_config"
|
||||||
| "protocol"
|
| "protocol"
|
||||||
|
| "listprint"
|
||||||
| "qualification"
|
| "qualification"
|
||||||
| "award"
|
| "award"
|
||||||
| "executive_position"
|
| "executive_position"
|
||||||
|
@ -50,6 +51,7 @@ export const permissionModules: Array<PermissionModule> = [
|
||||||
"newsletter",
|
"newsletter",
|
||||||
"newsletter_config",
|
"newsletter_config",
|
||||||
"protocol",
|
"protocol",
|
||||||
|
"listprint",
|
||||||
"qualification",
|
"qualification",
|
||||||
"award",
|
"award",
|
||||||
"executive_position",
|
"executive_position",
|
||||||
|
@ -68,7 +70,7 @@ export const permissionModules: Array<PermissionModule> = [
|
||||||
];
|
];
|
||||||
export const permissionTypes: Array<PermissionType> = ["read", "create", "update", "delete"];
|
export const permissionTypes: Array<PermissionType> = ["read", "create", "update", "delete"];
|
||||||
export const sectionsAndModules: SectionsAndModulesObject = {
|
export const sectionsAndModules: SectionsAndModulesObject = {
|
||||||
club: ["member", "calendar", "newsletter", "protocol", "query"],
|
club: ["member", "calendar", "newsletter", "protocol", "query", "listprint"],
|
||||||
configuration: [
|
configuration: [
|
||||||
"qualification",
|
"qualification",
|
||||||
"award",
|
"award",
|
||||||
|
|
|
@ -2,6 +2,7 @@ export interface ProtocolAgendaViewModel {
|
||||||
id: number;
|
id: number;
|
||||||
topic: string;
|
topic: string;
|
||||||
context: string;
|
context: string;
|
||||||
|
sort: number;
|
||||||
protocolId: number;
|
protocolId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,4 +10,5 @@ export interface SyncProtocolAgendaViewModel {
|
||||||
id?: number;
|
id?: number;
|
||||||
topic: string;
|
topic: string;
|
||||||
context: string;
|
context: string;
|
||||||
|
sort?: number;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ export interface ProtocolDecisionViewModel {
|
||||||
id: number;
|
id: number;
|
||||||
topic: string;
|
topic: string;
|
||||||
context: string;
|
context: string;
|
||||||
|
sort: number;
|
||||||
protocolId: number;
|
protocolId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,4 +10,5 @@ export interface SyncProtocolDecisionViewModel {
|
||||||
id?: number;
|
id?: number;
|
||||||
topic: string;
|
topic: string;
|
||||||
context: string;
|
context: string;
|
||||||
|
sort?: number;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ export interface ProtocolVotingViewModel {
|
||||||
favour: number;
|
favour: number;
|
||||||
abstain: number;
|
abstain: number;
|
||||||
against: number;
|
against: number;
|
||||||
|
sort: number;
|
||||||
protocolId: number;
|
protocolId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,5 +16,5 @@ export interface SyncProtocolVotingViewModel {
|
||||||
favour: number;
|
favour: number;
|
||||||
abstain: number;
|
abstain: number;
|
||||||
against: number;
|
against: number;
|
||||||
protocolId: number;
|
sort?: number;
|
||||||
}
|
}
|
||||||
|
|
155
src/views/admin/club/listprint/ListPrint.vue
Normal file
155
src/views/admin/club/listprint/ListPrint.vue
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
<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">Liste Drucken</h1>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #diffMain>
|
||||||
|
<div class="flex flex-col w-full h-full gap-2 px-7 overflow-y-auto">
|
||||||
|
<form
|
||||||
|
class="flex flex-col h-fit w-full border border-primary rounded-md p-2 gap-2"
|
||||||
|
@submit.prevent="sendPrintJob"
|
||||||
|
>
|
||||||
|
<div class="flex flex-row gap-2 items-center">
|
||||||
|
<p class="min-w-16">Titel:</p>
|
||||||
|
<input id="title" type="text" required />
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row gap-2 items-center">
|
||||||
|
<p class="min-w-16">Query:</p>
|
||||||
|
<select id="query" value="member">
|
||||||
|
<option value="member">(system) alle Mitglieder</option>
|
||||||
|
<option value="memberByRunningMembership">(system) alle Mitglieder mit laufender Mitgliedschaft</option>
|
||||||
|
<option v-for="query in queries" :key="query.id" :value="query.id">
|
||||||
|
{{ query.title }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col md:flex-row gap-2 md:items-center">
|
||||||
|
<div class="flex flex-row w-full gap-2 items-center">
|
||||||
|
<p class="min-w-16">Kopfzeile:</p>
|
||||||
|
<select id="header" value="def">
|
||||||
|
<option value="def">Standard-Vorlage verwenden</option>
|
||||||
|
<option v-for="template in templates" :key="template.id" :value="template.id">
|
||||||
|
{{ template.template }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row gap-2 items-center">
|
||||||
|
<p class="whitespace-nowrap">Höhe [mm]:</p>
|
||||||
|
<input id="headerHeight" type="number" :min="15" class="!w-24" placeholder="15" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row gap-2 items-center">
|
||||||
|
<p class="min-w-16">Hauptteil:</p>
|
||||||
|
<select id="body" value="def">
|
||||||
|
<option value="def">Standard-Vorlage verwenden</option>
|
||||||
|
<option value="listprint.member">(system) Mitgliederliste</option>
|
||||||
|
<option v-for="template in templates" :key="template.id" :value="template.id">
|
||||||
|
{{ template.template }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col md:flex-row gap-2 md:items-center">
|
||||||
|
<div class="flex flex-row w-full gap-2 items-center">
|
||||||
|
<p class="min-w-16">Fußzeile:</p>
|
||||||
|
<select id="footer" value="def">
|
||||||
|
<option value="def">Standard-Vorlage verwenden</option>
|
||||||
|
<option v-for="template in templates" :key="template.id" :value="template.id">
|
||||||
|
{{ template.template }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row gap-2 items-center">
|
||||||
|
<p class="whitespace-nowrap">Höhe [mm]:</p>
|
||||||
|
<input id="footerHeight" type="number" :min="15" class="!w-24" placeholder="15" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row gap-2">
|
||||||
|
<button type="submit" primary class="!w-fit">Liste drucken</button>
|
||||||
|
<button type="reset" primary-outline class="!w-fit">zurücksetzen</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="w-full grow min-h-[50%] flex flex-col gap-2">
|
||||||
|
<Spinner v-if="status == 'loading'" />
|
||||||
|
<div class="grow">
|
||||||
|
<iframe v-show="status == 'success'" ref="viewer" class="w-full h-full" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-show="status == 'success'" class="flex flex-row gap-2 justify-end">
|
||||||
|
<a ref="download" button primary class="!w-fit">download</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MainTemplate>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import { mapActions, mapState } from "pinia";
|
||||||
|
import MainTemplate from "@/templates/Main.vue";
|
||||||
|
import { useQueryStoreStore } from "@/stores/admin/configuration/queryStore";
|
||||||
|
import { useTemplateStore } from "@/stores/admin/configuration/template";
|
||||||
|
import Spinner from "@/components/Spinner.vue";
|
||||||
|
import type { AxiosResponse } from "axios";
|
||||||
|
import { useListPrintStore } from "@/stores/admin/club/listprint";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
status: null as null | "loading" | "success" | "failed",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useQueryStoreStore, ["queries"]),
|
||||||
|
...mapState(useTemplateStore, ["templates"]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchQueries();
|
||||||
|
this.fetchTemplates();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useQueryStoreStore, ["fetchQueries"]),
|
||||||
|
...mapActions(useTemplateStore, ["fetchTemplates"]),
|
||||||
|
...mapActions(useListPrintStore, ["printList"]),
|
||||||
|
sendPrintJob(e: any) {
|
||||||
|
this.status = "loading";
|
||||||
|
|
||||||
|
const fromData = e.target.elements;
|
||||||
|
const title = fromData.title.value;
|
||||||
|
const queryStore = fromData.query.value;
|
||||||
|
const headerId = fromData.header.value === "def" ? undefined : fromData.header.value;
|
||||||
|
const bodyId = fromData.body.value === "def" ? undefined : fromData.body.value;
|
||||||
|
const footerId = fromData.footer.value === "def" ? undefined : fromData.footer.value;
|
||||||
|
const headerHeight = fromData.headerHeight.value === "" ? undefined : parseInt(fromData.headerHeight.value);
|
||||||
|
const footerHeight = fromData.footerHeight.value === "" ? undefined : parseInt(fromData.footerHeight.value);
|
||||||
|
|
||||||
|
this.printList({
|
||||||
|
title,
|
||||||
|
queryStore,
|
||||||
|
headerId,
|
||||||
|
bodyId,
|
||||||
|
footerId,
|
||||||
|
headerHeight,
|
||||||
|
footerHeight,
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
this.status = "success";
|
||||||
|
const blob = new Blob([response.data], { type: "application/pdf" });
|
||||||
|
(this.$refs.viewer as HTMLIFrameElement).src = window.URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
const fileURL = window.URL.createObjectURL(new Blob([response.data]));
|
||||||
|
const fileLink = this.$refs.download as HTMLAnchorElement;
|
||||||
|
fileLink.href = fileURL;
|
||||||
|
fileLink.setAttribute("download", `Listen-Ausdruck_${title}.pdf`);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.status = "failed";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -3,9 +3,6 @@
|
||||||
<template #topBar>
|
<template #topBar>
|
||||||
<div class="flex flex-row items-center justify-between pt-5 pb-3 px-7">
|
<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>
|
<h1 class="font-bold text-xl h-8">Mitglieder</h1>
|
||||||
<div title="Mitgliederliste drucken" @click="openPrintModal">
|
|
||||||
<DocumentTextIcon class="w-5 h-5 cursor-pointer" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #diffMain>
|
<template #diffMain>
|
||||||
|
@ -69,11 +66,6 @@ export default defineComponent({
|
||||||
markRaw(defineAsyncComponent(() => import("@/components/admin/club/member/CreateMemberModal.vue")))
|
markRaw(defineAsyncComponent(() => import("@/components/admin/club/member/CreateMemberModal.vue")))
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
openPrintModal() {
|
|
||||||
this.openModal(
|
|
||||||
markRaw(defineAsyncComponent(() => import("@/components/admin/club/member/MemberNameListModal.vue")))
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -9,6 +9,10 @@
|
||||||
{{ activeMemberObj?.lastname }}, {{ activeMemberObj?.firstname }}
|
{{ activeMemberObj?.lastname }}, {{ activeMemberObj?.firstname }}
|
||||||
{{ activeMemberObj?.nameaffix ? `- ${activeMemberObj?.nameaffix}` : "" }}
|
{{ activeMemberObj?.nameaffix ? `- ${activeMemberObj?.nameaffix}` : "" }}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
|
<div title="Mitgliederliste drucken" @click="openPrintModal">
|
||||||
|
<DocumentTextIcon class="w-5 h-5 cursor-pointer" />
|
||||||
|
</div>
|
||||||
<RouterLink v-if="can('update', 'club', 'member')" :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>
|
||||||
|
@ -49,7 +53,7 @@ import { mapActions, mapState } from "pinia";
|
||||||
import MainTemplate from "@/templates/Main.vue";
|
import MainTemplate from "@/templates/Main.vue";
|
||||||
import { RouterLink, RouterView } from "vue-router";
|
import { RouterLink, RouterView } from "vue-router";
|
||||||
import { useMemberStore } from "@/stores/admin/club/member/member";
|
import { useMemberStore } from "@/stores/admin/club/member/member";
|
||||||
import { PencilIcon, TrashIcon } from "@heroicons/vue/24/outline";
|
import { PencilIcon, TrashIcon, DocumentTextIcon } from "@heroicons/vue/24/outline";
|
||||||
import { useModalStore } from "@/stores/modal";
|
import { useModalStore } from "@/stores/modal";
|
||||||
import { useAbilityStore } from "@/stores/ability";
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
</script>
|
</script>
|
||||||
|
@ -87,6 +91,11 @@ export default defineComponent({
|
||||||
parseInt(this.memberId ?? "")
|
parseInt(this.memberId ?? "")
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
openPrintModal() {
|
||||||
|
this.openModal(
|
||||||
|
markRaw(defineAsyncComponent(() => import("@/components/admin/club/member/MemberPrintModal.vue")))
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
↺ laden fehlgeschlagen
|
↺ laden fehlgeschlagen
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="flex flex-col gap-2 h-full overflow-y-auto">
|
<div class="flex flex-col gap-2 h-full overflow-y-scroll">
|
||||||
<details
|
<details
|
||||||
v-for="item in agenda"
|
v-for="(item, index) in sortedAgenda"
|
||||||
class="flex flex-col gap-2 rounded-lg w-full justify-between border border-primary overflow-hidden min-h-fit"
|
class="flex flex-col rounded-lg w-full justify-between border border-primary overflow-hidden min-h-fit"
|
||||||
>
|
>
|
||||||
<summary class="flex flex-row gap-2 bg-primary p-2 w-full justify-between items-center cursor-pointer">
|
<summary class="flex flex-row gap-2 bg-primary p-2 w-full justify-between items-center cursor-pointer">
|
||||||
<svg
|
<svg
|
||||||
|
@ -30,6 +30,19 @@
|
||||||
@keyup.prevent
|
@keyup.prevent
|
||||||
:disabled="!can('create', 'club', 'protocol')"
|
:disabled="!can('create', 'club', 'protocol')"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<ChevronUpIcon
|
||||||
|
v-if="index != 0"
|
||||||
|
class="text-white w-4 h-4 stroke-2"
|
||||||
|
@click.prevent="changeSort('up', item.id, index)"
|
||||||
|
/>
|
||||||
|
<ChevronDownIcon
|
||||||
|
v-if="index != agenda.length - 1"
|
||||||
|
class="text-white w-4 h-4 stroke-2"
|
||||||
|
@click.prevent="changeSort('down', item.id, index)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</summary>
|
</summary>
|
||||||
<QuillEditor
|
<QuillEditor
|
||||||
id="top"
|
id="top"
|
||||||
|
@ -59,8 +72,8 @@ 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 { useProtocolAgendaStore } from "@/stores/admin/club/protocol/protocolAgenda";
|
import { useProtocolAgendaStore } from "@/stores/admin/club/protocol/protocolAgenda";
|
||||||
import type { ProtocolAgendaViewModel } from "@/viewmodels/admin/club/protocol/protocolAgenda.models";
|
|
||||||
import { useAbilityStore } from "@/stores/ability";
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import { ChevronDownIcon, ChevronUpIcon } from "@heroicons/vue/24/outline";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
@ -71,12 +84,31 @@ export default defineComponent({
|
||||||
computed: {
|
computed: {
|
||||||
...mapWritableState(useProtocolAgendaStore, ["agenda", "loading"]),
|
...mapWritableState(useProtocolAgendaStore, ["agenda", "loading"]),
|
||||||
...mapState(useAbilityStore, ["can"]),
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
sortedAgenda() {
|
||||||
|
return this.agenda.slice().sort((a, b) => a.sort - b.sort);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
// this.fetchProtocolAgenda();
|
this.normalizeSort();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(useProtocolAgendaStore, ["fetchProtocolAgenda", "createProtocolAgenda"]),
|
...mapActions(useProtocolAgendaStore, ["fetchProtocolAgenda", "createProtocolAgenda"]),
|
||||||
|
changeSort(dir: "up" | "down", thisId: number, index: number) {
|
||||||
|
let affected = this.sortedAgenda[dir == "up" ? index - 1 : index + 1]?.id;
|
||||||
|
if (affected) {
|
||||||
|
this.agenda.find((a) => a.id == thisId)!.sort = dir == "up" ? index - 1 : index + 1;
|
||||||
|
this.agenda.find((a) => a.id == affected)!.sort = dir == "up" ? index + 1 : index - 1;
|
||||||
|
}
|
||||||
|
this.normalizeSort();
|
||||||
|
},
|
||||||
|
normalizeSort() {
|
||||||
|
let rightSort = this.agenda.every((val, index) => val.sort == index);
|
||||||
|
if (!rightSort) {
|
||||||
|
this.sortedAgenda.forEach((e, index) => {
|
||||||
|
e.sort = index;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
↺ laden fehlgeschlagen
|
↺ laden fehlgeschlagen
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="flex flex-col gap-2 h-full overflow-y-auto">
|
<div class="flex flex-col gap-2 h-full overflow-y-scroll">
|
||||||
<details
|
<details
|
||||||
v-for="item in decision"
|
v-for="(item, index) in sortedDecision"
|
||||||
class="flex flex-col gap-2 rounded-lg w-full justify-between border border-primary overflow-hidden min-h-fit"
|
class="flex flex-col rounded-lg w-full justify-between border border-primary overflow-hidden min-h-fit"
|
||||||
>
|
>
|
||||||
<summary class="flex flex-row gap-2 bg-primary p-2 w-full justify-between items-center cursor-pointer">
|
<summary class="flex flex-row gap-2 bg-primary p-2 w-full justify-between items-center cursor-pointer">
|
||||||
<svg
|
<svg
|
||||||
|
@ -30,6 +30,19 @@
|
||||||
@keyup.prevent
|
@keyup.prevent
|
||||||
:disabled="!can('create', 'club', 'protocol')"
|
:disabled="!can('create', 'club', 'protocol')"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<ChevronUpIcon
|
||||||
|
v-if="index != 0"
|
||||||
|
class="text-white w-4 h-4 stroke-2"
|
||||||
|
@click.prevent="changeSort('up', item.id, index)"
|
||||||
|
/>
|
||||||
|
<ChevronDownIcon
|
||||||
|
v-if="index != decision.length - 1"
|
||||||
|
class="text-white w-4 h-4 stroke-2"
|
||||||
|
@click.prevent="changeSort('down', item.id, index)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</summary>
|
</summary>
|
||||||
<QuillEditor
|
<QuillEditor
|
||||||
id="top"
|
id="top"
|
||||||
|
@ -55,12 +68,12 @@
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
import { mapActions, mapState, mapWritableState } from "pinia";
|
import { mapActions, mapState, mapWritableState } from "pinia";
|
||||||
import Spinner from "@/components/Spinner.vue";
|
import Spinner from "@/components/Spinner.vue";
|
||||||
import { useProtocolStore } from "@/stores/admin/club/protocol/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/club/protocol/protocolDecision";
|
import { useProtocolDecisionStore } from "@/stores/admin/club/protocol/protocolDecision";
|
||||||
import { useAbilityStore } from "@/stores/ability";
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import { ChevronDownIcon, ChevronUpIcon } from "@heroicons/vue/24/outline";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
@ -71,12 +84,31 @@ export default defineComponent({
|
||||||
computed: {
|
computed: {
|
||||||
...mapWritableState(useProtocolDecisionStore, ["decision", "loading"]),
|
...mapWritableState(useProtocolDecisionStore, ["decision", "loading"]),
|
||||||
...mapState(useAbilityStore, ["can"]),
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
sortedDecision() {
|
||||||
|
return this.decision.slice().sort((a, b) => a.sort - b.sort);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
// this.fetchProtocolDecision();
|
this.normalizeSort();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(useProtocolDecisionStore, ["fetchProtocolDecision", "createProtocolDecision"]),
|
...mapActions(useProtocolDecisionStore, ["fetchProtocolDecision", "createProtocolDecision"]),
|
||||||
|
changeSort(dir: "up" | "down", thisId: number, index: number) {
|
||||||
|
let affected = this.sortedDecision[dir == "up" ? index - 1 : index + 1]?.id;
|
||||||
|
if (affected) {
|
||||||
|
this.decision.find((a) => a.id == thisId)!.sort = dir == "up" ? index - 1 : index + 1;
|
||||||
|
this.decision.find((a) => a.id == affected)!.sort = dir == "up" ? index + 1 : index - 1;
|
||||||
|
}
|
||||||
|
this.normalizeSort();
|
||||||
|
},
|
||||||
|
normalizeSort() {
|
||||||
|
let rightSort = this.decision.every((val, index) => val.sort == index);
|
||||||
|
if (!rightSort) {
|
||||||
|
this.sortedDecision.forEach((e, index) => {
|
||||||
|
e.sort = index;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
↺ laden fehlgeschlagen
|
↺ laden fehlgeschlagen
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="flex flex-col gap-2 h-full overflow-y-auto">
|
<div class="flex flex-col gap-2 h-full overflow-y-scroll">
|
||||||
<details
|
<details
|
||||||
v-for="item in voting"
|
v-for="(item, index) in sortedVoting"
|
||||||
class="flex flex-col gap-2 rounded-lg w-full justify-between border border-primary overflow-hidden min-h-fit"
|
class="flex flex-col rounded-lg w-full justify-between border border-primary overflow-hidden min-h-fit"
|
||||||
>
|
>
|
||||||
<summary class="flex flex-row gap-2 bg-primary p-2 w-full justify-between items-center cursor-pointer">
|
<summary class="flex flex-row gap-2 bg-primary p-2 w-full justify-between items-center cursor-pointer">
|
||||||
<svg
|
<svg
|
||||||
|
@ -30,6 +30,19 @@
|
||||||
@keyup.prevent
|
@keyup.prevent
|
||||||
:disabled="!can('create', 'club', 'protocol')"
|
:disabled="!can('create', 'club', 'protocol')"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<ChevronUpIcon
|
||||||
|
v-if="index != 0"
|
||||||
|
class="text-white w-4 h-4 stroke-2"
|
||||||
|
@click.prevent="changeSort('up', item.id, index)"
|
||||||
|
/>
|
||||||
|
<ChevronDownIcon
|
||||||
|
v-if="index != voting.length - 1"
|
||||||
|
class="text-white w-4 h-4 stroke-2"
|
||||||
|
@click.prevent="changeSort('down', item.id, index)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</summary>
|
</summary>
|
||||||
<QuillEditor
|
<QuillEditor
|
||||||
id="top"
|
id="top"
|
||||||
|
@ -72,12 +85,12 @@
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
import { mapActions, mapState } from "pinia";
|
import { mapActions, mapState } from "pinia";
|
||||||
import Spinner from "@/components/Spinner.vue";
|
import Spinner from "@/components/Spinner.vue";
|
||||||
import { useProtocolStore } from "@/stores/admin/club/protocol/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/club/protocol/protocolVoting";
|
import { useProtocolVotingStore } from "@/stores/admin/club/protocol/protocolVoting";
|
||||||
import { useAbilityStore } from "@/stores/ability";
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import { ChevronDownIcon, ChevronUpIcon } from "@heroicons/vue/24/outline";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
@ -88,12 +101,31 @@ export default defineComponent({
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(useProtocolVotingStore, ["voting", "loading"]),
|
...mapState(useProtocolVotingStore, ["voting", "loading"]),
|
||||||
...mapState(useAbilityStore, ["can"]),
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
sortedVoting() {
|
||||||
|
return this.voting.slice().sort((a, b) => a.sort - b.sort);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
// this.fetchProtocolVoting();
|
this.normalizeSort();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(useProtocolVotingStore, ["fetchProtocolVoting", "createProtocolVoting"]),
|
...mapActions(useProtocolVotingStore, ["fetchProtocolVoting", "createProtocolVoting"]),
|
||||||
|
changeSort(dir: "up" | "down", thisId: number, index: number) {
|
||||||
|
let affected = this.sortedVoting[dir == "up" ? index - 1 : index + 1]?.id;
|
||||||
|
if (affected) {
|
||||||
|
this.voting.find((a) => a.id == thisId)!.sort = dir == "up" ? index - 1 : index + 1;
|
||||||
|
this.voting.find((a) => a.id == affected)!.sort = dir == "up" ? index + 1 : index - 1;
|
||||||
|
}
|
||||||
|
this.normalizeSort();
|
||||||
|
},
|
||||||
|
normalizeSort() {
|
||||||
|
let rightSort = this.voting.every((val, index) => val.sort == index);
|
||||||
|
if (!rightSort) {
|
||||||
|
this.sortedVoting.forEach((e, index) => {
|
||||||
|
e.sort = index;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Add table
Reference in a new issue