From c12f30cbbf5def6cb6ea65560c13730e344c7717 Mon Sep 17 00:00:00 2001 From: Guido Schmit Date: Tue, 5 May 2026 18:11:33 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20'Vor=20dem=20Kopieren=20bearbeiten'=20C?= =?UTF-8?q?heckbox=20im=20Kopieren-Popup=20=C3=9Cber=20der=20Kalenderliste?= =?UTF-8?q?=20im=20Kopieren-Men=C3=BC=20gibt=20es=20jetzt=20eine=20Checkbo?= =?UTF-8?q?x=20'Vor=20dem=20Kopieren=20bearbeiten'.=20Wenn=20aktiviert=20u?= =?UTF-8?q?nd=20ein=20Ziel-Kalender=20geklickt=20wird,=20=C3=B6ffnet=20sic?= =?UTF-8?q?h=20der=20Termin-erstellen-Dialog=20mit=20allen=20Daten=20des?= =?UTF-8?q?=20Quell-Termins=20vorausgef=C3=BCllt=20(Titel,=20Datum,=20Ort,?= =?UTF-8?q?=20Beschreibung,=20Farbe,=20Wiederholung)=20und=20dem=20Ziel-Ka?= =?UTF-8?q?lender=20vorausgew=C3=A4hlt.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/css/app.css | 9 +++++++ frontend/js/calendar.js | 55 +++++++++++++++++++++++++++++++++++++++-- frontend/js/i18n.js | 2 ++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/frontend/css/app.css b/frontend/css/app.css index c109e3e..39f383e 100644 --- a/frontend/css/app.css +++ b/frontend/css/app.css @@ -846,6 +846,15 @@ a { color: var(--primary); text-decoration: none; } } .popup-copy-item:hover { background: var(--bg-hover); } .popup-copy-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; } +.popup-copy-edit-toggle { + display: flex; align-items: center; gap: 8px; + padding: 6px 14px 8px; + font-size: 12px; color: var(--text-2); + cursor: pointer; + border-bottom: 1px solid var(--border); + margin-bottom: 4px; +} +.popup-copy-edit-toggle input[type="checkbox"] { margin: 0; cursor: pointer; } /* ── Settings Page ──────────────────────────────────────── */ #modal-settings.modal-overlay { diff --git a/frontend/js/calendar.js b/frontend/js/calendar.js index 4f27111..a7faea5 100644 --- a/frontend/js/calendar.js +++ b/frontend/js/calendar.js @@ -1027,7 +1027,12 @@ function showEventPopup(ev, anchor) { if (!menu.classList.contains('hidden')) { menu.classList.add('hidden'); return; } const targets = buildWritableCalendars(ev); if (!targets.length) { showToast('Keine Zielkalender verfügbar', true); return; } - menu.innerHTML = `` + + menu.innerHTML = + `` + + `` + targets.map(c => `` ).join(''); menu.classList.remove('hidden'); + + // Stop clicks on the checkbox label from closing the menu + menu.querySelector('.popup-copy-edit-toggle').addEventListener('click', e2 => e2.stopPropagation()); + menu.querySelectorAll('.popup-copy-item').forEach(el => { el.addEventListener('click', async ev2 => { ev2.stopPropagation(); + const editFirst = document.getElementById('popup-copy-edit-cb').checked; menu.classList.add('hidden'); popup.classList.add('hidden'); const cal = targets[parseInt(el.dataset.calIdx)]; - await copyEventToCalendar(ev, cal); + if (editFirst) { + openCopyEditModal(ev, cal); + } else { + await copyEventToCalendar(ev, cal); + } }); }); }; @@ -1149,6 +1163,43 @@ function openNewEventModal(date) { openModal('modal-event'); } +// Open the create-event modal pre-filled with an existing event's data, so +// the user can edit before copying it into the target calendar. +function openCopyEditModal(ev, targetCal) { + state.editingEvent = null; + state.selectedEventColor = ev.color || ''; + + document.getElementById('modal-event-title-label').textContent = 'Termin erstellen'; + document.getElementById('ev-title').value = ev.title || ''; + document.getElementById('ev-location').value = ev.location || ''; + document.getElementById('ev-description').value = ev.description || ''; + document.getElementById('ev-allday').checked = !!ev.allDay; + + if (ev.allDay) { + setDtValue('ev-start-date', (ev.start || '').slice(0, 10), 'date'); + setDtValue('ev-end-date', (ev.end || '').slice(0, 10), 'date'); + } else { + const s = new Date(ev.start); + const e = new Date(ev.end); + setDtValue('ev-start', toLocalDatetimeInput(s), 'datetime'); + setDtValue('ev-end', toLocalDatetimeInput(e), 'datetime'); + } + + toggleAlldayFields(!!ev.allDay); + + // Map target calendar to the dropdown's option value + let selectedId; + if (targetCal.type === 'caldav') selectedId = targetCal.id; + else selectedId = `${targetCal.type}-${targetCal.id}`; + populateCalendarSelect(selectedId); + + resetColorPicker(ev.color || ''); + resetRecurrenceUI(); + if (ev.rrule) parseRruleIntoUI(ev.rrule); + document.getElementById('ev-delete').classList.add('hidden'); + openModal('modal-event'); +} + function openEditEventModal(ev) { if (ev.source === 'ical') { showToast(t('event_readonly'), true); return; } state.editingEvent = ev; diff --git a/frontend/js/i18n.js b/frontend/js/i18n.js index a9690ec..66a15f4 100644 --- a/frontend/js/i18n.js +++ b/frontend/js/i18n.js @@ -153,6 +153,7 @@ const translations = { rec_ends: 'Endet', rec_never: 'Nie', rec_after_count: 'Nach Anzahl', rec_on_date: 'Am Datum', rec_occurrences: 'Termine', copy_to_calendar: 'Kopieren nach…', event_copied: 'Termin kopiert', + edit_before_copy: 'Vor dem Kopieren bearbeiten', event_updated: 'Termin aktualisiert', event_created: 'Termin erstellt', confirm_delete_event: '"{title}" wirklich löschen?', confirm_delete_title: 'Termin löschen', @@ -361,6 +362,7 @@ const translations = { rec_ends: 'Ends', rec_never: 'Never', rec_after_count: 'After count', rec_on_date: 'On date', rec_occurrences: 'occurrences', copy_to_calendar: 'Copy to…', event_copied: 'Event copied', + edit_before_copy: 'Edit before copying', event_updated: 'Event updated', event_created: 'Event created', confirm_delete_event: 'Really delete "{title}"?', confirm_delete_title: 'Delete event',