Store owner sub-admin

Manage your approved store

Edit your store details, template, products/services/menu items, appearance and website order flow. You can manage only stores approved under your account.

Checking account…

Please wait while we check your store owner access.

`); w.document.close(); } function payoutHTML(s,t){ return `

Request payout number change

For security, store owners cannot directly change payout numbers. Enter the correct new payout number here; the main admin will review and update it.

Loading payout requests…

`; } function settingsHTML(s,t){ return `

Admin can still override marketplace visibility if needed.

Only this signed-in account can update this store dashboard. Other store owners cannot see it.

`; } function productRowHTML(product,t){ const id='pe-'+(++productCounter); return `
`; } function addProductEntryDashboard(){ const t=window.StoreTemplates.getTemplate(document.getElementById('f-storeType')?.value || currentStore()?.storeType || 'fashion'); document.getElementById('productEntries').insertAdjacentHTML('beforeend', productRowHTML({},t)); } function templateChangedInDashboard(){ const t=window.StoreTemplates.getTemplate(document.getElementById('f-storeType').value); document.getElementById('f-category').value = t.category; document.getElementById('f-coverColor').value = t.cover; document.getElementById('f-logoColor').value = t.accent; refreshPreview(); } function refreshPreview(){ const box=document.getElementById('storePreview'); if(!box) return; const coverImage=(document.getElementById('f-coverImage')||{}).value || ''; const coverColor=(document.getElementById('f-coverColor')||{}).value || '#111'; box.style.background = coverImage ? `url('${coverImage}') center/cover no-repeat` : coverColor; const logo=document.getElementById('previewLogo'); const name=document.getElementById('previewName'); if(logo){ logo.textContent=(document.getElementById('f-logo')||{}).value || initials((document.getElementById('f-name')||{}).value); logo.style.background=(document.getElementById('f-logoColor')||{}).value || '#c09444'; } if(name) name.textContent=(document.getElementById('f-name')||{}).value || ''; } function getOwnerPromoFromFields(){ const img=i=>(document.getElementById('pb-img'+i)||{}).value || ''; return { enabled: !!(document.getElementById('pb-enabled')||{}).checked, eyebrow:(document.getElementById('pb-eyebrow')||{}).value || '', title:(document.getElementById('pb-title')||{}).value || '', subtitle:(document.getElementById('pb-subtitle')||{}).value || '', buttonText:(document.getElementById('pb-buttonText')||{}).value || '', buttonUrl:(document.getElementById('pb-buttonUrl')||{}).value || '', bgColor:(document.getElementById('pb-bgColor')||{}).value || '', images:[img(1),img(2),img(3)].filter(Boolean) }; } function refreshOwnerPromoPreview(){ const target=document.getElementById('ownerPromoPreview'); if(target && window.VHPromoBanner) window.VHPromoBanner.renderInto(target, getOwnerPromoFromFields()); } ['pb-enabled','pb-eyebrow','pb-title','pb-subtitle','pb-buttonText','pb-buttonUrl','pb-bgColor','pb-img1','pb-img2','pb-img3'].forEach(id=>{ document.addEventListener('input', e=>{ if(e.target && e.target.id===id) refreshOwnerPromoPreview(); }); document.addEventListener('change', e=>{ if(e.target && e.target.id===id) refreshOwnerPromoPreview(); }); }); function ownerImageToDataUrl(file, maxW, maxH, quality){ return new Promise((resolve,reject)=>{ const reader=new FileReader(); reader.onerror=()=>reject(new Error('Could not read image.')); reader.onload=()=>{ const img=new Image(); img.onload=()=>{ const ratio=Math.min((maxW||1400)/img.width,(maxH||1400)/img.height,1); const canvas=document.createElement('canvas'); canvas.width=Math.round(img.width*ratio); canvas.height=Math.round(img.height*ratio); const ctx=canvas.getContext('2d'); ctx.drawImage(img,0,0,canvas.width,canvas.height); resolve(canvas.toDataURL('image/jpeg',quality||.78)); }; img.onerror=()=>reject(new Error('Unsupported image file.')); img.src=reader.result; }; reader.readAsDataURL(file); }); } async function uploadOwnerPromoImage(input,index){ const file=input.files && input.files[0]; if(!file) return; try{ showDashAlert('ok','Uploading promo image…'); const dataUrl=await ownerImageToDataUrl(file,1200,1200,.78); const res=await apiPost({ action:'uploadStoreImage', filename:file.name, role:'owner-promo', dataUrl }); if(!res.success) throw new Error(res.error || 'Upload failed'); const field=document.getElementById('pb-img'+index); if(field) field.value=res.url; refreshOwnerPromoPreview(); showDashAlert('ok','✅ Promo image uploaded. Remember to save changes.'); } catch(e){ showDashAlert('err','❌ Promo image upload failed: '+e.message); } input.value=''; } function clearOwnerPromoImage(index){ const field=document.getElementById('pb-img'+index); if(field) field.value=''; refreshOwnerPromoPreview(); } async function loadPayoutRequests(){ const u=user(); const s=currentStore(); const box=document.getElementById('payoutRequestsList'); if(!box || !u || !s) return; box.innerHTML='

Loading payout requests…

'; try{ const res=await apiGet({ action:'getMyPayoutChangeRequests', uid:u.uid, storeId:s.id }); const requests=(res && res.success && Array.isArray(res.requests)) ? res.requests : []; if(!requests.length){ box.innerHTML='

No payout change request yet.

'; return; } box.innerHTML='
'+requests.map(r=>`
${esc(r.newPayoutNumber || '')}

