-1;
return ''
+ '
'
+ (p.badge ? '
' + p.badge + '
' : '')
+ '
'
+ '
' + p.icon + ''
+ '
'
+ '
'
+ '
' + p.brand + '
'
+ '
' + p.name + '
'
+ '
' + stars + '(' + p.reviews.toLocaleString('en-IN') + ')
'
+ '
'
+ '₹' + p.price.toLocaleString('en-IN') + ''
+ '₹' + p.orig.toLocaleString('en-IN') + ''
+ '' + disc + '% off'
+ '
'
+ (p.eco ? '
♻ Recrafted · Rural Jobs
' : '')
+ '
'
+ ''
+ ''
+ '
'
+ '
'
+ '
';
}
function renderGrid(id, arr) {
var el = document.getElementById(id);
if (!el) return;
el.innerHTML = arr.map(makeCard).join('');
}
// ════════════════════════════════════════════════════════════════
// HOME CONTENT
// ════════════════════════════════════════════════════════════════
var allProducts = Object.values(PRODUCTS);
function renderHomeContent() {
ss('heroBadgeEl',T.heroBadge,true); ss('heroH1El',T.heroH1,true); ss('heroSubEl',T.heroSub,true);
ss('rbTitleEl',T.rbTitle,false); ss('rbDescEl',T.rbDesc,false);
ss('hcta1El',T.hcta1,true); ss('hcta2El',T.hcta2,true);
ss('hs2El',T.hs2,false); ss('hs3El',T.hs3,false);
ss('hcBadgeEl',T.hcBadge,false); ss('hcTitleEl',T.hcTitle,false);
ss('hcDescEl',T.hcDesc,false); ss('hcPriceEl',T.hcPrice,true);
ss('flashPillEl',T.flashPill,true); ss('timerLblEl',T.timerEnd,false); ss('tlhEl',T.tlh,false);
ss('catTitleEl',T.catTitle,true);
var cg = document.getElementById('catGridEl');
if (cg) {
cg.innerHTML = T.cats.map(function(c) {
return ''
+ '
' + c.i + '
'
+ '
' + c.n + '
'
+ '
' + c.c + '
'
+ '
';
}).join('');
}
ss('b1tEl',T.b1t,false); ss('b1sEl',T.b1s,true); ss('b3tEl',T.b3t,false);
ss('trendTitleEl',T.trendTitle,true); ss('ecoHEl',T.ecoH,false); ss('ecoPEl',T.ecoP,false);
ss('handTitleEl',T.handTitle,true); ss('elecTitleEl',T.elecTitle,true); ss('trustTitleEl',T.trustTitle,true);
ss('t1hEl',T.t1h,false); ss('t1pEl',T.t1p,false); ss('t2hEl',T.t2h,false); ss('t2pEl',T.t2p,false);
ss('t3hEl',T.t3h,false); ss('t3pEl',T.t3p,false); ss('t4hEl',T.t4h,false); ss('t4pEl',T.t4p,false);
ss('fashTitleEl',T.fashTitle,true);
ss('selBadgeEl',T.selBadge,false); ss('selTitleEl',T.selTitle,false); ss('selSubEl',T.selSub,true); ss('selBtnEl',T.selBtn,true);
ss('footDescEl',T.footDesc,false); ss('footCopyEl',T.footCopy,true);
renderGrid('trendGridEl', allProducts.sort(function(a,b){return b.reviews-a.reviews;}).slice(0,8));
renderGrid('handmadeScrollEl', allProducts.filter(function(p){return p.cat==='handmade';}));
renderGrid('electronicsScrollEl', allProducts.filter(function(p){return p.cat==='electronics';}));
renderGrid('fashionScrollEl', allProducts.filter(function(p){return p.cat==='fashion';}));
['fc1El','fc2El','fc3El','fc4El'].forEach(function(id,i) {
var col = T['fc'+(i+1)]; var el = document.getElementById(id);
if (!el||!col) return;
el.innerHTML = ''+col.h+'
' + col.l.map(function(l){return ''+l+'';}).join('');
});
}
// ════════════════════════════════════════════════════════════════
// CATEGORIES PAGE
// ════════════════════════════════════════════════════════════════
var catTabActive = 'all';
function renderCategoriesPage(cat) {
if (cat) catTabActive = cat;
ss('catPageTitleEl', T.catPageTitle, false);
ss('catPageBcEl', T.catPageTitle, false);
ss('filterTitleEl', T.filterTitle, false);
ss('ecoChkLblEl', T.ecoChkL, false);
var te = document.getElementById('catTabsEl');
if (te) {
te.innerHTML = T.catTabs.map(function(tab, i) {
var key = T.catTabKeys[i];
return '';
}).join('');
}
renderCatProducts();
}
function renderCatProducts() {
var priceFilter = (document.querySelector('input[name="price"]:checked') || {value:''}).value;
var sortVal = (document.getElementById('sortSelectEl') || {value:''}).value;
var ecoOnly = (document.getElementById('ecoCheckEl') || {checked:false}).checked;
var filtered = allProducts.filter(function(p) {
if (catTabActive && catTabActive !== 'all' && p.cat !== catTabActive) return false;
if (ecoOnly && !p.eco) return false;
if (priceFilter) {
var parts = priceFilter.split('-');
var min = parseInt(parts[0]);
var max = parts[1] === '+' ? Infinity : parseInt(parts[1]);
if (p.price < min || p.price > max) return false;
}
return true;
});
if (sortVal === 'price_asc') filtered.sort(function(a,b){return a.price-b.price;});
else if (sortVal === 'price_desc') filtered.sort(function(a,b){return b.price-a.price;});
else if (sortVal === 'rating') filtered.sort(function(a,b){return b.rating-a.rating;});
else filtered.sort(function(a,b){return b.reviews-a.reviews;});
ss('showingEl', T.showingL, false);
ss('prodCountEl', filtered.length + ' products', false);
renderGrid('catProductsEl', filtered);
}
function doSearch() {
var q = (document.getElementById('searchInpEl') || {value:''}).value.toLowerCase().trim();
if (!q) return;
var results = allProducts.filter(function(p) {
return p.name.toLowerCase().includes(q) || p.brand.toLowerCase().includes(q) || p.cat.toLowerCase().includes(q);
});
showPage('categories');
catTabActive = 'all';
renderCategoriesPage();
renderGrid('catProductsEl', results);
ss('prodCountEl', results.length + ' results for "' + q + '"', false);
}
// ════════════════════════════════════════════════════════════════
// TONE CONTENT
// ════════════════════════════════════════════════════════════════
var V = {
topbar:'🔄 90 Din Easy Return — Kisi bhi condition mein FREE! | 🚀 FREE Delivery above ₹499 | ⚡ Instant Refund | 📧 Team.Support@hubooze.in',
searchPh:'Kuch bhi dhundhein...', locLbl:'Yahan deliver karo',
signIn:'Login', myAcc:'Mera Account', ordLbl:'Orders', retLbl:'& Returns', cartLbl:'Cart',
timerEnd:'Khatam hoga:', tlh:'GHANTE',
addCartL:'Cart Mein Daalo', buyNowL:'Abhi Kharido',
addedMsg:function(n){return '"'+n+'" cart mein add ho gaya! 🛒';},
savedTabLbl:'Save Kiya', wishTabLbl:'Wishlist',
saveForLaterLbl:'Baad ke liye save karo',
savedEmptyP:'Koi item save nahi kiya.',
wishEmptyP:'Wishlist khaali hai. ♡ dabao products pe.',
sumSubL:'Subtotal', sumDiscL:'Discount', sumCouponL:'Coupon discount', sumDelivL:'Delivery',
retGuaranteeText:'90 Din FREE return — kisi bhi condition mein!',
checkoutBtn:'Checkout Karo →', continueBtn:'Shopping Jari Rakho',
suggestTitleL:'Aapko yeh bhi pasand aayega',
heroBadge:'♻ India ki Eco Commerce',
heroH1:'Khareedein Aaram Se.
Return Karen Free Mein.
♻ Recycle Hoga Sab!',
heroSub:'Hubooze par 90 din tak return kar sakte hain — kisi bhi condition mein! Instant refund milega.',
rbTitle:'90 Din Easy Return — Bilkul FREE!', rbDesc:'Na pasand aaye to 90 din mein wapas bhejo. Pickup free, refund instant!',
hcta1:'Abhi Khareedein →', hcta2:'Return Policy Dekho 🔄',
hs2:'Din Return', hs3:'Instant Refund', hcBadge:'NAYA COLLECTION',
hcTitle:'Ethnic Festive Collection', hcDesc:'Rural artisans ke haath se bane', hcPrice:'Sirf ₹299 se',
flashPill:'⚡ FLASH SALE',
catTitle:'Category se Khareedein',
cats:[{i:'😉',n:'Kapde & Fashion',c:'12,000+',cat:'fashion',hl:true},{i:'🏠',n:'Ghar & Rasoi',c:'8,400+',cat:'home'},{i:'🤜',n:'Roz Ka Saman',c:'6,200+',cat:'daily'},{i:'📱',n:'Electronics',c:'5,700+',cat:'electronics'},{i:'🍺',n:'Desi/Handmade',c:'3,100+',cat:'handmade',hl:true},{i:'🏷',n:'Offers',c:'2,800+',cat:''},{i:'👟',n:'Joote',c:'4,300+',cat:'fashion'},{i:'🮸',n:'Bacchon ka Saman',c:'3,600+',cat:'daily'}],
b1t:'Recrafted', b1s:'Return se bana naya product', b3t:'90 Din Return',
trendTitle:'🔥 Trending Products',
ecoH:'Desi Artisans ka Kaam', ecoP:'Yahan har product ek returned item tha.',
handTitle:'🍺 Desi & Handmade',
elecTitle:'📱 Electronics & Gadgets',
trustTitle:'Hubooze pe kyun Bharosa?',
t1h:'90 Din Return', t1p:'90 puri dinon tak return karo.',
t2h:'Instant Refund', t2p:'Return approve hote hi turant paisa wapas.',
t3h:'Free Delivery', t3p:'₹499 se upar delivery bilkul muft.',
t4h:'Return = Recycle', t4p:'Rural artisans se naya product banta hai.',
fashTitle:'😉 Fashion Picks',
selBadge:'Seller Bano', selTitle:'Hubooze pe Bechna Shuru Karo',
selSub:'Apna samaan list karo — bilkul free mein.', selBtn:'Seller Bano →',
catPageTitle:'Sab Categories', filterTitle:'FILTER & SORT',
catTabs:['Sab','Fashion','Electronics','Ghar','Roz Ka Saman','Desi/Handmade'],
catTabKeys:['all','fashion','electronics','home','daily','handmade'],
showingL:'Products dikh rahe hain:', ecoChkL:'Sirf Recrafted Dikhao',
navItems:[{t:'🔥 Aaj ke Deals',c:'hot',pg:''},{t:'🏠 Home',c:'nactive',pg:'home'},{t:'😉 Fashion',c:'',pg:'categories',cat:'fashion'},{t:'📱 Electronics',c:'',pg:'categories',cat:'electronics'},{t:'♻ Desi/Handmade',c:'special',pg:'categories',cat:'handmade'},{t:'🏷 Offers',c:'',pg:'categories',cat:''},{t:'🧑 Seller Bano',c:'',pg:'seller'},{t:'🔄 Return Policy',c:'',pg:'returns'},{t:'⚖ Admin',c:'',pg:'admin',adminOnly:true}],
fc1:{h:'Shopping',l:['Kapde & Fashion','Electronics','Ghar & Rasoi','Roz Ka Saman','Desi/Handmade','Offers']},
fc2:{h:'Customer',l:['Mera Account','Order Track Karo','Return Karo','Wishlist','Help & Support']},
fc3:{h:'Sellers',l:['Seller Bano','Seller Dashboard','Seller Support','Payment Info']},
fc4:{h:'Company',l:['Hamare Baare Mein','Recycle Mission','Rural Jobs','Contact Us']},
footDesc:'India ki pehli return-to-recycle marketplace.',
footCopy:'2025 Hubooze. Sab haq surakshit. ♻ Made with love in India.',
mnHome:'Home', mnCat:'Category', mnRet:'Return', mnCart:'Cart', mnAcc:'Account',
toneToast:'🏡 Village mode on!'
};
var N = {
topbar:'🔄 90-Day Easy Returns — Any condition, FREE! | 🚀 Free Delivery above ₹499 | ⚡ Instant Refund | 📧 Team.Support@hubooze.in',
searchPh:'Search products, brands and more...', locLbl:'Deliver to',
signIn:'Sign In', myAcc:'My Account', ordLbl:'Returns', retLbl:'& Orders', cartLbl:'Cart',
timerEnd:'Ends in:', tlh:'HRS',
addCartL:'Add to Cart', buyNowL:'Buy Now',
addedMsg:function(n){return '"'+n+'" added to cart! 🛒';},
savedTabLbl:'Saved', wishTabLbl:'Wishlist',
saveForLaterLbl:'Save for later',
savedEmptyP:'No items saved for later.',
wishEmptyP:'Your wishlist is empty. Click ♡ on any product.',
sumSubL:'Subtotal', sumDiscL:'Discount', sumCouponL:'Coupon discount', sumDelivL:'Delivery',
retGuaranteeText:'90-Day free returns on all items — any condition!',
checkoutBtn:'Proceed to Checkout →', continueBtn:'Continue Shopping',
suggestTitleL:'You might also like',
heroBadge:"♻ India's Eco-Commerce",
heroH1:'Shop Smart.
Return Freely.
♻ Recycle Everything.',
heroSub:'Shop with confidence. 90-day returns on everything — any condition. Instant refunds and free pickup.',
rbTitle:'90-Day Easy Returns — Always FREE!', rbDesc:'Changed your mind? Return within 90 days. Free pickup, instant refund.',
hcta1:'Shop Now →', hcta2:'View Return Policy 🔄',
hs2:'Day Returns', hs3:'Instant Refund', hcBadge:'NEW ARRIVAL',
hcTitle:'Ethnic Festive Collection', hcDesc:'Handcrafted by rural artisans', hcPrice:'From ₹299',
flashPill:'⚡ FLASH DEALS',
catTitle:'Shop by Category',
cats:[{i:'😉',n:'Fashion',c:'12,000+',cat:'fashion',hl:true},{i:'🏠',n:'Home & Kitchen',c:'8,400+',cat:'home'},{i:'🤜',n:'Daily Use',c:'6,200+',cat:'daily'},{i:'📱',n:'Electronics',c:'5,700+',cat:'electronics'},{i:'🍺',n:'Handmade/Rural',c:'3,100+',cat:'handmade',hl:true},{i:'🏷',n:'Offers',c:'2,800+',cat:''},{i:'👟',n:'Footwear',c:'4,300+',cat:'fashion'},{i:'🮸',n:'Kids & Baby',c:'3,600+',cat:'daily'}],
b1t:'Recrafted', b1s:'From returned items → new life', b3t:'90-Day Returns',
trendTitle:'🔥 Trending Now',
ecoH:'Recrafted by Rural Artisans', ecoP:'Every product here was once a return.',
handTitle:'🍺 Handmade & Rural',
elecTitle:'📱 Electronics & Accessories',
trustTitle:'Why Hubooze?',
t1h:'90-Day Returns', t1p:'Return anything within 90 days — any condition.',
t2h:'Instant Refund', t2p:'Refund credited immediately upon return approval.',
t3h:'Free Delivery', t3p:'Free shipping on all orders above ₹499.',
t4h:'Returns → Recycled', t4p:"Rural artisans turn your return into a new product.",
fashTitle:'😉 Fashion Picks',
selBadge:'Become a Seller', selTitle:'Start Selling on Hubooze Today',
selSub:'List your products for free. Zero commission for first 3 months!', selBtn:'Register as Seller →',
catPageTitle:'All Categories', filterTitle:'FILTER & SORT',
catTabs:['All','Fashion','Electronics','Home','Daily Use','Handmade'],
catTabKeys:['all','fashion','electronics','home','daily','handmade'],
showingL:'Showing products:', ecoChkL:'Show Recrafted Only',
navItems:[{t:"🔥 Today's Deals",c:'hot',pg:''},{t:'🏠 Home',c:'nactive',pg:'home'},{t:'😉 Fashion',c:'',pg:'categories',cat:'fashion'},{t:'📱 Electronics',c:'',pg:'categories',cat:'electronics'},{t:'♻ Handmade/Rural',c:'special',pg:'categories',cat:'handmade'},{t:'🏷 Offers',c:'',pg:'categories',cat:''},{t:'🧑 Sell on Hubooze',c:'',pg:'seller'},{t:'🔄 Return Policy',c:'',pg:'returns'},{t:'⚖ Admin',c:'',pg:'admin',adminOnly:true}],
fc1:{h:'Shop',l:['Fashion','Electronics','Home & Kitchen','Daily Use','Handmade/Rural','Offers']},
fc2:{h:'Customer',l:['My Account','Track Order','Return & Refund','Wishlist','Help & Support']},
fc3:{h:'Sellers',l:['Sell on Hubooze','Seller Dashboard','Seller Support','Payout Info']},
fc4:{h:'Company',l:['About Us','Recycle Mission','Rural Jobs Program','Contact Us']},
footDesc:"India's first return-to-recycle marketplace. Shop, return, and support rural artisans.",
footCopy:'2025 Hubooze. All rights reserved. ♻ Made with love in India.',
mnHome:'Home', mnCat:'Categories', mnRet:'Returns', mnCart:'Cart', mnAcc:'Account',
toneToast:'🌆 Normal mode on!'
};
var T = V;
// ════════════════════════════════════════════════════════════════
// SHARED UTILS
// ════════════════════════════════════════════════════════════════
function ss(id, v, html) {
var el = document.getElementById(id);
if (!el || v === undefined || v === null) return;
if (html) el.innerHTML = v; else el.textContent = v;
}
function showToast(msg, type) {
var t = document.getElementById('toastEl');
var icons = {success:'✓', error:'✗', info:'ℹ'};
document.getElementById('toastIcon').innerHTML = icons[type||'success'] || icons.success;
document.getElementById('toastMsg').innerHTML = msg;
t.className = 'toast ' + (type||'') + ' show';
setTimeout(function(){t.className='toast '+(type||'');}, 3000);
}
// ════════════════════════════════════════════════════════════════
// TONE SWITCH
// ════════════════════════════════════════════════════════════════
function setTone(tone) {
T = (tone === 'village') ? V : N;
document.body.className = (tone === 'village') ? 'village' : '';
document.getElementById('btnV').className = 'tbtn' + (tone==='village'?' active':'');
document.getElementById('btnN').className = 'tbtn' + (tone==='normal'?' active':'');
// Header
ss('topbarEl', T.topbar, true);
ss('locLblEl', T.locLbl, false);
ss('hdrSignEl', T.signIn, false); ss('hdrAccEl', T.myAcc, false);
ss('hdrOrdEl', T.ordLbl, false); ss('hdrRetEl', T.retLbl, false);
ss('hdrCartEl', T.cartLbl, false);
var si = document.getElementById('searchInpEl'); if(si) si.placeholder = T.searchPh;
// Mob nav
ss('mnHomeLbl',T.mnHome,false); ss('mnCatLbl',T.mnCat,false);
ss('mnRetLbl',T.mnRet,false); ss('mnCartLbl',T.mnCart,false); ss('mnAccLbl',T.mnAcc,false);
// Cart texts
ss('cartTitleEl', '🛒 ' + T.cartLbl, true);
ss('retGuaranteeText', T.retGuaranteeText, true);
ss('checkoutBtn', T.checkoutBtn, true);
ss('continueBtn', T.continueBtn, true);
ss('sumSubL', T.sumSubL, false);
ss('sumDiscL', T.sumDiscL, false);
ss('sumCouponL', T.sumCouponL, false);
ss('sumDelivL', T.sumDelivL, false);
ss('sumTotalL', 'Total', false);
ss('suggestTitleEl', T.suggestTitleL, false);
ss('cartEmptyH', T.cartLbl + ' Empty', false);
ss('cartEmptyP', 'Add products you love.', false);
// Nav
renderNav();
// Home
renderHomeContent();
// Current page specific
if (currentPage === 'categories') renderCategoriesPage();
// Tabs
var ts = document.getElementById('tab-saved');
var tw = document.getElementById('tab-wishlist');
if (ts) ts.textContent = T.savedTabLbl + ' (' + savedItems.length + ')';
if (tw) tw.textContent = T.wishTabLbl + ' (' + wishlist.length + ')';
var tabItems = document.getElementById('tab-items');
if (tabItems) tabItems.textContent = T.cartLbl;
showToast(T.toneToast, 'success');
}
// ════════════════════════════════════════════════════════════════
// PAGE NAV
// ════════════════════════════════════════════════════════════════
var currentPage = 'home';
function showPage(name, cat) {
currentPage = name;
['home','categories','returns','account','orders','seller','admin','checkout','notifications'].forEach(function(p) {
var el = document.getElementById('page-'+p);
if (el) el.className = 'page' + (p===name?' active':'');
});
['mnHome','mnCat','mnRet','mnAcc'].forEach(function(id, i) {
var el = document.getElementById(id);
if (el) el.className = 'mob-btn' + (['home','categories','returns','account'][i]===name?' nactive':'');
});
renderNav();
if (name === 'categories') { renderCategoriesPage(cat); }
if (name === 'orders') { if (typeof renderOrdersPage === 'function') renderOrdersPage(); }
if (name === 'seller') { if (typeof renderSellerPage === 'function') renderSellerPage(); }
if (name === 'account') { if (typeof renderAccountPage === 'function') renderAccountPage(); }
if (name === 'admin') { if (typeof renderAdminPanel === 'function') renderAdminPanel(); }
if (name === 'checkout') { if (typeof renderCheckoutPage === 'function') renderCheckoutPage(); }
if (name === 'notifications') { if (typeof renderNotificationsPage === 'function') renderNotificationsPage(); }
window.scrollTo(0, 0);
}
function renderNav() {
var nav = document.getElementById('mainNavEl'); if (!nav) return;
nav.innerHTML = T.navItems.map(function(n) {
var cls = 'nav-link ' + n.c + (n.pg === currentPage ? ' nactive' : '');
if (n.adminOnly && (!currentUser || currentUser.role !== 'admin')) return '';
var oc = n.pg ? 'onclick="showPage(\'' + n.pg + '\'' + (n.cat ? ',\'' + n.cat + '\'' : '') + ')"' : '';
return '' + n.t + '';
}).join('');
}
// ════════════════════════════════════════════════════════════════
// TIMER
// ════════════════════════════════════════════════════════════════
var timerSec = 4*3600 + 27*60 + 33;
function tick() {
if (timerSec > 0) timerSec--;
var h=Math.floor(timerSec/3600), m=Math.floor((timerSec%3600)/60), s=timerSec%60;
var th=document.getElementById('timerH'), tm=document.getElementById('timerM'), ts=document.getElementById('timerS');
if(th) th.textContent = String(h).padStart(2,'0');
if(tm) tm.textContent = String(m).padStart(2,'0');
if(ts) ts.textContent = String(s).padStart(2,'0');
}
setInterval(tick, 1000); tick();
// ════════════════════════════════════════════════════════════════
// SCROLL ANIMATE
// ════════════════════════════════════════════════════════════════
var obs = new IntersectionObserver(function(entries) {
entries.forEach(function(e) {
if (e.isIntersecting) {
e.target.style.opacity = '1'; e.target.style.transform = 'translateY(0)';
e.target.style.transition = 'opacity .55s ease, transform .55s ease';
}
});
}, {threshold: 0.06});
document.querySelectorAll('.section').forEach(function(el) {
el.style.opacity = '0'; el.style.transform = 'translateY(24px)'; obs.observe(el);
});
// ════════════════════════════════════════════════════════════════
// INIT
// ════════════════════════════════════════════════════════════════
loadCartState();
setTone('village');
showPage('home');
updateCartBadge(false);
// ════════════════════════════════════════════════════════════════
// AUTH / USER SYSTEM
// ════════════════════════════════════════════════════════════════
var DEMO_USERS = {
'priya@demo.com': {id:'u1', name:'Priya Sharma', email:'priya@demo.com', phone:'9999999999', city:'Indore', password:'demo123', role:'customer'},
'amit@demo.com': {id:'u2', name:'Amit Kumar', email:'amit@demo.com', phone:'8888888888', city:'Pithampur', password:'demo123', role:'seller', businessName:'AK Textiles'},
'admin@hubooze.in':{id:'u3',name:'Admin User', email:'admin@hubooze.in',phone:'7777777777',city:'Indore', password:'admin123', role:'admin'}
};
var currentUser = null;
var registeredUsers = {};
function loadUsers() {
try { registeredUsers = JSON.parse(localStorage.getItem('hb_users') || '{}'); } catch(e) { registeredUsers = {}; }
try {
var saved = localStorage.getItem('hb_session');
if (saved) { currentUser = JSON.parse(saved); }
} catch(e) {}
}
function saveUsers() { localStorage.setItem('hb_users', JSON.stringify(registeredUsers)); }
// ── LOGIN HELPER ─────────────────────────────────────────────
function showLoginOverlay(msg) { return {}; }
function hideLoginOverlay(timers) {}
function callLoginAPI(email, password, role, cb) {
fetch('/api/auth/login', {
method:'POST',
headers:{'Content-Type':'application/json'},
body: JSON.stringify({email:email, password:password})
})
.then(function(r){ return r.json().then(function(d){ return {ok:r.ok, data:d}; }); })
.then(function(res){
if (!res.ok) { showToast(res.data.error||'Login failed','error'); return; }
if (role && res.data.user.role!==role && !(role==='seller' && res.data.user.role==='admin')) {
showToast('Wrong portal. This is a '+res.data.user.role+' account.','error'); return;
}
var user = res.data.user; var token = res.data.token;
localStorage.setItem('hb_token', token);
localStorage.setItem('token', token);
try { localStorage.setItem('hb_session', JSON.stringify(user)); } catch(e) {}
currentUser = user;
if (window.api) window.api.token = res.data.token;
try { updateHeaderAuth(); } catch(e) {}
try { renderNav(); } catch(e) {}
showToast('Welcome ' + res.data.user.name.split(' ')[0] + '! 🎉', 'success');
setTimeout(function(){
try {
if (res.data.user.role==='admin') showPage('admin');
else if (res.data.user.role==='seller') showPage('seller');
else showPage('account');
} catch(e) {}
}, 300);
if (cb) cb(res.data.user);
})
.catch(function(){ showToast('Connection error. Please try again.','error'); });
}
function doLogin() {
var email = (document.getElementById('loginEmail')||{value:''}).value.trim().toLowerCase();
var pass = (document.getElementById('loginPass') ||{value:''}).value;
if (!email || !pass) { showToast('Please enter email and password','error'); return; }
var btn = document.querySelector('#cLoginForm .portal-submit');
if (btn) { btn.textContent = 'Please wait...'; btn.disabled = true; }
fetch('/api/auth/login', {
method:'POST',
headers:{'Content-Type':'application/json'},
body: JSON.stringify({email:email, password:pass})
})
.then(function(r){ return r.json().then(function(d){return{ok:r.ok,data:d};}); })
.then(function(res){
if (btn) { btn.innerHTML='Login →'; btn.disabled=false; }
if (!res.ok) { showToast(res.data.error||'Login failed','error'); return; }
localStorage.setItem('hb_token', res.data.token);
localStorage.setItem('hb_session', JSON.stringify(res.data.user));
localStorage.setItem('hb_after_login', res.data.user.role==='admin'?'admin':(res.data.user.role==='seller'?'seller':'account'));
window.location.reload();
})
.catch(function(){
if (btn) { btn.innerHTML='Login →'; btn.disabled=false; }
showToast('Cannot reach server. Please try again.','error');
});
}
function doRegister() {
// Overridden by app.js
showToast('Connecting...', 'info');
}
function loginSuccess(user) {
currentUser = user;
try { localStorage.setItem('hb_session', JSON.stringify(user)); } catch(e) {}
try { updateHeaderAuth(); } catch(e) {}
try { renderNav(); } catch(e) {}
}
function doLogout() {
currentUser = null;
localStorage.removeItem('hb_session');
localStorage.removeItem('hb_token');
if (window.api) window.api.token = null;
updateHeaderAuth();
renderNav();
showToast('Logged out successfully', 'info');
showPage('account');
}
function updateHeaderAuth() {
var signEl = document.getElementById('hdrSignEl');
var accEl = document.getElementById('hdrAccEl');
if (currentUser) {
if (signEl) signEl.textContent = currentUser.name.split(' ')[0];
if (accEl) accEl.textContent = T.myAcc || 'My Account';
} else {
if (signEl) signEl.textContent = T.signIn || 'Login';
if (accEl) accEl.textContent = T.myAcc || 'Account';
}
// Always re-render nav so role-specific links (Admin) show/hide correctly
if (typeof renderNav === 'function') renderNav();
}
function fillDemo(type) {
var map = {customer:{e:'priya@demo.com',p:'demo123'}, seller:{e:'amit@demo.com',p:'demo123'}, admin:{e:'admin@hubooze.in',p:'admin123'}};
var d = map[type]; if (!d) return;
var ei = document.getElementById('loginEmail'); if(ei) ei.value = d.e;
var pi = document.getElementById('loginPass'); if(pi) pi.value = d.p;
showToast('Credentials filled — click Login!', 'info');
}
function switchAuthTab(tab) {
var btnL = document.getElementById('authTabLogin');
var btnR = document.getElementById('authTabReg');
var lf = document.getElementById('loginFormWrap');
var rf = document.getElementById('registerFormWrap');
if (tab === 'login') {
if (btnL) btnL.className = 'auth-tab-btn active';
if (btnR) btnR.className = 'auth-tab-btn';
if (lf) lf.style.display = '';
if (rf) rf.style.display = 'none';
} else {
if (btnL) btnL.className = 'auth-tab-btn';
if (btnR) btnR.className = 'auth-tab-btn active';
if (lf) lf.style.display = 'none';
if (rf) rf.style.display = '';
}
}
// ════════════════════════════════════════════════════════════════
// ACCOUNT PAGE
// ════════════════════════════════════════════════════════════════
var accSection = 'profile';
// ════════════════════════════════════════════════════════════════
// PORTAL SYSTEM — 3 Separate Login Portals
// ════════════════════════════════════════════════════════════════
var activePortal = null; // 'customer' | 'seller' | 'admin'
function openPortal(type) {
activePortal = type;
document.getElementById('portalChoiceScreen').style.display = 'none';
['customer','seller','admin'].forEach(function(p) {
var el = document.getElementById(p + 'Portal');
if (el) el.style.display = (p === type) ? 'block' : 'none';
});
}
function closePortal() {
activePortal = null;
document.getElementById('portalChoiceScreen').style.display = 'block';
['customer','seller','admin'].forEach(function(p) {
var el = document.getElementById(p + 'Portal');
if (el) el.style.display = 'none';
});
}
function switchPortalTab(portal, tab) {
if (portal === 'customer') {
var lf = document.getElementById('cLoginForm');
var rf = document.getElementById('cRegForm');
var bl = document.getElementById('cTabLogin');
var br = document.getElementById('cTabReg');
if (tab === 'login') {
if (lf) lf.style.display = ''; if (rf) rf.style.display = 'none';
if (bl) bl.className = 'plt-btn active'; if (br) br.className = 'plt-btn';
} else {
if (lf) lf.style.display = 'none'; if (rf) rf.style.display = '';
if (bl) bl.className = 'plt-btn'; if (br) br.className = 'plt-btn active';
}
} else if (portal === 'seller') {
var sf = document.getElementById('sLoginForm');
var sr = document.getElementById('sRegForm');
var sl = document.getElementById('sTabLogin');
var sg = document.getElementById('sTabReg');
if (tab === 'login') {
if (sf) sf.style.display = ''; if (sr) sr.style.display = 'none';
if (sl) sl.className = 'plt-btn active'; if (sg) sg.className = 'plt-btn';
} else {
if (sf) sf.style.display = 'none'; if (sr) sr.style.display = '';
if (sl) sl.className = 'plt-btn'; if (sg) sg.className = 'plt-btn active';
}
}
}
// Quick demo login — one click auto-login
function quickLogin(type) {
var map = {
customer:{email:'priya@demo.com',password:'demo123'},
seller:{email:'amit@demo.com',password:'demo123'},
admin:{email:'admin@hubooze.in',password:'admin123'}
};
var c = map[type]; if (!c) return;
openPortal(type);
setTimeout(function(){
var ef = type==='customer'?'loginEmail':(type==='seller'?'sLoginEmail':'aLoginEmail');
var pf = type==='customer'?'loginPass':(type==='seller'?'sLoginPass':'aLoginPass');
var e = document.getElementById(ef); if(e) e.value=c.email;
var p = document.getElementById(pf); if(p) p.value=c.password;
if(type==='customer') doLogin();
else if(type==='seller') doSellerLogin();
else doAdminLogin();
}, 100);
}
// Customer login uses loginEmail + loginPass (same as before — app.js overrides doLogin)
// Seller login
function doSellerLogin() {
var email = (document.getElementById('sLoginEmail')||{value:''}).value.trim().toLowerCase();
var pass = (document.getElementById('sLoginPass') ||{value:''}).value;
if (!email || !pass) { showToast('Please enter email and password','error'); return; }
var btn = document.querySelector('#sLoginForm .portal-submit');
if (btn) { btn.textContent='Please wait...'; btn.disabled=true; }
fetch('/api/auth/login', {
method:'POST',
headers:{'Content-Type':'application/json'},
body: JSON.stringify({email:email, password:pass})
})
.then(function(r){ return r.json().then(function(d){return{ok:r.ok,data:d};}); })
.then(function(res){
if (btn) { btn.innerHTML='Login to Seller Dashboard →'; btn.disabled=false; }
if (!res.ok) { showToast(res.data.error||'Login failed','error'); return; }
if (res.data.user.role!=='seller'&&res.data.user.role!=='admin') {
showToast('Not a seller account','error'); return;
}
localStorage.setItem('hb_token', res.data.token);
localStorage.setItem('hb_session', JSON.stringify(res.data.user));
localStorage.setItem('hb_after_login', 'seller');
window.location.reload();
})
.catch(function(){
if (btn) { btn.innerHTML='Login to Seller Dashboard →'; btn.disabled=false; }
showToast('Cannot reach server. Please try again.','error');
});
}
// Seller register
function doSellerRegister() {
var name = (document.getElementById('sRegName') || {}).value || '';
var phone = (document.getElementById('sRegPhone') || {}).value || '';
var business = (document.getElementById('sRegBusiness') || {}).value || '';
var email = (document.getElementById('sRegEmail') || {}).value || '';
var pass = (document.getElementById('sRegPass') || {}).value || '';
var city = (document.getElementById('sRegCity') || {}).value || '';
if (!name || !email || !pass || !business) {
showToast('Name, business name, email and password are required', 'error'); return;
}
if (pass.length < 6) { showToast('Password must be at least 6 characters', 'error'); return; }
// Mirror into the shared register fields
var setVal = function(id, v) { var el=document.getElementById(id); if(el) el.value=v; };
setVal('regName', name); setVal('regEmail', email);
setVal('regPass', pass); setVal('regPhone', phone); setVal('regCity', city);
var isSellerEl = document.getElementById('regIsSeller');
if (isSellerEl) isSellerEl.value = 'true'; // signal seller role
// Call doRegister which reads from reg* fields
doRegister();
}
// Admin login
function doAdminLogin() {
var email = (document.getElementById('aLoginEmail')||{value:''}).value.trim().toLowerCase();
var pass = (document.getElementById('aLoginPass') ||{value:''}).value;
if (!email || !pass) { showToast('Please enter admin credentials','error'); return; }
var btn = document.querySelector('#adminPortal .portal-submit');
if (btn) { btn.textContent='Please wait...'; btn.disabled=true; }
fetch('/api/auth/login', {
method:'POST',
headers:{'Content-Type':'application/json'},
body: JSON.stringify({email:email, password:pass})
})
.then(function(r){ return r.json().then(function(d){return{ok:r.ok,data:d};}); })
.then(function(res){
if (btn) { btn.innerHTML='⚖ Access Admin Panel →'; btn.disabled=false; }
if (!res.ok) { showToast(res.data.error||'Login failed','error'); return; }
if (res.data.user.role!=='admin') { showToast('Admin access required','error'); return; }
localStorage.setItem('hb_token', res.data.token);
localStorage.setItem('hb_session', JSON.stringify(res.data.user));
localStorage.setItem('hb_after_login', 'admin');
window.location.reload();
})
.catch(function(){
if (btn) { btn.innerHTML='⚖ Access Admin Panel →'; btn.disabled=false; }
showToast('Cannot reach server. Please try again.','error');
});
}
// ── renderAccountPage — shows portal choice or logged-in dashboard ──
function renderAccountPage() {
var loggedOut = document.getElementById('authLoggedOut');
var loggedIn = document.getElementById('authLoggedIn');
if (!loggedOut || !loggedIn) return;
if (!currentUser) {
loggedOut.style.display = '';
loggedIn.style.display = 'none';
closePortal();
return;
}
loggedOut.style.display = 'none';
loggedIn.style.display = '';
ss('accNameEl', currentUser.name, false);
ss('accEmailEl', currentUser.email, false);
var rolePill = document.getElementById('accRolePill');
if (rolePill) {
var roleStyles = {
customer: 'background:rgba(0,161,255,.2);color:#00a1ff',
seller: 'background:rgba(0,255,143,.2);color:#00ff8f',
admin: 'background:rgba(255,77,77,.2);color:#ff4d4d'
};
rolePill.style.cssText = roleStyles[currentUser.role] || roleStyles.customer;
rolePill.textContent = currentUser.role.toUpperCase();
}
var avatarEl = document.getElementById('accAvatarEl');
if (avatarEl) {
var roleGrads = {
customer: 'var(--grad)',
seller: 'linear-gradient(135deg,#00a1ff,#9c27b0)',
admin: 'linear-gradient(135deg,#ff4d4d,#ff6b35)'
};
avatarEl.style.background = roleGrads[currentUser.role] || 'var(--grad)';
}
var menuItems = [
{id:'profile', icon:'👤', label:'My Profile'},
{id:'orders', icon:'📦', label:'My Orders', action:'orders'},
{id:'wishlist', icon:'❤', label:'Wishlist'},
{id:'returns', icon:'🔄', label:'My Returns'},
{id:'addresses', icon:'📍', label:'Addresses'},
];
if (currentUser.role === 'seller' || currentUser.role === 'admin') {
menuItems.push({id:'seller_dash', icon:'🧑', label:'Seller Panel', action:'seller'});
}
if (currentUser.role === 'admin') {
menuItems.push({id:'admin_dash', icon:'⚖', label:'Admin Panel', action:'admin'});
}
menuItems.push({id:'logout', icon:'🚪', label:'Logout', danger:true});
var menuEl = document.getElementById('accMenuEl');
if (menuEl) {
menuEl.innerHTML = menuItems.map(function(m) {
var cls = 'acc-menu-link' + (accSection===m.id?' active':'') + (m.danger?' danger':'');
var oc;
if (m.danger) oc = 'onclick="doLogout()"';
else if (m.action) oc = 'onclick="showPage(\'' + m.action + '\')"';
else oc = 'onclick="setAccSection(\'' + m.id + '\')"';
return '' + m.icon + ' ' + m.label + '
';
}).join('');
}
renderAccContent();
}
function setAccSection(id) { accSection = id; renderAccountPage(); }
function renderAccContent() {
var el = document.getElementById('accContentEl');
if (!el || !currentUser) return;
if (accSection === 'profile') {
el.innerHTML = ''
+ '
My Profile
'
+ '
'
+ '
'
+ '
';
} else if (accSection === 'wishlist') {
var wProds = wishlist.map(function(pid){return PRODUCTS[pid];}).filter(Boolean);
el.innerHTML = ''
+ '
❤ My Wishlist (' + wProds.length + ')
'
+ (wProds.length
? '
' + wProds.map(makeCard).join('') + '
'
: '
❤️
Your wishlist is empty. Click ♡ on any product to add.
')
+ '
';
} else if (accSection === 'returns') {
var myReturns = RETURNS_DB.filter(function(r){ return r.userId === currentUser.id; });
el.innerHTML = ''
+ '
🔄 My Returns
'
+ (myReturns.length
? myReturns.map(function(r){
return '
'
+ '
'
+ '
' + r.productName + '
'
+ '
Order #' + r.orderId + ' • ' + r.createdAt + '
'
+ '
Reason: ' + r.reason + '
'
+ '
' + r.status.toUpperCase() + '
'
+ '
₹' + r.refundAmt.toLocaleString('en-IN') + ' refund
'
+ '
';
}).join('')
: '
🔄
No returns initiated yet.
')
+ '
';
} else if (accSection === 'addresses') {
var myAddrs = ADDRESSES_DB.filter(function(a){ return a.userId === currentUser.id; });
el.innerHTML = ''
+ '
'
+ '
📍 Saved Addresses
'
+ ''
+ ''
+ (myAddrs.length
? myAddrs.map(function(a){
return '
'
+ (a.isDefault ? '
Default' : '')
+ '
' + esc(a.name) + ' • ' + esc(a.phone) + '
'
+ '
' + esc(a.line1) + (a.line2?', '+esc(a.line2):'') + ', ' + esc(a.city) + ' — ' + esc(a.pincode) + '
'
+ '
';
}).join('')
: '
')
+ (myAddrs.length ? '
' : '')
+ '
';
} else if (accSection === 'admin_dash') {
renderAdminPanel();
}
}
function saveProfile() {
if (!currentUser) return;
currentUser.name = (document.getElementById('profName')||{value:currentUser.name}).value.trim() || currentUser.name;
currentUser.phone = (document.getElementById('profPhone')||{value:''}).value.trim();
currentUser.city = (document.getElementById('profCity')||{value:''}).value.trim();
localStorage.setItem('hb_session', JSON.stringify(currentUser));
// Also update in registered users
if (registeredUsers[currentUser.email]) {
registeredUsers[currentUser.email] = {...registeredUsers[currentUser.email], ...currentUser};
saveUsers();
}
ss('accNameEl', currentUser.name, false);
showToast('Profile updated!', 'success');
}
function showAddAddressForm() {
var formEl = document.getElementById('addAddrForm');
if (!formEl) return;
formEl.innerHTML = ''
+ '
Add New Address
'
+ '
'
+ '
'
+ '
'
+ '
'
+ '
';
}
function addAddress() {
if (!currentUser) return;
var name = (document.getElementById('addrName')||{value:''}).value.trim();
var phone = (document.getElementById('addrPhone')||{value:''}).value.trim();
var line1 = (document.getElementById('addrLine1')||{value:''}).value.trim();
var city = (document.getElementById('addrCity')||{value:''}).value.trim();
var state = (document.getElementById('addrState')||{value:''}).value.trim();
var pin = (document.getElementById('addrPin')||{value:''}).value.trim();
if (!name || !line1 || !city || !pin) { showToast('Please fill all required fields', 'error'); return; }
var addr = {id:'addr_'+Date.now(), userId:currentUser.id, name:name, phone:phone, line1:line1, city:city, state:state, pincode:pin, isDefault: !ADDRESSES_DB.some(function(a){return a.userId===currentUser.id;})};
ADDRESSES_DB.push(addr);
saveReturnsDB();
showToast('Address saved!', 'success');
setAccSection('addresses');
}
function esc(str) { return String(str||'').replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"'); }
// ════════════════════════════════════════════════════════════════
// ORDERS SYSTEM
// ════════════════════════════════════════════════════════════════
var ORDERS_DB = [
{id:'ORD2412001', userId:'u1', items:[{productId:'p1',name:'Floral Printed Kurti',icon:'😉',price:299,qty:2,size:'M'}], subtotal:598, discount:0, couponD:0, delivery:0, total:598, status:'delivered', address:'123 MG Road, Indore, MP 452001', payment:'UPI', date:'2024-12-28', deliveredOn:'2025-01-01'},
{id:'ORD2501002', userId:'u1', items:[{productId:'p2',name:'Wireless Earphones Pro',icon:'🎧',price:799,qty:1,size:null}], subtotal:799, discount:700, couponD:0, delivery:0, total:799, status:'shipped', address:'123 MG Road, Indore, MP 452001', payment:'Card', date:'2025-01-02'},
{id:'ORD2501003', userId:'u1', items:[{productId:'p5',name:'Face Glow Serum',icon:'✨',price:179,qty:1,size:'30ml'},{productId:'p6',name:'Handloom Jute Bag',icon:'👜',price:249,qty:1,size:'S'}], subtotal:428, discount:622, couponD:0, delivery:49, total:477, status:'processing', address:'123 MG Road, Indore, MP 452001', payment:'COD', date:'2025-01-05'},
{id:'ORD2412004', userId:'u1', items:[{productId:'p8',name:'Banarasi Saree',icon:'🧽',price:1299,qty:1,size:'Free Size'}], subtotal:1299, discount:1700, couponD:0, delivery:0, total:1299, status:'delivered', address:'123 MG Road, Indore, MP 452001', payment:'UPI', date:'2024-12-15', deliveredOn:'2024-12-20'},
{id:'ORD2412005', userId:'u2', items:[{productId:'p10',name:'Bluetooth Speaker',icon:'🔊',price:699,qty:2,size:null}], subtotal:1398, discount:0, couponD:50, delivery:0, total:1348, status:'delivered', address:'45 Industrial Area, Pithampur, MP', payment:'UPI', date:'2024-12-20', deliveredOn:'2024-12-24'}
];
var ordTabActive = 'all';
function renderOrdersPage() {
if (!currentUser) { showPage('account'); return; }
ss('ordTitleEl', 'My Orders', false);
ss('ordBcEl', 'My Orders', false);
var TABS = [
{key:'all',key2:'',label:'All Orders'},
{key:'processing', label:'Processing'},
{key:'shipped', label:'Shipped'},
{key:'delivered', label:'Delivered'},
{key:'cancelled', label:'Cancelled'},
{key:'returned', label:'Returned'}
];
var te = document.getElementById('ordTabsEl');
if (te) {
te.innerHTML = TABS.map(function(t) {
return '';
}).join('');
}
var myOrders = ORDERS_DB.filter(function(o) {
if (o.userId !== currentUser.id) return false;
if (ordTabActive && ordTabActive !== 'all' && o.status !== ordTabActive) return false;
return true;
});
var el = document.getElementById('ordListEl');
if (!el) return;
if (!myOrders.length) {
el.innerHTML = ''
+ '
📦
'
+ '
No orders found
'
+ '
Start shopping to see your orders here!
'
+ '
'
+ '
';
return;
}
el.innerHTML = myOrders.map(function(o) {
var disc = o.discount + o.couponD;
return ''
// Header
+ '
'
+ '
'
+ '
Order #' + o.id + '
'
+ '
'
+ o.date + (o.deliveredOn ? ' • Delivered: ' + o.deliveredOn : '') + ' • ' + o.payment
+ '
'
+ '
'
+ '
' + o.status.toUpperCase() + ''
+ '
'
// Items
+ '
'
+ o.items.map(function(it) {
return '
'
+ '
' + it.icon + ''
+ '
'
+ '
' + it.name + '
'
+ '
Qty: ' + it.qty + (it.size ? ' • ' + it.size : '') + '
'
+ '
'
+ '
₹' + (it.price * it.qty).toLocaleString('en-IN') + '
'
+ '
';
}).join('')
+ '
'
// Summary row
+ '
'
+ '
'
+ '📍 ' + o.address.substring(0, 35) + (o.address.length > 35 ? '...' : '')
+ (disc > 0 ? ' • Saved ₹' + disc.toLocaleString('en-IN') + '' : '')
+ '
'
+ '
₹' + o.total.toLocaleString('en-IN') + '
'
+ '
'
// Actions
+ '
'
+ (o.status === 'delivered'
? ''
: '')
+ (o.status === 'processing'
? ''
: '')
+ ''
+ ''
+ '
'
+ '
';
}).join('');
}
function switchOrdTab(key) { ordTabActive = key; renderOrdersPage(); }
function cancelOrder(ordId) {
var ord = ORDERS_DB.find(function(o){return o.id===ordId;});
if (!ord) return;
if (!confirm('Cancel order #' + ordId + '?')) return;
ord.status = 'cancelled';
showToast('Order #' + ordId + ' cancelled. Refund in 3-5 days.', 'info');
renderOrdersPage();
}
function reorderItems(ordId) {
var ord = ORDERS_DB.find(function(o){return o.id===ordId;});
if (!ord) return;
ord.items.forEach(function(it) { addToCart(it.productId, it.size); });
openCart();
}
// ════════════════════════════════════════════════════════════════
// RETURN SYSTEM
// ════════════════════════════════════════════════════════════════
var RETURNS_DB = [];
var ADDRESSES_DB = [
{id:'addr1', userId:'u1', name:'Priya Sharma', phone:'9999999999', line1:'123 MG Road', city:'Indore', state:'MP', pincode:'452001', isDefault:true}
];
function saveReturnsDB() {
localStorage.setItem('hb_returns', JSON.stringify(RETURNS_DB));
localStorage.setItem('hb_addresses', JSON.stringify(ADDRESSES_DB));
}
function loadReturnsDB() {
try { RETURNS_DB = JSON.parse(localStorage.getItem('hb_returns') || '[]'); } catch(e){ RETURNS_DB=[]; }
try { ADDRESSES_DB = JSON.parse(localStorage.getItem('hb_addresses') || JSON.stringify(ADDRESSES_DB)); } catch(e){}
}
var returnModal_orderId = null;
function openReturnModal(ordId) {
returnModal_orderId = ordId;
var ord = ORDERS_DB.find(function(o){return o.id===ordId;});
if (!ord) return;
var overlay = document.getElementById('returnModalOverlay');
if (!overlay) return;
var itemOptions = ord.items.map(function(it, i) {
return '';
}).join('');
document.getElementById('returnModalBody').innerHTML =
''
+ ''
+ itemOptions
+ '
'
+ ''
+ ''
+ ''
+ '
'
+ ''
+ ''
+ ''
+ '
'
+ ''
+ '
🔄 90-Day Return Promise
'
+ '
Free pickup from your door within 24 hours • Instant refund after pickup • Any condition accepted
'
+ '
'
+ '';
overlay.className = 'modal-overlay open';
}
function submitReturn() {
if (!currentUser || !returnModal_orderId) return;
var ord = ORDERS_DB.find(function(o){return o.id===returnModal_orderId;});
if (!ord) return;
var reason = (document.getElementById('retReason')||{value:'other'}).value;
var cond = (document.getElementById('retCondition')||{value:'used'}).value;
// Find selected items
var checkedEls = document.querySelectorAll('input[name="retItem"]:checked');
var selectedItems = Array.from(checkedEls).map(function(el){ return ord.items[parseInt(el.value)]; });
if (!selectedItems.length) { showToast('Select at least one item to return', 'error'); return; }
var refundAmt = selectedItems.reduce(function(s,it){return s + it.price * it.qty;}, 0);
var ret = {
id: 'RET' + Date.now(),
orderId: returnModal_orderId,
userId: currentUser.id,
productName: selectedItems.map(function(it){return it.name;}).join(', '),
reason: {size:'Wrong size',quality:'Quality issue',wrong:'Wrong product',damaged:'Arrived damaged',mind:'Changed mind',other:'Other'}[reason] || reason,
condition: cond,
refundAmt: refundAmt,
status: 'initiated',
createdAt: new Date().toLocaleDateString('en-IN'),
pickupDate: new Date(Date.now() + 86400000).toLocaleDateString('en-IN')
};
RETURNS_DB.push(ret);
ord.status = 'returned';
saveReturnsDB();
document.getElementById('returnModalOverlay').className = 'modal-overlay';
showToast('Return initiated! Pickup on ' + ret.pickupDate + '. Refund ₹' + refundAmt.toLocaleString('en-IN'), 'success');
renderOrdersPage();
}
function closeReturnModal() {
var ov = document.getElementById('returnModalOverlay');
if (ov) ov.className = 'modal-overlay';
}
// ════════════════════════════════════════════════════════════════
// CHECKOUT WITH ADDRESS
// ════════════════════════════════════════════════════════════════
function proceedToCheckout() {
if (!cartItems.length) { showToast('Your cart is empty!', 'error'); return; }
if (!currentUser) {
showToast('Please login to checkout', 'error');
setTimeout(function(){ closeCart(); showPage('account'); }, 800);
return;
}
openCheckoutModal();
}
function getSellerProducts() {
if (!currentUser) return [];
var base = Object.values(PRODUCTS).filter(function(p){ return p.sellerId === currentUser.id; });
var own = SELLER_OWN_PRODUCTS[currentUser.id] || [];
var runtime = SELLER_PRODUCTS_DB[currentUser.id] || [];
return base.concat(own).concat(runtime);
}
function getSellerOrders() {
if (!currentUser) return [];
var myProds = getSellerProducts();
var myIds = myProds.map(function(p){ return p.id; });
if (currentUser.role === 'admin') return ORDERS_DB;
return ORDERS_DB.filter(function(o){
return o.items.some(function(it){ return myIds.indexOf(it.productId) > -1; });
});
}
// ── MAIN RENDER ──────────────────────────────────────────────────
function renderSellerPage() {
var el = document.getElementById('sellerContentEl');
if (!el) return;
// ─ Not logged in ─
if (!currentUser) {
el.innerHTML = sellerGate(
'🧑',
'Login Required',
'Login as a seller to access your dashboard.',
'Login / Register',
"showPage('account')"
);
return;
}
// ─ Not a seller ─
if (currentUser.role !== 'seller' && currentUser.role !== 'admin') {
el.innerHTML = sellerGate(
'🧑',
'Seller Account Required',
'Your account role is ' + currentUser.role + '. Register a new account with "Register as Seller" checked, or use:
amit@demo.com / demo123',
'Switch Account',
"showPage('account')"
);
return;
}
var myProds = getSellerProducts();
var myOrders = getSellerOrders();
var myReturns = RETURNS_DB.filter(function(r){
var ids = myProds.map(function(p){return p.id;});
return currentUser.role==='admin' || ids.indexOf(r.productId) > -1;
});
var totalRevenue = myOrders.filter(function(o){return o.status!=='cancelled';}).reduce(function(s,o){return s+o.total;},0);
var pendingOrders = myOrders.filter(function(o){return o.status==='processing';}).length;
var unreadNotifs = SELLER_NOTIFICATIONS.filter(function(n){return !n.read;}).length;
// ─ Stats bar ─
var statsHtml = ''
+ selStat(myProds.length, 'Products', 'var(--green)', '📦')
+ selStat(myOrders.length, 'Total Orders', 'var(--blue)', '📩')
+ selStat(pendingOrders, 'Pending', 'var(--yellow)', '⏳')
+ selStat('₹'+totalRevenue.toLocaleString('en-IN'), 'Revenue', 'var(--green)', '💰')
+ selStat('₹'+Math.round(totalRevenue*.9).toLocaleString('en-IN'), 'Net Payout', 'var(--blue)', '💸')
+ selStat(myReturns.length, 'Returns', 'var(--red)', '🔄')
+ '
';
// ─ Tab bar ─
var tabs = [
{id:'overview', label:'📈 Overview'},
{id:'products', label:'📦 Products ('+myProds.length+')'},
{id:'orders', label:'📩 Orders ('+myOrders.length+')'},
{id:'returns', label:'🔄 Returns ('+myReturns.length+')'},
{id:'add_product', label:'➕ Add Product'},
{id:'payouts', label:'💸 Payouts'},
{id:'notifications',label:'🔔 Alerts'+(unreadNotifs?' ('+unreadNotifs+')':'')},
];
if (currentUser.role === 'admin') {
tabs.push({id:'admin', label:'⚖ Admin'});
}
var tabsHtml = ''
+ tabs.map(function(t){
return '';
}).join('')
+ '
';
el.innerHTML = statsHtml + tabsHtml + '';
renderSellerTab(myProds, myOrders, myReturns, totalRevenue);
}
function sellerGate(icon, title, body, btnLabel, btnAction) {
return ''
+ '
'+icon+'
'
+ '
'+title+'
'
+ '
'+body+'
'
+ '
'
+ '
';
}
function selStat(val, label, color, icon) {
return ''
+ '
'+icon+'
'
+ '
'+val+'
'
+ '
'+label+'
'
+ '
';
}
// ── TAB SWITCH ───────────────────────────────────────────────────
function switchSelTab(t) {
sellerTabActive = t;
renderSellerPage();
}
// ── TAB CONTENT ──────────────────────────────────────────────────
function renderSellerTab(myProds, myOrders, myReturns, totalRevenue) {
var el = document.getElementById('selTabContent');
if (!el) return;
var t = sellerTabActive;
if (t === 'overview') renderSelOverview(myProds, myOrders, myReturns, totalRevenue, el);
else if (t === 'products') renderSelProducts(myProds, el);
else if (t === 'orders') renderSelOrders(myOrders, el);
else if (t === 'returns') renderSelReturns(myReturns, el);
else if (t === 'add_product') renderSelAddProduct(el);
else if (t === 'payouts') renderSelPayouts(myOrders, totalRevenue, el);
else if (t === 'notifications') renderSelNotifications(el);
else if (t === 'admin') renderAdminPanel(el);
}
// ── OVERVIEW TAB ─────────────────────────────────────────────────
function renderSelOverview(myProds, myOrders, myReturns, totalRevenue, el) {
var recent = myOrders.slice().reverse().slice(0, 5);
var topProds = myProds.slice().sort(function(a,b){return (b.reviews||0)-(a.reviews||0);}).slice(0,5);
var pendRets = myReturns.filter(function(r){return r.status==='initiated';});
el.innerHTML =
// Revenue chart placeholder + quick metrics
''
// Revenue breakdown card
+ '
'
+ '
💰 Revenue Breakdown
'
+ metricRow('Gross Revenue', '₹'+totalRevenue.toLocaleString('en-IN'), 'var(--text)')
+ metricRow('Platform Fee (10%)', '-₹'+Math.round(totalRevenue*.1).toLocaleString('en-IN'), 'var(--red)')
+ metricRow('Net Payout', '₹'+Math.round(totalRevenue*.9).toLocaleString('en-IN'), 'var(--green)')
+ '
'
+ metricRow('Avg Order Value', myOrders.length ? '₹'+Math.round(totalRevenue/myOrders.length).toLocaleString('en-IN') : '₹0', 'var(--blue)')
+ metricRow('Return Rate', myOrders.length ? (myReturns.length/myOrders.length*100).toFixed(1)+'%' : '0%', 'var(--yellow)')
+ '
'
// Quick actions card
+ '
'
+ '
⚡ Quick Actions
'
+ '
'
+ ''
+ ''
+ ''
+ ''
+ '
'
+ '
'
// Recent orders
+ ''
+ '
'
+ '
📩 Recent Orders
'
+ ''
+ ''
+ (recent.length
? recent.map(function(o){ return orderRowCompact(o); }).join('')
: '
No orders yet.
')
+ '
'
// Top products
+ ''
+ '
'
+ '
🏆 Top Products
'
+ ''
+ ''
+ (topProds.length
? topProds.map(function(p){ return productRowCompact(p); }).join('')
: '
No products listed yet.
')
+ '
';
}
function metricRow(label, val, color) {
return ''
+ ''+label+''
+ ''+val+''
+ '
';
}
function orderRowCompact(o) {
return ''
+ '
'
+ '
#'+o.id+'
'
+ '
'+o.date+' • '+o.items.length+' item(s)
'
+ '
'
+ '
'+o.status+''
+ '
₹'+o.total.toLocaleString('en-IN')+''
+ '
'
+ '
';
}
function productRowCompact(p) {
var disc = Math.round((1-p.price/p.orig)*100);
return ''
+ '
'+p.icon+''
+ '
'
+ '
'+p.name+'
'
+ '
'+p.brand+' • '+(p.reviews||0)+' reviews
'
+ '
'
+ '
'
+ '
₹'+p.price.toLocaleString('en-IN')+' '+disc+'% off
'
+ '
Stock: '+(p.stock||'N/A')+'
'
+ '
'
+ '
';
}
// ── PRODUCTS TAB ─────────────────────────────────────────────────
function renderSelProducts(myProds, el) {
var listed = myProds.filter(function(p){ return p.listed !== false; });
var unlisted = myProds.filter(function(p){ return p.listed === false; });
el.innerHTML = ''
+ '
'
+ '
📦 My Products ('+listed.length+' active)
'
+ ''
+ ''
+ (listed.length
? '
'
+ listed.map(function(p){ return sellerProductRow(p); }).join('')
+ '
'
: '
')
+ '
'
+ (unlisted.length
? ''
+ '
❌ Unlisted Products ('+unlisted.length+')
'
+ unlisted.map(function(p){ return sellerProductRow(p); }).join('')
+ ''
: '');
}
function sellerProductRow(p) {
var disc = Math.round((1-(p.price||0)/(p.orig||1))*100);
return ''
+ '
'+p.icon+''
+ '
'
+ '
'+p.name+'
'
+ '
'+p.brand+' • '+p.cat+' • Stock: '+(p.stock||'N/A')+'
'
+ '
'
+ (p.badge ? ''+p.badge+'' : '')
+ (p.eco ? '♻ ECO' : '')
+ '
'
+ '
'
+ '
'
+ '
₹'+p.price.toLocaleString('en-IN')+'
'
+ '
₹'+p.orig.toLocaleString('en-IN')+'
'
+ '
'+disc+'% off
'
+ '
'
+ '
'
+ ''
+ ''
+ '
'
+ '
';
}
function editSellerProduct(pid) {
sellerEditProductId = pid;
switchSelTab('add_product');
}
function toggleListProduct(pid) {
var p = PRODUCTS[pid];
if (!p) {
// search in seller own products
var uid = currentUser ? currentUser.id : null;
var own = SELLER_OWN_PRODUCTS[uid] || [];
p = own.find(function(x){return x.id===pid;});
if (!p) {
var run = SELLER_PRODUCTS_DB[uid] || [];
p = run.find(function(x){return x.id===pid;});
}
}
if (!p) { showToast('Product not found','error'); return; }
p.listed = (p.listed === false) ? true : false;
showToast(p.listed === false ? '"'+p.name+'" unlisted' : '"'+p.name+'" listed again', 'info');
renderSellerPage();
}
// ── ORDERS TAB ───────────────────────────────────────────────────
function renderSelOrders(myOrders, el) {
var statusFilters = ['all','processing','confirmed','shipped','delivered','cancelled'];
var selOrdFilter = window._selOrdFilter || 'all';
var filtered = selOrdFilter === 'all' ? myOrders
: myOrders.filter(function(o){ return o.status === selOrdFilter; });
el.innerHTML = ''
+ '
'
+ '
📩 Orders ('+filtered.length+')
'
+ '
'
+ statusFilters.map(function(s){
var cnt = s==='all' ? myOrders.length : myOrders.filter(function(o){return o.status===s;}).length;
return '';
}).join('')
+ '
'
+ '
'
+ (filtered.length
? '
'
+ filtered.slice().reverse().map(function(o){ return sellerOrderCard(o); }).join('')
+ '
'
: '
📩
No orders in this category.
')
+ '
';
}
function sellerOrderCard(o) {
return ''
+ '
'
+ '
'
+ '
#'+o.id+'
'
+ '
'+o.date+' • '+o.payment+' • '+o.address.substring(0,35)+'...
'
+ '
'
+ '
'
+ ''+o.status.toUpperCase()+''
+ '₹'+o.total.toLocaleString('en-IN')+''
+ '
'
+ '
'
+ '
'
+ o.items.map(function(it){
return '
'
+ '
'+it.icon+''
+ '
'+it.name+'
'
+ '
Qty: '+it.qty+(it.size?' • '+it.size:'')+'
'
+ '
';
}).join('')
+ '
'
+ '
'
+ 'Update Status:'
+ ''
+ ''
+ '
'
+ '
';
}
// ── RETURNS TAB ──────────────────────────────────────────────────
function renderSelReturns(myReturns, el) {
el.innerHTML = ''
+ '
🔄 Return Requests ('+myReturns.length+')
'
+ (myReturns.length
? '
'
+ myReturns.map(function(r){ return sellerReturnCard(r); }).join('')
+ '
'
: '
🔄
No return requests. Great job!
')
+ '
';
}
function sellerReturnCard(r) {
return ''
+ '
'
+ '
'
+ '
'+r.productName+'
'
+ '
Order #'+r.orderId+' • '+r.createdAt+'
'
+ '
Reason: '+r.reason+' • Condition: '+r.condition+'
'
+ '
'
+ '
'
+ '
'+r.status.toUpperCase()+''
+ '
₹'+r.refundAmt.toLocaleString('en-IN')+' refund
'
+ '
'
+ '
'
+ (r.status === 'initiated'
? '
'
+ ''
+ ''
+ ''
+ '
'
: '
Resolved on: '+(r.resolvedAt||r.createdAt)+'
')
+ '
';
}
// ── ADD PRODUCT TAB ──────────────────────────────────────────────
function renderSelAddProduct(el) {
var isEdit = sellerEditProductId !== null;
var ep = isEdit ? (PRODUCTS[sellerEditProductId]
|| (SELLER_OWN_PRODUCTS[currentUser.id]||[]).find(function(p){return p.id===sellerEditProductId;})
|| (SELLER_PRODUCTS_DB[currentUser.id]||[]).find(function(p){return p.id===sellerEditProductId;})
) : null;
el.innerHTML = ''
+ '
'
+ '
'+(isEdit?'✎ Edit Product':'➕ List New Product')+'
'
+ (isEdit ? '' : '')
+ ''
+ '
'
// Name
+ formField('Product Name *', 'text', 'np_name', 'e.g. Handblock Print Dupatta', ep?ep.name:'')
// Brand
+ formField('Brand *', 'text', 'np_brand', 'e.g. AK Textiles', ep?ep.brand:'')
// Price
+ formField('Selling Price (₹) *', 'number', 'np_price', '299', ep?ep.price:'')
// MRP
+ formField('MRP / Original (₹) *', 'number', 'np_orig', '599', ep?ep.orig:'')
+ '
'
+ '
'
// Category
+ '
'
+ '
'
// Stock
+ formField('Stock Qty *', 'number', 'np_stock', '50', ep?ep.stock:'')
// Emoji icon
+ formField('Icon Emoji', 'text', 'np_icon', '😉', ep?ep.icon:'', 'maxlength="4"')
+ '
'
// Description
+ '
'
+ '
'
// Sizes
+ '
'
+ '
'
// Badge + Eco row
+ '
'
// Submit
+ '
'
+ ''
+ (isEdit ? '' : '')
+ '
'
+ '
';
}
function formField(label, type, id, placeholder, value, extra) {
return ''
+ ''
+ '
';
}
function saveSellerProduct() {
if (!currentUser) return;
var name = (document.getElementById('np_name')||{value:''}).value.trim();
var brand = (document.getElementById('np_brand')||{value:''}).value.trim();
var price = parseInt((document.getElementById('np_price')||{value:'0'}).value)||0;
var orig = parseInt((document.getElementById('np_orig')||{value:'0'}).value)||0;
var stock = parseInt((document.getElementById('np_stock')||{value:'0'}).value)||0;
var cat = (document.getElementById('np_cat')||{value:'fashion'}).value;
var icon = (document.getElementById('np_icon')||{value:'📦'}).value || '📦';
var desc = (document.getElementById('np_desc')||{value:''}).value.trim();
var sizesRaw= (document.getElementById('np_sizes')||{value:''}).value;
var badge = (document.getElementById('np_badge')||{value:''}).value || null;
var eco = (document.getElementById('np_eco')||{checked:false}).checked;
var listed = (document.getElementById('np_listed')||{checked:true}).checked;
var sizes = sizesRaw.split(',').map(function(s){return s.trim();}).filter(Boolean);
if (!name) { showToast('Product name is required','error'); document.getElementById('np_name').classList.add('error'); return; }
if (!brand) { showToast('Brand is required','error'); document.getElementById('np_brand').classList.add('error'); return; }
if (!price || price <= 0) { showToast('Please enter a valid selling price','error'); return; }
if (!orig || orig <= 0) { showToast('Please enter a valid MRP','error'); return; }
if (price >= orig) { showToast('Selling price must be less than MRP','error'); return; }
if (stock <= 0) { showToast('Please enter valid stock quantity','error'); return; }
if (sellerEditProductId) {
// Update existing
var existing = PRODUCTS[sellerEditProductId]
|| (SELLER_OWN_PRODUCTS[currentUser.id]||[]).find(function(p){return p.id===sellerEditProductId;})
|| (SELLER_PRODUCTS_DB[currentUser.id]||[]).find(function(p){return p.id===sellerEditProductId;});
if (existing) {
existing.name = name; existing.brand = brand; existing.price = price;
existing.orig = orig; existing.stock = stock; existing.cat = cat;
existing.icon = icon; existing.description = desc; existing.sizes = sizes;
existing.badge = badge; existing.eco = eco; existing.listed = listed;
showToast('✓ "'+name+'" updated successfully!', 'success');
}
sellerEditProductId = null;
switchSelTab('products');
} else {
// Create new
var newProd = {
id: 'sp_'+Date.now(),
name: name, brand: brand, cat: cat,
price: price, orig: orig, stock: stock,
icon: icon, rating: 0, reviews: 0,
eco: eco, badge: badge,
sizes: sizes, inStock: true,
sellerId: currentUser.id,
description: desc,
listed: listed
};
if (!SELLER_PRODUCTS_DB[currentUser.id]) SELLER_PRODUCTS_DB[currentUser.id] = [];
SELLER_PRODUCTS_DB[currentUser.id].push(newProd);
if (listed) PRODUCTS[newProd.id] = newProd; // make visible in shop too
showToast('🎉 "'+name+'" listed successfully!', 'success');
switchSelTab('products');
}
}
// ── PAYOUTS TAB ──────────────────────────────────────────────────
function renderSelPayouts(myOrders, totalRevenue, el) {
var net = Math.round(totalRevenue * .9);
var paid = Math.round(net * .6);
var pending = net - paid;
var history = [
{date:'2025-01-01', amount: Math.round(net*.3), status:'paid', ref:'PAY_2501_001'},
{date:'2024-12-15', amount: Math.round(net*.2), status:'paid', ref:'PAY_2412_002'},
{date:'2024-12-01', amount: Math.round(net*.1), status:'paid', ref:'PAY_2412_001'},
{date:'2025-01-10', amount: pending, status:'pending', ref:'PAY_2501_002'},
];
el.innerHTML = ''
+ '
₹'+net.toLocaleString('en-IN')+'
Total Net Earnings
'
+ '
₹'+paid.toLocaleString('en-IN')+'
Total Paid Out
'
+ '
₹'+pending.toLocaleString('en-IN')+'
Pending Payout
'
+ '
'
+ ''
+ '
Payment Schedule
'
+ '
Payouts are processed every 7 days on Monday. Platform fee is 10%.
'
+ '
'
+ '
Next payout date
'
+ '
Monday, 13 Jan 2025
'
+ '
Estimated amount: ₹'+pending.toLocaleString('en-IN')+'
'
+ '
'
+ ''
+ '
Payout History
'
+ history.map(function(h){
return '
'
+ '
'
+ '
₹'+h.amount.toLocaleString('en-IN')+'
'
+ '
'+h.date+' • Ref: '+h.ref+'
'
+ '
'
+ '
'+h.status.toUpperCase()+''
+ '
';
}).join('')
+ '
';
}
// ── NOTIFICATIONS TAB ────────────────────────────────────────────
function renderSelNotifications(el) {
var icons = {order:'📩', return:'🔄', payout:'💰', system:'🔔'};
el.innerHTML = ''
+ '
'
+ '
🔔 Notifications
'
+ ''
+ ''
+ '
'
+ SELLER_NOTIFICATIONS.map(function(n){
return '
'
+ '
'
+ (icons[n.type]||'🔔')
+ '
'
+ '
'
+ '
'+n.text+'
'
+ '
'+n.time+'
'
+ '
'
+ (!n.read ? '
' : '')
+ '
';
}).join('')
+ '
';
}
function markAllRead() {
SELLER_NOTIFICATIONS.forEach(function(n){ n.read = true; });
renderSellerPage();
showToast('All notifications marked as read', 'info');
}
// ── ADMIN TAB ────────────────────────────────────────────────────
// ════════════════════════════════════════════════════════════════
// ADMIN PANEL — FULL FEATURED
// ════════════════════════════════════════════════════════════════
var adminTabActive = 'overview';
var adminUserFilter = 'all';
var adminOrdFilter = 'all';
var adminRetFilter = 'all';
var adminProdFilter = 'all';
// ── ENTRY POINT ──────────────────────────────────────────────────
function renderAdminPanel() {
var el = document.getElementById('adminContentEl');
if (!el) return;
// Guard: admin only
if (!currentUser) {
el.innerHTML = adminGate('⚖', 'Admin Access Required',
'Please login with an admin account to access this panel.',
'Login', "showPage('account')");
return;
}
if (currentUser.role !== 'admin') {
el.innerHTML = adminGate('🚫', 'Access Denied',
'Your account role is ' + currentUser.role + '.
Admin credentials: admin@hubooze.in / admin123',
'Switch Account', "showPage('account')");
return;
}
// Compute live stats
var stats = computeAdminStats();
// Stat cards
var statsHtml = ''
+ adminStat('📩', stats.totalOrders, 'Total Orders', 'var(--blue)')
+ adminStat('₹', stats.totalRevenue.toLocaleString('en-IN'), 'Platform Revenue', 'var(--green)')
+ adminStat('👤', stats.totalUsers, 'Registered Users', 'var(--yellow)')
+ adminStat('🔄', stats.totalReturns, 'Return Requests', 'var(--red)')
+ '
'
+ ''
+ adminStat('🧑', stats.totalSellers, 'Active Sellers', 'var(--orange)')
+ adminStat('📦', stats.totalProducts, 'Products Listed', 'var(--blue)')
+ adminStat('♻', stats.ecoProducts, 'Eco Products', 'var(--green)')
+ adminStat('🎉', stats.deliveredOrders, 'Delivered Orders', 'var(--green)')
+ '
';
// Tab bar
var tabs = [
{id:'overview', label:'📈 Overview'},
{id:'users', label:'👤 Users (' + stats.totalUsers + ')'},
{id:'orders', label:'📩 Orders (' + stats.totalOrders + ')'},
{id:'products', label:'📦 Products (' + stats.totalProducts + ')'},
{id:'returns', label:'🔄 Returns (' + stats.totalReturns + ')'},
{id:'sellers', label:'🧑 Sellers (' + stats.totalSellers + ')'},
{id:'analytics', label:'📊 Analytics'},
{id:'settings', label:'⚙ Settings'},
];
var tabsHtml = ''
+ tabs.map(function(t) {
return '';
}).join('')
+ '
';
el.innerHTML = statsHtml + tabsHtml + '';
renderAdminTab(stats);
}
function adminGate(icon, title, body, btn, action) {
return ''
+ '
' + icon + '
'
+ '
' + title + '
'
+ '
' + body + '
'
+ '
'
+ '
';
}
function adminStat(icon, val, label, color) {
return ''
+ '
' + icon + '
'
+ '
' + val + '
'
+ '
' + label + '
'
+ '
';
}
function computeAdminStats() {
var allUsers = Object.values(Object.assign({}, DEMO_USERS, registeredUsers));
var allProducts = Object.values(PRODUCTS);
var totalRev = ORDERS_DB.filter(function(o) { return o.status !== 'cancelled'; })
.reduce(function(s, o) { return s + o.total; }, 0);
return {
totalOrders: ORDERS_DB.length,
totalRevenue: totalRev,
platformFee: Math.round(totalRev * 0.1),
totalUsers: allUsers.length,
totalSellers: allUsers.filter(function(u) { return u.role === 'seller'; }).length,
totalProducts: allProducts.length,
ecoProducts: allProducts.filter(function(p) { return p.eco; }).length,
totalReturns: RETURNS_DB.length,
pendingReturns: RETURNS_DB.filter(function(r) { return r.status === 'initiated'; }).length,
deliveredOrders: ORDERS_DB.filter(function(o) { return o.status === 'delivered'; }).length,
cancelledOrders: ORDERS_DB.filter(function(o) { return o.status === 'cancelled'; }).length,
processingOrders:ORDERS_DB.filter(function(o) { return o.status === 'processing'; }).length,
allUsers: allUsers,
};
}
function switchAdminTab(t) {
adminTabActive = t;
renderAdminPanel();
}
function renderAdminTab(stats) {
var el = document.getElementById('adminTabContent');
if (!el) return;
var t = adminTabActive;
if (t === 'overview') renderAdminOverview(stats, el);
else if (t === 'users') renderAdminUsers(stats.allUsers, el);
else if (t === 'orders') renderAdminOrders(el);
else if (t === 'products') renderAdminProducts(el);
else if (t === 'returns') renderAdminReturns(el);
else if (t === 'sellers') renderAdminSellers(stats.allUsers, el);
else if (t === 'analytics') renderAdminAnalytics(stats, el);
else if (t === 'settings') renderAdminSettings(el);
}
// ── OVERVIEW TAB ─────────────────────────────────────────────────
function renderAdminOverview(stats, el) {
var recentOrders = ORDERS_DB.slice().reverse().slice(0, 6);
var recentReturns = RETURNS_DB.slice().reverse().slice(0, 4);
var pendingRets = RETURNS_DB.filter(function(r) { return r.status === 'initiated'; });
el.innerHTML =
// Top row: Revenue + Order breakdown
''
// Revenue card
+ '
'
+ '
📈 Revenue Summary
'
+ adminMetricRow('Gross Revenue', '₹' + stats.totalRevenue.toLocaleString('en-IN'), 'var(--text)')
+ adminMetricRow('Platform Fee (10%)', '₹' + stats.platformFee.toLocaleString('en-IN'), 'var(--green)')
+ adminMetricRow('Seller Payouts (90%)', '₹' + (stats.totalRevenue - stats.platformFee).toLocaleString('en-IN'), 'var(--blue)')
+ '
'
+ adminMetricRow('Avg Order Value', stats.totalOrders ? '₹' + Math.round(stats.totalRevenue / stats.totalOrders).toLocaleString('en-IN') : '₹0', 'var(--yellow)')
+ adminMetricRow('Return Rate', stats.totalOrders ? (stats.totalReturns / stats.totalOrders * 100).toFixed(1) + '%' : '0%', stats.totalReturns / Math.max(stats.totalOrders, 1) > 0.15 ? 'var(--red)' : 'var(--green)')
+ '
'
// Order status breakdown
+ '
'
+ '
📩 Order Status
'
+ adminStatusBar('Processing', stats.processingOrders, stats.totalOrders, 'var(--yellow)')
+ adminStatusBar('Delivered', stats.deliveredOrders, stats.totalOrders, 'var(--green)')
+ adminStatusBar('Cancelled', stats.cancelledOrders, stats.totalOrders, 'var(--red)')
+ adminStatusBar('Returned', stats.totalReturns, stats.totalOrders, 'var(--blue)')
+ ''
+ '
'
// Alerts
+ (pendingRets.length ? ''
+ '
🔌
'
+ '
' + pendingRets.length + ' Return Request(s) Pending Approval
'
+ '
Review and approve/reject to process refunds.
'
+ '
'
+ '
' : '')
// Recent orders
+ ''
+ '
'
+ '
📩 Recent Orders
'
+ ''
+ ''
+ recentOrders.map(function(o) { return adminOrderRowCompact(o); }).join('')
+ '
'
// Recent returns
+ ''
+ '
'
+ '
🔄 Recent Returns
'
+ ''
+ ''
+ (recentReturns.length
? recentReturns.map(function(r) { return adminReturnRowCompact(r); }).join('')
: '
No returns yet.
')
+ '
';
}
function adminMetricRow(label, val, color) {
return ''
+ '' + label + ''
+ '' + val + ''
+ '
';
}
function adminStatusBar(label, count, total, color) {
var pct = total > 0 ? Math.round(count / total * 100) : 0;
return ''
+ '
'
+ '' + label + ''
+ '' + count + ' (' + pct + '%)'
+ '
'
+ '
';
}
function adminOrderRowCompact(o) {
return ''
+ '
'
+ '
#' + o.id + '
'
+ '
' + o.date + ' • ' + o.items.length + ' item(s) • ' + o.payment + '
'
+ '
'
+ '
' + o.status + ''
+ '
₹' + o.total.toLocaleString('en-IN') + ''
+ '
'
+ '
';
}
function adminReturnRowCompact(r) {
return ''
+ '
'
+ '
' + r.productName + '
'
+ '
Order #' + r.orderId + ' • ' + r.createdAt + ' • ' + r.reason + '
'
+ '
'
+ '
' + r.status + ''
+ '
₹' + r.refundAmt.toLocaleString('en-IN') + ''
+ (r.status === 'initiated'
? '
'
+ ''
+ ''
+ '
'
: '')
+ '
';
}
// ── USERS TAB ────────────────────────────────────────────────────
function renderAdminUsers(allUsers, el) {
var roleFilters = ['all', 'customer', 'seller', 'admin'];
var filtered = adminUserFilter === 'all'
? allUsers
: allUsers.filter(function(u) { return u.role === adminUserFilter; });
el.innerHTML =
''
+ '
'
+ '
👤 All Users (' + filtered.length + ')
'
+ '
'
+ roleFilters.map(function(r) {
var cnt = r === 'all' ? allUsers.length : allUsers.filter(function(u) { return u.role === r; }).length;
return '';
}).join('')
+ '
'
+ '
'
// Table header
+ '
'
+ 'NameEmailCityRoleActions'
+ '
'
+ (filtered.length
? filtered.map(function(u) { return adminUserRow(u); }).join('')
: '
No users in this category.
')
+ '
';
}
function adminUserRow(u) {
var roleColor = {customer:'var(--blue)', seller:'var(--green)', admin:'var(--red)'}[u.role] || 'var(--text2)';
return ''
+ '
' + esc(u.name) + ''
+ '
' + esc(u.email) + ''
+ '
' + esc(u.city || 'N/A') + ''
+ '
' + u.role.toUpperCase() + ''
+ '
'
+ (u.role !== 'admin' ? '' : 'Admin')
+ '
'
+ '
';
}
function adminToggleRole(email) {
var user = DEMO_USERS[email] || registeredUsers[email];
if (!user) return;
var newRole = user.role === 'seller' ? 'customer' : 'seller';
if (!confirm('Change ' + user.name + '\'s role to ' + newRole + '?')) return;
user.role = newRole;
if (registeredUsers[email]) { registeredUsers[email].role = newRole; saveUsers(); }
showToast(user.name + ' is now a ' + newRole, 'success');
renderAdminPanel();
}
// ── ORDERS TAB ───────────────────────────────────────────────────
function renderAdminOrders(el) {
var statusFilters = ['all', 'processing', 'confirmed', 'shipped', 'delivered', 'cancelled'];
var filtered = adminOrdFilter === 'all'
? ORDERS_DB
: ORDERS_DB.filter(function(o) { return o.status === adminOrdFilter; });
var sorted = filtered.slice().reverse();
el.innerHTML =
''
+ '
'
+ '
📩 All Orders (' + filtered.length + ')
'
+ '
'
+ statusFilters.map(function(s) {
var cnt = s === 'all' ? ORDERS_DB.length : ORDERS_DB.filter(function(o) { return o.status === s; }).length;
return '';
}).join('')
+ '
'
+ '
'
+ sorted.map(function(o) { return adminOrderCard(o); }).join('')
+ '
';
}
function adminOrderCard(o) {
return ''
+ '
'
+ '
'
+ '
#' + o.id + '
'
+ '
' + o.date + ' • ' + o.payment + ' • ' + esc(o.address.substring(0, 40)) + '...
'
+ '
'
+ '
'
+ '' + o.status.toUpperCase() + ''
+ '₹' + o.total.toLocaleString('en-IN') + ''
+ '
'
+ '
'
+ '
'
+ o.items.map(function(it) {
return '
'
+ '
' + it.icon + ''
+ '
' + esc(it.name) + '
'
+ '
x' + it.qty + (it.size ? ' • ' + it.size : '') + '
'
+ '
';
}).join('')
+ '
'
+ '
'
+ 'Update:'
+ ''
+ ''
+ (o.status === 'delivered' ? '' : '')
+ '
'
+ '
';
}
// ── PRODUCTS TAB ─────────────────────────────────────────────────
function renderAdminProducts(el) {
var catFilters = ['all', 'fashion', 'electronics', 'home', 'daily', 'handmade'];
var allProds = Object.values(PRODUCTS);
var filtered = adminProdFilter === 'all'
? allProds
: allProds.filter(function(p) { return p.cat === adminProdFilter; });
el.innerHTML =
''
+ '
'
+ '
📦 All Products (' + filtered.length + ')
'
+ '
'
+ catFilters.map(function(c) {
var cnt = c === 'all' ? allProds.length : allProds.filter(function(p) { return p.cat === c; }).length;
return '';
}).join('')
+ '
'
+ '
'
// Table header
+ '
'
+ 'IconProductCategoryPriceRatingReviewsActions'
+ '
'
+ filtered.map(function(p) { return adminProductRow(p); }).join('')
+ '
';
}
function adminProductRow(p) {
var disc = Math.round((1 - p.price / p.orig) * 100);
return ''
+ '
' + p.icon + ''
+ '
'
+ '
' + esc(p.name) + '
'
+ '
' + esc(p.brand) + (p.eco ? ' • ♻ Eco' : '') + '
'
+ '
'
+ '
' + p.cat + ''
+ '
₹' + p.price.toLocaleString('en-IN') + '
'
+ '
' + disc + '% off
'
+ '
' + (p.rating || 0) + ' ★'
+ '
' + (p.reviews || 0).toLocaleString('en-IN') + ''
+ '
'
+ ''
+ '
'
+ '
';
}
function adminToggleProduct(pid) {
var p = PRODUCTS[pid];
if (!p) return;
p.active = (p.active === false) ? true : false;
showToast('"' + p.name + '" ' + (p.active === false ? 'deactivated' : 'activated'), 'info');
renderAdminPanel();
}
// ── RETURNS TAB ──────────────────────────────────────────────────
function renderAdminReturns(el) {
var retFilters = ['all', 'initiated', 'approved', 'rejected'];
var filtered = adminRetFilter === 'all'
? RETURNS_DB
: RETURNS_DB.filter(function(r) { return r.status === adminRetFilter; });
el.innerHTML =
''
+ '
'
+ '
🔄 Return Requests (' + filtered.length + ')
'
+ '
'
+ retFilters.map(function(r) {
var cnt = r === 'all' ? RETURNS_DB.length : RETURNS_DB.filter(function(x) { return x.status === r; }).length;
return '';
}).join('')
+ '
'
+ '
'
+ (filtered.length
? filtered.slice().reverse().map(function(r) { return adminReturnCard(r); }).join('')
: '
🔄
No returns in this category.
')
+ '
';
}
function adminReturnCard(r) {
return ''
+ '
'
+ '
'
+ '
' + esc(r.productName) + '
'
+ '
Return ID: ' + r.id + ' • Order: #' + r.orderId + '
'
+ '
Initiated: ' + r.createdAt + ' • Reason: ' + esc(r.reason) + ' • Condition: ' + esc(r.condition) + '
'
+ (r.pickupDate ? '
🚚 Pickup scheduled: ' + r.pickupDate + '
' : '')
+ '
'
+ '
'
+ '
' + r.status.toUpperCase() + ''
+ '
₹' + r.refundAmt.toLocaleString('en-IN') + '
'
+ '
Refund amount
'
+ '
'
+ '
'
+ (r.status === 'initiated'
? '
'
+ ''
+ ''
+ ''
+ '
'
: '
Resolved: ' + (r.resolvedAt || 'N/A') + '
')
+ '
';
}
// ── SELLERS TAB ──────────────────────────────────────────────────
function renderAdminSellers(allUsers, el) {
var sellers = allUsers.filter(function(u) { return u.role === 'seller'; });
el.innerHTML =
''
+ '
'
+ '
🧑 Seller Accounts (' + sellers.length + ')
'
+ ''
+ (sellers.length
? sellers.map(function(u) { return adminSellerCard(u); }).join('')
: '
🧑
No seller accounts yet. Users can register as sellers from the Account page.
')
+ '
';
}
function adminSellerCard(u) {
var myProds = Object.values(PRODUCTS).filter(function(p) { return p.sellerId === u.id; });
var myOrds = ORDERS_DB.filter(function(o) {
var pIds = myProds.map(function(p) { return p.id; });
return o.items.some(function(it) { return pIds.indexOf(it.productId) > -1; });
});
var rev = myOrds.filter(function(o) { return o.status !== 'cancelled'; }).reduce(function(s, o) { return s + o.total; }, 0);
return ''
+ '
'
+ '
'
+ (u.name.charAt(0).toUpperCase())
+ '
'
+ '
'
+ '
' + esc(u.name) + '
'
+ '
' + esc(u.email) + ' • ' + esc(u.city || 'N/A') + '
'
+ (u.businessName ? '
Business: ' + esc(u.businessName) + '
' : '')
+ '
'
+ '
' + myProds.length + '
Products
'
+ '
' + myOrds.length + '
Orders
'
+ '
₹' + rev.toLocaleString('en-IN') + '
Revenue
'
+ '
'
+ '
'
+ '
'
+ ''
+ ''
+ '
'
+ '
'
+ '
';
}
function adminDemoteSeller(email) {
if (!confirm('Demote ' + email + ' to customer?')) return;
adminToggleRole(email);
}
// ── ANALYTICS TAB ────────────────────────────────────────────────
function renderAdminAnalytics(stats, el) {
var allProds = Object.values(PRODUCTS);
var catBreakdown = {};
allProds.forEach(function(p) {
if (!catBreakdown[p.cat]) catBreakdown[p.cat] = {count: 0, revenue: 0};
catBreakdown[p.cat].count++;
});
ORDERS_DB.filter(function(o) { return o.status !== 'cancelled'; }).forEach(function(o) {
o.items.forEach(function(it) {
var p = PRODUCTS[it.productId];
if (p) {
if (!catBreakdown[p.cat]) catBreakdown[p.cat] = {count: 0, revenue: 0};
catBreakdown[p.cat].revenue += it.price * it.qty;
}
});
});
var maxRev = Math.max.apply(null, Object.values(catBreakdown).map(function(c) { return c.revenue; })) || 1;
var catColors = {fashion:'var(--green)', electronics:'var(--blue)', home:'var(--yellow)', daily:'var(--orange)', handmade:'#a78bfa'};
el.innerHTML =
''
// Category revenue bar chart
+ '
'
+ '
📊 Revenue by Category
'
+ Object.entries(catBreakdown).map(function(entry) {
var cat = entry[0], data = entry[1];
var pct = Math.round(data.revenue / maxRev * 100);
var color = catColors[cat] || 'var(--text2)';
return '
'
+ '
'
+ '' + cat + ''
+ '₹' + data.revenue.toLocaleString('en-IN') + ''
+ '
'
+ '
'
+ '
' + data.count + ' products
'
+ '
';
}).join('')
+ '
'
// Key metrics
+ '
'
+ '
'
+ '
🏆 Top Products
'
+ allProds.sort(function(a, b) { return (b.reviews || 0) - (a.reviews || 0); }).slice(0, 5).map(function(p, i) {
return '
'
+ '
' + (i + 1) + ''
+ '
' + p.icon + ''
+ '
' + p.name + '
'
+ '
' + (p.reviews || 0).toLocaleString('en-IN') + ' reviews • ' + (p.rating || 0) + '★
'
+ '
₹' + p.price.toLocaleString('en-IN') + ''
+ '
';
}).join('')
+ '
'
+ '
'
+ '
📊 Platform Health
'
+ adminMetricRow('Eco Products Ratio', Math.round(stats.ecoProducts / Math.max(stats.totalProducts, 1) * 100) + '%', 'var(--green)')
+ adminMetricRow('Order Success Rate', stats.totalOrders ? Math.round((stats.totalOrders - stats.cancelledOrders) / stats.totalOrders * 100) + '%' : 'N/A', 'var(--blue)')
+ adminMetricRow('Return Approval Rate', RETURNS_DB.length ? Math.round(RETURNS_DB.filter(function(r) { return r.status === 'approved'; }).length / RETURNS_DB.length * 100) + '%' : 'N/A', 'var(--green)')
+ adminMetricRow('Avg Product Rating', (allProds.reduce(function(s, p) { return s + (p.rating || 0); }, 0) / Math.max(allProds.length, 1)).toFixed(1) + ' / 5.0', 'var(--yellow)')
+ ''
+ '
'
+ '
'
// Order timeline (monthly simulation)
+ ''
+ '
📅 Order Volume (Last 7 days)
'
+ '
'
+ [42, 58, 35, 71, 89, 63, ORDERS_DB.length * 12].map(function(v, i) {
var days = ['Mon','Tue','Wed','Thu','Fri','Sat','Today'];
var maxV = 120;
var pct = Math.round(v / maxV * 100);
return '
'
+ '
' + v + ''
+ '
'
+ '
' + days[i] + ''
+ '
';
}).join('')
+ '
'
+ '
';
}
// ── SETTINGS TAB ─────────────────────────────────────────────────
function renderAdminSettings(el) {
el.innerHTML =
''
// Platform settings
+ '
'
+ '
⚙ Platform Settings
'
+ settingRow('Platform Fee (%)', ' ')
+ settingRow('Free Delivery Above (₹)', ' ')
+ settingRow('Return Window (days)', ' ')
+ settingRow('Payout Cycle', '')
+ settingRow('New Seller Auto-approve', '')
+ settingRow('Maintenance Mode', '')
+ ''
// Notification settings
+ '
'
+ '
'
+ '
🔔 Notification Settings
'
+ '
'
+ notifToggle('New order alerts', true)
+ notifToggle('Return requests', true)
+ notifToggle('New seller registrations', true)
+ notifToggle('Low stock alerts', false)
+ notifToggle('Weekly revenue report', true)
+ '
'
+ '
'
+ '
'
+ '
⚒ Danger Zone
'
+ '
'
+ ''
+ ''
+ ''
+ '
'
+ '
'
+ '
'
+ '
';
}
function settingRow(label, control) {
return ''
+ '
' + label + ''
+ '
' + control + '
'
+ '
';
}
function notifToggle(label, on) {
return ''
+ '' + label + ''
+ ''
+ '
';
}
// ── ADMIN ACTION HELPERS ──────────────────────────────────────────
function adminUpdateOrderStatus(ordId, status) {
var ord = ORDERS_DB.find(function(o) { return o.id === ordId; });
if (!ord) return;
ord.status = status;
showToast('Order #' + ordId + ' → ' + status, 'success');
renderAdminPanel();
}
function adminApproveReturn(retId) {
var ret = RETURNS_DB.find(function(r) { return r.id === retId; });
if (!ret) return;
ret.status = 'approved';
ret.resolvedAt = new Date().toLocaleDateString('en-IN');
saveReturnsDB();
showToast('✓ Return approved. ₹' + ret.refundAmt.toLocaleString('en-IN') + ' refund processed!', 'success');
renderAdminPanel();
}
function adminRejectReturn(retId) {
var ret = RETURNS_DB.find(function(r) { return r.id === retId; });
if (!ret) return;
if (!confirm('Reject this return request for "' + ret.productName + '"?')) return;
ret.status = 'rejected';
ret.resolvedAt = new Date().toLocaleDateString('en-IN');
saveReturnsDB();
showToast('Return rejected for ' + ret.productName, 'info');
renderAdminPanel();
}
function adminClearOrders() {
if (!confirm('Clear ALL orders? This cannot be undone.')) return;
ORDERS_DB.splice(0, ORDERS_DB.length);
showToast('All orders cleared', 'info');
renderAdminPanel();
}
function adminResetReturns() {
if (!confirm('Reset ALL return requests? This cannot be undone.')) return;
RETURNS_DB.splice(0, RETURNS_DB.length);
saveReturnsDB();
showToast('All returns cleared', 'info');
renderAdminPanel();
}
function adminClearUsers() {
if (!confirm('Clear all registered users? Demo accounts will remain.')) return;
registeredUsers = {};
saveUsers();
if (currentUser && !DEMO_USERS[currentUser.email]) {
doLogout();
}
showToast('Registered users cleared', 'info');
renderAdminPanel();
}
// ════════════════════════════════════════════════════════════════
// INIT EXTENSION
// ════════════════════════════════════════════════════════════════
loadUsers();
loadReturnsDB();
updateHeaderAuth();
// ════════════════════════════════════════════════════════════════
// CHECKOUT FLOW — FULL MULTI-STEP
// ════════════════════════════════════════════════════════════════
var checkoutStep = 1; // 1=address 2=payment 3=review
var checkoutAddrId = null;
var checkoutPayment = 'UPI';
var checkoutPayRef = ''; // UPI id / card last4 etc
var checkoutGiftWrap = false;
var checkoutNote = '';
var lastPlacedOrder = null;
// UPI apps shown on payment step
var UPI_APPS = [
{id:'gpay', label:'Google Pay', icon:'📱', color:'#4285f4'},
{id:'phonepe',label:'PhonePe', icon:'📱', color:'#6739b7'},
{id:'paytm', label:'Paytm', icon:'📱', color:'#00baf2'},
{id:'bhim', label:'BHIM UPI', icon:'📱', color:'#00a651'},
];
// ── ENTRY POINT ──────────────────────────────────────────────────
function proceedToCheckout() {
if (!cartItems.length) {
showToast('Your cart is empty!', 'error');
return;
}
if (!currentUser) {
showToast('Please login to checkout', 'error');
setTimeout(function() { closeCart(); showPage('account'); }, 700);
return;
}
checkoutStep = 1;
checkoutAddrId = null;
lastPlacedOrder = null;
// Auto-select default address
var def = ADDRESSES_DB.find(function(a) {
return a.userId === currentUser.id && a.isDefault;
});
if (def) checkoutAddrId = def.id;
closeCart();
showPage('checkout');
}
// ── RENDER CHECKOUT PAGE ─────────────────────────────────────────
function renderCheckoutPage() {
// If order was just placed show confirmation
if (lastPlacedOrder) {
renderOrderConfirmation(lastPlacedOrder);
return;
}
renderCheckoutSteps();
renderCheckoutSummary();
if (checkoutStep === 1) renderAddressStep();
else if (checkoutStep === 2) renderPaymentStep();
else if (checkoutStep === 3) renderReviewStep();
}
// ── STEP INDICATOR ───────────────────────────────────────────────
function renderCheckoutSteps() {
var el = document.getElementById('checkoutStepsEl');
if (!el) return;
var steps = [
{n:1, label:'Address'},
{n:2, label:'Payment'},
{n:3, label:'Review'},
];
el.innerHTML = steps.map(function(s, i) {
var done = checkoutStep > s.n;
var active = checkoutStep === s.n;
var color = done ? 'var(--green)' : active ? 'var(--green)' : 'var(--border2)';
var txtClr = done || active ? '#000' : 'var(--text3)';
var bgClr = done || active ? 'var(--green)' : 'var(--bg4)';
return (i > 0 ? '' : '')
+ ''
+ '
'
+ (done ? '✓' : s.n)
+ '
'
+ '
' + s.label + ''
+ '
';
}).join('');
}
function goToStep(n) {
if (n < checkoutStep) { checkoutStep = n; renderCheckoutPage(); }
}
// ── ORDER SUMMARY SIDEBAR ────────────────────────────────────────
function renderCheckoutSummary() {
var el = document.getElementById('checkoutSummaryEl');
if (!el) return;
var sub = cartSubtotal();
var disc = cartDiscount();
var couponD = couponDiscount();
var deliv = deliveryFee();
var total = cartTotal();
var savings = disc + couponD;
el.innerHTML =
''
// Header
+ '
'
+ '
Order Summary
'
+ '' + cartCount() + ' item(s)'
+ ''
// Items
+ '
'
+ cartItems.map(function(ci) {
var p = PRODUCTS[ci.productId]; if (!p) return '';
var disc_pct = Math.round((1 - p.price / p.orig) * 100);
return '
'
+ '
' + p.icon + '
'
+ '
'
+ '
' + p.name + '
'
+ '
'
+ (ci.size ? ci.size + ' • ' : '') + 'Qty: ' + ci.qty
+ '
'
+ '
'
+ '
'
+ '
₹' + (p.price * ci.qty).toLocaleString('en-IN') + '
'
+ (disc_pct > 0 ? '
' + disc_pct + '% off
' : '')
+ '
'
+ '
';
}).join('')
+ '
'
// Price breakdown
+ '
'
+ sumRow('Subtotal', '₹' + sub.toLocaleString('en-IN'), 'var(--text)')
+ (disc > 0 ? sumRow('Product Discount', '-₹' + disc.toLocaleString('en-IN'), 'var(--green)') : '')
+ (couponD > 0 ? sumRow('Coupon (' + (appliedCoupon ? appliedCoupon.code : '') + ')', '-₹' + couponD.toLocaleString('en-IN'), 'var(--green)') : '')
+ sumRow('Delivery', deliv === 0 ? '
FREE 🎉' : '₹' + deliv, deliv === 0 ? 'var(--green)' : 'var(--text)')
+ (checkoutGiftWrap ? sumRow('Gift Wrap', '₹49', 'var(--text)') : '')
+ '
'
+ '
'
+ 'Total'
+ '₹' + (total + (checkoutGiftWrap ? 49 : 0)).toLocaleString('en-IN') + ''
+ '
'
+ (savings > 0
? '
You save ₹' + savings.toLocaleString('en-IN') + '! 🎉
'
: '')
+ '
'
+ '🔄 90-Day free returns on all items'
+ '
'
+ '
'
+ '
';
}
function sumRow(label, val, color) {
return ''
+ '' + label + ''
+ '' + val + ''
+ '
';
}
// ════════════════════════════════════════════════════════════════
// STEP 1 — ADDRESS
// ════════════════════════════════════════════════════════════════
var showAddNewAddr = false;
function renderAddressStep() {
var el = document.getElementById('checkoutStepContent');
if (!el) return;
var myAddrs = ADDRESSES_DB.filter(function(a) { return a.userId === currentUser.id; });
var addrsHtml = myAddrs.length
? myAddrs.map(function(a) {
var selected = checkoutAddrId === a.id;
return ''
+ '
'
+ '
'
+ (selected ? '✓' : '')
+ '
'
+ '
'
+ '
'
+ '' + esc(a.name) + ''
+ '' + esc(a.phone) + ''
+ (a.isDefault ? 'DEFAULT' : '')
+ '
'
+ '
' + esc(a.line1) + (a.line2 ? ', ' + esc(a.line2) : '') + '
'
+ '
' + esc(a.city) + ', ' + esc(a.state || '') + ' — ' + esc(a.pincode) + '
'
+ '
'
+ '
'
+ '
';
}).join('')
: ''
+ '
📍
'
+ '
No saved addresses
'
+ '
Add a delivery address to continue
'
+ '
';
el.innerHTML =
''
+ '
📍 Delivery Address
'
+ addrsHtml
// Add new address toggle
+ '
'
// Inline add-new form (hidden by default)
+ '
'
// Delivery options
+ '
'
+ '
Delivery Option
'
+ '
'
+ deliveryOption('std', '🚚 Standard Delivery', '3-5 business days', deliveryFee() === 0 ? 'FREE' : '₹49', true)
+ deliveryOption('exp', '⚡ Express Delivery', '1-2 business days', '₹99', false)
+ deliveryOption('same', '💢 Same Day Delivery', 'Order before 12 PM', '₹149', false)
+ '
'
+ '
'
// Gift wrap
+ '
'
+ '
'
+ '
🎁'
+ '
Gift Wrap
Beautiful eco-friendly packaging (+₹49)
'
+ '
'
+ '
'
+ '
'
// Order note
+ '
'
+ ''
+ ''
+ '
'
+ '
'
+ '
';
}
function deliveryOption(id, label, sub, price, checked) {
return '';
}
function toggleAddNewAddr() {
showAddNewAddr = !showAddNewAddr;
var el = document.getElementById('newAddrFormEl');
if (el) el.style.display = showAddNewAddr ? 'block' : 'none';
}
function selectAddr(id) {
checkoutAddrId = id;
renderAddressStep();
renderCheckoutSummary();
}
function saveNewAddrAndContinue() {
var name = (document.getElementById('ckAddrName') || {value:''}).value.trim();
var phone = (document.getElementById('ckAddrPhone') || {value:''}).value.trim();
var line1 = (document.getElementById('ckAddrLine1') || {value:''}).value.trim();
var line2 = (document.getElementById('ckAddrLine2') || {value:''}).value.trim();
var city = (document.getElementById('ckAddrCity') || {value:''}).value.trim();
var state = (document.getElementById('ckAddrState') || {value:''}).value.trim();
var pin = (document.getElementById('ckAddrPin') || {value:''}).value.trim();
var isDef = (document.getElementById('ckAddrDefault')|| {checked:false}).checked;
if (!name || !phone || !line1 || !city || !pin) {
showToast('Please fill all required fields *', 'error'); return;
}
if (!/^\d{6}$/.test(pin)) {
showToast('Enter a valid 6-digit pincode', 'error'); return;
}
if (isDef) ADDRESSES_DB.forEach(function(a) { if (a.userId === currentUser.id) a.isDefault = false; });
var newAddr = {
id: 'addr_' + Date.now(), userId: currentUser.id,
name: name, phone: phone, line1: line1, line2: line2,
city: city, state: state, pincode: pin, isDefault: isDef
};
ADDRESSES_DB.push(newAddr);
saveReturnsDB();
checkoutAddrId = newAddr.id;
showAddNewAddr = false;
showToast('Address saved!', 'success');
goToPayment();
}
function goToPayment() {
checkoutNote = (document.getElementById('orderNoteCk') || {value:''}).value;
if (!checkoutAddrId) {
showToast('Please select a delivery address', 'error'); return;
}
checkoutStep = 2;
renderCheckoutPage();
window.scrollTo(0, 0);
}
// ════════════════════════════════════════════════════════════════
// STEP 2 — PAYMENT
// ════════════════════════════════════════════════════════════════
var activePayMethod = 'UPI';
var selectedUpiApp = 'gpay';
function renderPaymentStep() {
var el = document.getElementById('checkoutStepContent');
if (!el) return;
el.innerHTML =
''
+ '
💸 Payment Method
'
// Payment method tabs
+ '
'
+ payMethodBtn('UPI', '📱', 'UPI')
+ payMethodBtn('Card', '💳', 'Card')
+ payMethodBtn('NB', '🏠', 'Net Banking')
+ payMethodBtn('COD', '💰', 'Cash on Delivery')
+ '
'
// UPI panel
+ '
'
+ '
Select UPI App
'
+ '
'
+ UPI_APPS.map(function(app) {
var sel = selectedUpiApp === app.id;
return '
'
+ '
' + app.icon + '
'
+ '
' + app.label + '
'
+ '
';
}).join('')
+ '
'
+ '
'
+ '
'
+ '
We will send a payment request to this UPI ID
'
+ '
'
+ '
'
// Card panel
+ '
'
+ '
'
+ ''
+ '
'
+ '
'
+ '
'
+ '🔒 Your card info is encrypted & secure'
+ '
'
+ '
'
// Net banking panel
+ '
'
+ '
Select Bank
'
+ '
'
+ ['SBI','HDFC','ICICI','Axis','Kotak','BOB','PNB','Yes Bank'].map(function(b) {
return '
' + b + '
';
}).join('')
+ '
'
+ '
You will be redirected to your bank\'s website to complete payment.
'
+ '
'
// COD panel
+ '
'
+ '
'
+ '
💰
'
+ '
Cash on Delivery
'
+ '
Pay in cash when your order arrives at your doorstep.
'
+ 'Please keep exact change ready. Our delivery partner will accept cash only.
'
+ '
'
+ '⚠ COD available on orders up to ₹5,000 only'
+ '
'
+ '
'
+ '
'
// Offers / saved cards section
+ '
'
+ '
🏷 Available Offers
'
+ '
'
+ offerChip('💳 Get 5% cashback on HDFC Credit Card', 'Max ₹250')
+ offerChip('📱 Extra ₹50 off with GPay or PhonePe', 'Min order ₹499')
+ offerChip('📚 No-cost EMI on orders above ₹999', 'Select cards only')
+ '
'
+ '
'
// Nav buttons
+ '
'
+ ''
+ ''
+ '
'
+ '
';
}
function payMethodBtn(id, icon, label) {
var active = activePayMethod === id;
return ''
+ '
' + icon + '
'
+ '
' + label + '
'
+ '
';
}
function offerChip(title, sub) {
return ''
+ '' + title + ''
+ '' + sub + ''
+ '
';
}
function switchPayMethod(id) {
activePayMethod = id;
checkoutPayment = id;
['UPI','Card','NB','COD'].forEach(function(m) {
var panel = document.getElementById('payPanel_' + m);
if (panel) panel.style.display = m === id ? 'block' : 'none';
});
// Update tab styles
document.querySelectorAll('[onclick^="switchPayMethod"]').forEach(function(btn) {
var m = btn.getAttribute('onclick').match(/'(\w+)'/)[1];
var active = m === id;
btn.style.background = active ? 'rgba(0,255,143,.1)' : 'var(--bg4)';
btn.style.borderColor = active ? 'var(--green)' : 'var(--border)';
btn.querySelector('div:last-child').style.color = active ? 'var(--green)' : 'var(--text2)';
});
}
function formatCardNum(inp) {
var v = inp.value.replace(/\D/g,'').substring(0,16);
inp.value = v.replace(/(.{4})/g,'$1 ').trim();
}
function formatExpiry(inp) {
var v = inp.value.replace(/\D/g,'').substring(0,4);
if (v.length >= 2) v = v.substring(0,2) + '/' + v.substring(2);
inp.value = v;
}
function goToReview() {
// Collect payment ref
if (activePayMethod === 'UPI') {
checkoutPayRef = (document.getElementById('upiIdInput')||{value:''}).value.trim();
if (!checkoutPayRef) { showToast('Please enter your UPI ID', 'error'); return; }
} else if (activePayMethod === 'Card') {
var cn = (document.getElementById('cardNum')||{value:''}).value.replace(/\s/g,'');
if (cn.length < 16) { showToast('Please enter a valid card number', 'error'); return; }
checkoutPayRef = '****' + cn.slice(-4);
}
checkoutPayment = activePayMethod;
checkoutStep = 3;
renderCheckoutPage();
window.scrollTo(0, 0);
}
// ════════════════════════════════════════════════════════════════
// STEP 3 — REVIEW & CONFIRM
// ════════════════════════════════════════════════════════════════
function renderReviewStep() {
var el = document.getElementById('checkoutStepContent');
if (!el) return;
var addr = ADDRESSES_DB.find(function(a) { return a.id === checkoutAddrId; });
var sub = cartSubtotal();
var disc = cartDiscount();
var couponD = couponDiscount();
var deliv = deliveryFee();
var total = cartTotal() + (checkoutGiftWrap ? 49 : 0);
var payLabel = {
UPI: '📱 UPI — ' + (checkoutPayRef || 'Selected app'),
Card: '💳 Card — ' + (checkoutPayRef || 'Saved card'),
NB: '🏠 Net Banking — ' + (checkoutPayRef || 'Selected bank'),
COD: '💰 Cash on Delivery'
}[checkoutPayment] || checkoutPayment;
el.innerHTML =
// Delivery address review
''
+ '
'
+ '
📍 DELIVER TO
'
+ ''
+ ''
+ (addr
? '
' + esc(addr.name) + ' ' + esc(addr.phone) + '
'
+ '
' + esc(addr.line1) + (addr.line2 ? ', ' + esc(addr.line2) : '') + '
'
+ '
' + esc(addr.city) + ', ' + esc(addr.state || '') + ' — ' + esc(addr.pincode) + '
'
: '
No address selected
')
+ '
'
// Payment review
+ ''
+ '
'
+ '
💸 PAYMENT
'
+ ''
+ ''
+ '
' + payLabel + '
'
+ '
'
// Items review
+ ''
+ '
📦 ORDER ITEMS (' + cartCount() + ')
'
+ cartItems.map(function(ci) {
var p = PRODUCTS[ci.productId]; if (!p) return '';
return '
'
+ '
' + p.icon + '
'
+ '
'
+ '
' + esc(p.name) + '
'
+ '
' + esc(p.brand) + (ci.size ? ' • ' + ci.size : '') + ' • Qty: ' + ci.qty + '
'
+ '
'
+ '
'
+ '
₹' + (p.price * ci.qty).toLocaleString('en-IN') + '
'
+ '
₹' + (p.orig * ci.qty).toLocaleString('en-IN') + '
'
+ '
'
+ '
';
}).join('')
+ '
'
// Price breakdown
+ ''
+ '
💰 PRICE DETAILS
'
+ sumRow('Price (' + cartCount() + ' items)', '₹' + sub.toLocaleString('en-IN'), 'var(--text)')
+ sumRow('Product Discount', '-₹' + disc.toLocaleString('en-IN'), 'var(--green)')
+ (couponD > 0 ? sumRow('Coupon Discount', '-₹' + couponD.toLocaleString('en-IN'), 'var(--green)') : '')
+ sumRow('Delivery Charges', deliv === 0 ? '
FREE' : '₹' + deliv, 'var(--text)')
+ (checkoutGiftWrap ? sumRow('Gift Wrap', '₹49', 'var(--text)') : '')
+ '
'
+ '
'
+ 'Total Amount'
+ '₹' + total.toLocaleString('en-IN') + ''
+ '
'
+ '
🎉 You will save ₹' + (disc + couponD).toLocaleString('en-IN') + ' on this order
'
+ '
'
// T&C + place order
+ ''
+ ''
+ ''
+ '
'
+ ''
+ ''
+ ''
+ '
';
}
// ════════════════════════════════════════════════════════════════
// PLACE ORDER
// ════════════════════════════════════════════════════════════════
function placeOrderFinal() {
if (!currentUser) return;
if (!checkoutAddrId) { showToast('Please select a delivery address', 'error'); goToStep(1); return; }
var tc = document.getElementById('tcCheck');
if (tc && !tc.checked) { showToast('Please accept the Terms & Conditions', 'error'); return; }
var btn = document.getElementById('placeOrderBtn');
if (btn) {
btn.disabled = true;
btn.innerHTML = '⏳ Placing Order...';
btn.style.opacity = '0.7';
}
var addr = ADDRESSES_DB.find(function(a) { return a.id === checkoutAddrId; });
var sub = cartSubtotal();
var disc = cartDiscount();
var couponD = couponDiscount();
var deliv = deliveryFee();
var total = cartTotal() + (checkoutGiftWrap ? 49 : 0);
// Simulate network delay
setTimeout(function() {
var orderId = 'ORD' + Date.now();
var order = {
id: orderId,
userId: currentUser.id,
items: cartItems.map(function(ci) {
var p = PRODUCTS[ci.productId];
return {
productId: ci.productId,
name: p ? p.name : 'Unknown',
icon: p ? p.icon : '📦',
price: p ? p.price : 0,
qty: ci.qty,
size: ci.size || null
};
}),
subtotal: sub,
discount: disc,
couponD: couponD,
giftWrap: checkoutGiftWrap ? 49 : 0,
delivery: deliv,
total: total,
status: 'processing',
address: addr ? (addr.line1 + ', ' + addr.city + ' - ' + addr.pincode) : '',
addrFull: addr || {},
payment: checkoutPayment,
payRef: checkoutPayRef,
note: checkoutNote,
couponCode:appliedCoupon ? appliedCoupon.code : null,
date: new Date().toLocaleDateString('en-IN'),
estimatedDelivery: getEstimatedDelivery()
};
ORDERS_DB.unshift(order);
lastPlacedOrder = order;
// Clear cart
cartItems = [];
appliedCoupon = null;
saveCartState();
updateCartBadge(false);
checkoutGiftWrap = false;
checkoutNote = '';
renderOrderConfirmation(order);
window.scrollTo(0, 0);
}, 1800);
}
function getEstimatedDelivery() {
var d = new Date();
d.setDate(d.getDate() + 4);
return d.toLocaleDateString('en-IN', {weekday:'long', day:'numeric', month:'long'});
}
// ════════════════════════════════════════════════════════════════
// ORDER CONFIRMATION SCREEN
// ════════════════════════════════════════════════════════════════
function renderOrderConfirmation(order) {
// Hide the step layout, show confirmation full width
var layout = document.getElementById('checkoutLayout');
var confirm = document.getElementById('orderConfirmationEl');
var steps = document.getElementById('checkoutStepsEl');
if (layout) layout.style.display = 'none';
if (steps) steps.style.display = 'none';
if (!confirm) return;
confirm.style.display = 'block';
var totalSaved = (order.discount || 0) + (order.couponD || 0);
confirm.innerHTML =
// Success animation block
''
+ '
✓
'
+ '
Order Placed!
'
+ '
Thank you for shopping with Hubooze! 🏭
'
+ '
'
+ 'Order ID: #' + order.id + ''
+ '
'
+ '
'
// Info cards row
+ ''
+ confirmInfoCard('🚚', 'Estimated Delivery', order.estimatedDelivery, 'var(--green)')
+ confirmInfoCard('💸', 'Payment', ({UPI:'UPI Payment',Card:'Card Payment',NB:'Net Banking',COD:'Cash on Delivery'}[order.payment]||order.payment), 'var(--blue)')
+ confirmInfoCard('🔄', 'Return Policy', '90-day free returns', 'var(--green)')
+ '
'
// Delivery address
+ ''
+ '
'
+ '
Delivering to
'
+ (order.addrFull && order.addrFull.name
? '
' + esc(order.addrFull.name) + '
'
+ '
' + esc(order.addrFull.line1 || '') + '
'
+ '
' + esc(order.addrFull.city || '') + ' — ' + esc(order.addrFull.pincode || '') + '
'
+ '
Phone: ' + esc(order.addrFull.phone || '') + '
'
: '
' + esc(order.address || '') + '
')
+ '
'
+ '
'
+ '
Price Paid
'
+ '
'
+ '
Subtotal₹' + order.subtotal.toLocaleString('en-IN') + '
'
+ (order.discount > 0 ? '
Discount-₹' + order.discount.toLocaleString('en-IN') + '
' : '')
+ (order.couponD > 0 ? '
Coupon-₹' + order.couponD.toLocaleString('en-IN') + '
' : '')
+ '
Delivery' + (order.delivery === 0 ? 'FREE' : '₹' + order.delivery) + '
'
+ '
'
+ '
Total₹' + order.total.toLocaleString('en-IN') + '
'
+ (totalSaved > 0 ? '
Saved ₹' + totalSaved.toLocaleString('en-IN') + '!
' : '')
+ '
'
+ '
'
+ '
'
// Items
+ ''
+ '
Items Ordered
'
+ order.items.map(function(it) {
return '
'
+ '
' + it.icon + '
'
+ '
'
+ '
' + esc(it.name) + '
'
+ '
' + (it.size ? it.size + ' • ' : '') + 'Qty: ' + it.qty + '
'
+ '
'
+ '
₹' + (it.price * it.qty).toLocaleString('en-IN') + '
'
+ '
';
}).join('')
+ (order.note ? '
📝 Note: ' + esc(order.note) + '
' : '')
+ '
'
// Tracking timeline
+ ''
+ '
Order Timeline
'
+ '
'
+ timelineStep('✓', 'Order Placed', new Date().toLocaleDateString('en-IN'), true, false)
+ timelineStep('●', 'Processing', 'Being prepared', false, false)
+ timelineStep('●', 'Shipped', 'In transit', false, false)
+ timelineStep('🏠', 'Out for Delivery', 'Almost there!', false, false)
+ timelineStep('🎉', 'Delivered', order.estimatedDelivery, false, true)
+ '
'
+ '
'
// Action buttons
+ ''
+ ''
+ ''
+ ''
+ '
';
// confetti-style animation
startConfetti();
}
function confirmInfoCard(icon, label, val, color) {
return ''
+ '
' + icon + '
'
+ '
' + label + '
'
+ '
' + val + '
'
+ '
';
}
function timelineStep(icon, label, sub, active, last) {
return ''
+ '
'
+ '
' + icon + '
'
+ (!last ? '
' : '')
+ '
'
+ '
'
+ '
' + label + '
'
+ '
' + sub + '
'
+ '
'
+ '
';
}
function continueAfterOrder() {
lastPlacedOrder = null;
checkoutStep = 1;
showPage('home');
}
// ── Simple confetti ──────────────────────────────────────────────
function startConfetti() {
var colors = ['#00ff8f','#00a1ff','#ffc107','#ff4d4d','#ff6b35'];
for (var i = 0; i < 60; i++) {
(function(i) {
setTimeout(function() {
var d = document.createElement('div');
d.style.cssText = 'position:fixed;top:-10px;left:' + Math.random() * 100 + 'vw;'
+ 'width:' + (6 + Math.random() * 8) + 'px;height:' + (6 + Math.random() * 8) + 'px;'
+ 'background:' + colors[Math.floor(Math.random() * colors.length)] + ';'
+ 'border-radius:' + (Math.random() > .5 ? '50%' : '2px') + ';'
+ 'z-index:99999;pointer-events:none;'
+ 'animation:confettiFall ' + (1.5 + Math.random() * 2) + 's linear forwards';
document.body.appendChild(d);
setTimeout(function() { d.remove(); }, 4000);
}, i * 50);
})(i);
}
}
// ── Patch showPage to handle checkout ───────────────────────────
// showPage is fully defined above
// ── Patch openCheckoutModal to redirect to full page ────────────
function openCheckoutModal() { proceedToCheckout(); }
function closeCheckoutModal() { } // no-op (we use full page now)
function placeOrder() { placeOrderFinal(); } // backward compat alias
// ════════════════════════════════════════════════════════════════
// NOTIFICATION SYSTEM — Full Featured
// WhatsApp simulator · Email previewer · OTP login · Bell panel
// ════════════════════════════════════════════════════════════════
// ── DATA STORE ───────────────────────────────────────────────────
var NOTIFS_DB = []; // all notifications for current user
var notifTabActive = 'all';
// ── NOTIFICATION PREFERENCES (default all on) ────────────────────
var NOTIF_PREFS = {
order_placed_wa: true,
order_placed_email: true,
order_shipped_wa: true,
order_shipped_email:false,
order_delivered_wa: true,
order_delivered_email:true,
return_initiated_wa:true,
return_initiated_email:true,
return_approved_wa: true,
return_approved_email:true,
promo_wa: false,
promo_email: true,
otp_wa: true,
otp_sms: true,
login_alert_email: true,
};
function loadNotifPrefs() {
try {
var saved = localStorage.getItem('hb_notif_prefs');
if (saved) NOTIF_PREFS = Object.assign({}, NOTIF_PREFS, JSON.parse(saved));
var savedNotifs = localStorage.getItem('hb_notifs');
if (savedNotifs) NOTIFS_DB = JSON.parse(savedNotifs);
} catch(e) {}
}
function saveNotifPrefs() {
localStorage.setItem('hb_notif_prefs', JSON.stringify(NOTIF_PREFS));
localStorage.setItem('hb_notifs', JSON.stringify(NOTIFS_DB.slice(0, 100)));
}
// ── CORE: ADD NOTIFICATION ────────────────────────────────────────
function addNotification(type, title, body, channels, data) {
var notif = {
id: 'n_' + Date.now() + '_' + Math.random().toString(36).slice(2,6),
type: type, // order|return|promo|system|otp
title: title,
body: body,
channels: channels || ['push'], // wa|email|sms|push
data: data || {},
read: false,
time: new Date().toLocaleTimeString('en-IN', {hour:'2-digit', minute:'2-digit'}),
date: new Date().toLocaleDateString('en-IN'),
ts: Date.now()
};
NOTIFS_DB.unshift(notif);
saveNotifPrefs();
// Update bell badge
updateNotifBadge();
// Show channels
channels.forEach(function(ch) {
if (ch === 'wa' && (data.waMsg || body)) sendWaNotification(notif);
if (ch === 'email' && (data.emailHtml|| body)) queueEmailNotification(notif);
if (ch === 'push') showPushToast(notif);
});
return notif;
}
// ── BELL + PANEL ─────────────────────────────────────────────────
function updateNotifBadge() {
var unread = NOTIFS_DB.filter(function(n) { return !n.read; }).length;
var badge = document.getElementById('notifBadge');
var bellEl = document.getElementById('notifBellEl');
if (badge) {
badge.textContent = unread > 9 ? '9+' : unread;
badge.style.display= unread > 0 ? 'flex' : 'none';
}
if (bellEl && unread > 0) {
bellEl.classList.add('bell-shake');
setTimeout(function() { bellEl.classList.remove('bell-shake'); }, 600);
}
var label = document.getElementById('notifCountLabel');
if (label) label.textContent = unread + ' unread';
}
function openNotifPanel() {
renderNotifList();
document.getElementById('notifOverlay').className = 'cart-overlay open';
document.getElementById('notifPanel').className = 'notif-panel open';
}
function closeNotifPanel() {
document.getElementById('notifOverlay').className = 'cart-overlay';
document.getElementById('notifPanel').className = 'notif-panel';
}
function switchNotifTab(tab) {
notifTabActive = tab;
['all','order','return','promo'].forEach(function(t) {
var el = document.getElementById('ntab-' + t);
if (el) el.className = 'cart-tab' + (t === tab ? ' active' : '');
});
renderNotifList();
}
function renderNotifList() {
var el = document.getElementById('notifList');
if (!el) return;
var list = notifTabActive === 'all'
? NOTIFS_DB
: NOTIFS_DB.filter(function(n) { return n.type === notifTabActive; });
if (!list.length) {
el.innerHTML = ''
+ '
🔔
'
+ '
No notifications yet
';
return;
}
var typeIcons = {order:'📩', return:'🔄', promo:'🏷', system:'⚙', otp:'📱'};
var typeBgs = {order:'rgba(0,161,255,.12)', return:'rgba(0,255,143,.12)', promo:'rgba(255,107,53,.12)', system:'rgba(170,170,170,.12)', otp:'rgba(156,39,176,.12)'};
var typeColors= {order:'var(--blue)', return:'var(--green)', promo:'var(--orange)', system:'var(--text2)', otp:'#9c27b0'};
var chLabels = {wa:'WhatsApp', email:'Email', sms:'SMS', push:'Push'};
el.innerHTML = list.map(function(n) {
return ''
+ '
'
+ '
'
+ (typeIcons[n.type] || '🔔')
+ '
'
+ '
'
+ '
' + n.title + '
'
+ '
' + n.body + '
'
+ '
'
+ '
' + n.date + ' ' + n.time + '
'
+ n.channels.filter(function(c){return c!=='push';}).map(function(c){
return '
' + (chLabels[c]||c) + '';
}).join('')
+ '
'
+ '
'
+ (!n.read ? '
' : '')
+ '
'
+ '
';
}).join('');
}
function readNotif(id, data) {
var n = NOTIFS_DB.find(function(x) { return x.id === id; });
if (n) { n.read = true; saveNotifPrefs(); }
updateNotifBadge();
renderNotifList();
// Navigate based on type
if (data && data.orderId) { closeNotifPanel(); showPage('orders'); }
if (data && data.retId) { closeNotifPanel(); showPage('orders'); }
}
function markAllNotifsRead() {
NOTIFS_DB.forEach(function(n) { n.read = true; });
saveNotifPrefs();
updateNotifBadge();
renderNotifList();
showToast('All notifications marked as read', 'info');
}
// ── PUSH TOAST (in-app banner) ────────────────────────────────────
function showPushToast(notif) {
// Use a slightly different style from the main toast
showToast(notif.title + ' — ' + notif.body.substring(0,60) + (notif.body.length > 60 ? '...' : ''), 'info');
}
// ════════════════════════════════════════════════════════════════
// WHATSAPP SIMULATOR
// ════════════════════════════════════════════════════════════════
var WA_MSGS = [];
var waChatOpen = false;
var WA_BOT_REPLIES = {
'hi': 'Hello! Welcome to Hubooze Support 😊 How can I help you today?',
'hello': 'Hi there! 🎉 How can we help you?',
'order': 'I can help with your order! Please share your Order ID (e.g. ORD2501003) and I\'ll look it up.',
'return': 'For returns, you have 90 days from purchase. I\'ll initiate a free pickup for you. Which order would you like to return?',
'refund': 'Refunds are processed instantly once pickup is done. Credited to your original payment method within 24 hours.',
'delivery': 'Standard delivery takes 3-5 business days. Express (1-2 days) and Same-day options are also available at checkout.',
'cancel': 'To cancel an order, go to My Orders and click Cancel. Orders can be cancelled until they are shipped.',
'track': 'You can track your order at Hubooze.in > My Orders. Live tracking will be available once shipped.',
'help': 'I can help with: orders, returns, refunds, delivery, cancellations, payments. What do you need?',
'payment': 'We accept UPI, Credit/Debit Cards, Net Banking, EMI and Cash on Delivery. All payments are secure.',
'default': 'Thank you for reaching out! Our team will respond within 1 hour. For urgent help, call 1800-XXX-XXXX (toll free).'
};
function sendWaNotification(notif) {
if (!NOTIF_PREFS.order_placed_wa && notif.type === 'order') return;
WA_MSGS.push({
from: 'hub',
text: notif.data.waMsg || formatWaMsg(notif),
time: notif.time
});
// If chat is open, refresh
if (waChatOpen) renderWaMsgs();
}
function formatWaMsg(notif) {
return '*Hubooze* 🛒\n' + notif.title + '\n\n' + notif.body
+ (notif.data.orderId ? '\n\n📩 Order: #' + notif.data.orderId : '')
+ '\n\n_Reply HELP for support_';
}
function openWaChat() {
waChatOpen = true;
document.getElementById('waChatOverlay').className = 'wa-chat-overlay open';
if (!WA_MSGS.length) {
WA_MSGS.push({
from: 'hub',
text: 'Hello ' + (currentUser ? currentUser.name.split(' ')[0] : '') + '! 👋\nWelcome to Hubooze WhatsApp Support.\n\nReply with:\n📩 *ORDER* — Track order\n🔄 *RETURN* — Initiate return\n💰 *REFUND* — Refund status\n❓ *HELP* — All options',
time: new Date().toLocaleTimeString('en-IN', {hour:'2-digit', minute:'2-digit'})
});
}
renderWaMsgs();
}
function closeWaChat() {
waChatOpen = false;
document.getElementById('waChatOverlay').className = 'wa-chat-overlay';
}
function renderWaMsgs() {
var el = document.getElementById('waMsgs');
if (!el) return;
el.innerHTML = WA_MSGS.map(function(m) {
return ''
+ m.text.replace(/\*(.*?)\*/g, '
$1').replace(/\n/g, '
')
+ '
' + m.time + (m.from === 'user' ? ' ✓✓' : '') + '
'
+ '
';
}).join('');
// Scroll to bottom
el.scrollTop = el.scrollHeight;
}
function sendWaMsg() {
var inp = document.getElementById('waInput');
if (!inp || !inp.value.trim()) return;
var text = inp.value.trim();
var time = new Date().toLocaleTimeString('en-IN', {hour:'2-digit', minute:'2-digit'});
WA_MSGS.push({from:'user', text:text, time:time});
inp.value = '';
renderWaMsgs();
// Show typing indicator then bot reply
var el = document.getElementById('waMsgs');
if (el) {
var typing = document.createElement('div');
typing.className = 'wa-typing';
typing.innerHTML = '';
el.appendChild(typing);
el.scrollTop = el.scrollHeight;
}
setTimeout(function() {
var lower = text.toLowerCase();
var reply = WA_BOT_REPLIES.default;
Object.keys(WA_BOT_REPLIES).forEach(function(key) {
if (lower.includes(key)) reply = WA_BOT_REPLIES[key];
});
// Remove typing indicator
var t = document.querySelector('.wa-typing');
if (t) t.remove();
WA_MSGS.push({
from: 'hub',
text: reply,
time: new Date().toLocaleTimeString('en-IN', {hour:'2-digit', minute:'2-digit'})
});
renderWaMsgs();
}, 1200 + Math.random() * 800);
}
// ════════════════════════════════════════════════════════════════
// EMAIL SIMULATOR
// ════════════════════════════════════════════════════════════════
function queueEmailNotification(notif) {
// Store for viewing — show preview badge
var el = document.getElementById('emailPreviewBadge');
if (el) { el.style.display = 'flex'; el.textContent = '1'; }
}
function showEmail(notif) {
var overlay = document.getElementById('emailOverlay');
if (!overlay) return;
var templates = {
order: buildOrderEmail(notif),
return: buildReturnEmail(notif),
promo: buildPromoEmail(notif),
system: buildSystemEmail(notif),
};
var html = templates[notif.type] || templates.system;
document.getElementById('emailContent').innerHTML =
''
+ ''
+ html
+ '
';
overlay.className = 'email-overlay open';
}
function closeEmail() {
document.getElementById('emailOverlay').className = 'email-overlay';
}
function buildOrderEmail(notif) {
var o = notif.data.order || {};
return ''
+ '

'
+ '
Hubooze — Shop. Return. Recycle.
'
+ '
'
+ ''
+ '
🎉 Order Confirmed!
'
+ '
Hi ' + (currentUser ? currentUser.name.split(' ')[0] : 'there') + ', your order has been placed successfully!
'
+ '
'
+ '
Order ID
'
+ '
#' + (o.id || notif.data.orderId || 'ORD' + Date.now()) + '
'
+ '
'
+ (o.items ? '
' + o.items.map(function(it){return '
' + it.icon + '' + it.name + '
Qty: ' + it.qty + '
₹' + (it.price * it.qty).toLocaleString('en-IN') + '
';}).join('') + '
' : '')
+ '
'
+ '
'
+ '
🔄 90-Day Free Returns
'
+ '
Not satisfied? Return in any condition — free pickup, instant refund.
'
+ '
'
+ ''
+ '
';
}
function buildReturnEmail(notif) {
return ''
+ '
Hubooze
'
+ '
Return & Refund Update
'
+ '
'
+ ''
+ '
🔄 ' + notif.title + '
'
+ '
' + notif.body + '
'
+ '
'
+ '
What happens next?
'
+ '
1. Our pickup partner will contact you within 24 hours
2. Free pickup from your doorstep
3. Refund processed instantly after pickup
'
+ '
'
+ '
'
+ ''
+ '
';
}
function buildPromoEmail(notif) {
return ''
+ '
Hubooze
'
+ '
' + notif.title + '
'
+ '
'
+ ''
+ '
' + (notif.data.emoji || '🏷') + '
'
+ '
' + notif.title + '
'
+ '
' + notif.body + '
'
+ (notif.data.coupon ? '
USE COUPON CODE
' + notif.data.coupon + '
' : '')
+ '
'
+ ''
+ '
';
}
function buildSystemEmail(notif) {
return ''
+ ''
+ '
' + notif.title + '
'
+ '
' + notif.body + '
'
+ ''
+ '
';
}
// ════════════════════════════════════════════════════════════════
// OTP LOGIN
// ════════════════════════════════════════════════════════════════
var otpValue = '';
var otpGenerated = '';
var otpTimerInt = null;
var otpContact = '';
var otpCallback = null;
function sendOTP(contact, callback) {
otpContact = contact;
otpCallback = callback;
// Generate 6-digit OTP
otpGenerated = String(Math.floor(100000 + Math.random() * 900000));
// Show notification
addNotification('otp', 'Your OTP is Ready', 'OTP: ' + otpGenerated + ' — valid for 5 minutes. Do not share.', ['wa','push'], {waMsg:'*Hubooze OTP*\n\nYour one-time password is: *' + otpGenerated + '*\n\nValid for 5 minutes.\n_Do not share this OTP with anyone._'});
// Render modal
var overlay = document.getElementById('otpOverlay');
var subtitle= document.getElementById('otpSubtitle');
if (subtitle) subtitle.textContent = 'OTP sent to ' + contact + ' via WhatsApp & SMS';
// Build 6 digit inputs
var inputsEl = document.getElementById('otpInputs');
if (inputsEl) {
inputsEl.innerHTML = '';
for (var i = 0; i < 6; i++) {
var inp = document.createElement('input');
inp.type = 'tel';
inp.maxLength = 1;
inp.className = 'otp-digit';
inp.id = 'otp_' + i;
inp.setAttribute('inputmode', 'numeric');
inp.setAttribute('pattern', '[0-9]');
(function(idx) {
inp.addEventListener('input', function(e) {
this.value = this.value.replace(/\D/g,'').slice(-1);
if (this.value) {
this.classList.add('filled');
var next = document.getElementById('otp_' + (idx + 1));
if (next) next.focus();
} else {
this.classList.remove('filled');
}
});
inp.addEventListener('keydown', function(e) {
if (e.key === 'Backspace' && !this.value) {
var prev = document.getElementById('otp_' + (idx - 1));
if (prev) { prev.value = ''; prev.classList.remove('filled'); prev.focus(); }
}
});
})(i);
inputsEl.appendChild(inp);
}
}
// Start timer
startOTPTimer();
if (overlay) overlay.className = 'otp-overlay open';
// Auto-hint: show OTP in WA chat
setTimeout(function() { openWaChat(); }, 500);
}
function startOTPTimer() {
if (otpTimerInt) clearInterval(otpTimerInt);
var secs = 30;
var cntEl = document.getElementById('otpCountdown');
var timerEl = document.getElementById('otpTimerEl');
otpTimerInt = setInterval(function() {
secs--;
if (cntEl) cntEl.textContent = secs;
if (secs <= 0) {
clearInterval(otpTimerInt);
if (timerEl) timerEl.innerHTML = '';
}
}, 1000);
}
function resendOTP() {
otpGenerated = String(Math.floor(100000 + Math.random() * 900000));
addNotification('otp', 'New OTP Sent', 'New OTP: ' + otpGenerated + ' — valid for 5 minutes.', ['wa','push'], {waMsg:'*Hubooze — New OTP*\n\nYour new OTP is: *' + otpGenerated + '*\n\nValid for 5 minutes.'});
// Clear inputs
for (var i = 0; i < 6; i++) {
var inp = document.getElementById('otp_' + i);
if (inp) { inp.value = ''; inp.classList.remove('filled'); }
}
document.getElementById('otp_0').focus();
var timerEl = document.getElementById('otpTimerEl');
if (timerEl) timerEl.innerHTML = 'Resend OTP in 30s';
startOTPTimer();
showToast('New OTP sent to ' + otpContact, 'success');
}
function verifyOTP() {
var entered = '';
for (var i = 0; i < 6; i++) {
var inp = document.getElementById('otp_' + i);
entered += inp ? (inp.value || '') : '';
}
if (entered.length < 6) { showToast('Please enter the complete 6-digit OTP', 'error'); return; }
if (entered === otpGenerated) {
closeOTPModal();
showToast('✓ OTP verified successfully!', 'success');
if (typeof otpCallback === 'function') otpCallback();
} else {
showToast('Incorrect OTP. Please try again.', 'error');
// Shake inputs
var inputsEl = document.getElementById('otpInputs');
if (inputsEl) {
inputsEl.style.animation = 'none';
inputsEl.style.transform = 'translateX(10px)';
setTimeout(function() { inputsEl.style.transform = 'translateX(-10px)'; }, 80);
setTimeout(function() { inputsEl.style.transform = 'translateX(0)'; }, 160);
}
}
}
function closeOTPModal() {
if (otpTimerInt) clearInterval(otpTimerInt);
var overlay = document.getElementById('otpOverlay');
if (overlay) overlay.className = 'otp-overlay';
}
// ════════════════════════════════════════════════════════════════
// NOTIFICATION SETTINGS PAGE
// ════════════════════════════════════════════════════════════════
function renderNotificationsPage() {
var el = document.getElementById('notifSettingsContent');
if (!el) return;
var sections = [
{
title: '📩 Order Notifications',
prefs: [
{key:'order_placed_wa', label:'Order Confirmed', sub:'Get WhatsApp message when order is placed', ch:'WhatsApp'},
{key:'order_placed_email', label:'Order Confirmed', sub:'Get email confirmation when order is placed', ch:'Email'},
{key:'order_shipped_wa', label:'Order Shipped', sub:'WhatsApp alert when your order is out for delivery', ch:'WhatsApp'},
{key:'order_shipped_email',label:'Order Shipped', sub:'Email when your order ships', ch:'Email'},
{key:'order_delivered_wa', label:'Order Delivered', sub:'WhatsApp when order reaches you', ch:'WhatsApp'},
{key:'order_delivered_email',label:'Order Delivered', sub:'Email delivery confirmation', ch:'Email'},
]
},
{
title: '🔄 Return & Refund Notifications',
prefs: [
{key:'return_initiated_wa', label:'Return Initiated', sub:'WhatsApp when return is initiated', ch:'WhatsApp'},
{key:'return_initiated_email',label:'Return Initiated', sub:'Email confirmation of return request', ch:'Email'},
{key:'return_approved_wa', label:'Refund Processed', sub:'WhatsApp when refund is credited', ch:'WhatsApp'},
{key:'return_approved_email', label:'Refund Processed', sub:'Email when refund is credited to account', ch:'Email'},
]
},
{
title: '🏷 Promotions & Offers',
prefs: [
{key:'promo_wa', label:'Deals & Offers', sub:'WhatsApp alerts for flash sales and coupons', ch:'WhatsApp'},
{key:'promo_email', label:'Newsletter', sub:'Weekly email digest of best deals', ch:'Email'},
]
},
{
title: '🔒 Security',
prefs: [
{key:'otp_wa', label:'OTP via WhatsApp', sub:'Receive login OTPs on WhatsApp', ch:'WhatsApp'},
{key:'otp_sms', label:'OTP via SMS', sub:'Receive login OTPs via SMS', ch:'SMS'},
{key:'login_alert_email',label:'Login Alert', sub:'Email when a new device logs in', ch:'Email'},
]
}
];
// Notification history section
var histHtml = ''
+ '
'
+ '
📋 Recent Notifications
'
+ ''
+ ''
+ (NOTIFS_DB.length
? NOTIFS_DB.slice(0,8).map(function(n) {
var typeIcons = {order:'📩',return:'🔄',promo:'🏷',system:'⚙',otp:'📱'};
return '
'
+ '
' + (typeIcons[n.type]||'🔔') + '
'
+ '
'
+ '
' + n.title + '
'
+ '
' + n.body.substring(0,80) + (n.body.length>80?'...':'') + '
'
+ '
' + n.date + ' ' + n.time + '
'
+ '
'
+ (n.channels.includes('email') ? '
' : '')
+ '
';
}).join('')
: '
No notifications yet
')
+ '
';
// Test section
var testHtml = ''
+ '
⚡ Test Notifications
'
+ '
'
+ ''
+ ''
+ ''
+ ''
+ '
'
+ '
';
var prefsHtml = sections.map(function(section) {
return ''
+ '
' + section.title + '
'
+ section.prefs.map(function(p) {
var isOn = NOTIF_PREFS[p.key] !== false;
return '
'
+ '
'
+ '
' + p.label + ' ' + p.ch + '
'
+ '
' + p.sub + '
'
+ '
'
+ '
'
+ '
';
}).join('')
+ '
';
}).join('');
el.innerHTML = histHtml + testHtml + prefsHtml;
}
// ── TEST TRIGGERS ─────────────────────────────────────────────────
function triggerTestNotif(type) {
var templates = {
order: {
title: 'Order Confirmed! 🎉',
body: 'Your order #ORD_TEST has been placed. Estimated delivery: ' + getEstimatedDelivery() + '.',
channels: ['wa','email','push'],
data: {orderId:'ORD_TEST', waMsg:'*Hubooze* 🛒\n\n*Order Confirmed!* 🎉\n\nOrder #ORD_TEST placed successfully.\nEstimated delivery: ' + getEstimatedDelivery() + '\n\n_Reply TRACK to track your order_', emailHtml: true}
},
return: {
title: 'Return Initiated 🔄',
body: 'Your return for "Test Product" has been initiated. Free pickup scheduled within 24 hours.',
channels: ['wa','email','push'],
data: {retId:'RET_TEST', waMsg:'*Hubooze* 🔄\n\n*Return Initiated*\n\nFree pickup scheduled for tomorrow.\nRefund ₹299 will be credited after pickup.\n\n_Reply RETURN for help_', emailHtml: true}
},
promo: {
title: '⚡ Flash Sale — 70% OFF!',
body: 'Limited time! Use code HUBOOZE for ₹100 off on orders above ₹499.',
channels: ['email','push'],
data: {coupon:'HUBOOZE', emoji:'⚡', emailHtml: true}
}
};
var t = templates[type];
if (!t) return;
var notif = addNotification(type, t.title, t.body, t.channels, t.data);
if (t.channels.includes('email')) setTimeout(function() { showEmail(notif); }, 300);
}
function triggerTestOTP() {
var contact = currentUser ? (currentUser.phone || currentUser.email) : '+91 9999999999';
sendOTP(contact, function() {
showToast('OTP verified! Login successful.', 'success');
});
}
// ════════════════════════════════════════════════════════════════
// HOOKS INTO EXISTING EVENTS
// ════════════════════════════════════════════════════════════════
// ── After login ──────────────────────────────────────────────────
var _origLoginSuccess = loginSuccess;
function loginSuccess(user) {
_origLoginSuccess(user);
// Login alert email
if (NOTIF_PREFS.login_alert_email) {
addNotification('system',
'New Login Detected',
'Someone logged into your Hubooze account at ' + new Date().toLocaleString('en-IN') + '. If this was you, no action needed.',
['email'],
{emailHtml: true}
);
}
// Welcome push
addNotification('system',
'Welcome back, ' + user.name.split(' ')[0] + '! 👋',
'You have ' + ORDERS_DB.filter(function(o){return o.userId===user.id;}).length + ' orders. Check your order status anytime.',
['push'], {}
);
// Load saved notifications
loadNotifPrefs();
updateNotifBadge();
}
// ── After order placed ────────────────────────────────────────────
var _origPlaceOrderFinal = placeOrderFinal;
function placeOrderFinal() {
_origPlaceOrderFinal();
// Hook fires after order is added — watch for lastPlacedOrder
setTimeout(function() {
if (lastPlacedOrder) {
var o = lastPlacedOrder;
var channels = [];
if (NOTIF_PREFS.order_placed_wa) channels.push('wa');
if (NOTIF_PREFS.order_placed_email) channels.push('email');
channels.push('push');
addNotification('order',
'Order Confirmed! 🎉',
'Your order #' + o.id + ' for ₹' + o.total.toLocaleString('en-IN') + ' is confirmed. Delivery by ' + (o.estimatedDelivery || '3-5 days') + '.',
channels,
{
orderId: o.id,
order: o,
waMsg: '*Hubooze* 🛒\n\n*Order Confirmed!* 🎉\n\nOrder ID: *#' + o.id + '*\nAmount: ₹' + o.total.toLocaleString('en-IN') + '\nDelivery by: ' + (o.estimatedDelivery || '3-5 days') + '\n\n_Reply ORDER for tracking_',
emailHtml: true
}
);
}
}, 2000);
}
// ── After return submitted ────────────────────────────────────────
var _origSubmitReturn = submitReturn;
function submitReturn() {
_origSubmitReturn();
setTimeout(function() {
var latest = RETURNS_DB[RETURNS_DB.length - 1];
if (latest && (Date.now() - latest.ts < 3000 || !latest.ts)) {
var channels = [];
if (NOTIF_PREFS.return_initiated_wa) channels.push('wa');
if (NOTIF_PREFS.return_initiated_email) channels.push('email');
channels.push('push');
addNotification('return',
'Return Initiated 🔄',
'Return for "' + latest.productName + '" initiated. Free pickup within 24 hours. Refund ₹' + latest.refundAmt.toLocaleString('en-IN') + ' after pickup.',
channels,
{
retId: latest.id,
waMsg: '*Hubooze* 🔄\n\n*Return Initiated*\n\nProduct: ' + latest.productName + '\nRefund: ₹' + latest.refundAmt.toLocaleString('en-IN') + '\nPickup: Within 24 hours\n\n_Reply RETURN to track_',
emailHtml: true
}
);
}
}, 500);
}
// ── After return approved (admin/seller) ──────────────────────────
var _origAdminApproveReturn = adminApproveReturn;
function adminApproveReturn(retId) {
_origAdminApproveReturn(retId);
var ret = RETURNS_DB.find(function(r){ return r.id === retId; });
if (ret) {
var channels = [];
if (NOTIF_PREFS.return_approved_wa) channels.push('wa');
if (NOTIF_PREFS.return_approved_email) channels.push('email');
channels.push('push');
addNotification('return',
'Refund Processed! 💸',
'Your return for "' + ret.productName + '" is approved. ₹' + ret.refundAmt.toLocaleString('en-IN') + ' refund has been initiated to your account.',
channels,
{
retId: retId,
waMsg: '*Hubooze* 💸\n\n*Refund Processed!*\n\nReturn approved for: ' + ret.productName + '\nRefund: ₹' + ret.refundAmt.toLocaleString('en-IN') + '\nCredited to: original payment method\nTimeline: Within 24 hours\n\n🎉 Thank you for shopping with Hubooze!',
emailHtml: true
}
);
}
}
// ── Promo notification (daily/weekly simulation) ──────────────────
function sendPromoNotification() {
if (!NOTIF_PREFS.promo_email && !NOTIF_PREFS.promo_wa) return;
var promos = [
{title:'Flash Sale — 70% OFF! ⚡', body:'Limited time! Electronics, fashion & more.', coupon:'SAVE10', emoji:'⚡'},
{title:'Exclusive for You 🏷', body:'Use code HUBOOZE for ₹100 off on ₹499+', coupon:'HUBOOZE', emoji:'🏷'},
{title:'Weekend Special 😊', body:'Eco products at 20% extra off. Support rural artisans!', coupon:'ECO20', emoji:'🍺'},
];
var p = promos[Math.floor(Math.random() * promos.length)];
var channels = [];
if (NOTIF_PREFS.promo_wa) channels.push('wa');
if (NOTIF_PREFS.promo_email) channels.push('email');
channels.push('push');
addNotification('promo', p.title, p.body, channels, {coupon: p.coupon, emoji: p.emoji, emailHtml: true});
}
// ════════════════════════════════════════════════════════════════
// HEADER BELL INJECTION
// ════════════════════════════════════════════════════════════════
function injectNotifBell() {
var hdrRight = document.querySelector('.hdr-right');
if (!hdrRight || document.getElementById('notifBellEl')) return;
var bell = document.createElement('button');
bell.className = 'hdr-btn notif-bell';
bell.id = 'notifBellEl';
bell.onclick = openNotifPanel;
bell.innerHTML = '🔔'
+ '0';
// Insert before cart button
var cartBtn = hdrRight.querySelector('.cart-hbtn');
if (cartBtn) hdrRight.insertBefore(bell, cartBtn);
else hdrRight.appendChild(bell);
}
// ── WA FAB → open chat ────────────────────────────────────────────
document.addEventListener('DOMContentLoaded', function() {
var waFab = document.querySelector('.wa-fab');
if (waFab) waFab.onclick = openWaChat;
});
// ════════════════════════════════════════════════════════════════
// PAGE: NOTIFICATIONS (settings)
// ════════════════════════════════════════════════════════════════
// showPage notification dispatch handled in main showPage function
// ════════════════════════════════════════════════════════════════
// INIT
// ════════════════════════════════════════════════════════════════
function initNotifications() {
loadNotifPrefs();
injectNotifBell();
updateNotifBadge();
// Seed a welcome notification for demo
if (NOTIFS_DB.length === 0) {
addNotification('promo',
'Welcome to Hubooze! 🎉',
'Get 15% off your first order with code FIRST. Shop 50,000+ products with 90-day returns.',
['push','email'],
{coupon:'FIRST', emoji:'🎉', emailHtml:true}
);
setTimeout(function() {
addNotification('promo',
'⚡ Flash Sale — Today Only!',
'Electronics at 70% off. Use code SAVE10 for an extra 10% off.',
['push'],
{coupon:'SAVE10', emoji:'⚡'}
);
}, 2000);
}
// Send a promo every 90 seconds (demo only)
setInterval(sendPromoNotification, 90000);
}
// ── Pre-wake server on page load ────────────────────────────────
(function() {
setTimeout(function() {
fetch('/api/health').catch(function(){});
}, 1000);
})();
// ── Secret Admin Access ───────────────────────────────────────────
var _adminClickCount = 0;
var _adminClickTimer = null;
function secretAdminReveal() {
_adminClickCount++;
clearTimeout(_adminClickTimer);
_adminClickTimer = setTimeout(function() { _adminClickCount = 0; }, 800);
if (_adminClickCount >= 5) {
_adminClickCount = 0;
var card = document.getElementById('adminPortalCard');
if (card) {
card.style.display = 'flex';
card.style.flexDirection = 'column';
showToast('Admin portal unlocked', 'info');
}
}
}
initNotifications();
// ── Restore session from API token ────────────────────────────────
(function() {
var savedSession = localStorage.getItem('hb_session');
var token = localStorage.getItem('hb_token') || localStorage.getItem('token');
if (savedSession && !currentUser) {
try {
var u = JSON.parse(savedSession);
if (u && u.id) { currentUser = u; }
} catch(_) {}
}
// If token exists but no session, restore from API
if (token && !currentUser) {
fetch('/api/auth/me', {headers:{'Authorization':'Bearer '+token}})
.then(function(r){ return r.json(); })
.then(function(d){
if (d.user) {
currentUser = d.user;
localStorage.setItem('hb_session', JSON.stringify(d.user));
try { updateHeaderAuth(); } catch(e) {}
try { renderNav(); } catch(e) {}
}
}).catch(function(){});
}
if (token && window.api) { window.api.token = token; }
// Navigate to target page after login redirect
var afterLogin = localStorage.getItem('hb_after_login');
if (afterLogin) {
localStorage.removeItem('hb_after_login');
// Wait for session restore to complete before navigating
var navAttempts = 0;
var navInterval = setInterval(function() {
navAttempts++;
if (currentUser || navAttempts > 20) {
clearInterval(navInterval);
if (currentUser) {
try { showPage(afterLogin); } catch(e) {}
}
}
}, 100);
}
})();
// Service Worker disabled — prevents caching issues
// SW disabled
// SW disabled
// SW disabled
// SW disabled