|
|
|
|
@@ -1,13 +1,14 @@
|
|
|
|
|
import { layout, flash } from './layout.js';
|
|
|
|
|
import { renderProductEditForm } from './catalogProduct.js';
|
|
|
|
|
|
|
|
|
|
export function renderCatalog(tree, products, filter, categories, subcategories, msg, msgType) {
|
|
|
|
|
export function renderCatalog(tree, products, filter, categories, subcategories, locations, msg, msgType) {
|
|
|
|
|
const { loc, cat, sub } = filter;
|
|
|
|
|
const catOptions = categories.map(c => `<option value="${c.id}">${esc(c.name)}</option>`).join('');
|
|
|
|
|
const subcatJson = JSON.stringify(subcategories.map(s => ({ id: s.id, name: s.name, category_id: s.category_id })));
|
|
|
|
|
const addFormHtml = renderProductEditForm('/catalog/products', catOptions, subcatJson)
|
|
|
|
|
const locJson = JSON.stringify(locations || []);
|
|
|
|
|
const addFormHtml = renderProductEditForm('/catalog/products', catOptions, subcatJson, locations)
|
|
|
|
|
.replace(/`/g, '\\`').replace(/\$/g, '\\$');
|
|
|
|
|
const editFormHtml = renderProductEditForm('/catalog/products/__ID__/edit', catOptions, subcatJson)
|
|
|
|
|
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>';
|
|
|
|
|
@@ -67,15 +68,93 @@ export function renderCatalog(tree, products, filter, categories, subcategories,
|
|
|
|
|
<div id="product-modal" class="modal" style="display:none"><div class="modal-content" id="modal-body"></div></div>
|
|
|
|
|
<script>
|
|
|
|
|
const subcats = ${subcatJson};
|
|
|
|
|
const allLocations = ${locJson};
|
|
|
|
|
const addFormTpl = \`${addFormHtml}\`;
|
|
|
|
|
const editFormTpl = \`${editFormHtml}\`;
|
|
|
|
|
|
|
|
|
|
function initLocationSelects(selectedLocId) {
|
|
|
|
|
const cs = document.getElementById('loc-country');
|
|
|
|
|
const ci = document.getElementById('loc-city');
|
|
|
|
|
const di = document.getElementById('loc-district');
|
|
|
|
|
if (!cs) return;
|
|
|
|
|
|
|
|
|
|
const countries = [...new Set(allLocations.map(l => l.country))].sort();
|
|
|
|
|
cs.innerHTML = '<option value="">-- Country --</option>' + countries.map(c => '<option value="'+escHtml(c)+'">'+escHtml(c)+'</option>').join('');
|
|
|
|
|
ci.innerHTML = '<option value="">-- City --</option>'; ci.disabled = true;
|
|
|
|
|
di.innerHTML = '<option value="">-- District --</option>';
|
|
|
|
|
|
|
|
|
|
if (selectedLocId) {
|
|
|
|
|
const sel = allLocations.find(l => l.id == selectedLocId);
|
|
|
|
|
if (sel) {
|
|
|
|
|
cs.value = sel.country;
|
|
|
|
|
locOnCountryChange();
|
|
|
|
|
ci.value = sel.city;
|
|
|
|
|
locOnCityChange();
|
|
|
|
|
di.value = sel.id;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function locOnCountryChange() {
|
|
|
|
|
const cs = document.getElementById('loc-country');
|
|
|
|
|
const ci = document.getElementById('loc-city');
|
|
|
|
|
const di = document.getElementById('loc-district');
|
|
|
|
|
const country = cs.value;
|
|
|
|
|
ci.innerHTML = '<option value="">-- City --</option>';
|
|
|
|
|
di.innerHTML = '<option value="">-- District --</option>';
|
|
|
|
|
if (!country) { ci.disabled = true; return; }
|
|
|
|
|
ci.disabled = false;
|
|
|
|
|
const cities = [...new Set(allLocations.filter(l => l.country === country).map(l => l.city))].sort();
|
|
|
|
|
ci.innerHTML = '<option value="">-- City --</option>' + cities.map(c => '<option value="'+escHtml(c)+'">'+escHtml(c)+'</option>').join('');
|
|
|
|
|
filterCategoriesByLocation();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function locOnCityChange() {
|
|
|
|
|
const cs = document.getElementById('loc-country');
|
|
|
|
|
const ci = document.getElementById('loc-city');
|
|
|
|
|
const di = document.getElementById('loc-district');
|
|
|
|
|
const country = cs.value;
|
|
|
|
|
const city = ci.value;
|
|
|
|
|
di.innerHTML = '<option value="">-- District --</option>';
|
|
|
|
|
if (!city) { filterCategoriesByLocation(); return; }
|
|
|
|
|
const locs = allLocations.filter(l => l.country === country && l.city === city);
|
|
|
|
|
di.innerHTML = '<option value="">-- District --</option>' + locs.map(l => '<option value="'+l.id+'">'+escHtml(l.district || l.city)+'</option>').join('');
|
|
|
|
|
filterCategoriesByLocation();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function locOnDistrictChange() {
|
|
|
|
|
filterCategoriesByLocation();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function filterCategoriesByLocation() {
|
|
|
|
|
const di = document.getElementById('loc-district');
|
|
|
|
|
const catSel = document.getElementById('pf-category');
|
|
|
|
|
if (!catSel) return;
|
|
|
|
|
const locId = di ? di.value : '';
|
|
|
|
|
const allCats = catSel.querySelectorAll('option');
|
|
|
|
|
if (!locId) {
|
|
|
|
|
allCats.forEach(o => o.style.display = '');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const loc = allLocations.find(l => l.id == locId);
|
|
|
|
|
allCats.forEach(o => {
|
|
|
|
|
if (!o.value) { o.style.display = ''; return; }
|
|
|
|
|
const cat = ${JSON.stringify(categories)}.find(c => c.id == o.value);
|
|
|
|
|
o.style.display = (cat && cat.location_id == locId) ? '' : 'none';
|
|
|
|
|
});
|
|
|
|
|
catSel.value = '';
|
|
|
|
|
updateSubcats('');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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');
|
|
|
|
|
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; const loc=document.getElementById('pf-location'); if(loc)loc.style.display='none'; document.getElementById('product-modal').style.display='flex'; }
|
|
|
|
|
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'; }
|
|
|
|
|
function fillEditForm(p){ const f=document.getElementById('product-modal').querySelector('form'); if(!f)return;
|
|
|
|
|
@@ -83,13 +162,9 @@ function fillEditForm(p){ const f=document.getElementById('product-modal').query
|
|
|
|
|
f.querySelector('[name=quantity_in_stock]').value=p.quantity_in_stock||''; f.querySelector('[name=description]').value=p.description||'';
|
|
|
|
|
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);
|
|
|
|
|
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';}
|
|
|
|
|
f.querySelector('[name=private_data]').value=p.private_data||'';
|
|
|
|
|
if(p.category_id) { f.querySelector('[name=category_id]').value=p.category_id; updateSubcats(p.category_id, p.subcategory_id); }
|
|
|
|
|
initLocationSelects(p.location_id);
|
|
|
|
|
}
|
|
|
|
|
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)}}); }
|
|
|
|
|
@@ -99,4 +174,4 @@ document.addEventListener('change',e=>{if(e.target.name==='category_id'&&e.targe
|
|
|
|
|
return layout('Catalog', content, 'catalog');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function esc(str) { return String(str||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); }
|
|
|
|
|
function esc(str) { return String(str||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); }
|