From bc61311ebc4d90e1c6db693de13f5b66982c6734 Mon Sep 17 00:00:00 2001 From: Ulas Kalayci Date: Mon, 4 May 2026 22:27:48 +0200 Subject: [PATCH] fix(settings): resolve scope error for CalDAV/CardDAV load functions Root cause: loadCalDAVAccounts and loadCardDAVAccounts were defined inside the render function but called from bindIcsEvents (outside render), causing ReferenceError when accessing Settings page. Changes: - Move loadCalDAVAccounts and loadCardDAVAccounts to top-level functions - Add user parameter to both functions (no longer in render scope) - Update all recursive calls to pass user parameter - Move initial load code from bindIcsEvents to render function Fixes the "loadCalDAVAccounts is not defined" error in v0.47.0. Co-Authored-By: Claude Sonnet 4.5 --- public/pages/settings.js | 558 +++++++++++++++++++-------------------- 1 file changed, 279 insertions(+), 279 deletions(-) diff --git a/public/pages/settings.js b/public/pages/settings.js index d7995d1..900c2f7 100644 --- a/public/pages/settings.js +++ b/public/pages/settings.js @@ -872,9 +872,286 @@ docker cp oikos:/data/oikos-backup.db ./oikos-backup.db }); } + // Initial Load: CalDAV & CardDAV Accounts + if (container.querySelector('#caldav-accounts-list')) { + loadCalDAVAccounts(container, user); + } + if (container.querySelector('#cardav-accounts-list')) { + loadCardDAVAccounts(container, user); + } + bindEvents(container, user, users, categories, icsSubscriptions, apiTokens); if (window.lucide) window.lucide.createIcons(); } +// CalDAV-Konten laden +async function loadCalDAVAccounts(container, user) { + const listEl = container.querySelector('#caldav-accounts-list'); + const emptyEl = container.querySelector('#caldav-empty-state'); + if (!listEl || !emptyEl) return; + + try { + const accountsRes = await api.get('/calendar/caldav/accounts'); + const accounts = accountsRes.data || []; + + if (accounts.length === 0) { + listEl.replaceChildren(); + emptyEl.style.display = ''; + return; + } + + emptyEl.style.display = 'none'; + listEl.replaceChildren(); + + for (const account of accounts) { + const calendarsRes = await api.get(`/calendar/caldav/accounts/${account.id}/calendars`); + const calendars = calendarsRes.data || []; + + const accountCard = document.createElement('div'); + accountCard.className = 'caldav-account-item'; + accountCard.insertAdjacentHTML('beforeend', ` + +
+ + ${t('settings.caldavCalendarsToggle')} (${calendars.length}) + +
+ ${calendars.map((cal) => ` + + `).join('')} +
+
+ + `); + listEl.appendChild(accountCard); + } + + if (window.lucide) lucide.createIcons({ el: listEl }); + + // Bind calendar checkbox events + listEl.querySelectorAll('.caldav-calendar-checkbox').forEach((checkbox) => { + checkbox.addEventListener('change', async () => { + const accountId = parseInt(checkbox.dataset.accountId, 10); + const calendarUrl = checkbox.dataset.calendarUrl; + const enabled = checkbox.checked; + + try { + await api.patch(`/calendar/caldav/accounts/${accountId}/calendars`, { + calendarUrl, + enabled, + }); + window.oikos?.showToast( + enabled ? t('settings.calendarEnabled') : t('settings.calendarDisabled'), + 'success' + ); + } catch (err) { + window.oikos?.showToast(err.message, 'danger'); + checkbox.checked = !enabled; // Revert on error + } + }); + }); + + // Bind sync buttons + listEl.querySelectorAll('[data-caldav-sync]').forEach((btn) => { + btn.addEventListener('click', async () => { + btn.disabled = true; + const originalText = btn.textContent; + btn.textContent = t('settings.synchronizing'); + try { + await api.post('/calendar/caldav/sync'); + window.oikos?.showToast(t('settings.caldavSyncSuccess'), 'success'); + await loadCalDAVAccounts(container, user); + } catch (err) { + window.oikos?.showToast(err.message || t('settings.caldavSyncFailed'), 'danger'); + } finally { + btn.disabled = false; + btn.textContent = originalText; + } + }); + }); + + // Bind refresh buttons + listEl.querySelectorAll('[data-caldav-refresh]').forEach((btn) => { + btn.addEventListener('click', async () => { + const accountId = parseInt(btn.dataset.caldavRefresh, 10); + btn.disabled = true; + const originalText = btn.textContent; + btn.textContent = t('settings.loading'); + try { + await api.get(`/calendar/caldav/accounts/${accountId}/calendars?refresh=true`); + await loadCalDAVAccounts(container, user); + window.oikos?.showToast(t('settings.calendarsRefreshed'), 'success'); + } catch (err) { + window.oikos?.showToast(err.message, 'danger'); + } finally { + btn.disabled = false; + btn.textContent = originalText; + } + }); + }); + + // Bind delete buttons + listEl.querySelectorAll('[data-caldav-delete]').forEach((btn) => { + btn.addEventListener('click', async () => { + const accountId = parseInt(btn.dataset.caldavDelete, 10); + if (!await confirmModal(t('settings.deleteAccountConfirm'), { danger: true })) return; + try { + await api.delete(`/calendar/caldav/accounts/${accountId}`); + window.oikos?.showToast(t('settings.caldavAccountDeleted'), 'success'); + await loadCalDAVAccounts(container, user); + } catch (err) { + window.oikos?.showToast(err.message, 'danger'); + } + }); + }); + + } catch (err) { + console.error('Failed to load CalDAV accounts:', err); + window.oikos?.showToast(t('settings.caldavConnectionFailed'), 'danger'); + } +} + +// CardDAV-Konten laden +async function loadCardDAVAccounts(container, user) { + const listEl = container.querySelector('#cardav-accounts-list'); + const emptyEl = container.querySelector('#cardav-empty-state'); + if (!listEl || !emptyEl) return; + + try { + const accountsRes = await api.get('/contacts/cardav/accounts'); + const accounts = accountsRes.data || []; + + if (accounts.length === 0) { + listEl.replaceChildren(); + emptyEl.style.display = ''; + return; + } + + emptyEl.style.display = 'none'; + listEl.replaceChildren(); + + for (const account of accounts) { + const addressbooksRes = await api.get(`/contacts/cardav/accounts/${account.id}/addressbooks`); + const addressbooks = addressbooksRes.data || []; + + const accountCard = document.createElement('div'); + accountCard.className = 'caldav-account-item'; + accountCard.insertAdjacentHTML('beforeend', ` + +
+ + ${t('settings.cardavAddressbooksToggle')} (${addressbooks.length}) + +
+ ${addressbooks.map((ab) => ` + + `).join('')} +
+
+ + `); + + // Addressbook toggle + accountCard.querySelectorAll('.cardav-addressbook-checkbox').forEach((checkbox) => { + checkbox.addEventListener('change', async () => { + const accountId = parseInt(checkbox.dataset.accountId, 10); + const addressbookUrl = checkbox.dataset.addressbookUrl; + const enabled = checkbox.checked; + try { + await api.post(`/contacts/cardav/accounts/${accountId}/addressbooks/toggle`, { + addressbookUrl, + enabled, + }); + window.oikos?.showToast(enabled ? t('settings.addressbookEnabled') : t('settings.addressbookDisabled'), 'success'); + } catch (err) { + window.oikos?.showToast(err.message, 'error'); + checkbox.checked = !enabled; + } + }); + }); + + // Sync button + const syncBtn = accountCard.querySelector(`[data-cardav-sync="${account.id}"]`); + if (syncBtn) { + syncBtn.addEventListener('click', async () => { + try { + await api.post(`/contacts/cardav/accounts/${account.id}/sync`); + window.oikos?.showToast(t('settings.cardavSyncSuccess'), 'success'); + await loadCardDAVAccounts(container, user); + } catch (err) { + window.oikos?.showToast(t('settings.cardavSyncFailed'), 'error'); + } + }); + } + + // Refresh button + const refreshBtn = accountCard.querySelector(`[data-cardav-refresh="${account.id}"]`); + if (refreshBtn) { + refreshBtn.addEventListener('click', async () => { + try { + await api.post(`/contacts/cardav/accounts/${account.id}/addressbooks/refresh`); + window.oikos?.showToast(t('settings.addressbooksRefreshed'), 'success'); + await loadCardDAVAccounts(container, user); + } catch (err) { + window.oikos?.showToast(err.message, 'error'); + } + }); + } + + // Delete button + const deleteBtn = accountCard.querySelector(`[data-cardav-delete="${account.id}"]`); + if (deleteBtn) { + deleteBtn.addEventListener('click', async () => { + const confirmed = await confirmModal(t('settings.deleteCardDAVAccountConfirm')); + if (!confirmed) return; + try { + await api.delete(`/contacts/cardav/accounts/${account.id}`); + window.oikos?.showToast(t('settings.cardavAccountDeleted'), 'success'); + await loadCardDAVAccounts(container, user); + } catch (err) { + window.oikos?.showToast(err.message, 'error'); + } + }); + } + + listEl.appendChild(accountCard); + } + } catch (err) { + console.error('Failed to load CardDAV accounts:', err); + } +} // -------------------------------------------------------- // Event-Binding @@ -1207,275 +1484,6 @@ function bindEvents(container, user, users, categories, icsSubscriptions, apiTok }); } - // CalDAV-Konten laden - async function loadCalDAVAccounts(container) { - const listEl = container.querySelector('#caldav-accounts-list'); - const emptyEl = container.querySelector('#caldav-empty-state'); - if (!listEl || !emptyEl) return; - - try { - const accountsRes = await api.get('/calendar/caldav/accounts'); - const accounts = accountsRes.data || []; - - if (accounts.length === 0) { - listEl.replaceChildren(); - emptyEl.style.display = ''; - return; - } - - emptyEl.style.display = 'none'; - listEl.replaceChildren(); - - for (const account of accounts) { - const calendarsRes = await api.get(`/calendar/caldav/accounts/${account.id}/calendars`); - const calendars = calendarsRes.data || []; - - const accountCard = document.createElement('div'); - accountCard.className = 'caldav-account-item'; - accountCard.insertAdjacentHTML('beforeend', ` - -
- - ${t('settings.caldavCalendarsToggle')} (${calendars.length}) - -
- ${calendars.map((cal) => ` - - `).join('')} -
-
- - `); - listEl.appendChild(accountCard); - } - - if (window.lucide) lucide.createIcons({ el: listEl }); - - // Bind calendar checkbox events - listEl.querySelectorAll('.caldav-calendar-checkbox').forEach((checkbox) => { - checkbox.addEventListener('change', async () => { - const accountId = parseInt(checkbox.dataset.accountId, 10); - const calendarUrl = checkbox.dataset.calendarUrl; - const enabled = checkbox.checked; - - try { - await api.patch(`/calendar/caldav/accounts/${accountId}/calendars`, { - calendarUrl, - enabled, - }); - window.oikos?.showToast( - enabled ? t('settings.calendarEnabled') : t('settings.calendarDisabled'), - 'success' - ); - } catch (err) { - window.oikos?.showToast(err.message, 'danger'); - checkbox.checked = !enabled; // Revert on error - } - }); - }); - - // Bind sync buttons - listEl.querySelectorAll('[data-caldav-sync]').forEach((btn) => { - btn.addEventListener('click', async () => { - btn.disabled = true; - const originalText = btn.textContent; - btn.textContent = t('settings.synchronizing'); - try { - await api.post('/calendar/caldav/sync'); - window.oikos?.showToast(t('settings.caldavSyncSuccess'), 'success'); - await loadCalDAVAccounts(container); - } catch (err) { - window.oikos?.showToast(err.message || t('settings.caldavSyncFailed'), 'danger'); - } finally { - btn.disabled = false; - btn.textContent = originalText; - } - }); - }); - - // Bind refresh buttons - listEl.querySelectorAll('[data-caldav-refresh]').forEach((btn) => { - btn.addEventListener('click', async () => { - const accountId = parseInt(btn.dataset.caldavRefresh, 10); - btn.disabled = true; - const originalText = btn.textContent; - btn.textContent = t('settings.loading'); - try { - await api.get(`/calendar/caldav/accounts/${accountId}/calendars?refresh=true`); - await loadCalDAVAccounts(container); - window.oikos?.showToast(t('settings.calendarsRefreshed'), 'success'); - } catch (err) { - window.oikos?.showToast(err.message, 'danger'); - } finally { - btn.disabled = false; - btn.textContent = originalText; - } - }); - }); - - // Bind delete buttons - listEl.querySelectorAll('[data-caldav-delete]').forEach((btn) => { - btn.addEventListener('click', async () => { - const accountId = parseInt(btn.dataset.caldavDelete, 10); - if (!await confirmModal(t('settings.deleteAccountConfirm'), { danger: true })) return; - try { - await api.delete(`/calendar/caldav/accounts/${accountId}`); - window.oikos?.showToast(t('settings.caldavAccountDeleted'), 'success'); - await loadCalDAVAccounts(container); - } catch (err) { - window.oikos?.showToast(err.message, 'danger'); - } - }); - }); - - } catch (err) { - console.error('Failed to load CalDAV accounts:', err); - window.oikos?.showToast(t('settings.caldavConnectionFailed'), 'danger'); - } - } - - // CardDAV-Konten laden - async function loadCardDAVAccounts(container) { - const listEl = container.querySelector('#cardav-accounts-list'); - const emptyEl = container.querySelector('#cardav-empty-state'); - if (!listEl || !emptyEl) return; - - try { - const accountsRes = await api.get('/contacts/cardav/accounts'); - const accounts = accountsRes.data || []; - - if (accounts.length === 0) { - listEl.replaceChildren(); - emptyEl.style.display = ''; - return; - } - - emptyEl.style.display = 'none'; - listEl.replaceChildren(); - - for (const account of accounts) { - const addressbooksRes = await api.get(`/contacts/cardav/accounts/${account.id}/addressbooks`); - const addressbooks = addressbooksRes.data || []; - - const accountCard = document.createElement('div'); - accountCard.className = 'caldav-account-item'; - accountCard.insertAdjacentHTML('beforeend', ` - -
- - ${t('settings.cardavAddressbooksToggle')} (${addressbooks.length}) - -
- ${addressbooks.map((ab) => ` - - `).join('')} -
-
- - `); - - // Addressbook toggle - accountCard.querySelectorAll('.cardav-addressbook-checkbox').forEach((checkbox) => { - checkbox.addEventListener('change', async () => { - const accountId = parseInt(checkbox.dataset.accountId, 10); - const addressbookUrl = checkbox.dataset.addressbookUrl; - const enabled = checkbox.checked; - try { - await api.post(`/contacts/cardav/accounts/${accountId}/addressbooks/toggle`, { - addressbookUrl, - enabled, - }); - window.oikos?.showToast(enabled ? t('settings.addressbookEnabled') : t('settings.addressbookDisabled'), 'success'); - } catch (err) { - window.oikos?.showToast(err.message, 'error'); - checkbox.checked = !enabled; - } - }); - }); - - // Sync button - const syncBtn = accountCard.querySelector(`[data-cardav-sync="${account.id}"]`); - if (syncBtn) { - syncBtn.addEventListener('click', async () => { - try { - await api.post(`/contacts/cardav/accounts/${account.id}/sync`); - window.oikos?.showToast(t('settings.cardavSyncSuccess'), 'success'); - await loadCardDAVAccounts(container); - } catch (err) { - window.oikos?.showToast(t('settings.cardavSyncFailed'), 'error'); - } - }); - } - - // Refresh button - const refreshBtn = accountCard.querySelector(`[data-cardav-refresh="${account.id}"]`); - if (refreshBtn) { - refreshBtn.addEventListener('click', async () => { - try { - await api.post(`/contacts/cardav/accounts/${account.id}/addressbooks/refresh`); - window.oikos?.showToast(t('settings.addressbooksRefreshed'), 'success'); - await loadCardDAVAccounts(container); - } catch (err) { - window.oikos?.showToast(err.message, 'error'); - } - }); - } - - // Delete button - const deleteBtn = accountCard.querySelector(`[data-cardav-delete="${account.id}"]`); - if (deleteBtn) { - deleteBtn.addEventListener('click', async () => { - const confirmed = await confirmModal(t('settings.deleteCardDAVAccountConfirm')); - if (!confirmed) return; - try { - await api.delete(`/contacts/cardav/accounts/${account.id}`); - window.oikos?.showToast(t('settings.cardavAccountDeleted'), 'success'); - await loadCardDAVAccounts(container); - } catch (err) { - window.oikos?.showToast(err.message, 'error'); - } - }); - } - - listEl.appendChild(accountCard); - } - } catch (err) { - console.error('Failed to load CardDAV accounts:', err); - } - } // CalDAV add account button const caldavAddBtn = container.querySelector('#caldav-add-account-btn'); @@ -1533,7 +1541,7 @@ function bindEvents(container, user, users, categories, icsSubscriptions, apiTok }); closeModal({ force: true }); window.oikos?.showToast(t('settings.caldavAccountAdded'), 'success'); - await loadCalDAVAccounts(container); + await loadCalDAVAccounts(container, user); } catch (err) { showError(errorEl, err.message); } @@ -1597,7 +1605,7 @@ function bindEvents(container, user, users, categories, icsSubscriptions, apiTok }); closeModal({ force: true }); window.oikos?.showToast(t('settings.cardavAccountAdded'), 'success'); - await loadCardDAVAccounts(container); + await loadCardDAVAccounts(container, user); } catch (err) { showError(errorEl, err.message); } @@ -2564,14 +2572,6 @@ function bindIcsEvents(container, user, initialSubs) { } }); } - - // Initial Load: CalDAV & CardDAV Accounts - if (container.querySelector('#caldav-accounts-list')) { - loadCalDAVAccounts(container); - } - if (container.querySelector('#cardav-accounts-list')) { - loadCardDAVAccounts(container); - } } function initials(name) {