date editing
This commit is contained in:
parent
be2bd5e6e3
commit
ec477b7d72
9 changed files with 139 additions and 44 deletions
|
@ -98,12 +98,12 @@ select[disabled] {
|
||||||
|
|
||||||
details {
|
details {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
& summary svg {
|
& summary svg[indicator] {
|
||||||
transform: rotate(90deg);
|
transform: rotate(90deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
details[open] {
|
details[open] {
|
||||||
& summary svg {
|
& summary svg[indicator] {
|
||||||
transform: rotate(-90deg);
|
transform: rotate(-90deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,16 @@ export const useNewsletterDatesStore = defineStore("newsletterDates", {
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
detectedChangeNewsletterDates: (state) =>
|
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: {
|
actions: {
|
||||||
setNewsletterDatesSyncingState(state: "synced" | "syncing" | "detectedChanges" | "failed") {
|
setNewsletterDatesSyncingState(state: "synced" | "syncing" | "detectedChanges" | "failed") {
|
||||||
|
@ -45,7 +54,7 @@ export const useNewsletterDatesStore = defineStore("newsletterDates", {
|
||||||
|
|
||||||
await http
|
await http
|
||||||
.patch(`/admin/newsletter/${newsletterId}/synchronize/dates`, {
|
.patch(`/admin/newsletter/${newsletterId}/synchronize/dates`, {
|
||||||
dates: differenceWith(this.dates, this.origin, isEqual),
|
dates: this.dates,
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
this.syncingNewsletterDates = "synced";
|
this.syncingNewsletterDates = "synced";
|
||||||
|
|
|
@ -2,14 +2,14 @@ import type { CalendarViewModel } from "./calendar.models";
|
||||||
|
|
||||||
export interface NewsletterDatesViewModel {
|
export interface NewsletterDatesViewModel {
|
||||||
newsletterId: number;
|
newsletterId: number;
|
||||||
calendarId: number;
|
calendarId: string;
|
||||||
diffTitle: string | null;
|
diffTitle: string | null;
|
||||||
diffDescription: string | null;
|
diffDescription: string | null;
|
||||||
calendar: CalendarViewModel;
|
calendar: CalendarViewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SyncNewsletterDatesViewModel {
|
export interface SyncNewsletterDatesViewModel {
|
||||||
calendarId: number;
|
calendarId: string;
|
||||||
diffTitle?: string;
|
diffTitle?: string;
|
||||||
diffDescription?: string;
|
diffDescription?: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,41 +12,89 @@
|
||||||
>
|
>
|
||||||
<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
|
||||||
|
indicator
|
||||||
class="fill-white stroke-white opacity-75 w-4 h-4"
|
class="fill-white stroke-white opacity-75 w-4 h-4"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 20 20"
|
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" />
|
<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>
|
</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>
|
</summary>
|
||||||
<input
|
<div class="flex flex-col gap-2 px-1">
|
||||||
type="text"
|
<input
|
||||||
name="title"
|
type="text"
|
||||||
id="title"
|
name="title"
|
||||||
placeholder="Einscheidung"
|
id="title"
|
||||||
autocomplete="off"
|
placeholder="alternativer Titel"
|
||||||
v-model="item.diffTitle"
|
autocomplete="off"
|
||||||
@keyup.prevent
|
v-model="item.diffTitle"
|
||||||
:disabled="!can('create', 'club', 'newsletter')"
|
@keyup.prevent
|
||||||
/>
|
:disabled="!can('create', 'club', 'newsletter')"
|
||||||
<QuillEditor
|
/>
|
||||||
id="top"
|
<div>
|
||||||
theme="snow"
|
<QuillEditor
|
||||||
placeholder="Entscheidung Inhalt..."
|
id="top"
|
||||||
style="height: 250px; max-height: 250px; min-height: 250px"
|
theme="snow"
|
||||||
contentType="html"
|
placeholder="alternative Beschreibung..."
|
||||||
:toolbar="toolbarOptions"
|
style="height: 150px; max-height: 150px; min-height: 150px"
|
||||||
v-model:content="item.diffDescription"
|
contentType="html"
|
||||||
:enable="can('create', 'club', 'newsletter')"
|
:toolbar="toolbarOptions"
|
||||||
:style="!can('create', 'club', 'newsletter') ? 'opacity: 75%; background: rgb(243 244 246)' : ''"
|
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>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button v-if="can('create', 'club', 'newsletter')" primary class="!w-fit" @click="addEntry">
|
<form class="flex flex-row md:flex-row gap-2" @submit.prevent="addEntry">
|
||||||
Eintrag hinzufügen
|
<select id="date" ref="date" value="" required>
|
||||||
</button>
|
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -54,12 +102,16 @@
|
||||||
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 { useNewsletterStore } from "@/stores/admin/newsletter";
|
|
||||||
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 { useNewsletterDatesStore } from "@/stores/admin/newsletterDates";
|
import { useNewsletterDatesStore } from "@/stores/admin/newsletterDates";
|
||||||
import { useAbilityStore } from "@/stores/ability";
|
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>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
@ -69,16 +121,48 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapWritableState(useNewsletterDatesStore, ["dates", "loading"]),
|
...mapWritableState(useNewsletterDatesStore, ["dates", "loading"]),
|
||||||
|
...mapState(useCalendarStore, ["calendars"]),
|
||||||
...mapState(useAbilityStore, ["can"]),
|
...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() {
|
mounted() {
|
||||||
this.fetchNewsletterDates();
|
this.fetchNewsletterDates();
|
||||||
|
this.fetchCalendars();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(useNewsletterDatesStore, ["fetchNewsletterDates"]),
|
...mapActions(useNewsletterDatesStore, ["fetchNewsletterDates"]),
|
||||||
addEntry(){
|
...mapActions(useCalendarStore, ["fetchCalendars"]),
|
||||||
// modal to select date
|
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>
|
</script>
|
||||||
|
|
|
@ -72,10 +72,10 @@
|
||||||
:key="member.id"
|
: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"
|
class="flex flex-row h-fit w-full border border-primary rounded-md bg-primary p-2 text-white justify-between items-center"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<p>{{ member.lastname }}, {{ member.firstname }} {{ member.nameaffix ? `- ${member.nameaffix}` : "" }}</p>
|
<p>{{ member.lastname }}, {{ member.firstname }} {{ member.nameaffix ? `- ${member.nameaffix}` : "" }}</p>
|
||||||
<p>Newsletter senden an Typ: {{ member.sendNewsletter?.type.type }}</p>
|
<p>Newsletter senden an Typ: {{ member.sendNewsletter?.type.type }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<TrashIcon
|
<TrashIcon
|
||||||
v-if="can('create', 'club', 'newsletter')"
|
v-if="can('create', 'club', 'newsletter')"
|
||||||
|
@ -102,7 +102,6 @@ import {
|
||||||
} from "@headlessui/vue";
|
} from "@headlessui/vue";
|
||||||
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
||||||
import { TrashIcon } from "@heroicons/vue/24/outline";
|
import { TrashIcon } from "@heroicons/vue/24/outline";
|
||||||
import { useNewsletterStore } from "@/stores/admin/newsletter";
|
|
||||||
import { useMemberStore } from "@/stores/admin/member";
|
import { useMemberStore } from "@/stores/admin/member";
|
||||||
import type { MemberViewModel } from "@/viewmodels/admin/member.models";
|
import type { MemberViewModel } from "@/viewmodels/admin/member.models";
|
||||||
import { useNewsletterRecipientsStore } from "@/stores/admin/newsletterRecipients";
|
import { useNewsletterRecipientsStore } from "@/stores/admin/newsletterRecipients";
|
||||||
|
|
|
@ -74,7 +74,7 @@ export default defineComponent({
|
||||||
{ route: "admin-club-newsletter-overview", title: "Übersicht" },
|
{ route: "admin-club-newsletter-overview", title: "Übersicht" },
|
||||||
{ route: "admin-club-newsletter-dates", title: "Termine" },
|
{ route: "admin-club-newsletter-dates", title: "Termine" },
|
||||||
{ route: "admin-club-newsletter-recipients", title: "Empfänger" },
|
{ 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,
|
wantToClose: false as boolean,
|
||||||
executeSyncAll: undefined as any,
|
executeSyncAll: undefined as any,
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
>
|
>
|
||||||
<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
|
||||||
|
indicator
|
||||||
class="fill-white stroke-white opacity-75 w-4 h-4"
|
class="fill-white stroke-white opacity-75 w-4 h-4"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 20 20"
|
viewBox="0 0 20 20"
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
>
|
>
|
||||||
<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
|
||||||
|
indicator
|
||||||
class="fill-white stroke-white opacity-75 w-4 h-4"
|
class="fill-white stroke-white opacity-75 w-4 h-4"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 20 20"
|
viewBox="0 0 20 20"
|
||||||
|
@ -23,7 +24,7 @@
|
||||||
type="text"
|
type="text"
|
||||||
name="title"
|
name="title"
|
||||||
id="title"
|
id="title"
|
||||||
placeholder="Einscheidung"
|
placeholder="Entscheidung"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
v-model="item.topic"
|
v-model="item.topic"
|
||||||
@keyup.prevent
|
@keyup.prevent
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
>
|
>
|
||||||
<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
|
||||||
|
indicator
|
||||||
class="fill-white stroke-white opacity-75 w-4 h-4"
|
class="fill-white stroke-white opacity-75 w-4 h-4"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 20 20"
|
viewBox="0 0 20 20"
|
||||||
|
@ -23,7 +24,7 @@
|
||||||
type="text"
|
type="text"
|
||||||
name="title"
|
name="title"
|
||||||
id="title"
|
id="title"
|
||||||
placeholder="Einscheidung"
|
placeholder="Abstimmung"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
v-model="item.topic"
|
v-model="item.topic"
|
||||||
@keyup.prevent
|
@keyup.prevent
|
||||||
|
|
Loading…
Reference in a new issue