Requested: ${esc(r.requestedAt || '')}${r.reviewNote ? ' · Note: '+esc(r.reviewNote) : ''}

${esc(r.status || 'Pending')}
`).join('')+'
'; }catch(e){ box.innerHTML='

Could not load payout requests.

'; } } async function requestPayoutChange(){ const u=user(); const s=currentStore(); if(!u||!s) return; const newPayoutNumber=(document.getElementById('payout-new-number')?.value || '').trim(); const accountName=(document.getElementById('payout-account-name')?.value || '').trim(); const reason=(document.getElementById('payout-reason')?.value || '').trim(); if(!newPayoutNumber || newPayoutNumber.replace(/\D/g,'').length < 9){ showDashAlert('err','❌ Please enter the correct payout number before submitting.'); return; } try{ showDashAlert('ok','Submitting payout change request…'); const res=await apiPost({ action:'requestPayoutNumberChange', data:{ uid:u.uid, storeId:s.id, ownerName:u.name || s.ownerName || '', ownerEmail:u.email || s.ownerEmail || '', newPayoutNumber, accountName, reason }}); if(!res.success) throw new Error(res.error || 'Request failed'); showDashAlert('ok','✅ Payout number change request submitted. Admin will review and update it.'); ['payout-new-number','payout-account-name','payout-reason'].forEach(id=>{ const el=document.getElementById(id); if(el) el.value=''; }); loadPayoutRequests(); }catch(e){ showDashAlert('err','❌ '+e.message); } } function showDashAlert(type,msg){ const el=document.getElementById('dashAlert'); el.className='alert '+(type==='ok'?'ok':'err'); el.innerHTML=msg; } async function saveOwnerStore(){ const u=user(); const s=currentStore(); if(!u||!s) return; let products=[]; const productRows=Array.from(document.querySelectorAll('.product-row')); if(productRows.length){ products=productRows.map(row=>({ name: row.querySelector('.pe-name')?.value.trim() || '', price: parseFloat(row.querySelector('.pe-price')?.value) || 0, stockQty: Math.max(0, parseInt(row.querySelector('.pe-stock')?.value,10) || 0), category: row.querySelector('.pe-category')?.value || '', desc: row.querySelector('.pe-desc')?.value.trim() || '', image: row.querySelector('.pe-image')?.value.trim() || '' })).filter(p=>p.name); } else { products=(s.products||[]).map((p,i)=>({ ...p })); Array.from(document.querySelectorAll('.stock-row')).forEach(row=>{ const i=parseInt(row.dataset.index,10); if(products[i]){ products[i].stockQty=Math.max(0, parseInt(row.querySelector('.stock-qty')?.value,10) || 0); products[i].stockStatus=row.querySelector('.stock-status')?.value || stockStatus(products[i].stockQty); } }); } const store={ id:s.id, name:document.getElementById('f-name')?.value.trim() || s.name, storeType:document.getElementById('f-storeType')?.value || s.storeType || 'fashion', category:document.getElementById('f-category')?.value.trim() || s.category, location:document.getElementById('f-location')?.value.trim() || '', tagline:document.getElementById('f-tagline')?.value.trim() || '', businessHours:document.getElementById('f-hours')?.value.trim() || '', whatsapp:document.getElementById('f-whatsapp')?.value.trim() || '', instagram:(document.getElementById('f-instagram')?.value || '').trim().replace(/^@/,''), description:document.getElementById('f-description')?.value.trim() || '', logo:document.getElementById('f-logo')?.value.trim().toUpperCase() || initials(s.name), logoColor:document.getElementById('f-logoColor')?.value || '#c09444', coverImage:document.getElementById('f-coverImage')?.value.trim() || '', coverColor:document.getElementById('f-coverColor')?.value.trim() || '', active:(document.getElementById('f-active')?.value || 'true') === 'true', products, templateConfig:{ ...(s.templateConfig || {}), promoBanner:getOwnerPromoFromFields(), editedByOwner:true, updatedAt:new Date().toISOString() } }; try{ const res=await apiPost({ action:'ownerSaveStore', uid:u.uid, store }); if(!res.success) throw new Error(res.error || 'Save failed'); const idx=myStores.findIndex(x=>x.id===s.id); if(idx>=0) myStores[idx]={...myStores[idx],...store}; showDashAlert('ok','✅ Store saved successfully. Refresh the public store page to see the updates.'); renderDashboard(); }catch(e){ showDashAlert('err','❌ '+e.message); } } init();