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('')}
+
+
+
+
+
+ ${user?.role === 'admin' ? `` : ''}
+
+ `);
+ 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('')}
-
-
-
-
-
- ${user?.role === 'admin' ? `` : ''}
-
- `);
- 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) {