Initial bundle implementation

This commit is contained in:
Jürgen Mummert
2026-02-18 21:30:04 +01:00
commit 00ef6aba91
18 changed files with 1488 additions and 0 deletions
@@ -0,0 +1,249 @@
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/themes/dark.css">
<div class="eventlist-layout">
<div class="eventlist-sidebar">
<form method="get" id="event-filter-form">
<h3>Filtern</h3>
<input type="hidden" name="archive_filter_applied" value="1">
{% if availableArchives is not empty %}
<fieldset>
<legend>Event-Archive</legend>
{% for archive in availableArchives %}
<label>
<input
type="checkbox"
name="archive_ids[]"
value="{{ archive.id }}"
class="archive-filter-checkbox"
{% if archive.id in selectedArchiveIds %}checked{% endif %}
>
{{ archive.title }}
</label>
{% endfor %}
</fieldset>
{% endif %}
<label for="filter-from-input">
<span>Von</span>
</label>
<div class="flatpickr" id="filter-from-wrap">
<input id="filter-from-input" type="text" name="from" value="{{ from }}" autocomplete="off" data-input>
<a id="filter-from-clear" class="input-button" title="clear" data-clear {% if from is empty %}hidden{% endif %}>×</a>
</div>
<label for="filter-to-input">
<span>Bis</span>
</label>
<div class="flatpickr" id="filter-to-wrap">
<input id="filter-to-input" type="text" name="to" value="{{ to }}" autocomplete="off" data-input>
<a id="filter-to-clear" class="input-button" title="clear" data-clear {% if to is empty %}hidden{% endif %}>×</a>
</div>
</form>
<form method="post" action="/events/pdf" id="pdf-export-form">
<input type="hidden" name="from" value="{{ from }}">
<input type="hidden" name="to" value="{{ to }}">
{% if pdfError == 'no_selection' %}
<p>Bitte wählen Sie mindestens einen Termin für den PDF-Export aus.</p>
{% elseif pdfError == 'not_found' %}
<p>Die ausgewählten Termine konnten nicht geladen werden.</p>
{% endif %}
<p id="pdf-selection-error" role="alert" aria-live="polite" hidden>Bitte wählen Sie mindestens einen Termin für den PDF-Export aus.</p>
<p>
<label for="pdf_heading">Überschrift (optional)</label>
<input id="pdf_heading" type="text" name="pdf_heading" value="">
</p>
<p>
<label for="pdf_intro">Einleitungstext (optional)</label>
<textarea id="pdf_intro" name="pdf_intro" rows="4"></textarea>
</p>
<p>
<button type="submit">PDF erstellen</button>
</p>
</form>
</div>
<div class="eventlist-results">
{% if events is not empty %}
<p>
<label>
<input type="checkbox" id="select-all-events">
Alle auswählen
</label>
</p>
<ul>
{% for event in events %}
<li class="pid-{{ event.pid }}{% if event.isFeatured %} is-featured{% endif %}">
<label>
<input type="checkbox" name="event_ids[]" value="{{ event.id }}" class="event-select-checkbox" form="pdf-export-form">
{{ event.title }}
</label>
<p>
{{ event.startDate }}{% if event.endDate %} - {{ event.endDate }}{% endif %}
</p>
{% if event.url %}
<a href="{{ event.url }}">weiterlesen…</a>
{% endif %}
</li>
{% endfor %}
</ul>
{% else %}
<p>Keine Termine gefunden.</p>
{% endif %}
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
<script src="https://cdn.jsdelivr.net/npm/flatpickr/dist/l10n/de.js"></script>
<script>
(function () {
const filterForm = document.getElementById('event-filter-form');
const filterFromWrap = document.getElementById('filter-from-wrap');
const filterToWrap = document.getElementById('filter-to-wrap');
const filterFromInput = document.getElementById('filter-from-input');
const filterToInput = document.getElementById('filter-to-input');
const filterFromClear = document.getElementById('filter-from-clear');
const filterToClear = document.getElementById('filter-to-clear');
const archiveFilterCheckboxes = document.querySelectorAll('.archive-filter-checkbox');
if (filterForm && filterFromInput && filterToInput) {
const submitFilterForm = function () {
filterForm.submit();
};
const updateClearButtons = function () {
if (filterFromClear) {
filterFromClear.hidden = filterFromInput.value === '';
}
if (filterToClear) {
filterToClear.hidden = filterToInput.value === '';
}
};
updateClearButtons();
if (typeof flatpickr === 'function') {
const commonConfig = {
wrap: true,
dateFormat: 'Y-m-d',
allowInput: true,
locale: (window.flatpickr && window.flatpickr.l10ns && window.flatpickr.l10ns.de) ? window.flatpickr.l10ns.de : 'default',
onChange: [
function () {
updateClearButtons();
submitFilterForm();
}
]
};
if (filterFromWrap) {
flatpickr(filterFromWrap, commonConfig);
}
if (filterToWrap) {
flatpickr(filterToWrap, commonConfig);
}
filterFromInput.addEventListener('input', function () {
updateClearButtons();
if (filterFromInput.value === '') {
submitFilterForm();
}
});
filterToInput.addEventListener('input', function () {
updateClearButtons();
if (filterToInput.value === '') {
submitFilterForm();
}
});
} else {
filterFromInput.addEventListener('change', function () {
updateClearButtons();
submitFilterForm();
});
filterToInput.addEventListener('change', function () {
updateClearButtons();
submitFilterForm();
});
}
if (filterFromClear) {
filterFromClear.addEventListener('click', function () {
window.setTimeout(updateClearButtons, 0);
});
}
if (filterToClear) {
filterToClear.addEventListener('click', function () {
window.setTimeout(updateClearButtons, 0);
});
}
archiveFilterCheckboxes.forEach(function (checkbox) {
checkbox.addEventListener('change', submitFilterForm);
});
}
const selectAll = document.getElementById('select-all-events');
if (!selectAll) {
return;
}
const eventCheckboxes = document.querySelectorAll('.event-select-checkbox');
const exportForm = document.getElementById('pdf-export-form');
const selectionError = document.getElementById('pdf-selection-error');
selectAll.addEventListener('change', function () {
eventCheckboxes.forEach(function (checkbox) {
checkbox.checked = selectAll.checked;
});
});
eventCheckboxes.forEach(function (checkbox) {
checkbox.addEventListener('change', function () {
const allChecked = Array.from(eventCheckboxes).every(function (item) {
return item.checked;
});
selectAll.checked = allChecked;
if (selectionError && checkbox.checked) {
selectionError.hidden = true;
}
});
});
if (exportForm) {
exportForm.addEventListener('submit', function (event) {
const anyChecked = Array.from(eventCheckboxes).some(function (item) {
return item.checked;
});
if (!anyChecked) {
event.preventDefault();
if (selectionError) {
selectionError.hidden = false;
}
}
});
}
})();
</script>
+70
View File
@@ -0,0 +1,70 @@
<style>
body {
font-family: DejaVu Sans, sans-serif;
font-size: 11pt;
line-height: 1.4;
margin: 1.3cm 1.2cm 1.2cm 1.2cm;
}
.event-time {
color: #666;
font-style: italic;
}
h1 {
margin: 0 0 0.55cm 0;
}
.pdf-title {
max-width: 70%;
line-height: 1.1;
}
p {
margin: 0 0 0.38cm 0;
}
.event-list {
margin: 0.45cm 0 0 0;
}
.event-item {
margin: 0 0 0.18cm 0;
}
.pdf-logo {
position: absolute;
top: 0.3cm;
right: 1.3cm;
width: 2.4cm;
height: auto;
}
</style>
{% if logoDataUri %}
<img src="{{ logoDataUri }}" alt="Gymnasium Nossen" class="pdf-logo">
{% endif %}
<h1 class="pdf-title">{{ heading }}</h1>
{% if introText %}
<p>{{ introText }}</p>
{% endif %}
{% if dateRange %}
<p>{{ dateRange }}</p>
{% endif %}
<div class="event-list">
{% for event in events %}
<p class="event-item">
{{ event.title }}
<span class="event-time">
{{ event.startText }}
{% if event.endText %}
- {{ event.endText }}
{% endif %}
</span>
</p>
{% endfor %}
</div>