From fb783609465d3387bf9a160bdd2bb1c566bdf4bb Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Sat, 19 Apr 2025 09:24:55 +0200 Subject: [PATCH 01/26] show who does not have newsletter configured --- src/components/admin/MemberSearchSelect.vue | 9 +- .../club/newsletter/NewsletterRecipients.vue | 82 +++++++++++-------- 2 files changed, 55 insertions(+), 36 deletions(-) diff --git a/src/components/admin/MemberSearchSelect.vue b/src/components/admin/MemberSearchSelect.vue index 50efd6f..c56b0ef 100644 --- a/src/components/admin/MemberSearchSelect.vue +++ b/src/components/admin/MemberSearchSelect.vue @@ -1,10 +1,11 @@ @@ -67,7 +69,7 @@ import { TransitionRoot, } from "@headlessui/vue"; import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid"; -import { TrashIcon } from "@heroicons/vue/24/outline"; +import { ArchiveBoxIcon, ExclamationTriangleIcon, TrashIcon, UserPlusIcon } from "@heroicons/vue/24/outline"; import { useMemberStore } from "@/stores/admin/club/member/member"; import type { MemberViewModel } from "@/viewmodels/admin/club/member/member.models"; import { useNewsletterStore } from "@/stores/admin/club/newsletter/newsletter"; @@ -93,6 +95,7 @@ export default defineComponent({ return { query: "" as String, members: [] as Array, + showMemberSelect: false as boolean, }; }, computed: { @@ -125,6 +128,17 @@ export default defineComponent({ .some((d) => (d.memberId ?? d.id) == m.id) ); }, + showRecipientsByMode() { + return (this.showMemberSelect ? this.selected : this.queried).sort((a, b) => { + const aHasConfig = a.sendNewsletter?.type.type != null; + const bHasConfig = b.sendNewsletter?.type.type != null; + if (aHasConfig === bHasConfig) return 0; + return aHasConfig ? -1 : 1; + }); + }, + countOfNoConfig() { + return this.showRecipientsByMode.filter((member) => member.sendNewsletter?.type.type == null).length; + }, recipientsByQueryId: { get() { return this.activeNewsletterObj?.recipientsByQueryId ?? "def"; From b19e8df5616f9dc3bc6776a8ca14b725303bc34c Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Sat, 19 Apr 2025 09:42:25 +0200 Subject: [PATCH 02/26] add send none to newsletter config --- .../newsletterConfig/NewsletterConfigListItem.vue | 8 ++++---- src/enums/newsletterConfigEnum.ts | 5 +++++ src/enums/newsletterConfigType.ts | 4 ---- src/stores/admin/configuration/newsletterConfig.ts | 2 +- .../admin/configuration/newsletterConfig.models.ts | 6 +++--- .../admin/club/newsletter/NewsletterRecipients.vue | 10 ++++++---- 6 files changed, 19 insertions(+), 16 deletions(-) create mode 100644 src/enums/newsletterConfigEnum.ts delete mode 100644 src/enums/newsletterConfigType.ts diff --git a/src/components/admin/configuration/newsletterConfig/NewsletterConfigListItem.vue b/src/components/admin/configuration/newsletterConfig/NewsletterConfigListItem.vue index da19c83..a4e7842 100644 --- a/src/components/admin/configuration/newsletterConfig/NewsletterConfigListItem.vue +++ b/src/components/admin/configuration/newsletterConfig/NewsletterConfigListItem.vue @@ -3,13 +3,13 @@

Newsletter bei Type "{{ comType.type }}" versenden/exportieren als

