-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
const btn = document.createElement('button'); btn.textContent='â'; btn.style.position='absolute'; btn.style.right='-6px'; btn.style.top='-6px'; btn.title='Remove';
btn.addEventListener('click', ()=>{ stagedImages.splice(i,1); renderPreviews(); });
wrapper.appendChild(btn);
container.appendChild(wrapper);
});
}
// Utilities: image resize via canvas -> dataURL
function fileToDataUrlResize(file, maxDim=1200, quality=0.8){
return new Promise((resolve,reject)=>{
const img = new Image();
const reader = new FileReader();
reader.onload = ()=>{
img.onload = ()=>{
const [w,h] = [img.width,img.height];
let nw=w, nh=h;
if(Math.max(w,h)>maxDim){
if(w>=h){ nw = maxDim; nh = Math.round(h * (maxDim/w)); }
else { nh = maxDim; nw = Math.round(w * (maxDim/h)); }
}
const canvas = document.createElement('canvas'); canvas.width = nw; canvas.height = nh;
const ctx = canvas.getContext('2d'); ctx.drawImage(img,0,0,nw,nh);
const mime = 'image/jpeg';
try{
const dataUrl = canvas.toDataURL(mime, quality);
resolve(dataUrl);
}catch(err){ reject(err); }
};
img.onerror = (e)=>reject(e);
img.src = reader.result;
};
reader.onerror = (e)=>reject(e);
reader.readAsDataURL(file);
});
}
// Persistence
function loadReviews(){
try{ const raw = localStorage.getItem(LS_KEY); return raw? JSON.parse(raw): []; }catch(e){ return []; }
}
function saveReviews(list){ localStorage.setItem(LS_KEY, JSON.stringify(list.slice(0,200))); }
// Render reviews list
function sanitizeText(str){ const div = document.createElement('div'); div.textContent = str; return div.innerHTML; }
function renderReviews(list){ const container = $('reviewsList'); container.innerHTML=''; if(list.length===0){ container.innerHTML = '
No reviews yet â be the first to write one!
'; return; }
list.forEach(r=>{
const card = document.createElement('div'); card.className='card';
const meta = document.createElement('div'); meta.className='meta';
const header = document.createElement('div'); header.style.display='flex'; header.style.alignItems='center'; header.style.gap='10px';
const name = document.createElement('strong'); name.textContent = r.name;
const when = document.createElement('div'); when.className='small'; when.style.marginLeft='8px'; when.textContent = timeAgo(new Date(r.createdAt));
const stars = document.createElement('div'); stars.className='stars'; stars.style.marginLeft='auto';
for(let i=1;i<=5;i++){ const s = document.createElement('span'); s.textContent='â
'; s.className='star'+(i<=r.rating?' selected':''); stars.appendChild(s); }
header.appendChild(name); header.appendChild(when); header.appendChild(stars);
meta.appendChild(header);
const text = document.createElement('div'); text.className='review-text'; text.innerHTML = sanitizeText(r.text).replace(/\n/g,'
');
meta.appendChild(text);
card.appendChild(meta);
if(r.images && r.images.length){
const imgs = document.createElement('div'); imgs.className='images';
r.images.forEach(src=>{
const im = document.createElement('img'); im.src = src; im.className='thumb'; im.loading='lazy'; imgs.appendChild(im);
});
card.appendChild(imgs);
}
container.appendChild(card);
});
}
function timeAgo(d){ const s = Math.floor((Date.now()-d.getTime())/1000); if(s<60) return 'just now'; if(s<3600) return Math.floor(s/60)+'m'; if(s<86400) return Math.floor(s/3600)+'h'; if(s<2592000) return Math.floor(s/86400)+'d'; return d.toLocaleDateString(); }
})();