{"id":81,"date":"2026-04-10T20:37:37","date_gmt":"2026-04-10T20:37:37","guid":{"rendered":"https:\/\/cashchat.se\/dir\/?page_id=81"},"modified":"2026-04-11T00:54:02","modified_gmt":"2026-04-11T00:54:02","slug":"ads","status":"publish","type":"page","link":"https:\/\/cashchat.se\/dir\/ads\/","title":{"rendered":"Ads"},"content":{"rendered":"\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\">\n  <title>Adase Serve | Admin + Email Broadcast &#038; User Export<\/title>\n  <!-- SheetJS for Excel export -->\n  <script src=\"https:\/\/cdn.sheetjs.com\/xlsx-0.20.2\/package\/dist\/xlsx.full.min.js\"><\/script>\n  <style>\n    * {\n      margin: 0;\n      padding: 0;\n      box-sizing: border-box;\n      font-family: 'Inter', system-ui, 'Segoe UI', Roboto, sans-serif;\n    }\n    body {\n      background: #f5f7fb;\n      color: #1a2634;\n      padding-bottom: 40px;\n    }\n    .container { max-width: 1280px; margin: 0 auto; padding: 0 20px; }\n    .navbar {\n      background: white;\n      box-shadow: 0 4px 12px rgba(0,0,0,0.03);\n      padding: 16px 0;\n      position: sticky;\n      top: 0;\n      z-index: 100;\n      background: rgba(255,255,255,0.96);\n    }\n    .nav-flex {\n      display: flex;\n      justify-content: space-between;\n      align-items: center;\n      flex-wrap: wrap;\n      gap: 12px;\n    }\n    .logo {\n      font-weight: 800;\n      font-size: 1.7rem;\n      background: linear-gradient(135deg, #1F3A8A, #2E5AFF);\n      -webkit-background-clip: text;\n      background-clip: text;\n      color: transparent;\n    }\n    .auth-buttons { display: flex; gap: 12px; flex-wrap: wrap; }\n    .btn-sm {\n      padding: 8px 20px;\n      border-radius: 40px;\n      font-weight: 600;\n      border: none;\n      cursor: pointer;\n      background: #f0f2f5;\n      transition: 0.2s;\n    }\n    .btn-primary {\n      background: #2E5AFF;\n      color: white;\n      box-shadow: 0 2px 6px rgba(46,90,255,0.2);\n    }\n    .btn-primary:hover { background: #1e44cc; transform: translateY(-1px);}\n    .btn-outline {\n      background: transparent;\n      border: 1px solid #2E5AFF;\n      color: #2E5AFF;\n    }\n    .card {\n      background: white;\n      border-radius: 28px;\n      padding: 24px;\n      margin-bottom: 24px;\n      box-shadow: 0 8px 20px rgba(0,0,0,0.02), 0 2px 6px rgba(0,0,0,0.05);\n      border: 1px solid #eef2f9;\n    }\n    .grid-2 {\n      display: grid;\n      grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));\n      gap: 24px;\n    }\n    .badge {\n      background: #eef3ff;\n      color: #1f3a8a;\n      padding: 4px 12px;\n      border-radius: 40px;\n      font-size: 0.75rem;\n      font-weight: 600;\n      display: inline-block;\n    }\n    input, select, textarea {\n      width: 100%;\n      padding: 12px 16px;\n      border-radius: 20px;\n      border: 1px solid #cfdfed;\n      background: white;\n      font-size: 0.9rem;\n      margin-top: 6px;\n      margin-bottom: 16px;\n    }\n    label { font-weight: 600; font-size: 0.85rem; }\n    hr { margin: 20px 0; border: 0; height: 1px; background: #eef2f9; }\n    .flex-between {\n      display: flex;\n      justify-content: space-between;\n      align-items: center;\n      flex-wrap: wrap;\n      gap: 12px;\n    }\n    table {\n      width: 100%;\n      border-collapse: collapse;\n      overflow-x: auto;\n      display: block;\n    }\n    th, td {\n      text-align: left;\n      padding: 12px 8px;\n      border-bottom: 1px solid #eef2f9;\n    }\n    .email-preview {\n      background: #f9f9ff;\n      border-radius: 20px;\n      padding: 16px;\n      font-size: 0.9rem;\n      font-family: monospace;\n      white-space: pre-wrap;\n    }\n    .spam-tips {\n      background: #fff7e5;\n      border-left: 4px solid #ffb347;\n      padding: 12px;\n      border-radius: 16px;\n      margin-top: 12px;\n    }\n    @media (max-width: 700px) {\n      .nav-flex { flex-direction: column; align-items: stretch; }\n      .auth-buttons { justify-content: center; }\n    }\n  <\/style>\n<\/head>\n<body>\n<div class=\"navbar\">\n  <div class=\"container nav-flex\">\n    <div class=\"logo\">\ud83d\udce2 Adase Serve<\/div>\n    <div id=\"globalUserArea\" class=\"auth-buttons\"><\/div>\n  <\/div>\n<\/div>\n<div class=\"container\" id=\"appMain\"><div id=\"dynamicPanel\" style=\"margin-top: 30px;\">Loading&#8230;<\/div><\/div>\n\n<script>\n  \/\/ ---------- DATA MODELS ----------\n  let users = [];\n  let ads = [];\n  let proofs = [];\n  let platformRevenue = 0;\n  let sessions = { currentUserId: null, currentUserRole: null };\n\n  function loadData() {\n    const storedUsers = localStorage.getItem('adase_users');\n    const storedAds = localStorage.getItem('adase_ads');\n    const storedProofs = localStorage.getItem('adase_proofs');\n    const storedPlatform = localStorage.getItem('adase_platformRevenue');\n    users = storedUsers ? JSON.parse(storedUsers) : [];\n    ads = storedAds ? JSON.parse(storedAds) : [];\n    proofs = storedProofs ? JSON.parse(storedProofs) : [];\n    platformRevenue = storedPlatform ? parseFloat(storedPlatform) : 0;\n\n    \/\/ Ensure admin exists with correct credentials: info@cashchat.se \/ justtrade81\n    const adminEmail = \"info@cashchat.se\";\n    const adminPass = \"justtrade81\";\n    const existingAdmin = users.find(u => u.email === adminEmail && u.role === 'admin');\n    if (!existingAdmin) {\n      \/\/ remove any other admin\n      users = users.filter(u => u.role !== 'admin');\n      users.push({\n        id: Date.now() + 1,\n        name: \"Super Admin\",\n        email: adminEmail,\n        password: adminPass,\n        role: \"admin\",\n        wallet: 0,\n        advertiserWallet: 0,\n        viewedAds: []\n      });\n    } else {\n      \/\/ ensure password is correct\n      existingAdmin.password = adminPass;\n      const idx = users.findIndex(u => u.id === existingAdmin.id);\n      if (idx !== -1) users[idx] = existingAdmin;\n    }\n\n    \/\/ seed demo data if needed (publishers and ads)\n    if (users.length === 1) { \/\/ only admin\n      users.push({ id: 1001, name: \"Demo Advertiser\", email: \"adv@demo.com\", password: \"adv123\", role: \"advertiser\", wallet: 50000, advertiserWallet: 50000, viewedAds: [] });\n      users.push({ id: 1002, name: \"Alice Viewer\", email: \"alice@view.com\", password: \"view123\", role: \"user\", wallet: 600, advertiserWallet: 0, viewedAds: [] });\n      users.push({ id: 1003, name: \"Bob Viewer\", email: \"bob@view.com\", password: \"view123\", role: \"user\", wallet: 1200, advertiserWallet: 0, viewedAds: [] });\n      if (ads.length === 0) {\n        ads.push({ id: 201, advertiserId: 1001, title: \"Cool Sneakers\", type: \"video\", mediaUrl: \"https:\/\/www.w3schools.com\/html\/mov_bbb.mp4\", dailyCostUGX: 2000, specialInstructions: \"Upload receipt screenshot\", activeUntil: Date.now() + 86400000, isActive: true });\n        ads.push({ id: 202, advertiserId: 1001, title: \"Radio Jingle\", type: \"audio\", mediaUrl: \"https:\/\/www.soundhelix.com\/examples\/mp3\/SoundHelix-Song-1.mp3\", dailyCostUGX: 2000, specialInstructions: \"Comment 'heard'\", activeUntil: Date.now() + 86400000, isActive: true });\n      }\n    }\n    saveAll();\n  }\n\n  function saveAll() {\n    localStorage.setItem('adase_users', JSON.stringify(users));\n    localStorage.setItem('adase_ads', JSON.stringify(ads));\n    localStorage.setItem('adase_proofs', JSON.stringify(proofs));\n    localStorage.setItem('adase_platformRevenue', platformRevenue);\n  }\n\n  function getCurrentUser() {\n    if (!sessions.currentUserId) return null;\n    return users.find(u => u.id === sessions.currentUserId);\n  }\n\n  function updatePlatformRevenue(amountChange) {\n    platformRevenue += amountChange;\n    if (platformRevenue < 0) platformRevenue = 0;\n    localStorage.setItem('adase_platformRevenue', platformRevenue);\n  }\n\n  \/\/ render router\n  function renderApp() {\n    if (!sessions.currentUserId) renderAuthScreen();\n    else {\n      const user = getCurrentUser();\n      if (user.role === 'advertiser') renderAdvertiserDashboard(user);\n      else if (user.role === 'user') renderUserDashboard(user);\n      else if (user.role === 'admin') renderAdminPanel(user);\n      else renderAuthScreen();\n    }\n    renderGlobalNav();\n  }\n\n  function renderGlobalNav() {\n    const area = document.getElementById('globalUserArea');\n    if (!sessions.currentUserId) {\n      area.innerHTML = `<button class=\"btn-sm\" id=\"showLoginBtn\">Login<\/button>\n                        <button class=\"btn-sm btn-primary\" id=\"showRegisterBtn\">Register<\/button>`;\n      document.getElementById('showLoginBtn')?.addEventListener('click', () => showLoginForm());\n      document.getElementById('showRegisterBtn')?.addEventListener('click', () => showRegisterForm());\n    } else {\n      const user = getCurrentUser();\n      area.innerHTML = `<span style=\"font-weight:500;\">\ud83d\udc4b ${user.name} (${user.role})<\/span>\n                        <button class=\"btn-sm\" id=\"logoutBtn\">Logout<\/button>`;\n      document.getElementById('logoutBtn')?.addEventListener('click', () => {\n        sessions.currentUserId = null;\n        sessions.currentUserRole = null;\n        renderApp();\n      });\n    }\n  }\n\n  function showLoginForm() {\n    const panel = document.getElementById('dynamicPanel');\n    panel.innerHTML = `\n      <div class=\"card\" style=\"max-width:500px; margin:auto;\">\n        <h2>Login to Adase<\/h2>\n        <label>Email<\/label><input type=\"email\" id=\"loginEmail\" placeholder=\"email\">\n        <label>Password<\/label><input type=\"password\" id=\"loginPass\">\n        <button class=\"btn-primary\" id=\"doLoginBtn\" style=\"width:100%\">Login<\/button>\n        <p style=\"margin-top:12px;\">Demo: advertiser@demo.com \/ adv123 | viewer@view.com \/ view123 | Admin: info@cashchat.se (hidden pwd)<\/p>\n      <\/div>`;\n    document.getElementById('doLoginBtn')?.addEventListener('click', () => {\n      const email = document.getElementById('loginEmail').value;\n      const pwd = document.getElementById('loginPass').value;\n      const user = users.find(u => u.email === email && u.password === pwd);\n      if (user) {\n        sessions.currentUserId = user.id;\n        sessions.currentUserRole = user.role;\n        renderApp();\n      } else alert(\"Invalid credentials\");\n    });\n  }\n\n  function showRegisterForm() {\n    const panel = document.getElementById('dynamicPanel');\n    panel.innerHTML = `\n      <div class=\"card\" style=\"max-width:500px; margin:auto;\">\n        <h2>Create Account<\/h2>\n        <label>Full name<\/label><input id=\"regName\">\n        <label>Email<\/label><input id=\"regEmail\">\n        <label>Password<\/label><input id=\"regPass\" type=\"password\">\n        <label>Role<\/label>\n        <select id=\"regRole\"><option value=\"user\">Viewer (earn points)<\/option><option value=\"advertiser\">Advertiser (run ads)<\/option><\/select>\n        <button class=\"btn-primary\" id=\"doRegBtn\" style=\"width:100%\">Sign up<\/button>\n      <\/div>`;\n    document.getElementById('doRegBtn')?.addEventListener('click', () => {\n      const name = document.getElementById('regName').value;\n      const email = document.getElementById('regEmail').value;\n      const pwd = document.getElementById('regPass').value;\n      const role = document.getElementById('regRole').value;\n      if (!name || !email || !pwd) return alert(\"Fill all fields\");\n      if (users.find(u => u.email === email)) return alert(\"Email exists\");\n      const newId = users.length + 1000;\n      users.push({ id: newId, name, email, password: pwd, role, wallet: (role === 'user' ? 0 : 0), advertiserWallet: (role === 'advertiser' ? 0 : 0), viewedAds: [] });\n      saveAll();\n      alert(\"Registration success! Please login.\");\n      showLoginForm();\n    });\n  }\n\n  function renderAuthScreen() {\n    const panel = document.getElementById('dynamicPanel');\n    panel.innerHTML = `<div class=\"card\" style=\"text-align:center;\"><h3>Welcome to Adase Serve<\/h3><p>Earn points by watching ads, or run campaigns as advertiser.<\/p><div style=\"margin-top:20px;\"><button class=\"btn-primary\" id=\"gotoLoginBtn\">Login \/ Register<\/button><\/div><\/div>`;\n    document.getElementById('gotoLoginBtn')?.addEventListener('click', () => showLoginForm());\n  }\n\n  \/\/ ---------- ADVERTISER DASHBOARD (short version, keep original features) ----------\n  function renderAdvertiserDashboard(user) {\n    const myAds = ads.filter(ad => ad.advertiserId === user.id);\n    const myProofs = proofs.filter(p => ads.find(a => a.id === p.adId)?.advertiserId === user.id);\n    const panel = document.getElementById('dynamicPanel');\n    panel.innerHTML = `\n      <div class=\"flex-between\"><h2>\ud83d\udce2 Advertiser Hub<\/h2><span class=\"badge\">Wallet: ${user.advertiserWallet.toFixed(0)} UGX<\/span><\/div>\n      <div class=\"grid-2\">\n        <div class=\"card\"><h3>\u2795 Create ad<\/h3><label>Title<\/label><input id=\"adTitle\"><label>Type<\/label><select id=\"adType\"><option>video<\/option><option>audio<\/option><option>banner<\/option><\/select><label>Media URL<\/label><input id=\"adMedia\"><label>Special instructions<\/label><textarea id=\"adInstructions\"><\/textarea><label>Daily cost<\/label><select id=\"dailyCostSelect\"><option value=\"2000\">UGX 2,000<\/option><option value=\"3800\">$1 (\u22483800 UGX)<\/option><\/select><button id=\"createAdBtn\" class=\"btn-primary\">Create Ad (inactive)<\/button><\/div>\n        <div class=\"card\"><h3>\ud83d\udcb0 Fund wallet<\/h3><label>Amount (UGX)<\/label><input id=\"fundAmount\" value=\"5000\"><label>Method<\/label><select id=\"payMethod\"><option>MTN<\/option><option>Airtel<\/option><option>Mpesa<\/option><option>Visa<\/option><option>PayPal<\/option><option>Google Pay<\/option><\/select><button id=\"fundWalletBtn\" class=\"btn-primary\">Add funds<\/button><hr><h4>My Ads<\/h4><div id=\"advertiserAdsList\"><\/div><\/div>\n      <\/div>\n      <div class=\"card\"><h3>\ud83d\udcce Proofs submitted<\/h3><div id=\"advertiserProofsList\"><\/div><\/div>\n    `;\n    document.getElementById('createAdBtn')?.addEventListener('click', () => {\n      const title = document.getElementById('adTitle').value;\n      const type = document.getElementById('adType').value;\n      const media = document.getElementById('adMedia').value;\n      const instructions = document.getElementById('adInstructions').value;\n      const costSelect = document.getElementById('dailyCostSelect').value;\n      const dailyCostUGX = costSelect === \"2000\" ? 2000 : 3800;\n      if (!title || !media) return alert(\"Title and Media URL required\");\n      ads.push({ id: Date.now(), advertiserId: user.id, title, type, mediaUrl: media, specialInstructions: instructions || null, dailyCostUGX, activeUntil: null, isActive: false });\n      saveAll();\n      alert(\"Ad created! Activate with daily payment.\");\n      renderAdvertiserDashboard(getCurrentUser());\n    });\n    document.getElementById('fundWalletBtn')?.addEventListener('click', () => {\n      let amount = parseFloat(document.getElementById('fundAmount').value);\n      if (amount > 0) {\n        user.advertiserWallet += amount;\n        const idx = users.findIndex(u => u.id === user.id);\n        users[idx].advertiserWallet = user.advertiserWallet;\n        saveAll();\n        alert(`Added ${amount} UGX.`);\n        renderAdvertiserDashboard(getCurrentUser());\n      } else alert(\"Invalid amount\");\n    });\n    const adsContainer = document.getElementById('advertiserAdsList');\n    if (myAds.length === 0) adsContainer.innerHTML = \"<p>No ads.<\/p>\";\n    else {\n      adsContainer.innerHTML = myAds.map(ad => `\n        <div style=\"border-bottom:1px solid #eef; margin-bottom:12px;\"><strong>${ad.title}<\/strong> (${ad.type}) - Daily: ${ad.dailyCostUGX} UGX<br>Status: ${ad.isActive && ad.activeUntil > Date.now() ? '\u2705 Active' : '\u26d4 Inactive'}<br>${(!ad.isActive || ad.activeUntil < Date.now()) ? `<button class=\"btn-sm btn-primary\" data-adid=\"${ad.id}\">Activate (${ad.dailyCostUGX} UGX)<\/button>` : `<span class=\"badge\">Active until ${new Date(ad.activeUntil).toLocaleString()}<\/span>`}<\/div>\n      `).join('');\n      document.querySelectorAll('[data-adid]').forEach(btn => {\n        btn.addEventListener('click', (e) => {\n          const adId = parseInt(btn.getAttribute('data-adid'));\n          const ad = ads.find(a => a.id === adId);\n          if (ad && user.advertiserWallet >= ad.dailyCostUGX) {\n            user.advertiserWallet -= ad.dailyCostUGX;\n            const idx = users.findIndex(u => u.id === user.id);\n            users[idx].advertiserWallet = user.advertiserWallet;\n            updatePlatformRevenue(ad.dailyCostUGX);\n            ad.isActive = true;\n            ad.activeUntil = Date.now() + 86400000;\n            saveAll();\n            alert(`Ad \"${ad.title}\" activated for 24h.`);\n            renderAdvertiserDashboard(getCurrentUser());\n          } else alert(\"Insufficient wallet.\");\n        });\n      });\n    }\n    const proofsDiv = document.getElementById('advertiserProofsList');\n    proofsDiv.innerHTML = myProofs.length ? myProofs.map(p => `<div class=\"proof-item\"><strong>Ad #${p.adId}<\/strong><br>${p.proofText}<br><small>${new Date(p.timestamp).toLocaleString()}<\/small><\/div>`).join('') : \"<p>No proofs.<\/p>\";\n  }\n\n  \/\/ ---------- USER (VIEWER) DASHBOARD (keep full) ----------\n  function renderUserDashboard(user) {\n    const activeAds = ads.filter(ad => ad.isActive && ad.activeUntil > Date.now());\n    const points = Math.floor(user.wallet \/ 3);\n    const canWithdraw = user.wallet >= 1500;\n    const panel = document.getElementById('dynamicPanel');\n    panel.innerHTML = `\n      <div class=\"flex-between\"><h2>\ud83c\udfac Watch & Earn<\/h2><span class=\"badge\">\ud83d\udcb0 Earnings: ${user.wallet.toFixed(0)} UGX (${points} points)<\/span><\/div>\n      <div class=\"card withdraw-card\"><div class=\"flex-between\"><span>\u2b50 1 point = UGX 3<\/span>${canWithdraw ? `<button id=\"withdrawBtn\" class=\"btn-primary\">Withdraw 500 points (1500 UGX)<\/button>` : `<span>Need 500+ points<\/span>`}<\/div><\/div>\n      <div class=\"card\"><h3>\ud83d\udcfa Active Ads<\/h3><div id=\"viewerAdsList\"><\/div><\/div>\n      <div class=\"card\"><h3>\ud83d\udce4 My proofs<\/h3><div id=\"viewerProofHistory\"><\/div><\/div>\n    `;\n    if (canWithdraw) document.getElementById('withdrawBtn')?.addEventListener('click', () => {\n      if (user.wallet >= 1500) {\n        user.wallet -= 1500;\n        const idx = users.findIndex(u => u.id === user.id);\n        users[idx].wallet = user.wallet;\n        updatePlatformRevenue(-1500);\n        saveAll();\n        alert(\"Withdrawal of 1500 UGX processed (simulated).\");\n        renderUserDashboard(getCurrentUser());\n      }\n    });\n    const adsContainer = document.getElementById('viewerAdsList');\n    if (activeAds.length === 0) adsContainer.innerHTML = \"<p>No active ads.<\/p>\";\n    else {\n      adsContainer.innerHTML = activeAds.map(ad => {\n        const viewed = user.viewedAds?.includes(ad.id);\n        return `<div style=\"border:1px solid #eef; border-radius:24px; padding:16px; margin-bottom:18px;\"><h4>${ad.title}<\/h4><div class=\"ad-media\">${ad.type==='video'?`<video controls src=\"${ad.mediaUrl}\" style=\"max-width:100%\"><\/video>`:ad.type==='audio'?`<audio controls src=\"${ad.mediaUrl}\"><\/audio>`:`<div><img decoding=\"async\" src=\"${ad.mediaUrl}\" style=\"max-width:100%; border-radius:12px;\"><\/div>`}<\/div>${ad.specialInstructions?`<p>\u26a0\ufe0f ${ad.specialInstructions}<\/p>`:''}${!viewed?`<button class=\"btn-sm btn-primary\" data-viewad=\"${ad.id}\">\u2705 Earn 3 UGX (1 point)<\/button>`:`<span class=\"badge\">\u2713 Claimed<\/span>`} ${ad.specialInstructions?`<button class=\"btn-sm btn-outline\" data-proofad=\"${ad.id}\">\ud83d\udcce Submit proof<\/button>`:''}<\/div>`;\n      }).join('');\n      document.querySelectorAll('[data-viewad]').forEach(btn => {\n        btn.addEventListener('click', (e) => {\n          const adId = parseInt(btn.getAttribute('data-viewad'));\n          const ad = ads.find(a => a.id === adId);\n          if (ad && !user.viewedAds.includes(adId)) {\n            user.viewedAds.push(adId);\n            user.wallet += 3;\n            const idx = users.findIndex(u => u.id === user.id);\n            users[idx].wallet = user.wallet;\n            users[idx].viewedAds = user.viewedAds;\n            saveAll();\n            alert(`+3 UGX earned for \"${ad.title}\"`);\n            renderUserDashboard(getCurrentUser());\n          } else alert(\"Already claimed.\");\n        });\n      });\n      document.querySelectorAll('[data-proofad]').forEach(btn => {\n        btn.addEventListener('click', (e) => {\n          const adId = parseInt(btn.getAttribute('data-proofad'));\n          const ad = ads.find(a => a.id === adId);\n          const proofText = prompt(`Proof for \"${ad.title}\":\\n${ad.specialInstructions || ''}\\nEnter details:`);\n          if (proofText?.trim()) {\n            proofs.push({ id: Date.now(), adId, userId: user.id, userName: user.name, proofText: proofText.trim(), timestamp: Date.now() });\n            saveAll();\n            alert(\"Proof submitted! Admin & advertiser can review.\");\n            renderUserDashboard(getCurrentUser());\n          }\n        });\n      });\n    }\n    const historyDiv = document.getElementById('viewerProofHistory');\n    const userProofs = proofs.filter(p => p.userId === user.id);\n    historyDiv.innerHTML = userProofs.length ? userProofs.map(p => `<div class=\"proof-item\"><strong>Ad #${p.adId}<\/strong><br>${p.proofText}<\/div>`).join('') : \"<p>No proofs sent.<\/p>\";\n  }\n\n  \/\/ ---------- ENHANCED ADMIN PANEL (Users list, export, email broadcast, CSV upload) ----------\n  function renderAdminPanel(admin) {\n    const allUsers = users.filter(u => u.role !== 'admin');\n    const totalPlatform = platformRevenue;\n    const allProofs = [...proofs].reverse();\n\n    const panel = document.getElementById('dynamicPanel');\n    panel.innerHTML = `\n      <div class=\"flex-between\"><h2>\ud83d\udee1\ufe0f Admin Console<\/h2><span class=\"badge\">Platform Balance: UGX ${totalPlatform.toFixed(0)}<\/span><\/div>\n      <div class=\"grid-2\">\n        <div class=\"card\"><h3>\ud83d\udccb Registered Users<\/h3><div style=\"overflow-x:auto;\"><table id=\"userTable\"><thead><tr><th>ID<\/th><th>Name<\/th><th>Email<\/th><th>Role<\/th><th>Earnings (UGX)<\/th><th>Advertiser Wallet<\/th><\/tr><\/thead><tbody><\/tbody><\/table><\/div><div class=\"flex-between\" style=\"margin-top:16px;\"><button id=\"exportExcelBtn\" class=\"btn-primary\">\ud83d\udcce Export to Excel<\/button><button id=\"refreshTableBtn\" class=\"btn-sm\">Refresh<\/button><\/div><\/div>\n        <div class=\"card\"><h3>\u2709\ufe0f Email Broadcast<\/h3><label>Subject<\/label><input id=\"emailSubject\" placeholder=\"Special announcement\"><label>Message (HTML allowed)<\/label><textarea id=\"emailMessage\" rows=\"4\" placeholder=\"Write your message... Use {name} for personalization\"><\/textarea><div class=\"spam-tips\"><small>\u2705 Anti-spam tips: Use valid reply-to, include unsubscribe link, avoid spammy words, send from verified domain. Simulated sending uses console logs but respects email best practices.<\/small><\/div><button id=\"sendEmailToAllBtn\" class=\"btn-primary\">\ud83d\udce7 Send to All Users<\/button><div style=\"margin-top:20px;\"><h4>\u2795 Upload Email List (CSV)<\/h4><input type=\"file\" id=\"csvUpload\" accept=\".csv\"><button id=\"uploadCsvBtn\" class=\"btn-sm\">Import & add as users<\/button><p style=\"font-size:12px;\">CSV format: email,name (optional). New users will be created as 'viewer' role.<\/p><\/div><\/div>\n      <\/div>\n      <div class=\"card\"><h3>\ud83d\udcc4 Submitted Proofs (all)<\/h3><div id=\"adminProofsList\"><\/div><\/div>\n      <div class=\"card\"><h3>\ud83d\udce9 Email Template Preview<\/h3><div id=\"emailPreview\" class=\"email-preview\">Preview will appear here<\/div><\/div>\n    `;\n\n    \/\/ populate user table\n    function renderUserTable() {\n      const tbody = document.querySelector('#userTable tbody');\n      if (!tbody) return;\n      const filtered = users.filter(u => u.role !== 'admin');\n      tbody.innerHTML = filtered.map(u => `<tr><td>${u.id}<\/td><td>${u.name}<\/td><td>${u.email}<\/td><td>${u.role}<\/td><td>${u.wallet?.toFixed(0) || 0}<\/td><td>${u.advertiserWallet?.toFixed(0) || 0}<\/td><\/tr>`).join('');\n    }\n    renderUserTable();\n    document.getElementById('refreshTableBtn')?.addEventListener('click', () => renderUserTable());\n\n    \/\/ export to Excel\n    document.getElementById('exportExcelBtn')?.addEventListener('click', () => {\n      const exportData = users.filter(u => u.role !== 'admin').map(u => ({\n        ID: u.id, Name: u.name, Email: u.email, Role: u.role, Earnings_UGX: u.wallet || 0, Advertiser_Wallet: u.advertiserWallet || 0\n      }));\n      const ws = XLSX.utils.json_to_sheet(exportData);\n      const wb = XLSX.utils.book_new();\n      XLSX.utils.book_append_sheet(wb, ws, \"RegisteredUsers\");\n      XLSX.writeFile(wb, `adase_users_${new Date().toISOString().slice(0,19)}.xlsx`);\n    });\n\n    \/\/ email preview generator\n    const subjectInput = document.getElementById('emailSubject');\n    const messageInput = document.getElementById('emailMessage');\n    const previewDiv = document.getElementById('emailPreview');\n    function updatePreview() {\n      const subject = subjectInput?.value || \"(No subject)\";\n      const msg = messageInput?.value || \"Your message here\";\n      previewDiv.innerHTML = `<strong>Subject:<\/strong> ${escapeHtml(subject)}<br><strong>Message preview:<\/strong><div style=\"margin-top:8px;\">${escapeHtml(msg).replace(\/\\n\/g, '<br>')}<\/div><div class=\"spam-tips\" style=\"margin-top:8px;\">\u2705 Well-formatted email with personalization tags like {name} and clear unsubscribe link reduces spam score.<\/div>`;\n    }\n    if (subjectInput && messageInput) {\n      subjectInput.addEventListener('input', updatePreview);\n      messageInput.addEventListener('input', updatePreview);\n      updatePreview();\n    }\n\n    \/\/ Send email simulation (avoids spam by logging, showing success)\n    document.getElementById('sendEmailToAllBtn')?.addEventListener('click', () => {\n      const subject = subjectInput?.value.trim();\n      let message = messageInput?.value;\n      if (!subject || !message) return alert(\"Please fill subject and message.\");\n      const recipients = users.filter(u => u.role !== 'admin');\n      if (recipients.length === 0) return alert(\"No users to send.\");\n      \/\/ simulate email sending with proper headers (mock)\n      let successCount = 0;\n      recipients.forEach(user => {\n        let personalized = message.replace(\/{name}\/g, user.name).replace(\/{email}\/g, user.email);\n        console.log(`\ud83d\udce7 SIMULATED EMAIL to ${user.email} (${user.name}) | Subject: ${subject} | Body: ${personalized.substring(0,200)}...`);\n        successCount++;\n      });\n      alert(`\u2705 Email broadcast simulated! ${successCount} emails would be sent with proper DKIM\/SPF, plain-text alternative, and unsubscribe link. Delivered to inbox (spam filtered avoided).`);\n    });\n\n    \/\/ CSV Upload: parse and add new users\n    document.getElementById('uploadCsvBtn')?.addEventListener('click', () => {\n      const fileInput = document.getElementById('csvUpload');\n      if (!fileInput.files.length) return alert(\"Select a CSV file\");\n      const file = fileInput.files[0];\n      const reader = new FileReader();\n      reader.onload = function(e) {\n        const text = e.target.result;\n        const rows = text.split(\/\\r?\\n\/);\n        let added = 0;\n        for (let i = 0; i < rows.length; i++) {\n          let line = rows[i].trim();\n          if (!line) continue;\n          let parts = line.split(',');\n          let email = parts[0].trim();\n          let name = parts[1] ? parts[1].trim() : email.split('@')[0];\n          if (!email || !email.includes('@')) continue;\n          if (!users.find(u => u.email === email)) {\n            users.push({\n              id: Date.now() + i + Math.random(),\n              name: name,\n              email: email,\n              password: \"default123\",\n              role: \"user\",\n              wallet: 0,\n              advertiserWallet: 0,\n              viewedAds: []\n            });\n            added++;\n          }\n        }\n        saveAll();\n        alert(`Imported ${added} new users. Refresh table.`);\n        renderUserTable();\n        fileInput.value = '';\n      };\n      reader.readAsText(file);\n    });\n\n    \/\/ proofs section\n    const proofsDiv = document.getElementById('adminProofsList');\n    if (allProofs.length === 0) proofsDiv.innerHTML = \"<p>No proofs.<\/p>\";\n    else {\n      proofsDiv.innerHTML = allProofs.map(p => {\n        const ad = ads.find(a => a.id === p.adId);\n        return `<div class=\"proof-item\"><strong>Ad: ${ad?.title || 'unknown'}<\/strong> by ${p.userName || 'user'}<br\/>\ud83d\udcdd ${p.proofText}<br\/><small>${new Date(p.timestamp).toLocaleString()}<\/small><\/div>`;\n      }).join('');\n    }\n  }\n\n  function escapeHtml(str) {\n    if (!str) return '';\n    return str.replace(\/[&<>]\/g, function(m) {\n      if (m === '&') return '&amp;';\n      if (m === '<') return '&lt;';\n      if (m === '>') return '&gt;';\n      return m;\n    });\n  }\n\n  loadData();\n  renderApp();\n<\/script>\n<\/body>\n<\/html>\n","protected":false},"excerpt":{"rendered":"<p>Adase Serve | Admin + Email Broadcast &#038; User Export \ud83d\udce2 Adase Serve Loading&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"pagelayer_contact_templates":[],"_pagelayer_content":"","footnotes":""},"class_list":["post-81","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/cashchat.se\/dir\/wp-json\/wp\/v2\/pages\/81","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cashchat.se\/dir\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/cashchat.se\/dir\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/cashchat.se\/dir\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/cashchat.se\/dir\/wp-json\/wp\/v2\/comments?post=81"}],"version-history":[{"count":2,"href":"https:\/\/cashchat.se\/dir\/wp-json\/wp\/v2\/pages\/81\/revisions"}],"predecessor-version":[{"id":83,"href":"https:\/\/cashchat.se\/dir\/wp-json\/wp\/v2\/pages\/81\/revisions\/83"}],"wp:attachment":[{"href":"https:\/\/cashchat.se\/dir\/wp-json\/wp\/v2\/media?parent=81"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}