USERS
@@ -990,6 +1041,7 @@ function loadTab(tab) {
email: ()=>{ loadEmailInbox(); loadEmailActionItems(); },
tasks: loadTasks,
appointments: loadAppts,
+ calendar: loadCalFeeds,
})[tab]?.();
}
@@ -1925,6 +1977,77 @@ function apptSave(id){ apiPost('appt_save',{id,title:document.getElementById('am
function apptDel(id){ if(!confirm('Delete appointment?'))return; apiPost('appt_delete',{id},()=>{toast('Deleted','ok');loadAppts();}); }
+// ── CALENDAR FEEDS ────────────────────────────────────────────────────────────
+async function loadCalFeeds() {
+ const feeds = await api('cal_feeds_list');
+ const el = document.getElementById('cal-feeds-tbl');
+ if (!feeds || !feeds.length) {
+ el.innerHTML = '
No calendar feeds configured. iCloud syncs via config.php credentials.
';
+ return;
+ }
+ const srcLabel = {google:'Google',icloud:'iCloud',outlook:'Outlook',caldav:'CalDAV',ics:'ICS URL'};
+ el.innerHTML = `
+ | NAME | SOURCE | ICS URL | LAST SYNC | COUNT | STATUS | ACTIONS |
+
${feeds.map(f=>`
+ | ${esc(f.name)} |
+ ${srcLabel[f.source]||f.source} |
+ ${esc(f.ics_url||'—')} |
+ ${ts(f.last_sync)} |
+ ${f.last_count||0} |
+ ${f.active?'ACTIVE':'PAUSED'} |
+
+ |
+
`).join('')}
`;
+}
+
+function calFeedModal(f={}) {
+ const id = f.id||0;
+ openModal(id?'EDIT CALENDAR FEED':'ADD CALENDAR FEED', `
+
+
+
+
+
+
+
+
+ `, () => calFeedSave(id));
+}
+
+async function calFeedSave(id) {
+ await apiPost('cal_feed_save', {
+ id, name: document.getElementById('cf-name').value,
+ source: document.getElementById('cf-source').value,
+ ics_url: document.getElementById('cf-ics').value,
+ username: document.getElementById('cf-user').value,
+ password: document.getElementById('cf-pass').value,
+ active: document.getElementById('cf-active').value
+ }, () => { toast('Saved','ok'); closeModal(); loadCalFeeds(); });
+}
+
+function calFeedDel(id) {
+ if (!confirm('Delete this calendar feed?')) return;
+ apiPost('cal_feed_delete', {id}, () => { toast('Deleted','ok'); loadCalFeeds(); });
+}
+
+async function syncCalNow() {
+ const btn = document.getElementById('calSyncBtn');
+ const status = document.getElementById('cal-sync-status');
+ btn.disabled = true; btn.textContent = '⟳ SYNCING...';
+ status.textContent = 'Syncing...';
+ apiPost('cal_sync_now', {}, d => {
+ status.textContent = d.ok ? Object.entries(d.results||{}).map(([k,v])=>k+': '+v).join(' | ') || 'Sync complete' : 'Error';
+ toast('Calendar sync complete','ok');
+ loadCalFeeds();
+ btn.disabled = false; btn.textContent = '⟳ SYNC NOW';
+ });
+}
+