Events renderable

This commit is contained in:
Torsten Schulz
2024-06-20 13:31:20 +02:00
parent d78bc26e30
commit 240409d52a
10 changed files with 493 additions and 28 deletions

View File

@@ -0,0 +1,208 @@
<template>
<div>
<div v-if="isOpen" class="dialog-overlay">
<div class="dialog-content">
<h3>Ereignisse hinzufügen</h3>
<div>
<label for="selection-type">Wählen Sie den Typ:</label>
<multiselect id="selection-type" v-model="selectedTypes" :options="typeOptions" :multiple="true"
:close-on-select="false" :clear-on-select="false" :preserve-search="true" @select="onTypeSelect" />
</div>
<div>
<label><input type="checkbox" v-model="isHomepage" /> Auch Ereignisse für die Homepage zeigen</label>
</div>
<div v-if="selectedTypes.includes('Für bestimmte Orte')">
<label for="places-select">Wählen Sie Orte:</label>
<multiselect id="places-select" v-model="selectedPlaces" :options="places" :multiple="true"
:close-on-select="false" :clear-on-select="false" :preserve-search="true" label="name" track-by="id" />
</div>
<div v-if="selectedTypes.includes('Für bestimmte Typen')">
<label for="types-select">Wählen Sie Typen:</label>
<multiselect id="types-select" v-model="selectedEventTypes" :options="eventTypes" :multiple="true"
:close-on-select="false" :clear-on-select="false" :preserve-search="true" label="caption" track-by="id" />
</div>
<div v-if="selectedTypes.includes('Ein bestimmtes')">
<label for="event-select">Wählen Sie ein Event:</label>
<multiselect id="event-select" v-model="selectedEvent" :options="events" :multiple="false" label="name"
track-by="id" />
</div>
<div>
<label>Wählen Sie welche Elemente angezeigt werden sollen:</label>
<div class="display-options">
<label><input type="checkbox" v-model="displayOptions.name" /> Name</label>
<label><input type="checkbox" v-model="displayOptions.type" /> Typ</label>
<label><input type="checkbox" v-model="displayOptions.place" /> Ort</label>
<label><input type="checkbox" v-model="displayOptions.description" /> Beschreibung</label>
<label><input type="checkbox" v-model="displayOptions.time" /> Uhrzeit</label>
<label><input type="checkbox" v-model="displayOptions.contactPerson" /> Kontaktperson</label>
<label><input type="checkbox" v-model="displayOptions.day" /> Veranstaltungstag</label>
<label><input type="checkbox" v-model="displayOptions.institution" /> Institution</label>
</div>
</div>
<div>
<button @click="confirmAddEventConfiguration">Bestätigen</button>
<button @click="closeAddEventDialog">Schließen</button>
</div>
</div>
</div>
</div>
</template>
<script>
import { ref } from 'vue';
import axios from '@/axios';
import Multiselect from 'vue-multiselect';
export default {
name: 'AddEventDialog',
components: {
Multiselect,
},
emits: ['confirm'],
setup(props, { emit }) {
const isOpen = ref(false);
const typeOptions = ref(['Alle', 'Für bestimmte Orte', 'Für bestimmte Typen', 'Ein bestimmtes', 'Ereignisse für Homepage']);
const selectedTypes = ref([]);
const places = ref([]);
const eventTypes = ref([]);
const events = ref([]);
const selectedPlaces = ref([]);
const selectedEventTypes = ref([]);
const selectedEvent = ref(null);
const isHomepage = ref(false);
const displayOptions = ref({
name: false,
type: false,
place: false,
description: false,
time: false,
contactPerson: false,
day: false,
institution: false,
});
const openAddEventDialog = () => {
isOpen.value = true;
fetchPlaces();
fetchEventTypes();
fetchEvents();
};
const closeAddEventDialog = () => {
isOpen.value = false;
};
const onTypeSelect = (selectedOption) => {
if (selectedOption === 'Alle' || selectedOption === 'Ein bestimmtes') {
selectedTypes.value = [selectedOption];
} else {
selectedTypes.value = selectedTypes.value.filter(option => option !== 'Alle' && option !== 'Ein bestimmtes');
}
};
const confirmAddEventConfiguration = () => {
let configString = '';
const displayString = Object.keys(displayOptions.value)
.filter(key => displayOptions.value[key])
.join('|');
if (isHomepage.value) {
configString = `{{ events:id=home,display=${displayString} }}`
} else if (selectedTypes.value.includes('Alle')) {
configString = `{{ events:id=all,display=${displayString} }}`;
} else if (selectedTypes.value.includes('Ein bestimmtes')) {
configString = `{{ events:id=${selectedEvent.value.id},display=${displayString} }}`;
} else {
const placesString = selectedPlaces.value.map(place => place.id).join('|');
const eventTypesString = selectedEventTypes.value.map(eventType => eventType.id).join('|');
configString = `{{ events:${placesString ? `event-places=${placesString},` : ''}${eventTypesString ? `event-types=${eventTypesString},` : ''}display=${displayString} }}`;
}
emit('confirm', configString);
closeAddEventDialog();
};
const fetchPlaces = async () => {
try {
const response = await axios.get('/event-places');
places.value = response.data;
} catch (error) {
console.error('Fehler beim Laden der Orte:', error);
}
};
const fetchEventTypes = async () => {
try {
const response = await axios.get('/event-types');
eventTypes.value = response.data;
} catch (error) {
console.error('Fehler beim Laden der Typen:', error);
}
};
const fetchEvents = async () => {
try {
const response = await axios.get('/events');
events.value = response.data;
} catch (error) {
console.error('Fehler beim Laden der Events:', error);
}
};
return {
isOpen,
typeOptions,
selectedTypes,
places,
eventTypes,
events,
selectedPlaces,
selectedEventTypes,
selectedEvent,
displayOptions,
openAddEventDialog,
closeAddEventDialog,
confirmAddEventConfiguration,
onTypeSelect,
isHomepage,
};
},
};
</script>
<style scoped>
.dialog-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}
.dialog-content {
background: white;
padding: 20px;
border-radius: 8px;
}
.multiselect {
width: 100%;
}
.image-block {
display: inline-block;
margin: 2.5px;
}
.selected {
border: 1px solid black;
}
.display-options label {
display:block;
}
</style>

