ULK ADMIN PORTAL

Manage ULK Website Content

Manage gallery photos, services and the chainsaw catalogue from one private portal.

Checking access...
+(p.price_cents/100).toFixed(2)+' · '+esc(p.category)+'

').join('')||'

No chainsaws yet.

';host.querySelectorAll('[data-e]').forEach(b=>b.onclick=()=>{let p=list.find(x=>x.id===decodeURIComponent(b.dataset.e));if(!p)return;editing=p.id;imageKey=p.image_key;$('editName').value=p.name;$('editDesc').value=p.description;$('editPrice').value=(p.price_cents/100).toFixed(2);$('editCategory').value=p.category;$('editStock').checked=Boolean(p.in_stock);$('editPhoto').value='';$('editorHeading').textContent='Edit Chainsaw';$('productForm').scrollIntoView({behavior:'smooth',block:'center'});msg('editMsg','Editing '+p.name)});host.querySelectorAll('[data-d]').forEach(b=>b.onclick=async()=>{let p=list.find(x=>x.id===decodeURIComponent(b.dataset.d));if(!p||!confirm('Remove '+p.name+' from the catalogue?'))return;try{await api('/api/admin/products?id='+encodeURIComponent(p.id),{method:'DELETE'});reset();await load()}catch(e){alert(e.message)}})}async function startup(){try{let d=await api('/api/admin/status');show(d.authenticated?'adminDashboard':d.setup_required?'adminSetup':'adminLogin');if(d.authenticated)await load()}catch{show('adminLogin');msg('loginMsg','Cannot reach server. Refresh and try again.',true)}}$('setupForm').addEventListener('submit',async e=>{e.preventDefault();if($('setupPassword').value!==$('setupConfirm').value){msg('setupMsg','Passwords do not match.',true);return}try{await api('/api/admin/setup',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({email:$('setupEmail').value.trim().toLowerCase(),code:$('setupCode').value,password:$('setupPassword').value})});$('setupForm').reset();show('adminDashboard');await load()}catch(err){msg('setupMsg',err.message,true)}});$('loginForm').addEventListener('submit',async e=>{e.preventDefault();try{await api('/api/admin/login',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({username:$('loginUser').value,password:$('loginPassword').value})});$('loginForm').reset();show('adminDashboard');await load()}catch(err){msg('loginMsg',err.message,true)}});$('adminLogout').addEventListener('click',async()=>{try{await api('/api/admin/logout',{method:'POST',headers:{'content-type':'application/json'},body:'{}'})}catch{}show('adminLogin')});$('cancelEdit').addEventListener('click',reset);$('productForm').addEventListener('submit',async e=>{e.preventDefault();const button=$('saveProduct');button.disabled=true;button.textContent='Saving...';try{let key=imageKey;let f=$('editPhoto').files[0];if(f){f=await shrink(f);const fd=new FormData();fd.set('photo',f,'chainsaw.jpg');let image=await api('/api/admin/upload',{method:'POST',body:fd});key=image.key}let price=Math.round(Number($('editPrice').value)*100);const data={id:editing,name:$('editName').value.trim(),description:$('editDesc').value.trim(),category:$('editCategory').value,price_cents:price,image_key:key,in_stock:$('editStock').checked?1:0};await api('/api/admin/products',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify(data)});reset();msg('editMsg','Product saved successfully.');await load()}catch(err){msg('editMsg',err.message,true)}finally{button.disabled=false;button.textContent='Save Product'}});$('passwordForm').addEventListener('submit',async e=>{e.preventDefault();try{await api('/api/admin/password',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({old_password:$('oldPassword').value,new_password:$('newPassword').value})});$('passwordForm').reset();show('adminLogin');msg('loginMsg','Password changed. Sign in with your new password.')}catch(err){msg('passwordMsg',err.message,true)}});startup()})();