fixed SPA
This commit is contained in:
@@ -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
|
||||
};
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user