#15-messages #22

Merged
jkeffects merged 19 commits from #15-messages into main 2024-12-31 13:25:27 +00:00
9 changed files with 139 additions and 44 deletions
Showing only changes of commit ec477b7d72 - Show all commits

View file

@ -98,12 +98,12 @@ select[disabled] {
details {
user-select: none;
& summary svg {
& summary svg[indicator] {
transform: rotate(90deg);
}
}
details[open] {
& summary svg {
& summary svg[indicator] {
transform: rotate(-90deg);
}
}

View file

@ -17,7 +17,16 @@ export const useNewsletterDatesStore = defineStore("newsletterDates", {
},
getters: {
detectedChangeNewsletterDates: (state) =>
!isEqual(state.origin, state.dates) && state.syncingNewsletterDates != "syncing",
!isEqual(
state.origin.sort(
(a: NewsletterDatesViewModel, b: NewsletterDatesViewModel) =>
new Date(a.calendar.starttime).getTime() - new Date(b.calendar.starttime).getTime()
),
state.dates.sort(
(a: NewsletterDatesViewModel, b: NewsletterDatesViewModel) =>
new Date(a.calendar.starttime).getTime() - new Date(b.calendar.starttime).getTime()
)
) && state.syncingNewsletterDates != "syncing",
},
actions: {
setNewsletterDatesSyncingState(state: "synced" | "syncing" | "detectedChanges" | "failed") {
@ -45,7 +54,7 @@ export const useNewsletterDatesStore = defineStore("newsletterDates", {
await http
.patch(`/admin/newsletter/${newsletterId}/synchronize/dates`, {
dates: differenceWith(this.dates, this.origin, isEqual),
dates: this.dates,
})
.then((res) => {
this.syncingNewsletterDates = "synced";

View file

@ -2,14 +2,14 @@ import type { CalendarViewModel } from "./calendar.models";
export interface NewsletterDatesViewModel {
newsletterId: number;
calendarId: number;
calendarId: string;
diffTitle: string | null;
diffDescription: string | null;
calendar: CalendarViewModel;
}
export interface SyncNewsletterDatesViewModel {
calendarId: number;
calendarId: string;
diffTitle?: string;
diffDescription?: string;
}

View file

@ -12,41 +12,89 @@
>
<summary class="flex flex-row gap-2 bg-primary p-2 w-full justify-between items-center cursor-pointer">
<svg
indicator
class="fill-white stroke-white opacity-75 w-4 h-4"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
>
<path d="M12.95 10.707l.707-.707L8 4.343 6.586 5.757 10.828 10l-4.242 4.243L8 15.657l4.95-4.95z" />
</svg>
<p>{{ item.calendar.title }} {{ item.calendar.starttime }}</p>
<p class="w-full text-white">
{{ item.calendar.title }}:
{{
item.calendar.allDay
? new Date(item.calendar.starttime ?? "").toLocaleDateString("de-DE", {
day: "2-digit",
month: "long",
year: "numeric",
})
: new Date(item.calendar.starttime ?? "").toLocaleDateString("de-DE", {
day: "2-digit",
month: "long",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
})
}}
</p>
<TrashIcon
v-if="can('create', 'club', 'newsletter')"
class="w-5 h-5 p-1 box-content cursor-pointer text-white"
@click.prevent="removeSelected(item.calendarId)"
/>
</summary>
<div class="flex flex-col gap-2 px-1">
<input
type="text"
name="title"
id="title"
placeholder="Einscheidung"
placeholder="alternativer Titel"
autocomplete="off"
v-model="item.diffTitle"
@keyup.prevent
:disabled="!can('create', 'club', 'newsletter')"
/>
<div>
<QuillEditor
id="top"
theme="snow"
placeholder="Entscheidung Inhalt..."
style="height: 250px; max-height: 250px; min-height: 250px"
placeholder="alternative Beschreibung..."
style="height: 150px; max-height: 150px; min-height: 150px"
contentType="html"
:toolbar="toolbarOptions"
v-model:content="item.diffDescription"
:enable="can('create', 'club', 'newsletter')"
:style="!can('create', 'club', 'newsletter') ? 'opacity: 75%; background: rgb(243 244 246)' : ''"
/>
</div>
</div>
</details>
</div>
<button v-if="can('create', 'club', 'newsletter')" primary class="!w-fit" @click="addEntry">
Eintrag hinzufügen
</button>
<form class="flex flex-row md:flex-row gap-2" @submit.prevent="addEntry">
<select id="date" ref="date" value="" required>
<option value="" disabled>Datum wählen</option>
<option v-for="cal in filteredCalendar" :key="cal.id" :value="cal.id">
{{ cal.title }}
{{
cal.allDay
? new Date(cal.starttime).toLocaleDateString("de-DE", {
day: "2-digit",
month: "long",
year: "numeric",
})
: new Date(cal.starttime).toLocaleDateString("de-DE", {
day: "2-digit",
month: "long",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
})
}}
</option>
</select>
<button type="submit" primary class="!w-fit">hinzufügen</button>
</form>
</div>
</template>
@ -54,12 +102,16 @@
import { defineComponent } from "vue";
import { mapActions, mapState, mapWritableState } from "pinia";
import Spinner from "@/components/Spinner.vue";
import { useNewsletterStore } from "@/stores/admin/newsletter";
import { QuillEditor } from "@vueup/vue-quill";
import "@vueup/vue-quill/dist/vue-quill.snow.css";
import { toolbarOptions } from "@/helpers/quillConfig";
import { useNewsletterDatesStore } from "@/stores/admin/newsletterDates";
import { useAbilityStore } from "@/stores/ability";
import { useCalendarStore } from "@/stores/admin/calendar";
import type { CalendarViewModel } from "@/viewmodels/admin/calendar.models";
import { TrashIcon } from "@heroicons/vue/24/outline";
import cloneDeep from "lodash.clonedeep";
import type { NewsletterDatesViewModel } from "@/viewmodels/admin/newsletterDates.models";
</script>
<script lang="ts">
@ -69,16 +121,48 @@ export default defineComponent({
},
computed: {
...mapWritableState(useNewsletterDatesStore, ["dates", "loading"]),
...mapState(useCalendarStore, ["calendars"]),
...mapState(useAbilityStore, ["can"]),
filteredCalendar() {
return this.calendars.filter((c) => !this.dates.map((d) => d.calendarId).includes(c.id));
},
sortedDates() {
return this.dates.sort(
(a: NewsletterDatesViewModel, b: NewsletterDatesViewModel) =>
new Date(a.calendar.starttime).getTime() - new Date(b.calendar.starttime).getTime()
);
},
calendarData() {
return (dateId: string) => this.calendars.find((c) => c.id == dateId);
},
},
mounted() {
this.fetchNewsletterDates();
this.fetchCalendars();
},
methods: {
...mapActions(useNewsletterDatesStore, ["fetchNewsletterDates"]),
addEntry(){
// modal to select date
...mapActions(useCalendarStore, ["fetchCalendars"]),
addEntry(e: any) {
const formData = e.target.elements;
const dateId = formData.date.value;
this.dates.push({
newsletterId: parseInt(this.newsletterId ?? "0"),
calendarId: dateId,
diffTitle: "",
diffDescription: "",
calendar: cloneDeep(this.calendarData(dateId)) as CalendarViewModel,
});
(this.$refs.date as HTMLSelectElement).value = "";
},
removeSelected(id: string) {
let index = this.dates.findIndex((d) => d.calendarId == id);
if (index != -1) {
this.dates.splice(index, 1);
}
},
},
});
</script>

View file

@ -102,7 +102,6 @@ import {
} from "@headlessui/vue";
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
import { TrashIcon } from "@heroicons/vue/24/outline";
import { useNewsletterStore } from "@/stores/admin/newsletter";
import { useMemberStore } from "@/stores/admin/member";
import type { MemberViewModel } from "@/viewmodels/admin/member.models";
import { useNewsletterRecipientsStore } from "@/stores/admin/newsletterRecipients";

View file

@ -74,7 +74,7 @@ export default defineComponent({
{ route: "admin-club-newsletter-overview", title: "Übersicht" },
{ route: "admin-club-newsletter-dates", title: "Termine" },
{ route: "admin-club-newsletter-recipients", title: "Empfänger" },
{ route: "admin-club-newsletter-printout", title: "Druck" },
{ route: "admin-club-newsletter-printout", title: "Druck/Versand" },
],
wantToClose: false as boolean,
executeSyncAll: undefined as any,

View file

@ -12,6 +12,7 @@
>
<summary class="flex flex-row gap-2 bg-primary p-2 w-full justify-between items-center cursor-pointer">
<svg
indicator
class="fill-white stroke-white opacity-75 w-4 h-4"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"

View file

@ -12,6 +12,7 @@
>
<summary class="flex flex-row gap-2 bg-primary p-2 w-full justify-between items-center cursor-pointer">
<svg
indicator
class="fill-white stroke-white opacity-75 w-4 h-4"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
@ -23,7 +24,7 @@
type="text"
name="title"
id="title"
placeholder="Einscheidung"
placeholder="Entscheidung"
autocomplete="off"
v-model="item.topic"
@keyup.prevent

View file

@ -12,6 +12,7 @@
>
<summary class="flex flex-row gap-2 bg-primary p-2 w-full justify-between items-center cursor-pointer">
<svg
indicator
class="fill-white stroke-white opacity-75 w-4 h-4"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
@ -23,7 +24,7 @@
type="text"
name="title"
id="title"
placeholder="Einscheidung"
placeholder="Abstimmung"
autocomplete="off"
v-model="item.topic"
@keyup.prevent