Added multiple features
95
controllers/imageController.js
Normal file
@@ -0,0 +1,95 @@
|
||||
const { Image } = require('../models');
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
const multer = require('multer');
|
||||
const path = require('path');
|
||||
|
||||
const storage = multer.diskStorage({
|
||||
destination: (req, file, cb) => {
|
||||
cb(null, 'public/images/uploads');
|
||||
},
|
||||
filename: (req, file, cb) => {
|
||||
const uniqueSuffix = `${uuidv4()}${path.extname(file.originalname)}`;
|
||||
cb(null, uniqueSuffix);
|
||||
}
|
||||
});
|
||||
|
||||
const upload = multer({ storage });
|
||||
|
||||
exports.uploadImage = upload.single('image');
|
||||
|
||||
exports.saveImageDetails = async (req, res) => {
|
||||
try {
|
||||
const { title, description, pageId } = req.body;
|
||||
const filename = req.file.filename;
|
||||
const newImage = await Image.create({
|
||||
id: uuidv4(),
|
||||
filename,
|
||||
title,
|
||||
description,
|
||||
pageId: pageId || null
|
||||
});
|
||||
res.status(201).json(newImage);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Speichern des Bildes:', error);
|
||||
res.status(500).send('Fehler beim Speichern des Bildes');
|
||||
}
|
||||
};
|
||||
|
||||
exports.getImages = async (req, res) => {
|
||||
try {
|
||||
const images = await Image.findAll();
|
||||
res.status(200).json(images);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Abrufen der Bilder:', error);
|
||||
res.status(500).send('Fehler beim Abrufen der Bilder');
|
||||
}
|
||||
};
|
||||
|
||||
exports.getImagesByPage = async (req, res) => {
|
||||
try {
|
||||
const { pageId } = req.params;
|
||||
const images = await Image.findAll({ where: { pageId } });
|
||||
res.status(200).json(images);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Abrufen der Bilder:', error);
|
||||
res.status(500).send('Fehler beim Abrufen der Bilder');
|
||||
}
|
||||
};
|
||||
|
||||
exports.getImageById = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const image = await Image.findByPk(id);
|
||||
if (image) {
|
||||
res.status(200).json(image);
|
||||
} else {
|
||||
res.status(404).send('Bild nicht gefunden');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Abrufen des Bildes:', error);
|
||||
res.status(500).send('Fehler beim Abrufen des Bildes');
|
||||
}
|
||||
};
|
||||
|
||||
exports.updateImage = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { title, description } = req.body;
|
||||
|
||||
const image = await Image.findByPk(id);
|
||||
|
||||
if (!image) {
|
||||
return res.status(404).json({ error: 'Bild nicht gefunden' });
|
||||
}
|
||||
|
||||
image.title = title;
|
||||
image.description = description;
|
||||
|
||||
await image.save();
|
||||
|
||||
res.json(image);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Aktualisieren des Bildes:', error);
|
||||
res.status(500).json({ error: 'Fehler beim Aktualisieren des Bildes' });
|
||||
}
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
const { MenuItem } = require('../models'); // Stellen Sie sicher, dass das Modell korrekt importiert wird
|
||||
const { MenuItem } = require('../models');
|
||||
const fetchMenuData = require('../utils/fetchMenuData');
|
||||
|
||||
exports.getMenuData = async (req, res) => {
|
||||
@@ -13,8 +13,15 @@ exports.getMenuData = async (req, res) => {
|
||||
exports.saveMenuData = async (req, res) => {
|
||||
try {
|
||||
const menuData = req.body;
|
||||
const adjustedMenuData = menuData.map(item => {
|
||||
item.parent_id = item.parent_id < 0 ? null : item.parent_id;
|
||||
return item;
|
||||
})
|
||||
.sort((a, b) => (a.parent_id === null ? -1 : 1) - (b.parent_id === null ? -1 : 1));
|
||||
await MenuItem.destroy({ where: {} });
|
||||
await MenuItem.bulkCreate(menuData, { include: [{ model: MenuItem, as: 'submenu' }] });
|
||||
for (const item of adjustedMenuData) {
|
||||
await MenuItem.create(item);
|
||||
}
|
||||
res.status(200).send('Menü-Daten erfolgreich gespeichert');
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Speichern der Menü-Daten:', error);
|
||||
|
||||
60
controllers/userController.js
Normal file
@@ -0,0 +1,60 @@
|
||||
const { User } = require('../models');
|
||||
|
||||
exports.getAllUsers = async (req, res) => {
|
||||
try {
|
||||
const users = await User.findAll();
|
||||
res.status(200).json(users);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: 'Error fetching users' });
|
||||
}
|
||||
};
|
||||
|
||||
exports.getUserById = async (req, res) => {
|
||||
try {
|
||||
const user = await User.findByPk(req.params.id);
|
||||
if (user) {
|
||||
res.status(200).json(user);
|
||||
} else {
|
||||
res.status(404).json({ message: 'User not found' });
|
||||
}
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: 'Error fetching user' });
|
||||
}
|
||||
};
|
||||
|
||||
exports.createUser = async (req, res) => {
|
||||
try {
|
||||
const user = await User.create(req.body);
|
||||
res.status(201).json(user);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: 'Error creating user' });
|
||||
}
|
||||
};
|
||||
|
||||
exports.updateUser = async (req, res) => {
|
||||
try {
|
||||
const user = await User.findByPk(req.params.id);
|
||||
if (user) {
|
||||
await user.update(req.body);
|
||||
res.status(200).json(user);
|
||||
} else {
|
||||
res.status(404).json({ message: 'User not found' });
|
||||
}
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: 'Error updating user' });
|
||||
}
|
||||
};
|
||||
|
||||
exports.deleteUser = async (req, res) => {
|
||||
try {
|
||||
const user = await User.findByPk(req.params.id);
|
||||
if (user) {
|
||||
await user.destroy();
|
||||
res.status(200).json({ message: 'User deleted successfully' });
|
||||
} else {
|
||||
res.status(404).json({ message: 'User not found' });
|
||||
}
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: 'Error deleting user' });
|
||||
}
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
const { Worship, EventPlace } = require('../models');
|
||||
const { Worship, EventPlace, Sequelize } = require('../models');
|
||||
const { Op } = require('sequelize'); // Importieren Sie die Operatoren von Sequelize
|
||||
|
||||
exports.getAllWorships = async (req, res) => {
|
||||
@@ -53,7 +53,14 @@ exports.getFilteredWorships = async (req, res) => {
|
||||
const where = {};
|
||||
|
||||
if (location && location !== '-1') {
|
||||
where.eventPlaceId = location;
|
||||
if (location.includes('|')) {
|
||||
const locationsArray = location.split('|');
|
||||
where.eventPlaceId = {
|
||||
[Sequelize.Op.in]: locationsArray
|
||||
}
|
||||
} else {
|
||||
where.eventPlaceId = location;
|
||||
}
|
||||
}
|
||||
|
||||
where.date = {
|
||||
|
||||
35
migrations/20240617191416-create-images-table.js
Normal file
@@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
up: async (queryInterface, Sequelize) => {
|
||||
await queryInterface.createTable('images', {
|
||||
id: {
|
||||
allowNull: false,
|
||||
primaryKey: true,
|
||||
type: Sequelize.UUID,
|
||||
defaultValue: Sequelize.UUIDV4
|
||||
},
|
||||
filename: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
title: {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
description: {
|
||||
type: Sequelize.TEXT,
|
||||
allowNull: true
|
||||
},
|
||||
uploadDate: {
|
||||
type: Sequelize.DATE,
|
||||
allowNull: false,
|
||||
defaultValue: Sequelize.NOW
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
down: async (queryInterface, Sequelize) => {
|
||||
await queryInterface.dropTable('images');
|
||||
}
|
||||
};
|
||||
15
migrations/20240617192740-create-images-table.js
Normal file
@@ -0,0 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
module.exports = {
|
||||
async up (queryInterface, Sequelize) {
|
||||
await queryInterface.addColumn('images', 'pageId', {
|
||||
type: Sequelize.INTEGER,
|
||||
allowNull: true,
|
||||
});
|
||||
},
|
||||
|
||||
async down (queryInterface, Sequelize) {
|
||||
await queryInterface.removeColumn('images', 'pageId');
|
||||
}
|
||||
};
|
||||
36
models/Image.js
Normal file
@@ -0,0 +1,36 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
|
||||
module.exports = (sequelize) => {
|
||||
const Image = sequelize.define('Image', {
|
||||
id: {
|
||||
type: DataTypes.UUID,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
primaryKey: true
|
||||
},
|
||||
filename: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
title: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true
|
||||
},
|
||||
description: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true
|
||||
},
|
||||
uploadDate: {
|
||||
type: DataTypes.DATE,
|
||||
defaultValue: DataTypes.NOW
|
||||
},
|
||||
pageId: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: true
|
||||
}
|
||||
}, {
|
||||
tableName: 'images',
|
||||
timestamps: false
|
||||
});
|
||||
|
||||
return Image;
|
||||
};
|
||||
238
package-lock.json
generated
@@ -8,6 +8,7 @@
|
||||
"name": "miriamgemeinde",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@iconoir/vue": "^7.7.0",
|
||||
"@tiptap/extension-bold": "^2.4.0",
|
||||
"@tiptap/extension-bullet-list": "^2.4.0",
|
||||
"@tiptap/extension-heading": "^2.4.0",
|
||||
@@ -21,7 +22,6 @@
|
||||
"@tiptap/extension-underline": "^2.4.0",
|
||||
"@tiptap/starter-kit": "^2.4.0",
|
||||
"@tiptap/vue-3": "^2.4.0",
|
||||
"@vueup/vue-quill": "^1.2.0",
|
||||
"axios": "^1.7.2",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"body-parser": "^1.20.2",
|
||||
@@ -32,10 +32,12 @@
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.19.2",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"mysql2": "^3.10.1",
|
||||
"nodemon": "^3.1.3",
|
||||
"sequelize": "^6.37.3",
|
||||
"sequelize-cli": "^6.6.2",
|
||||
"uuid": "^10.0.0",
|
||||
"vm-browserify": "^1.1.2",
|
||||
"vue": "^3.2.13",
|
||||
"vue-multiselect": "^3.0.0",
|
||||
@@ -1958,6 +1960,52 @@
|
||||
"deprecated": "Use @eslint/object-schema instead",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@iconoir/vue": {
|
||||
"version": "7.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@iconoir/vue/-/vue-7.7.0.tgz",
|
||||
"integrity": "sha512-EvJK80DUGpCFd2MZsC5K6duv/h4zafruBSQYV9F5b+kJOrdWNCHxEHpwbUwjsI35GpSzMD0N2jAR86s3YTsMmw==",
|
||||
"dependencies": {
|
||||
"vue-demi": "^0.14.6"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/iconoir"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": ">=1.0.0-rc.1",
|
||||
"vue": "^2.6.11 || >=3.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@iconoir/vue/node_modules/vue-demi": {
|
||||
"version": "0.14.8",
|
||||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz",
|
||||
"integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==",
|
||||
"hasInstallScript": true,
|
||||
"bin": {
|
||||
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": "^1.0.0-rc.1",
|
||||
"vue": "^3.0.0-0 || ^2.6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/cliui": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||
@@ -3741,18 +3789,6 @@
|
||||
"integrity": "sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@vueup/vue-quill": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueup/vue-quill/-/vue-quill-1.2.0.tgz",
|
||||
"integrity": "sha512-kd5QPSHMDpycklojPXno2Kw2JSiKMYduKYQckTm1RJoVDA557MnyUXgcuuDpry4HY/Rny9nGNcK+m3AHk94wag==",
|
||||
"dependencies": {
|
||||
"quill": "^1.3.7",
|
||||
"quill-delta": "^4.2.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.2.41"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/ast": {
|
||||
"version": "1.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz",
|
||||
@@ -4114,6 +4150,11 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/append-field": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
|
||||
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="
|
||||
},
|
||||
"node_modules/arch": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz",
|
||||
@@ -4657,8 +4698,7 @@
|
||||
"node_modules/buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
|
||||
},
|
||||
"node_modules/buffer-xor": {
|
||||
"version": "1.0.3",
|
||||
@@ -4666,6 +4706,17 @@
|
||||
"integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/busboy": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
||||
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
|
||||
"dependencies": {
|
||||
"streamsearch": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.16.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
@@ -5143,6 +5194,47 @@
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
|
||||
},
|
||||
"node_modules/concat-stream": {
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
|
||||
"integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
|
||||
"engines": [
|
||||
"node >= 0.8"
|
||||
],
|
||||
"dependencies": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^2.2.2",
|
||||
"typedarray": "^0.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/concat-stream/node_modules/readable-stream": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/concat-stream/node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"node_modules/concat-stream/node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/config-chain": {
|
||||
"version": "1.1.13",
|
||||
"resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
|
||||
@@ -5280,8 +5372,7 @@
|
||||
"node_modules/core-util-is": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
|
||||
},
|
||||
"node_modules/cors": {
|
||||
"version": "2.8.5",
|
||||
@@ -7158,11 +7249,6 @@
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fast-diff": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
|
||||
"integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w=="
|
||||
},
|
||||
"node_modules/fast-glob": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
|
||||
@@ -8373,8 +8459,7 @@
|
||||
"node_modules/isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
|
||||
},
|
||||
"node_modules/isexe": {
|
||||
"version": "2.0.0",
|
||||
@@ -8822,11 +8907,6 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"node_modules/lodash.clonedeep": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
|
||||
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ=="
|
||||
},
|
||||
"node_modules/lodash.debounce": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||
@@ -8849,11 +8929,6 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
|
||||
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
|
||||
},
|
||||
"node_modules/lodash.isequal": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
||||
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="
|
||||
},
|
||||
"node_modules/lodash.isinteger": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
|
||||
@@ -9463,7 +9538,6 @@
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
@@ -9490,7 +9564,6 @@
|
||||
"version": "0.5.6",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.6"
|
||||
},
|
||||
@@ -9537,6 +9610,23 @@
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/multer": {
|
||||
"version": "1.4.5-lts.1",
|
||||
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz",
|
||||
"integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==",
|
||||
"dependencies": {
|
||||
"append-field": "^1.0.0",
|
||||
"busboy": "^1.0.0",
|
||||
"concat-stream": "^1.5.2",
|
||||
"mkdirp": "^0.5.4",
|
||||
"object-assign": "^4.1.1",
|
||||
"type-is": "^1.6.4",
|
||||
"xtend": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/multicast-dns": {
|
||||
"version": "7.2.5",
|
||||
"resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz",
|
||||
@@ -10995,8 +11085,7 @@
|
||||
"node_modules/process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"dev": true
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
||||
},
|
||||
"node_modules/progress": {
|
||||
"version": "2.0.3",
|
||||
@@ -11358,16 +11447,6 @@
|
||||
"quill-delta": "^3.6.2"
|
||||
}
|
||||
},
|
||||
"node_modules/quill-delta": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-4.2.2.tgz",
|
||||
"integrity": "sha512-qjbn82b/yJzOjstBgkhtBjN2TNK+ZHP/BgUQO+j6bRhWQQdmj2lH6hXG7+nwwLF41Xgn//7/83lxs9n2BkTtTg==",
|
||||
"dependencies": {
|
||||
"fast-diff": "1.2.0",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"lodash.isequal": "^4.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/quill/node_modules/fast-diff": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz",
|
||||
@@ -11972,6 +12051,14 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/sequelize/node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/serialize-javascript": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
|
||||
@@ -12290,6 +12377,15 @@
|
||||
"websocket-driver": "^0.7.4"
|
||||
}
|
||||
},
|
||||
"node_modules/sockjs/node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
@@ -12436,6 +12532,14 @@
|
||||
"readable-stream": "^3.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/streamsearch": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
|
||||
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
@@ -12939,6 +13043,11 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/typedarray": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
|
||||
},
|
||||
"node_modules/uc.micro": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
|
||||
@@ -13064,8 +13173,7 @@
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||
},
|
||||
"node_modules/utila": {
|
||||
"version": "0.4.0",
|
||||
@@ -13082,9 +13190,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
|
||||
"integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
@@ -13752,9 +13864,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-server/node_modules/ws": {
|
||||
"version": "8.17.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz",
|
||||
"integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==",
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
@@ -13995,9 +14107,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "7.5.9",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
|
||||
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
|
||||
"version": "7.5.10",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
|
||||
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8.3.0"
|
||||
@@ -14015,6 +14127,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
||||
"engines": {
|
||||
"node": ">=0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@iconoir/vue": "^7.7.0",
|
||||
"@tiptap/extension-bold": "^2.4.0",
|
||||
"@tiptap/extension-bullet-list": "^2.4.0",
|
||||
"@tiptap/extension-heading": "^2.4.0",
|
||||
@@ -21,7 +22,6 @@
|
||||
"@tiptap/extension-underline": "^2.4.0",
|
||||
"@tiptap/starter-kit": "^2.4.0",
|
||||
"@tiptap/vue-3": "^2.4.0",
|
||||
"@vueup/vue-quill": "^1.2.0",
|
||||
"axios": "^1.7.2",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"body-parser": "^1.20.2",
|
||||
@@ -32,10 +32,12 @@
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.19.2",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"mysql2": "^3.10.1",
|
||||
"nodemon": "^3.1.3",
|
||||
"sequelize": "^6.37.3",
|
||||
"sequelize-cli": "^6.6.2",
|
||||
"uuid": "^10.0.0",
|
||||
"vm-browserify": "^1.1.2",
|
||||
"vue": "^3.2.13",
|
||||
"vue-multiselect": "^3.0.0",
|
||||
|
||||
BIN
public/images/uploads/44390f72-e407-4796-8cc7-a5253dae4ebd.jpg
Normal file
|
After Width: | Height: | Size: 38 KiB |
12
routes/image.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const { uploadImage, saveImageDetails, getImages, getImagesByPage, getImageById, updateImage } = require('../controllers/imageController');
|
||||
const authMiddleware = require('../middleware/authMiddleware')
|
||||
|
||||
router.post('/', authMiddleware, uploadImage, saveImageDetails);
|
||||
router.get('/', authMiddleware, getImages);
|
||||
router.get('/page/:pageId', getImagesByPage);
|
||||
router.get('/:id', getImageById);
|
||||
router.put('/:id', authMiddleware, updateImage);
|
||||
|
||||
module.exports = router;
|
||||
12
routes/users.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const { getAllUsers, createUser, updateUser, deleteUser, getUserById } = require('../controllers/userController');
|
||||
const authMiddleware = require('../middleware/authMiddleware');
|
||||
|
||||
router.get('/', authMiddleware, getAllUsers);
|
||||
router.get('/:id', authMiddleware, getUserById);
|
||||
router.post('/', authMiddleware, createUser);
|
||||
router.put('/:id', authMiddleware, updateUser);
|
||||
router.delete('/:id', authMiddleware, deleteUser);
|
||||
|
||||
module.exports = router;
|
||||
@@ -12,6 +12,8 @@ const eventRouter = require('./routes/event');
|
||||
const menuDataRouter = require('./routes/menuData');
|
||||
const worshipRouter = require('./routes/worships');
|
||||
const pageRoutes = require('./routes/pages');
|
||||
const userRoutes = require('./routes/users');
|
||||
const imageRoutes = require('./routes/image');
|
||||
|
||||
const app = express();
|
||||
const PORT = 3000;
|
||||
@@ -29,7 +31,8 @@ app.use('/api/events', eventRouter);
|
||||
app.use('/api/menu-data', menuDataRouter);
|
||||
app.use('/api/worships', worshipRouter);
|
||||
app.use('/api/page-content', pageRoutes);
|
||||
|
||||
app.use('/api/users', userRoutes);
|
||||
app.use('/api/image', imageRoutes);
|
||||
sequelize.sync().then(() => {
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Server läuft auf Port ${PORT}`);
|
||||
|
||||
BIN
src/assets/icons/Bold_Italic_Underline.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
src/assets/icons/format_list_bulleted.png
Normal file
|
After Width: | Height: | Size: 962 B |
BIN
src/assets/icons/format_list_numbered.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/icons/image.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/assets/icons/strikethrough.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
src/assets/icons/table.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<footer class="footer">
|
||||
<div class="left-links">
|
||||
<router-link class="login-link" to="/login" v-if="!isLoggedIn">Login</router-link>
|
||||
<router-link class="login-link" to="/auth/login" v-if="!isLoggedIn">Login</router-link>
|
||||
<a v-if="isLoggedIn" @click="logout" class="logout-link">Logout</a>
|
||||
</div>
|
||||
<div class="right-links">
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<script>
|
||||
import NavbarComponent from './NavbarComponent.vue';
|
||||
import { mapActions } from 'vuex';
|
||||
import router from '@/router'; // Importieren Sie den Router
|
||||
|
||||
export default {
|
||||
name: 'HeaderComponent',
|
||||
@@ -19,8 +20,16 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['loadMenuData']),
|
||||
reloadMenu() {
|
||||
this.loadMenuData();
|
||||
async reloadMenu() {
|
||||
await this.loadMenuData();
|
||||
this.$router.push({ path: '/' }); // Zurück zur Startseite oder eine andere Seite, um sicherzustellen, dass der Router neu geladen wird
|
||||
const routes = this.$store.state.menuData.map(item => {
|
||||
return {
|
||||
path: item.link,
|
||||
component: () => import(`../components/${item.component}.vue`)
|
||||
};
|
||||
});
|
||||
routes.forEach(route => router.addRoute(route)); // Neue Routen hinzufügen
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -7,12 +7,19 @@
|
||||
<label for="location-select">Bitte wählen Sie den Ort für den Gottestdienst aus:</label>
|
||||
</div>
|
||||
<div>
|
||||
<select id="location-select" v-model="selectedLocation">
|
||||
<option :value="-1">Alle Orte</option>
|
||||
<option v-for="location in locations" :key="location.id" :value="location.id">
|
||||
{{ location.name }}
|
||||
</option>
|
||||
</select>
|
||||
<multiselect
|
||||
v-model="selectedLocations"
|
||||
:options="locations"
|
||||
:multiple="true"
|
||||
:close-on-select="false"
|
||||
:clear-on-select="false"
|
||||
:preserve-search="true"
|
||||
:preselect-first="false"
|
||||
label="name"
|
||||
track-by="id"
|
||||
placeholder="Orte auswählen"
|
||||
>
|
||||
</multiselect>
|
||||
</div>
|
||||
<div>
|
||||
<button @click="confirmWorshipConfiguration">Bestätigen</button>
|
||||
@@ -26,14 +33,19 @@
|
||||
<script>
|
||||
import { ref } from 'vue';
|
||||
import axios from '@/axios';
|
||||
import Multiselect from 'vue-multiselect';
|
||||
import 'vue-multiselect/dist/vue-multiselect.css';
|
||||
|
||||
export default {
|
||||
name: 'WorshipDialog',
|
||||
components: {
|
||||
Multiselect
|
||||
},
|
||||
emits: ['confirm'],
|
||||
setup(props, { emit }) {
|
||||
const isOpen = ref(false);
|
||||
const locations = ref([]);
|
||||
const selectedLocation = ref(-1);
|
||||
const selectedLocations = ref([]);
|
||||
|
||||
const openWorshipDialog = () => {
|
||||
isOpen.value = true;
|
||||
@@ -54,14 +66,15 @@ export default {
|
||||
};
|
||||
|
||||
const confirmWorshipConfiguration = () => {
|
||||
emit('confirm', selectedLocation.value);
|
||||
const selectedLocationIds = selectedLocations.value.map(location => location.id).join('|');
|
||||
emit('confirm', selectedLocationIds);
|
||||
closeWorshipDialog();
|
||||
};
|
||||
|
||||
return {
|
||||
isOpen,
|
||||
locations,
|
||||
selectedLocation,
|
||||
selectedLocations,
|
||||
openWorshipDialog,
|
||||
closeWorshipDialog,
|
||||
confirmWorshipConfiguration,
|
||||
@@ -88,4 +101,8 @@ export default {
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
.multiselect {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<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> -
|
||||
{{ !worship.neighborInvitation ? worship.title : '' }}
|
||||
{{ !worship.neighborInvitation ? worship.title : `Gottesdienst in ${worship.eventPlace.name}` }}
|
||||
</h3>
|
||||
<div v-if="worship.organizer">Gestaltung: {{ worship.organizer }}</div>
|
||||
<div v-if="worship.collection">Kollekte: {{ worship.collection }}</div>
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<h2></h2>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AdminWorshipService',
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -11,11 +11,18 @@
|
||||
<button @click="editor.chain().focus().toggleHeading({ level: 1 }).run()">H1</button>
|
||||
<button @click="editor.chain().focus().toggleHeading({ level: 2 }).run()">H2</button>
|
||||
<button @click="editor.chain().focus().toggleHeading({ level: 3 }).run()">H3</button>
|
||||
<button @click="editor.chain().focus().toggleBold().run()">Fett</button>
|
||||
<button @click="editor.chain().focus().toggleItalic().run()">Kursiv</button>
|
||||
<button @click="editor.chain().focus().toggleUnderline().run()">Unterstrichen</button>
|
||||
<button @click="editor.chain().focus().toggleBold().run()" width="24" height="24">
|
||||
<BoldIcon width="24" height="24" />
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleItalic().run()">
|
||||
<ItalicIcon width="24" height="24" />
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleUnderline().run()">
|
||||
<UnderlineIcon width="24" height="24" />
|
||||
</button>
|
||||
<button @click="editor.chain().focus().toggleStrike().run()">Durchgestrichen</button>
|
||||
<button @click="editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run()">Tabelle</button>
|
||||
<button
|
||||
@click="editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run()">Tabelle</button>
|
||||
<button @click="editor.chain().focus().toggleBulletList().run()">Liste</button>
|
||||
<button @click="editor.chain().focus().toggleOrderedList().run()">Nummerierte Liste</button>
|
||||
</div>
|
||||
@@ -62,12 +69,16 @@ import OrderedList from '@tiptap/extension-ordered-list';
|
||||
import Heading from '@tiptap/extension-heading';
|
||||
import { CustomTableCell, CustomTableHeader } from '../../extensions/CustomTableCell';
|
||||
import WorshipDialog from '@/components/WorshipDialog.vue';
|
||||
import { BoldIcon, ItalicIcon, UnderlineIcon } from '@/icons';
|
||||
|
||||
export default {
|
||||
name: 'EditPagesComponent',
|
||||
components: {
|
||||
EditorContent,
|
||||
WorshipDialog,
|
||||
BoldIcon,
|
||||
ItalicIcon,
|
||||
UnderlineIcon
|
||||
},
|
||||
setup() {
|
||||
const store = useStore();
|
||||
@@ -110,7 +121,7 @@ export default {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const fetchPages = async () => {
|
||||
try {
|
||||
const response = await axios.get('/menu-data');
|
||||
@@ -179,9 +190,10 @@ export default {
|
||||
worshipDialog.value.openWorshipDialog();
|
||||
};
|
||||
|
||||
const insertWorshipList = (configuration) => {
|
||||
const insertWorshipList = (selectedLocations) => {
|
||||
if (editor.value) {
|
||||
editor.value.chain().focus().insertContent(`{{ worshipslist:location=${configuration},order:"date asc" }}`).run();
|
||||
const configuration = `location=${selectedLocations},order:"date asc"`;
|
||||
editor.value.chain().focus().insertContent(`{{ worshipslist:${configuration} }}`).run();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -215,7 +227,7 @@ export default {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.toolbar button {
|
||||
.toolbar button {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
@@ -242,4 +254,19 @@ export default {
|
||||
.ql-editor {
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
.edit-pages div>button {
|
||||
border: none;
|
||||
padding: 0.25em;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
.button-icon {
|
||||
background-image: url(@/assets/icons/Bold_Italic_Underline.png);
|
||||
background-position: left top;
|
||||
background-size: 10px 24px;
|
||||
background-repeat: no-repeat;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
</style>
|
||||
|
||||
134
src/content/admin/ImageUpload.vue
Normal file
@@ -0,0 +1,134 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Bild hochladen</h1>
|
||||
<form @submit.prevent="uploadImage">
|
||||
<div>
|
||||
<label for="title">Titel</label>
|
||||
<input type="text" id="title" v-model="title" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="description">Beschreibung</label>
|
||||
<textarea id="description" v-model="description"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label for="image">Bild</label>
|
||||
<input type="file" id="image" @change="onFileChange" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="page">Seite</label>
|
||||
<select id="page" v-model="selectedPage">
|
||||
<option value="">Keine Seite</option>
|
||||
<option v-for="page in pages" :key="page.id" :value="page.id">{{ page.title }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit">Hochladen</button>
|
||||
</form>
|
||||
|
||||
<div v-if="images.length">
|
||||
<h2>Hochgeladene Bilder</h2>
|
||||
<div v-for="image in images" :key="image.id" class="uploaded-image">
|
||||
<img :src="`/images/uploads/${image.filename}`" :alt="image.title" width="100" />
|
||||
<input type="text" v-model="image.title" @change="updateImage(image)" />
|
||||
<textarea v-model="image.description" @change="updateImage(image)"></textarea>
|
||||
<p>{{ formatDate(image.uploadDate) }} {{ formatTimeFromDate(image.uploadDate) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from '../../axios';
|
||||
import { formatDate, formatTimeFromDate } from '@/utils/strings'
|
||||
|
||||
export default {
|
||||
name: 'ImageUpload',
|
||||
data() {
|
||||
return {
|
||||
title: '',
|
||||
description: '',
|
||||
image: null,
|
||||
selectedPage: '',
|
||||
pages: [],
|
||||
images: []
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
formatDate,
|
||||
formatTimeFromDate,
|
||||
onFileChange(e) {
|
||||
this.image = e.target.files[0];
|
||||
},
|
||||
async uploadImage() {
|
||||
const formData = new FormData();
|
||||
formData.append('title', this.title);
|
||||
formData.append('description', this.description);
|
||||
formData.append('image', this.image);
|
||||
formData.append('pageId', this.selectedPage);
|
||||
|
||||
try {
|
||||
await axios.post('/image/', formData);
|
||||
this.fetchImages();
|
||||
this.resetForm();
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Hochladen des Bildes:', error);
|
||||
}
|
||||
},
|
||||
async fetchImages() {
|
||||
try {
|
||||
const response = await axios.get('/image');
|
||||
this.images = response.data;
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Abrufen der Bilder:', error);
|
||||
}
|
||||
},
|
||||
async fetchPages() {
|
||||
try {
|
||||
const response = await axios.get('/image/pages');
|
||||
this.pages = response.data;
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Abrufen der Seiten:', error);
|
||||
}
|
||||
},
|
||||
async updateImage(image) {
|
||||
try {
|
||||
await axios.put(`/image/${image.id}`, {
|
||||
title: image.title,
|
||||
description: image.description
|
||||
});
|
||||
this.fetchImages();
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Aktualisieren des Bildes:', error);
|
||||
}
|
||||
},
|
||||
resetForm() {
|
||||
this.title = '';
|
||||
this.description = '';
|
||||
this.image = null;
|
||||
this.selectedPage = '';
|
||||
document.getElementById('image').value = null;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.fetchImages();
|
||||
this.fetchPages();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
form div {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.uploaded-image {
|
||||
display: inline-block;
|
||||
margin: 0 0 0.5em 0.5em;
|
||||
border: 1px solid #e0e0e0;
|
||||
padding: 10px;
|
||||
}
|
||||
.uploaded-image input,
|
||||
.uploaded-image textarea {
|
||||
width: 100%;
|
||||
margin: 5px 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,19 +1,63 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Administration</h1>
|
||||
<p>Hier kommt eine Navigation hin.</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'DefaultComponent'
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
div {
|
||||
padding: 20px;
|
||||
<div>
|
||||
<h1>Seitenpflege</h1>
|
||||
<p>Herzlich Willkommen. Auf diesen Seiten können Sie die Inhalte der Webseiten pflegen.</p>
|
||||
<ul>
|
||||
<li v-for="item in adminSubmenu" :key="item.id">
|
||||
<router-link :to="item.link">{{ item.name }}</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from "../../axios";
|
||||
import { ref, onMounted } from 'vue';
|
||||
|
||||
export default {
|
||||
name: 'DefaultComponent',
|
||||
setup() {
|
||||
const adminSubmenu = ref([]);
|
||||
|
||||
const fetchMenuData = async () => {
|
||||
try {
|
||||
const response = await axios.get('/menu-data');
|
||||
const menuData = response.data;
|
||||
|
||||
// Suche nach dem Admin-Submenü
|
||||
const adminMenu = menuData.find(item => item.name === 'Admin');
|
||||
if (adminMenu) {
|
||||
adminSubmenu.value = adminMenu.submenu;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Abrufen der Menü-Daten:', error);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
fetchMenuData();
|
||||
});
|
||||
|
||||
return {
|
||||
adminSubmenu
|
||||
};
|
||||
}
|
||||
</style>
|
||||
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
div {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<button @click="saveMenuData">Speichern</button>
|
||||
</div>
|
||||
<div v-if="selectedMenuItem" class="edit-form">
|
||||
<h2>Menüpunkt bearbeiten</h2>
|
||||
<h2>{{ isEditMode ? 'Menüpunkt bearbeiten' : 'Menüpunkt erstellen' }}</h2>
|
||||
<form @submit.prevent="saveMenuData">
|
||||
<label for="name">Name</label>
|
||||
<input id="name" v-model="selectedMenuItem.name" placeholder="Name" />
|
||||
@@ -18,8 +18,7 @@
|
||||
<input id="page-title" v-model="selectedMenuItem.pageTitle" placeholder="Seitenname" />
|
||||
|
||||
<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.order_id" placeholder="Order ID" type="number" class="order-id" />
|
||||
|
||||
<div class="checkbox-container">
|
||||
<label>
|
||||
@@ -33,8 +32,8 @@
|
||||
</div>
|
||||
|
||||
<label for="parent-id">Elternelement</label>
|
||||
<select id="parent-id" v-model="selectedMenuItem.parent_id">
|
||||
<option value="">Ohne Elternelement</option>
|
||||
<select id="parent-id" v-model.number="selectedMenuItem.parent_id">
|
||||
<option value="-1">Ohne Elternelement</option>
|
||||
<option v-for="item in flattenedMenuData" :key="item.id" :value="item.id">
|
||||
<span v-html="getIndentedName(item)"></span>
|
||||
</option>
|
||||
@@ -42,6 +41,9 @@
|
||||
|
||||
<label for="component">Vue-Komponente</label>
|
||||
<input id="component" v-model="selectedMenuItem.component" placeholder="Vue-Komponente" />
|
||||
|
||||
<button type="button" @click="resetForm">Neuen Menüpunkt erstellen</button>
|
||||
<button type="submit">Speichern</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="tree-view">
|
||||
@@ -49,7 +51,7 @@
|
||||
<li v-for="menuItem in sortedMenuData" :key="menuItem.id">
|
||||
<div class="menu-item">
|
||||
<span @click="selectMenuItem(menuItem)">
|
||||
{{ menuItem.name }} (ID: {{ menuItem.orderId }})
|
||||
{{ menuItem.name }} (ID: {{ menuItem.order_id }})
|
||||
</span>
|
||||
<div class="action-buttons">
|
||||
<button @click="addSubmenu(menuItem)" class="action-button">Untermenü hinzufügen</button>
|
||||
@@ -60,10 +62,10 @@
|
||||
<li v-for="submenuItem in sortedSubmenu(menuItem)" :key="submenuItem.id">
|
||||
<div class="menu-item">
|
||||
<span @click="selectMenuItem(submenuItem)">
|
||||
{{ submenuItem.name }} (ID: {{ submenuItem.orderId }})
|
||||
{{ submenuItem.name }} (ID: {{ submenuItem.order_id }})
|
||||
</span>
|
||||
<div class="action-buttons">
|
||||
<button @click="addSubmenu(menuItem)" class="action-button">Untermenü hinzufügen</button>
|
||||
<button @click="addSubmenu(submenuItem)" class="action-button">Untermenü hinzufügen</button>
|
||||
<button @click="removeSubmenu(menuItem, submenuItem)" class="action-button">Löschen</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -78,12 +80,15 @@
|
||||
<script>
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import axios from '../../axios';
|
||||
import { useStore } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: 'MenuManagement',
|
||||
setup() {
|
||||
const store = useStore();
|
||||
const menuData = ref([]);
|
||||
const selectedMenuItem = ref(null);
|
||||
const isEditMode = ref(false);
|
||||
|
||||
const fetchMenuData = async () => {
|
||||
try {
|
||||
@@ -104,9 +109,14 @@ export default {
|
||||
}
|
||||
};
|
||||
|
||||
const flattenMenuData = (data, parentId = null) => {
|
||||
const flattenMenuData = (data) => {
|
||||
return data.reduce((acc, item) => {
|
||||
const newItem = { ...item, parent_id: parentId, page_title: item.pageTitle };
|
||||
const newItem = {
|
||||
...item,
|
||||
page_title: item.pageTitle,
|
||||
show_in_menu: item.showInMenu,
|
||||
requires_auth: item.requiresAuth,
|
||||
};
|
||||
const { submenu, ...rest } = newItem;
|
||||
acc.push(rest);
|
||||
if (submenu && submenu.length) {
|
||||
@@ -116,6 +126,17 @@ export default {
|
||||
}, []);
|
||||
};
|
||||
|
||||
const flattenMenuStructure = (menuItems, indent = 0) => {
|
||||
return menuItems.reduce((acc, item) => {
|
||||
acc.push({ ...item, indent });
|
||||
if (item.submenu && item.submenu.length) {
|
||||
acc.push(...flattenMenuStructure(item.submenu, item.id, indent + 1));
|
||||
}
|
||||
console.log
|
||||
return acc;
|
||||
}, []);
|
||||
};
|
||||
|
||||
const addMenuItem = () => {
|
||||
const newItem = {
|
||||
name: '',
|
||||
@@ -124,12 +145,13 @@ export default {
|
||||
pageTitle: '',
|
||||
showInMenu: true,
|
||||
requiresAuth: false,
|
||||
orderId: 0,
|
||||
order_id: 0,
|
||||
submenu: [],
|
||||
parent_id: null,
|
||||
parent_id: 0,
|
||||
};
|
||||
menuData.value.push(newItem);
|
||||
selectMenuItem(newItem);
|
||||
isEditMode.value = false;
|
||||
};
|
||||
|
||||
const addSubmenu = (menuItem) => {
|
||||
@@ -140,11 +162,11 @@ export default {
|
||||
pageTitle: '',
|
||||
showInMenu: true,
|
||||
requiresAuth: false,
|
||||
orderId: 0,
|
||||
parent_id: menuItem.id,
|
||||
order_id: 0,
|
||||
};
|
||||
menuItem.submenu.push(newSubItem);
|
||||
selectMenuItem(newSubItem);
|
||||
isEditMode.value = false;
|
||||
};
|
||||
|
||||
const removeMenuItem = (menuItem) => {
|
||||
@@ -165,21 +187,35 @@ export default {
|
||||
|
||||
const selectMenuItem = (menuItem) => {
|
||||
selectedMenuItem.value = menuItem;
|
||||
isEditMode.value = true;
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
selectedMenuItem.value = null;
|
||||
isEditMode.value = false;
|
||||
};
|
||||
|
||||
const sortedMenuData = computed(() => {
|
||||
return [...menuData.value].sort((a, b) => a.orderId - b.orderId);
|
||||
return [...menuData.value].sort((a, b) => a.order_id - b.order_id);
|
||||
});
|
||||
|
||||
const sortedSubmenu = (menuItem) => {
|
||||
return menuItem.submenu.slice().sort((a, b) => a.orderId - b.orderId);
|
||||
return menuItem.submenu.slice().sort((a, b) => a.order_id - b.order_id);
|
||||
};
|
||||
|
||||
const getIndentedName = (item) => {
|
||||
return ' '.repeat(item.indent * 2) + item.name;
|
||||
};
|
||||
|
||||
onMounted(fetchMenuData);
|
||||
const flattenedMenuData = computed(() => {
|
||||
const menuStruct = flattenMenuStructure(store.state.menuData);
|
||||
console.log(menuStruct);
|
||||
return menuStruct;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
fetchMenuData();
|
||||
});
|
||||
|
||||
return {
|
||||
menuData,
|
||||
@@ -194,6 +230,9 @@ export default {
|
||||
removeSubmenu,
|
||||
selectMenuItem,
|
||||
getIndentedName,
|
||||
isEditMode,
|
||||
resetForm,
|
||||
flattenedMenuData,
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -277,4 +316,4 @@ export default {
|
||||
.edit-form button {
|
||||
margin-top: 5px;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
153
src/content/admin/UserAdministration.vue
Normal file
@@ -0,0 +1,153 @@
|
||||
<template>
|
||||
<div class="user-administration">
|
||||
<h1>Benutzerverwaltung</h1>
|
||||
|
||||
<h2>{{ formTitle }}</h2>
|
||||
<form @submit.prevent="saveUser">
|
||||
<label for="name">Name:</label>
|
||||
<input id="name" v-model="currentUser.name" required />
|
||||
|
||||
<label for="email">Email:</label>
|
||||
<input id="email" v-model="currentUser.email" type="email" required />
|
||||
|
||||
<label for="password">Passwort:</label>
|
||||
<input id="password" v-model="currentUser.password" type="password" :required="isCreating" />
|
||||
|
||||
<div>
|
||||
<label for="active">Aktiv:</label>
|
||||
<input id="active" v-model="currentUser.active" type="checkbox" />
|
||||
</div>
|
||||
|
||||
<button type="submit">{{ isCreating ? 'Erstellen' : 'Aktualisieren' }}</button>
|
||||
</form>
|
||||
|
||||
<button v-if="!isCreating" @click="resetForm">Zurück zu Benutzer erstellen</button>
|
||||
|
||||
<div v-if="users.length">
|
||||
<h2>Vorhandene Benutzer</h2>
|
||||
<ul>
|
||||
<li v-for="user in users" :key="user.id" @click="editUser(user)">
|
||||
{{ user.name }} ({{ user.email }})
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from '@/axios';
|
||||
|
||||
export default {
|
||||
name: 'UserAdministration',
|
||||
data() {
|
||||
return {
|
||||
users: [],
|
||||
currentUser: {
|
||||
name: '',
|
||||
email: '',
|
||||
password: '',
|
||||
active: false
|
||||
},
|
||||
isCreating: true
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
formTitle() {
|
||||
return this.isCreating ? 'Benutzer erstellen' : 'Benutzer bearbeiten';
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async fetchUsers() {
|
||||
try {
|
||||
const response = await axios.get('/users');
|
||||
this.users = response.data;
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Abrufen der Benutzer:', error);
|
||||
}
|
||||
},
|
||||
async saveUser() {
|
||||
if (this.isCreating) {
|
||||
await this.createUser();
|
||||
} else {
|
||||
await this.updateUser();
|
||||
}
|
||||
this.resetForm();
|
||||
this.fetchUsers();
|
||||
},
|
||||
async createUser() {
|
||||
try {
|
||||
await axios.post('/users', this.currentUser);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Erstellen des Benutzers:', error);
|
||||
}
|
||||
},
|
||||
async updateUser() {
|
||||
try {
|
||||
await axios.put(`/users/${this.currentUser.id}`, this.currentUser);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Aktualisieren des Benutzers:', error);
|
||||
}
|
||||
},
|
||||
editUser(user) {
|
||||
this.currentUser = { ...user, password: '' };
|
||||
this.isCreating = false;
|
||||
},
|
||||
resetForm() {
|
||||
this.currentUser = {
|
||||
name: '',
|
||||
email: '',
|
||||
password: '',
|
||||
active: false
|
||||
};
|
||||
this.isCreating = true;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.fetchUsers();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.user-administration {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.user-administration h1,
|
||||
.user-administration h2 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.user-administration form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.user-administration label {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.user-administration input[type="text"],
|
||||
.user-administration input[type="email"],
|
||||
.user-administration input[type="password"] {
|
||||
padding: 5px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.user-administration ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.user-administration li {
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.user-administration li:hover {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -57,7 +57,7 @@ export default {
|
||||
localStorage.setItem('token', token);
|
||||
this.login(data.user);
|
||||
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
|
||||
this.$router.push('/admin');
|
||||
this.$router.push('/admin/index');
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
this.showDialog('Fehler', error.response.data.message);
|
||||
24
src/content/worship/NeighborhoodContent.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<div class="some-page">
|
||||
<ContentComponent :link="currentLink" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ContentComponent from '@/components/ContentComponent.vue';
|
||||
|
||||
export default {
|
||||
name: 'SomePage',
|
||||
components: {
|
||||
ContentComponent,
|
||||
},
|
||||
computed: {
|
||||
currentLink() {
|
||||
return this.$route.path;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
8
src/icons.js
Normal file
@@ -0,0 +1,8 @@
|
||||
// src/icons.js
|
||||
import { Bold, Italic, Underline } from '@iconoir/vue';
|
||||
|
||||
export {
|
||||
Bold as BoldIcon,
|
||||
Italic as ItalicIcon,
|
||||
Underline as UnderlineIcon,
|
||||
};
|
||||
@@ -16,20 +16,26 @@ function generateRoutesFromMenu(menu) {
|
||||
if (item.link === '/admin/edit-pages') {
|
||||
return;
|
||||
}
|
||||
let route = {
|
||||
path: item.link,
|
||||
meta: { requiresAuth: item.requiresAuth || false },
|
||||
components: {
|
||||
default: loadComponent(item.component),
|
||||
rightColumn: loadComponent('ImageContent')
|
||||
}
|
||||
};
|
||||
|
||||
let route = null;
|
||||
if (item.link && item.link !== '') {
|
||||
route = {
|
||||
path: item.link,
|
||||
meta: { requiresAuth: item.requiresAuth || false },
|
||||
components: {
|
||||
default: loadComponent(item.component),
|
||||
rightColumn: loadComponent('ImageContent')
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (item.submenu && item.submenu.length > 0) {
|
||||
let children = generateRoutesFromMenu(item.submenu);
|
||||
routes.push(...children);
|
||||
}
|
||||
routes.push(route);
|
||||
if (route) {
|
||||
routes.push(route);
|
||||
}
|
||||
});
|
||||
return routes;
|
||||
}
|
||||
|
||||
@@ -9,3 +9,11 @@ export function formatDate(date) {
|
||||
const options = { year: 'numeric', month: '2-digit', day: '2-digit' };
|
||||
return new Date(date).toLocaleDateString('de-DE', options);
|
||||
}
|
||||
|
||||
export function formatTimeFromDate(dateString) {
|
||||
const date = new Date(dateString);
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
return `${hours}:${minutes}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ function buildMenuStructure(menuItems) {
|
||||
component: item.component,
|
||||
showInMenu: item.show_in_menu,
|
||||
requiresAuth: item.requires_auth,
|
||||
orderId: item.order_id,
|
||||
order_id: item.order_id,
|
||||
pageTitle: item.page_title,
|
||||
submenu: []
|
||||
};
|
||||
|
||||