Added Worship page rendering

This commit is contained in:
Torsten Schulz
2024-06-17 13:36:15 +02:00
parent 4f1390b794
commit 48a54ecdbb
17 changed files with 637 additions and 563 deletions

View File

@@ -1,4 +1,5 @@
const { Worship } = require('../models'); const { Worship, EventPlace } = require('../models');
const { Op } = require('sequelize'); // Importieren Sie die Operatoren von Sequelize
exports.getAllWorships = async (req, res) => { exports.getAllWorships = async (req, res) => {
try { try {
@@ -14,6 +15,7 @@ exports.createWorship = async (req, res) => {
const worship = await Worship.create(req.body); const worship = await Worship.create(req.body);
res.status(201).json(worship); res.status(201).json(worship);
} catch (error) { } catch (error) {
console.log(error);
res.status(500).json({ message: 'Fehler beim Erstellen des Gottesdienstes' }); res.status(500).json({ message: 'Fehler beim Erstellen des Gottesdienstes' });
} }
}; };
@@ -45,3 +47,31 @@ exports.deleteWorship = async (req, res) => {
res.status(500).json({ message: 'Fehler beim Löschen des Gottesdienstes' }); res.status(500).json({ message: 'Fehler beim Löschen des Gottesdienstes' });
} }
}; };
exports.getFilteredWorships = async (req, res) => {
const { location, orderBy } = req.query;
const where = {};
if (location && location !== '-1') {
where.eventPlaceId = location;
}
where.date = {
[Op.gte]: new Date(), // Only include events from today onwards
};
try {
const worships = await Worship.findAll({
where,
include: {
model: EventPlace,
as: 'eventPlace',
},
order: [orderBy.split(' ')],
});
res.status(200).json(worships);
} catch (error) {
console.log(error);
res.status(500).json({ message: 'Fehler beim Abrufen der gefilterten Gottesdienste' });
}
};

View File

@@ -0,0 +1,16 @@
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.addColumn('worships', 'day_name', {
type: Sequelize.STRING,
allowNull: false,
default: ''
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.removeColumn('worships', 'day_name');
}
};

View File

@@ -34,7 +34,7 @@ module.exports = (sequelize) => {
allowNull: false, allowNull: false,
defaultValue: 0 defaultValue: 0
}, },
page_title: { // Neuer Eintrag page_title: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: true allowNull: true
} }

View File

