Setup wizard for Settings
This commit is contained in:
parent
5d9007f517
commit
8880af2880
11 changed files with 622 additions and 60 deletions
48
src/components/CheckProgressBar.vue
Normal file
48
src/components/CheckProgressBar.vue
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
<template>
|
||||||
|
<div class="w-full flex flex-row items-center">
|
||||||
|
<div class="contents" v-for="(i, index) in total" :key="index">
|
||||||
|
<SuccessCheckmark v-if="index <= successfull && index != step" class="h-8!" />
|
||||||
|
<div
|
||||||
|
v-else-if="index <= step"
|
||||||
|
class="flex items-center justify-center h-8 w-8 border-4 border-success rounded-full"
|
||||||
|
>
|
||||||
|
<div class="h-2 w-2 border-4 border-success bg-success rounded-full"></div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="h-8 w-8 border-4 border-gray-400 rounded-full"></div>
|
||||||
|
<div v-if="i != total" class="grow border-2" :class="index < step ? ' border-success' : 'border-gray-400'"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import SuccessCheckmark from "./SuccessCheckmark.vue";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
step: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
validator(value: number) {
|
||||||
|
return value >= 0;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
successfull: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
validator(value: number) {
|
||||||
|
return value >= 0;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
total: {
|
||||||
|
type: Number,
|
||||||
|
default: 1,
|
||||||
|
validator(value: number) {
|
||||||
|
return value >= 1;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -1,10 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col text-gray-400 text-sm mt-4 items-center">
|
<div class="flex flex-col text-gray-400 text-sm mt-4 items-center">
|
||||||
<div class="flex flex-row gap-2 justify-center">
|
<p v-if="appCustom_login_message">{{ appCustom_login_message }}</p>
|
||||||
|
<div class="flex flex-row gap-2 justify-center mb-3">
|
||||||
|
<a v-if="clubWebsite" :href="clubWebsite" target="_blank">Webseite</a>
|
||||||
<a v-if="clubImprint" :href="clubImprint" target="_blank">Datenschutz</a>
|
<a v-if="clubImprint" :href="clubImprint" target="_blank">Datenschutz</a>
|
||||||
<a v-if="clubPrivacy" :href="clubPrivacy" target="_blank">Impressum</a>
|
<a v-if="clubPrivacy" :href="clubPrivacy" target="_blank">Impressum</a>
|
||||||
</div>
|
</div>
|
||||||
<p v-if="appCustom_login_message">{{ appCustom_login_message }}</p>
|
|
||||||
<p>
|
<p>
|
||||||
<a href="https://ff-admin.de/admin" target="_blank">FF Admin</a>
|
<a href="https://ff-admin.de/admin" target="_blank">FF Admin</a>
|
||||||
entwickelt von
|
entwickelt von
|
||||||
|
|
70
src/components/setup/Account.vue
Normal file
70
src/components/setup/Account.vue
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
<template>
|
||||||
|
<form class="flex flex-col gap-2" @submit.prevent="setup">
|
||||||
|
<p class="text-center">Admin Account</p>
|
||||||
|
<div class="-space-y-px">
|
||||||
|
<div>
|
||||||
|
<input id="username" name="username" type="text" required placeholder="Benutzer" class="rounded-b-none!" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input id="mail" name="mail" type="email" required placeholder="Mailadresse" class="rounded-none!" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input id="firstname" name="firstname" type="text" required placeholder="Vorname" class="rounded-none!" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input id="lastname" name="lastname" type="text" required placeholder="Nachname" class="rounded-t-none!" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-row gap-2">
|
||||||
|
<button type="submit" primary :disabled="setupStatus == 'loading' || setupStatus == 'success'">
|
||||||
|
Admin-Account anlegen
|
||||||
|
</button>
|
||||||
|
<Spinner v-if="setupStatus == 'loading'" class="my-auto" />
|
||||||
|
<SuccessCheckmark v-else-if="setupStatus == 'success'" />
|
||||||
|
<FailureXMark v-else-if="setupStatus == 'failed'" />
|
||||||
|
</div>
|
||||||
|
<p v-if="setupMessage" class="text-center">{{ setupMessage }}</p>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import Spinner from "@/components/Spinner.vue";
|
||||||
|
import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
|
||||||
|
import FailureXMark from "@/components/FailureXMark.vue";
|
||||||
|
import { mapActions } from "pinia";
|
||||||
|
import { useSetupStore } from "../../stores/setup";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
setupStatus: undefined as undefined | "loading" | "success" | "failed",
|
||||||
|
setupMessage: "" as string,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useSetupStore, ["createAdmin"]),
|
||||||
|
setup(e: any) {
|
||||||
|
let formData = e.target.elements;
|
||||||
|
this.setupStatus = "loading";
|
||||||
|
this.setupMessage = "";
|
||||||
|
this.createAdmin({
|
||||||
|
username: formData.username.value,
|
||||||
|
mail: formData.mail.value,
|
||||||
|
firstname: formData.firstname.value,
|
||||||
|
lastname: formData.lastname.value,
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
// this.setupStatus = "success";
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.setupStatus = "failed";
|
||||||
|
this.setupMessage = err.response.data;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
66
src/components/setup/App.vue
Normal file
66
src/components/setup/App.vue
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<template>
|
||||||
|
<form class="flex flex-col gap-2" @submit.prevent="setup">
|
||||||
|
<p class="text-center">App Konfiguration</p>
|
||||||
|
<div class="-space-y-px">
|
||||||
|
<div>
|
||||||
|
<input id="login_message" name="login_message" type="text" placeholder="Nachricht unter Login (optional)" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-row items-center gap-2 pt-1">
|
||||||
|
<input type="checkbox" id="show_cal_link" checked />
|
||||||
|
<label for="show_cal_link">Link zum Kalender anzeigen (optional)</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="text-primary cursor-pointer ml-auto" @click="skip('app')">überspringen</p>
|
||||||
|
|
||||||
|
<div class="flex flex-row gap-2">
|
||||||
|
<button type="submit" primary :disabled="setupStatus == 'loading' || setupStatus == 'success'">
|
||||||
|
Anwendungsdaten speichern
|
||||||
|
</button>
|
||||||
|
<Spinner v-if="setupStatus == 'loading'" class="my-auto" />
|
||||||
|
<SuccessCheckmark v-else-if="setupStatus == 'success'" />
|
||||||
|
<FailureXMark v-else-if="setupStatus == 'failed'" />
|
||||||
|
</div>
|
||||||
|
<p v-if="setupMessage" class="text-center">{{ setupMessage }}</p>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import Spinner from "@/components/Spinner.vue";
|
||||||
|
import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
|
||||||
|
import FailureXMark from "@/components/FailureXMark.vue";
|
||||||
|
import { mapActions } from "pinia";
|
||||||
|
import { useSetupStore } from "../../stores/setup";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
setupStatus: undefined as undefined | "loading" | "success" | "failed",
|
||||||
|
setupMessage: "" as string,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useSetupStore, ["setApp", "skip"]),
|
||||||
|
setup(e: any) {
|
||||||
|
let formData = e.target.elements;
|
||||||
|
this.setupStatus = "loading";
|
||||||
|
this.setupMessage = "";
|
||||||
|
this.setApp({
|
||||||
|
login_message: formData.login_message.value,
|
||||||
|
show_cal_link: formData.show_cal_link.checked,
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
// this.setupStatus = "success";
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.setupStatus = "failed";
|
||||||
|
this.setupMessage = err.response.data;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
99
src/components/setup/Club.vue
Normal file
99
src/components/setup/Club.vue
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
<template>
|
||||||
|
<form class="flex flex-col gap-2" @submit.prevent="setup">
|
||||||
|
<p class="text-center">Feuerwehr-/Vereinsdaten</p>
|
||||||
|
<div class="-space-y-px">
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
id="name"
|
||||||
|
name="name"
|
||||||
|
type="text"
|
||||||
|
placeholder="Feuerwehr-/Vereinsname (optional)"
|
||||||
|
class="rounded-b-none!"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
id="imprint"
|
||||||
|
name="imprint"
|
||||||
|
type="url"
|
||||||
|
placeholder="Link zum Impressum (optional)"
|
||||||
|
class="rounded-none!"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
id="privacy"
|
||||||
|
name="privacy"
|
||||||
|
type="url"
|
||||||
|
placeholder="Link zur Datenschutzerklärung (optional)"
|
||||||
|
class="rounded-none!"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
id="website"
|
||||||
|
name="website"
|
||||||
|
type="url"
|
||||||
|
placeholder="Link zur Webseite (optional)"
|
||||||
|
class="rounded-t-none!"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="text-primary cursor-pointer ml-auto" @click="skip('club')">überspringen</p>
|
||||||
|
|
||||||
|
<div class="flex flex-row gap-2">
|
||||||
|
<button type="submit" primary :disabled="setupStatus == 'loading' || setupStatus == 'success'">
|
||||||
|
Vereinsdaten speichern
|
||||||
|
</button>
|
||||||
|
<Spinner v-if="setupStatus == 'loading'" class="my-auto" />
|
||||||
|
<SuccessCheckmark v-else-if="setupStatus == 'success'" />
|
||||||
|
<FailureXMark v-else-if="setupStatus == 'failed'" />
|
||||||
|
</div>
|
||||||
|
<p v-if="setupMessage" class="text-center">{{ setupMessage }}</p>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import Spinner from "@/components/Spinner.vue";
|
||||||
|
import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
|
||||||
|
import FailureXMark from "@/components/FailureXMark.vue";
|
||||||
|
import { mapActions } from "pinia";
|
||||||
|
import { useSetupStore } from "../../stores/setup";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
setupStatus: undefined as undefined | "loading" | "success" | "failed",
|
||||||
|
setupMessage: "" as string,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useSetupStore, ["setClub", "skip"]),
|
||||||
|
setup(e: any) {
|
||||||
|
let formData = e.target.elements;
|
||||||
|
this.setupStatus = "loading";
|
||||||
|
this.setupMessage = "";
|
||||||
|
this.setClub({
|
||||||
|
name: formData.name.value,
|
||||||
|
imprint: formData.imprint.value,
|
||||||
|
privacy: formData.privacy.value,
|
||||||
|
website: formData.website.value,
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
// this.setupStatus = "success";
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.setupStatus = "failed";
|
||||||
|
this.setupMessage = err.response.data;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
3
src/components/setup/Finished.vue
Normal file
3
src/components/setup/Finished.vue
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<template>
|
||||||
|
<p class="text-center">Sie haben einen Verifizierungslink per Mail erhalten.</p>
|
||||||
|
</template>
|
87
src/components/setup/Images.vue
Normal file
87
src/components/setup/Images.vue
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
<template>
|
||||||
|
<form class="flex flex-col gap-2" @submit.prevent="setup">
|
||||||
|
<p class="text-center">Feuerwehr-/Vereins-Auftritt</p>
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<p>quadratisches Icon für App (optional)</p>
|
||||||
|
<img ref="icon_img" class="hidden w-full h-20 object-contain" />
|
||||||
|
<input class="hidden!" type="file" ref="icon" accept="image/*" @change="previewImage('icon')" />
|
||||||
|
<button type="button" primary-outline @click="($refs.icon as HTMLInputElement).click()">Icon auswählen</button>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<p>Logo (optional)</p>
|
||||||
|
<img ref="logo_img" class="hidden w-full h-20 object-contain" />
|
||||||
|
<input class="hidden!" type="file" ref="logo" accept="image/*" @change="previewImage('logo')" />
|
||||||
|
<button type="button" primary-outline @click="($refs.logo as HTMLInputElement).click()">Logo auswählen</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<p class="text-primary cursor-pointer ml-auto" @click="skip('appImages')">überspringen</p>
|
||||||
|
|
||||||
|
<div class="flex flex-row gap-2">
|
||||||
|
<button type="submit" primary :disabled="setupStatus == 'loading' || setupStatus == 'success'">
|
||||||
|
Bilder speichern
|
||||||
|
</button>
|
||||||
|
<Spinner v-if="setupStatus == 'loading'" class="my-auto" />
|
||||||
|
<SuccessCheckmark v-else-if="setupStatus == 'success'" />
|
||||||
|
<FailureXMark v-else-if="setupStatus == 'failed'" />
|
||||||
|
</div>
|
||||||
|
<p v-if="setupMessage" class="text-center">{{ setupMessage }}</p>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import Spinner from "@/components/Spinner.vue";
|
||||||
|
import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
|
||||||
|
import FailureXMark from "@/components/FailureXMark.vue";
|
||||||
|
import { mapActions } from "pinia";
|
||||||
|
import { useSetupStore } from "../../stores/setup";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
setupStatus: undefined as undefined | "loading" | "success" | "failed",
|
||||||
|
setupMessage: "" as string,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useSetupStore, ["setClubImages", "skip"]),
|
||||||
|
previewImage(inputname: "icon" | "logo") {
|
||||||
|
let input = this.$refs[inputname] as HTMLInputElement;
|
||||||
|
let previewElement = this.$refs[inputname + "_img"] as HTMLImageElement;
|
||||||
|
if (input.files && input.files[0]) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
|
||||||
|
reader.onload = function (e) {
|
||||||
|
previewElement.src = e.target?.result as string;
|
||||||
|
previewElement.style.display = "block";
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.readAsDataURL(input.files[0]);
|
||||||
|
} else {
|
||||||
|
previewElement.src = "";
|
||||||
|
previewElement.style.display = "none";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup(e: any) {
|
||||||
|
this.setupStatus = "loading";
|
||||||
|
this.setupMessage = "";
|
||||||
|
this.setClubImages({
|
||||||
|
icon: (this.$refs.icon as HTMLInputElement).files?.[0],
|
||||||
|
logo: (this.$refs.logo as HTMLInputElement).files?.[0],
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
// this.setupStatus = "success";
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.setupStatus = "failed";
|
||||||
|
this.setupMessage = err.response.data;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
95
src/components/setup/Mail.vue
Normal file
95
src/components/setup/Mail.vue
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
<template>
|
||||||
|
<form class="flex flex-col gap-2" @submit.prevent="setup">
|
||||||
|
<p class="text-center">Mailversand</p>
|
||||||
|
<div class="-space-y-px">
|
||||||
|
<div class="mb-2">
|
||||||
|
<input id="mail" name="mail" type="email" placeholder="Mailadresse" required autocomplete="email" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
id="user"
|
||||||
|
name="user"
|
||||||
|
type="text"
|
||||||
|
placeholder="Benutzername (kann auch Mail sein)"
|
||||||
|
required
|
||||||
|
autocomplete="username"
|
||||||
|
class="rounded-b-none!"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
type="password"
|
||||||
|
placeholder="Passwort"
|
||||||
|
required
|
||||||
|
autocomplete="new-password"
|
||||||
|
class="rounded-none!"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input id="host" name="host" type="text" placeholder="Server-Host" required class="rounded-none!" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input id="port" name="port" type="number" placeholder="Port (25, 465, 587)" required class="rounded-t-none!" />
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row items-center gap-2 pt-1">
|
||||||
|
<input type="checkbox" id="secure" />
|
||||||
|
<label for="secure">SSL-Verbindung (setzen bei Port 465)</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-row gap-2">
|
||||||
|
<button type="submit" primary :disabled="setupStatus == 'loading' || setupStatus == 'success'">
|
||||||
|
Mailversand speichern
|
||||||
|
</button>
|
||||||
|
<Spinner v-if="setupStatus == 'loading'" class="my-auto" />
|
||||||
|
<SuccessCheckmark v-else-if="setupStatus == 'success'" />
|
||||||
|
<FailureXMark v-else-if="setupStatus == 'failed'" />
|
||||||
|
</div>
|
||||||
|
<p v-if="setupMessage" class="text-center">{{ setupMessage }}</p>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import Spinner from "@/components/Spinner.vue";
|
||||||
|
import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
|
||||||
|
import FailureXMark from "@/components/FailureXMark.vue";
|
||||||
|
import { mapActions } from "pinia";
|
||||||
|
import { useSetupStore } from "../../stores/setup";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
setupStatus: undefined as undefined | "loading" | "success" | "failed",
|
||||||
|
setupMessage: "" as string,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useSetupStore, ["setMailConfig", "skip"]),
|
||||||
|
setup(e: any) {
|
||||||
|
let formData = e.target.elements;
|
||||||
|
this.setupStatus = "loading";
|
||||||
|
this.setupMessage = "";
|
||||||
|
this.setMailConfig({
|
||||||
|
host: formData.host.value,
|
||||||
|
port: formData.port.value,
|
||||||
|
secure: formData.secure.checked,
|
||||||
|
mail: formData.mail.value,
|
||||||
|
username: formData.user.value,
|
||||||
|
password: formData.password.value,
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
// this.setupStatus = "success";
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.setupStatus = "failed";
|
||||||
|
this.setupMessage = err.response.data;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -18,7 +18,7 @@
|
||||||
--error: #9a0d55;
|
--error: #9a0d55;
|
||||||
--warning: #bb6210;
|
--warning: #bb6210;
|
||||||
--info: #388994;
|
--info: #388994;
|
||||||
--success: #73ad0f;
|
--success: #7ac142;
|
||||||
}
|
}
|
||||||
.dark {
|
.dark {
|
||||||
--primary: #ff0d00;
|
--primary: #ff0d00;
|
||||||
|
@ -27,7 +27,7 @@
|
||||||
--error: #9a0d55;
|
--error: #9a0d55;
|
||||||
--warning: #bb6210;
|
--warning: #bb6210;
|
||||||
--info: #4ccbda;
|
--info: #4ccbda;
|
||||||
--success: #73ad0f;
|
--success: #7ac142;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
130
src/stores/setup.ts
Normal file
130
src/stores/setup.ts
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { http } from "../serverCom";
|
||||||
|
import type { AxiosResponse } from "axios";
|
||||||
|
import { useConfigurationStore } from "./configuration";
|
||||||
|
|
||||||
|
export const useSetupStore = defineStore("setup", {
|
||||||
|
state: () => {
|
||||||
|
return {
|
||||||
|
dictionary: ["club", "clubImages", "app", "mail", "account", "finished"],
|
||||||
|
step: 0 as number,
|
||||||
|
successfull: 0 as number,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getters: {
|
||||||
|
stepIndex: (state) => (dict: string) => state.dictionary.findIndex((d) => d == dict),
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
skip(dict: string) {
|
||||||
|
let myIndex = this.stepIndex(dict);
|
||||||
|
this.step += 1;
|
||||||
|
if (this.successfull <= myIndex) {
|
||||||
|
this.successfull = myIndex + 1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async setClub(data: {
|
||||||
|
name?: string;
|
||||||
|
imprint?: string;
|
||||||
|
privacy?: string;
|
||||||
|
website?: string;
|
||||||
|
}): Promise<AxiosResponse<any, any>> {
|
||||||
|
let configStore = useConfigurationStore();
|
||||||
|
|
||||||
|
let myIndex = this.stepIndex("club");
|
||||||
|
const res = await http.post(`/setup/club`, {
|
||||||
|
name: data.name,
|
||||||
|
imprint: data.imprint,
|
||||||
|
privacy: data.privacy,
|
||||||
|
website: data.website,
|
||||||
|
});
|
||||||
|
configStore.configure();
|
||||||
|
|
||||||
|
this.step += 1;
|
||||||
|
if (this.successfull <= myIndex) {
|
||||||
|
this.successfull = myIndex;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
async setClubImages(data: { icon?: File; logo?: File }): Promise<AxiosResponse<any, any>> {
|
||||||
|
let configStore = useConfigurationStore();
|
||||||
|
|
||||||
|
let myIndex = this.stepIndex("clubImages");
|
||||||
|
const formData = new FormData();
|
||||||
|
|
||||||
|
if (data.icon) {
|
||||||
|
formData.append("icon", data.icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.logo) {
|
||||||
|
formData.append("logo", data.logo);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await http.post(`/setup/club/images`, formData, {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "multipart/form-data",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
configStore.configure();
|
||||||
|
|
||||||
|
this.step += 1;
|
||||||
|
if (this.successfull <= myIndex) {
|
||||||
|
this.successfull = myIndex;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
async setApp(data: { login_message: string; show_cal_link: boolean }): Promise<AxiosResponse<any, any>> {
|
||||||
|
let myIndex = this.stepIndex("app");
|
||||||
|
const res = await http.post(`/setup/app`, {
|
||||||
|
custom_login_message: data.login_message,
|
||||||
|
show_link_to_calendar: data.show_cal_link,
|
||||||
|
});
|
||||||
|
this.step += 1;
|
||||||
|
if (this.successfull <= myIndex) {
|
||||||
|
this.successfull = myIndex;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
async setMailConfig(data: {
|
||||||
|
host: string;
|
||||||
|
port: number;
|
||||||
|
secure: boolean;
|
||||||
|
mail: string;
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
}): Promise<AxiosResponse<any, any>> {
|
||||||
|
let myIndex = this.stepIndex("mail");
|
||||||
|
const res = await http.post(`/setup/mail`, {
|
||||||
|
host: data.host,
|
||||||
|
port: data.port,
|
||||||
|
secure: data.secure,
|
||||||
|
mail: data.mail,
|
||||||
|
username: data.username,
|
||||||
|
password: data.password,
|
||||||
|
});
|
||||||
|
this.step += 1;
|
||||||
|
if (this.successfull <= myIndex) {
|
||||||
|
this.successfull = myIndex;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
async createAdmin(credentials: {
|
||||||
|
username: string;
|
||||||
|
mail: string;
|
||||||
|
firstname: string;
|
||||||
|
lastname: string;
|
||||||
|
}): Promise<AxiosResponse<any, any>> {
|
||||||
|
let myIndex = this.stepIndex("account");
|
||||||
|
const res = await http.post(`/setup/me`, {
|
||||||
|
username: credentials.username,
|
||||||
|
mail: credentials.mail,
|
||||||
|
firstname: credentials.firstname,
|
||||||
|
lastname: credentials.lastname,
|
||||||
|
});
|
||||||
|
this.step += 1;
|
||||||
|
if (this.successfull < myIndex) {
|
||||||
|
this.successfull = myIndex;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
|
@ -5,33 +5,15 @@
|
||||||
<AppLogo />
|
<AppLogo />
|
||||||
<h2 class="text-center text-4xl font-extrabold text-gray-900">Einrichtung</h2>
|
<h2 class="text-center text-4xl font-extrabold text-gray-900">Einrichtung</h2>
|
||||||
</div>
|
</div>
|
||||||
|
<CheckProgressBar :total="dictionary.length" :step="step" :successfull="successfull" />
|
||||||
|
|
||||||
<form class="flex flex-col gap-2" @submit.prevent="setup">
|
<Club v-if="step == stepIndex('club')" />
|
||||||
<div class="-space-y-px">
|
<Images v-else-if="step == stepIndex('clubImages')" />
|
||||||
<div>
|
<App v-else-if="step == stepIndex('app')" />
|
||||||
<input id="username" name="username" type="text" required placeholder="Benutzer" class="rounded-b-none!" />
|
<Mail v-else-if="step == stepIndex('mail')" />
|
||||||
</div>
|
<Account v-else-if="step == stepIndex('account')" />
|
||||||
<div>
|
<Finished v-else-if="step == stepIndex('finished')" />
|
||||||
<input id="mail" name="mail" type="email" required placeholder="Mailadresse" class="rounded-none!" />
|
<p v-else class="text-center">UI-Fehler - versuchen Sie einen Reload der Seite</p>
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input id="firstname" name="firstname" type="text" required placeholder="Vorname" class="rounded-none!" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input id="lastname" name="lastname" type="text" required placeholder="Nachname" class="rounded-t-none!" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-row gap-2">
|
|
||||||
<button type="submit" primary :disabled="setupStatus == 'loading' || setupStatus == 'success'">
|
|
||||||
Admin-Account anlegen
|
|
||||||
</button>
|
|
||||||
<Spinner v-if="setupStatus == 'loading'" class="my-auto" />
|
|
||||||
<SuccessCheckmark v-else-if="setupStatus == 'success'" />
|
|
||||||
<FailureXMark v-else-if="setupStatus == 'failed'" />
|
|
||||||
</div>
|
|
||||||
<p v-if="setupMessage" class="text-center">{{ setupMessage }}</p>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<FormBottomBar />
|
<FormBottomBar />
|
||||||
</div>
|
</div>
|
||||||
|
@ -40,42 +22,23 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
import Spinner from "@/components/Spinner.vue";
|
|
||||||
import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
|
|
||||||
import FailureXMark from "@/components/FailureXMark.vue";
|
|
||||||
import FormBottomBar from "@/components/FormBottomBar.vue";
|
import FormBottomBar from "@/components/FormBottomBar.vue";
|
||||||
import AppLogo from "@/components/AppLogo.vue";
|
import AppLogo from "@/components/AppLogo.vue";
|
||||||
|
import App from "@/components/setup/App.vue";
|
||||||
|
import Account from "@/components/setup/Account.vue";
|
||||||
|
import CheckProgressBar from "@/components/CheckProgressBar.vue";
|
||||||
|
import { mapState } from "pinia";
|
||||||
|
import { useSetupStore } from "@/stores/setup";
|
||||||
|
import Club from "@/components/setup/Club.vue";
|
||||||
|
import Images from "@/components/setup/Images.vue";
|
||||||
|
import Finished from "@/components/setup/Finished.vue";
|
||||||
|
import Mail from "../../components/setup/Mail.vue";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data() {
|
computed: {
|
||||||
return {
|
...mapState(useSetupStore, ["step", "stepIndex", "successfull", "dictionary"]),
|
||||||
setupStatus: undefined as undefined | "loading" | "success" | "failed",
|
|
||||||
setupMessage: "" as string,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
setup(e: any) {
|
|
||||||
let formData = e.target.elements;
|
|
||||||
this.setupStatus = "loading";
|
|
||||||
this.setupMessage = "";
|
|
||||||
this.$http
|
|
||||||
.post(`/setup/me`, {
|
|
||||||
username: formData.username.value,
|
|
||||||
mail: formData.mail.value,
|
|
||||||
firstname: formData.firstname.value,
|
|
||||||
lastname: formData.lastname.value,
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
this.setupStatus = "success";
|
|
||||||
this.setupMessage = "Sie haben einen Verifizierungslink per Mail erhalten.";
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
this.setupStatus = "failed";
|
|
||||||
this.setupMessage = err.response.data;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Add table
Reference in a new issue