diff --git a/README.md b/README.md index d6430fe..1ac5ff6 100644 --- a/README.md +++ b/README.md @@ -32,9 +32,9 @@ services: #environment: # - SERVERADDRESS= # wichtig: ohne Pfad - # - APPNAMEOVERWRITE=Mitgliederverwaltung # ersetzt den Namen FF-Admin auf der Login-Seite und sonstigen Positionen in der Oberfläche - # - IMPRINTLINK=https://mywebsite-imprint-url - # - PRIVACYLINK=https://mywebsite-privacy-url + # - APPNAMEOVERWRITE= # ersetzt den Namen FF-Admin auf der Login-Seite und sonstigen Positionen in der Oberfläche + # - IMPRINTLINK= + # - PRIVACYLINK= # - CUSTOMLOGINMESSAGE=betrieben von xy #volumes: # - /favicon.ico:/usr/share/nginx/html/favicon.ico # 48x48 px Auflösung @@ -68,7 +68,7 @@ Ein eigenes Favicon und Logo kann über das verwenden Volume ausgetauscht werden ## Einrichtung -1. **Admin Benutzer erstellen**: Erstellen Sie einen Admin Benutzer unter dem Pfad /setup, um auf die Migliederverwaltung Zugriff zu erhalten. Nach der Erstellung des ersten Benutzers wird der Pfad automatisch geblockt. +1. **Admin Benutzer erstellen**: Erstellen Sie einen Admin Benutzer unter dem Pfad /setup, um auf die Mitgliederverwaltung Zugriff zu erhalten. Nach der Erstellung des ersten Benutzers wird der Pfad automatisch geblockt. 2. **Rollen und Berechtigungen**: Unter `Benutzer > Rollen` können die Rollen und Berechtigungen für die Benutzer erstellt und angepasst werden. diff --git a/src/components/admin/club/calendar/CreateCalendarModal.vue b/src/components/admin/club/calendar/CreateCalendarModal.vue index 7e27380..a1a77f6 100644 --- a/src/components/admin/club/calendar/CreateCalendarModal.vue +++ b/src/components/admin/club/calendar/CreateCalendarModal.vue @@ -79,11 +79,30 @@
- +
- +
@@ -93,8 +112,12 @@ type="date" id="startdate" required - :value="data.start" - @change="($event) => (($refs.enddate as HTMLInputElement).max = ($event.target as HTMLInputElement).value)" + :value="formatForDateInput(data.start)" + @change=" + ($event) => { + ($refs.enddate as HTMLInputElement).min = formatForDateInput(($event.target as HTMLInputElement).value); + } + " />
@@ -105,7 +128,7 @@ id="enddate" required :value="decrementEndDate(data.end)" - :min="data.start" + :min="formatForDateInput(data.start)" />
@@ -180,11 +203,11 @@ export default defineComponent({ let createCalendar: CreateCalendarViewModel = { typeId: this.selectedType.id, starttime: this.allDay - ? new Date(new Date(formData.startdate.value).setHours(0, 0, 0, 0)) - : formData.starttime.value, + ? new Date(new Date(formData.startdate.value).setHours(0, 0, 0, 0)).toISOString() + : new Date(formData.starttime.value).toISOString(), endtime: this.allDay - ? new Date(new Date(formData.enddate.value).setHours(23, 59, 59, 999)) - : formData.endtime.value, + ? new Date(new Date(formData.enddate.value).setHours(23, 59, 59, 999)).toISOString() + : new Date(formData.endtime.value).toISOString(), title: formData.title.value, content: formData.content.value, location: formData.location.value, @@ -209,6 +232,26 @@ export default defineComponent({ const month = String(localDate.getMonth() + 1).padStart(2, "0"); const day = String(localDate.getDate() - 1).padStart(2, "0"); + return `${year}-${month}-${day}`; + }, + formatForDateTimeLocalInput(utcDateString: string) { + const localDate = new Date(utcDateString); + + const year = localDate.getFullYear(); + const month = String(localDate.getMonth() + 1).padStart(2, "0"); + const day = String(localDate.getDate()).padStart(2, "0"); + const hours = String(localDate.getHours()).padStart(2, "0"); + const minutes = String(localDate.getMinutes()).padStart(2, "0"); + + return `${year}-${month}-${day}T${hours}:${minutes}`; + }, + formatForDateInput(utcDateString: string) { + const localDate = new Date(utcDateString); + + const year = localDate.getFullYear(); + const month = String(localDate.getMonth() + 1).padStart(2, "0"); + const day = String(localDate.getDate()).padStart(2, "0"); + return `${year}-${month}-${day}`; }, }, diff --git a/src/components/admin/club/calendar/UpdateCalendarModal.vue b/src/components/admin/club/calendar/UpdateCalendarModal.vue index 0f06597..19b46ee 100644 --- a/src/components/admin/club/calendar/UpdateCalendarModal.vue +++ b/src/components/admin/club/calendar/UpdateCalendarModal.vue @@ -78,7 +78,7 @@ -
+
import("@/views/admin/user/version/VersionDisplay.vue"), + }, ], }, { @@ -701,11 +706,6 @@ const router = createRouter({ name: "account-administration", component: () => import("@/views/account/Administration.vue"), }, - { - path: "version", - name: "account-version", - component: () => import("@/views/account/VersionDisplay.vue"), - }, { path: ":pathMatch(.*)*", name: "account-404", diff --git a/src/stores/ability.ts b/src/stores/ability.ts index 5b8a951..d5190af 100644 --- a/src/stores/ability.ts +++ b/src/stores/ability.ts @@ -43,6 +43,11 @@ export const useAbilityStore = defineStore("ability", { return true; return false; }, + isAdmin: (state) => (): boolean => { + const permissions = state.permissions; + if (state.isOwner) return true; + return permissions?.admin ?? false; + }, _can: () => ( diff --git a/src/stores/account.ts b/src/stores/account.ts index 09e05c8..f3de2af 100644 --- a/src/stores/account.ts +++ b/src/stores/account.ts @@ -5,6 +5,7 @@ import { useAbilityStore } from "./ability"; export const useAccountStore = defineStore("account", { state: () => { return { + id: "" as string, firstname: "" as string, lastname: "" as string, mail: "" as string, @@ -17,7 +18,8 @@ export const useAccountStore = defineStore("account", { localStorage.removeItem("refreshToken"); window.open("/login", "_self"); }, - setAccountData(firstname: string, lastname: string, mail: string, alias: string) { + setAccountData(id: string, firstname: string, lastname: string, mail: string, alias: string) { + this.id = id; this.firstname = firstname; this.lastname = lastname; this.mail = mail; diff --git a/src/stores/admin/club/newsletter/newsletterRecipients.ts b/src/stores/admin/club/newsletter/newsletterRecipients.ts index 2a7efb3..cc62005 100644 --- a/src/stores/admin/club/newsletter/newsletterRecipients.ts +++ b/src/stores/admin/club/newsletter/newsletterRecipients.ts @@ -20,7 +20,10 @@ export const useNewsletterRecipientsStore = defineStore("newsletterRecipients", }, getters: { detectedChangeNewsletterRecipients: (state) => - !isEqual(state.origin, state.recipients) && state.syncingNewsletterRecipients != "syncing", + !isEqual( + state.origin.sort((a: string, b: string) => a.localeCompare(b)), + state.recipients.sort((a: string, b: string) => a.localeCompare(b)) + ) && state.syncingNewsletterRecipients != "syncing", }, actions: { setNewsletterRecipientsSyncingState(state: "synced" | "syncing" | "detectedChanges" | "failed") { diff --git a/src/stores/admin/club/queryBuilder.ts b/src/stores/admin/club/queryBuilder.ts index 159a423..25a87e2 100644 --- a/src/stores/admin/club/queryBuilder.ts +++ b/src/stores/admin/club/queryBuilder.ts @@ -30,7 +30,7 @@ export const useQueryBuilderStore = defineStore("queryBuilder", { this.loading = "failed"; }); }, - sendQuery(offset = 0, count = 25, query?: DynamicQueryStructure | string) { + async sendQuery(offset = 0, count = 25, query?: DynamicQueryStructure | string) { this.queryError = ""; if (offset == 0) { this.data = []; @@ -40,7 +40,7 @@ export const useQueryBuilderStore = defineStore("queryBuilder", { if (queryToSend == undefined || queryToSend == "" || (typeof queryToSend != "string" && queryToSend.table == "")) return; this.loadingData = "loading"; - http + await http .post(`/admin/querybuilder/query?offset=${offset}&count=${count}`, { query: queryToSend, }) @@ -65,7 +65,8 @@ export const useQueryBuilderStore = defineStore("queryBuilder", { this.queryError = ""; this.loadingData = "fetched"; }, - exportData() { + async exportData() { + await this.sendQuery(0, this.totalLength); if (this.data.length == 0) return; const csvString = [Object.keys(this.data[0]), ...this.data.map((d) => Object.values(d))] .map((e) => e.join(";")) diff --git a/src/stores/admin/navigation.ts b/src/stores/admin/navigation.ts index 07a7532..33297b6 100644 --- a/src/stores/admin/navigation.ts +++ b/src/stores/admin/navigation.ts @@ -134,6 +134,7 @@ export const useNavigationStore = defineStore("navigation", { ...(abilityStore.can("read", "user", "role") ? [{ key: "role", title: "Rollen" }] : []), ...(abilityStore.can("read", "user", "webapi") ? [{ key: "webapi", title: "Webapi-Token" }] : []), ...(abilityStore.can("read", "user", "backup") ? [{ key: "backup", title: "Backups" }] : []), + ...(abilityStore.isAdmin() ? [{ key: "version", title: "Version" }] : []), ], }, } as navigationModel; diff --git a/src/views/account/Administration.vue b/src/views/account/Administration.vue index 558e3a6..de72c68 100644 --- a/src/views/account/Administration.vue +++ b/src/views/account/Administration.vue @@ -103,6 +103,7 @@ import { } from "@headlessui/vue"; import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid"; import type { UserViewModel } from "@/viewmodels/admin/user/user.models"; +import { useAccountStore } from "@/stores/account"; diff --git a/vite.config.ts b/vite.config.ts index d78607e..a967155 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -43,11 +43,13 @@ export default defineConfig({ name: "__APPNAMEOVERWRITE__", short_name: "__APPNAMEOVERWRITE__", theme_color: "#990b00", + display: "standalone", + start_url: "/", icons: [ { src: "favicon.ico", sizes: "48x48", - type: "image/png", + type: "image/ico", }, { src: "favicon.png",