View File

@@ -0,0 +1,97 @@
<template>
<div>
<table v-if="events.length > 1" class="event-table">
<tr v-for="event in events" :key="event.id">
<td>
<div v-if="shouldDisplay('name')" class="event-name">{{ event.name }}</div>
<div>{{ formatDateOrDay(event.date, event.dayOfWeek) }}</div>
<div v-if="shouldDisplay('time')">{{ event.time }} <span v-if="event.endTime"> - {{ event.endTime }}</span> Uhr</div>
<div v-if="shouldDisplay('place')">{{ event.eventPlace?.name }}</div>
<div v-if="shouldDisplay('description')" class="description">{{ event.description }}</div>
<div v-if="shouldDisplay('contactPerson')">{{ event.contactPersons.map(cp => cp.name).join(', ') }}</div>
<div v-if="shouldDisplay('institution')">{{ event.institution?.name }}</div>
<div v-if="shouldDisplay('type')">{{ event.eventType?.caption }}</div>
</td>
</tr>
</table>
<div v-else-if="events.length === 1" :class="events[0].alsoOnHomepage && config.id === 'home' ? 'homepage' : ''">
<div v-if="shouldDisplay('name')" class="event-name">{{ events[0].name }}</div>
<div>{{ formatDateOrDay(events[0].date, events[0].dayOfWeek) }}</div>
<div v-if="shouldDisplay('time')">{{ events[0].time }} <span v-if="events[0].endTime"> - {{ events[0].endTime }}</span> Uhr</div>
<div v-if="shouldDisplay('place')">{{ events[0].eventPlace?.name }}</div>
<div v-if="shouldDisplay('description')" class="description">{{ events[0].description }}</div>
<div v-if="shouldDisplay('contactPerson')">{{ events[0].contactPersons.map(cp => cp.name).join(', ') }}</div>
<div v-if="shouldDisplay('institution')">{{ events[0].institution?.name }}</div>
<div v-if="shouldDisplay('type')">{{ events[0].eventType?.caption }}</div>
</div>
</div>
</template>
<script>
import axios from '@/axios';
import { format } from 'date-fns'; // Importiere date-fns für die Datumsformatierung
export default {
name: 'EventRender',
props: {
config: {
type: Object,
required: true,
}
},
data() {
return {
events: [],
};
},
async created() {
await this.fetchEvents();
},
methods: {
async fetchEvents() {
try {
const response = await axios.get('/events/filter', {
params: this.config
});
console.log('Fetched events:', response.data.events);
this.events = response.data.events;
} catch (error) {
console.error('Fehler beim Abrufen der Events', error);
}
},
shouldDisplay(field) {
const displayFields = this.config.display.split('|');
return displayFields.includes(field);
},
formatDateOrDay(date, dayOfWeek) {
if (date) {
return format(new Date(date), 'dd.MM.yyyy');
} else if (dayOfWeek !== null && dayOfWeek !== undefined) {
const daysOfWeek = ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'];
return daysOfWeek[dayOfWeek];
}
return '';
}
}
};
</script>
<style scoped>
.event-name {
font-weight: bold;
}
.event-table {
border-collapse: collapse;
}
.event-table td {
border: 1px solid black;
}
.homepage {
border: 1px solid #9400ff;
padding: 0.5em;
text-align: center;
}
.description {
padding: 0.5em 0;
}
</style>

