Compare commits
2 Commits
492b47a927
...
1c71ea60a8
| Author | SHA1 | Date | |
|---|---|---|---|
| 1c71ea60a8 | |||
| e959c83a0d |
@ -656,7 +656,7 @@ footer {
|
|||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
border-top: 0;
|
border-top: 0;
|
||||||
border-radius: 0 0 6px 6px;
|
border-radius: 0 0 6px 6px;
|
||||||
overflow: hidden;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.calendar-weekday-header {
|
.calendar-weekday-header {
|
||||||
@ -712,6 +712,11 @@ footer {
|
|||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.calendar-day-hidden {
|
||||||
|
visibility: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
@media (min-width: 992px) {
|
@media (min-width: 992px) {
|
||||||
.calendar-grid {
|
.calendar-grid {
|
||||||
grid-template-columns: repeat(7, minmax(0, 1fr));
|
grid-template-columns: repeat(7, minmax(0, 1fr));
|
||||||
@ -727,7 +732,12 @@ footer {
|
|||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
border-bottom: 0;
|
border-bottom: 0;
|
||||||
border-radius: 8px 8px 0 0;
|
border-radius: 8px 8px 0 0;
|
||||||
overflow: hidden;
|
overflow: visible;
|
||||||
|
position: sticky;
|
||||||
|
top: 70px;
|
||||||
|
z-index: 900;
|
||||||
|
background: var(--card-background, #fff);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04);
|
||||||
}
|
}
|
||||||
|
|
||||||
.calendar-day {
|
.calendar-day {
|
||||||
|
|||||||
@ -24,15 +24,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="calendar-weekday-header">
|
<div class="calendar-weekday-header"></div>
|
||||||
<div>Mo</div>
|
|
||||||
<div>Di</div>
|
|
||||||
<div>Mi</div>
|
|
||||||
<div>Do</div>
|
|
||||||
<div>Fr</div>
|
|
||||||
<div>Sa</div>
|
|
||||||
<div>So</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="calendar-days" class="calendar-grid">
|
<div id="calendar-days" class="calendar-grid">
|
||||||
<!-- Populated via inline script below -->
|
<!-- Populated via inline script below -->
|
||||||
@ -61,6 +53,15 @@
|
|||||||
<input type="time" class="form-control" id="calendarTime">
|
<input type="time" class="form-control" id="calendarTime">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% if admin_enabled %}
|
||||||
|
<div class="row g-3 mt-1" id="calendarRecurrenceRow">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="calendarRepeatCount" class="form-label">Wöchentliche Wiederholung</label>
|
||||||
|
<input type="number" class="form-control" id="calendarRepeatCount" min="1" max="52" step="1" value="1">
|
||||||
|
<div class="form-text">Legt einzelne wöchentliche Termine an (für Nachbearbeitung).</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
<label for="calendarTitle" class="form-label">Titel</label>
|
<label for="calendarTitle" class="form-label">Titel</label>
|
||||||
<input type="text" class="form-control" id="calendarTitle" required>
|
<input type="text" class="form-control" id="calendarTitle" required>
|
||||||
@ -148,18 +149,40 @@
|
|||||||
return holidays.get(toLocalISO(date)) || '';
|
return holidays.get(toLocalISO(date)) || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isDesktopView() {
|
||||||
|
return window.matchMedia && window.matchMedia('(min-width: 992px)').matches;
|
||||||
|
}
|
||||||
|
|
||||||
function getCalendarRange() {
|
function getCalendarRange() {
|
||||||
const weeks = window._calendarIsAdmin ? 12 : 4;
|
const weeks = window._calendarIsAdmin ? 12 : 4;
|
||||||
const start = new Date();
|
const start = new Date();
|
||||||
start.setHours(0, 0, 0, 0);
|
start.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
if (isDesktopView()) {
|
||||||
// shift back to Monday (0 = Sunday)
|
// shift back to Monday (0 = Sunday)
|
||||||
const diffToMonday = (start.getDay() + 6) % 7;
|
const diffToMonday = (start.getDay() + 6) % 7;
|
||||||
start.setDate(start.getDate() - diffToMonday);
|
start.setDate(start.getDate() - diffToMonday);
|
||||||
|
}
|
||||||
|
|
||||||
const end = new Date(start);
|
const end = new Date(start);
|
||||||
end.setDate(end.getDate() + (weeks * 7) - 1);
|
end.setDate(end.getDate() + (weeks * 7) - 1);
|
||||||
return { start, end };
|
return { start, end };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderWeekdayHeader(startDate) {
|
||||||
|
const header = document.querySelector('.calendar-weekday-header');
|
||||||
|
if (!header) return;
|
||||||
|
header.innerHTML = '';
|
||||||
|
const formatDayShort = new Intl.DateTimeFormat('de-DE', { weekday: 'short' });
|
||||||
|
for (let i = 0; i < 7; i++) {
|
||||||
|
const d = new Date(startDate);
|
||||||
|
d.setDate(startDate.getDate() + i);
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.textContent = formatDayShort.format(d);
|
||||||
|
header.appendChild(div);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
(function renderInitialCalendarRange() {
|
(function renderInitialCalendarRange() {
|
||||||
const daysContainer = document.getElementById('calendar-days');
|
const daysContainer = document.getElementById('calendar-days');
|
||||||
if (!daysContainer || daysContainer.childElementCount > 0) return;
|
if (!daysContainer || daysContainer.childElementCount > 0) return;
|
||||||
@ -172,6 +195,8 @@
|
|||||||
const formatDayName = new Intl.DateTimeFormat('de-DE', { weekday: 'long' });
|
const formatDayName = new Intl.DateTimeFormat('de-DE', { weekday: 'long' });
|
||||||
const formatDate = new Intl.DateTimeFormat('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' });
|
const formatDate = new Intl.DateTimeFormat('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' });
|
||||||
const { start, end } = getCalendarRange();
|
const { start, end } = getCalendarRange();
|
||||||
|
const todayIso = toLocalISO(new Date());
|
||||||
|
renderWeekdayHeader(start);
|
||||||
const dayCount = Math.round((end - start) / (1000 * 60 * 60 * 24)) + 1;
|
const dayCount = Math.round((end - start) / (1000 * 60 * 60 * 24)) + 1;
|
||||||
|
|
||||||
for (let i = 0; i < dayCount; i++) {
|
for (let i = 0; i < dayCount; i++) {
|
||||||
@ -194,6 +219,9 @@
|
|||||||
if (holidayName) {
|
if (holidayName) {
|
||||||
dayBlock.classList.add('calendar-day-holiday');
|
dayBlock.classList.add('calendar-day-holiday');
|
||||||
}
|
}
|
||||||
|
if (isDesktopView() && isoDate < todayIso) {
|
||||||
|
dayBlock.classList.add('calendar-day-hidden');
|
||||||
|
}
|
||||||
|
|
||||||
dayBlock.innerHTML = `
|
dayBlock.innerHTML = `
|
||||||
<div class="calendar-day-header">
|
<div class="calendar-day-header">
|
||||||
@ -435,6 +463,22 @@
|
|||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function createRecurringEntries(entry, repeatCount) {
|
||||||
|
const count = Math.max(1, Math.min(52, repeatCount || 1));
|
||||||
|
const baseDate = entry.date;
|
||||||
|
if (!baseDate) return [];
|
||||||
|
const base = new Date(baseDate);
|
||||||
|
const savedEntries = [];
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
const next = new Date(base);
|
||||||
|
next.setDate(base.getDate() + i * 7);
|
||||||
|
const entryForWeek = { ...entry, date: toLocalISO(next) };
|
||||||
|
const saved = await saveCalendarEntry(entryForWeek);
|
||||||
|
savedEntries.push(saved);
|
||||||
|
}
|
||||||
|
return savedEntries;
|
||||||
|
}
|
||||||
|
|
||||||
async function deleteCalendarEntry(id) {
|
async function deleteCalendarEntry(id) {
|
||||||
const response = await fetch(`/api/calendar/${id}`, { method: 'DELETE' });
|
const response = await fetch(`/api/calendar/${id}`, { method: 'DELETE' });
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@ -490,12 +534,22 @@
|
|||||||
const form = document.getElementById('calendarForm');
|
const form = document.getElementById('calendarForm');
|
||||||
const daysContainer = document.getElementById('calendar-days');
|
const daysContainer = document.getElementById('calendar-days');
|
||||||
const locationFilter = document.getElementById('calendar-location-filter');
|
const locationFilter = document.getElementById('calendar-location-filter');
|
||||||
|
const repeatCountInput = document.getElementById('calendarRepeatCount');
|
||||||
|
const recurrenceRow = document.getElementById('calendarRecurrenceRow');
|
||||||
const calendarModal = modalEl && window.bootstrap ? new bootstrap.Modal(modalEl) : null;
|
const calendarModal = modalEl && window.bootstrap ? new bootstrap.Modal(modalEl) : null;
|
||||||
if (calendarModal) {
|
if (calendarModal) {
|
||||||
window.calendarModal = calendarModal;
|
window.calendarModal = calendarModal;
|
||||||
}
|
}
|
||||||
let calendarHasLoaded = false;
|
let calendarHasLoaded = false;
|
||||||
|
|
||||||
|
const setRecurrenceMode = (isEditing) => {
|
||||||
|
if (!recurrenceRow) return;
|
||||||
|
recurrenceRow.style.display = isEditing ? 'none' : '';
|
||||||
|
if (!isEditing && repeatCountInput) {
|
||||||
|
repeatCountInput.value = '1';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (addBtn && calendarModal) {
|
if (addBtn && calendarModal) {
|
||||||
addBtn.addEventListener('click', () => {
|
addBtn.addEventListener('click', () => {
|
||||||
if (form) form.reset();
|
if (form) form.reset();
|
||||||
@ -507,6 +561,7 @@
|
|||||||
const idInput = document.getElementById('calendarEntryId');
|
const idInput = document.getElementById('calendarEntryId');
|
||||||
if (idInput) idInput.value = '';
|
if (idInput) idInput.value = '';
|
||||||
|
|
||||||
|
setRecurrenceMode(false);
|
||||||
calendarModal.show();
|
calendarModal.show();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -523,14 +578,24 @@
|
|||||||
location: document.getElementById('calendarLocation')?.value,
|
location: document.getElementById('calendarLocation')?.value,
|
||||||
details: document.getElementById('calendarDetails')?.value
|
details: document.getElementById('calendarDetails')?.value
|
||||||
};
|
};
|
||||||
|
const isEditing = Boolean(entry.id);
|
||||||
|
const repeatCount = (!isEditing && window._calendarIsAdmin && repeatCountInput)
|
||||||
|
? Math.max(1, Math.min(52, parseInt(repeatCountInput.value, 10) || 1))
|
||||||
|
: 1;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (entry.id) {
|
if (isEditing) {
|
||||||
removeCalendarEntryFromUI(entry.id);
|
removeCalendarEntryFromUI(entry.id);
|
||||||
}
|
|
||||||
const saved = await saveCalendarEntry(entry);
|
const saved = await saveCalendarEntry(entry);
|
||||||
addLocationOption(saved.location);
|
addLocationOption(saved.location);
|
||||||
addCalendarEntry(saved);
|
addCalendarEntry(saved);
|
||||||
|
} else {
|
||||||
|
const savedEntries = await createRecurringEntries(entry, repeatCount);
|
||||||
|
savedEntries.forEach(saved => {
|
||||||
|
addLocationOption(saved.location);
|
||||||
|
addCalendarEntry(saved);
|
||||||
|
});
|
||||||
|
}
|
||||||
calendarModal.hide();
|
calendarModal.hide();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@ -603,6 +668,7 @@
|
|||||||
document.getElementById('calendarTitle').value = entry.title || '';
|
document.getElementById('calendarTitle').value = entry.title || '';
|
||||||
document.getElementById('calendarLocation').value = entry.location || '';
|
document.getElementById('calendarLocation').value = entry.location || '';
|
||||||
document.getElementById('calendarDetails').value = entry.details || '';
|
document.getElementById('calendarDetails').value = entry.details || '';
|
||||||
|
setRecurrenceMode(true);
|
||||||
if (calendarModal) calendarModal.show();
|
if (calendarModal) calendarModal.show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user