Product Store
Search, filter, view details in a new tab — data loaded from API.
Cart:
0
Home
Search
1
Done
'
+ ' '
+ '
');
doc.close();
const titleEl = doc.getElementById("pdTitle");
const nameEl = doc.getElementById("pdName");
const metaEl = doc.getElementById("pdMeta");
const priceEl = doc.getElementById("pdPrice");
const strikeEl= doc.getElementById("pdStrike");
const saveEl = doc.getElementById("pdSave");
const descEl = doc.getElementById("pdDesc");
const mainImg = doc.getElementById("pdMain");
const thumbs = doc.getElementById("pdThumbs");
const noteEl = doc.getElementById("pdNote");
const btnClose= doc.getElementById("pdClose");
const btnRefresh= doc.getElementById("pdRefresh");
const detailsGrid = doc.getElementById("pdDetailsGrid");
btnClose.addEventListener("click", () => w.close());
function pill(text, tid){
const s = doc.createElement("span");
s.className = "pill";
s.textContent = text;
if(tid){ s.setAttribute("data-testid", tid); s.setAttribute("data-qa", tid); }
return s;
}
function addDetail(label, value, idx){
if(value === undefined || value === null || value === "") return;
const item = doc.createElement("div");
item.className = "detail";
item.setAttribute("data-testid", "pd-detail-" + idx);
item.setAttribute("data-qa", "pd-detail-" + idx);
const k = doc.createElement("div");
k.className = "k";
k.textContent = label;
const v = doc.createElement("div");
v.className = "v";
v.textContent = String(value);
item.appendChild(k);
item.appendChild(v);
detailsGrid.appendChild(item);
}
function moneyLocal(n){
return "\u20B9 " + Number(n || 0).toLocaleString("en-IN");
}
function setSelected(btn){
Array.from(thumbs.children).forEach(x => x.setAttribute("aria-selected","false"));
btn.setAttribute("aria-selected","true");
}
function renderDetails(p){
const title = String(p.title || ("Product " + id));
w.document.title = title + " - Details";
titleEl.textContent = title;
nameEl.textContent = title;
metaEl.innerHTML = "";
if(p.brand) metaEl.appendChild(pill("Brand: " + p.brand, "pd-pill-brand"));
if(p.category) metaEl.appendChild(pill("Category: " + p.category, "pd-pill-category"));
metaEl.appendChild(pill("ID: " + id, "pd-pill-id"));
const price = Number(p.price || 0);
const disc = (p.discountPercentage != null) ? Number(p.discountPercentage) : null;
priceEl.textContent = moneyLocal(price);
strikeEl.style.display = "none";
saveEl.style.display = "none";
if(disc !== null && !Number.isNaN(disc) && disc > 0){
const original = price / (1 - (disc/100));
strikeEl.textContent = moneyLocal(original.toFixed(0));
strikeEl.style.display = "block";
saveEl.textContent = "Save " + disc.toFixed(2) + "%";
saveEl.style.display = "block";
metaEl.appendChild(pill("Discount: " + disc.toFixed(2) + "%", "pd-pill-discount"));
}
descEl.textContent = p.description || "No description available.";
detailsGrid.innerHTML = "";
let i = 0;
addDetail("Price", moneyLocal(price), i++);
if(disc !== null && !Number.isNaN(disc)) addDetail("Discount", disc.toFixed(2) + "%", i++);
if(p.rating != null) addDetail("Rating", p.rating, i++);
if(p.stock != null) addDetail("Stock", p.stock, i++);
// Optional fields (only show if present)
addDetail("SKU", p.sku, i++);
addDetail("Availability", p.availabilityStatus, i++);
addDetail("Warranty", p.warrantyInformation, i++);
addDetail("Shipping", p.shippingInformation, i++);
addDetail("Return Policy", p.returnPolicy, i++);
addDetail("Min Order Qty", p.minimumOrderQuantity, i++);
const images = Array.isArray(p.images) ? p.images.filter(Boolean) : [];
const primary = p.thumbnail || images[0] || "";
mainImg.src = primary || "";
mainImg.alt = title;
thumbs.innerHTML = "";
const all = [];
if(primary) all.push(primary);
images.forEach(u => { if(u && !all.includes(u)) all.push(u); });
all.forEach((u, idx) => {
const b = doc.createElement("button");
b.type = "button";
b.className = "thumb";
b.setAttribute("aria-selected", idx === 0 ? "true" : "false");
b.setAttribute("data-testid", "pd-thumb-" + idx);
b.setAttribute("data-qa", "pd-thumb-" + idx);
const img = doc.createElement("img");
img.src = u;
img.alt = "thumb " + (idx + 1);
b.appendChild(img);
b.addEventListener("click", () => {
mainImg.src = u;
setSelected(b);
});
thumbs.appendChild(b);
});
// Unicode escape avoids ✅ becoming ✅
noteEl.textContent = "Loaded from API \u2705";
}
async function loadDetails(){
noteEl.textContent = "Loading product from API...";
try{
const res = await fetch(API_SINGLE(id), { headers: {"Accept":"application/json"} });
if(!res.ok) throw new Error("HTTP " + res.status);
const data = await res.json();
renderDetails(data);
} catch (e){
console.warn("Details load failed:", e);
noteEl.textContent = "Failed to load from API. Click Refresh.";
titleEl.textContent = "Product " + id;
nameEl.textContent = "Product " + id;
}
}
btnRefresh.addEventListener("click", loadDetails);
loadDetails();
}
/* ============================================================
8) EVENTS (delegation + pagination)
============================================================ */
root.addEventListener("click", function(e){
const chipBtn = e.target.closest && e.target.closest("[data-action='remove-chip']");
if (chipBtn){
const chip = chipBtn.closest(".chip");
const key = chip && chip.getAttribute("data-chip-key");
if (key){
if (key === "search") els.search.value = "";
else if (key === "min") els.minPrice.value = "";
else if (key === "max") els.maxPrice.value = "";
else if (key.startsWith("cat:")){
const v = key.split(":")[1];
const idMap = {fashion:"catFashion", electronics:"catElectronics", household:"catHousehold"};
const cb = root.querySelector("#" + idMap[v]);
if (cb) cb.checked = false;
} else if (key.startsWith("sub:")){
const v = key.split(":")[1];
const idMap = {"t-shirts":"subTshirts", shirts:"subShirts", shoes:"subShoes", mobiles:"subMobiles", laptops:"subLaptops"};
const cb = root.querySelector("#" + idMap[v]);
if (cb) cb.checked = false;
} else if (key.startsWith("aud:")){
const v = key.split(":")[1];
const idMap = {men:"audMen", women:"audWomen"};
const cb = root.querySelector("#" + idMap[v]);
if (cb) cb.checked = false;
}
state.page = 1;
renderGrid();
toast("Filter removed");
}
return;
}
const actionBtn = e.target.closest && e.target.closest("[data-action]");
if (actionBtn){
const action = actionBtn.getAttribute("data-action");
const pid = actionBtn.getAttribute("data-product-id");
if (action === "add"){
state.cart += 1;
els.cartCount.textContent = String(state.cart);
toast("Added to cart");
} else if (action === "view"){
openProductDetails(pid);
}
}
}, {passive:true});
["input","change"].forEach(evt => {
root.addEventListener(evt, function(e){
const id = e.target && e.target.id;
if (!id) return;
const watch = new Set([
"searchInput","minPrice","maxPrice",
"catFashion","catElectronics","catHousehold",
"subTshirts","subShirts","subShoes","subMobiles","subLaptops",
"audMen","audWomen"
]);
if (watch.has(id)){
state.page = 1;
renderGrid();
}
}, {passive:true});
});
els.btnPrev.addEventListener("click", function(){
state.page = Math.max(1, state.page - 1);
renderGrid();
}, {passive:true});
els.btnNext.addEventListener("click", function(){
state.page = state.page + 1;
renderGrid();
}, {passive:true});
els.reset.addEventListener("click", resetAll, {passive:true});
/* ============================================================
9) INIT
============================================================ */
(async function init(){
await loadProducts();
renderGrid();
})();
})();
DETAILS
'
+ ' Loading...
'
+ '
'
+ '
'
+ ' '
+ '
'
+ ' '
+ '
'
+ ' Loading...
' + ' ' + ''
+ ' '
+ ' '
+ ' '
+ '
'
+ ' '
+ ' '
+ '
'
+ ' More Details
'
+ ' '
+ ' '
+ ' '
+ ' '
+ '
'
+ ' Loading product from API...
'
+ '