View File

@@ -6,6 +6,7 @@
import { createApp, h, ref, watch } from 'vue';
import WorshipRender from './WorshipRender.vue';
import ImageRender from './ImageRender.vue';
import EventRender from './EventRender.vue';
export default {
name: 'RenderContentComponent',
@@ -20,7 +21,8 @@ export default {
const renderContent = (content) => {
let result = renderWorship(content);
result = renderImage(content);
result = renderImage(result); // Use result here
result = renderEvent(result); // Use result here
return result;
};
@@ -44,7 +46,7 @@ export default {
return `<div id="${placeholderId}"></div>`;
});
return result;
}
};
const renderImage = (content) => {
const imagePattern = /{{ image:(.*?) }}/g;
@@ -57,7 +59,7 @@ export default {
const app = createApp({
render() {
console.log(config);
return h(ImageRender, { 'id': config });
return h(ImageRender, { id: config });
},
});
app.mount(placeholder);
@@ -66,31 +68,55 @@ export default {
return `<span id="${placeholderId}"></span>`;
});
return result;
}
};
const parseConfig = (configString) => {
const config = {};
const configArray = configString.split(',');
configArray.forEach((item) => {
const [key, value] = item.split('=');
if (key && value !== undefined) {
config[key.trim()] = isNaN(value) ? value.trim() : Number(value);
}
});
return config;
};
const renderEvent = (content) => {
const eventsPattern = /{{ events:(.*?) }}/g;
let result = content;
result = result.replace(eventsPattern, (match, config) => {
console.log(config);
const props = parseConfig(config);
console.log(props);
const placeholderId = `event-render-placeholder-${Math.random().toString(36).substr(2, 9)}`;
setTimeout(() => {
const placeholder = document.getElementById(placeholderId);
if (placeholder) {
const app = createApp({
render() {
return h(EventRender, { id: props.id, config: props });
},
});
app.mount(placeholder);
}
}, 0);
return `<div id="${placeholderId}"></div>`;
});
return result;
};
watch(
const parseConfig = (configString) => {
const config = {};
const configArray = configString.split(',');
configArray.forEach((item) => {
const [key, value] = item.split('=');
if (key && value !== undefined) {
config[key.trim()] = isNaN(value) ? value.trim() : Number(value);
}
});
return config;
};
watch(
() => props.content,
(newContent) => {
parsedContent.value = renderContent(newContent);
},
{ immediate: true }
(newContent) => {
parsedContent.value = renderContent(newContent);
},
{ immediate: true }
);
return {
parsedContent,
};
return {
parsedContent,
};
},
};
</script>