Refactor database configuration and enhance server settings: Update database connection logic to utilize environment variables and improve error handling in database connection. Adjust server port configuration to prioritize BACKEND_PORT. Update HTML structure for better compatibility and add missing elements in various components.

This commit is contained in:
Torsten Schulz (local)
2026-04-08 08:37:36 +02:00
parent 7d54156112
commit 6ffc1fedd9
87 changed files with 870 additions and 233 deletions

View File

@@ -1,8 +1,25 @@
const { Sequelize } = require('sequelize'); const { Sequelize } = require('sequelize');
require('dotenv').config();
const sequelize = new Sequelize('miriamgemeinde', 'miriamgemeinde', 'hitomisan', { const envName = process.env.NODE_ENV || 'development';
host: 'localhost', const fileConfig = require('./config.json')[envName];
dialect: 'mysql',
if (!fileConfig) {
throw new Error(
`[DB] Kein Eintrag in config/config.json für NODE_ENV="${envName}".`
);
}
const database = process.env.DB_NAME || fileConfig.database;
const username = process.env.DB_USER || fileConfig.username;
const password =
process.env.DB_PASSWORD === undefined ? fileConfig.password : process.env.DB_PASSWORD;
const host = process.env.DB_HOST || fileConfig.host;
const sequelizeOptions = {
host,
dialect: fileConfig.dialect || 'mysql',
dialectOptions: fileConfig.dialectOptions,
retry: { retry: {
match: [ match: [
/ConnectionError/, /ConnectionError/,
@@ -11,24 +28,38 @@ const sequelize = new Sequelize('miriamgemeinde', 'miriamgemeinde', 'hitomisan',
/SequelizeHostNotFoundError/, /SequelizeHostNotFoundError/,
/SequelizeHostNotReachableError/, /SequelizeHostNotReachableError/,
/SequelizeInvalidConnectionError/, /SequelizeInvalidConnectionError/,
/SequelizeConnectionTimedOutError/ /SequelizeConnectionTimedOutError/,
], ],
max: 5 max: 5,
}, },
pool: { pool: {
max: 5, max: 5,
min: 0, min: 0,
acquire: 30000, acquire: 30000,
idle: 10000 idle: 10000,
} },
}); logging: process.env.DB_LOGGING === '1' ? console.log : false,
};
if (process.env.DB_PORT) {
sequelizeOptions.port = parseInt(process.env.DB_PORT, 10);
} else if (fileConfig.port) {
sequelizeOptions.port = fileConfig.port;
}
const sequelize = new Sequelize(database, username, password, sequelizeOptions);
async function connectWithRetry() { async function connectWithRetry() {
try { try {
await sequelize.authenticate(); await sequelize.authenticate();
console.log(`Connection has been established successfully. Database server: ${sequelize.config.host}`); console.log(
`[DB] Verbindung OK — host=${host} database=${database} user=${username} (NODE_ENV=${envName})`
);
} catch (error) { } catch (error) {
console.error('Unable to connect to the database:', error); console.error('[DB] Verbindung fehlgeschlagen:', error.message);
console.error(
`[DB] Erwartete Quelle: config/config.json → "${envName}" oder Umgebungsvariablen DB_HOST, DB_USER, DB_PASSWORD, DB_NAME`
);
setTimeout(connectWithRetry, 5000); setTimeout(connectWithRetry, 5000);
} }
} }

View File

@@ -0,0 +1,224 @@
# Konzept: Modernisierung von Design und Bedienbarkeit
**Projekt:** Evangelische Miriamgemeinde Frankfurt (Vue.js-Webauftritt)
**Stand:** April 2026
**Ziel:** Zeitgemäße, klare Oberfläche mit hoher Vertrauenswürdigkeit; kirchlich-seriös, ohne „Startup-Optik“.
---
## 1. Zielbild und Leitlinien
### 1.1 Positionierung
Die Website ist **Informations- und Gemeinschaftsangebot** einer evangelischen Gemeinde. Sie soll:
- **verlässlich und ruhig** wirken (kein visuelles „Rauschen“),
- **inhaltlich im Vordergrund** stehen (Typografie, Lesbarkeit, klare Hierarchie),
- **digital souverän** wirken (gute Struktur, schnelle Orientierung, respektvolle Hilfen für alle Nutzergruppen).
### 1.2 Nicht verhandelbar: EKHN-Violett
Die **Grundfarbe EKHN-Violett** bleibt die primäre Markenfarbe. Im Code aktuell u. a. als `#9400ff` mit Hover `#7a00d1` genutzt (Navigation). Diese Farbe wird **nicht ersetzt oder „neu interpretiert“**.
- Sie wird als **CSS-Design-Token** zentral definiert (z. B. `--color-ekhn-violet`, `--color-ekhn-violet-hover`), damit alle Komponenten konsistent darauf zugreifen.
- **Abstufungen** (heller für Hintergründe, transparenter für Overlays) sind **zulässig**, solange die wahrgenommene Marke **dieselbe Violett-Identität** bleibt.
- Kontrast zu Text und Icons muss **WCAG-konform** sein (siehe Abschnitt 7).
### 1.3 Seriosität vs. Modernität
| Modern (gewünscht) | Vermeiden (für kirchlichen Kontext) |
|--------------------|-------------------------------------|
| Klares Raster, viel Weißraum | Neon-Verläufe, Spielereien |
| Ruhige, lesbare Schrift | Display-Fonts, übertriebene Größen |
| Deutliche Fokuszustände (Tastatur) | Aggressive Animationen |
| Einheitliche Komponenten | Zufällige Abstände und Stile pro Seite |
| Verständliche Navigation | „Experimentelle“ Menüs ohne klare Labels |
**Leitmotiv:** *Ruhige Sachlichkeit mit warmer, einladender Sprache in der UI (Beschriftungen, Fehlermeldungen, leere Zustände).*
---
## 2. Kurze Ist-Analyse (Ausgangslage)
Aus dem aktuellen Aufbau (u. a. `AppComponent.vue`, `NavbarComponent.vue`, `HeaderComponent.vue`, `FooterComponent.vue`):
- **Typografie:** durchgängig `Arial, sans-serif` funktional, aber wenig Profil; keine skalierbare Typo-Skala.
- **Layout:** starre `min-width: 1000px` in der Hauptspalte begünstigt horizontales Scrollen auf Tablets/kleineren Viewports; Zwei-Spalten-Logik mit Breakpoints ist vorhanden, sollte aber **inhaltlich und technisch** weiterentwickelt werden.
- **Farben:** Violett in der Navigation; Footer dunkelblau (`#0b1735`); rechte Spalte hellblau (`#d9e2f3`); Header mit Schlagschatten in Lavendeltönen teils **uneinheitlich** zur Markenfarbe.
- **Navigation:** Hamburger/Menü-Button unter 768px; Dropdowns mit Hover auf Touch-Geräten und für Tastaturnutzer ist hier **Verbesserungspotenzial** (Fokus, ARIA, Touch-Targets).
- **Footer:** Login-Link in Grau auf dunklem Grund **Kontrast** prüfen und ggf. anpassen (ohne Marke zu verändern).
Diese Punkte fließen als konkrete Maßnahmen in die Phasenplanung (Abschnitt 9) ein.
---
## 3. Design-System: Farben
### 3.1 Primär (unverändert)
| Token (Vorschlag) | Verwendung | Hex (Ist) |
|-------------------|------------|-----------|
| `--color-brand-primary` | Navigationsleiste, primäre Buttons, aktive Zustände | `#9400ff` |
| `--color-brand-primary-hover` | Hover, aktive Menüpunkte | `#7a00d1` |
*Hinweis:* Falls das offizielle EKHN-Handbuch eine minimal abweichende Hex-Angabe vorsieht, **eine** kanonische Quelle festlegen und nur diese verwenden weiterhin **kein** Wechsel zu einer anderen Farbfamilie.
### 3.2 Neutrale Flächen (ergänzend, nicht markenersetzend)
- **Hintergrund Seite:** `#ffffff` oder sehr helles Neutral (`#f8f9fb`), konsistent.
- **Sekundärflächen** (Karten, rechte Spalte, Infoboxen): dezentes Grau oder ein **sehr zurückhaltendes Violett-Grau** (z. B. Mischung aus Weiß mit 36 % Primärfarbe), damit die Seite **ruhig** bleibt und nicht „bunt“ wirkt.
- **Text:** nahezu schwarz für Fließtext (`#1a1a1a` bis `#222`), sekundäre Texte etwas heller immer mit Kontrastprüfung.
### 3.3 Akzent (optional, sparsam)
- **Links** im Fließtext: z. B. unterstrichen oder klar farbig abgesetzt; Primärviolett oder eine **eine** abgestimmte dunklere Violett-Nuance für Lesbarkeit auf Weiß.
- **Erfolg/Warnung/Fehler:** Standard-Semantik (Grün/Gelb/Rot) nur für Status **nicht** als neue Hauptfarbe neben dem Violett.
### 3.4 Footer
- Dunkler Footer kann bleiben; **Links und Fokus** müssen gut lesbar sein. Primärviolett für Hover/Fokus auf dunklem Grund nur, wenn der Kontrast stimmt sonch neutrale helle Linkfarbe + sichtbarer Fokusring.
---
## 4. Typografie
### 4.1 Schriftwahl
- **Primärschrift:** eine gut lesbare **System- oder Webschrift** mit neutral-seriösem Charakter, z. B.:
- *Source Sans 3*, *Inter*, *Open Sans* oder **beibehaltene Arial** nach einheitlicher Skala Entscheidung in Phase 1 an **Performance** und **Corporate-Vorgaben** binden.
- **Überschriften:** dieselbe Familie mit klarer Gewichtsstaffelung (z. B. 600/700), keine verspielten Display-Schnitte.
### 4.2 Skala (Beispiel)
| Stufe | Verwendung | Größe (Orientierung, rem) |
|-------|------------|---------------------------|
| H1 | Seitentitel (nicht auf jeder Unterseite doppelt mit Logo-Text kollidieren) | `1.752rem` |
| H2 | Abschnitte | `1.351.5rem` |
| H3 | Unterabschnitte | `1.151.25rem` |
| Body | Fließtext | `1rem`, Zeilenlänge max. ca. 6575 Zeichen |
| Klein | Meta, Fußnoten | `0.875rem` mit ausreichend Kontrast |
### 4.3 Regeln
- **Keine** reine Großschreibung für lange Menütexte.
- **Zeilenabstand** für Fließtext mindestens ca. 1,5.
- **Kontrast** von Überschriften und Text zu Hintergrund einhalten (WCAG 2.1 AA).
---
## 5. Layout und Raster
### 5.1 Container
- Maximalbreite für Lesbarkeit (z. B. `min(100%, 72rem)`) mit **symmetrischem Innenabstand**.
- **Keine** feste `min-width` im vierstelligen Pixelbereich ohne Scroll-Alternative; stattdessen **flexibles Grid** + sinnvolle Mindestbreiten nur dort, wo nötig (Tabellen).
### 5.2 Breakpoints (Orientierung)
- **Mobil:** < 640px eine Spalte, Navigation als klares Overlay oder ausklappbare Liste mit großen Touch-Zielen.
- **Tablet:** 6401024px ggf. weiterhin eine Spalte oder kompakte Sidebar.
- **Desktop:** > 1024px Zwei-Spalten-Layout optional; rechte Spalte für Bilder/Termine **nicht** zwingend über volle Höhe „eingesperrt“, wenn das inhaltlich sinnvoller ist.
### 5.3 Weißraum
- Einheitliches Spacing-System (z. B. Vielfache von 4px oder 0,25rem): Abstände zwischen Blöcken, in Karten, zwischen Formularfeldern.
---
## 6. Navigation und Bedienung
### 6.1 Hauptnavigation
- **Desktop:** horizontale Leiste mit Vollfarbe EKHN-Violett; aktiver Eintrag **deutlich** (Unterstreichung, Hintergrund oder starker Kontrast weiterhin im Violett-System).
- **Touch:** Menüpunkte mindestens ca. **44×44 px** Klickfläche.
- **Untermenüs:** Hover für Maus; für Tastatur **Escape** schließt; **Fokus** sichtbar im gesamten Menübaum.
- **Mobile:** „Menü“-Button durch **Icon + Text** oder klares Label; Animationen **kurz** (< 200ms).
### 6.2 Orientierung
- Optional: **Brotkrumen** bei tieferen Seiten (Gemeindeleben → Gruppen → …), dezent unterhalb des Headers.
- Seitentitel konsistent: eine H1 pro Seite, semantisch korrekt.
### 6.3 Footer
- Impressum/Datenschutz bleiben gut auffindbar; gleiche visuelle Gewichtung wie bisher, mit verbessertem Kontrast und Fokus.
---
## 7. Barrierefreiheit (WCAG 2.1 Level AA als Ziel)
- **Kontrast:** Text auf Violett/Weiß/Grau messen (Tools: axe, Lighthouse, WebAIM Contrast Checker).
- **Tastatur:** alle interaktiven Elemente erreichbar; sichtbarer `:focus-visible`.
- **Screenreader:** Landmarks (`header`, `nav`, `main`, `footer`), Überschriftenhierarchie, `aria-expanded` für Untermenüs, sinnvolle `alt`-Texte bei Bildern.
- **Bewegung:** `prefers-reduced-motion` respektieren (Animationen abschwächen oder deaktivieren).
---
## 8. Komponentenbibliothek (schrittweise)
Einheitliche Bausteine reduzieren Streuung und erleichtern Wartung:
| Komponente | Anforderungen |
|------------|----------------|
| **Primärbutton** | Violett-Hintergrund, weißer Text, Hover, disabled-Zustand, Fokusring |
| **Sekundärbutton** | Outline in Violett oder neutral, gleiche Höhe wie Primär |
| **Karte** (Termine, News) | Klarer Titel, Datum, Link „weiterlesen“, ruhiger Schatten oder Rahmen |
| **Formularfelder** | Labels sichtbar, Fehler näch am Feld, keine rein farblichen Fehlerhinweise |
| **Tabellen** (Admin) | Zeilenwechsel, ausreichend Zellpadding, horizontales Scrollen auf schmalen Screens |
Technisch: zentrale **CSS-Variablen** + ggf. wiederverwendbare Vue-Komponenten oder Utility-Klassen ohne das Projekt unnötig zu überfrachten.
---
## 9. Umsetzung in Phasen
### Phase 1 Fundament (geringes Risiko, hoher Nutzen)
- Design-Tokens (Farben, Abstände, Schriftgrößen) in einer **globalen** Styleschicht.
- Entfernen/Ersetzen problematischer Layout-Regeln (`min-width` Hauptspalte), **Responsive** testen.
- Typografie-Skala und Basisabstände vereinheitlichen.
- Kontrast Footer/Links prüfen und anpassen.
### Phase 2 Navigation & Chrome
- Navbar optisch verfeinern (Padding, aktive Zustände, Touch) bei **unverändertem** `#9400ff` / Hover.
- Header (Titelzeile): Schlagschatten/Lavendel **an Markensystem anbinden** oder reduzieren für seriösere Wirkung.
- Optional Brotkrumen für tiefe Seiten.
### Phase 3 Inhaltsmodule
- Termine, Gottesdienste, Kontakt: Kartenlayout, konsistente Datumdarstellung (`date-fns` ist bereits im Projekt).
- Bilder: feste Aspect-Ratios oder `object-fit`, damit keine Layout-Sprünge entstehen.
### Phase 4 Feinschliff
- Mikro-Interaktionen (Hover, Fokus) konsistent.
- Performance: Schriftarten, Bildgrößen, Lazy Loading wo sinnvoll.
- Finale Accessibility-Prüfung.
---
## 10. Erfolgskriterien (messbar / reviewbar)
- Keine horizontale Scrollbarkeit bei Standard-Viewports durch feste Mindestbreiten.
- Lighthouse-Accessibility-Score deutlich verbessert (Ziel: **≥ 90**, wo technisch möglich).
- **Manuelle** Tastatur- und Screenreader-Stichprobe auf Startseite, Navigation, einem Formular.
- **Visuelles Review** mit 23 Stakeholdern: „wirkt seriös, klar, kirchlich passend“ **ohne** neue Hauptfarbe neben EKHN-Violett.
---
## 11. Explizite Nicht-Ziele
- Kein Rebranding und kein Ersatz der Primärfarbe.
- Kein „Gamification“-Design oder verspielte Illustrationen als Hauptstil.
- Keine Einführung schwerer UI-Frameworks nur wegen Optik, wenn das Projekt schlank bleiben soll (abwägen mit Wartbarkeit).
---
## 12. Nächster Schritt
Umsetzung beginnt mit **Phase 1**: globale Tokens + Layout-Fixes + Kontrast. Dieses Dokument dient als **Referenz** für alle weiteren UI-Änderungen und sollte bei größeren Designentscheidungen aktualisiert werden.
---
*Dokument erstellt als Arbeitsgrundlage für die Modernisierung; bei Abweichungen von offiziellen EKHN-CD-Vorgaben immer die gültige kirchliche Markenrichtlinie Vorrang haben.*

View File

@@ -3,20 +3,11 @@
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const Sequelize = require('sequelize'); const Sequelize = require('sequelize');
const sequelize = require('../config/database');
const basename = path.basename(__filename); const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];
const db = {}; const db = {};
let sequelize; fs.readdirSync(__dirname)
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
sequelize = new Sequelize(config.database, config.username, config.password, config);
}
fs
.readdirSync(__dirname)
.filter(file => { .filter(file => {
return ( return (
file.indexOf('.') !== 0 && file.indexOf('.') !== 0 &&

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
.menu-management[data-v-0d38d21e]{width:100%;margin:auto}.button-container[data-v-0d38d21e]{display:inline-flex;gap:10px;margin-bottom:20px}.tree-view[data-v-0d38d21e]{margin-top:20px}.tree-view ul[data-v-0d38d21e]{list-style-type:none;padding:0}.tree-view li[data-v-0d38d21e]{margin-bottom:5px;padding-left:20px}.tree-view .menu-item[data-v-0d38d21e]{display:inline-flex;width:100%;justify-content:space-between;align-items:center}.tree-view span[data-v-0d38d21e]{cursor:pointer;color:#000}.tree-view button[data-v-0d38d21e]{border:none;height:1.6em;padding:0 .5em;margin:1px;border-radius:5px}.tree-view span[data-v-0d38d21e]:hover{text-decoration:underline}.edit-form[data-v-0d38d21e]{margin-top:20px}.edit-form label[data-v-0d38d21e]{display:block;margin-bottom:5px;font-weight:700}.edit-form input[data-v-0d38d21e]:not([type=checkbox]){display:block;margin-bottom:10px}.edit-form .checkbox-container[data-v-0d38d21e]{display:flex;flex-direction:column;margin-right:10px}.edit-form .order-id[data-v-0d38d21e]{width:50px}.edit-form button[data-v-0d38d21e]{margin-top:5px}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>miriamgemeinde</title><script defer="defer" src="/js/chunk-vendors.a58901d9.js"></script><script defer="defer" src="/js/app.2b3ac443.js"></script><link href="/css/app.c2c4030a.css" rel="stylesheet"><script defer="defer" src="/js/chunk-vendors.a58901d9.js"></script><script defer="defer" src="/js/app.62331f73.js"></script><link href="/css/app.c2c4030a.css" rel="stylesheet"><script defer="defer" src="/js/chunk-vendors.a58901d9.js"></script><script defer="defer" src="/js/app.f7f58406.js"></script><link href="/css/app.c2c4030a.css" rel="stylesheet"><script defer="defer" src="/js/chunk-vendors.b7e76d39.js"></script><script defer="defer" src="/js/app.c50b5429.js"></script><link href="/css/app.3e68accd.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but miriamgemeinde doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html> <!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>miriamgemeinde</title><script defer="defer" src="/js/chunk-vendors.a58901d9.js"></script><script defer="defer" src="/js/app.2b3ac443.js"></script><link href="/css/app.c2c4030a.css" rel="stylesheet"><script defer="defer" src="/js/chunk-vendors.a58901d9.js"></script><script defer="defer" src="/js/app.62331f73.js"></script><link href="/css/app.c2c4030a.css" rel="stylesheet"><script defer="defer" src="/js/chunk-vendors.a58901d9.js"></script><script defer="defer" src="/js/app.f7f58406.js"></script><link href="/css/app.c2c4030a.css" rel="stylesheet"><script defer="defer" src="/js/chunk-vendors.b7e76d39.js"></script><script defer="defer" src="/js/app.c50b5429.js"></script><link href="/css/app.3e68accd.css" rel="stylesheet"><script defer="defer" src="/js/chunk-vendors.b7e76d39.js"></script><script defer="defer" src="/js/app.53c460b9.js"></script><link href="/css/app.43dcf86b.css" rel="stylesheet"><script defer="defer" src="/js/chunk-vendors.b7e76d39.js"></script><script defer="defer" src="/js/app.e71748c3.js"></script><link href="/css/app.43dcf86b.css" rel="stylesheet"><script defer="defer" src="/js/chunk-vendors.2da008aa.js"></script><script defer="defer" src="/js/app.e009cb77.js"></script><link href="/css/app.674aab9c.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but miriamgemeinde doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
"use strict";(self["webpackChunkmiriamgemeinde"]=self["webpackChunkmiriamgemeinde"]||[]).push([[187],{8187:function(e,t,n){n.r(t),n.d(t,{default:function(){return k}});var l=n(641),a=n(3751),i=n(33);const c={class:"event-places-management"},d=["onClick"],o=["onClick"];function r(e,t,n,r,s,u){return(0,l.uX)(),(0,l.CE)("div",c,[t[16]||(t[16]=(0,l.Lk)("h2",null,"Veranstaltungsorte verwalten",-1)),(0,l.Lk)("form",{onSubmit:t[7]||(t[7]=(0,a.D$)((...e)=>u.addEventPlace&&u.addEventPlace(...e),["prevent"]))},[t[8]||(t[8]=(0,l.Lk)("label",{for:"name"},"Name:",-1)),(0,l.bo)((0,l.Lk)("input",{type:"text",id:"name","onUpdate:modelValue":t[0]||(t[0]=e=>s.newEventPlace.name=e),placeholder:"Name",required:""},null,512),[[a.Jo,s.newEventPlace.name]]),t[9]||(t[9]=(0,l.Lk)("label",{for:"street"},"Straße:",-1)),(0,l.bo)((0,l.Lk)("input",{type:"text",id:"street","onUpdate:modelValue":t[1]||(t[1]=e=>s.newEventPlace.street=e),placeholder:"Straße",required:""},null,512),[[a.Jo,s.newEventPlace.street]]),t[10]||(t[10]=(0,l.Lk)("label",{for:"zipcode"},"PLZ:",-1)),(0,l.bo)((0,l.Lk)("input",{type:"text",id:"zipcode","onUpdate:modelValue":t[2]||(t[2]=e=>s.newEventPlace.zipcode=e),placeholder:"PLZ",required:""},null,512),[[a.Jo,s.newEventPlace.zipcode]]),t[11]||(t[11]=(0,l.Lk)("label",{for:"city"},"Stadt:",-1)),(0,l.bo)((0,l.Lk)("input",{type:"text",id:"city","onUpdate:modelValue":t[3]||(t[3]=e=>s.newEventPlace.city=e),placeholder:"Stadt",required:""},null,512),[[a.Jo,s.newEventPlace.city]]),t[12]||(t[12]=(0,l.Lk)("label",{for:"city"},"Webseite:",-1)),(0,l.bo)((0,l.Lk)("input",{type:"text",id:"website","onUpdate:modelValue":t[4]||(t[4]=e=>s.newEventPlace.website=e),placeholder:"Webseite",required:""},null,512),[[a.Jo,s.newEventPlace.website]]),t[13]||(t[13]=(0,l.Lk)("label",{for:"backgroundColor"},"Hintergrundfarbe:",-1)),(0,l.bo)((0,l.Lk)("input",{type:"color",id:"backgroundColor","onUpdate:modelValue":t[5]||(t[5]=e=>s.newEventPlace.backgroundColor=e)},null,512),[[a.Jo,s.newEventPlace.backgroundColor]]),t[14]||(t[14]=(0,l.Lk)("button",{type:"submit"},"Speichern",-1)),s.editMode?((0,l.uX)(),(0,l.CE)("button",{key:0,type:"button",onClick:t[6]||(t[6]=(...e)=>u.resetForm&&u.resetForm(...e))},"Neuen Veranstaltungsort erstellen")):(0,l.Q3)("",!0)],32),(0,l.Lk)("table",null,[t[15]||(t[15]=(0,l.Lk)("thead",null,[(0,l.Lk)("tr",null,[(0,l.Lk)("th",null,"Name"),(0,l.Lk)("th",null,"Bearbeiten"),(0,l.Lk)("th",null,"Löschen")])],-1)),(0,l.Lk)("tbody",null,[((0,l.uX)(!0),(0,l.CE)(l.FK,null,(0,l.pI)(s.eventPlaces,e=>((0,l.uX)(),(0,l.CE)("tr",{key:e.id},[(0,l.Lk)("td",null,(0,i.v_)(e.name),1),(0,l.Lk)("td",null,[(0,l.Lk)("button",{onClick:t=>u.editEventPlace(e)},"Bearbeiten",8,d)]),(0,l.Lk)("td",null,[(0,l.Lk)("button",{onClick:t=>u.deleteEventPlace(e.id)},"Löschen",8,o)])]))),128))])])])}n(4114);var s=n(4335),u={data(){return{eventPlaces:[],newEventPlace:{name:"",street:"",zipcode:"",city:"",backgroundColor:"#ffffff",website:""},editMode:!1,editId:null}},methods:{async fetchEventPlaces(){const e=await s.A.get("/event-places");this.eventPlaces=e.data},async addEventPlace(){if(this.editMode)await s.A.put(`/event-places/${this.editId}`,this.newEventPlace);else{const e=await s.A.post("/event-places",this.newEventPlace);this.eventPlaces.push(e.data)}this.resetForm(),await this.fetchEventPlaces()},async updateEventPlace(e){await s.A.put(`/event-places/${e.id}`,e),this.fetchEventPlaces()},async deleteEventPlace(e){await s.A.delete(`/event-places/${e}`),this.fetchEventPlaces()},editEventPlace(e){this.newEventPlace={...e},this.editMode=!0,this.editId=e.id},resetForm(){this.newEventPlace={name:"",street:"",zipcode:"",city:"",backgroundColor:"#ffffff",website:""},this.editMode=!1,this.editId=null}},created(){this.fetchEventPlaces()}},p=n(6262);const v=(0,p.A)(u,[["render",r],["__scopeId","data-v-4e6631f7"]]);var k=v}}]);
//# sourceMappingURL=187.bdfdd8d0.js.map

File diff suppressed because one or more lines are too long

2
public/js/23.0a6b6bcd.js Normal file
View File

@@ -0,0 +1,2 @@
"use strict";(self["webpackChunkmiriamgemeinde"]=self["webpackChunkmiriamgemeinde"]||[]).push([[23],{116:function(e,n,t){var r=t(6518),u=t(9565),i=t(2652),a=t(9306),l=t(8551),o=t(1767),d=t(9539),c=t(4549),s=c("find",TypeError);r({target:"Iterator",proto:!0,real:!0,forced:s},{find:function(e){l(this);try{a(e)}catch(r){d(this,"throw",r)}if(s)return u(s,this,e);var n=o(this),t=0;return i(n,function(n,r){if(e(n,t++))return r(n)},{IS_RECORD:!0,INTERRUPTED:!0}).result}})},2023:function(e,n,t){t.r(n),t.d(n,{default:function(){return s}});var r=t(641),u=t(33);function i(e,n,t,i,a,l){const o=(0,r.g2)("router-link");return(0,r.uX)(),(0,r.CE)("div",null,[n[0]||(n[0]=(0,r.Lk)("h1",null,"Seitenpflege",-1)),n[1]||(n[1]=(0,r.Lk)("p",null,"Herzlich Willkommen. Auf diesen Seiten können Sie die Inhalte der Webseiten pflegen.",-1)),(0,r.Lk)("ul",null,[((0,r.uX)(!0),(0,r.CE)(r.FK,null,(0,r.pI)(i.adminSubmenu,e=>((0,r.uX)(),(0,r.CE)("li",{key:e.id},[(0,r.bF)(o,{to:e.link},{default:(0,r.k6)(()=>[(0,r.eW)((0,u.v_)(e.name),1)]),_:2},1032,["to"])]))),128))])])}t(8111),t(116);var a=t(6296),l=t(953),o={name:"DefaultComponent",setup(){const e=(0,l.KR)([]),n=async()=>{try{const n=await a.A.get("/menu-data"),t=n.data,r=t.find(e=>"Admin"===e.name);r&&(e.value=r.submenu)}catch(n){console.error("Fehler beim Abrufen der Menü-Daten:",n)}};return(0,r.sV)(()=>{n()}),{adminSubmenu:e}}},d=t(6262);const c=(0,d.A)(o,[["render",i],["__scopeId","data-v-68b32234"]]);var s=c}}]);
//# sourceMappingURL=23.0a6b6bcd.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
"use strict";(self["webpackChunkmiriamgemeinde"]=self["webpackChunkmiriamgemeinde"]||[]).push([[246],{6246:function(e,t,n){n.r(t),n.d(t,{default:function(){return h}});var i=n(641),a=n(3751),s=n(33);const p={class:"event-types-management"},r={type:"submit"},d=["onClick"],o=["onClick"];function y(e,t,n,y,l,v){return(0,i.uX)(),(0,i.CE)("div",p,[t[4]||(t[4]=(0,i.Lk)("h2",null,"Event-Typen Verwaltung",-1)),(0,i.Lk)("form",{onSubmit:t[2]||(t[2]=(0,a.D$)((...e)=>v.saveEventType&&v.saveEventType(...e),["prevent"]))},[t[3]||(t[3]=(0,i.Lk)("label",{for:"newEventType"},"Event-Typ:",-1)),(0,i.bo)((0,i.Lk)("input",{type:"text",id:"newEventType","onUpdate:modelValue":t[0]||(t[0]=e=>l.eventTypeData.caption=e),placeholder:"Event-Typ",required:""},null,512),[[a.Jo,l.eventTypeData.caption]]),(0,i.Lk)("button",r,(0,s.v_)(l.editMode?"Aktualisieren":"Hinzufügen"),1),l.editMode?((0,i.uX)(),(0,i.CE)("button",{key:0,type:"button",onClick:t[1]||(t[1]=(...e)=>v.resetForm&&v.resetForm(...e))},"Abbrechen")):(0,i.Q3)("",!0)],32),(0,i.Lk)("table",null,[((0,i.uX)(!0),(0,i.CE)(i.FK,null,(0,i.pI)(l.eventTypes,e=>((0,i.uX)(),(0,i.CE)("tr",{key:e.id},[(0,i.Lk)("td",null,(0,s.v_)(e.caption),1),(0,i.Lk)("td",null,[(0,i.Lk)("button",{onClick:t=>v.editEventType(e)},"Bearbeiten",8,d)]),(0,i.Lk)("td",null,[(0,i.Lk)("button",{onClick:t=>v.deleteEventType(e.id)},"Löschen",8,o)])]))),128))])])}n(4114);var l=n(4335),v={data(){return{eventTypes:[],eventTypeData:{caption:""},editMode:!1,editId:null}},methods:{async fetchEventTypes(){try{const e=await l.A.get("/event-types");this.eventTypes=e.data}catch(e){console.error("Fehler beim Abrufen der Event-Typen:",e)}},async saveEventType(){try{if(this.editMode)await l.A.put(`/event-types/${this.editId}`,this.eventTypeData);else{const e=await l.A.post("/event-types",this.eventTypeData);this.eventTypes.push(e.data)}this.resetForm(),await this.fetchEventTypes()}catch(e){console.error("Fehler beim Speichern des Event-Typs:",e)}},editEventType(e){this.eventTypeData={...e},this.editMode=!0,this.editId=e.id},async deleteEventType(e){try{await l.A.delete(`/event-types/${e}`),await this.fetchEventTypes()}catch(t){console.error("Fehler beim Löschen des Event-Typs:",t)}},resetForm(){this.eventTypeData={caption:""},this.editMode=!1,this.editId=null}},async created(){await this.fetchEventTypes()}},c=n(6262);const u=(0,c.A)(v,[["render",y],["__scopeId","data-v-60fe58a4"]]);var h=u}}]);
//# sourceMappingURL=246.5b0c0fa9.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
"use strict";(self["webpackChunkmiriamgemeinde"]=self["webpackChunkmiriamgemeinde"]||[]).push([[324],{1324:function(e,i,s){s.r(i),s.d(i,{default:function(){return k}});var a=s(641),t=s(3751),o=s(33);const l={class:"forgot-password"},n={key:0,class:"dialog"},r={class:"dialog-content"};function d(e,i,s,d,g,u){const m=(0,a.g2)("router-link");return(0,a.uX)(),(0,a.CE)("div",l,[i[7]||(i[7]=(0,a.Lk)("h2",null,"Passwort vergessen",-1)),(0,a.Lk)("form",{onSubmit:i[1]||(i[1]=(0,t.D$)((...e)=>u.submitForgotPassword&&u.submitForgotPassword(...e),["prevent"]))},[i[3]||(i[3]=(0,a.Lk)("label",{for:"email"},"Email-Adresse:",-1)),(0,a.bo)((0,a.Lk)("input",{type:"email",id:"email","onUpdate:modelValue":i[0]||(i[0]=e=>g.email=e),required:""},null,512),[[t.Jo,g.email]]),i[4]||(i[4]=(0,a.Lk)("button",{type:"submit"},"Link zum Zurücksetzen senden",-1))],32),(0,a.Lk)("p",null,[(0,a.bF)(m,{to:"/login"},{default:(0,a.k6)(()=>[...i[5]||(i[5]=[(0,a.eW)("Login",-1)])]),_:1})]),(0,a.Lk)("p",null,[(0,a.bF)(m,{to:"/register"},{default:(0,a.k6)(()=>[...i[6]||(i[6]=[(0,a.eW)("Registrieren",-1)])]),_:1})]),g.dialogVisible?((0,a.uX)(),(0,a.CE)("div",n,[(0,a.Lk)("div",r,[(0,a.Lk)("h3",null,(0,o.v_)(g.dialogTitle),1),(0,a.Lk)("p",null,(0,o.v_)(g.dialogMessage),1),(0,a.Lk)("button",{type:"button",onClick:i[2]||(i[2]=(...e)=>u.closeDialog&&u.closeDialog(...e))},"Schließen")])])):(0,a.Q3)("",!0)])}var g=s(6296),u={name:"ForgotPassword",data(){return{email:"",dialogTitle:"",dialogMessage:"",dialogVisible:!1}},methods:{async submitForgotPassword(){try{const e=await g.A.post("/auth/forgot-password",{email:this.email});this.showDialog("E-Mail gesendet",e.data?.message||"Ein Link zum Zurücksetzen wurde an Ihre E-Mail-Adresse gesendet."),this.email=""}catch(e){const i=e?.response?.data?.message||e?.message||"Ein unbekannter Fehler ist aufgetreten";this.showDialog("Fehler",i)}},showDialog(e,i){this.dialogTitle=e,this.dialogMessage=i,this.dialogVisible=!0},closeDialog(){this.dialogVisible=!1}}},m=s(6262);const c=(0,m.A)(u,[["render",d],["__scopeId","data-v-c694cf4e"]]);var k=c}}]);
//# sourceMappingURL=324.b84a9462.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
"use strict";(self["webpackChunkmiriamgemeinde"]=self["webpackChunkmiriamgemeinde"]||[]).push([[355],{5355:function(e,l,t){t.r(l),t.d(l,{default:function(){return c}});var i=t(641),a=t(33);const o={key:0,class:"dialog-overlay"},n={class:"dialog"};function s(e,l,t,s,r,u){return t.modelValue?((0,i.uX)(),(0,i.CE)("div",o,[(0,i.Lk)("div",n,[(0,i.Lk)("h2",null,(0,a.v_)(t.title),1),(0,i.Lk)("p",null,(0,a.v_)(t.message),1),(0,i.Lk)("button",{onClick:l[0]||(l[0]=(...e)=>u.closeDialog&&u.closeDialog(...e))},"OK")])])):(0,i.Q3)("",!0)}var r={name:"DialogComponent",props:{title:{type:String,required:!0},message:{type:String,required:!0},modelValue:{type:Boolean,default:!1}},methods:{closeDialog(){this.$emit("update:modelValue",!1),this.$emit("close")}}},u=t(6262);const d=(0,u.A)(r,[["render",s],["__scopeId","data-v-ce9d9498"]]);var c=d}}]);
//# sourceMappingURL=355.97e174a9.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"js/355.97e174a9.js","mappings":"4MACyBA,MAAM,kB,GACtBA,MAAM,U,+BADFC,EAAAC,a,WAAXC,EAAAA,EAAAA,IAMM,MANNC,EAMM,EALJC,EAAAA,EAAAA,IAIM,MAJNC,EAIM,EAHJD,EAAAA,EAAAA,IAAoB,WAAAE,EAAAA,EAAAA,IAAbN,EAAAO,OAAK,IACZH,EAAAA,EAAAA,IAAoB,UAAAE,EAAAA,EAAAA,IAAdN,EAAAQ,SAAO,IACbJ,EAAAA,EAAAA,IAAwC,UAA/BK,QAAKC,EAAA,KAAAA,EAAA,OAAAC,IAAEC,EAAAC,aAAAD,EAAAC,eAAAF,KAAa,Y,eAMnC,OACEG,KAAM,kBACNC,MAAO,CACLR,MAAO,CACLS,KAAMC,OACNC,UAAU,GAEZV,QAAS,CACPQ,KAAMC,OACNC,UAAU,GAEZjB,WAAY,CACVe,KAAMG,QACNC,SAAS,IAGbC,QAAS,CACPR,WAAAA,GACES,KAAKC,MAAM,qBAAqB,GAChCD,KAAKC,MAAM,QACb,I,UCxBJ,MAAMC,GAA2B,OAAgB,EAAQ,CAAC,CAAC,SAASC,GAAQ,CAAC,YAAY,qBAEzF,O","sources":["webpack://miriamgemeinde/./src/common/components/DialogComponent.vue","webpack://miriamgemeinde/./src/common/components/DialogComponent.vue?92fc"],"sourcesContent":["<template>\n <div v-if=\"modelValue\" class=\"dialog-overlay\">\n <div class=\"dialog\">\n <h2>{{ title }}</h2>\n <p>{{ message }}</p>\n <button @click=\"closeDialog\">OK</button>\n </div>\n </div>\n</template>\n\n<script>\nexport default {\n name: 'DialogComponent',\n props: {\n title: {\n type: String,\n required: true\n },\n message: {\n type: String,\n required: true\n },\n modelValue: {\n type: Boolean,\n default: false\n }\n },\n methods: {\n closeDialog() {\n this.$emit('update:modelValue', false);\n this.$emit('close');\n }\n }\n};\n</script>\n\n<style scoped>\n.dialog-overlay {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n justify-content: center;\n align-items: center;\n z-index: 1000;\n}\n\n.dialog {\n background: white;\n padding: 30px;\n border-radius: 8px;\n max-width: 400px;\n width: 90%;\n text-align: center;\n box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\n}\n\n.dialog h2 {\n margin-top: 0;\n margin-bottom: 15px;\n color: #333;\n font-size: 20px;\n}\n\n.dialog p {\n margin: 15px 0;\n color: #666;\n line-height: 1.5;\n}\n\n.dialog button {\n margin-top: 20px;\n padding: 10px 20px;\n background-color: #007BFF;\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 16px;\n}\n\n.dialog button:hover {\n background-color: #0056b3;\n}\n</style>","import { render } from \"./DialogComponent.vue?vue&type=template&id=ce9d9498&scoped=true\"\nimport script from \"./DialogComponent.vue?vue&type=script&lang=js\"\nexport * from \"./DialogComponent.vue?vue&type=script&lang=js\"\n\nimport \"./DialogComponent.vue?vue&type=style&index=0&id=ce9d9498&scoped=true&lang=css\"\n\nimport exportComponent from \"../../../node_modules/vue-loader/dist/exportHelper.js\"\nconst __exports__ = /*#__PURE__*/exportComponent(script, [['render',render],['__scopeId',\"data-v-ce9d9498\"]])\n\nexport default __exports__"],"names":["class","$props","modelValue","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_toDisplayString","title","message","onClick","_cache","args","$options","closeDialog","name","props","type","String","required","Boolean","default","methods","this","$emit","__exports__","render"],"ignoreList":[],"sourceRoot":""}

View File

@@ -0,0 +1,2 @@
"use strict";(self["webpackChunkmiriamgemeinde"]=self["webpackChunkmiriamgemeinde"]||[]).push([[404],{3023:function(t,e,i){i.r(e),i.d(e,{default:function(){return p}});var n=i(641),o=i(3751),a=i(33);const s={class:"institution-management"},u=["onClick"],l=["onClick"];function r(t,e,i,r,d,c){return(0,n.uX)(),(0,n.CE)("div",s,[e[18]||(e[18]=(0,n.Lk)("h2",null,"Institutionenverwaltung",-1)),(0,n.Lk)("form",{onSubmit:e[8]||(e[8]=(0,o.D$)((...t)=>c.saveInstitution&&c.saveInstitution(...t),["prevent"]))},[e[9]||(e[9]=(0,n.Lk)("label",{for:"name"},"Name:",-1)),(0,n.bo)((0,n.Lk)("input",{type:"text",id:"name","onUpdate:modelValue":e[0]||(e[0]=t=>d.institutionData.name=t),required:""},null,512),[[o.Jo,d.institutionData.name]]),e[10]||(e[10]=(0,n.Lk)("label",{for:"street"},"Straße:",-1)),(0,n.bo)((0,n.Lk)("input",{type:"text",id:"street","onUpdate:modelValue":e[1]||(e[1]=t=>d.institutionData.street=t)},null,512),[[o.Jo,d.institutionData.street]]),e[11]||(e[11]=(0,n.Lk)("label",{for:"zipcode"},"PLZ:",-1)),(0,n.bo)((0,n.Lk)("input",{type:"text",id:"zipcode","onUpdate:modelValue":e[2]||(e[2]=t=>d.institutionData.zipcode=t)},null,512),[[o.Jo,d.institutionData.zipcode]]),e[12]||(e[12]=(0,n.Lk)("label",{for:"city"},"Stadt:",-1)),(0,n.bo)((0,n.Lk)("input",{type:"text",id:"city","onUpdate:modelValue":e[3]||(e[3]=t=>d.institutionData.city=t)},null,512),[[o.Jo,d.institutionData.city]]),e[13]||(e[13]=(0,n.Lk)("label",{for:"phone"},"Telefon:",-1)),(0,n.bo)((0,n.Lk)("input",{type:"text",id:"phone","onUpdate:modelValue":e[4]||(e[4]=t=>d.institutionData.phone=t)},null,512),[[o.Jo,d.institutionData.phone]]),e[14]||(e[14]=(0,n.Lk)("label",{for:"fax"},"Fax:",-1)),(0,n.bo)((0,n.Lk)("input",{type:"text",id:"fax","onUpdate:modelValue":e[5]||(e[5]=t=>d.institutionData.fax=t)},null,512),[[o.Jo,d.institutionData.fax]]),e[15]||(e[15]=(0,n.Lk)("label",{for:"email"},"Email:",-1)),(0,n.bo)((0,n.Lk)("input",{type:"email",id:"email","onUpdate:modelValue":e[6]||(e[6]=t=>d.institutionData.email=t)},null,512),[[o.Jo,d.institutionData.email]]),e[16]||(e[16]=(0,n.Lk)("button",{type:"submit"},"Speichern",-1)),d.editMode?((0,n.uX)(),(0,n.CE)("button",{key:0,type:"button",onClick:e[7]||(e[7]=(...t)=>c.resetForm&&c.resetForm(...t))},"Neue Institution erstellen")):(0,n.Q3)("",!0)],32),(0,n.Lk)("table",null,[e[17]||(e[17]=(0,n.Lk)("thead",null,[(0,n.Lk)("tr",null,[(0,n.Lk)("th",null,"Name"),(0,n.Lk)("th",null,"Bearbeiten"),(0,n.Lk)("th",null,"Löschen")])],-1)),(0,n.Lk)("tbody",null,[((0,n.uX)(!0),(0,n.CE)(n.FK,null,(0,n.pI)(d.institutions,t=>((0,n.uX)(),(0,n.CE)("tr",{key:t.id},[(0,n.Lk)("td",null,(0,a.v_)(t.name),1),(0,n.Lk)("td",null,[(0,n.Lk)("button",{onClick:e=>c.editInstitution(t)},"Bearbeiten",8,u)]),(0,n.Lk)("td",null,[(0,n.Lk)("button",{onClick:e=>c.deleteInstitution(t.id)},"Löschen",8,l)])]))),128))])])])}i(4114);var d=i(4335),c={name:"InstitutionManagement",data(){return{institutions:[],contactPersons:[],institutionData:{name:"",street:"",zipcode:"",city:"",phone:"",fax:"",email:""},selectedInstitution:null,showForm:!1,editMode:!1,editId:null}},created(){this.fetchInstitutions(),this.fetchContactPersons()},methods:{async fetchInstitutions(){try{const t=await d.A.get("/institutions");this.institutions=t.data}catch(t){console.error("Fehler beim Abrufen der Institutionen:",t)}},async fetchContactPersons(){try{const t=await d.A.get("/contact-persons");this.contactPersons=t.data}catch(t){console.error("Fehler beim Abrufen der Kontaktpersonen:",t)}},async saveInstitution(){try{if(this.editMode)await d.A.put(`/institutions/${this.editId}`,this.institutionData);else{const t=await d.A.post("/institutions",this.institutionData);this.institutions.push(t.data)}this.resetForm(),await this.fetchInstitutions()}catch(t){console.error("Fehler beim Speichern der Institution:",t)}},editInstitution(t){this.institutionData={...t},this.editMode=!0,this.editId=t.id,this.showForm=!0},async deleteInstitution(t){try{await d.A.delete(`/institutions/${t}`),this.fetchInstitutions()}catch(e){console.error("Fehler beim Löschen der Institution:",e)}},resetForm(){this.institutionData={name:"",street:"",zipcode:"",city:"",phone:"",fax:"",email:""},this.editMode=!1,this.editId=null,this.showForm=!1},showCreateForm(){this.resetForm(),this.showForm=!0}}},h=i(6262);const m=(0,h.A)(c,[["render",r],["__scopeId","data-v-ff992c44"]]);var p=m}}]);
//# sourceMappingURL=404.e9108d34.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
"use strict";(self["webpackChunkmiriamgemeinde"]=self["webpackChunkmiriamgemeinde"]||[]).push([[423],{1423:function(e,s,t){t.r(s),t.d(s,{default:function(){return m}});var o=t(641),i=t(3751),a=t(33);const n={class:"reset-password"},r=["disabled"],l={key:0,class:"dialog"},d={class:"dialog-content"};function u(e,s,t,u,h,c){const g=(0,o.g2)("router-link");return(0,o.uX)(),(0,o.CE)("div",n,[s[7]||(s[7]=(0,o.Lk)("h2",null,"Neues Passwort setzen",-1)),(0,o.Lk)("form",{onSubmit:s[2]||(s[2]=(0,i.D$)((...e)=>c.submitResetPassword&&c.submitResetPassword(...e),["prevent"]))},[s[4]||(s[4]=(0,o.Lk)("label",{for:"password"},"Neues Passwort:",-1)),(0,o.bo)((0,o.Lk)("input",{type:"password",id:"password","onUpdate:modelValue":s[0]||(s[0]=e=>h.password=e),required:"",minlength:"6"},null,512),[[i.Jo,h.password]]),s[5]||(s[5]=(0,o.Lk)("label",{for:"confirmPassword"},"Passwort bestätigen:",-1)),(0,o.bo)((0,o.Lk)("input",{type:"password",id:"confirmPassword","onUpdate:modelValue":s[1]||(s[1]=e=>h.confirmPassword=e),required:"",minlength:"6"},null,512),[[i.Jo,h.confirmPassword]]),(0,o.Lk)("button",{type:"submit",disabled:!c.isFormValid},"Passwort zurücksetzen",8,r)],32),(0,o.Lk)("p",null,[(0,o.bF)(g,{to:"/login"},{default:(0,o.k6)(()=>[...s[6]||(s[6]=[(0,o.eW)("Zurück zum Login",-1)])]),_:1})]),h.dialogVisible?((0,o.uX)(),(0,o.CE)("div",l,[(0,o.Lk)("div",d,[(0,o.Lk)("h3",null,(0,a.v_)(h.dialogTitle),1),(0,o.Lk)("p",null,(0,a.v_)(h.dialogMessage),1),(0,o.Lk)("button",{type:"button",onClick:s[3]||(s[3]=(...e)=>c.closeDialog&&c.closeDialog(...e))},"Schließen")])])):(0,o.Q3)("",!0)])}t(4114),t(4603),t(7566),t(8721);var h=t(6296),c={name:"ResetPasswordComponent",data(){return{password:"",confirmPassword:"",token:"",dialogTitle:"",dialogMessage:"",dialogVisible:!1}},computed:{isFormValid(){return this.password.length>=6&&this.password===this.confirmPassword&&this.token}},mounted(){const e=new URLSearchParams(window.location.search);this.token=e.get("token"),this.token||this.showDialog("Fehler","Ungültiger Reset-Link. Bitte fordern Sie einen neuen Link an.")},methods:{async submitResetPassword(){if(this.password===this.confirmPassword)try{const e=await h.A.post("/auth/reset-password",{token:this.token,password:this.password});this.showDialog("Erfolg",e.data?.message||"Passwort erfolgreich zurückgesetzt. Sie können sich jetzt anmelden."),this.password="",this.confirmPassword="",setTimeout(()=>{this.$router.push("/auth/login")},3e3)}catch(e){const s=e?.response?.data?.message||e?.message||"Ein unbekannter Fehler ist aufgetreten";this.showDialog("Fehler",s)}else this.showDialog("Fehler","Die Passwörter stimmen nicht überein.")},showDialog(e,s){this.dialogTitle=e,this.dialogMessage=s,this.dialogVisible=!0},closeDialog(){this.dialogVisible=!1}}},g=t(6262);const w=(0,g.A)(c,[["render",u],["__scopeId","data-v-e49a033c"]]);var m=w},2812:function(e){var s=TypeError;e.exports=function(e,t){if(e<t)throw new s("Not enough arguments");return e}},4603:function(e,s,t){var o=t(6840),i=t(9504),a=t(655),n=t(2812),r=URLSearchParams,l=r.prototype,d=i(l.append),u=i(l["delete"]),h=i(l.forEach),c=i([].push),g=new r("a=1&a=2&b=3");g["delete"]("a",1),g["delete"]("b",void 0),g+""!=="a=2"&&o(l,"delete",function(e){var s=arguments.length,t=s<2?void 0:arguments[1];if(s&&void 0===t)return u(this,e);var o=[];h(this,function(e,s){c(o,{key:s,value:e})}),n(s,1);var i,r=a(e),l=a(t),g=0,w=0,m=!1,p=o.length;while(g<p)i=o[g++],m||i.key===r?(m=!0,u(this,i.key)):w++;while(w<p)i=o[w++],i.key===r&&i.value===l||d(this,i.key,i.value)},{enumerable:!0,unsafe:!0})},7566:function(e,s,t){var o=t(6840),i=t(9504),a=t(655),n=t(2812),r=URLSearchParams,l=r.prototype,d=i(l.getAll),u=i(l.has),h=new r("a=1");!h.has("a",2)&&h.has("a",void 0)||o(l,"has",function(e){var s=arguments.length,t=s<2?void 0:arguments[1];if(s&&void 0===t)return u(this,e);var o=d(this,e);n(s,1);var i=a(t),r=0;while(r<o.length)if(o[r++]===i)return!0;return!1},{enumerable:!0,unsafe:!0})},8721:function(e,s,t){var o=t(3724),i=t(9504),a=t(2106),n=URLSearchParams.prototype,r=i(n.forEach);o&&!("size"in n)&&a(n,"size",{get:function(){var e=0;return r(this,function(){e++}),e},configurable:!0,enumerable:!0})}}]);
//# sourceMappingURL=423.408d1494.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
"use strict";(self["webpackChunkmiriamgemeinde"]=self["webpackChunkmiriamgemeinde"]||[]).push([[441],{3441:function(t,i,e){e.r(i),e.d(i,{default:function(){return k}});var n=e(641),o=e(3751),s=e(33);const l={class:"position-management"},d=["onClick"],a=["onClick"];function u(t,i,e,u,c,h){return(0,n.uX)(),(0,n.CE)("div",l,[i[6]||(i[6]=(0,n.Lk)("h2",null,"Verwalten der Rollen",-1)),(0,n.Lk)("form",{onSubmit:i[2]||(i[2]=(0,o.D$)((...t)=>h.addPosition&&h.addPosition(...t),["prevent"]))},[i[3]||(i[3]=(0,n.Lk)("label",{for:"caption"},"Rollenbezeichnung:",-1)),(0,n.bo)((0,n.Lk)("input",{type:"text",id:"caption","onUpdate:modelValue":i[0]||(i[0]=t=>c.newPosition.caption=t),placeholder:"Rollenbezeichnung",required:""},null,512),[[o.Jo,c.newPosition.caption]]),i[4]||(i[4]=(0,n.Lk)("button",{type:"submit"},"Speichern",-1)),c.editMode?((0,n.uX)(),(0,n.CE)("button",{key:0,type:"button",onClick:i[1]||(i[1]=(...t)=>h.resetForm&&h.resetForm(...t))},"Neue Rolle erstellen")):(0,n.Q3)("",!0)],32),(0,n.Lk)("table",null,[i[5]||(i[5]=(0,n.Lk)("thead",null,[(0,n.Lk)("tr",null,[(0,n.Lk)("th",null,"Rollenbezeichnung"),(0,n.Lk)("th",null,"Bearbeiten"),(0,n.Lk)("th",null,"Löschen")])],-1)),(0,n.Lk)("tbody",null,[((0,n.uX)(!0),(0,n.CE)(n.FK,null,(0,n.pI)(c.positions,t=>((0,n.uX)(),(0,n.CE)("tr",{key:t.id},[(0,n.Lk)("td",null,(0,s.v_)(t.caption),1),(0,n.Lk)("td",null,[(0,n.Lk)("button",{onClick:i=>h.editPosition(t)},"Bearbeiten",8,d)]),(0,n.Lk)("td",null,[(0,n.Lk)("button",{onClick:i=>h.deletePosition(t.id)},"Löschen",8,a)])]))),128))])])])}e(4114);var c=e(4335),h={data(){return{positions:[],newPosition:{caption:""},editMode:!1,editId:null}},methods:{async fetchPositions(){const t=await c.A.get("/positions");this.positions=t.data},async addPosition(){if(this.editMode)await c.A.put(`/positions/${this.editId}`,this.newPosition);else{const t=await c.A.post("/positions",this.newPosition);this.positions.push(t.data)}this.resetForm(),await this.fetchPositions()},async updatePosition(t){await c.A.put(`/positions/${t.id}`,t),this.fetchPositions()},async deletePosition(t){await c.A.delete(`/positions/${t}`),this.fetchPositions()},editPosition(t){this.newPosition={...t},this.editMode=!0,this.editId=t.id},resetForm(){this.newPosition={caption:""},this.editMode=!1,this.editId=null}},created(){this.fetchPositions()}},r=e(6262);const p=(0,r.A)(h,[["render",u],["__scopeId","data-v-1684a375"]]);var k=p}}]);
//# sourceMappingURL=441.ef14f2c9.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
"use strict";(self["webpackChunkmiriamgemeinde"]=self["webpackChunkmiriamgemeinde"]||[]).push([[446],{446:function(e,a,l){l.r(a),l.d(a,{default:function(){return w}});var t=l(641),n=l(3751),i=l(33);const o={class:"upload-files"},r={class:"file-list"},u={class:"file-info"},d=["onClick"],s=["onClick"],c={class:"file-date"};function f(e,a,l,f,h,p){return(0,t.uX)(),(0,t.CE)("div",o,[a[5]||(a[5]=(0,t.Lk)("h2",null,"Dateien hochladen",-1)),(0,t.Lk)("div",null,[a[3]||(a[3]=(0,t.Lk)("label",{for:"file-upload"},"Datei auswählen:",-1)),(0,t.Lk)("input",{id:"file-upload",type:"file",onChange:a[0]||(a[0]=(...e)=>f.handleFileUpload&&f.handleFileUpload(...e))},null,32)]),(0,t.Lk)("div",null,[a[4]||(a[4]=(0,t.Lk)("label",{for:"file-title"},"Titel eingeben:",-1)),(0,t.bo)((0,t.Lk)("input",{id:"file-title",type:"text","onUpdate:modelValue":a[1]||(a[1]=e=>f.fileTitle=e)},null,512),[[n.Jo,f.fileTitle]])]),(0,t.Lk)("button",{onClick:a[2]||(a[2]=(...e)=>f.uploadFiles&&f.uploadFiles(...e))},"Hochladen"),(0,t.Lk)("ul",r,[((0,t.uX)(!0),(0,t.CE)(t.FK,null,(0,t.pI)(f.uploadedFiles,e=>((0,t.uX)(),(0,t.CE)("li",{key:e.id},[(0,t.Lk)("div",u,[(0,t.Lk)("span",{class:"file-title",onClick:a=>f.downloadFile(e)},(0,i.v_)(e.title),9,d),(0,t.Lk)("span",{class:"file-name",onClick:a=>f.downloadFile(e)},(0,i.v_)(e.originalName),9,s),(0,t.Lk)("span",c,(0,i.v_)(f.formatDate(e.createdAt)),1)])]))),128))])])}l(4114),l(4603),l(7566),l(8721);var h=l(953),p=l(6296),v={name:"UploadFilesComponent",setup(){const e=(0,h.KR)(null),a=(0,h.KR)(""),l=(0,h.KR)([]),n=a=>{e.value=a.target.files[0]},i=async()=>{if(!e.value||!a.value)return void alert("Bitte wählen Sie eine Datei aus und geben Sie einen Titel ein.");const t=new FormData;t.append("file",e.value),t.append("title",a.value);try{const n=await p.A.post("/files",t,{headers:{"Content-Type":"multipart/form-data"}}),i=n.data;l.value.push({id:i.id,title:i.title,originalName:i.originalName,createdAt:i.createdAt,hash:i.hash}),e.value=null,a.value=""}catch(n){console.error("Fehler beim Hochladen der Datei:",n)}},o=async e=>{const a=e.originalName.substring(e.originalName.lastIndexOf(".")),l=await p.A.get(`/files/download/${e.hash}`,{responseType:"blob"}),t=new Blob([l.data],{type:l.data.type}),n=document.createElement("a");n.href=window.URL.createObjectURL(t),n.download=`${e.title}${a}`,n.click(),window.URL.revokeObjectURL(n.href)},r=e=>{const a={year:"numeric",month:"long",day:"numeric"};return new Date(e).toLocaleDateString(void 0,a)},u=async()=>{try{const e=await p.A.get("/files");l.value=e.data}catch(e){console.error("Fehler beim Abrufen der Dateien:",e)}};return(0,t.sV)(u),{fileToUpload:e,fileTitle:a,uploadedFiles:l,handleFileUpload:n,uploadFiles:i,downloadFile:o,formatDate:r}}},m=l(6262);const k=(0,m.A)(v,[["render",f],["__scopeId","data-v-f2694614"]]);var w=k},2812:function(e){var a=TypeError;e.exports=function(e,l){if(e<l)throw new a("Not enough arguments");return e}},4603:function(e,a,l){var t=l(6840),n=l(9504),i=l(655),o=l(2812),r=URLSearchParams,u=r.prototype,d=n(u.append),s=n(u["delete"]),c=n(u.forEach),f=n([].push),h=new r("a=1&a=2&b=3");h["delete"]("a",1),h["delete"]("b",void 0),h+""!=="a=2"&&t(u,"delete",function(e){var a=arguments.length,l=a<2?void 0:arguments[1];if(a&&void 0===l)return s(this,e);var t=[];c(this,function(e,a){f(t,{key:a,value:e})}),o(a,1);var n,r=i(e),u=i(l),h=0,p=0,v=!1,m=t.length;while(h<m)n=t[h++],v||n.key===r?(v=!0,s(this,n.key)):p++;while(p<m)n=t[p++],n.key===r&&n.value===u||d(this,n.key,n.value)},{enumerable:!0,unsafe:!0})},7566:function(e,a,l){var t=l(6840),n=l(9504),i=l(655),o=l(2812),r=URLSearchParams,u=r.prototype,d=n(u.getAll),s=n(u.has),c=new r("a=1");!c.has("a",2)&&c.has("a",void 0)||t(u,"has",function(e){var a=arguments.length,l=a<2?void 0:arguments[1];if(a&&void 0===l)return s(this,e);var t=d(this,e);o(a,1);var n=i(l),r=0;while(r<t.length)if(t[r++]===n)return!0;return!1},{enumerable:!0,unsafe:!0})},8721:function(e,a,l){var t=l(3724),n=l(9504),i=l(2106),o=URLSearchParams.prototype,r=n(o.forEach);t&&!("size"in o)&&i(o,"size",{get:function(){var e=0;return r(this,function(){e++}),e},configurable:!0,enumerable:!0})}}]);
//# sourceMappingURL=446.a766133d.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
"use strict";(self["webpackChunkmiriamgemeinde"]=self["webpackChunkmiriamgemeinde"]||[]).push([[448],{7448:function(e,n,i){i.r(n),i.d(n,{default:function(){return d}});var t=i(641);function r(e,n,i,r,u,a){return(0,t.uX)(),(0,t.CE)("div",null,[...n[0]||(n[0]=[(0,t.Lk)("h1",null,"Seite existiert nicht",-1),(0,t.Lk)("p",null,"Leider existiert die aufgerufene Seite nicht.",-1)])])}var u={name:"DefaultComponent"},a=i(6262);const c=(0,a.A)(u,[["render",r],["__scopeId","data-v-334e7b82"]]);var d=c}}]);
//# sourceMappingURL=448.74e93fb6.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"js/448.74e93fb6.js","mappings":"+NACIA,EAAAA,EAAAA,IAGM,eAAAC,EAAA,KAAAA,EAAA,KAFJC,EAAAA,EAAAA,IAA8B,UAA1B,yBAAqB,IACzBA,EAAAA,EAAAA,IAAoD,SAAjD,iDAA6C,M,CAKpD,OACEC,KAAM,oB,UCFV,MAAMC,GAA2B,OAAgB,EAAQ,CAAC,CAAC,SAASC,GAAQ,CAAC,YAAY,qBAEzF,O","sources":["webpack://miriamgemeinde/./src/content/DefaultComponent.vue","webpack://miriamgemeinde/./src/content/DefaultComponent.vue?e630"],"sourcesContent":["<template>\n <div>\n <h1>Seite existiert nicht</h1>\n <p>Leider existiert die aufgerufene Seite nicht.</p>\n </div>\n </template>\n \n <script>\n export default {\n name: 'DefaultComponent'\n };\n </script>\n \n <style scoped>\n div {\n padding: 20px;\n }\n </style>\n ","import { render } from \"./DefaultComponent.vue?vue&type=template&id=334e7b82&scoped=true\"\nimport script from \"./DefaultComponent.vue?vue&type=script&lang=js\"\nexport * from \"./DefaultComponent.vue?vue&type=script&lang=js\"\n\nimport \"./DefaultComponent.vue?vue&type=style&index=0&id=334e7b82&scoped=true&lang=css\"\n\nimport exportComponent from \"../../node_modules/vue-loader/dist/exportHelper.js\"\nconst __exports__ = /*#__PURE__*/exportComponent(script, [['render',render],['__scopeId',\"data-v-334e7b82\"]])\n\nexport default __exports__"],"names":["_createElementBlock","_cache","_createElementVNode","name","__exports__","render"],"ignoreList":[],"sourceRoot":""}

View File

@@ -0,0 +1,2 @@
"use strict";(self["webpackChunkmiriamgemeinde"]=self["webpackChunkmiriamgemeinde"]||[]).push([[468],{5468:function(e,r,t){t.r(r),t.d(r,{default:function(){return k}});var s=t(641),n=t(33),i=t(3751);const a={class:"user-administration"},u=["required"],l={type:"submit"},o={key:1},c=["onClick"];function d(e,r,t,d,m,h){return(0,s.uX)(),(0,s.CE)("div",a,[r[11]||(r[11]=(0,s.Lk)("h1",null,"Benutzerverwaltung",-1)),(0,s.Lk)("h2",null,(0,n.v_)(h.formTitle),1),(0,s.Lk)("form",{onSubmit:r[4]||(r[4]=(0,i.D$)((...e)=>h.saveUser&&h.saveUser(...e),["prevent"]))},[r[7]||(r[7]=(0,s.Lk)("label",{for:"name"},"Name:",-1)),(0,s.bo)((0,s.Lk)("input",{id:"name","onUpdate:modelValue":r[0]||(r[0]=e=>m.currentUser.name=e),required:""},null,512),[[i.Jo,m.currentUser.name]]),r[8]||(r[8]=(0,s.Lk)("label",{for:"email"},"Email:",-1)),(0,s.bo)((0,s.Lk)("input",{id:"email","onUpdate:modelValue":r[1]||(r[1]=e=>m.currentUser.email=e),type:"email",required:""},null,512),[[i.Jo,m.currentUser.email]]),r[9]||(r[9]=(0,s.Lk)("label",{for:"password"},"Passwort:",-1)),(0,s.bo)((0,s.Lk)("input",{id:"password","onUpdate:modelValue":r[2]||(r[2]=e=>m.currentUser.password=e),type:"password",required:m.isCreating},null,8,u),[[i.Jo,m.currentUser.password]]),(0,s.Lk)("div",null,[r[6]||(r[6]=(0,s.Lk)("label",{for:"active"},"Aktiv:",-1)),(0,s.bo)((0,s.Lk)("input",{id:"active","onUpdate:modelValue":r[3]||(r[3]=e=>m.currentUser.active=e),type:"checkbox"},null,512),[[i.lH,m.currentUser.active]])]),(0,s.Lk)("button",l,(0,n.v_)(m.isCreating?"Erstellen":"Aktualisieren"),1)],32),m.isCreating?(0,s.Q3)("",!0):((0,s.uX)(),(0,s.CE)("button",{key:0,onClick:r[5]||(r[5]=(...e)=>h.resetForm&&h.resetForm(...e))},"Zurück zu Benutzer erstellen")),m.users.length?((0,s.uX)(),(0,s.CE)("div",o,[r[10]||(r[10]=(0,s.Lk)("h2",null,"Vorhandene Benutzer",-1)),(0,s.Lk)("ul",null,[((0,s.uX)(!0),(0,s.CE)(s.FK,null,(0,s.pI)(m.users,e=>((0,s.uX)(),(0,s.CE)("li",{key:e.id,onClick:r=>h.editUser(e)},(0,n.v_)(e.name)+" ("+(0,n.v_)(e.email)+") ",9,c))),128))])])):(0,s.Q3)("",!0)])}var m=t(6296),h={name:"UserAdministration",data(){return{users:[],currentUser:{name:"",email:"",password:"",active:!1},isCreating:!0}},computed:{formTitle(){return this.isCreating?"Benutzer erstellen":"Benutzer bearbeiten"}},methods:{async fetchUsers(){try{const e=await m.A.get("/users");this.users=e.data}catch(e){console.error("Fehler beim Abrufen der Benutzer:",e)}},async saveUser(){this.isCreating?await this.createUser():await this.updateUser(),this.resetForm(),this.fetchUsers()},async createUser(){try{await m.A.post("/users",this.currentUser)}catch(e){console.error("Fehler beim Erstellen des Benutzers:",e)}},async updateUser(){try{await m.A.put(`/users/${this.currentUser.id}`,this.currentUser)}catch(e){console.error("Fehler beim Aktualisieren des Benutzers:",e)}},editUser(e){this.currentUser={...e,password:""},this.isCreating=!1},resetForm(){this.currentUser={name:"",email:"",password:"",active:!1},this.isCreating=!0}},mounted(){this.fetchUsers()}},p=t(6262);const U=(0,p.A)(h,[["render",d],["__scopeId","data-v-a495c756"]]);var k=U}}]);
//# sourceMappingURL=468.c02c45c3.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
"use strict";(self["webpackChunkmiriamgemeinde"]=self["webpackChunkmiriamgemeinde"]||[]).push([[493],{5493:function(e,i,s){s.r(i),s.d(i,{default:function(){return h}});var a=s(641),t=s(3751),o=s(33);const l={class:"register"},r={key:0,class:"dialog"},n={class:"dialog-content"};function d(e,i,s,d,g,u){const m=(0,a.g2)("router-link");return(0,a.uX)(),(0,a.CE)("div",l,[i[11]||(i[11]=(0,a.Lk)("h2",null,"Registrieren",-1)),(0,a.Lk)("form",{onSubmit:i[3]||(i[3]=(0,t.D$)((...e)=>u.register&&u.register(...e),["prevent"]))},[i[5]||(i[5]=(0,a.Lk)("label",{for:"name"},"Name:",-1)),(0,a.bo)((0,a.Lk)("input",{type:"text",id:"name","onUpdate:modelValue":i[0]||(i[0]=e=>g.name=e),required:""},null,512),[[t.Jo,g.name]]),i[6]||(i[6]=(0,a.Lk)("label",{for:"email"},"Email-Adresse:",-1)),(0,a.bo)((0,a.Lk)("input",{type:"email",id:"email","onUpdate:modelValue":i[1]||(i[1]=e=>g.email=e),required:""},null,512),[[t.Jo,g.email]]),i[7]||(i[7]=(0,a.Lk)("label",{for:"password"},"Passwort:",-1)),(0,a.bo)((0,a.Lk)("input",{type:"password",id:"password","onUpdate:modelValue":i[2]||(i[2]=e=>g.password=e),required:""},null,512),[[t.Jo,g.password]]),i[8]||(i[8]=(0,a.Lk)("button",{type:"submit"},"Registrieren",-1))],32),(0,a.Lk)("p",null,[(0,a.bF)(m,{to:"/login"},{default:(0,a.k6)(()=>[...i[9]||(i[9]=[(0,a.eW)("Login",-1)])]),_:1})]),(0,a.Lk)("p",null,[(0,a.bF)(m,{to:"/forgot-password"},{default:(0,a.k6)(()=>[...i[10]||(i[10]=[(0,a.eW)("Passwort vergessen?",-1)])]),_:1})]),g.dialogVisible?((0,a.uX)(),(0,a.CE)("div",r,[(0,a.Lk)("div",n,[(0,a.Lk)("h3",null,(0,o.v_)(g.dialogTitle),1),(0,a.Lk)("p",null,(0,o.v_)(g.dialogMessage),1),(0,a.Lk)("button",{type:"button",onClick:i[4]||(i[4]=(...e)=>u.closeDialog&&u.closeDialog(...e))},"Schließen")])])):(0,a.Q3)("",!0)])}var g=s(6296),u={name:"RegisterComponent",components:{},data(){return{name:"",email:"",password:"",dialogTitle:"",dialogMessage:"",dialogVisible:!1}},methods:{async register(){try{const e=await g.A.post("/auth/register",{name:this.name,email:this.email,password:this.password});this.showDialog("Registrierung erfolgreich",e.data?.message||"Ihr Konto wurde erfolgreich erstellt."),this.name="",this.email="",this.password=""}catch(e){const i=e?.response?.data?.message||e?.message||"Ein unbekannter Fehler ist aufgetreten";this.showDialog("Fehler",i)}},showDialog(e,i){this.dialogTitle=e,this.dialogMessage=i,this.dialogVisible=!0},closeDialog(){this.dialogVisible=!1}}},m=s(6262);const p=(0,m.A)(u,[["render",d],["__scopeId","data-v-63b3c0a3"]]);var h=p}}]);
//# sourceMappingURL=493.716a232a.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
"use strict";(self["webpackChunkmiriamgemeinde"]=self["webpackChunkmiriamgemeinde"]||[]).push([[355,702],{5355:function(e,o,a){a.r(o),a.d(o,{default:function(){return g}});var i=a(641),l=a(33);const s={key:0,class:"dialog-overlay"},t={class:"dialog"};function n(e,o,a,n,r,d){return a.modelValue?((0,i.uX)(),(0,i.CE)("div",s,[(0,i.Lk)("div",t,[(0,i.Lk)("h2",null,(0,l.v_)(a.title),1),(0,i.Lk)("p",null,(0,l.v_)(a.message),1),(0,i.Lk)("button",{onClick:o[0]||(o[0]=(...e)=>d.closeDialog&&d.closeDialog(...e))},"OK")])])):(0,i.Q3)("",!0)}var r={name:"DialogComponent",props:{title:{type:String,required:!0},message:{type:String,required:!0},modelValue:{type:Boolean,default:!1}},methods:{closeDialog(){this.$emit("update:modelValue",!1),this.$emit("close")}}},d=a(6262);const u=(0,d.A)(r,[["render",n],["__scopeId","data-v-ce9d9498"]]);var g=u},9702:function(e,o,a){a.r(o),a.d(o,{default:function(){return p}});var i=a(641),l=a(3751);const s={class:"login"};function t(e,o,a,t,n,r){const d=(0,i.g2)("router-link"),u=(0,i.g2)("DialogComponent");return(0,i.uX)(),(0,i.CE)("div",s,[o[9]||(o[9]=(0,i.Lk)("h2",null,"Login",-1)),(0,i.Lk)("form",{onSubmit:o[2]||(o[2]=(0,l.D$)((...e)=>r.runLogin&&r.runLogin(...e),["prevent"]))},[o[4]||(o[4]=(0,i.Lk)("label",{for:"email"},"Email-Adresse:",-1)),(0,i.bo)((0,i.Lk)("input",{type:"email",id:"email","onUpdate:modelValue":o[0]||(o[0]=e=>n.email=e),required:""},null,512),[[l.Jo,n.email]]),o[5]||(o[5]=(0,i.Lk)("label",{for:"password"},"Passwort:",-1)),(0,i.bo)((0,i.Lk)("input",{type:"password",id:"password","onUpdate:modelValue":o[1]||(o[1]=e=>n.password=e),required:""},null,512),[[l.Jo,n.password]]),o[6]||(o[6]=(0,i.Lk)("button",{type:"submit"},"Login",-1))],32),(0,i.Lk)("p",null,[(0,i.bF)(d,{to:"/register"},{default:(0,i.k6)(()=>[...o[7]||(o[7]=[(0,i.eW)("Registrieren",-1)])]),_:1})]),(0,i.Lk)("p",null,[(0,i.bF)(d,{to:"/forgot-password"},{default:(0,i.k6)(()=>[...o[8]||(o[8]=[(0,i.eW)("Passwort vergessen?",-1)])]),_:1})]),(0,i.bF)(u,{title:n.dialogTitle,message:n.dialogMessage,modelValue:n.dialogVisible,"onUpdate:modelValue":o[3]||(o[3]=e=>n.dialogVisible=e),onClose:r.closeDialog},null,8,["title","message","modelValue","onClose"])])}a(4114);var n=a(4335),r=a(5355),d=a(6278),u={name:"LoginComponent",components:{DialogComponent:r["default"]},data(){return{email:"",password:"",dialogTitle:"",dialogMessage:"",dialogVisible:!1}},methods:{...(0,d.i0)(["login"]),async runLogin(){try{const e=await n.A.post("/auth/login",{email:this.email,password:this.password}),o=e.data.token,a=e.data;localStorage.setItem("token",o),this.login(a.user),n.A.defaults.headers.common["Authorization"]=`Bearer ${o}`,this.$router.push("/admin/index")}catch(e){e.response?this.showDialog("Fehler",e.response.data.message):this.showDialog("Ein Fehler ist aufgetreten",e.message)}},showDialog(e,o){this.dialogTitle=e,this.dialogMessage=o,this.dialogVisible=!0},closeDialog(){this.dialogVisible=!1}}},g=a(6262);const m=(0,g.A)(u,[["render",t],["__scopeId","data-v-40a158c0"]]);var p=m}}]);
//# sourceMappingURL=702.dce87a34.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
"use strict";(self["webpackChunkmiriamgemeinde"]=self["webpackChunkmiriamgemeinde"]||[]).push([[768],{1768:function(e,n,i){i.r(n),i.d(n,{default:function(){return o}});var r=i(641),t=i(33);const a={class:"service-unavailable"};function s(e,n,i,s,c,u){return(0,r.uX)(),(0,r.CE)("div",a,[n[0]||(n[0]=(0,r.Lk)("h1",null,"Vorübergehend nicht erreichbar",-1)),(0,r.Lk)("p",null,(0,t.v_)(u.message),1),n[1]||(n[1]=(0,r.Lk)("p",{class:"hint"}," Es liegt vermutlich ein technisches Problem vor (z. B. die Datenbank). Bitte laden Sie die Seite in einigen Minuten erneut. ",-1))])}var c=i(6278),u={name:"ServiceUnavailableComponent",computed:{...(0,c.L8)(["menuLoadError"]),message(){return this.menuLoadError||"Die Website ist vorübergehend nicht vollständig erreichbar."}}},l=i(6262);const d=(0,l.A)(u,[["render",s],["__scopeId","data-v-fd9b1088"]]);var o=d}}]);
//# sourceMappingURL=768.d801babf.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"js/768.d801babf.js","mappings":"sMACOA,MAAM,uB,0CAAXC,EAAAA,EAAAA,IAMM,MANNC,EAMM,C,aALJC,EAAAA,EAAAA,IAAuC,UAAnC,kCAA8B,KAClCA,EAAAA,EAAAA,IAAoB,UAAAC,EAAAA,EAAAA,IAAdC,EAAAC,SAAO,G,aACbH,EAAAA,EAAAA,IAEI,KAFDH,MAAM,QAAO,iIAEhB,K,eAOJ,GACEO,KAAM,8BACNC,SAAU,KACLC,EAAAA,EAAAA,IAAW,CAAC,kBACfH,OAAAA,GACE,OACEI,KAAKC,eACL,6DAEJ,I,UCfJ,MAAMC,GAA2B,OAAgB,EAAQ,CAAC,CAAC,SAASC,GAAQ,CAAC,YAAY,qBAEzF,O","sources":["webpack://miriamgemeinde/./src/content/ServiceUnavailableComponent.vue","webpack://miriamgemeinde/./src/content/ServiceUnavailableComponent.vue?7a9d"],"sourcesContent":["<template>\n <div class=\"service-unavailable\">\n <h1>Vorübergehend nicht erreichbar</h1>\n <p>{{ message }}</p>\n <p class=\"hint\">\n Es liegt vermutlich ein technisches Problem vor (z.&nbsp;B. die Datenbank). Bitte laden Sie die Seite in einigen Minuten erneut.\n </p>\n </div>\n</template>\n\n<script>\nimport { mapGetters } from 'vuex';\n\nexport default {\n name: 'ServiceUnavailableComponent',\n computed: {\n ...mapGetters(['menuLoadError']),\n message() {\n return (\n this.menuLoadError ||\n 'Die Website ist vorübergehend nicht vollständig erreichbar.'\n );\n },\n },\n};\n</script>\n\n<style scoped>\n.service-unavailable {\n padding: 20px;\n}\n.hint {\n color: #444;\n margin-top: 1rem;\n}\n</style>\n","import { render } from \"./ServiceUnavailableComponent.vue?vue&type=template&id=fd9b1088&scoped=true\"\nimport script from \"./ServiceUnavailableComponent.vue?vue&type=script&lang=js\"\nexport * from \"./ServiceUnavailableComponent.vue?vue&type=script&lang=js\"\n\nimport \"./ServiceUnavailableComponent.vue?vue&type=style&index=0&id=fd9b1088&scoped=true&lang=css\"\n\nimport exportComponent from \"../../node_modules/vue-loader/dist/exportHelper.js\"\nconst __exports__ = /*#__PURE__*/exportComponent(script, [['render',render],['__scopeId',\"data-v-fd9b1088\"]])\n\nexport default __exports__"],"names":["class","_createElementBlock","_hoisted_1","_createElementVNode","_toDisplayString","$options","message","name","computed","mapGetters","this","menuLoadError","__exports__","render"],"ignoreList":[],"sourceRoot":""}

View File

@@ -0,0 +1,2 @@
"use strict";(self["webpackChunkmiriamgemeinde"]=self["webpackChunkmiriamgemeinde"]||[]).push([[785],{3149:function(n,t,e){e.r(t),e.d(t,{default:function(){return k}});var o=e(641);const r={class:"some-page"};function i(n,t,e,i,u,a){const c=(0,o.g2)("ContentComponent");return(0,o.uX)(),(0,o.CE)("div",r,[(0,o.bF)(c,{link:a.currentLink},null,8,["link"])])}var u=e(33);function a(n,t,e,r,i,a){const c=(0,o.g2)("RenderContentComponent");return(0,o.uX)(),(0,o.CE)("div",null,[(0,o.Lk)("h1",null,(0,u.v_)(i.title),1),(0,o.bF)(c,{content:i.content},null,8,["content"])])}var c=e(6296),s=e(6278),l=e(6562),m={name:"ContentComponent",components:{RenderContentComponent:l.A},props:{link:{type:String,required:!0}},data(){return{content:"",title:""}},computed:{...(0,s.aH)(["menuData"]),...(0,s.L8)(["getMenuData"])},watch:{link:{immediate:!0,handler(n){this.fetchContent(n),this.setTitle(n)}}},methods:{async fetchContent(n){try{const t=await c.A.get(`/page-content?link=${n}`);this.content=t.data.content}catch(t){console.error("Fehler beim Abrufen des Inhalts:",t)}},setTitle(n){const t=(n,e)=>{for(const o of n){if(o.link===e)return o.pageTitle||o.name;if(o.submenu&&o.submenu.length>0){const n=t(o.submenu,e);if(n)return`${n}`}}return""};this.title=t(this.menuData,n)}}},h=e(6262);const d=(0,h.A)(m,[["render",a]]);var p=d,f={name:"DefaultPage",components:{ContentComponent:p},computed:{currentLink(){return this.$route.path}}};const C=(0,h.A)(f,[["render",i]]);var k=C}}]);
//# sourceMappingURL=785.0c674ce5.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
"use strict";(self["webpackChunkmiriamgemeinde"]=self["webpackChunkmiriamgemeinde"]||[]).push([[814],{9644:function(e,n,t){t.r(n),t.d(n,{default:function(){return m}});var a=t(641),s=t(33);function u(e,n,t,u,i,o){const r=(0,a.g2)("RenderContentComponent");return(0,a.uX)(),(0,a.CE)("div",null,[n[0]||(n[0]=(0,a.Lk)("div",{class:"previewinfo"},"Dies ist eine Vorschau.",-1)),(0,a.Lk)("h1",null,(0,s.v_)(u.title),1),(0,a.bF)(r,{content:u.content},null,8,["content"])])}var i=t(6278),o=t(6562),r={name:"PagePreview",components:{RenderContentComponent:o.A},setup(){const e=(0,i.Pj)(),n=(0,a.EW)(()=>e.state.pageContent),t=(0,a.EW)(()=>e.state.selectedPage),s=(0,a.EW)(()=>e.state.menuData),u=(0,a.EW)(()=>e.state.pageTitle),o=n=>{const t=(e,n)=>{for(const a of e){if(a.link===n)return a.pageTitle||a.name;if(a.submenu&&a.submenu.length>0){const e=t(a.submenu,n);if(e)return e}}return""};e.dispatch("setPageTitle",t(s.value,n))};return(0,a.nT)(()=>{o(t.value)}),{content:n,title:u}}},c=t(6262);const l=(0,c.A)(r,[["render",u],["__scopeId","data-v-9a71cbf6"]]);var m=l}}]);
//# sourceMappingURL=814.fc451eae.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"js/814.fc451eae.js","mappings":"kRACEA,EAAAA,EAAAA,IAIM,Y,aAHJC,EAAAA,EAAAA,IAAsD,OAAjDC,MAAM,eAAc,2BAAuB,KAChDD,EAAAA,EAAAA,IAAoB,WAAAE,EAAAA,EAAAA,IAAbC,EAAAC,OAAK,IACZC,EAAAA,EAAAA,IAA6CC,EAAA,CAApBC,QAASJ,EAAAI,SAAO,qB,yBAS7C,GACEC,KAAM,cACNC,WAAY,CACVC,uBAAsBA,EAAAA,GAExBC,KAAAA,GACE,MAAMC,GAAQC,EAAAA,EAAAA,MACRN,GAAUO,EAAAA,EAAAA,IAAS,IAAMF,EAAMG,MAAMC,aACrCC,GAAeH,EAAAA,EAAAA,IAAS,IAAMF,EAAMG,MAAME,cAC1CC,GAAWJ,EAAAA,EAAAA,IAAS,IAAMF,EAAMG,MAAMG,UACtCd,GAAQU,EAAAA,EAAAA,IAAS,IAAMF,EAAMG,MAAMI,WAEnCC,EAAYC,IAChB,MAAMC,EAAYA,CAACC,EAAWF,KAC5B,IAAK,MAAMG,KAAQD,EAAW,CAC5B,GAAIC,EAAKH,OAASA,EAChB,OAAOG,EAAKL,WAAaK,EAAKhB,KAEhC,GAAIgB,EAAKC,SAAWD,EAAKC,QAAQC,OAAS,EAAG,CAC3C,MAAMC,EAAQL,EAAUE,EAAKC,QAASJ,GACtC,GAAIM,EACF,OAAOA,CAEX,CACF,CACA,MAAO,IAETf,EAAMgB,SAAS,eAAgBN,EAAUJ,EAASW,MAAOR,KAO3D,OAJAS,EAAAA,EAAAA,IAAY,KACVV,EAASH,EAAaY,SAGjB,CACLtB,UACAH,QAEJ,G,UC5CF,MAAM2B,GAA2B,OAAgB,EAAQ,CAAC,CAAC,SAASC,GAAQ,CAAC,YAAY,qBAEzF,O","sources":["webpack://miriamgemeinde/./src/content/admin/PagePreviewComponent.vue","webpack://miriamgemeinde/./src/content/admin/PagePreviewComponent.vue?4023"],"sourcesContent":["<template>\n <div>\n <div class=\"previewinfo\">Dies ist eine Vorschau.</div>\n <h1>{{ title }}</h1>\n <RenderContentComponent :content=\"content\" />\n </div>\n</template>\n\n<script>\nimport { computed, watchEffect } from 'vue';\nimport { useStore } from 'vuex';\nimport RenderContentComponent from '@/components/RenderContentComponent.vue';\n\nexport default {\n name: 'PagePreview',\n components: {\n RenderContentComponent\n },\n setup() {\n const store = useStore();\n const content = computed(() => store.state.pageContent);\n const selectedPage = computed(() => store.state.selectedPage);\n const menuData = computed(() => store.state.menuData);\n const title = computed(() => store.state.pageTitle);\n\n const setTitle = (link) => {\n const findTitle = (menuItems, link) => {\n for (const item of menuItems) {\n if (item.link === link) {\n return item.pageTitle || item.name;\n }\n if (item.submenu && item.submenu.length > 0) {\n const found = findTitle(item.submenu, link);\n if (found) {\n return found;\n }\n }\n }\n return '';\n };\n store.dispatch('setPageTitle', findTitle(menuData.value, link));\n };\n\n watchEffect(() => {\n setTitle(selectedPage.value);\n });\n\n return {\n content,\n title\n };\n }\n};\n</script>\n\n<style scoped>\n.previewinfo {\n background-color: black;\n color: #d00000;\n position: absolute;\n top: 93px;\n left: 0;\n padding: 2px 10px;\n font-weight: bold;\n}\n</style>\n","import { render } from \"./PagePreviewComponent.vue?vue&type=template&id=9a71cbf6&scoped=true\"\nimport script from \"./PagePreviewComponent.vue?vue&type=script&lang=js\"\nexport * from \"./PagePreviewComponent.vue?vue&type=script&lang=js\"\n\nimport \"./PagePreviewComponent.vue?vue&type=style&index=0&id=9a71cbf6&scoped=true&lang=css\"\n\nimport exportComponent from \"../../../node_modules/vue-loader/dist/exportHelper.js\"\nconst __exports__ = /*#__PURE__*/exportComponent(script, [['render',render],['__scopeId',\"data-v-9a71cbf6\"]])\n\nexport default __exports__"],"names":["_createElementBlock","_createElementVNode","class","_toDisplayString","$setup","title","_createVNode","_component_RenderContentComponent","content","name","components","RenderContentComponent","setup","store","useStore","computed","state","pageContent","selectedPage","menuData","pageTitle","setTitle","link","findTitle","menuItems","item","submenu","length","found","dispatch","value","watchEffect","__exports__","render"],"ignoreList":[],"sourceRoot":""}

2
public/js/93.8cb43993.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
public/js/93.d6ceb929.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
"use strict";(self["webpackChunkmiriamgemeinde"]=self["webpackChunkmiriamgemeinde"]||[]).push([[957],{8957:function(e,t,a){a.r(t),a.d(t,{default:function(){return d}});var n=a(641);const u=["src"];function m(e,t,a,m,r,i){return(0,n.uX)(),(0,n.CE)("img",{src:r.currentImage},null,8,u)}var r=a(6278),i={name:"ImageContent",data(){return{defaultImage:"/images/homepage1.png",currentImage:"/images/homepage1.png"}},computed:{...(0,r.aH)(["menuData"])},watch:{$route:{immediate:!0,handler(){this.updateImage()}}},methods:{updateImage(){const e=this.$route.path,t=this.menuData,a=this.findMenuItemByPath(t,e);a&&a.image?this.currentImage=`/images/${a.image}`:this.currentImage=this.defaultImage},findMenuItemByPath(e,t){for(let a of e){if(a.link===t)return a;if(a.submenu){const e=this.findMenuItemByPath(a.submenu,t);if(e)return e}}return null}}},s=a(6262);const g=(0,s.A)(i,[["render",m],["__scopeId","data-v-d1b58e08"]]);var d=g}}]);
//# sourceMappingURL=957.aa15187e.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"js/957.aa15187e.js","mappings":"+OACIA,EAAAA,EAAAA,IAA2B,OAArBC,IAAKC,EAAAC,cAAY,OAAAC,E,eAM3B,GACEC,KAAM,eACNC,IAAAA,GACE,MAAO,CACLC,aAAc,wBACdJ,aAAc,wBAElB,EACAK,SAAU,KACLC,EAAAA,EAAAA,IAAS,CAAC,cAEfC,MAAO,CACLC,OAAQ,CACNC,WAAW,EACXC,OAAAA,GACEC,KAAKC,aACP,IAGJC,QAAS,CACPD,WAAAA,GACE,MAAME,EAAYH,KAAKH,OAAOO,KACxBC,EAAWL,KAAKK,SAChBC,EAAWN,KAAKO,mBAAmBF,EAAUF,GAC/CG,GAAYA,EAASE,MACvBR,KAAKX,aAAe,WAAWiB,EAASE,QAExCR,KAAKX,aAAeW,KAAKP,YAE7B,EACAc,kBAAAA,CAAmBE,EAAML,GACvB,IAAK,IAAIM,KAAQD,EAAM,CACrB,GAAIC,EAAKC,OAASP,EAChB,OAAOM,EAET,GAAIA,EAAKE,QAAS,CAChB,MAAMC,EAAUb,KAAKO,mBAAmBG,EAAKE,QAASR,GACtD,GAAIS,EACF,OAAOA,CAEX,CACF,CACA,OAAO,IACT,I,UC3CJ,MAAMC,GAA2B,OAAgB,EAAQ,CAAC,CAAC,SAASC,GAAQ,CAAC,YAAY,qBAEzF,O","sources":["webpack://miriamgemeinde/./src/content/ImageContent.vue","webpack://miriamgemeinde/./src/content/ImageContent.vue?ee30"],"sourcesContent":["<template>\n <img :src=\"currentImage\" />\n</template>\n\n<script>\nimport { mapState } from 'vuex';\n\nexport default {\n name: 'ImageContent',\n data() {\n return {\n defaultImage: '/images/homepage1.png',\n currentImage: '/images/homepage1.png'\n };\n },\n computed: {\n ...mapState(['menuData']),\n },\n watch: {\n $route: {\n immediate: true,\n handler() {\n this.updateImage();\n }\n }\n },\n methods: {\n updateImage() {\n const routePath = this.$route.path;\n const menuData = this.menuData;\n const menuItem = this.findMenuItemByPath(menuData, routePath);\n if (menuItem && menuItem.image) {\n this.currentImage = `/images/${menuItem.image}`;\n } else {\n this.currentImage = this.defaultImage;\n }\n },\n findMenuItemByPath(menu, path) {\n for (let item of menu) {\n if (item.link === path) {\n return item;\n }\n if (item.submenu) {\n const subItem = this.findMenuItemByPath(item.submenu, path);\n if (subItem) {\n return subItem;\n }\n }\n }\n return null;\n }\n }\n};\n</script>\n\n<style scoped>\n.right-column h2 {\n text-align: center;\n color: #000;\n}\n\n.right-column img {\n display: block;\n margin: 0 auto;\n max-width: 100%;\n height: auto;\n}\n</style>\n","import { render } from \"./ImageContent.vue?vue&type=template&id=d1b58e08&scoped=true\"\nimport script from \"./ImageContent.vue?vue&type=script&lang=js\"\nexport * from \"./ImageContent.vue?vue&type=script&lang=js\"\n\nimport \"./ImageContent.vue?vue&type=style&index=0&id=d1b58e08&scoped=true&lang=css\"\n\nimport exportComponent from \"../../node_modules/vue-loader/dist/exportHelper.js\"\nconst __exports__ = /*#__PURE__*/exportComponent(script, [['render',render],['__scopeId',\"data-v-d1b58e08\"]])\n\nexport default __exports__"],"names":["_createElementBlock","src","$data","currentImage","_hoisted_1","name","data","defaultImage","computed","mapState","watch","$route","immediate","handler","this","updateImage","methods","routePath","path","menuData","menuItem","findMenuItemByPath","image","menu","item","link","submenu","subItem","__exports__","render"],"ignoreList":[],"sourceRoot":""}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -27,7 +27,7 @@ const filesRouter = require('./routes/files');
const liturgicalDaysRouter = require('./routes/liturgicalDays'); const liturgicalDaysRouter = require('./routes/liturgicalDays');
const app = express(); const app = express();
const PORT = parseInt(process.env.PORT, 10) || 3000; const PORT = parseInt(process.env.BACKEND_PORT || process.env.PORT, 10) || 3000;
// CORS mit Whitelist und tolerantem Fallback für fehlende Origin-Header // CORS mit Whitelist und tolerantem Fallback für fehlende Origin-Header
const allowedOrigins = (process.env.ALLOWED_ORIGINS || '') const allowedOrigins = (process.env.ALLOWED_ORIGINS || '')

View File

@@ -62,49 +62,36 @@ export default {
<style> <style>
/* eslint-disable */ /* eslint-disable */
html, /* Layout: globale Typografie/Farben in design-tokens.css */
body {
height: 100%;
margin: 0;
padding: 0;
background-color: #ffffff;
font-family: Arial, sans-serif;
width: 100%;
overflow-x: hidden; /* Prevent horizontal scrolling */
}
#app {
display: flex;
flex-direction: column;
height: 100%;
}
.content-section { .content-section {
flex: 1; flex: 1;
min-height: 0;
display: flex; display: flex;
color: #000; color: var(--color-text);
overflow-y: hidden; overflow-y: hidden;
} }
.left-column { .left-column {
flex: 1; flex: 1;
min-width: 1000px; min-width: 0;
margin: 0.5em 0 0.5em 0.5em; max-width: 100%;
padding-right: 0.5em; margin: var(--space-2) 0 var(--space-2) var(--space-2);
background-color: #ffffff; padding-right: var(--space-2);
background-color: var(--color-bg-page);
overflow-y: auto; overflow-y: auto;
} }
.right-column { .right-column {
flex: 1; flex: 1;
background-color: #d9e2f3; min-width: 0;
background-color: var(--color-bg-sidebar);
overflow-y: auto; overflow-y: auto;
margin: 0 7px 7px 0; margin: 0 var(--space-2) var(--space-2) 0;
} }
.right-column h2 { .right-column h2 {
text-align: center; text-align: center;
color: #000; color: var(--color-text);
} }
.right-column img { .right-column img {
@@ -117,8 +104,8 @@ body {
.right-column-overlay { .right-column-overlay {
max-height: 150px; max-height: 150px;
overflow-y: hidden; overflow-y: hidden;
margin-top: 10px; margin-top: var(--space-3);
background-color: #d9e2f3; background-color: var(--color-bg-sidebar);
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
@@ -136,7 +123,9 @@ body {
} }
.left-column { .left-column {
padding: 10px; padding: var(--space-3);
margin-left: 0;
margin-right: 0;
} }
.right-column { .right-column {
@@ -146,7 +135,7 @@ body {
.right-column-overlay { .right-column-overlay {
display: flex; display: flex;
max-height: 150px; max-height: 150px;
background-color: #ffffff; background-color: var(--color-bg-page);
} }
.right-column-overlay img { .right-column-overlay img {
@@ -163,7 +152,7 @@ body {
.left-column, .left-column,
.right-column { .right-column {
padding: 10px; padding: var(--space-3);
} }
.right-column { .right-column {

View File

@@ -0,0 +1,99 @@
/**
* Design-Tokens Phase 1 Farben (EKHN-Violett unverändert), Abstände, Typografie.
* Globale Basistyles für html/body/#app.
*/
:root {
/* Marke EKHN Primärfarben fix */
--color-brand-primary: #9400ff;
--color-brand-primary-hover: #7a00d1;
--color-brand-tint: #e0bfff;
/* Neutrale Flächen & Text */
--color-bg-page: #ffffff;
--color-bg-subtle: #f8f9fb;
--color-bg-sidebar: #d9e2f3;
--color-text: #1a1a1a;
--color-text-muted: #4a4a4a;
/* Footer (dunkler Grund) */
--color-footer-bg: #0b1735;
--color-footer-link: #e8edf5;
--color-footer-link-hover: #ffffff;
/* Abstände (4px-Raster) */
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-3: 0.75rem;
--space-4: 1rem;
--space-5: 1.25rem;
--space-6: 1.5rem;
--space-8: 2rem;
/* Typografie */
--font-family-base: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial,
sans-serif;
--font-size-body: 1rem;
--line-height-body: 1.5;
--font-size-h1: clamp(1.25rem, 2.5vw, 1.75rem);
--font-size-h2: clamp(1.15rem, 2vw, 1.35rem);
--font-size-h3: clamp(1.05rem, 1.5vw, 1.2rem);
--shadow-dropdown: 2px 2px 6px rgba(0, 0, 0, 0.22);
}
html,
body {
height: 100%;
margin: 0;
padding: 0;
width: 100%;
overflow-x: hidden;
}
body {
background-color: var(--color-bg-page);
color: var(--color-text);
font-family: var(--font-family-base);
font-size: var(--font-size-body);
line-height: var(--line-height-body);
}
#app {
display: flex;
flex-direction: column;
height: 100%;
}
/* Sanfte Standard-Hierarchie; Komponenten können gezielt überschreiben */
:where(h1) {
font-size: var(--font-size-h1);
font-weight: 600;
line-height: 1.25;
}
:where(h2) {
font-size: var(--font-size-h2);
font-weight: 600;
line-height: 1.3;
}
:where(h3) {
font-size: var(--font-size-h3);
font-weight: 600;
line-height: 1.35;
}
:focus-visible {
outline: 2px solid var(--color-brand-primary);
outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}

View File

@@ -0,0 +1,90 @@
<template>
<nav v-if="crumbs.length" class="breadcrumbs" aria-label="Brotkrumen">
<ol>
<li v-for="(c, idx) in crumbs" :key="c.to + '-' + idx">
<router-link v-if="idx < crumbs.length - 1" :to="c.to">{{ c.label }}</router-link>
<span v-else aria-current="page">{{ c.label }}</span>
</li>
</ol>
</nav>
</template>
<script>
import { computed } from 'vue';
import { useRoute } from 'vue-router';
import { useStore } from 'vuex';
function findLabel(menuItems, link) {
for (const item of menuItems) {
if (item?.link === link) {
return item.pageTitle || item.name || '';
}
if (item?.submenu?.length) {
const found = findLabel(item.submenu, link);
if (found) return found;
}
}
return '';
}
export default {
name: 'BreadcrumbsComponent',
setup() {
const route = useRoute();
const store = useStore();
const crumbs = computed(() => {
const path = route.path || '/';
const list = [{ label: 'Startseite', to: '/' }];
if (path === '/') return [];
const label = findLabel(store.state.menuData || [], path);
list.push({ label: label || 'Seite', to: path });
return list;
});
return { crumbs };
},
};
</script>
<style scoped>
.breadcrumbs {
margin: 0 var(--space-3) var(--space-2) var(--space-3);
font-size: 0.875rem;
color: var(--color-text-muted);
}
.breadcrumbs ol {
list-style: none;
display: flex;
flex-wrap: wrap;
gap: var(--space-2);
margin: 0;
padding: 0;
}
.breadcrumbs li {
display: inline-flex;
align-items: center;
gap: var(--space-2);
}
.breadcrumbs li:not(:last-child)::after {
content: '';
color: var(--color-text-muted);
}
.breadcrumbs a {
color: var(--color-text-muted);
text-decoration: none;
}
.breadcrumbs a:hover,
.breadcrumbs a:focus-visible {
color: var(--color-text);
text-decoration: underline;
text-underline-offset: 2px;
}
</style>

View File

@@ -34,14 +34,16 @@ export default {
<style scoped> <style scoped>
.footer { .footer {
background-color: #0b1735; background-color: var(--color-footer-bg);
bottom: 0; bottom: 0;
left: 0; left: 0;
width: 100%; width: 100%;
padding: 7px; padding: var(--space-2) var(--space-3);
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
flex-wrap: wrap;
gap: var(--space-2);
} }
.left-links { .left-links {
@@ -52,17 +54,22 @@ export default {
.right-links { .right-links {
display: flex; display: flex;
align-items: center; align-items: center;
flex-wrap: wrap;
} }
.footer a { .footer a {
color: #fff; color: var(--color-footer-link);
padding-right: 20px; padding-right: var(--space-5);
text-decoration: none; text-decoration: none;
text-underline-offset: 2px;
} }
.footer a.login-link { .footer a:hover,
color: #444; .footer a:focus-visible {
color: var(--color-footer-link-hover);
text-decoration: underline;
} }
.footer a.logout-link { .footer a.logout-link {
cursor: pointer; cursor: pointer;
} }

View File

@@ -5,18 +5,21 @@
<span class="reload-icon" @click="reloadMenu">&#x27F3;</span> <span class="reload-icon" @click="reloadMenu">&#x27F3;</span>
</div> </div>
<NavbarComponent /> <NavbarComponent />
<BreadcrumbsComponent />
</header> </header>
</template> </template>
<script> <script>
import NavbarComponent from './NavbarComponent.vue'; import NavbarComponent from './NavbarComponent.vue';
import BreadcrumbsComponent from './BreadcrumbsComponent.vue';
import { mapActions } from 'vuex'; import { mapActions } from 'vuex';
import router from '@/router'; import router from '@/router';
export default { export default {
name: 'HeaderComponent', name: 'HeaderComponent',
components: { components: {
NavbarComponent NavbarComponent,
BreadcrumbsComponent,
}, },
methods: { methods: {
...mapActions(['loadMenuData']), ...mapActions(['loadMenuData']),
@@ -42,7 +45,7 @@ header {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 100%; width: 100%;
background-color: #ffffff; background-color: var(--color-bg-page);
} }
.header-title { .header-title {
@@ -50,28 +53,30 @@ header {
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
width: 100%; width: 100%;
padding: .3em .5em; padding: var(--space-2) var(--space-3);
} }
header h1 { header h1 {
margin: 0; margin: 0;
flex: 1; flex: 1;
text-align: center; text-align: center;
text-shadow: 2px 2px 1px #e0bfff; text-shadow: 1px 1px 0 var(--color-brand-tint);
padding-bottom: 4px; padding-bottom: var(--space-1);
} }
.reload-icon { .reload-icon {
font-size: 16px; font-size: 1rem;
cursor: pointer; cursor: pointer;
margin-left: 10px; margin-left: var(--space-3);
background-color: #e0bfff; background-color: var(--color-brand-tint);
color: white; color: var(--color-bg-page);
padding: 5px; padding: var(--space-1) 6px;
border-radius: 50%; border-radius: 50%;
line-height: 1;
} }
.reload-icon:hover { .reload-icon:hover,
color: #7a00d1; .reload-icon:focus-visible {
color: var(--color-brand-primary-hover);
} }
</style> </style>

View File

@@ -1,15 +1,33 @@
<template> <template>
<nav class="navbar"> <nav class="navbar">
<button class="menu-toggle" @click="toggleMenu"> <button
Menü class="menu-toggle"
type="button"
@click="toggleMenu"
:aria-expanded="String(isMenuOpen)"
aria-controls="main-menu"
>
<span class="menu-toggle__icon" aria-hidden="true"></span>
<span>Menü</span>
</button> </button>
<ul v-if="isMenuOpen || windowWidth > 768"> <ul id="main-menu" v-if="isMenuOpen || windowWidth > 768">
<li class="ekhnlogo"><img src="/images/facettenkreuz.png" class="facettenkreuz" /></li> <li class="ekhnlogo">
<li v-for="item in menu" :key="item.name" @click="toggleSubmenu(item.name)"> <img src="/images/facettenkreuz.png" class="facettenkreuz" alt="EKHN" />
<router-link :to="item.link" v-if="item.link" @click="closeMenu"> </li>
<li v-for="item in menu" :key="item.name">
<router-link :to="item.link" v-if="item.link" @click="closeMenu" class="nav-link">
{{ item.name }} {{ item.name }}
</router-link> </router-link>
<span v-if="!item.link" class="pointer"> <span
v-if="!item.link"
class="nav-link pointer"
role="button"
tabindex="0"
@click="toggleSubmenu(item.name)"
@keydown.enter.prevent="toggleSubmenu(item.name)"
@keydown.space.prevent="toggleSubmenu(item.name)"
:aria-expanded="String(!!isSubmenuOpen[item.name])"
>
{{ item.name }} {{ item.name }}
</span> </span>
<transition name="fade"> <transition name="fade">
@@ -38,16 +56,20 @@ export default {
const windowWidth = ref(window.innerWidth); const windowWidth = ref(window.innerWidth);
const menu = computed(() => { const menu = computed(() => {
return store.state.menuData.filter(item => { return (store.state.menuData || []).map((item) => {
const submenu = Array.isArray(item.submenu)
? item.submenu.filter(
(subitem) => subitem.showInMenu && (!subitem.requiresAuth || store.getters.isLoggedIn)
)
: item.submenu;
return { ...item, submenu };
}).filter(item => {
if (!item.showInMenu) { if (!item.showInMenu) {
return false; return false;
} }
if (item.requiresAuth && !store.getters.isLoggedIn) { if (item.requiresAuth && !store.getters.isLoggedIn) {
return false; return false;
} }
if (item.submenu) {
item.submenu = item.submenu.filter(subitem => subitem.showInMenu && (!subitem.requiresAuth || store.getters.isLoggedIn));
}
return true; return true;
}); });
}); });
@@ -102,25 +124,34 @@ export default {
<style scoped> <style scoped>
.navbar { .navbar {
background-color: #9400ff; background-color: var(--color-brand-primary);
overflow: visible; overflow: visible;
min-height: 31px; min-height: 31px;
display: inline-flex; display: inline-flex;
flex-direction: column; flex-direction: column;
width: auto; width: calc(100% - (var(--space-3) * 2));
margin: 0.1em 0.75em 9px 0.75em; margin: var(--space-1) var(--space-3) var(--space-3) var(--space-3);
box-shadow: 0 0 2px 5px #9400ff; border-radius: 4px;
box-shadow: 0 0 0 2px var(--color-brand-primary);
} }
.menu-toggle { .menu-toggle {
background-color: #9400ff; background-color: var(--color-brand-primary);
color: white; color: #fff;
border: none; border: none;
padding: 14px 20px; padding: var(--space-3) var(--space-4);
text-align: center; text-align: left;
text-decoration: none; text-decoration: none;
display: none; display: none;
font-weight: bold; font-weight: bold;
align-items: center;
gap: var(--space-2);
border-radius: 4px;
}
.menu-toggle__icon {
font-size: 1.25rem;
line-height: 1;
} }
.navbar ul { .navbar ul {
@@ -128,24 +159,39 @@ export default {
margin: 0; margin: 0;
padding: 0; padding: 0;
display: flex; display: flex;
align-items: center;
gap: 2px;
} }
.navbar li { .navbar li {
position: relative; position: relative;
} }
.nav-link,
.navbar a, .navbar a,
.navbar li>span { .navbar li > span {
display: block; display: block;
color: white; color: white;
text-align: center; text-align: center;
padding: 6px 20px; padding: 10px 14px;
text-decoration: none; text-decoration: none;
font-weight: bold; font-weight: bold;
border-radius: 3px;
line-height: 1.2;
} }
.navbar a:hover { .navbar a:hover,
background-color: #7a00d1; .nav-link:hover {
background-color: var(--color-brand-primary-hover);
}
.navbar a:focus-visible,
.menu-toggle:focus-visible {
outline-color: #fff;
}
.navbar :deep(.router-link-exact-active) {
background-color: rgba(255, 255, 255, 0.18);
} }
.menu-icon { .menu-icon {
@@ -156,7 +202,7 @@ export default {
.dropdown-content { .dropdown-content {
position: absolute; position: absolute;
background-color: #9400ff; background-color: var(--color-brand-primary);
min-width: 200px; min-width: 200px;
z-index: 1; z-index: 1;
top: 100%; top: 100%;
@@ -164,7 +210,9 @@ export default {
opacity: 0; opacity: 0;
visibility: hidden; visibility: hidden;
transition: opacity 0.2s ease-in-out, visibility 0.2s ease-in-out; transition: opacity 0.2s ease-in-out, visibility 0.2s ease-in-out;
box-shadow: 2px 2px 4px #666; box-shadow: var(--shadow-dropdown);
border-radius: 4px;
padding: var(--space-1);
} }
.dropdown-content a { .dropdown-content a {
@@ -176,7 +224,7 @@ export default {
} }
.dropdown-content a:hover { .dropdown-content a:hover {
background-color: #7a00d1; background-color: var(--color-brand-primary-hover);
} }
.navbar li:hover .dropdown-content { .navbar li:hover .dropdown-content {
@@ -205,6 +253,8 @@ export default {
.navbar ul { .navbar ul {
flex-direction: column; flex-direction: column;
align-items: stretch;
gap: 0;
} }
.navbar li { .navbar li {
@@ -214,11 +264,12 @@ export default {
.navbar a, .navbar a,
.navbar li>span { .navbar li>span {
text-align: left; text-align: left;
padding: 14px 20px; padding: 14px 16px;
border-radius: 0;
} }
.menu-toggle { .menu-toggle {
display: block; display: inline-flex;
} }
.dropdown-content { .dropdown-content {
@@ -226,12 +277,12 @@ export default {
box-shadow: none; box-shadow: none;
opacity: 1; opacity: 1;
visibility: visible; visibility: visible;
display: none; padding-left: var(--space-4);
padding-left: 1em; padding-bottom: var(--space-2);
} }
.navbar li:hover .dropdown-content { .navbar :deep(.router-link-exact-active) {
display: block; background-color: rgba(255, 255, 255, 0.14);
} }
} }
@@ -240,12 +291,16 @@ export default {
} }
.facettenkreuz { .facettenkreuz {
max-width:30px; width: 28px;
max-height:30px; height: 28px;
position: fixed; display: block;
margin: 0 var(--space-2);
} }
.ekhnlogo { .ekhnlogo {
width: 32px; width: auto;
display: flex;
align-items: center;
padding: 0;
} }
</style> </style>

View File

@@ -4,6 +4,7 @@
<span class="close" @click="closeDialog">&times;</span> <span class="close" @click="closeDialog">&times;</span>
<h2>Datei zum Download hinzufügen</h2> <h2>Datei zum Download hinzufügen</h2>
<table> <table>
<tbody>
<tr> <tr>
<td> <td>
<label for="file-select">Datei auswählen:</label> <label for="file-select">Datei auswählen:</label>
@@ -16,6 +17,7 @@
</select> </select>
</td> </td>
</tr> </tr>
</tbody>
</table> </table>
<button @click="confirm">Hinzufügen</button> <button @click="confirm">Hinzufügen</button>
</div> </div>

View File

@@ -4,6 +4,7 @@
<span class="close" @click="closeDialog">&times;</span> <span class="close" @click="closeDialog">&times;</span>
<h2>Link hinzufügen</h2> <h2>Link hinzufügen</h2>
<table> <table>
<tbody>
<tr> <tr>
<td> <td>
<label for="link-url">URL:</label> <label for="link-url">URL:</label>
@@ -20,6 +21,7 @@
<input id="link-text" v-model="text" type="text" /> <input id="link-text" v-model="text" type="text" />
</td> </td>
</tr> </tr>
</tbody>
</table> </table>
<button @click="confirm">Hinzufügen</button> <button @click="confirm">Hinzufügen</button>
</div> </div>

View File

@@ -3,6 +3,7 @@
<h2>Veranstaltung Formular</h2> <h2>Veranstaltung Formular</h2>
<form @submit.prevent="saveEvent"> <form @submit.prevent="saveEvent">
<table> <table>
<tbody>
<tr> <tr>
<td><label for="name">Name:</label></td> <td><label for="name">Name:</label></td>
<td><input type="text" id="name" v-model="eventData.name" required></td> <td><input type="text" id="name" v-model="eventData.name" required></td>
@@ -99,6 +100,7 @@
<button type="submit">Speichern</button> <button type="submit">Speichern</button>
</td> </td>
</tr> </tr>
</tbody>
</table> </table>
</form> </form>
</div> </div>

View File

@@ -35,7 +35,7 @@
<select id="parent-id" v-model.number="selectedMenuItem.parent_id"> <select id="parent-id" v-model.number="selectedMenuItem.parent_id">
<option value="-1">Ohne Elternelement</option> <option value="-1">Ohne Elternelement</option>
<option v-for="item in flattenedMenuData" :key="item.id" :value="item.id"> <option v-for="item in flattenedMenuData" :key="item.id" :value="item.id">
<span v-html="getIndentedName(item)"></span> {{ getIndentedName(item) }}
</option> </option>
</select> </select>
@@ -204,7 +204,7 @@ export default {
}; };
const getIndentedName = (item) => { const getIndentedName = (item) => {
return '&nbsp;'.repeat(item.indent * 2) + item.name; return '\u00A0'.repeat(item.indent * 2) + item.name;
}; };
const flattenedMenuData = computed(() => { const flattenedMenuData = computed(() => {

View File

@@ -3,6 +3,7 @@ import AppComponent from './AppComponent.vue';
import router from './router'; import router from './router';
import store from './store'; import store from './store';
import axios from './axios'; import axios from './axios';
import './assets/css/design-tokens.css';
import './assets/css/editor.css'; import './assets/css/editor.css';
const app = createApp(AppComponent); const app = createApp(AppComponent);