BillTracker/legacy/js/settings.js

208 lines
8.3 KiB
JavaScript

/* ── Settings page ── */
const SettingsPage = (() => {
async function init(container) {
let settings, notifPrefs;
try {
[settings, notifPrefs] = await Promise.all([
API.settings(),
fetch('/api/notifications/me').then(r => r.ok ? r.json() : null),
]);
} catch (e) {
container.innerHTML = `<div class="empty-state"><p>Failed to load settings.</p></div>`;
return;
}
const notifSection = buildNotifSection(notifPrefs);
container.innerHTML = `
<div class="page-header">
<h1 class="page-title">Settings</h1>
</div>
<div class="settings-section">
<h3>General</h3>
<div class="settings-row">
<label for="s-currency">Currency</label>
<select id="s-currency" style="max-width:120px">
<option value="USD" ${settings.currency === 'USD' ? 'selected' : ''}>USD $</option>
<option value="EUR" ${settings.currency === 'EUR' ? 'selected' : ''}>EUR €</option>
<option value="GBP" ${settings.currency === 'GBP' ? 'selected' : ''}>GBP £</option>
<option value="CAD" ${settings.currency === 'CAD' ? 'selected' : ''}>CAD $</option>
</select>
</div>
<div class="settings-row">
<label for="s-date-format">Date Format</label>
<select id="s-date-format" style="max-width:160px">
<option value="MM/DD/YYYY" ${settings.date_format === 'MM/DD/YYYY' ? 'selected' : ''}>MM/DD/YYYY</option>
<option value="DD/MM/YYYY" ${settings.date_format === 'DD/MM/YYYY' ? 'selected' : ''}>DD/MM/YYYY</option>
<option value="YYYY-MM-DD" ${settings.date_format === 'YYYY-MM-DD' ? 'selected' : ''}>YYYY-MM-DD</option>
</select>
</div>
</div>
<div class="settings-section">
<h3>Billing Behavior</h3>
<div class="settings-row">
<label for="s-grace">Grace Period (days)</label>
<input type="number" id="s-grace" min="0" max="30" value="${settings.grace_period_days || 5}" style="max-width:80px">
</div>
</div>
<div class="settings-section">
<h3>Backup</h3>
<div class="settings-row">
<label>Auto Backup</label>
<label style="cursor:pointer">
<input type="checkbox" id="s-backup-enabled" ${settings.backup_enabled === 'true' ? 'checked' : ''}>
Enabled
</label>
</div>
<div class="settings-row">
<label for="s-backup-freq">Frequency (days)</label>
<input type="number" id="s-backup-freq" min="1" max="30" value="${settings.backup_frequency_days || 1}" style="max-width:80px">
</div>
<div class="settings-row">
<label for="s-backup-keep">Keep N Backups</label>
<input type="number" id="s-backup-keep" min="1" max="90" value="${settings.backup_keep_count || 14}" style="max-width:80px">
</div>
</div>
${notifSection}
<div style="display:flex; justify-content:flex-end; gap:8px; margin-top:8px">
<button class="btn btn-primary" id="save-settings-btn">Save Settings</button>
</div>
`;
document.getElementById('save-settings-btn').onclick = async () => {
const data = {
currency: document.getElementById('s-currency').value,
date_format: document.getElementById('s-date-format').value,
grace_period_days: document.getElementById('s-grace').value,
backup_enabled: document.getElementById('s-backup-enabled').checked ? 'true' : 'false',
backup_frequency_days: document.getElementById('s-backup-freq').value,
backup_keep_count: document.getElementById('s-backup-keep').value,
};
try {
await API.saveSettings(data);
showToast('Settings saved', 'success');
} catch (err) {
showToast('Error: ' + err.message, 'error');
}
};
// Notification save (only wired if section exists)
const notifForm = document.getElementById('notif-user-form');
if (notifForm) {
notifForm.onsubmit = async (e) => {
e.preventDefault();
const btn = notifForm.querySelector('[type="submit"]');
btn.disabled = true;
try {
const res = await fetch('/api/notifications/me', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
notification_email: document.getElementById('n-email').value.trim(),
notifications_enabled:document.getElementById('n-enabled').checked,
notify_3d: document.getElementById('n-3d').checked,
notify_1d: document.getElementById('n-1d').checked,
notify_due: document.getElementById('n-due').checked,
notify_overdue: document.getElementById('n-overdue').checked,
}),
});
if (!res.ok) throw new Error((await res.json()).error);
showToast('Notification preferences saved', 'success');
} catch (err) {
showToast('Error: ' + err.message, 'error');
} finally {
btn.disabled = false;
}
};
// Toggle field visibility based on enabled checkbox
const toggle = () => {
const on = document.getElementById('n-enabled').checked;
document.getElementById('n-options').style.opacity = on ? '1' : '.4';
document.getElementById('n-options').style.pointerEvents = on ? '' : 'none';
};
document.getElementById('n-enabled').addEventListener('change', toggle);
toggle();
}
}
function buildNotifSection(p) {
if (!p) return ''; // API call failed, skip silently
if (!p.smtp_enabled) {
return `
<div class="settings-section">
<h3>Notifications</h3>
<p style="color:var(--text-muted);font-size:13px;">
Email notifications have not been configured by the admin.
</p>
</div>`;
}
if (!p.allow_user_config) {
return `
<div class="settings-section">
<h3>Notifications</h3>
<p style="color:var(--text-muted);font-size:13px;">
Email notifications are enabled and managed by the admin.
Your bills will generate reminders automatically.
</p>
</div>`;
}
// Full user-configurable notification section
const chk = (id, label, val) =>
`<label style="display:flex;align-items:center;gap:8px;cursor:pointer;font-size:13px;font-weight:normal;color:var(--text)">
<input type="checkbox" id="${id}" ${val ? 'checked' : ''}> ${label}
</label>`;
return `
<div class="settings-section">
<h3>Notifications</h3>
<form id="notif-user-form">
<div class="settings-row">
<label>Enable Notifications</label>
<label style="cursor:pointer;display:flex;align-items:center;gap:6px;font-weight:normal;color:var(--text)">
<input type="checkbox" id="n-enabled" ${p.notifications_enabled ? 'checked' : ''}>
Send me email reminders
</label>
</div>
<div id="n-options">
<div class="settings-row" style="margin-top:6px">
<label for="n-email">Notification Email</label>
<input type="email" id="n-email" value="${escHtml(p.notification_email)}"
placeholder="your@email.com" style="max-width:280px">
</div>
<div class="settings-row" style="align-items:flex-start">
<label style="padding-top:2px">Remind me</label>
<div style="display:flex;flex-direction:column;gap:8px">
${chk('n-3d', '3 days before due', p.notify_3d)}
${chk('n-1d', '1 day before due', p.notify_1d)}
${chk('n-due', 'On the day it\'s due', p.notify_due)}
${chk('n-overdue', 'Daily while overdue', p.notify_overdue)}
</div>
</div>
</div>
<div style="display:flex;justify-content:flex-end;margin-top:12px">
<button type="submit" class="btn btn-ghost btn-sm">Save Notifications</button>
</div>
</form>
</div>`;
}
function escHtml(s) {
return String(s || '').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}
return { init };
})();