// ============================================================ // PAGERS & EXTENSIONS — personal directory with async search + soft delete // ============================================================ (function () { var _inited = false; var _items = []; var _mode = 'active'; // 'active' | 'trash' var _editId = null; // null = creating, non-null = editing var _searchDebounce = null; document.addEventListener('tabChanged', function (e) { if (e.detail.tab !== 'extensions' || _inited) return; _inited = true; init(); }); function init() { document.getElementById('ext-add-btn').addEventListener('click', onAddClick); document.getElementById('ext-cancel-btn').addEventListener('click', hideForm); document.getElementById('ext-save-btn').addEventListener('click', saveItem); document.getElementById('ext-trash-btn').addEventListener('click', toggleTrashMode); document.getElementById('ext-back-active').addEventListener('click', function () { _mode = 'active'; updateModeBanner(); load(); }); var searchInp = document.getElementById('ext-search'); searchInp.addEventListener('input', function () { clearTimeout(_searchDebounce); _searchDebounce = setTimeout(load, 200); }); // Keyboard: Esc cancels form document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && !document.getElementById('ext-form-wrap').classList.contains('hidden')) { hideForm(); } }); load(); loadTrashCount(); } function loadTrashCount() { fetch('/api/extensions?trash=1', { headers: getAuthHeaders() }) .then(function (r) { return r.json(); }) .then(function (d) { if (d.success) { var n = (d.items || []).length; document.getElementById('ext-trash-count').textContent = n ? '(' + n + ')' : ''; } }) .catch(function () {}); } function load() { var q = document.getElementById('ext-search').value.trim(); var url = '/api/extensions?' + (_mode === 'trash' ? 'trash=1&' : '') + (q ? 'q=' + encodeURIComponent(q) : ''); var listEl = document.getElementById('ext-list'); listEl.innerHTML = '

Loading...

'; fetch(url, { headers: getAuthHeaders() }) .then(function (r) { return r.json(); }) .then(function (d) { if (!d.success) { listEl.innerHTML = '

' + esc(d.error || 'Load failed') + '

'; return; } _items = d.items || []; render(); refreshLocationList(); }) .catch(function (err) { listEl.innerHTML = '

Load failed: ' + esc(err.message) + '

'; }); } function refreshLocationList() { // Populate datalist with existing unique locations for autocomplete var dl = document.getElementById('ext-location-list'); if (!dl) return; var seen = {}; var opts = []; _items.forEach(function (x) { if (!seen[x.location]) { seen[x.location] = true; opts.push(x.location); } }); dl.innerHTML = opts.map(function (l) { return '