const favoriteIcons = { star: '', link: '', play: '', note: '', book: '', code: '', mail: '', calendar: '', image: '', tool: '' }; const iconLabels = { star: 'Stern', link: 'Link', play: 'Video', note: 'Notiz', book: 'Buch', code: 'Code', mail: 'Mail', calendar: 'Kalender', image: 'Bild', tool: 'Tool' }; const loginButton = document.querySelector('#favoritesLoginButton'); const logoutButton = document.querySelector('#favoritesLogoutButton'); const loginPanel = document.querySelector('#favoritesLoginPanel'); const panel = document.querySelector('#favoritesPanel'); const message = document.querySelector('#favoritesMessage'); const userBox = document.querySelector('#favoritesUser'); const editor = document.querySelector('#favoriteEditor'); const preview = document.querySelector('#favoritePreview'); const addButton = document.querySelector('#addFavoriteButton'); const saveButton = document.querySelector('#saveFavoritesButton'); const reloadButton = document.querySelector('#reloadFavoritesButton'); let favorites = []; function escapeHtml(value) { return String(value || '') .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function createDefaultFavorite() { return { id: crypto.randomUUID(), name: 'Neuer Link', url: 'https://', group: 'Favoriten', description: '', color: '#67e8f9', icon: 'star', logoUrl: '', order: favorites.length }; } function iconOptions(selected) { return Object.keys(favoriteIcons).map((icon) => ( `` )).join(''); } function syncFromInputs() { editor.querySelectorAll('.favorite-edit-card').forEach((row) => { const favorite = favorites[Number(row.dataset.index)]; if (!favorite) return; row.querySelectorAll('[data-field]').forEach((input) => { favorite[input.dataset.field] = input.value; }); }); } function moveFavorite(index, direction) { const next = index + direction; if (next < 0 || next >= favorites.length) return; syncFromInputs(); [favorites[index], favorites[next]] = [favorites[next], favorites[index]]; favorites = favorites.map((favorite, order) => ({ ...favorite, order })); render(); } function removeFavorite(index) { syncFromInputs(); favorites.splice(index, 1); favorites = favorites.map((favorite, order) => ({ ...favorite, order })); render(); } function renderPreviewCard(favorite) { const color = /^#[0-9a-fA-F]{6}$/.test(favorite.color) ? favorite.color : '#67e8f9'; const iconSvg = favoriteIcons[favorite.icon] || favoriteIcons.star; let host = ''; try { host = new URL(favorite.url).hostname; } catch { host = 'ungueltige-url'; } return `
${favorite.logoUrl ? `` : ``}
${escapeHtml(favorite.group || 'Favorit')}

${escapeHtml(favorite.name)}

${escapeHtml(favorite.description || 'Eigener Weblink')}

${escapeHtml(host)}
`; } function renderPreview() { preview.innerHTML = favorites.map(renderPreviewCard).join('') || '
Die Vorschau erscheint hier.
'; } function render() { editor.innerHTML = favorites.map((favorite, index) => `
${escapeHtml(favorite.name || 'Neuer Link')}
`).join('') || '
Noch keine Favoriten angelegt.
'; renderPreview(); editor.querySelectorAll('[data-field]').forEach((input) => { input.addEventListener('input', () => { syncFromInputs(); renderPreview(); }); }); editor.querySelectorAll('[data-action]').forEach((button) => { button.addEventListener('click', () => { const index = Number(button.closest('.favorite-edit-card').dataset.index); if (button.dataset.action === 'up') moveFavorite(index, -1); if (button.dataset.action === 'down') moveFavorite(index, 1); if (button.dataset.action === 'remove') removeFavorite(index); }); }); } async function loadFavorites() { const response = await fetch('/api/favorites', { cache: 'no-store', headers: MischLabsAuth.authHeader() }); const payload = await response.json().catch(() => ({})); if (!response.ok) throw new Error(payload.error || `HTTP ${response.status}`); favorites = Array.isArray(payload.favorites) ? payload.favorites : []; userBox.textContent = `Angemeldet als ${payload.user?.name || MischLabsAuth.displayName()}`; render(); } async function saveFavorites() { syncFromInputs(); const response = await fetch('/api/favorites', { method: 'PUT', headers: { 'Content-Type': 'application/json', ...MischLabsAuth.authHeader() }, body: JSON.stringify({ favorites }) }); const payload = await response.json().catch(() => ({})); if (!response.ok) throw new Error(payload.error || `HTTP ${response.status}`); favorites = payload.favorites || []; render(); userBox.textContent = 'Favoriten gespeichert.'; } function updateAuthUi() { const authenticated = MischLabsAuth.isAuthenticated(); loginButton.hidden = authenticated; logoutButton.hidden = !authenticated; loginPanel.hidden = authenticated; panel.hidden = !authenticated; } async function boot() { try { await MischLabsAuth.handleCallback(); } catch (error) { message.textContent = error.message; } updateAuthUi(); if (MischLabsAuth.isAuthenticated()) { await loadFavorites(); } } loginButton.addEventListener('click', () => MischLabsAuth.login()); logoutButton.addEventListener('click', () => MischLabsAuth.logout()); addButton.addEventListener('click', () => { syncFromInputs(); favorites.push(createDefaultFavorite()); render(); }); saveButton.addEventListener('click', () => { saveFavorites().catch((error) => { userBox.textContent = `Speichern fehlgeschlagen: ${error.message}`; }); }); reloadButton.addEventListener('click', () => { loadFavorites().catch((error) => { userBox.textContent = `Laden fehlgeschlagen: ${error.message}`; }); }); boot().catch((error) => { message.textContent = `Favoritenseite konnte nicht starten: ${error.message}`; });