/* ── Categories page ── */
const CategoriesPage = (() => {
function escHtml(str) {
return String(str || '').replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"');
}
async function init(container) {
await render(container);
}
async function render(container) {
let cats;
try {
cats = await API.categories();
} catch (e) {
container.innerHTML = `
Failed to load: ${e.message}
`;
return;
}
container.innerHTML = `
${cats.map(c => renderItem(c)).join('')}
${cats.length === 0 ? `
` : ''}
`;
document.getElementById('cat-add-form').onsubmit = async (e) => {
e.preventDefault();
const name = document.getElementById('cat-new-name').value.trim();
if (!name) return;
try {
await API.createCategory(name);
document.getElementById('cat-new-name').value = '';
showToast('Category added', 'success');
render(container);
} catch (err) {
showToast('Error: ' + err.message, 'error');
}
};
container.querySelectorAll('.btn-delete-cat').forEach(btn => {
btn.onclick = async () => {
if (!confirm('Delete this category? Bills using it will be uncategorized.')) return;
try {
await API.deleteCategory(btn.dataset.id);
showToast('Category deleted', 'success');
render(container);
} catch (err) {
showToast('Error: ' + err.message, 'error');
}
};
});
container.querySelectorAll('.cat-name-span').forEach(span => {
span.ondblclick = () => startRename(span, cats.find(c => c.id == span.dataset.id), container);
});
}
function renderItem(cat) {
return `
${escHtml(cat.name)}
`;
}
function startRename(span, cat, container) {
const input = document.createElement('input');
input.type = 'text';
input.value = cat.name;
input.className = 'cat-name';
input.style.flex = '1';
span.replaceWith(input);
input.focus();
input.select();
async function commit() {
const name = input.value.trim();
if (!name || name === cat.name) { render(container); return; }
try {
await API.updateCategory(cat.id, name);
showToast('Renamed', 'success');
render(container);
} catch (err) {
showToast('Error: ' + err.message, 'error');
render(container);
}
}
input.addEventListener('blur', commit);
input.addEventListener('keydown', e => {
if (e.key === 'Enter') input.blur();
if (e.key === 'Escape') render(container);
});
}
return { init };
})();