// Project sections (Pages) editor — markdown content blocks for a project. // Mirrors item sections but scoped to projects. Loaded by the Settings tab. (function() { 'use strict'; function csrfHeaders() { var token = document.querySelector('meta[name="csrf-token"]'); return token ? { 'X-CSRF-Token': token.content } : {}; } function escapeHtml(s) { var d = document.createElement('div'); d.textContent = s; return d.innerHTML; } function showToast(msg) { if (window.showToast) { window.showToast(msg); return; } alert(msg); } function bodyFor(id) { var el = document.querySelector('textarea[data-body-for="' + id + '"]'); return el ? el.value : ''; } function updateCount(delta) { var el = document.getElementById('psection-count'); if (el) el.textContent = parseInt(el.textContent || '0') + delta; } function attachRowHandlers(row) { var delBtn = row.querySelector('.psection-del-btn'); var editBtn = row.querySelector('.psection-edit-btn'); delBtn.addEventListener('click', function() { var id = this.dataset.id; if (!confirm('Delete this page?')) return; fetch('/api/project-sections/' + id, { method: 'DELETE', headers: csrfHeaders() }) .then(function(res) { if (res.ok) { var hidden = document.querySelector('textarea[data-body-for="' + id + '"]'); if (hidden) hidden.remove(); row.remove(); updateCount(-1); } else { apiErrorMessage(res, 'Failed to delete').then(function(m) { showToast(m); }); } }) .catch(function() { showToast('Failed to delete'); }); }); editBtn.addEventListener('click', function() { var id = this.dataset.id; var title = this.dataset.title || row.querySelector('span').textContent; document.getElementById('edit-psec-id').value = id; document.getElementById('edit-psec-title').value = title; document.getElementById('edit-psec-body').value = bodyFor(id); document.getElementById('psec-edit-status').textContent = ''; document.getElementById('psection-edit-modal').classList.remove('hidden'); }); } function init() { var addBtn = document.getElementById('add-psec-btn'); if (!addBtn) return; var projectId = addBtn.dataset.projectId; addBtn.addEventListener('click', function() { var title = document.getElementById('new-psec-title').value.trim(); var body = document.getElementById('new-psec-body').value; var status = document.getElementById('psec-add-status'); if (!title) { status.textContent = 'Title is required'; return; } this.disabled = true; status.textContent = ''; fetch('/api/projects/' + projectId + '/sections', { method: 'POST', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()), body: JSON.stringify({ title: title, body: body }) }) .then(function(res) { if (!res.ok) return res.json().then(function(d) { throw new Error(d.error || 'Failed'); }); return res.json(); }) .then(function(sec) { var empty = document.getElementById('psections-empty'); if (empty) empty.remove(); var list = document.getElementById('psections-list'); var row = document.createElement('div'); row.className = 'psection-row'; row.dataset.id = sec.id; row.innerHTML = '' + escapeHtml(sec.title) + '' + '#section-' + escapeHtml(sec.slug) + '' + '' + (sec.body || '').length + ' chars' + '' + ''; list.appendChild(row); var hidden = document.createElement('textarea'); hidden.className = 'hidden'; hidden.dataset.bodyFor = sec.id; hidden.value = sec.body || ''; list.appendChild(hidden); attachRowHandlers(row); updateCount(1); document.getElementById('new-psec-title').value = ''; document.getElementById('new-psec-body').value = ''; document.getElementById('psection-add-details').removeAttribute('open'); }) .catch(function(err) { status.textContent = err.message; }) .finally(function() { addBtn.disabled = false; }); }); document.getElementById('save-psec-btn').addEventListener('click', function() { var id = document.getElementById('edit-psec-id').value; var title = document.getElementById('edit-psec-title').value.trim(); var body = document.getElementById('edit-psec-body').value; var status = document.getElementById('psec-edit-status'); if (!title) { status.textContent = 'Title is required'; return; } this.disabled = true; fetch('/api/project-sections/' + id, { method: 'PUT', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()), body: JSON.stringify({ title: title, body: body }) }) .then(function(res) { if (!res.ok) return res.json().then(function(d) { throw new Error(d.error || 'Failed'); }); return res.json(); }) .then(function(sec) { var row = document.querySelector('.psection-row[data-id="' + id + '"]'); if (row) { row.querySelector('.psection-row-title').textContent = sec.title; row.querySelector('.psection-row-anchor').textContent = '#section-' + sec.slug; row.querySelector('.psection-row-length').textContent = (sec.body || '').length + ' chars'; row.querySelector('.psection-edit-btn').dataset.title = sec.title; } var hidden = document.querySelector('textarea[data-body-for="' + id + '"]'); if (hidden) hidden.value = sec.body || ''; document.getElementById('psection-edit-modal').classList.add('hidden'); }) .catch(function(err) { status.textContent = err.message; }) .finally(function() { document.getElementById('save-psec-btn').disabled = false; }); }); document.getElementById('cancel-psec-btn').addEventListener('click', function() { document.getElementById('psection-edit-modal').classList.add('hidden'); }); document.querySelectorAll('.psection-row').forEach(attachRowHandlers); } // HTMX swaps in the settings partial; bind on swap + on initial load. if (document.getElementById('add-psec-btn')) init(); document.body.addEventListener('htmx:afterSettle', function(e) { if (e.target && e.target.querySelector && e.target.querySelector('#add-psec-btn')) init(); }); })();