fixed SPA

This commit is contained in:
2025-12-16 15:23:40 +01:00
parent 1555dc02e9
commit 2809d18c12
13 changed files with 3096 additions and 1026 deletions

View File

@@ -1,4 +1,6 @@
(function () {
let active = false;
let initialized = false;
const API_URL = window.API_URL || 'https://fb.srv.medeba-media.de/api';
const BULK_COUNT_STORAGE_KEY = 'dailyBookmarkBulkCount';
const FILTER_STORAGE_KEY = 'dailyBookmarkFilters';
@@ -27,55 +29,63 @@
let editingId = null;
const dayLabel = document.getElementById('dayLabel');
const daySubLabel = document.getElementById('daySubLabel');
const prevDayBtn = document.getElementById('prevDayBtn');
const nextDayBtn = document.getElementById('nextDayBtn');
const todayBtn = document.getElementById('todayBtn');
const refreshBtn = document.getElementById('refreshBtn');
const heroStats = document.getElementById('heroStats');
const listSummary = document.getElementById('listSummary');
const listStatus = document.getElementById('listStatus');
const tableBody = document.getElementById('tableBody');
const bulkCountSelect = document.getElementById('bulkCountSelect');
const bulkOpenBtn = document.getElementById('bulkOpenBtn');
const autoOpenToggle = document.getElementById('autoOpenToggle');
const autoOpenOverlay = document.getElementById('autoOpenOverlay');
const autoOpenOverlayPanel = document.getElementById('autoOpenOverlayPanel');
const autoOpenCountdown = document.getElementById('autoOpenCountdown');
const openCreateBtn = document.getElementById('openCreateBtn');
const dailyDayLabel = document.getElementById('dailyDayLabel');
const dailyDaySubLabel = document.getElementById('dailyDaySubLabel');
const dailyPrevDayBtn = document.getElementById('dailyPrevDayBtn');
const dailyNextDayBtn = document.getElementById('dailyNextDayBtn');
const dailyTodayBtn = document.getElementById('dailyTodayBtn');
const dailyRefreshBtn = document.getElementById('dailyRefreshBtn');
const dailyHeroStats = document.getElementById('dailyHeroStats');
const dailyListSummary = document.getElementById('dailyListSummary');
const dailyListStatus = document.getElementById('dailyListStatus');
const dailyTableBody = document.getElementById('dailyTableBody');
const dailyBulkCountSelect = document.getElementById('dailyBulkCountSelect');
const dailyBulkOpenBtn = document.getElementById('dailyBulkOpenBtn');
const dailyAutoOpenToggle = document.getElementById('dailyAutoOpenToggle');
const dailyAutoOpenOverlay = document.getElementById('dailyAutoOpenOverlay');
const dailyAutoOpenOverlayPanel = document.getElementById('dailyAutoOpenOverlayPanel');
const dailyAutoOpenCountdown = document.getElementById('dailyAutoOpenCountdown');
const dailyOpenCreateBtn = document.getElementById('dailyOpenCreateBtn');
const modal = document.getElementById('bookmarkModal');
const modalCloseBtn = document.getElementById('modalCloseBtn');
const modalBackdrop = modal ? modal.querySelector('.modal__backdrop') : null;
const formEl = document.getElementById('bookmarkForm');
const titleInput = document.getElementById('titleInput');
const urlInput = document.getElementById('urlInput');
const notesInput = document.getElementById('notesInput');
const resetBtn = document.getElementById('resetBtn');
const submitBtn = document.getElementById('submitBtn');
const previewLink = document.getElementById('previewLink');
const formStatus = document.getElementById('formStatus');
const formModeLabel = document.getElementById('formModeLabel');
const urlSuggestionBox = document.getElementById('urlSuggestionBox');
const markerInput = document.getElementById('markerInput');
const markerFilterSelect = document.getElementById('markerFilter');
const urlFilterInput = document.getElementById('urlFilter');
const resetViewBtn = document.getElementById('resetViewBtn');
const modal = document.getElementById('dailyBookmarkModal');
const dailyModalCloseBtn = document.getElementById('dailyModalCloseBtn');
const dailyModalBackdrop = modal ? modal.querySelector('.modal__backdrop') : null;
const formEl = document.getElementById('dailyBookmarkForm');
const dailyTitleInput = document.getElementById('dailyTitleInput');
const dailyUrlInput = document.getElementById('dailyUrlInput');
const dailyNotesInput = document.getElementById('dailyNotesInput');
const dailyResetBtn = document.getElementById('dailyResetBtn');
const dailySubmitBtn = document.getElementById('dailySubmitBtn');
const dailyPreviewLink = document.getElementById('dailyPreviewLink');
const dailyFormStatus = document.getElementById('dailyFormStatus');
const dailyFormModeLabel = document.getElementById('dailyFormModeLabel');
const dailyUrlSuggestionBox = document.getElementById('dailyUrlSuggestionBox');
const dailyMarkerInput = document.getElementById('dailyMarkerInput');
const dailyActiveInput = document.getElementById('dailyActiveInput');
const markerFilterSelect = document.getElementById('dailyMarkerFilter');
const urlFilterInput = document.getElementById('dailyUrlFilter');
const dailyResetViewBtn = document.getElementById('dailyResetViewBtn');
const sortButtons = Array.from(document.querySelectorAll('[data-sort-key]'));
const openImportBtn = document.getElementById('openImportBtn');
const importModal = document.getElementById('importModal');
const importCloseBtn = document.getElementById('importCloseBtn');
const importBackdrop = importModal ? importModal.querySelector('.modal__backdrop') : null;
const importForm = document.getElementById('importForm');
const importInput = document.getElementById('importInput');
const importMarkerInput = document.getElementById('importMarkerInput');
const importResetBtn = document.getElementById('importResetBtn');
const importSubmitBtn = document.getElementById('importSubmitBtn');
const importStatus = document.getElementById('importStatus');
const dailyOpenImportBtn = document.getElementById('dailyOpenImportBtn');
const dailyImportModal = document.getElementById('dailyImportModal');
const dailyImportCloseBtn = document.getElementById('dailyImportCloseBtn');
const dailyImportBackdrop = dailyImportModal ? dailyImportModal.querySelector('.modal__backdrop') : null;
const dailyImportForm = document.getElementById('dailyImportForm');
const dailyImportInput = document.getElementById('dailyImportInput');
const dailyImportMarkerInput = document.getElementById('dailyImportMarkerInput');
const dailyImportResetBtn = document.getElementById('dailyImportResetBtn');
const dailyImportSubmitBtn = document.getElementById('dailyImportSubmitBtn');
const dailyImportStatus = document.getElementById('dailyImportStatus');
const PLACEHOLDER_PATTERN = /\{\{\s*([^}]+)\s*\}\}/gi;
function ensureStyles(enabled) {
const link = document.getElementById('dailyBookmarksCss');
if (link) {
link.disabled = !enabled;
}
}
function formatDayKey(date) {
const d = date instanceof Date ? date : new Date();
const year = d.getFullYear();
@@ -214,6 +224,16 @@
}
}
function normalizeItem(item) {
if (!item || typeof item !== 'object') {
return null;
}
return {
...item,
is_active: Number(item.is_active ?? 1) !== 0
};
}
function resolveTemplate(template, dayKey) {
if (typeof template !== 'string') {
return '';
@@ -343,32 +363,32 @@
}
function renderUrlSuggestions() {
if (!urlSuggestionBox) {
if (!dailyUrlSuggestionBox) {
return;
}
const suggestions = buildUrlSuggestions(urlInput ? urlInput.value : '');
urlSuggestionBox.innerHTML = '';
const suggestions = buildUrlSuggestions(dailyUrlInput ? dailyUrlInput.value : '');
dailyUrlSuggestionBox.innerHTML = '';
if (!suggestions.length) {
urlSuggestionBox.hidden = true;
dailyUrlSuggestionBox.hidden = true;
return;
}
const text = document.createElement('span');
text.className = 'suggestion-box__text';
text.textContent = 'Mögliche Platzhalter:';
urlSuggestionBox.appendChild(text);
dailyUrlSuggestionBox.appendChild(text);
const applySuggestion = (value) => {
if (!urlInput) {
if (!dailyUrlInput) {
return;
}
urlInput.value = value;
dailyUrlInput.value = value;
updatePreviewLink();
renderUrlSuggestions();
const end = urlInput.value.length;
urlInput.focus();
const end = dailyUrlInput.value.length;
dailyUrlInput.focus();
try {
urlInput.setSelectionRange(end, end);
dailyUrlInput.setSelectionRange(end, end);
} catch (error) {
// ignore if not supported
}
@@ -400,10 +420,10 @@
});
item.appendChild(preview);
urlSuggestionBox.appendChild(item);
dailyUrlSuggestionBox.appendChild(item);
});
urlSuggestionBox.hidden = false;
dailyUrlSuggestionBox.hidden = false;
}
async function apiFetch(url, options = {}) {
@@ -438,60 +458,64 @@
}
function updateDayUI() {
if (dayLabel) {
dayLabel.textContent = formatDayLabel(state.dayKey);
if (dailyDayLabel) {
dailyDayLabel.textContent = formatDayLabel(state.dayKey);
}
if (daySubLabel) {
daySubLabel.textContent = formatRelativeDay(state.dayKey);
if (dailyDaySubLabel) {
dailyDaySubLabel.textContent = formatRelativeDay(state.dayKey);
}
updatePreviewLink();
}
function setDayKey(dayKey) {
state.dayKey = formatDayKey(parseDayKey(dayKey));
if (!active) return;
updateDayUI();
loadDailyBookmarks();
}
function setFormStatus(message, isError = false) {
if (!formStatus) {
if (!dailyFormStatus) {
return;
}
formStatus.textContent = message || '';
formStatus.classList.toggle('form-status--error', !!isError);
dailyFormStatus.textContent = message || '';
dailyFormStatus.classList.toggle('form-status--error', !!isError);
}
function setListStatus(message, isError = false) {
if (!listStatus) {
if (!dailyListStatus) {
return;
}
listStatus.textContent = message || '';
listStatus.classList.toggle('list-status--error', !!isError);
dailyListStatus.textContent = message || '';
dailyListStatus.classList.toggle('list-status--error', !!isError);
}
function updatePreviewLink() {
if (!previewLink || !urlInput) {
if (!dailyPreviewLink || !dailyUrlInput) {
return;
}
const resolved = resolveTemplate(urlInput.value || '', state.dayKey);
previewLink.textContent = resolved || '';
const resolved = resolveTemplate(dailyUrlInput.value || '', state.dayKey);
dailyPreviewLink.textContent = resolved || '';
if (resolved) {
previewLink.href = resolved;
previewLink.target = '_blank';
previewLink.rel = 'noopener';
dailyPreviewLink.href = resolved;
dailyPreviewLink.target = '_blank';
dailyPreviewLink.rel = 'noopener';
} else {
previewLink.removeAttribute('href');
dailyPreviewLink.removeAttribute('href');
}
renderUrlSuggestions();
}
function resetForm() {
editingId = null;
formModeLabel.textContent = 'Neues Bookmark';
submitBtn.textContent = 'Speichern';
dailyFormModeLabel.textContent = 'Neues Bookmark';
dailySubmitBtn.textContent = 'Speichern';
formEl.reset();
if (markerInput) {
markerInput.value = '';
if (dailyMarkerInput) {
dailyMarkerInput.value = '';
}
if (dailyActiveInput) {
dailyActiveInput.checked = true;
}
setFormStatus('');
updatePreviewLink();
@@ -499,18 +523,21 @@
}
function openModal(mode, bookmark) {
if (importModal && !importModal.hidden) {
if (dailyImportModal && !dailyImportModal.hidden) {
closeImportModal();
}
if (mode === 'edit' && bookmark) {
editingId = bookmark.id;
formModeLabel.textContent = 'Bookmark bearbeiten';
submitBtn.textContent = 'Aktualisieren';
titleInput.value = bookmark.title || '';
urlInput.value = bookmark.url_template || '';
notesInput.value = bookmark.notes || '';
if (markerInput) {
markerInput.value = bookmark.marker || '';
dailyFormModeLabel.textContent = 'Bookmark bearbeiten';
dailySubmitBtn.textContent = 'Aktualisieren';
dailyTitleInput.value = bookmark.title || '';
dailyUrlInput.value = bookmark.url_template || '';
dailyNotesInput.value = bookmark.notes || '';
if (dailyMarkerInput) {
dailyMarkerInput.value = bookmark.marker || '';
}
if (dailyActiveInput) {
dailyActiveInput.checked = bookmark.is_active !== false;
}
setFormStatus('Bearbeite vorhandenes Bookmark');
} else {
@@ -522,10 +549,10 @@
modal.focus();
}
updatePreviewLink();
if (mode === 'edit' && titleInput) {
titleInput.focus();
} else if (urlInput) {
urlInput.focus();
if (mode === 'edit' && dailyTitleInput) {
dailyTitleInput.focus();
} else if (dailyUrlInput) {
dailyUrlInput.focus();
}
}
@@ -572,22 +599,22 @@
}
function getFilteredItems() {
const markerFilter = state.filters.marker || '';
const urlFilter = (state.filters.url || '').toLowerCase();
const dailyMarkerFilter = state.filters.marker || '';
const dailyUrlFilter = (state.filters.url || '').toLowerCase();
return state.items.filter((item) => {
const currentMarker = normalizeMarkerValue(item.marker).toLowerCase();
if (markerFilter === '__none') {
if (dailyMarkerFilter === '__none') {
if (currentMarker) {
return false;
}
} else if (markerFilter) {
if (currentMarker !== markerFilter.toLowerCase()) {
} else if (dailyMarkerFilter) {
if (currentMarker !== dailyMarkerFilter.toLowerCase()) {
return false;
}
}
if (urlFilter) {
if (dailyUrlFilter) {
const urlValue = (item.resolved_url || item.url_template || '').toLowerCase();
if (!urlValue.includes(urlFilter)) {
if (!urlValue.includes(dailyUrlFilter)) {
return false;
}
}
@@ -676,11 +703,11 @@
}
function renderTable() {
if (!tableBody) {
if (!dailyTableBody) {
return;
}
tableBody.innerHTML = '';
dailyTableBody.innerHTML = '';
updateSortIndicators();
if (state.loading) {
@@ -706,7 +733,12 @@
visibleItems.forEach((item) => {
const tr = document.createElement('tr');
tr.classList.add(item.completed_for_day ? 'is-done' : 'is-open');
const isActive = item.is_active !== false;
if (isActive) {
tr.classList.add(item.completed_for_day ? 'is-done' : 'is-open');
} else {
tr.classList.add('is-inactive');
}
const urlTd = document.createElement('td');
urlTd.className = 'url-cell';
@@ -722,14 +754,23 @@
const markerTd = document.createElement('td');
markerTd.className = 'marker-cell';
markerTd.textContent = '';
if (normalizeMarkerValue(item.marker)) {
const markerChip = document.createElement('span');
markerChip.className = 'chip chip--marker';
markerChip.textContent = item.marker;
markerTd.appendChild(markerChip);
} else {
markerTd.textContent = '';
markerTd.classList.add('muted');
const placeholder = document.createElement('span');
placeholder.textContent = '';
placeholder.classList.add('muted');
markerTd.appendChild(placeholder);
}
if (!isActive) {
const inactiveChip = document.createElement('span');
inactiveChip.className = 'chip chip--inactive';
inactiveChip.textContent = 'Deaktiviert';
markerTd.appendChild(inactiveChip);
}
tr.appendChild(markerTd);
@@ -748,9 +789,11 @@
openBtn.className = 'ghost-btn';
openBtn.type = 'button';
openBtn.textContent = '🔗';
openBtn.title = 'Öffnen';
openBtn.title = isActive ? 'Öffnen' : 'Deaktiviert';
openBtn.disabled = !isActive;
openBtn.addEventListener('click', () => {
const target = item.resolved_url || item.url_template;
if (!isActive) return;
if (target) {
window.open(target, '_blank', 'noopener');
}
@@ -761,7 +804,10 @@
toggleBtn.className = 'ghost-btn';
toggleBtn.type = 'button';
toggleBtn.textContent = item.completed_for_day ? '↩️' : '✅';
toggleBtn.title = item.completed_for_day ? 'Zurücksetzen' : 'Heute erledigt';
toggleBtn.title = isActive
? (item.completed_for_day ? 'Zurücksetzen' : 'Heute erledigt')
: 'Deaktiviert';
toggleBtn.disabled = !isActive;
toggleBtn.addEventListener('click', () => {
if (item.completed_for_day) {
undoDailyBookmark(item.id);
@@ -792,7 +838,7 @@
actionsTd.appendChild(deleteBtn);
tr.appendChild(actionsTd);
tableBody.appendChild(tr);
dailyTableBody.appendChild(tr);
});
}
@@ -803,16 +849,20 @@
td.className = className;
td.textContent = text;
tr.appendChild(td);
tableBody.appendChild(tr);
dailyTableBody.appendChild(tr);
}
function updateHeroStats() {
const total = state.items.length;
const done = state.items.filter((item) => item.completed_for_day).length;
const visibleItems = getFilteredItems();
const activeItems = state.items.filter((item) => item.is_active !== false);
const totalActive = activeItems.length;
const inactiveCount = state.items.length - totalActive;
const done = activeItems.filter((item) => item.completed_for_day).length;
const visibleItems = getFilteredItems().filter((item) => item.is_active !== false);
const visibleDone = visibleItems.filter((item) => item.completed_for_day).length;
const filterSuffix = visibleItems.length !== total ? ` · Gefiltert: ${visibleDone}/${visibleItems.length}` : '';
const text = total ? `${done}/${total} erledigt${filterSuffix}` : 'Keine Bookmarks vorhanden';
const filterSuffix = visibleItems.length !== totalActive ? ` · Gefiltert: ${visibleDone}/${visibleItems.length}` : '';
const inactiveSuffix = inactiveCount ? ` · ${inactiveCount} deaktiviert` : '';
const baseText = totalActive ? `${done}/${totalActive} erledigt${filterSuffix}` : 'Keine aktiven Bookmarks';
const text = `${baseText}${inactiveSuffix}`;
const filterParts = [];
if (state.filters.marker) {
filterParts.push(state.filters.marker === '__none' ? 'ohne Marker' : `Marker: ${state.filters.marker}`);
@@ -821,11 +871,11 @@
filterParts.push(`URL enthält „${state.filters.url}`);
}
const filterText = filterParts.length ? ` · Filter: ${filterParts.join(' · ')}` : '';
if (heroStats) {
heroStats.textContent = `${text}${filterText}`;
if (dailyHeroStats) {
dailyHeroStats.textContent = `${text}${filterText}`;
}
if (listSummary) {
listSummary.textContent = `${text} Tag ${state.dayKey}${filterText}`;
if (dailyListSummary) {
dailyListSummary.textContent = `${text} Tag ${state.dayKey}${filterText}`;
}
}
@@ -837,27 +887,27 @@
}
function updateAutoOpenCountdownLabel(remainingMs) {
if (!autoOpenCountdown) return;
if (!dailyAutoOpenCountdown) return;
const safeMs = Math.max(0, remainingMs);
const seconds = safeMs / 1000;
const formatted = seconds >= 10 ? seconds.toFixed(0) : seconds.toFixed(1);
autoOpenCountdown.textContent = formatted;
dailyAutoOpenCountdown.textContent = formatted;
}
function hideAutoOpenOverlay() {
clearAutoOpenCountdown();
if (autoOpenOverlay) {
autoOpenOverlay.classList.remove('visible');
autoOpenOverlay.hidden = true;
if (dailyAutoOpenOverlay) {
dailyAutoOpenOverlay.classList.remove('visible');
dailyAutoOpenOverlay.hidden = true;
}
}
function showAutoOpenOverlay(delayMs) {
if (!autoOpenOverlay) return;
if (!dailyAutoOpenOverlay) return;
const duration = Math.max(0, delayMs);
hideAutoOpenOverlay();
autoOpenOverlay.hidden = false;
requestAnimationFrame(() => autoOpenOverlay.classList.add('visible'));
dailyAutoOpenOverlay.hidden = false;
requestAnimationFrame(() => dailyAutoOpenOverlay.classList.add('visible'));
updateAutoOpenCountdownLabel(duration);
const start = Date.now();
state.autoOpenCountdownIntervalId = setInterval(() => {
@@ -882,13 +932,19 @@
}
function maybeAutoOpen(reason = '', delayMs = AUTO_OPEN_DELAY_MS) {
if (!active) {
hideAutoOpenOverlay();
return;
}
if (!state.autoOpenEnabled) {
hideAutoOpenOverlay();
return;
}
if (state.processingBatch) return;
if (state.autoOpenTriggered) return;
const undone = getVisibleItems().filter((item) => !item.completed_for_day);
const undone = getVisibleItems().filter(
(item) => item.is_active !== false && !item.completed_for_day
);
if (!undone.length) {
hideAutoOpenOverlay();
return;
@@ -921,6 +977,7 @@
}
async function loadDailyBookmarks() {
if (!active) return;
state.loading = true;
if (state.autoOpenTimerId) {
clearTimeout(state.autoOpenTimerId);
@@ -933,7 +990,8 @@
renderTable();
try {
const data = await apiFetch(`${API_URL}/daily-bookmarks?day=${encodeURIComponent(state.dayKey)}`);
state.items = Array.isArray(data) ? data : [];
const rows = Array.isArray(data) ? data : [];
state.items = rows.map((item) => normalizeItem(item)).filter(Boolean);
state.loading = false;
updateHeroStats();
renderMarkerFilterOptions();
@@ -949,13 +1007,19 @@
async function completeDailyBookmark(id) {
if (!id) return;
const target = state.items.find((item) => item.id === id);
if (target && target.is_active === false) {
setListStatus('Bookmark ist deaktiviert.', true);
return;
}
state.error = '';
try {
const updated = await apiFetch(`${API_URL}/daily-bookmarks/${encodeURIComponent(id)}/check`, {
method: 'POST',
body: JSON.stringify({ day: state.dayKey })
});
state.items = state.items.map((item) => (item.id === id ? updated : item));
const normalized = normalizeItem(updated);
state.items = state.items.map((item) => (item.id === id ? normalized || item : item));
updateHeroStats();
renderTable();
} catch (error) {
@@ -967,12 +1031,18 @@
async function undoDailyBookmark(id) {
if (!id) return;
const target = state.items.find((item) => item.id === id);
if (target && target.is_active === false) {
setListStatus('Bookmark ist deaktiviert.', true);
return;
}
state.error = '';
try {
const updated = await apiFetch(`${API_URL}/daily-bookmarks/${encodeURIComponent(id)}/check?day=${encodeURIComponent(state.dayKey)}`, {
method: 'DELETE'
});
state.items = state.items.map((item) => (item.id === id ? updated : item));
const normalized = normalizeItem(updated);
state.items = state.items.map((item) => (item.id === id ? normalized || item : item));
updateHeroStats();
renderTable();
} catch (error) {
@@ -1007,19 +1077,19 @@
event.preventDefault();
if (state.saving) return;
const title = titleInput.value.trim();
const url = urlInput.value.trim();
const notes = notesInput.value.trim();
const marker = markerInput ? markerInput.value.trim() : '';
const title = dailyTitleInput.value.trim();
const url = dailyUrlInput.value.trim();
const notes = dailyNotesInput.value.trim();
const marker = dailyMarkerInput ? dailyMarkerInput.value.trim() : '';
if (!url) {
setFormStatus('URL-Template ist Pflicht.', true);
urlInput.focus();
dailyUrlInput.focus();
return;
}
state.saving = true;
submitBtn.disabled = true;
dailySubmitBtn.disabled = true;
setFormStatus('');
const payload = {
@@ -1027,6 +1097,7 @@
url_template: url,
notes,
marker,
is_active: dailyActiveInput ? dailyActiveInput.checked : true,
day: state.dayKey
};
@@ -1040,10 +1111,11 @@
method,
body: JSON.stringify(payload)
});
const normalized = normalizeItem(saved);
if (editingId) {
state.items = state.items.map((item) => (item.id === editingId ? saved : item));
} else {
state.items = [saved, ...state.items];
state.items = state.items.map((item) => (item.id === editingId ? normalized || item : item));
} else if (normalized) {
state.items = [normalized, ...state.items];
}
updateHeroStats();
renderMarkerFilterOptions();
@@ -1053,7 +1125,7 @@
setFormStatus('Speichern fehlgeschlagen.', true);
} finally {
state.saving = false;
submitBtn.disabled = false;
dailySubmitBtn.disabled = false;
}
}
@@ -1062,7 +1134,9 @@
if (!auto) {
cancelAutoOpen(false);
}
const undone = getVisibleItems().filter((item) => !item.completed_for_day);
const undone = getVisibleItems().filter(
(item) => item.is_active !== false && !item.completed_for_day
);
if (!undone.length) {
if (!auto) {
setListStatus('Keine offenen Bookmarks für den gewählten Tag.', true);
@@ -1074,8 +1148,8 @@
const selection = undone.slice(0, count);
state.processingBatch = true;
if (bulkOpenBtn) {
bulkOpenBtn.disabled = true;
if (dailyBulkOpenBtn) {
dailyBulkOpenBtn.disabled = true;
}
if (!auto) {
setListStatus('');
@@ -1093,15 +1167,16 @@
method: 'POST',
body: JSON.stringify({ day: state.dayKey })
});
state.items = state.items.map((entry) => (entry.id === item.id ? updated : entry));
const normalized = normalizeItem(updated);
state.items = state.items.map((entry) => (entry.id === item.id ? normalized || entry : entry));
} catch (error) {
setListStatus('Einige Bookmarks konnten nicht abgehakt werden.', true);
}
}
state.processingBatch = false;
if (bulkOpenBtn) {
bulkOpenBtn.disabled = false;
if (dailyBulkOpenBtn) {
dailyBulkOpenBtn.disabled = false;
}
if (auto) {
setListStatus('');
@@ -1111,20 +1186,20 @@
}
function setImportStatus(message, isError = false) {
if (!importStatus) {
if (!dailyImportStatus) {
return;
}
importStatus.textContent = message || '';
importStatus.classList.toggle('form-status--error', !!isError);
dailyImportStatus.textContent = message || '';
dailyImportStatus.classList.toggle('form-status--error', !!isError);
}
function resetImportForm() {
if (importForm) {
importForm.reset();
if (dailyImportForm) {
dailyImportForm.reset();
}
const suggestedMarker = state.filters.marker && state.filters.marker !== '__none' ? state.filters.marker : '';
if (importMarkerInput) {
importMarkerInput.value = suggestedMarker;
if (dailyImportMarkerInput) {
dailyImportMarkerInput.value = suggestedMarker;
}
setImportStatus('');
}
@@ -1134,18 +1209,18 @@
closeModal();
}
resetImportForm();
if (importModal) {
importModal.hidden = false;
importModal.focus();
if (dailyImportModal) {
dailyImportModal.hidden = false;
dailyImportModal.focus();
}
if (importInput) {
importInput.focus();
if (dailyImportInput) {
dailyImportInput.focus();
}
}
function closeImportModal() {
if (importModal) {
importModal.hidden = true;
if (dailyImportModal) {
dailyImportModal.hidden = true;
}
resetImportForm();
}
@@ -1154,12 +1229,12 @@
event.preventDefault();
if (state.importing) return;
const rawText = importInput ? importInput.value.trim() : '';
const marker = importMarkerInput ? importMarkerInput.value.trim() : '';
const rawText = dailyImportInput ? dailyImportInput.value.trim() : '';
const marker = dailyImportMarkerInput ? dailyImportMarkerInput.value.trim() : '';
if (!rawText) {
setImportStatus('Bitte füge mindestens eine URL ein.', true);
if (importInput) {
importInput.focus();
if (dailyImportInput) {
dailyImportInput.focus();
}
return;
}
@@ -1175,8 +1250,8 @@
}
state.importing = true;
if (importSubmitBtn) {
importSubmitBtn.disabled = true;
if (dailyImportSubmitBtn) {
dailyImportSubmitBtn.disabled = true;
}
setImportStatus('Import läuft...');
@@ -1186,7 +1261,8 @@
body: JSON.stringify({ urls, marker, day: state.dayKey })
});
const importedItems = Array.isArray(result && result.items) ? result.items : [];
const importedItemsRaw = Array.isArray(result && result.items) ? result.items : [];
const importedItems = importedItemsRaw.map((item) => normalizeItem(item)).filter(Boolean);
const importedIds = new Set(importedItems.map((entry) => entry.id));
const remaining = state.items.filter((item) => !importedIds.has(item.id));
state.items = [...importedItems, ...remaining];
@@ -1207,30 +1283,30 @@
setImportStatus(error && error.message ? error.message : 'Import fehlgeschlagen.', true);
} finally {
state.importing = false;
if (importSubmitBtn) {
importSubmitBtn.disabled = false;
if (dailyImportSubmitBtn) {
dailyImportSubmitBtn.disabled = false;
}
}
}
function setupEvents() {
if (prevDayBtn) {
prevDayBtn.addEventListener('click', () => setDayKey(formatDayKey(addDays(parseDayKey(state.dayKey), -1))));
if (dailyPrevDayBtn) {
dailyPrevDayBtn.addEventListener('click', () => setDayKey(formatDayKey(addDays(parseDayKey(state.dayKey), -1))));
}
if (nextDayBtn) {
nextDayBtn.addEventListener('click', () => setDayKey(formatDayKey(addDays(parseDayKey(state.dayKey), 1))));
if (dailyNextDayBtn) {
dailyNextDayBtn.addEventListener('click', () => setDayKey(formatDayKey(addDays(parseDayKey(state.dayKey), 1))));
}
if (todayBtn) {
todayBtn.addEventListener('click', () => setDayKey(formatDayKey(new Date())));
if (dailyTodayBtn) {
dailyTodayBtn.addEventListener('click', () => setDayKey(formatDayKey(new Date())));
}
if (refreshBtn) {
refreshBtn.addEventListener('click', () => loadDailyBookmarks());
if (dailyRefreshBtn) {
dailyRefreshBtn.addEventListener('click', () => loadDailyBookmarks());
}
if (openCreateBtn) {
openCreateBtn.addEventListener('click', () => openModal('create'));
if (dailyOpenCreateBtn) {
dailyOpenCreateBtn.addEventListener('click', () => openModal('create'));
}
if (modalCloseBtn) {
modalCloseBtn.addEventListener('click', closeModal);
if (dailyModalCloseBtn) {
dailyModalCloseBtn.addEventListener('click', closeModal);
}
if (modal) {
modal.addEventListener('click', (event) => {
@@ -1239,49 +1315,50 @@
}
});
}
if (modalBackdrop) {
modalBackdrop.addEventListener('click', closeModal);
if (dailyModalBackdrop) {
dailyModalBackdrop.addEventListener('click', closeModal);
}
document.addEventListener('keydown', (event) => {
if (!active) return;
if (event.key === 'Escape') {
if (modal && !modal.hidden) {
closeModal();
}
if (importModal && !importModal.hidden) {
if (dailyImportModal && !dailyImportModal.hidden) {
closeImportModal();
}
}
});
if (urlInput) {
urlInput.addEventListener('input', updatePreviewLink);
urlInput.addEventListener('blur', renderUrlSuggestions);
if (dailyUrlInput) {
dailyUrlInput.addEventListener('input', updatePreviewLink);
dailyUrlInput.addEventListener('blur', renderUrlSuggestions);
}
if (formEl) {
formEl.addEventListener('submit', submitForm);
}
if (resetBtn) {
resetBtn.addEventListener('click', resetForm);
if (dailyResetBtn) {
dailyResetBtn.addEventListener('click', resetForm);
}
if (bulkCountSelect) {
bulkCountSelect.value = String(state.bulkCount);
bulkCountSelect.addEventListener('change', () => {
const value = parseInt(bulkCountSelect.value, 10);
if (dailyBulkCountSelect) {
dailyBulkCountSelect.value = String(state.bulkCount);
dailyBulkCountSelect.addEventListener('change', () => {
const value = parseInt(dailyBulkCountSelect.value, 10);
if (!Number.isNaN(value)) {
state.bulkCount = value;
persistBulkCount(value);
}
});
}
if (bulkOpenBtn) {
bulkOpenBtn.addEventListener('click', () => openBatch());
if (dailyBulkOpenBtn) {
dailyBulkOpenBtn.addEventListener('click', () => openBatch());
}
if (autoOpenOverlayPanel) {
autoOpenOverlayPanel.addEventListener('click', () => cancelAutoOpen(true));
if (dailyAutoOpenOverlayPanel) {
dailyAutoOpenOverlayPanel.addEventListener('click', () => cancelAutoOpen(true));
}
if (autoOpenToggle) {
autoOpenToggle.checked = !!state.autoOpenEnabled;
autoOpenToggle.addEventListener('change', () => {
state.autoOpenEnabled = autoOpenToggle.checked;
if (dailyAutoOpenToggle) {
dailyAutoOpenToggle.checked = !!state.autoOpenEnabled;
dailyAutoOpenToggle.addEventListener('change', () => {
state.autoOpenEnabled = dailyAutoOpenToggle.checked;
persistAutoOpenEnabled(state.autoOpenEnabled);
state.autoOpenTriggered = false;
if (!state.autoOpenEnabled && state.autoOpenTimerId) {
@@ -1322,8 +1399,8 @@
renderTable();
});
}
if (resetViewBtn) {
resetViewBtn.addEventListener('click', () => {
if (dailyResetViewBtn) {
dailyResetViewBtn.addEventListener('click', () => {
state.filters = { marker: '', url: '' };
state.sort = { ...DEFAULT_SORT };
persistFilters(state.filters);
@@ -1352,36 +1429,54 @@
});
});
}
if (openImportBtn) {
openImportBtn.addEventListener('click', openImportModal);
if (dailyOpenImportBtn) {
dailyOpenImportBtn.addEventListener('click', openImportModal);
}
if (importCloseBtn) {
importCloseBtn.addEventListener('click', closeImportModal);
if (dailyImportCloseBtn) {
dailyImportCloseBtn.addEventListener('click', closeImportModal);
}
if (importModal) {
importModal.addEventListener('click', (event) => {
if (event.target === importModal) {
if (dailyImportModal) {
dailyImportModal.addEventListener('click', (event) => {
if (event.target === dailyImportModal) {
closeImportModal();
}
});
}
if (importBackdrop) {
importBackdrop.addEventListener('click', closeImportModal);
if (dailyImportBackdrop) {
dailyImportBackdrop.addEventListener('click', closeImportModal);
}
if (importForm) {
importForm.addEventListener('submit', submitImportForm);
if (dailyImportForm) {
dailyImportForm.addEventListener('submit', submitImportForm);
}
if (importResetBtn) {
importResetBtn.addEventListener('click', resetImportForm);
if (dailyImportResetBtn) {
dailyImportResetBtn.addEventListener('click', resetImportForm);
}
}
function init() {
updateDayUI();
if (initialized) return;
setupEvents();
loadDailyBookmarks();
updatePreviewLink();
initialized = true;
}
init();
function cleanup() {
active = false;
cancelAutoOpen(false);
ensureStyles(false);
hideAutoOpenOverlay();
}
function activate() {
ensureStyles(true);
init();
active = true;
updateDayUI();
updatePreviewLink();
loadDailyBookmarks();
}
window.DailyBookmarksPage = {
activate,
deactivate: cleanup
};
})();