fix: show location (country, city, district) in product edit modal

- JSON endpoint now joins locations+categories+subcategories for full info
- Edit form shows location tags (country, city, district) at top
- Location hidden when adding new product (no location yet)
- All fields properly filled by fillEditForm on edit
- Category and subcategory side by side in edit form
This commit is contained in:
NW
2026-06-23 21:48:53 +01:00
parent e00071b18a
commit 293236921c
4 changed files with 60 additions and 17 deletions

View File

@@ -438,6 +438,26 @@ pre { font-size: 0.8rem; white-space: pre-wrap; word-break: break-all; max-width
border-top: 1px solid var(--border);
}
.pf-location-row {
display: flex;
gap: 0.4rem;
flex-wrap: wrap;
}
.pf-loc-tag {
display: inline-block;
padding: 0.2rem 0.5rem;
background: #e0e7ff;
color: #3730a3;
border-radius: 999px;
font-size: 0.8rem;
font-weight: 600;
}
.pf-location {
margin-bottom: 0.25rem;
}
.form-row {
display: flex;
gap: 0.5rem;

View File

@@ -48,7 +48,16 @@ router.post('/products/:id/delete', async (req, res) => {
});
router.get('/products/:id/json', async (req, res) => {
const p = await db.getAsync('SELECT * FROM products WHERE id=?', [req.params.id]);
const p = await db.getAsync(
`SELECT p.*, l.country, l.city, l.district, c.name as category_name,
COALESCE(sc.name, '') as subcategory_name
FROM products p
LEFT JOIN locations l ON p.location_id = l.id
LEFT JOIN categories c ON p.category_id = c.id
LEFT JOIN subcategories sc ON p.subcategory_id = sc.id
WHERE p.id = ?`,
[req.params.id]
);
if (!p) return res.status(404).json({ error: 'Not found' });
res.json(p);
});

View File

@@ -75,7 +75,7 @@ document.querySelectorAll('.tree-toggle').forEach(el=>{el.addEventListener('clic
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; document.getElementById('product-modal').style.display='flex'; }
function openAdd(){ document.getElementById('modal-body').innerHTML=addFormTpl; const loc=document.getElementById('pf-location'); if(loc)loc.style.display='none'; 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'; }
function fillEditForm(p){ const f=document.getElementById('product-modal').querySelector('form'); if(!f)return;
@@ -84,7 +84,13 @@ function fillEditForm(p){ const f=document.getElementById('product-modal').query
f.querySelector('[name=photo_url]').value=p.photo_url||''; f.querySelector('[name=hidden_photo_url]').value=p.hidden_photo_url||'';
f.querySelector('[name=hidden_coordinates]').value=p.hidden_coordinates||''; f.querySelector('[name=hidden_description]').value=p.hidden_description||'';
f.querySelector('[name=private_data]').value=p.private_data||''; f.querySelector('[name=category_id]').value=p.category_id||'';
updateSubcats(p.category_id,p.subcategory_id); }
updateSubcats(p.category_id,p.subcategory_id);
const loc=document.getElementById('pf-location');
if(loc&&(p.country||p.city||p.district)){
loc.style.display=''; document.getElementById('pf-country').textContent=p.country||'';
document.getElementById('pf-city').textContent=p.city||''; document.getElementById('pf-district').textContent=p.district||'';
} else if(loc){loc.style.display='none';}
}
function updateSubcats(catId,selSub){ const ss=document.getElementById('product-modal').querySelector('[name=subcategory_id]'); if(!ss)return;
ss.innerHTML='<option value="">-- Subcategory --</option>'; subcats.forEach(s=>{if(s.category_id==catId){const o=document.createElement('option');o.value=s.id;o.textContent=s.name;if(s.id==selSub)o.selected=true;ss.appendChild(o)}}); }
document.getElementById('product-modal').addEventListener('click',e=>{if(e.target===document.getElementById('product-modal'))document.getElementById('product-modal').style.display='none'});

View File

@@ -3,6 +3,14 @@ export function renderProductEditForm(action, catOptions, subcatJson) {
const title = isEdit ? 'Edit Product' : 'Add Product';
return `<h2>${title}</h2>
<form method="POST" action="${action}" enctype="multipart/form-data" class="product-form">
<div class="pf-group pf-location" id="pf-location" style="display:none">
<label>Location</label>
<div class="pf-location-row">
<span class="pf-loc-tag" id="pf-country"></span>
<span class="pf-loc-tag" id="pf-city"></span>
<span class="pf-loc-tag" id="pf-district"></span>
</div>
</div>
<div class="pf-group">
<label>Name</label>
<input name="name" required placeholder="Product name">
@@ -17,15 +25,17 @@ export function renderProductEditForm(action, catOptions, subcatJson) {
<input name="quantity_in_stock" type="number" min="0" value="0" placeholder="0">
</div>
</div>
<div class="pf-group">
<label>Category</label>
<select name="category_id" required onchange="updateSubcats(this.value)">
<option value="">-- Select --</option>${catOptions}
</select>
</div>
<div class="pf-group">
<label>Subcategory</label>
<select name="subcategory_id"><option value="">-- Subcategory --</option></select>
<div class="pf-row">
<div class="pf-group">
<label>Category</label>
<select name="category_id" required onchange="updateSubcats(this.value)">
<option value="">-- Select --</option>${catOptions}
</select>
</div>
<div class="pf-group">
<label>Subcategory</label>
<select name="subcategory_id"><option value="">-- Subcategory --</option></select>
</div>
</div>
<div class="pf-group">
<label>Description</label>
@@ -49,11 +59,9 @@ export function renderProductEditForm(action, catOptions, subcatJson) {
<label>Or Upload Hidden Photo</label>
<input type="file" name="hidden_photo_file" accept="image/*" class="pf-file">
</div>
<div class="pf-row">
<div class="pf-group">
<label>Hidden Coordinates</label>
<input name="hidden_coordinates" placeholder="lat,lng">
</div>
<div class="pf-group">
<label>Hidden Coordinates</label>
<input name="hidden_coordinates" placeholder="lat,lng">
</div>
<div class="pf-group">
<label>Hidden Description</label>