fix: separate tree arrow (expand) from label (filter) in catalog

Arrow ▶ only toggles expand/collapse. Node label click filters products.
Forms inside tree (Add Category, Add District, etc.) no longer trigger
navigation when clicking their buttons or inputs.
This commit is contained in:
NW
2026-06-25 08:33:43 +01:00
parent c55ec47ea0
commit 1768c6a5c4
2 changed files with 21 additions and 11 deletions

View File

@@ -291,7 +291,6 @@ pre { font-size: 0.8rem; white-space: pre-wrap; word-break: break-all; max-width
.tree-node { margin-left: 0; }
.tree-toggle {
cursor: pointer;
user-select: none;
padding: 0.4rem 0;
display: flex;
@@ -308,10 +307,16 @@ pre { font-size: 0.8rem; white-space: pre-wrap; word-break: break-all; max-width
transition: transform 0.15s;
width: 12px;
text-align: center;
cursor: pointer;
}
.tree-toggle .arrow.open { transform: rotate(90deg); }
.tree-toggle .node-label {
cursor: pointer;
flex: 1;
}
.tree-children { display: none; }
.tree-children.open { display: block; }

View File

@@ -12,7 +12,7 @@ export function renderCatalog(tree, products, filter, categories, subcategories,
const editFormHtml = renderProductEditForm('/catalog/products/__ID__/edit', catOptions, subcatJson, locations)
.replace(/`/g, '\\`').replace(/\$/g, '\\$');
let treeHtml = '<div class="tree-node"><div class="tree-toggle" data-all="1"><span class="arrow">▶</span> <strong>All Products</strong><span class="tree-count">(' + products.length + ')</span></div></div>';
let treeHtml = '<div class="tree-node"><div class="tree-toggle"><span class="arrow" data-toggle="1">▶</span> <span class="node-label" data-all="1"><strong>All Products</strong></span><span class="tree-count">(' + products.length + ')</span></div></div>';
for (const [country, cdata] of Object.entries(tree)) {
let countryCount = 0, cityHtml = '';
for (const [city, ddata] of Object.entries(cdata.cities)) {
@@ -24,17 +24,17 @@ export function renderCatalog(tree, products, filter, categories, subcategories,
districtCount += catCount;
let subHtml = '';
for (const s of (c.subs||[])) {
subHtml += `<div class="tree-node"><div class="tree-toggle" data-sub="${s.id}"><span class="arrow">▶</span> ${esc(s.name)}<span class="tree-count">(${s.pc||0})</span><span class="tree-actions"><form method="POST" action="/catalog/subcategories/${s.id}/delete" onsubmit="return confirm('Delete?')"><button class="btn-sm btn-danger">✕</button></form></span></div><div class="tree-children"><form method="POST" action="/catalog/categories/${c.id}/subcategories" class="inline-form tree-add"><input name="name" placeholder="+ Subcategory" required size="12"><button class="btn-sm">Add</button></form></div></div>`;
subHtml += `<div class="tree-node"><div class="tree-toggle"><span class="arrow" data-toggle="1">▶</span> <span class="node-label" data-sub="${s.id}">${esc(s.name)}</span><span class="tree-count">(${s.pc||0})</span><span class="tree-actions"><form method="POST" action="/catalog/subcategories/${s.id}/delete" onsubmit="return confirm('Delete?')"><button class="btn-sm btn-danger">✕</button></form></span></div><div class="tree-children"><form method="POST" action="/catalog/categories/${c.id}/subcategories" class="inline-form tree-add"><input name="name" placeholder="+ Subcategory" required size="12"><button class="btn-sm">Add</button></form></div></div>`;
}
catHtml += `<div class="tree-node"><div class="tree-toggle" data-cat="${c.id}"><span class="arrow">▶</span> ${esc(c.name)}<span class="tree-count">(${catCount})</span><span class="tree-actions"><form method="POST" action="/catalog/categories/${c.id}/delete" onsubmit="return confirm('Delete?')"><button class="btn-sm btn-danger">✕</button></form></span></div><div class="tree-children">${subHtml}<form method="POST" action="/catalog/categories/${c.id}/subcategories" class="inline-form tree-add"><input name="name" placeholder="+ Subcategory" required size="12"><button class="btn-sm">Add</button></form></div></div>`;
catHtml += `<div class="tree-node"><div class="tree-toggle"><span class="arrow" data-toggle="1">▶</span> <span class="node-label" data-cat="${c.id}">${esc(c.name)}</span><span class="tree-count">(${catCount})</span><span class="tree-actions"><form method="POST" action="/catalog/categories/${c.id}/delete" onsubmit="return confirm('Delete?')"><button class="btn-sm btn-danger">✕</button></form></span></div><div class="tree-children">${subHtml}<form method="POST" action="/catalog/categories/${c.id}/subcategories" class="inline-form tree-add"><input name="name" placeholder="+ Subcategory" required size="12"><button class="btn-sm">Add</button></form></div></div>`;
}
districtHtml += `<div class="tree-node"><div class="tree-toggle" data-loc="${ldata.id}"><span class="arrow">▶</span> ${esc(district)}<span class="tree-count">(${districtCount})</span><span class="tree-actions"><form method="POST" action="/catalog/locations/${ldata.id}/delete" onsubmit="return confirm('Delete?')"><button class="btn-sm btn-danger">✕</button></form></span></div><div class="tree-children">${catHtml}<form method="POST" action="/catalog/categories" class="inline-form tree-add"><input type="hidden" name="location_id" value="${ldata.id}"><input name="name" placeholder="+ Category" required size="12"><button class="btn-sm">Add</button></form></div></div>`;
districtHtml += `<div class="tree-node"><div class="tree-toggle"><span class="arrow" data-toggle="1">▶</span> <span class="node-label" data-loc="${ldata.id}">${esc(district)}</span><span class="tree-count">(${districtCount})</span><span class="tree-actions"><form method="POST" action="/catalog/locations/${ldata.id}/delete" onsubmit="return confirm('Delete?')"><button class="btn-sm btn-danger">✕</button></form></span></div><div class="tree-children">${catHtml}<form method="POST" action="/catalog/categories" class="inline-form tree-add"><input type="hidden" name="location_id" value="${ldata.id}"><input name="name" placeholder="+ Category" required size="12"><button class="btn-sm">Add</button></form></div></div>`;
cityCount += districtCount;
}
cityHtml += `<div class="tree-node"><div class="tree-toggle" data-city="${city}" data-country="${esc(country)}"><span class="arrow">▶</span> ${esc(city)}<span class="tree-count">(${cityCount})</span><span class="tree-actions"><form method="POST" action="/catalog/locations/add-district" class="inline-form tree-add"><input type="hidden" name="country" value="${esc(country)}"><input type="hidden" name="city" value="${esc(city)}"><input name="district" placeholder="+ District" required size="12"><button class="btn-sm">Add</button></form></span></div><div class="tree-children">${districtHtml}</div></div>`;
cityHtml += `<div class="tree-node"><div class="tree-toggle"><span class="arrow" data-toggle="1">▶</span> <span class="node-label" data-city="${city}" data-country="${esc(country)}">${esc(city)}</span><span class="tree-count">(${cityCount})</span><span class="tree-actions"><form method="POST" action="/catalog/locations/add-district" class="inline-form tree-add"><input type="hidden" name="country" value="${esc(country)}"><input type="hidden" name="city" value="${esc(city)}"><input name="district" placeholder="+ District" required size="12"><button class="btn-sm">Add</button></form></span></div><div class="tree-children">${districtHtml}</div></div>`;
countryCount += cityCount;
}
treeHtml += `<div class="tree-node"><div class="tree-toggle" data-country="${esc(country)}"><span class="arrow">▶</span> <strong>${esc(country)}</strong><span class="tree-count">(${countryCount})</span><span class="tree-actions"><form method="POST" action="/catalog/locations/add-city" class="inline-form tree-add"><input type="hidden" name="country" value="${esc(country)}"><input name="city" placeholder="+ City" required size="10"><button class="btn-sm">Add</button></form></span></div><div class="tree-children">${cityHtml}</div></div>`;
treeHtml += `<div class="tree-node"><div class="tree-toggle"><span class="arrow" data-toggle="1">▶</span> <span class="node-label" data-country="${esc(country)}"><strong>${esc(country)}</strong></span><span class="tree-count">(${countryCount})</span><span class="tree-actions"><form method="POST" action="/catalog/locations/add-city" class="inline-form tree-add"><input type="hidden" name="country" value="${esc(country)}"><input name="city" placeholder="+ City" required size="10"><button class="btn-sm">Add</button></form></span></div><div class="tree-children">${cityHtml}</div></div>`;
}
let tableHtml = '<p class="muted">No products found.</p>';
@@ -181,12 +181,17 @@ async function addCategoryInline() {
function escHtml(s) { const d = document.createElement('div'); d.textContent = s; return d.innerHTML; }
document.querySelectorAll('.tree-toggle').forEach(el=>{el.addEventListener('click',()=>{
const ch=el.nextElementSibling; if(!ch||!ch.classList.contains('tree-children')) return;
ch.classList.toggle('open'); el.querySelector('.arrow').classList.toggle('open');
document.querySelectorAll('.tree-toggle .arrow').forEach(el=>{el.addEventListener('click',e=>{
e.stopPropagation();
const toggle=el.closest('.tree-toggle');
const ch=toggle.nextElementSibling; if(!ch||!ch.classList.contains('tree-children')) return;
ch.classList.toggle('open'); el.classList.toggle('open');
})});
document.querySelectorAll('.tree-toggle .node-label').forEach(el=>{el.addEventListener('click',e=>{
e.stopPropagation();
const loc=el.dataset.loc, cat=el.dataset.cat, sub=el.dataset.sub, all=el.dataset.all;
if(loc||cat||sub||all){ let u='/catalog?'; if(loc) u+='loc='+loc; if(cat) u+='cat='+cat; if(sub) u+='sub='+sub; location.href=u; }
})});
});
function openAdd(){ document.getElementById('modal-body').innerHTML=addFormTpl; initLocationSelects(null); document.getElementById('product-modal').style.display='flex'; }
async function openEdit(id){ const r=await fetch('/catalog/products/'+id+'/json'); const p=await r.json();
document.getElementById('modal-body').innerHTML=editFormTpl.replace('/__ID__/','/'+p.id+'/'); fillEditForm(p); document.getElementById('product-modal').style.display='flex'; }