Formulare im Admin Panel, Event Formular-Fremdschlüssel-Spalte hinzugefügt

This commit is contained in:
R40fendt
2026-01-05 18:14:51 +01:00
parent 4bb654bf4d
commit ae1feee25e
7 changed files with 195 additions and 43 deletions

View File

@@ -10,3 +10,5 @@ Bei der Migration beachten:
- Neue Formulartabelle - Neue Formulartabelle
- .env.production anpassen - .env.production anpassen
- Salt ändern (secret.php in der API) - Salt ändern (secret.php in der API)
- Event-Tabelle updaten (neue Formular-Spalte)
- Admin-Tabellen hinzufügen

View File

@@ -1,31 +1,54 @@
<script lang="ts" setup> <script lang="ts" setup>
import RitzenbergenLib from "../ritzenbergenlib.ts"; import RitzenbergenLib from "../ritzenbergenlib.ts";
import { defineProps, ref } from "vue"; import { defineProps, ref, watchEffect } from "vue";
const props = defineProps({ const props = defineProps({
form: { form: {
type: RitzenbergenLib.Formular, type: RitzenbergenLib.Formular,
required: true, required: true,
}, },
token: {
type: String,
required: false,
default: ""
}
}); });
const form = props.form; const form = ref(props.form);
let results = ref([]);
fetch( const results = ref([]);
RitzenbergenLib.RitzenbergenLib.api( const myfields = ref([]);
"/formulare/get_results.php?id=" + form.id watchEffect(() => {
fetch(
RitzenbergenLib.RitzenbergenLib.api(
"/formulare/get_results.php?id=" + form.value.id
)
) )
) .then((res) => res.json())
.then((res) => res.json()) .then((data) => {
.then((data) => { if(data.error){
results.value = data; // Admin Rechte nötig
fetch(RitzenbergenLib.RitzenbergenLib.api("/admin/formulare/ergebnisse.php?formular="+form.value.id),{
headers: {
"Authorization":props.token
}
})
.then((res)=>res.json())
.then((data)=>{
results.value=data;
})
} else {
results.value = data;
}
});
myfields.value = [];
form.value.fields.forEach((field: RitzenbergenLib.Field) => {
if (myfields.value.find((el) => el.name == field.name) == undefined) {
myfields.value.push(field);
}
}); });
const myfields = [];
form.fields.forEach((field: RitzenbergenLib.Field) => {
if (myfields.find((el) => el.name == field.name) == undefined) {
myfields.push(field);
}
}); });
function prettyResult(result: any) { function prettyResult(result: any) {
@@ -36,7 +59,7 @@ function prettyResult(result: any) {
let tempresult = result.value; let tempresult = result.value;
let cancel = false; let cancel = false;
tempresult = tempresult.map((el) => { tempresult = tempresult.map((el) => {
let field = form.fields.find((f) => f.value == el); let field = form.value.fields.find((f) => f.value == el);
if (field == undefined) { if (field == undefined) {
cancel = true; cancel = true;
return el; return el;
@@ -48,7 +71,7 @@ function prettyResult(result: any) {
return result.value.join(", "); return result.value.join(", ");
} }
if (result.type == "radio") { if (result.type == "radio") {
let field = form.fields.find((f) => f.value == result.value); let field = form.value.fields.find((f) => f.value == result.value);
if (field != undefined) { if (field != undefined) {
return field.displayvalue ?? result.value; return field.displayvalue ?? result.value;
} }
@@ -58,7 +81,9 @@ function prettyResult(result: any) {
</script> </script>
<template> <template>
<h1>Ergebnisse {{ form.name }}</h1> <h1>Ergebnisse {{ form.name }}</h1>
<div class="main">
<table> <table>
<thead> <thead>
<tr> <tr>
@@ -70,8 +95,23 @@ function prettyResult(result: any) {
</thead> </thead>
<tbody> <tbody>
<tr v-for="(result, ri) in results" :key="ri"> <tr v-for="(result, ri) in results" :key="ri">
<td v-for="(field, fi) in myfields" :key="fi" :style="field.type=='color'?{ 'backgroundColor': result.data.find((el) => el.name == field.name).value, width: '10px', height: '10px' }:{}"> <td
<span v-if="field.type!='color'">{{ v-if="result.data != undefined"
v-for="(field, fi) in myfields"
:key="fi"
:style="
field.type == 'color'
? {
backgroundColor: result.data.find(
(el) => el.name == field.name
).value,
width: '10px',
height: '10px',
}
: {}
"
>
<span v-if="field.type != 'color'">{{
prettyResult( prettyResult(
result.data.find( result.data.find(
(el) => (el) =>
@@ -80,17 +120,28 @@ function prettyResult(result: any) {
) )
) )
}}</span> }}</span>
<span v-else ></span> <span v-else></span>
</td> </td>
<td>{{ result.timestamp }}</td> <td>{{ result.timestamp }}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div>
</template> </template>
<style scoped> <style scoped>
@import "../assets/css/bulitipp2.css"; @import "../assets/css/bulitipp2.css";
td { td {
min-width: 150px; min-width: 150px;
min-height: 0;
}
tbody, table, .main{
overflow-x: scroll;
}
thead tr td {
background-color: #efefef;
}
h1 {
text-align: center;
} }
</style> </style>

View File

@@ -41,7 +41,7 @@ export default {
<br /> <br />
</p> </p>
<br /><br /> <RouterLink to="/adminpanel/login">Admin Login</RouterLink>
</Modal> </Modal>
<button @click="aboutVisible=true" class="text-white btn-ueber openBtn">Über</button> <button @click="aboutVisible=true" class="text-white btn-ueber openBtn">Über</button>

View File

@@ -6,6 +6,7 @@ import Modal from "../Modal.vue";
import $ from "jquery"; import $ from "jquery";
import MarkdownRender from "vue-renderer-markdown"; import MarkdownRender from "vue-renderer-markdown";
import "katex/dist/katex.min.css"; import "katex/dist/katex.min.css";
import Forms from "./Forms.vue";
export default { export default {
data() { data() {
@@ -27,7 +28,6 @@ export default {
result = JSON.parse(data); result = JSON.parse(data);
}, },
}); });
console.log(result);
return result; return result;
/* /*
Beispiel-Event-Array: Beispiel-Event-Array:
@@ -40,7 +40,8 @@ export default {
content: "# Test", content: "# Test",
link: undefined, link: undefined,
foto: "erntefest/2011/pic08.jpg", foto: "erntefest/2011/pic08.jpg",
minitext: "" minitext: "",
formular: 1
}, },
{ {
id: 1, id: 1,
@@ -111,6 +112,7 @@ export default {
components: { components: {
Modal, Modal,
MarkdownRender, MarkdownRender,
Forms
}, },
}; };
</script> </script>
@@ -158,6 +160,7 @@ export default {
:content="event.content" :content="event.content"
/> />
<div v-else v-html="event.content"></div> <div v-else v-html="event.content"></div>
<Forms v-if="event.formular" :formid="event.formular"/>
</Modal> </Modal>
<div class="item-wrapper"> <div class="item-wrapper">

View File

@@ -1,12 +1,23 @@
<script lang="ts" setup> <script lang="ts" setup>
import RitzenbergenLib from "../../ritzenbergenlib.ts"; import RitzenbergenLib from "../../ritzenbergenlib.ts";
import FormResults from "../FormResults.vue"; import FormResults from "../FormResults.vue";
import { ref } from "vue"; import { ref, defineProps } from "vue";
import Modal from "../Modal.vue"; import Modal from "../Modal.vue";
const Formular = RitzenbergenLib.Formular; const Formular = RitzenbergenLib.Formular;
const Field = RitzenbergenLib.Field; const Field = RitzenbergenLib.Field;
const forms = ref([] as Formular[]); const forms = ref([] as Formular[]);
const props=defineProps({
formid: {
type: [Number, null],
required: false,
default: null
}
});
Formular.getForms().then((f) => { Formular.getForms().then((f) => {
forms.value = f; forms.value = f;
}); });
@@ -23,6 +34,8 @@ function submit($event: Event) {
forms.value = Formular.getForms().then((f) => { forms.value = Formular.getForms().then((f) => {
forms.value = f; forms.value = f;
}); });
values.value={};
}); });
} }
@@ -38,12 +51,12 @@ function openModal(form: Formular){
const values=ref({}); const values=ref({});
</script> </script>
<template> <template>
<Modal v-show="modalOpened" @closemodal="modalOpened=false"> <Modal v-if="openedForm && openedForm.ispublic" v-show="modalOpened" @closemodal="modalOpened=false">
<FormResults v-if="openedForm" :form="openedForm"/> <FormResults v-if="modalOpened" :form="openedForm"/>
</Modal> </Modal>
<section ref="anmeldeformular" id="anmeldeformular"> <section ref="anmeldeformular" id="anmeldeformular">
<section class="form5 cid-u6k7q0BfGa"> <section class="form5 cid-u6k7q0BfGa">
<div class="container" v-for="(form, i) in forms" :key="i"> <div class="container" v-for="(form, i) in forms.filter((form)=>form.id==props.formid||props.id==null)" :key="form">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-12 content-head"> <div class="col-12 content-head">
<div class="mbr-section-head mb-5"> <div class="mbr-section-head mb-5">
@@ -54,8 +67,8 @@ const values=ref({});
</h3> </h3>
<br /> <br />
<h5 style="text-align: center"> <h5 style="text-align: center">
<b @click="openModal(form)" class="fakelink">{{ form.minitext }} <b @click="openModal(form)" class="fakelink" v-if="form.ispublic">{{ form.minitext }}
</b> <br /> </b> <b v-else>{{ form.minitext }}</b> <br />
</h5> </h5>
<br /> <br />
</div> </div>
@@ -73,7 +86,7 @@ const values=ref({});
:data-i="i" :data-i="i"
@submit.prevent="submit" @submit.prevent="submit"
> >
<div v-for="(field, j) in form.fields" :key="j"> <div v-for="(field, j) in form.fields" :key="field">
<label <label
v-if="field.placeholder == null" v-if="field.placeholder == null"
:for="'field-' + field.id" :for="'field-' + field.id"
@@ -82,7 +95,7 @@ const values=ref({});
field.displayvalue ?? field.displayname ?? field.name field.displayvalue ?? field.displayname ?? field.name
}}: }}:
<span v-if="field.type == 'range'" <span v-if="field.type == 'range'"
><br />{{ values[field.name] }}</span ><br />{{ values[field.id] }}</span
> >
</label> </label>
<FormKit <FormKit
@@ -98,7 +111,7 @@ const values=ref({});
:checked="field.checked" :checked="field.checked"
:title="field.title" :title="field.title"
:value="field.value" :value="field.value"
v-model="values[field.name]" v-model="values[field.id]"
/> />
<textarea <textarea
@@ -128,6 +141,10 @@ const values=ref({});
</form> </form>
</div> </div>
</div> </div>
<br/>
<br/>
<br/>
<br/>
</div> </div>
</section> </section>
</section> </section>

View File

@@ -1,12 +1,26 @@
<script lang="ts" setup> <script lang="ts" setup>
import AdminNavbar from "../../components/admin/AdminNavbar.vue"; import AdminNavbar from "../../components/admin/AdminNavbar.vue";
import { useRoute } from "vue-router";
import { ref } from "vue";
const route = useRoute();
const token = ref(route.params.token);
</script> </script>
<template> <template>
<AdminNavbar/> <AdminNavbar />
<h1>Events</h1> <br />
<br />
<br />
<br />
<br />
<h1>Events</h1>
</template> </template>
<style scoped lang="scss"> <style scoped lang="scss">
h1{
text-align: center;
}
</style> </style>

View File

@@ -4,6 +4,7 @@ import { ref } from "vue";
import AdminNavbar from "../../components/admin/AdminNavbar.vue"; import AdminNavbar from "../../components/admin/AdminNavbar.vue";
import RitzenbergenLib from "../../ritzenbergenlib.ts"; import RitzenbergenLib from "../../ritzenbergenlib.ts";
import InputP from "../../components/admin/InputP.vue"; import InputP from "../../components/admin/InputP.vue";
import FormResults from "../../components/FormResults.vue";
const route = useRoute(); const route = useRoute();
@@ -24,6 +25,23 @@ async function saveForm(form) {
} }
}) })
} }
async function deleteForm(form){
const url=new URL(RitzenbergenLib.RitzenbergenLib.api("/admin/formulare/deleteForm.php"));
url.searchParams.set("id",form.id);
return fetch(url,{
headers: {
"Authorization":"Bearer "+token.value
}
})
.then((response) => {
if (response.ok)
RitzenbergenLib.Formular.getForms().then(
(result) => (formulare.value = result)
);
return response;
})
}
function filterType(type: string) { function filterType(type: string) {
if (type in ["radio", "checkbox"]) return "text"; if (type in ["radio", "checkbox"]) return "text";
return type; return type;
@@ -49,8 +67,10 @@ async function newForm() {
<br /> <br />
<br /> <br />
<br /> <br />
<br />
<br />
<h1>Formulare</h1> <h1>Formulare</h1>
<div> <div class="formeditor">
<form <form
@submit.prevent="saveForm(form)" @submit.prevent="saveForm(form)"
v-for="(form, i) in formulare" v-for="(form, i) in formulare"
@@ -195,9 +215,14 @@ async function newForm() {
<br /> <br />
<input class="saveBtn" type="submit" value="Speichern" /> <input class="saveBtn" type="submit" value="Speichern" />
<br /> <br />
<br /> <input class="deleteBtn" type="button" value="Löschen (Doppelklick)" @dblclick="deleteForm(form)" />
</form> </form>
<button class="saveBtn" @click="newForm">Neues Formular</button> <button class="newFormBtn deleteBtn" @click="newForm">Neues Formular</button>
<br>
<br>
</div>
<div class="formresults">
<FormResults v-for="form in formulare.filter((el)=>el!=undefined)" :key="form" :form="form" :token="token"/>
</div> </div>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@@ -209,7 +234,47 @@ thead tr td {
margin-left: 20%; margin-left: 20%;
margin-right: 20%; margin-right: 20%;
} }
label, p, i { label, p, i, h1, h2 {
text-align: center; text-align: center;
} }
.formresults{
overflow: scroll;
}
input{
max-width: 300px;
}
form {
display: flex;
align-items: center;
justify-content: center;
border: 3px solid black;
border-radius: 10px;
margin: 20px;
padding: 30px;
}
.deleteBtn {
padding: 12px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s ease;
text-align: center;
background-color: #cc0000;
color: white;
}
.deleteBtn:hover{
background-color: #aa0000;
}
.newFormBtn{
background-color: #0000ff;
}
.newFormBtn:hover{
background-color: #0000aa;
}
.formeditor {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
</style> </style>