newsletter recipients

This commit is contained in:
Julian Krauser 2024-12-27 13:25:37 +01:00
parent ec477b7d72
commit 81b7f533c7
6 changed files with 81 additions and 17 deletions

View file

@ -15,7 +15,7 @@
<QuillEditor
id="summary"
theme="snow"
placeholder="Zusammenfassung zur Sitzung..."
placeholder="Zusammenfassung zum Newsletter..."
style="height: 250px; max-height: 250px; min-height: 250px"
contentType="html"
:toolbar="toolbarOptions"

View file

@ -5,11 +5,27 @@
&#8634; laden fehlgeschlagen
</p>
-- select members by query
<select v-model="recipientsByQueryId">
<option value="def">Optional</option>
<option v-for="query in queries" :key="query.id" :value="query.id">{{ query.title }}</option>
</select>
<p>Empfänger durch gespeicherte Abfrage</p>
<div class="flex flex-col gap-2 grow overflow-y-auto">
<div
v-for="member in queried"
:key="member.id"
class="flex flex-row h-fit w-full border border-primary rounded-md bg-primary p-2 text-white justify-between items-center"
>
<div>
<p>{{ member.lastname }}, {{ member.firstname }} {{ member.nameaffix ? `- ${member.nameaffix}` : "" }}</p>
<p>Newsletter senden an Typ: {{ member.sendNewsletter?.type.type }}</p>
</div>
</div>
</div>
<div class="w-full">
<Combobox v-model="recipients" :disabled="!can('create', 'club', 'newsletter')" multiple>
<ComboboxLabel>Empfänger suchen</ComboboxLabel>
<ComboboxLabel>weitere Empfänger suchen</ComboboxLabel>
<div class="relative mt-1">
<ComboboxInput
class="rounded-md shadow-sm relative block w-full px-3 py-2 border border-gray-300 focus:border-primary placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-none focus:ring-0 focus:z-10 sm:text-sm resize-none"
@ -64,7 +80,6 @@
</div>
</Combobox>
</div>
<br />
<p>Ausgewählte Empfänger</p>
<div class="flex flex-col gap-2 grow overflow-y-auto">
<div
@ -104,8 +119,12 @@ import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
import { TrashIcon } from "@heroicons/vue/24/outline";
import { useMemberStore } from "@/stores/admin/member";
import type { MemberViewModel } from "@/viewmodels/admin/member.models";
import { useNewsletterStore } from "@/stores/admin/newsletter";
import { useNewsletterRecipientsStore } from "@/stores/admin/newsletterRecipients";
import { useAbilityStore } from "@/stores/ability";
import { useQueryStoreStore } from "@/stores/admin/queryStore";
import { useQueryBuilderStore } from "@/stores/admin/queryBuilder";
import cloneDeep from "lodash.clonedeep";
</script>
<script lang="ts">
@ -113,6 +132,11 @@ export default defineComponent({
props: {
newsletterId: String,
},
watch: {
recipientsByQuery() {
this.loadQuery();
},
},
data() {
return {
query: "" as String,
@ -120,7 +144,10 @@ export default defineComponent({
},
computed: {
...mapWritableState(useNewsletterRecipientsStore, ["recipients", "loading"]),
...mapWritableState(useNewsletterStore, ["activeNewsletterObj"]),
...mapState(useMemberStore, ["members"]),
...mapState(useQueryStoreStore, ["queries"]),
...mapState(useQueryBuilderStore, ["data"]),
...mapState(useAbilityStore, ["can"]),
filtered(): Array<MemberViewModel> {
return this.query === ""
@ -144,20 +171,60 @@ export default defineComponent({
selected(): Array<MemberViewModel> {
return this.members.filter((m) => this.recipients.includes(m.id));
},
queried(): Array<MemberViewModel> {
let keys = Object.keys(this.data?.[0] ?? {});
let memberKey = keys.find((k) => k.includes("member_id"));
return this.members.filter((m) =>
this.data
.map((t) => ({
id: t.id,
...(memberKey ? { memberId: t[memberKey] } : {}),
}))
.some((d) => (d.memberId ?? d.id) == m.id)
);
},
recipientsByQueryId: {
get() {
return this.activeNewsletterObj?.recipientsByQueryId ?? "def";
},
set(val: string) {
if (this.activeNewsletterObj == undefined) return;
if (val == "def") {
this.activeNewsletterObj.recipientsByQueryId = null;
this.activeNewsletterObj.recipientsByQuery = null;
} else if (this.queries.find((q) => q.id == parseInt(val))) {
this.activeNewsletterObj.recipientsByQueryId = parseInt(val);
this.activeNewsletterObj.recipientsByQuery = cloneDeep(this.queries.find((q) => q.id == parseInt(val)));
this.sendQuery(0, 1000, this.recipientsByQuery?.query);
}
},
},
recipientsByQuery() {
return this.activeNewsletterObj?.recipientsByQuery;
},
},
mounted() {
this.fetchMembers();
this.fetchMembers(0, 1000, true);
this.fetchNewsletterRecipients();
this.fetchQueries();
this.loadQuery();
},
methods: {
...mapActions(useMemberStore, ["fetchMembers"]),
...mapActions(useNewsletterRecipientsStore, ["fetchNewsletterRecipients"]),
...mapActions(useQueryStoreStore, ["fetchQueries"]),
...mapActions(useQueryBuilderStore, ["sendQuery"]),
removeSelected(id: number) {
let index = this.recipients.findIndex((s) => s == id);
if (index != -1) {
this.recipients.splice(index, 1);
}
},
loadQuery() {
if (this.recipientsByQuery) {
this.sendQuery(0, 1000, this.recipientsByQuery.query);
}
},
},
});
</script>

View file

@ -141,7 +141,7 @@ export default defineComponent({
},
},
mounted() {
this.fetchMembers();
this.fetchMembers(0, 1000, true);
this.fetchProtocolPresence();
},
methods: {