@@ -50,6 +50,11 @@ module.exports = (sequelize) => {
introLine: { introLine: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: true allowNull: true
},
dayName: {
type: DataTypes.STRING,
defaultValue: '',
allowNull: false
} }
}, { }, {
tableName: 'worships', tableName: 'worships',

10
package-lock.json generated
View File

@@ -28,6 +28,7 @@
"core-js": "^3.8.3", "core-js": "^3.8.3",
"cors": "^2.8.5", "cors": "^2.8.5",
"crypto": "^1.0.1", "crypto": "^1.0.1",
"date-fns": "^3.6.0",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"express": "^4.19.2", "express": "^4.19.2",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
@@ -5708,6 +5709,15 @@
"node": ">=0.12" "node": ">=0.12"
} }
}, },
"node_modules/date-fns": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz",
"integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/kossnocorp"
}
},
"node_modules/debounce": { "node_modules/debounce": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",

View File

@@ -28,6 +28,7 @@
"core-js": "^3.8.3", "core-js": "^3.8.3",
"cors": "^2.8.5", "cors": "^2.8.5",
"crypto": "^1.0.1", "crypto": "^1.0.1",
"date-fns": "^3.6.0",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"express": "^4.19.2", "express": "^4.19.2",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",

View File

@@ -1,11 +1,12 @@
const express = require('express'); const express = require('express');
const router = express.Router(); const router = express.Router();
const { getAllWorships, createWorship, updateWorship, deleteWorship } = require('../controllers/worshipController'); const { getAllWorships, createWorship, updateWorship, deleteWorship, getFilteredWorships } = require('../controllers/worshipController');
const authMiddleware = require('../middleware/authMiddleware'); const authMiddleware = require('../middleware/authMiddleware');
router.get('/', authMiddleware, getAllWorships); router.get('/', getAllWorships);
router.post('/', authMiddleware, createWorship); router.post('/', authMiddleware, createWorship);
router.put('/:id', authMiddleware, updateWorship); router.put('/:id', authMiddleware, updateWorship);
router.delete('/:id', authMiddleware, deleteWorship); router.delete('/:id', authMiddleware, deleteWorship);
router.get('/filtered', getFilteredWorships);
module.exports = router; module.exports = router;

View File

@@ -1,17 +1,20 @@
<template> <template>
<div> <div>
<h1>{{ title }}</h1> <h1>{{ title }}</h1>
<div v-html="renderedContent"></div> <RenderContentComponent :content="content" />
</div> </div>
</template> </template>
<script> <script>
import axios from '@/axios'; import axios from '@/axios';
import { mapState, mapGetters } from 'vuex'; import { mapState, mapGetters } from 'vuex';
import { render } from '@/utils/render'; import RenderContentComponent from '@/components/RenderContentComponent.vue';
export default { export default {
name: 'ContentComponent', name: 'ContentComponent',
components: {
RenderContentComponent
},
props: { props: {
link: { link: {
type: String, type: String,
@@ -27,9 +30,6 @@ export default {
computed: { computed: {
...mapState(['menuData']), ...mapState(['menuData']),
...mapGetters(['getMenuData']), ...mapGetters(['getMenuData']),
renderedContent() {
return render(this.content);
}
}, },
watch: { watch: {
link: { link: {
@@ -67,7 +67,8 @@ export default {
this.title = findTitle(this.menuData, link); this.title = findTitle(this.menuData, link);
}, },
}, },
}; };
</script> </script>
<style scoped></style>
<style scoped></style>

View File

@@ -0,0 +1,74 @@
<template>
<div>
<component :is="dynamicComponent" v-bind="componentProps"></component>
</div>
</template>
<script>
import { createApp, h } from 'vue';
import WorshipRender from './WorshipRender.vue';
export default {
name: 'RenderContentComponent',
props: {
content: {
type: String,
required: true
}
},
data() {
return {
dynamicComponent: null,
componentProps: {}
};
},
watch: {
content: {
immediate: true,
handler(newContent) {
this.renderContent(newContent);
}
}
},
methods: {
renderContent(content) {
const worshipsPattern = /{{ worshipslist:(.*?) }}/g;
content.replace(worshipsPattern, (match, config) => {
const props = this.parseConfig(config);
this.dynamicComponent = WorshipRender;
this.componentProps = props;
return '<div id="worship-render-placeholder"></div>';
});
this.$nextTick(() => {
if (this.dynamicComponent) {
const placeholder = document.getElementById('worship-render-placeholder');
if (placeholder) {
const app = createApp({
render() {
return h(this.dynamicComponent, this.componentProps);
}
});
app.mount(placeholder);
}
}
});
},
parseConfig(configString) {
const config = {};
const configArray = configString.split(',');
configArray.forEach(item => {
const [key, value] = item.split('=');
if (key && value !== undefined) {
config[key.trim()] = isNaN(value) ? value.trim() : Number(value);
}
});
return config;
}
}
};
</script>
<style scoped>
/* Add styles if needed */
</style>

View File

@@ -0,0 +1,94 @@
<template>
<div>
<table v-if="worships.length" class="worships">
<tr v-for="worship in worships" :key="worship.id" :style="worship.eventPlace && worship.eventPlace.backgroundColor ? `background-color:${worship.eventPlace.backgroundColor}` : ''">
<td>
<div>{{ formatDate(worship.date) }}</div>
<div>{{ worship.dayName }}</div>
</td>
<td>
<div v-if="worship.neighborInvitation" class="neighborhood-invitation">Einladung zum Gottesdienst im Nachbarschaftsraum:</div>
<h3>
<span :class="worship.highlightTime ? 'highlight-time' : ''">{{ formatTime(worship.time) }}</span>&nbsp;-&nbsp;
{{ !worship.neighborInvitation ? worship.title : '' }}
</h3>
<div v-if="worship.organizer">Gestaltung: {{ worship.organizer }}</div>
<div v-if="worship.collection">Kollekte: {{ worship.collection }}</div>
<div v-if="worship.eventPlace.id && worship.eventPlace.id">
Adresse: {{ worship.eventPlace.name }}, {{ worship.eventPlace.street }}, {{ worship.eventPlace.city }}
</div>
<div v-if="worship.selfInformation" class="selfinformation">Bitte informieren Sie sich auch auf den Internetseiten dieser Gemeinde!</div>
</td>
</tr>
</table>
<p v-else>Keine Gottesdienste verfügbar.</p>
</div>
</template>
<script>
import axios from '@/axios';
import { formatTime, formatDate } from '@/utils/strings';
export default {
name: 'WorshipRender',
props: {
location: {
type: Number,
required: true
},
orderBy: {
type: String,
default: 'date ASC'
}
},
data() {
return {
worships: []
};
},
async created() {
await this.fetchWorships();
},
methods: {
formatTime,
formatDate,
async fetchWorships() {
try {
const response = await axios.get('/worships/filtered', {
params: {
location: this.location,
orderBy: this.orderBy
}
});
this.worships = response.data;
} catch (error) {
console.error('Fehler beim Abrufen der Gottesdienste:', error);
}
}
}
};
</script>
<style scoped>
table.worships {
border-collapse: collapse;
width: 100%;
}
table.worships td {
border: 1px solid #000;
text-align: center;
}
h3 {
margin: 0;
}
table.worships td div{
margin: 5px;
}
.highlight-time {
text-decoration: underline;
}
.neighborhood-invitation {
font-weight: bold;
color: #0020e0;
}
</style>

View File

@@ -1,207 +0,0 @@
<template>
<div class="admin-menu-management">
<h2>Menüverwaltung</h2>
<div>
<h3>Neuen Menüpunkt hinzufügen</h3>
<form @submit.prevent="addMenuItem">
<label for="name">Name:</label>
<input type="text" v-model="newItem.name" required />
<label for="link">Link:</label>
<input type="text" v-model="newItem.link" required />
<label for="component">Component:</label>
<input type="text" v-model="newItem.component" required />
<label for="showInMenu">Im Menü anzeigen:</label>
<input type="checkbox" v-model="newItem.showInMenu" />
<label for="requiresAuth">Authentifizierung erforderlich:</label>
<input type="checkbox" v-model="newItem.requiresAuth" />
<label for="image">Bild:</label>
<input type="text" v-model="newItem.image" />
<label for="parentId">Eltern-Menüpunkt:</label>
<select v-model="newItem.parentId">
<option :value="null">Kein Eltern-Menüpunkt</option>
<option v-for="item in menuItems" :key="item.id" :value="item.id">
{{ item.name }}
</option>
</select>
<button type="submit">Hinzufügen</button>
</form>
</div>
<div>
<h3>Menüpunkte bearbeiten</h3>
<ul>
<li v-for="item in menuItems" :key="item.id">
{{ item.name }}
<button @click="editMenuItem(item)">Bearbeiten</button>
<button @click="deleteMenuItem(item.id)">Löschen</button>
</li>
</ul>
</div>
<div v-if="editingItem">
<h3>Menüpunkt bearbeiten</h3>
<form @submit.prevent="updateMenuItem">
<label for="editName">Name:</label>
<input type="text" v-model="editingItem.name" required />
<label for="editLink">Link:</label>
<input type="text" v-model="editingItem.link" required />
<label for="editComponent">Component:</label>
<input type="text" v-model="editingItem.component" required />
<label for="editShowInMenu">Im Menü anzeigen:</label>
<input type="checkbox" v-model="editingItem.showInMenu" />
<label for="editRequiresAuth">Authentifizierung erforderlich:</label>
<input type="checkbox" v-model="editingItem.requiresAuth" />
<label for="editImage">Bild:</label>
<input type="text" v-model="editingItem.image" />
<label for="editParentId">Eltern-Menüpunkt:</label>
<select v-model="editingItem.parentId">
<option :value="null">Kein Eltern-Menüpunkt</option>
<option v-for="item in menuItems" :key="item.id" :value="item.id">
{{ item.name }}
</option>
</select>
<button type="submit">Aktualisieren</button>
<button @click="cancelEdit">Abbrechen</button>
</form>
</div>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex';
export default {
name: 'AdminMenuManagement',
data() {
return {
newItem: {
name: '',
link: '',
component: '',
showInMenu: false,
requiresAuth: false,
image: '',
parentId: null
},
editingItem: null
};
},
computed: {
...mapState(['menuData']),
menuItems() {
return this.menuData;
}
},
methods: {
...mapActions(['loadMenuData']),
async addMenuItem() {
this.menuData.push(this.newItem);
await this.saveMenuData();
this.resetNewItem();
},
editMenuItem(item) {
this.editingItem = { ...item };
},
async updateMenuItem() {
const index = this.menuData.findIndex(item => item.id === this.editingItem.id);
if (index !== -1) {
this.menuData.splice(index, 1, this.editingItem);
await this.saveMenuData();
this.cancelEdit();
}
},
async deleteMenuItem(id) {
this.menuData = this.menuData.filter(item => item.id !== id);
await this.saveMenuData();
},
cancelEdit() {
this.editingItem = null;
},
resetNewItem() {
this.newItem = {
name: '',
link: '',
component: '',
showInMenu: false,
requiresAuth: false,
image: '',
parentId: null
};
},
async saveMenuData() {
try {
await fetch('/menu-data', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(this.menuData)
});
await this.loadMenuData();
} catch (error) {
console.error('Fehler beim Speichern der Menü-Daten:', error);
}
}
},
created() {
this.loadMenuData();
}
};
</script>
<style scoped>
.admin-menu-management {
padding: 20px;
}
form {
margin-bottom: 20px;
}
form label {
display: block;
margin-top: 10px;
}
form input,
form select {
width: 100%;
padding: 5px;
margin-top: 5px;
}
button {
margin-top: 10px;
padding: 5px 10px;
cursor: pointer;
}
ul {
list-style-type: none;
padding: 0;
}
li {
margin: 10px 0;
display: flex;
justify-content: space-between;
align-items: center;
}
li button {
margin-left: 10px;
}
</style>

View File

@@ -46,7 +46,7 @@
</template> </template>
<script> <script>
import { ref, computed, onMounted } from 'vue'; import { ref, computed, onMounted, watch } from 'vue';
import { useStore } from 'vuex'; import { useStore } from 'vuex';
import axios from '../../axios'; import axios from '../../axios';
import { EditorContent, useEditor } from '@tiptap/vue-3'; import { EditorContent, useEditor } from '@tiptap/vue-3';
@@ -97,7 +97,7 @@ export default {
], ],
content: '', content: '',
onUpdate: ({ editor }) => { onUpdate: ({ editor }) => {
store.commit('UPDATE_PAGE_CONTENT', editor.getHTML()); store.commit('SET_PAGE_CONTENT', editor.getHTML());
}, },
}); });
@@ -118,6 +118,7 @@ export default {
const allPages = []; const allPages = [];
flattenPages(data, allPages); flattenPages(data, allPages);
pages.value = allPages.sort((a, b) => a.name.localeCompare(b.name)); pages.value = allPages.sort((a, b) => a.name.localeCompare(b.name));
store.commit('setMenuData', data);
} catch (error) { } catch (error) {
console.error('Fehler beim Abrufen der Seiten:', error); console.error('Fehler beim Abrufen der Seiten:', error);
} }
@@ -127,7 +128,6 @@ export default {
try { try {
await store.dispatch('loadPageContent', selectedPage.value); await store.dispatch('loadPageContent', selectedPage.value);
const content = store.getters.pageContent; const content = store.getters.pageContent;
const setEditorContent = () => { const setEditorContent = () => {
if (editor.value && editor.value.commands) { if (editor.value && editor.value.commands) {
editor.value.commands.setContent(content, false); editor.value.commands.setContent(content, false);
@@ -135,7 +135,6 @@ export default {
setTimeout(setEditorContent, 100); setTimeout(setEditorContent, 100);
} }
}; };
setEditorContent(); setEditorContent();
} catch (error) { } catch (error) {
console.error('Fehler beim Laden des Seiteninhalts:', error); console.error('Fehler beim Laden des Seiteninhalts:', error);
@@ -167,6 +166,15 @@ export default {
return pages.value; return pages.value;
}); });
watch(selectedPage, (newPage) => {
store.dispatch('setSelectedPage', newPage);
const page = pages.value.find(page => page.link === newPage);
if (page) {
store.dispatch('setPageTitle', page.name);
}
loadPageContent();
});
const openWorshipDialog = () => { const openWorshipDialog = () => {
worshipDialog.value.openWorshipDialog(); worshipDialog.value.openWorshipDialog();
}; };

View File

@@ -14,8 +14,12 @@
<label for="link">Link</label> <label for="link">Link</label>
<input id="link" v-model="selectedMenuItem.link" placeholder="Link" /> <input id="link" v-model="selectedMenuItem.link" placeholder="Link" />
<label for="page-title">Seitenname</label>
<input id="page-title" v-model="selectedMenuItem.pageTitle" placeholder="Seitenname" />
<label for="order-id">Order ID</label> <label for="order-id">Order ID</label>
<input id="order-id" v-model.number="selectedMenuItem.orderId" placeholder="Order ID" type="number" class="order-id" /> <input id="order-id" v-model.number="selectedMenuItem.orderId" placeholder="Order ID" type="number"
class="order-id" />
<div class="checkbox-container"> <div class="checkbox-container">
<label> <label>
@@ -35,6 +39,9 @@
<span v-html="getIndentedName(item)"></span> <span v-html="getIndentedName(item)"></span>
</option> </option>
</select> </select>
<label for="component">Vue-Komponente</label>
<input id="component" v-model="selectedMenuItem.component" placeholder="Vue-Komponente" />
</form> </form>
</div> </div>
<div class="tree-view"> <div class="tree-view">
@@ -66,13 +73,13 @@
</ul> </ul>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { ref, onMounted, computed } from 'vue'; import { ref, onMounted, computed } from 'vue';
import axios from '../../axios'; import axios from '../../axios';
export default { export default {
name: 'MenuManagement', name: 'MenuManagement',
setup() { setup() {
const menuData = ref([]); const menuData = ref([]);
@@ -99,7 +106,7 @@
const flattenMenuData = (data, parentId = null) => { const flattenMenuData = (data, parentId = null) => {
return data.reduce((acc, item) => { return data.reduce((acc, item) => {
const newItem = { ...item, parent_id: parentId }; const newItem = { ...item, parent_id: parentId, page_title: item.pageTitle };
const { submenu, ...rest } = newItem; const { submenu, ...rest } = newItem;
acc.push(rest); acc.push(rest);
if (submenu && submenu.length) { if (submenu && submenu.length) {
@@ -114,6 +121,7 @@
name: '', name: '',
link: '', link: '',
component: '', component: '',
pageTitle: '',
showInMenu: true, showInMenu: true,
requiresAuth: false, requiresAuth: false,
orderId: 0, orderId: 0,
@@ -129,6 +137,7 @@
name: '', name: '',
link: '', link: '',
component: '', component: '',
pageTitle: '',
showInMenu: true, showInMenu: true,
requiresAuth: false, requiresAuth: false,
orderId: 0, orderId: 0,
@@ -187,86 +196,85 @@
getIndentedName, getIndentedName,
}; };
}, },
}; };
</script> </script>
<style scoped> <style scoped>
.menu-management { .menu-management {
width: 100%; width: 100%;
margin: auto; margin: auto;
} }
.button-container { .button-container {
display: inline-flex; display: inline-flex;
gap: 10px; gap: 10px;
margin-bottom: 20px; margin-bottom: 20px;
} }
.tree-view { .tree-view {
margin-top: 20px; margin-top: 20px;
} }
.tree-view ul { .tree-view ul {
list-style-type: none; list-style-type: none;
padding: 0; padding: 0;
} }
.tree-view li { .tree-view li {
margin-bottom: 5px; margin-bottom: 5px;
padding-left: 20px; padding-left: 20px;
} }
.tree-view .menu-item { .tree-view .menu-item {
display: inline-flex; display: inline-flex;
width: 100%; width: 100%;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
} }
.tree-view span { .tree-view span {
cursor: pointer; cursor: pointer;
color: black; color: black;
} }
.tree-view button { .tree-view button {
border: none; border: none;
height: 1.6em; height: 1.6em;
padding: 0 0.5em; padding: 0 0.5em;
margin: 1px; margin: 1px;
border-radius: 5px; border-radius: 5px;
} }
.tree-view span:hover { .tree-view span:hover {
text-decoration: underline; text-decoration: underline;
} }
.edit-form { .edit-form {
margin-top: 20px; margin-top: 20px;
} }
.edit-form label { .edit-form label {
display: block; display: block;
margin-bottom: 5px; margin-bottom: 5px;
font-weight: bold; font-weight: bold;
} }
.edit-form input:not([type="checkbox"]) { .edit-form input:not([type="checkbox"]) {
display: block; display: block;
margin-bottom: 10px; margin-bottom: 10px;
} }
.edit-form .checkbox-container { .edit-form .checkbox-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin-right: 10px; margin-right: 10px;
} }
.edit-form .order-id { .edit-form .order-id {
width: 50px; width: 50px;
} }
.edit-form button { .edit-form button {
margin-top: 5px; margin-top: 5px;
} }
</style> </style>

View File

@@ -1,26 +1,55 @@
<template> <template>
<div> <div>
<div class="previewinfo">Dies ist eine Vorschau.</div> <div class="previewinfo">Dies ist eine Vorschau.</div>
<div v-html="renderedContent"></div> <h1>{{ title }}</h1>
<RenderContentComponent :content="content" />
</div> </div>
</template> </template>
<script> <script>
import { computed } from 'vue'; import { computed, watchEffect } from 'vue';
import { useStore } from 'vuex'; import { useStore } from 'vuex';
import { render } from '@/utils/render'; import RenderContentComponent from '@/components/RenderContentComponent.vue';
export default { export default {
name: 'PagePreview', name: 'PagePreview',
components: {
RenderContentComponent
},
setup() { setup() {
const store = useStore(); const store = useStore();
const content = computed(() => store.state.pageContent); const content = computed(() => store.state.pageContent);
const renderedContent = computed(() => render(content.value)); const selectedPage = computed(() => store.state.selectedPage);
const menuData = computed(() => store.state.menuData);
const title = computed(() => store.state.pageTitle);
const setTitle = (link) => {
const findTitle = (menuItems, link) => {
for (const item of menuItems) {
if (item.link === link) {
return item.pageTitle || item.name;
}
if (item.submenu && item.submenu.length > 0) {
const found = findTitle(item.submenu, link);
if (found) {
return found;
}
}
}
return '';
};
store.dispatch('setPageTitle', findTitle(menuData.value, link));
};
watchEffect(() => {
setTitle(selectedPage.value);
});
return { return {
renderedContent, content,
title
}; };
}, }
}; };
</script> </script>

View File

@@ -9,6 +9,9 @@
<label for="date">Datum:</label> <label for="date">Datum:</label>
<input type="date" id="date" v-model="worshipData.date" required> <input type="date" id="date" v-model="worshipData.date" required>
<label for="dayName">Name des Tags:</label>
<input type="text" id="dayName" v-model="worshipData.dayName" required>
<label for="time">Uhrzeit:</label> <label for="time">Uhrzeit:</label>
<input type="time" id="time" v-model="worshipData.time" required> <input type="time" id="time" v-model="worshipData.time" required>

View File

@@ -1,6 +1,6 @@
import { createStore } from 'vuex'; import { createStore } from 'vuex';
import axios from 'axios'; import axios from 'axios';
import router from '../router'; // Importieren des Routers import router from '../router';
let user = []; let user = [];
try { try {
@@ -16,6 +16,8 @@ export default createStore({
token: localStorage.getItem('token') || '', token: localStorage.getItem('token') || '',
menuData: [], menuData: [],
pageContent: '', pageContent: '',
pageTitle: '',
selectedPage: '',
}, },
mutations: { mutations: {
setLogin(state, { user, token }) { setLogin(state, { user, token }) {
@@ -44,6 +46,12 @@ export default createStore({
UPDATE_PAGE_CONTENT(state, content) { UPDATE_PAGE_CONTENT(state, content) {
state.pageContent = content; state.pageContent = content;
}, },
setPageTitle(state, title) {
state.pageTitle = title;
},
setSelectedPage(state, page) {
state.selectedPage = page;
},
}, },
actions: { actions: {
async loadMenuData({ commit }) { async loadMenuData({ commit }) {
@@ -77,6 +85,12 @@ export default createStore({
console.error('Fehler beim Speichern des Seiteninhalts:', error); console.error('Fehler beim Speichern des Seiteninhalts:', error);
} }
}, },
setPageTitle({ commit }, title) {
commit('setPageTitle', title);
},
setSelectedPage({ commit }, page) {
commit('setSelectedPage', page);
},
login({ commit }, { user, token }) { login({ commit }, { user, token }) {
commit('setLogin', { user, token }); commit('setLogin', { user, token });
}, },
@@ -89,5 +103,7 @@ export default createStore({
user: state => state.user, user: state => state.user,
menuData: state => state.menuData, menuData: state => state.menuData,
pageContent: state => state.pageContent, pageContent: state => state.pageContent,
pageTitle: state => state.pageTitle,
selectedPage: state => state.selectedPage,
} }
}); });

View File

@@ -1,15 +0,0 @@
export function render(content) {
console.log('do render', content);
const worshipsPattern = /{{ worshipslist:(.*?) }}/g;
const renderedContent = content.replace(worshipsPattern, (match, config) => {
return renderWorships(config);
});
return renderedContent;
}
function renderWorships(config) {
console.log('render worships', config);
return `<div class="worships-list">Worships at location ${location}</div>`;
}