- -
@@ -36,7 +36,7 @@ import Spinner from "@/components/Spinner.vue"; import SuccessCheckmark from "@/components/SuccessCheckmark.vue"; import FailureXMark from "@/components/FailureXMark.vue"; import { useModalStore } from "@/stores/modal"; -import { NewsletterConfigType } from "@/enums/newsletterConfigType"; +import { NewsletterConfigEnum } from "@/enums/newsletterConfigEnum"; import type { AxiosResponse } from "axios"; import type { CommunicationTypeViewModel } from "@/viewmodels/admin/configuration/communicationType.models"; import { useAbilityStore } from "@/stores/ability"; @@ -62,7 +62,7 @@ export default defineComponent({ }, }, mounted() { - this.configs = Object.values(NewsletterConfigType); + this.configs = Object.values(NewsletterConfigEnum); }, beforeUnmount() { try { diff --git a/src/enums/newsletterConfigEnum.ts b/src/enums/newsletterConfigEnum.ts new file mode 100644 index 0000000..1e7313f --- /dev/null +++ b/src/enums/newsletterConfigEnum.ts @@ -0,0 +1,5 @@ +export enum NewsletterConfigEnum { + pdf = "pdf", + mail = "mail", + none = "none", +} diff --git a/src/enums/newsletterConfigType.ts b/src/enums/newsletterConfigType.ts deleted file mode 100644 index 4703494..0000000 --- a/src/enums/newsletterConfigType.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum NewsletterConfigType { - pdf = "pdf", - mail = "mail", -} diff --git a/src/stores/admin/configuration/newsletterConfig.ts b/src/stores/admin/configuration/newsletterConfig.ts index 3bb12be..eef3687 100644 --- a/src/stores/admin/configuration/newsletterConfig.ts +++ b/src/stores/admin/configuration/newsletterConfig.ts @@ -6,7 +6,7 @@ import type { import { http } from "@/serverCom"; import type { AxiosResponse } from "axios"; -export const useNewsletterConfigStore = defineStore("newsletterConfi", { +export const useNewsletterConfigStore = defineStore("newsletterConfig", { state: () => { return { config: [] as Array, diff --git a/src/viewmodels/admin/configuration/newsletterConfig.models.ts b/src/viewmodels/admin/configuration/newsletterConfig.models.ts index 6d8be6f..a5a0684 100644 --- a/src/viewmodels/admin/configuration/newsletterConfig.models.ts +++ b/src/viewmodels/admin/configuration/newsletterConfig.models.ts @@ -1,13 +1,13 @@ -import type { NewsletterConfigType } from "@/enums/newsletterConfigType"; +import type { NewsletterConfigEnum } from "@/enums/newsletterConfigEnum"; import type { CommunicationTypeViewModel } from "./communicationType.models"; export interface NewsletterConfigViewModel { comTypeId: number; - config: NewsletterConfigType; + config: NewsletterConfigEnum; comType: CommunicationTypeViewModel; } export interface SetNewsletterConfigViewModel { comTypeId: number; - config: NewsletterConfigType; + config: NewsletterConfigEnum; } diff --git a/src/views/admin/club/newsletter/NewsletterRecipients.vue b/src/views/admin/club/newsletter/NewsletterRecipients.vue index c71df25..dac8ed7 100644 --- a/src/views/admin/club/newsletter/NewsletterRecipients.vue +++ b/src/views/admin/club/newsletter/NewsletterRecipients.vue @@ -4,6 +4,7 @@

↺ laden fehlgeschlagen

+
+
+

Vereins-Icon

+ +
+ Kein Icon hochgeladen +
+
+
+

Vereins-Logo

+ +
+ Kein Logo hochgeladen +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
diff --git a/src/components/admin/management/setting/MailSetting.vue b/src/components/admin/management/setting/MailSetting.vue index 57eeadf..b945b2a 100644 --- a/src/components/admin/management/setting/MailSetting.vue +++ b/src/components/admin/management/setting/MailSetting.vue @@ -4,15 +4,54 @@

E-Mail Einstellungen

- +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+ + +
+
+ + +
diff --git a/src/components/admin/management/setting/SessionSetting.vue b/src/components/admin/management/setting/SessionSetting.vue index 4f487ab..e93aee8 100644 --- a/src/components/admin/management/setting/SessionSetting.vue +++ b/src/components/admin/management/setting/SessionSetting.vue @@ -4,15 +4,35 @@

Login-Session Einstellungen

- +
+ + +
+
+ + +
+
+ + +
diff --git a/src/stores/admin/management/setting.ts b/src/stores/admin/management/setting.ts index c57cf69..174141a 100644 --- a/src/stores/admin/management/setting.ts +++ b/src/stores/admin/management/setting.ts @@ -1,6 +1,6 @@ import { defineStore } from "pinia"; import { http } from "@/serverCom"; -import { type SettingString, type SettingValueMapping } from "@/types/settingTypes"; +import type { SettingString, SettingTopic, SettingValueMapping } from "@/types/settingTypes"; import type { AxiosResponse } from "axios"; export const useSettingStore = defineStore("setting", { @@ -16,6 +16,19 @@ export const useSettingStore = defineStore("setting", { (key: K): SettingValueMapping[K] => { return state.settings[key]; }, + readByTopic: + (state) => + ( + topic: T + ): { [K in SettingString as K extends `${T}.${string}` ? K : never]: SettingValueMapping[K] } => { + return Object.entries(state.settings).reduce((acc, [key, value]) => { + const typedKey = key as SettingString; + if (key.startsWith(topic)) { + acc[typedKey] = value; + } + return acc; + }, {} as any); + }, }, actions: { fetchSettings() { From 06380e48c5e05082c514c9c7efc8daa97d719371 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Tue, 29 Apr 2025 13:10:30 +0200 Subject: [PATCH 12/26] Settings form and handling --- .../admin/management/setting/AppSetting.vue | 65 +++++++--- .../management/setting/BackupSetting.vue | 49 +++++--- .../admin/management/setting/BaseSetting.vue | 79 ++++++++++++ .../management/setting/ClubImageSetting.vue | 90 +++++++++++++ .../admin/management/setting/ClubSetting.vue | 107 ++++++++++------ .../admin/management/setting/MailSetting.vue | 119 ++++++++++++------ .../management/setting/SessionSetting.vue | 74 ++++++++--- src/stores/admin/management/setting.ts | 28 ++++- src/stores/configuration.ts | 6 +- .../admin/management/setting/Setting.vue | 3 + 10 files changed, 485 insertions(+), 135 deletions(-) create mode 100644 src/components/admin/management/setting/BaseSetting.vue create mode 100644 src/components/admin/management/setting/ClubImageSetting.vue diff --git a/src/components/admin/management/setting/AppSetting.vue b/src/components/admin/management/setting/AppSetting.vue index 234a4ab..787dbe1 100644 --- a/src/components/admin/management/setting/AppSetting.vue +++ b/src/components/admin/management/setting/AppSetting.vue @@ -1,42 +1,67 @@ diff --git a/src/components/admin/management/setting/BackupSetting.vue b/src/components/admin/management/setting/BackupSetting.vue index f18298a..f71a36a 100644 --- a/src/components/admin/management/setting/BackupSetting.vue +++ b/src/components/admin/management/setting/BackupSetting.vue @@ -1,34 +1,53 @@ diff --git a/src/components/admin/management/setting/BaseSetting.vue b/src/components/admin/management/setting/BaseSetting.vue new file mode 100644 index 0000000..96c1308 --- /dev/null +++ b/src/components/admin/management/setting/BaseSetting.vue @@ -0,0 +1,79 @@ + + + + + diff --git a/src/components/admin/management/setting/ClubImageSetting.vue b/src/components/admin/management/setting/ClubImageSetting.vue new file mode 100644 index 0000000..e2ab129 --- /dev/null +++ b/src/components/admin/management/setting/ClubImageSetting.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/src/components/admin/management/setting/ClubSetting.vue b/src/components/admin/management/setting/ClubSetting.vue index 51dfb40..8b6cbcc 100644 --- a/src/components/admin/management/setting/ClubSetting.vue +++ b/src/components/admin/management/setting/ClubSetting.vue @@ -1,58 +1,89 @@ diff --git a/src/components/admin/management/setting/MailSetting.vue b/src/components/admin/management/setting/MailSetting.vue index b945b2a..64ca03d 100644 --- a/src/components/admin/management/setting/MailSetting.vue +++ b/src/components/admin/management/setting/MailSetting.vue @@ -1,57 +1,100 @@ diff --git a/src/components/admin/management/setting/SessionSetting.vue b/src/components/admin/management/setting/SessionSetting.vue index e93aee8..712ea75 100644 --- a/src/components/admin/management/setting/SessionSetting.vue +++ b/src/components/admin/management/setting/SessionSetting.vue @@ -1,38 +1,76 @@ diff --git a/src/stores/admin/management/setting.ts b/src/stores/admin/management/setting.ts index 174141a..defb78b 100644 --- a/src/stores/admin/management/setting.ts +++ b/src/stores/admin/management/setting.ts @@ -50,14 +50,32 @@ export const useSettingStore = defineStore("setting", { return res; }); }, + async uploadImage(data: { key: SettingString; value?: File }[]): Promise> { + const formData = new FormData(); + for (let entry of data) { + if (entry.value) formData.append(entry.key, entry.value); + } + return await http.put("/admin/setting/img", formData, { + headers: { + "Content-Type": "multipart/form-data", + }, + }); + }, + async updateSettings( + data: { key: K; value: SettingValueMapping[K] }[] + ): Promise> { + return await http.put("/admin/setting", data); + }, async updateSetting( key: K, - val: SettingValueMapping[K] + value: SettingValueMapping[K] ): Promise> { - return await http.put("/admin/setting", { - setting: key, - value: val, - }); + return await http.put("/admin/setting", [ + { + setting: key, + value: value, + }, + ]); }, async resetSetting(key: SettingString): Promise> { return await http.delete(`/admin/setting/${key}`); diff --git a/src/stores/configuration.ts b/src/stores/configuration.ts index c33803a..1e8bfd8 100644 --- a/src/stores/configuration.ts +++ b/src/stores/configuration.ts @@ -10,6 +10,8 @@ export const useConfigurationStore = defineStore("configuration", { clubWebsite: "", appCustom_login_message: "", appShow_link_to_calendar: false as boolean, + + serverOffline: false as boolean, }; }, actions: { @@ -24,7 +26,9 @@ export const useConfigurationStore = defineStore("configuration", { this.appCustom_login_message = res.data["app.custom_login_message"]; this.appShow_link_to_calendar = res.data["app.show_link_to_calendar"]; }) - .catch(() => {}); + .catch(() => { + this.serverOffline = true; + }); }, }, }); diff --git a/src/views/admin/management/setting/Setting.vue b/src/views/admin/management/setting/Setting.vue index 2220105..25e602a 100644 --- a/src/views/admin/management/setting/Setting.vue +++ b/src/views/admin/management/setting/Setting.vue @@ -6,6 +6,8 @@