// ── CalDAV Settings + Calendar Config ──
//
// Genuinely CalDAV-scoped concerns only:
// * Settings panel (connection URL / user / password, Test + Save)
// * Per-calendar color/icon configuration (persisted in localStorage)
//
// Sync orchestration (triggerSync, background poll, autosync toggle,
// sync-interval, relay settings, sync.tick hook) lives in sync.js.
import { $, esc, api, getCsrf } from './util.js';
const CAL_ICONS = [
{ id: 'calendar-dots', label: 'calendar' },
{ id: 'briefcase', label: 'work' },
{ id: 'house', label: 'home' },
{ id: 'heart', label: 'personal' },
{ id: 'cake', label: 'birthday' },
{ id: 'graduation-cap', label: 'education' },
{ id: 'airplane', label: 'travel' },
{ id: 'basketball', label: 'sport' },
{ id: 'first-aid', label: 'health' },
{ id: 'star', label: 'important' },
{ id: 'users', label: 'team' },
{ id: 'phone', label: 'calls' },
];
const CAL_COLORS = ['#38bdf8', '#8b5cf6', '#fb7185', '#fbbf24', '#4ade80', '#fb923c', '#22d3ee', '#f472b6', '#a3e635', '#94a3b8'];
let calendarConfigs = {}; // slug → { color, icon }
const CAL_CONFIG_KEY = 'hyprcrd-cal-configs';
const settingsOverlay = $('settings-overlay');
const urlInput = $('caldav-url');
const userInput = $('caldav-user');
const passInput = $('caldav-pass');
const testStatus = $('caldav-test-status');
const syncStatus = $('caldav-sync-status');
const logEl = $('caldav-log');
export function openCaldavSettings() {
// Open settings overlay on sync tab
settingsOverlay?.classList.add('open');
settingsOverlay?.querySelectorAll('.settings-tab').forEach(t => t.classList.toggle('active', t.dataset.tab === 'caldav'));
settingsOverlay?.querySelectorAll('[id^="settings-panel-"]').forEach(p => p.classList.toggle('hidden', p.id !== 'settings-panel-caldav'));
loadSettings();
}
export function closeCaldavSettings() {
settingsOverlay?.classList.remove('open');
}
function loadCalConfigs() {
try { calendarConfigs = JSON.parse(localStorage.getItem(CAL_CONFIG_KEY) || '{}'); } catch { calendarConfigs = {}; }
}
function saveCalConfigs() {
localStorage.setItem(CAL_CONFIG_KEY, JSON.stringify(calendarConfigs));
}
export function getCalConfig(slug) {
loadCalConfigs();
return calendarConfigs[slug] || null;
}
export function getAllCalConfigs() {
loadCalConfigs();
return calendarConfigs;
}
function renderCalList(calendars) {
const container = $('caldav-cal-list');
const wrap = $('caldav-calendars');
if (!container || !wrap || !calendars?.length) return;
loadCalConfigs();
wrap.classList.remove('hidden');
container.innerHTML = calendars.map(cal => {
const slug = cal.name.toLowerCase().replace(/[^a-z0-9_-]/g, '');
const cfg = calendarConfigs[slug] || {};
const activeColor = cfg.color || '#94a3b8';
const activeIcon = cfg.icon || 'calendar-dots';
return `
${esc(cal.name)}
${CAL_COLORS.map(c => ``).join('')}
${CAL_ICONS.slice(0, 6).map(ic => ``).join('')}
`;
}).join('');
container.querySelectorAll('.cal-cfg-color').forEach(btn => {
btn.addEventListener('click', () => {
const slug = btn.closest('[data-cal-slug]').dataset.calSlug;
if (!calendarConfigs[slug]) calendarConfigs[slug] = {};
calendarConfigs[slug].color = btn.dataset.color;
saveCalConfigs();
renderCalList(calendars);
});
});
container.querySelectorAll('.cal-cfg-icon').forEach(btn => {
btn.addEventListener('click', () => {
const slug = btn.closest('[data-cal-slug]').dataset.calSlug;
if (!calendarConfigs[slug]) calendarConfigs[slug] = {};
calendarConfigs[slug].icon = btn.dataset.icon;
saveCalConfigs();
renderCalList(calendars);
});
});
}
export async function loadSettings() {
const s = await api('api/caldav/settings');
if (!s) return;
if (urlInput) urlInput.value = s.url || '';
if (userInput) userInput.value = s.username || '';
if (passInput) passInput.value = '';
if (passInput && s.has_password) passInput.placeholder = '(saved)';
if (syncStatus) syncStatus.textContent = s.last_sync ? `last sync: ${s.last_sync}` : '';
// Load calendar list if configured
if (s.url && s.has_password) {
const test = await fetch('api/caldav/test', {
method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': getCsrf() },
body: '{}',
}).then(r => r.json()).catch(() => null);
if (test?.calendars) renderCalList(test.calendars);
}
}
function setCaldavTestStatus(text, kind = 'info') {
if (!testStatus) return;
if (!text) {
testStatus.classList.add('hidden');
testStatus.textContent = '';
testStatus.style.borderColor = '';
testStatus.style.color = '';
return;
}
testStatus.classList.remove('hidden');
testStatus.textContent = text;
if (kind === 'error') {
testStatus.style.borderColor = 'var(--t-danger)';
testStatus.style.color = 'var(--t-danger)';
} else if (kind === 'success') {
testStatus.style.borderColor = 'var(--t-accent)';
testStatus.style.color = 'var(--t-accent)';
} else {
testStatus.style.borderColor = 'var(--t-border)';
testStatus.style.color = 'var(--t-muted)';
}
}
$('caldav-test')?.addEventListener('click', async () => {
setCaldavTestStatus('testing...');
const body = { url: urlInput.value, username: userInput.value };
if (passInput.value) body.password = passInput.value;
const res = await fetch('api/caldav/test', {
method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': getCsrf() },
body: JSON.stringify(body),
}).then(r => r.json()).catch(() => ({ ok: false, error: 'network error' }));
if (res.ok) {
const names = (res.calendars || []).map(c => c.name).join(', ');
setCaldavTestStatus(`connected (${names || 'no calendars'})`, 'success');
if (res.calendars) renderCalList(res.calendars);
} else {
setCaldavTestStatus(res.error || 'failed', 'error');
}
showConnectionLog(res.log);
});
$('caldav-save')?.addEventListener('click', async () => {
const body = { url: urlInput.value, username: userInput.value, enabled: true };
if (passInput.value) body.password = passInput.value;
await api('api/caldav/settings', { method: 'POST', body });
testStatus.textContent = 'saved';
testStatus.style.color = 'var(--t-accent)';
});
function showConnectionLog(lines) {
if (!logEl || !lines?.length) return;
logEl.classList.remove('hidden');
logEl.innerHTML = lines.map(l =>
`${l.replace(/`
).join('');
}
$('cal-caldav-settings')?.addEventListener('click', openCaldavSettings);