mail_calendar_sync/js/personal-settings.js
2026-02-09 23:02:34 -05:00

296 lines
10 KiB
JavaScript

/**
* Mail Calendar Sync - Personal Settings JavaScript
*/
(function() {
'use strict';
const BASE_URL = OC.generateUrl('/apps/mail_calendar_sync/api');
let currentConfig = {};
let mailAccounts = [];
let calendars = [];
async function init() {
try {
const [configData, accountsData, calendarsData] = await Promise.all([
fetchJson(`${BASE_URL}/config`),
fetchJson(`${BASE_URL}/mail-accounts`),
fetchJson(`${BASE_URL}/calendars`),
]);
currentConfig = configData;
mailAccounts = accountsData;
calendars = calendarsData;
renderForm();
loadLog();
} catch (error) {
console.error('Failed to load Mail Calendar Sync settings:', error);
showStatus('Failed to load settings: ' + error.message, 'error');
} finally {
document.getElementById('mcs-loading').style.display = 'none';
document.getElementById('mcs-config').style.display = '';
}
}
function renderForm() {
// Enabled checkbox
const enabledCheckbox = document.getElementById('mcs-enabled');
enabledCheckbox.checked = currentConfig.enabled || false;
enabledCheckbox.addEventListener('change', onEnabledChange);
// Mail accounts dropdown
const mailSelect = document.getElementById('mcs-mail-account');
mailSelect.innerHTML = '<option value="">Select a mail account…</option>';
mailAccounts.forEach(account => {
const option = document.createElement('option');
option.value = account.id;
option.textContent = `${account.name} (${account.email})`;
if (currentConfig.mailAccountId == account.id) {
option.selected = true;
}
mailSelect.appendChild(option);
});
if (mailAccounts.length === 0) {
const option = document.createElement('option');
option.value = '';
option.textContent = 'No mail accounts found — configure one in the Mail app first';
option.disabled = true;
mailSelect.appendChild(option);
}
// Calendars dropdown
const calSelect = document.getElementById('mcs-calendar');
calSelect.innerHTML = '<option value="">Select a calendar…</option>';
calendars.forEach(cal => {
const option = document.createElement('option');
option.value = cal.uri;
option.textContent = cal.name;
if (cal.color) {
option.style.borderLeft = `4px solid ${cal.color}`;
option.style.paddingLeft = '8px';
}
if (currentConfig.calendarUri === cal.uri) {
option.selected = true;
}
calSelect.appendChild(option);
});
// Sync interval dropdown
const intervalSelect = document.getElementById('mcs-sync-interval');
const currentInterval = currentConfig.syncInterval || 600;
for (let i = 0; i < intervalSelect.options.length; i++) {
if (parseInt(intervalSelect.options[i].value) === currentInterval) {
intervalSelect.options[i].selected = true;
break;
}
}
// Auto-accept checkbox
document.getElementById('mcs-auto-accept').checked = currentConfig.autoAccept || false;
// Show/hide sections based on enabled state
updateSectionVisibility(currentConfig.enabled || false);
// Bind buttons
document.getElementById('mcs-save').addEventListener('click', onSave);
document.getElementById('mcs-trigger-sync').addEventListener('click', onTriggerSync);
}
function onEnabledChange(event) {
updateSectionVisibility(event.target.checked);
}
function updateSectionVisibility(enabled) {
const sections = [
'mcs-mail-section',
'mcs-calendar-section',
'mcs-interval-section',
'mcs-auto-accept-section',
'mcs-save-section',
];
sections.forEach(id => {
document.getElementById(id).style.display = enabled ? '' : 'none';
});
}
async function onSave() {
const saveBtn = document.getElementById('mcs-save');
saveBtn.disabled = true;
showStatus('Saving…', 'loading');
const config = {
enabled: document.getElementById('mcs-enabled').checked,
mailAccountId: document.getElementById('mcs-mail-account').value || null,
calendarUri: document.getElementById('mcs-calendar').value || null,
autoAccept: document.getElementById('mcs-auto-accept').checked,
syncInterval: parseInt(document.getElementById('mcs-sync-interval').value) || 600,
};
if (config.enabled) {
if (!config.mailAccountId) {
showStatus('Please select a mail account', 'error');
saveBtn.disabled = false;
return;
}
if (!config.calendarUri) {
showStatus('Please select a calendar', 'error');
saveBtn.disabled = false;
return;
}
}
try {
const result = await fetchJson(`${BASE_URL}/config`, {
method: 'PUT',
body: JSON.stringify(config),
headers: {
'Content-Type': 'application/json',
},
});
currentConfig = result;
showStatus('Settings saved', 'success');
if (config.enabled) {
document.getElementById('mcs-log-section').style.display = '';
loadLog();
}
} catch (error) {
console.error('Failed to save config:', error);
showStatus('Failed to save: ' + error.message, 'error');
} finally {
saveBtn.disabled = false;
}
}
async function onTriggerSync() {
const btn = document.getElementById('mcs-trigger-sync');
btn.disabled = true;
showStatus('Syncing…', 'loading');
// Show the sync details section
const detailsSection = document.getElementById('mcs-sync-details');
const syncLog = document.getElementById('mcs-sync-log');
detailsSection.style.display = '';
syncLog.textContent = 'Running sync…';
try {
const result = await fetchJson(`${BASE_URL}/trigger-sync`, {
method: 'POST',
});
const stats = result.stats || {};
const messages = stats.messages || [];
// Show detailed log
if (messages.length > 0) {
syncLog.textContent = messages.join('\n');
} else {
syncLog.textContent = 'Sync completed with no messages.';
}
const msg = `Sync complete: ${stats.processed || 0} scanned, ${stats.updated || 0} updated, ${stats.errors || 0} errors`;
showStatus(msg, stats.errors > 0 ? 'warning' : 'success');
loadLog();
} catch (error) {
console.error('Sync failed:', error);
syncLog.textContent = 'Sync failed: ' + error.message;
showStatus('Sync failed — see details below', 'error');
} finally {
btn.disabled = false;
}
}
async function loadLog() {
try {
const entries = await fetchJson(`${BASE_URL}/log?limit=20`);
const tbody = document.getElementById('mcs-log-body');
const emptyMsg = document.getElementById('mcs-log-empty');
const logSection = document.getElementById('mcs-log-section');
if (currentConfig.enabled) {
logSection.style.display = '';
}
tbody.innerHTML = '';
if (entries.length === 0) {
emptyMsg.style.display = '';
return;
}
emptyMsg.style.display = 'none';
entries.forEach(entry => {
const tr = document.createElement('tr');
tr.className = `mcs-log-${entry.action.toLowerCase()}`;
const date = entry.createdAt
? new Date(entry.createdAt).toLocaleString()
: '—';
tr.innerHTML = `
<td class="mcs-log-date">${escapeHtml(date)}</td>
<td class="mcs-log-event">${escapeHtml(entry.eventSummary || entry.eventUid || '—')}</td>
<td class="mcs-log-action"><span class="mcs-badge mcs-badge-${entry.action.toLowerCase()}">${escapeHtml(entry.action)}</span></td>
<td class="mcs-log-message">${escapeHtml(entry.message || '—')}</td>
`;
tbody.appendChild(tr);
});
} catch (error) {
console.error('Failed to load log:', error);
}
}
function showStatus(message, type) {
const statusEl = document.getElementById('mcs-status');
statusEl.textContent = message;
statusEl.className = `mcs-status mcs-status-${type}`;
if (type === 'success') {
setTimeout(() => {
statusEl.textContent = '';
statusEl.className = 'mcs-status';
}, 5000);
}
}
async function fetchJson(url, options = {}) {
const { headers: optHeaders, ...restOptions } = options;
const response = await fetch(url, {
credentials: 'same-origin',
...restOptions,
headers: {
'requesttoken': OC.requestToken,
...(optHeaders || {}),
},
});
if (!response.ok) {
let errorMsg = `HTTP ${response.status}`;
try {
const errorData = await response.json();
if (errorData.error) {
errorMsg = errorData.error;
}
} catch (e) {
// ignore parse error
}
throw new Error(errorMsg);
}
return response.json();
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
document.addEventListener('DOMContentLoaded', init);
})();