docker build test
This commit is contained in:
parent
075c598a86
commit
5bb107e53a
39 changed files with 117 additions and 75 deletions
|
@ -2,3 +2,4 @@
|
||||||
node_modules/
|
node_modules/
|
||||||
dist/
|
dist/
|
||||||
.git/
|
.git/
|
||||||
|
.env
|
||||||
|
|
|
@ -8,9 +8,9 @@ RUN npm install
|
||||||
|
|
||||||
COPY . /app
|
COPY . /app
|
||||||
|
|
||||||
RUN npm run build
|
RUN npm run build-only
|
||||||
|
|
||||||
FROM nginx:18-alpine as prod
|
FROM nginx:stable-alpine AS prod
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
|
74
README.md
74
README.md
|
@ -1,22 +1,72 @@
|
||||||
# member-administration-ui
|
# member-administration-ui
|
||||||
|
|
||||||
Memberadministration
|
Mitgliederverwaltung für Feuerwehren und Vereine.
|
||||||
|
|
||||||
|
## Einleitung
|
||||||
|
|
||||||
|
Dieses Repository dient zur Verwaltung der Mitgliederdaten. Es ist ein Frontend-Client, der auf die Daten des [member-administration-server Backends](https://forgejo.jk-effects.cloud/Ehrenamt/member-administration-server) zugreift. Die Webapp bietet eine Möglichkeit Mitgliederdaten zu verwalten, Protokolle zu schreiben und Kaledereinträge zu erstellen. Benutzer können eingeladen und Rollen zugewiesen werden.
|
||||||
|
|
||||||
|
Eine Demo dieser Seite finden Sie unter [https://ff-admin-demo.jk-effects.cloud](https://ff-admin-demo.jk-effects.cloud).
|
||||||
|
|
||||||
|
Für die Verwendung muss ein TOTP-Code eingegeben werden.
|
||||||
|
|
||||||
|
Die Zugangsdaten (Lesebeschränkt) sind:\
|
||||||
|
EMAIL: demo-besucher\
|
||||||
|
TOTP: ![alt text](demo-totp-qrcode.png)\
|
||||||
|
TOTP-Code: FBMDAJKFOYQXM2DNH47GWWBGJ5KWOUCW
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### Requirements
|
### Docker Compose Setup
|
||||||
|
|
||||||
1. Access to the internet
|
Um den Container hochzufahren, erstellen Sie eine `docker-compose.yml` Datei mit folgendem Inhalt:
|
||||||
|
|
||||||
### Configuration
|
```yaml
|
||||||
|
version: "3"
|
||||||
|
|
||||||
1. Copy the .env.example file to .env and fill in the required information
|
services:
|
||||||
2. Install all packages via `npm install`
|
ff-member-administration-app:
|
||||||
3. Start the backend application
|
image: docker.registry.jk-effects.cloud/ehrenamt/member-administration/app:latest
|
||||||
4. Start the application
|
container_name: ff_member_administration_ui
|
||||||
5. Run `npm run dev` to run inside dev-environment
|
restart: unless-stopped
|
||||||
|
|
||||||
### Usage
|
#volumes:
|
||||||
|
# - <volume|local path>/favicon.png:/app/public/favicon.png
|
||||||
|
# - <volume|local path>/favicon.png:/app/public/FFW-Logo.svg
|
||||||
|
```
|
||||||
|
|
||||||
1. Open the browser and navigate to `http://localhost:5173` or the URL you specified in the server configuration
|
Führen Sie dann den folgenden Befehl im Verzeichnis der compose-Datei aus, um den Container zu starten:
|
||||||
2. Go to route `/setup` to create the first user (this path is disabled after the first user is created)
|
|
||||||
|
```sh
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manuelle Installation
|
||||||
|
|
||||||
|
Klonen Sie dieses Repository und installieren Sie die Abhängigkeiten:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://forgejo.jk-effects.cloud/Ehrenamt/member-administration-ui.git
|
||||||
|
cd member-administration-ui
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
npm run start
|
||||||
|
```
|
||||||
|
|
||||||
|
### Konfiguration
|
||||||
|
|
||||||
|
Ein eigenes favicon und Logo kann über ein volume ausgetauscht werden.
|
||||||
|
|
||||||
|
## Einrichtung
|
||||||
|
|
||||||
|
1. **Admin Benutzer erstellen**: Erstellen Sie einen Admin Benutzer unter dem Pfad [/setup](https://ff-admin-demo.jk-effects.cloud/setup), um auf die Migliederverwaltung 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.
|
||||||
|
|
||||||
|
3. **Nutzer einladen**: Unter `Benutzer > Benutzer` können weitere Nutzer eingeladen werden. Diese erhalten dann eine E-Mail mit einem Link, um ein TOTP zu erhalten.
|
||||||
|
|
||||||
|
## Fragen und Wünsche
|
||||||
|
|
||||||
|
Bei Fragen, Anregungen oder Wünschen können Sie sich gerne melden.\
|
||||||
|
Wir freuen uns über Ihr Feedback und helfen Ihnen gerne weiter.\
|
||||||
|
Schreiben Sie dafür eine Mail an julian.krauser@jk-effects.com.
|
||||||
|
|
BIN
demo-totp-qrcode.png
Normal file
BIN
demo-totp-qrcode.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
|
@ -1,22 +1,23 @@
|
||||||
<template>
|
<template>
|
||||||
<footer
|
<footer
|
||||||
v-if="authCheck && (routeName.includes('admin') || routeName.includes('account'))"
|
v-if="authCheck && (routeName.includes('admin-') || routeName.includes('account-'))"
|
||||||
class="md:hidden flex flex-row h-16 min-h-16 justify-center md:justify-normal p-1 bg-white"
|
class="md:hidden flex flex-row h-16 min-h-16 justify-center md:justify-normal p-1 bg-white"
|
||||||
>
|
>
|
||||||
<div class="w-full flex flex-row gap-2 h-full align-middle">
|
<div class="w-full flex flex-row gap-2 h-full align-middle">
|
||||||
<TopLevelLink
|
<TopLevelLink
|
||||||
v-if="routeName.includes('admin')"
|
v-if="routeName.includes('admin-')"
|
||||||
v-for="item in topLevel"
|
v-for="item in topLevel"
|
||||||
:key="item.key"
|
:key="item.key"
|
||||||
:link="item"
|
:link="item"
|
||||||
:disableSubLink="true"
|
:disableSubLink="true"
|
||||||
/>
|
/>
|
||||||
<TopLevelLink v-else :link="{ key: 'club', title: 'Zur Verwaltung' }" :disableSubLink="true" />
|
<TopLevelLink v-else :link="{ key: 'club', title: 'Zur Verwaltung', levelDefault: '' }" :disableSubLink="true" />
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
import { mapState } from "pinia";
|
import { mapState } from "pinia";
|
||||||
import { useAuthStore } from "@/stores/auth";
|
import { useAuthStore } from "@/stores/auth";
|
||||||
import { useNavigationStore } from "@/stores/admin/navigation";
|
import { useNavigationStore } from "@/stores/admin/navigation";
|
||||||
|
@ -24,7 +25,6 @@ import TopLevelLink from "./admin/TopLevelLink.vue";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from "vue";
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(useAuthStore, ["authCheck"]),
|
...mapState(useAuthStore, ["authCheck"]),
|
||||||
|
|
|
@ -6,10 +6,10 @@
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
<div class="flex flex-row gap-2 items-center">
|
<div class="flex flex-row gap-2 items-center">
|
||||||
<div v-if="authCheck" class="hidden md:flex flex-row gap-2 h-full align-middle">
|
<div v-if="authCheck" class="hidden md:flex flex-row gap-2 h-full align-middle">
|
||||||
<TopLevelLink v-if="routeName.includes('admin')" v-for="item in topLevel" :key="item.key" :link="item" />
|
<TopLevelLink v-if="routeName.includes('admin-')" v-for="item in topLevel" :key="item.key" :link="item" />
|
||||||
<TopLevelLink
|
<TopLevelLink
|
||||||
v-else-if="routeName.includes('account')"
|
v-else-if="routeName.includes('account-')"
|
||||||
:link="{ key: 'club', title: 'Zur Verwaltung' }"
|
:link="{ key: 'club', title: 'Zur Verwaltung', levelDefault: '' }"
|
||||||
:disable-sub-link="true"
|
:disable-sub-link="true"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -33,9 +33,9 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
copyToClipboard() {
|
copyToClipboard() {
|
||||||
navigator.clipboard.writeText(this.otp ?? "");
|
navigator.clipboard.writeText(this.copyText ?? "");
|
||||||
this.copySuccess = true;
|
this.copySuccess = true;
|
||||||
this.timputCopy = setTimeout(() => {
|
this.timeoutCopy = setTimeout(() => {
|
||||||
this.copySuccess = false;
|
this.copySuccess = false;
|
||||||
}, 2000);
|
}, 2000);
|
||||||
},
|
},
|
||||||
|
|
|
@ -95,7 +95,7 @@ import { mapState, mapActions } from "pinia";
|
||||||
import { EyeIcon, PencilIcon, PlusIcon, TrashIcon } from "@heroicons/vue/24/outline";
|
import { EyeIcon, PencilIcon, PlusIcon, TrashIcon } from "@heroicons/vue/24/outline";
|
||||||
import { useAbilityStore } from "@/stores/ability";
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
import isEqual from "lodash.isEqual";
|
import isEqual from "lodash.isequal";
|
||||||
import Spinner from "@/components/Spinner.vue";
|
import Spinner from "@/components/Spinner.vue";
|
||||||
import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
|
import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
|
||||||
import FailureXMark from "@/components/FailureXMark.vue";
|
import FailureXMark from "@/components/FailureXMark.vue";
|
||||||
|
|
|
@ -24,7 +24,7 @@ export default defineComponent({
|
||||||
default: "LINK",
|
default: "LINK",
|
||||||
},
|
},
|
||||||
link: {
|
link: {
|
||||||
type: [String, Object as PropType<{ name: string }>],
|
type: Object as PropType<string | { name: string }>,
|
||||||
default: "/",
|
default: "/",
|
||||||
},
|
},
|
||||||
active: {
|
active: {
|
||||||
|
|
|
@ -18,13 +18,13 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { defineComponent, type PropType } from "vue";
|
||||||
|
import { RouterLink } from "vue-router";
|
||||||
import { useNavigationStore, type topLevelNavigationModel } from "@/stores/admin/navigation";
|
import { useNavigationStore, type topLevelNavigationModel } from "@/stores/admin/navigation";
|
||||||
import { mapState } from "pinia";
|
import { mapState } from "pinia";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, type PropType } from "vue";
|
|
||||||
import { RouterLink } from "vue-router";
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
link: {
|
link: {
|
||||||
|
|
|
@ -94,7 +94,7 @@
|
||||||
id="startdate"
|
id="startdate"
|
||||||
required
|
required
|
||||||
:value="data.start"
|
:value="data.start"
|
||||||
@change="($event) => ($refs.enddate.max = ($event.target as HTMLInputElement).value)"
|
@change="($event) => (($refs.enddate as HTMLInputElement).max = ($event.target as HTMLInputElement).value)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
|
|
|
@ -89,7 +89,9 @@
|
||||||
@change="
|
@change="
|
||||||
($event) => {
|
($event) => {
|
||||||
calendar!.starttime = new Date(($event.target as HTMLInputElement).value).toISOString();
|
calendar!.starttime = new Date(($event.target as HTMLInputElement).value).toISOString();
|
||||||
$refs.endtime.min = formatForDateTimeLocalInput(($event.target as HTMLInputElement).value);
|
($refs.endtime as HTMLInputElement).min = formatForDateTimeLocalInput(
|
||||||
|
($event.target as HTMLInputElement).value
|
||||||
|
);
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
|
@ -122,7 +124,7 @@
|
||||||
@change="
|
@change="
|
||||||
($event) => {
|
($event) => {
|
||||||
calendar!.starttime = new Date(($event.target as HTMLInputElement).value).toISOString();
|
calendar!.starttime = new Date(($event.target as HTMLInputElement).value).toISOString();
|
||||||
$refs.enddate.min = formatForDateInput(($event.target as HTMLInputElement).value);
|
($refs.enddate as HTMLInputElement).min = formatForDateInput(($event.target as HTMLInputElement).value);
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
|
@ -194,7 +196,7 @@ import { CheckIcon, ChevronUpDownIcon, TrashIcon } from "@heroicons/vue/20/solid
|
||||||
import { useCalendarTypeStore } from "@/stores/admin/calendarType";
|
import { useCalendarTypeStore } from "@/stores/admin/calendarType";
|
||||||
import type { CalendarTypeViewModel } from "@/viewmodels/admin/calendarType.models";
|
import type { CalendarTypeViewModel } from "@/viewmodels/admin/calendarType.models";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
import isEqual from "lodash.isEqual";
|
import isEqual from "lodash.isequal";
|
||||||
import { useAbilityStore } from "@/stores/ability";
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ import type {
|
||||||
UpdateMemberAwardViewModel,
|
UpdateMemberAwardViewModel,
|
||||||
} from "@/viewmodels/admin/memberAward.models";
|
} from "@/viewmodels/admin/memberAward.models";
|
||||||
import { useMemberAwardStore } from "@/stores/admin/memberAward";
|
import { useMemberAwardStore } from "@/stores/admin/memberAward";
|
||||||
import isEqual from "lodash.isEqual";
|
import isEqual from "lodash.isequal";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ import type {
|
||||||
CommunicationViewModel,
|
CommunicationViewModel,
|
||||||
UpdateCommunicationViewModel,
|
UpdateCommunicationViewModel,
|
||||||
} from "@/viewmodels/admin/communication.models";
|
} from "@/viewmodels/admin/communication.models";
|
||||||
import isEqual from "lodash.isEqual";
|
import isEqual from "lodash.isequal";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -111,7 +111,7 @@ import type {
|
||||||
UpdateMemberExecutivePositionViewModel,
|
UpdateMemberExecutivePositionViewModel,
|
||||||
} from "@/viewmodels/admin/memberExecutivePosition.models";
|
} from "@/viewmodels/admin/memberExecutivePosition.models";
|
||||||
import { useMemberExecutivePositionStore } from "@/stores/admin/memberExecutivePosition";
|
import { useMemberExecutivePositionStore } from "@/stores/admin/memberExecutivePosition";
|
||||||
import isEqual from "lodash.isEqual";
|
import isEqual from "lodash.isequal";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -112,7 +112,7 @@ import type {
|
||||||
UpdateMemberQualificationViewModel,
|
UpdateMemberQualificationViewModel,
|
||||||
} from "@/viewmodels/admin/memberQualification.models";
|
} from "@/viewmodels/admin/memberQualification.models";
|
||||||
import { useMemberQualificationStore } from "@/stores/admin/memberQualification";
|
import { useMemberQualificationStore } from "@/stores/admin/memberQualification";
|
||||||
import isEqual from "lodash.isEqual";
|
import isEqual from "lodash.isequal";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -112,7 +112,7 @@ import type {
|
||||||
UpdateMembershipViewModel,
|
UpdateMembershipViewModel,
|
||||||
} from "@/viewmodels/admin/membership.models";
|
} from "@/viewmodels/admin/membership.models";
|
||||||
import { useMembershipStore } from "@/stores/admin/membership";
|
import { useMembershipStore } from "@/stores/admin/membership";
|
||||||
import isEqual from "lodash.isEqual";
|
import isEqual from "lodash.isequal";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,11 @@
|
||||||
|
|
||||||
<div class="flex flex-row justify-end">
|
<div class="flex flex-row justify-end">
|
||||||
<div class="flex flex-row gap-4 py-2">
|
<div class="flex flex-row gap-4 py-2">
|
||||||
<button primary-outline @click="closeModal" :disabled="status != null && status?.status != 'failed'">
|
<button
|
||||||
|
primary-outline
|
||||||
|
@click="closeModal"
|
||||||
|
:disabled="status != null && status != 'loading' && status?.status != 'failed'"
|
||||||
|
>
|
||||||
abbrechen
|
abbrechen
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -47,7 +51,6 @@ import { useCalendarTypeStore } from "@/stores/admin/calendarType";
|
||||||
import type { CreateCalendarTypeViewModel } from "@/viewmodels/admin/calendarType.models";
|
import type { CreateCalendarTypeViewModel } from "@/viewmodels/admin/calendarType.models";
|
||||||
import { Listbox, ListboxButton, ListboxOptions, ListboxOption, ListboxLabel } from "@headlessui/vue";
|
import { Listbox, ListboxButton, ListboxOptions, ListboxOption, ListboxLabel } from "@headlessui/vue";
|
||||||
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
||||||
import type { CalendarFieldType } from "@/types/fieldTypes";
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
@ -56,7 +59,6 @@ export default defineComponent({
|
||||||
return {
|
return {
|
||||||
status: null as null | "loading" | { status: "success" | "failed"; reason?: string },
|
status: null as null | "loading" | { status: "success" | "failed"; reason?: string },
|
||||||
timeout: undefined as any,
|
timeout: undefined as any,
|
||||||
selectedFields: [] as Array<CalendarFieldType>,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent, type PropType } from "vue";
|
import { defineComponent, type PropType } from "vue";
|
||||||
import { mapState, mapActions } from "pinia";
|
import { mapState, mapActions } from "pinia";
|
||||||
import type { InviteUserModal } from "@/viewmodels/admin/invite.models";
|
import type { InviteViewModel } from "@/viewmodels/admin/invite.models";
|
||||||
import { PencilIcon, UserGroupIcon, WrenchScrewdriverIcon, TrashIcon } from "@heroicons/vue/24/outline";
|
import { PencilIcon, UserGroupIcon, WrenchScrewdriverIcon, TrashIcon } from "@heroicons/vue/24/outline";
|
||||||
import { useAbilityStore } from "@/stores/ability";
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
import { useInviteStore } from "@/stores/admin/invite";
|
import { useInviteStore } from "@/stores/admin/invite";
|
||||||
|
@ -33,7 +33,7 @@ import { useInviteStore } from "@/stores/admin/invite";
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
invite: { type: Object as PropType<InviteUserModal>, default: {} },
|
invite: { type: Object as PropType<InviteViewModel>, default: {} },
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(useAbilityStore, ["can"]),
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
|
|
@ -57,7 +57,7 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*:not([headlessui]):not([id*="headlessui"]):not([class*="headlessui"])*/
|
/*:not([headlessui]):not([id*="headlessui"]):not([class*="headlessui"])*/
|
||||||
button:not([class*="ql"] *):not([class*="fc"]),
|
button:not([class*="ql"] *):not([class*="fc"]):not([id*="headlessui-combobox"]),
|
||||||
a[button] {
|
a[button] {
|
||||||
@apply relative box-border h-10 w-full flex justify-center py-2 px-4 text-sm font-medium rounded-md focus:outline-none focus:ring-0;
|
@apply relative box-border h-10 w-full flex justify-center py-2 px-4 text-sm font-medium rounded-md focus:outline-none focus:ring-0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import router from "./router";
|
||||||
let devMode = process.env.NODE_ENV === "development";
|
let devMode = process.env.NODE_ENV === "development";
|
||||||
|
|
||||||
const http = axios.create({
|
const http = axios.create({
|
||||||
baseURL: devMode ? "http://localhost:5000" : process.env.SERVER_ADDRESS,
|
baseURL: (devMode ? "http://localhost:5000" : "") + "/api",
|
||||||
headers: {
|
headers: {
|
||||||
"Cache-Control": "no-cache",
|
"Cache-Control": "no-cache",
|
||||||
Pragma: "no-cache",
|
Pragma: "no-cache",
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { http } from "@/serverCom";
|
||||||
import type { AxiosResponse } from "axios";
|
import type { AxiosResponse } from "axios";
|
||||||
import type { ProtocolViewModel } from "@/viewmodels/admin/protocol.models";
|
import type { ProtocolViewModel } from "@/viewmodels/admin/protocol.models";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
import isEqual from "lodash.isEqual";
|
import isEqual from "lodash.isequal";
|
||||||
import difference from "lodash.difference";
|
import difference from "lodash.difference";
|
||||||
|
|
||||||
export const useProtocolStore = defineStore("protocol", {
|
export const useProtocolStore = defineStore("protocol", {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import type {
|
||||||
} from "../../viewmodels/admin/protocolAgenda.models";
|
} from "../../viewmodels/admin/protocolAgenda.models";
|
||||||
import { useProtocolStore } from "./protocol";
|
import { useProtocolStore } from "./protocol";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
import isEqual from "lodash.isEqual";
|
import isEqual from "lodash.isequal";
|
||||||
import differenceWith from "lodash.differencewith";
|
import differenceWith from "lodash.differencewith";
|
||||||
|
|
||||||
export const useProtocolAgendaStore = defineStore("protocolAgenda", {
|
export const useProtocolAgendaStore = defineStore("protocolAgenda", {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import type {
|
||||||
} from "../../viewmodels/admin/protocolDecision.models";
|
} from "../../viewmodels/admin/protocolDecision.models";
|
||||||
import { useProtocolStore } from "./protocol";
|
import { useProtocolStore } from "./protocol";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
import isEqual from "lodash.isEqual";
|
import isEqual from "lodash.isequal";
|
||||||
import differenceWith from "lodash.differencewith";
|
import differenceWith from "lodash.differencewith";
|
||||||
|
|
||||||
export const useProtocolDecisionStore = defineStore("protocolDecision", {
|
export const useProtocolDecisionStore = defineStore("protocolDecision", {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import type {
|
||||||
} from "../../viewmodels/admin/protocolPresence.models";
|
} from "../../viewmodels/admin/protocolPresence.models";
|
||||||
import { useProtocolStore } from "./protocol";
|
import { useProtocolStore } from "./protocol";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
import isEqual from "lodash.isEqual";
|
import isEqual from "lodash.isequal";
|
||||||
|
|
||||||
export const useProtocolPresenceStore = defineStore("protocolPresence", {
|
export const useProtocolPresenceStore = defineStore("protocolPresence", {
|
||||||
state: () => {
|
state: () => {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import type {
|
||||||
} from "../../viewmodels/admin/protocolVoting.models";
|
} from "../../viewmodels/admin/protocolVoting.models";
|
||||||
import { useProtocolStore } from "./protocol";
|
import { useProtocolStore } from "./protocol";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
import isEqual from "lodash.isEqual";
|
import isEqual from "lodash.isequal";
|
||||||
import differenceWith from "lodash.differencewith";
|
import differenceWith from "lodash.differencewith";
|
||||||
|
|
||||||
export const useProtocolVotingStore = defineStore("protocolVoting", {
|
export const useProtocolVotingStore = defineStore("protocolVoting", {
|
||||||
|
|
|
@ -51,7 +51,7 @@
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<span class="block truncate" :class="{ 'font-medium': selected, 'font-normal': !selected }">
|
<span class="block truncate" :class="{ 'font-medium': selected, 'font-normal': !selected }">
|
||||||
{{ user.firstname }} {{ user.lastname }} {{ user.nameaffix }}
|
{{ user.firstname }} {{ user.lastname }}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
v-if="selected"
|
v-if="selected"
|
||||||
|
@ -124,15 +124,6 @@ export default defineComponent({
|
||||||
.includes(this.query.toLowerCase().replace(/\s+/g, ""))
|
.includes(this.query.toLowerCase().replace(/\s+/g, ""))
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
sorted(): Array<MemberViewModel> {
|
|
||||||
return this.selected.sort((a, b) => {
|
|
||||||
if (a.lastname < b.lastname) return -1;
|
|
||||||
if (a.lastname > b.lastname) return 1;
|
|
||||||
if (a.firstname < b.firstname) return -1;
|
|
||||||
if (a.firstname > b.firstname) return 1;
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.fetchUsers();
|
this.fetchUsers();
|
||||||
|
|
|
@ -48,12 +48,13 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent, markRaw, defineAsyncComponent } from "vue";
|
import { defineComponent, markRaw, defineAsyncComponent } from "vue";
|
||||||
import { mapActions, mapState } from "pinia";
|
import { mapActions, mapState } from "pinia";
|
||||||
|
import type { UserViewModel } from "@/viewmodels/admin/user.models";
|
||||||
import MainTemplate from "@/templates/Main.vue";
|
import MainTemplate from "@/templates/Main.vue";
|
||||||
import Spinner from "@/components/Spinner.vue";
|
import Spinner from "@/components/Spinner.vue";
|
||||||
import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
|
import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
|
||||||
import FailureXMark from "@/components/FailureXMark.vue";
|
import FailureXMark from "@/components/FailureXMark.vue";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
import isEqual from "lodash.isEqual";
|
import isEqual from "lodash.isequal";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
|
@ -96,7 +96,7 @@ import FailureXMark from "@/components/FailureXMark.vue";
|
||||||
import { Listbox, ListboxButton, ListboxOptions, ListboxOption, ListboxLabel } from "@headlessui/vue";
|
import { Listbox, ListboxButton, ListboxOptions, ListboxOption, ListboxLabel } from "@headlessui/vue";
|
||||||
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
import isEqual from "lodash.isEqual";
|
import isEqual from "lodash.isequal";
|
||||||
import { Salutation } from "@/enums/salutation";
|
import { Salutation } from "@/enums/salutation";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ import FailureXMark from "@/components/FailureXMark.vue";
|
||||||
import { RouterLink } from "vue-router";
|
import { RouterLink } from "vue-router";
|
||||||
import type { AwardViewModel, UpdateAwardViewModel } from "@/viewmodels/admin/award.models";
|
import type { AwardViewModel, UpdateAwardViewModel } from "@/viewmodels/admin/award.models";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
import isEqual from "lodash.isEqual";
|
import isEqual from "lodash.isequal";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
|
@ -57,7 +57,7 @@ import { RouterLink } from "vue-router";
|
||||||
import type { CalendarTypeViewModel, UpdateCalendarTypeViewModel } from "@/viewmodels/admin/calendarType.models";
|
import type { CalendarTypeViewModel, UpdateCalendarTypeViewModel } from "@/viewmodels/admin/calendarType.models";
|
||||||
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
import isEqual from "lodash.isEqual";
|
import isEqual from "lodash.isequal";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
|
@ -15,12 +15,7 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-row gap-4">
|
<div class="flex flex-row gap-4">
|
||||||
<button
|
<button v-if="can('create', 'settings', 'communication')" primary class="!w-fit" @click="openCreateModal">
|
||||||
v-if="can('create', 'settings', 'communication_type')"
|
|
||||||
primary
|
|
||||||
class="!w-fit"
|
|
||||||
@click="openCreateModal"
|
|
||||||
>
|
|
||||||
Kommunikationsart erstellen
|
Kommunikationsart erstellen
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -97,7 +97,7 @@ import type {
|
||||||
import { Listbox, ListboxButton, ListboxOptions, ListboxOption, ListboxLabel } from "@headlessui/vue";
|
import { Listbox, ListboxButton, ListboxOptions, ListboxOption, ListboxLabel } from "@headlessui/vue";
|
||||||
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
import isEqual from "lodash.isEqual";
|
import isEqual from "lodash.isequal";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
|
@ -50,7 +50,7 @@ import type {
|
||||||
UpdateExecutivePositionViewModel,
|
UpdateExecutivePositionViewModel,
|
||||||
} from "@/viewmodels/admin/executivePosition.models";
|
} from "@/viewmodels/admin/executivePosition.models";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
import isEqual from "lodash.isEqual";
|
import isEqual from "lodash.isequal";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
|
@ -50,7 +50,7 @@ import type {
|
||||||
MembershipStatusViewModel,
|
MembershipStatusViewModel,
|
||||||
} from "@/viewmodels/admin/membershipStatus.models";
|
} from "@/viewmodels/admin/membershipStatus.models";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
import isEqual from "lodash.isEqual";
|
import isEqual from "lodash.isequal";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
|
@ -51,7 +51,7 @@ import FailureXMark from "@/components/FailureXMark.vue";
|
||||||
import { RouterLink } from "vue-router";
|
import { RouterLink } from "vue-router";
|
||||||
import type { UpdateQualificationViewModel, QualificationViewModel } from "@/viewmodels/admin/qualification.models";
|
import type { UpdateQualificationViewModel, QualificationViewModel } from "@/viewmodels/admin/qualification.models";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
import isEqual from "lodash.isEqual";
|
import isEqual from "lodash.isequal";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
|
@ -46,7 +46,7 @@ import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
|
||||||
import FailureXMark from "@/components/FailureXMark.vue";
|
import FailureXMark from "@/components/FailureXMark.vue";
|
||||||
import { RouterLink } from "vue-router";
|
import { RouterLink } from "vue-router";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
import isEqual from "lodash.isEqual";
|
import isEqual from "lodash.isequal";
|
||||||
import type { RoleViewModel } from "@/viewmodels/admin/role.models";
|
import type { RoleViewModel } from "@/viewmodels/admin/role.models";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ import { RouterLink } from "vue-router";
|
||||||
import { useUserStore } from "@/stores/admin/user";
|
import { useUserStore } from "@/stores/admin/user";
|
||||||
import type { UpdateUserViewModel, UserViewModel } from "@/viewmodels/admin/user.models";
|
import type { UpdateUserViewModel, UserViewModel } from "@/viewmodels/admin/user.models";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
import isEqual from "lodash.isEqual";
|
import isEqual from "lodash.isequal";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
|
@ -66,7 +66,7 @@ import FailureXMark from "@/components/FailureXMark.vue";
|
||||||
import { XMarkIcon, PlusIcon } from "@heroicons/vue/24/outline";
|
import { XMarkIcon, PlusIcon } from "@heroicons/vue/24/outline";
|
||||||
import type { UserViewModel } from "@/viewmodels/admin/user.models";
|
import type { UserViewModel } from "@/viewmodels/admin/user.models";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
import isEqual from "lodash.isEqual";
|
import isEqual from "lodash.isequal";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
Loading…
Reference in a new issue