{"id":254,"date":"2026-06-12T04:16:49","date_gmt":"2026-06-12T04:16:49","guid":{"rendered":"https:\/\/cashchat.se\/dir\/?page_id=254"},"modified":"2026-06-12T04:33:50","modified_gmt":"2026-06-12T04:33:50","slug":"v3","status":"publish","type":"page","link":"https:\/\/cashchat.se\/dir\/v3\/","title":{"rendered":"v3"},"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, viewport-fit=cover\">\n  <title>CashChat Strip | Advertiser + Admin Dual Login | Earn per View<\/title>\n  <!-- Font Awesome 6 -->\n  <link rel=\"stylesheet\" href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/font-awesome\/6.0.0-beta3\/css\/all.min.css\">\n  <!-- SheetJS + jsPDF -->\n  <script src=\"https:\/\/cdn.sheetjs.com\/xlsx-0.20.2\/package\/dist\/xlsx.full.min.js\"><\/script>\n  <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jspdf\/2.5.1\/jspdf.umd.min.js\"><\/script>\n  <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jspdf-autotable\/3.8.2\/jspdf.plugin.autotable.min.js\"><\/script>\n  <style>\n    * {\n      margin: 0;\n      padding: 0;\n      box-sizing: border-box;\n      font-family: 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;\n    }\n    body {\n      background: #f0f2f5;\n      padding: 16px;\n      min-height: 100vh;\n    }\n    .app {\n      max-width: 1600px;\n      margin: 0 auto;\n    }\n    \/* Dual Login Panels *\/\n    .dual-login-container {\n      display: flex;\n      flex-wrap: wrap;\n      gap: 24px;\n      justify-content: center;\n      align-items: stretch;\n      max-width: 1000px;\n      margin: 40px auto;\n    }\n    .login-card {\n      background: white;\n      border-radius: 32px;\n      padding: 28px 24px;\n      flex: 1;\n      min-width: 280px;\n      box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);\n      transition: transform 0.2s;\n    }\n    .login-card:hover {\n      transform: translateY(-4px);\n    }\n    .login-card h2 {\n      margin-bottom: 20px;\n      font-size: 1.6rem;\n      display: flex;\n      align-items: center;\n      gap: 10px;\n    }\n    .login-card .form-group {\n      margin-bottom: 18px;\n    }\n    .login-card input {\n      width: 100%;\n      padding: 12px 16px;\n      border-radius: 40px;\n      border: 1px solid #ccc;\n      font-size: 0.95rem;\n    }\n    .login-card button {\n      width: 100%;\n      background: #2c6e9e;\n      color: white;\n      border: none;\n      padding: 12px;\n      border-radius: 40px;\n      font-weight: bold;\n      font-size: 1rem;\n      cursor: pointer;\n      transition: 0.2s;\n    }\n    .login-card button:hover {\n      opacity: 0.9;\n    }\n    .admin-card {\n      border-top: 5px solid #b91c1c;\n    }\n    .advertiser-card {\n      border-top: 5px solid #0f7b3a;\n    }\n    .error-msg {\n      color: #b91c1c;\n      font-size: 0.8rem;\n      margin-top: 12px;\n      text-align: center;\n    }\n    .success-msg {\n      color: #0f7b3a;\n      font-size: 0.8rem;\n      margin-top: 12px;\n      text-align: center;\n    }\n    \/* Main app styles *\/\n    .flex-between {\n      display: flex;\n      justify-content: space-between;\n      align-items: center;\n      flex-wrap: wrap;\n      gap: 12px;\n    }\n    .tabs {\n      display: flex;\n      flex-wrap: wrap;\n      gap: 10px;\n      margin-bottom: 24px;\n      border-bottom: 1px solid #cbd5e1;\n      padding-bottom: 8px;\n    }\n    .tab-btn {\n      background: #e2e8f0;\n      color: #1a2a3a;\n      padding: 8px 18px;\n      border-radius: 40px;\n      font-weight: 600;\n      border: none;\n      cursor: pointer;\n      font-size: 0.9rem;\n    }\n    .tab-btn.active {\n      background: #2c6e9e;\n      color: white;\n    }\n    .panel {\n      background: white;\n      border-radius: 28px;\n      padding: 20px;\n      margin-bottom: 24px;\n      box-shadow: 0 8px 20px rgba(0, 0, 0, 0.05);\n    }\n    .strip-container {\n      background: #0f172a;\n      border-radius: 32px;\n      padding: 20px 12px;\n      overflow-x: hidden;\n    }\n    .strip-track {\n      display: flex;\n      gap: 24px;\n      animation: scrollLeftToRight 35s linear infinite;\n      width: max-content;\n    }\n    .strip-container:hover .strip-track {\n      animation-play-state: paused;\n    }\n    @keyframes scrollLeftToRight {\n      0% { transform: translateX(0%); }\n      100% { transform: translateX(-50%); }\n    }\n    .strip-card {\n      width: 270px;\n      background: white;\n      border-radius: 24px;\n      overflow: hidden;\n      box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);\n      flex-shrink: 0;\n    }\n    .strip-img {\n      width: 100%;\n      height: 180px;\n      object-fit: cover;\n      display: block;\n    }\n    .strip-info {\n      padding: 12px;\n    }\n    .sponsor-badge {\n      background: #f1f5f9;\n      border-radius: 40px;\n      padding: 4px 12px;\n      font-size: 0.7rem;\n      display: inline-flex;\n      align-items: center;\n      gap: 6px;\n      margin: 2px;\n    }\n    .grid {\n      display: flex;\n      flex-wrap: wrap;\n      gap: 20px;\n      margin-top: 20px;\n    }\n    .post-preview-card {\n      width: 280px;\n      background: #fefce8;\n      border-radius: 24px;\n      border: 1px solid #ddd;\n      overflow: hidden;\n    }\n    .wallet-info {\n      background: #e6f7e6;\n      border-radius: 20px;\n      padding: 12px;\n      margin-bottom: 16px;\n    }\n    .export-buttons {\n      display: flex;\n      gap: 12px;\n      flex-wrap: wrap;\n    }\n    .badge {\n      background: #e2e8f0;\n      padding: 4px 12px;\n      border-radius: 30px;\n      font-size: 0.7rem;\n      display: inline-block;\n    }\n    hr {\n      margin: 16px 0;\n    }\n    .dashboard-stats {\n      background: #eef2ff;\n      padding: 16px;\n      border-radius: 24px;\n      margin-bottom: 20px;\n      display: grid;\n      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n      gap: 12px;\n    }\n    .logout-btn {\n      background: #b91c1c;\n      margin-left: 10px;\n    }\n    table {\n      width: 100%;\n      border-collapse: collapse;\n      overflow-x: auto;\n      display: block;\n    }\n    th, td {\n      padding: 10px;\n      border: 1px solid #ddd;\n      text-align: left;\n    }\n    @media (max-width: 640px) {\n      body { padding: 12px; }\n      .strip-card { width: 240px; }\n      .tab-btn { padding: 6px 14px; font-size: 0.8rem; }\n      .panel { padding: 16px; }\n    }\n  <\/style>\n<\/head>\n<body>\n\n<!-- DUAL LOGIN PANEL (shown when not logged in) -->\n<div id=\"dualLoginContainer\" class=\"dual-login-container\">\n  <!-- Advertiser Login Card -->\n  <div class=\"login-card advertiser-card\">\n    <h2><i class=\"fas fa-bullhorn\"><\/i> Advertiser Login<\/h2>\n    <div class=\"form-group\">\n      <input type=\"email\" id=\"advLoginEmail\" placeholder=\"Email address\">\n    <\/div>\n    <div class=\"form-group\">\n      <input type=\"password\" id=\"advLoginPassword\" placeholder=\"Password\">\n    <\/div>\n    <button id=\"advLoginBtn\"><i class=\"fas fa-sign-in-alt\"><\/i> Login as Advertiser<\/button>\n    <div id=\"advLoginError\" class=\"error-msg\"><\/div>\n    <hr style=\"margin: 16px 0;\">\n    <p style=\"text-align: center; font-size: 0.8rem;\">New advertiser? <strong id=\"showAdvertiserSignup\" style=\"color:#2c6e9e; cursor:pointer;\">Create account<\/strong><\/p>\n  <\/div>\n\n  <!-- Admin Login Card -->\n  <div class=\"login-card admin-card\">\n    <h2><i class=\"fas fa-shield-alt\"><\/i> Admin Login<\/h2>\n    <div class=\"form-group\">\n      <input type=\"email\" id=\"adminLoginEmail\" placeholder=\"ads@cashchat.se\">\n    <\/div>\n    <div class=\"form-group\">\n      <input type=\"password\" id=\"adminLoginPassword\" placeholder=\"Password\">\n    <\/div>\n    <button id=\"adminLoginBtn\"><i class=\"fas fa-lock\"><\/i> Login as Admin<\/button>\n    <div id=\"adminLoginError\" class=\"error-msg\"><\/div>\n    <p style=\"text-align: center; font-size: 0.75rem; margin-top: 12px;\">Demo admin: ads@cashchat.se \/ Justtrade81#<\/p>\n  <\/div>\n<\/div>\n\n<!-- Advertiser Signup Modal (lightbox) -->\n<div id=\"signupModal\" style=\"display: none; position: fixed; top:0; left:0; width:100%; height:100%; background: rgba(0,0,0,0.8); z-index: 2000; align-items: center; justify-content: center;\">\n  <div style=\"background: white; max-width: 400px; width: 90%; border-radius: 32px; padding: 28px;\">\n    <h2><i class=\"fas fa-user-plus\"><\/i> Advertiser Signup<\/h2>\n    <div class=\"form-group\"><input type=\"text\" id=\"signupName\" placeholder=\"Full Name\"><\/div>\n    <div class=\"form-group\"><input type=\"email\" id=\"signupEmail\" placeholder=\"Email\"><\/div>\n    <div class=\"form-group\"><input type=\"password\" id=\"signupPassword\" placeholder=\"Password\"><\/div>\n    <button id=\"confirmSignupBtn\" style=\"width:100%;\">Sign Up<\/button>\n    <div id=\"signupMsg\" style=\"margin-top:12px; text-align:center;\"><\/div>\n    <button id=\"closeSignupModal\" style=\"background:#6c757d; margin-top:12px; width:100%;\">Cancel<\/button>\n  <\/div>\n<\/div>\n\n<!-- Main App (hidden until login) -->\n<div class=\"app\" id=\"app\" style=\"display: none;\">\n  <div class=\"flex-between\">\n    <div class=\"tabs\">\n      <button class=\"tab-btn active\" data-tab=\"public\">\ud83d\udcf8 Moving Strip<\/button>\n      <button class=\"tab-btn\" data-tab=\"dashboard\">\ud83d\udcca My Dashboard<\/button>\n      <button class=\"tab-btn\" data-tab=\"upload\">\ud83d\udce4 Create Post<\/button>\n      <button class=\"tab-btn\" data-tab=\"admin\">\ud83d\udee1\ufe0f Admin Panel<\/button>\n    <\/div>\n    <div><span id=\"currentUserEmail\" style=\"font-weight:600;\"><\/span> <button id=\"logoutMainBtn\" class=\"logout-btn\"><i class=\"fas fa-sign-out-alt\"><\/i> Logout<\/button><\/div>\n  <\/div>\n\n  <!-- public strip -->\n  <div id=\"publicPanel\" class=\"panel\">\n    <h2><i class=\"fas fa-images\"><\/i> Activity Strip (Left \u2192 Right)<\/h2>\n    <div class=\"strip-container\"><div class=\"strip-track\" id=\"stripTrack\"><\/div><\/div>\n    <p class=\"badge\">\ud83d\udc41\ufe0f Each unique view = UGX 3 to sponsor&#8217;s wallet<\/p>\n  <\/div>\n  \n  <!-- Advertiser Dashboard -->\n  <div id=\"dashboardPanel\" class=\"panel\" style=\"display:none;\">\n    <h2><i class=\"fas fa-chart-line\"><\/i> My Advertiser Dashboard<\/h2>\n    <div class=\"dashboard-stats\">\n      <div><strong>\ud83d\udcb0 Balance:<\/strong> <span id=\"dashboardWallet\">0<\/span> UGX<\/div>\n      <div><strong>\ud83d\udce2 Active Posts:<\/strong> <span id=\"activePostsCount\">0<\/span><\/div>\n      <div><strong>\ud83d\udc41\ufe0f Total Views:<\/strong> <span id=\"totalViewsCount\">0<\/span><\/div>\n      <div><strong>\ud83d\udcb8 Total Earned:<\/strong> <span id=\"totalEarned\">0<\/span> UGX<\/div>\n    <\/div>\n    <h3>\ud83d\udccb My Posts<\/h3>\n    <div id=\"myPostsList\" style=\"max-height: 300px; overflow-y: auto;\"><\/div>\n    <hr>\n    <h3>\ud83d\udcb8 Withdraw Funds<\/h3>\n    <div class=\"form-group\"><label>Amount (UGX)<\/label><input type=\"number\" id=\"dashboardWithdrawAmount\" placeholder=\"Enter amount\"><button id=\"dashboardWithdrawBtn\">Request Withdrawal<\/button><\/div>\n    <div id=\"dashboardWithdrawMsg\"><\/div>\n    <h3>\ud83d\udcdc Withdrawal History<\/h3>\n    <div id=\"myWithdrawalsList\"><\/div>\n  <\/div>\n\n  <!-- upload panel -->\n  <div id=\"uploadPanel\" class=\"panel\" style=\"display:none;\">\n    <h2>\ud83d\udce4 Create Sponsored Post<\/h2>\n    <div class=\"form-group\"><label>Activity Image (URL or file)<\/label><input type=\"file\" id=\"activityImgFile\" accept=\"image\/*\"><input type=\"text\" id=\"activityImgUrl\" placeholder=\"https:\/\/...\"><\/div>\n    <div class=\"form-group\"><label>Sponsor Logos (URLs, comma separated)<\/label><input type=\"text\" id=\"sponsorUrls\" placeholder=\"https:\/\/logo1.png, https:\/\/logo2.png\"><\/div>\n    <div class=\"form-group\"><label>Caption<\/label><textarea id=\"captionText\" rows=\"2\"><\/textarea><\/div>\n    <div class=\"form-group\"><label>\ud83d\udcc5 Visibility (days)<\/label><input type=\"number\" id=\"visibilityDays\" min=\"1\" max=\"90\" value=\"7\"><\/div>\n    <button id=\"createPostBtn\">\ud83d\ude80 Submit Post (needs admin approval)<\/button>\n    <div id=\"postMsg\" style=\"margin-top:10px;\"><\/div>\n  <\/div>\n\n  <!-- admin panel -->\n  <div id=\"adminPanel\" class=\"panel\" style=\"display:none;\">\n    <h2>Admin Dashboard<\/h2>\n    <div class=\"flex-between\"><div><strong>\ud83d\udc51 Admin Wallet: <span id=\"adminWalletSpan\">0<\/span> UGX<\/strong><\/div><div class=\"export-buttons\"><button id=\"exportExcelBtn\"><i class=\"fas fa-file-excel\"><\/i> Excel<\/button><button id=\"exportPdfBtn\"><i class=\"fas fa-file-pdf\"><\/i> PDF<\/button><\/div><\/div>\n    <h3>\ud83d\udc65 Approved Advertisers<\/h3><div id=\"approvedUsersTableContainer\" style=\"overflow-x:auto;\"><\/div>\n    <h3>\ud83d\udcdd Pending Posts<\/h3><div id=\"pendingPostsGrid\" class=\"grid\"><\/div>\n    <h3>\ud83d\udcb8 Withdrawal Requests<\/h3><div id=\"withdrawRequestsList\"><\/div>\n    <h3>\u2705 Active Approved Posts<\/h3><div id=\"approvedPostsAdminList\"><\/div>\n  <\/div>\n<\/div>\n\n<script>\n  \/\/ ---------- DATA MODELS ----------\n  let advertisers = [];     \/\/ { id, name, email, password, approved, walletBalance, role }\n  let posts = [];           \n  let withdrawalRequests = [];\n  let adminWallet = 0;\n  let currentLoggedInUser = null; \/\/ stores email\n  let currentUserRole = null; \/\/ 'advertiser' or 'admin'\n\n  const ADMIN_EMAIL = \"ads@cashchat.se\";\n  const ADMIN_PASS = \"Justtrade81#\";\n\n  function saveData() {\n    localStorage.setItem('cc_advertisers', JSON.stringify(advertisers));\n    localStorage.setItem('cc_posts', JSON.stringify(posts));\n    localStorage.setItem('cc_withdrawals', JSON.stringify(withdrawalRequests));\n    localStorage.setItem('cc_adminWallet', adminWallet);\n  }\n  function loadData() {\n    const adv = localStorage.getItem('cc_advertisers'); if(adv) advertisers = JSON.parse(adv);\n    const p = localStorage.getItem('cc_posts'); if(p) posts = JSON.parse(p);\n    const w = localStorage.getItem('cc_withdrawals'); if(w) withdrawalRequests = JSON.parse(w);\n    const aw = localStorage.getItem('cc_adminWallet'); if(aw) adminWallet = Number(aw);\n    \n    if(advertisers.length === 0) {\n      advertisers.push({ id: 1, name: \"Alice Sponsor\", email: \"alice@chat.com\", password: \"alice123\", approved: true, walletBalance: 12000, role: \"advertiser\" });\n      advertisers.push({ id: 2, name: \"Bob Marketer\", email: \"bob@cashchat.se\", password: \"bob123\", approved: true, walletBalance: 5000, role: \"advertiser\" });\n      advertisers.push({ id: 3, name: \"Charlie Pending\", email: \"charlie@test.com\", password: \"charlie123\", approved: false, walletBalance: 0, role: \"advertiser\" });\n      \/\/ Admin user\n      advertisers.push({ id: 999, name: \"Super Admin\", email: ADMIN_EMAIL, password: ADMIN_PASS, approved: true, walletBalance: 0, role: \"admin\" });\n      adminWallet = 25000;\n      posts.push({ id: 101, userId: 1, activityImageUrl: \"https:\/\/picsum.photos\/id\/20\/400\/250\", sponsorUrlsArray: [\"https:\/\/picsum.photos\/id\/1\/40\/40\"], caption: \"Charity run 2025\", approved: true, createdAt: Date.now(), expiresAt: Date.now() + 30*86400000, uniqueViewers: [] });\n      posts.push({ id: 102, userId: 2, activityImageUrl: \"https:\/\/picsum.photos\/id\/29\/400\/250\", sponsorUrlsArray: [\"https:\/\/picsum.photos\/id\/13\/40\/40\"], caption: \"Bike festival\", approved: true, createdAt: Date.now(), expiresAt: Date.now() + 15*86400000, uniqueViewers: [] });\n      saveData();\n    }\n    advertisers.forEach(u => { if(u.walletBalance === undefined) u.walletBalance = 0; if(!u.role) u.role = \"advertiser\"; });\n    posts.forEach(p => { if(!p.uniqueViewers) p.uniqueViewers = []; if(!p.expiresAt) p.expiresAt = Date.now() + 7*86400000; });\n    saveData();\n  }\n\n  function escapeHtml(str) { if(!str) return ''; return str.replace(\/[&<>]\/g, m => ({'&':'&amp;','<':'&lt;','>':'&gt;'}[m])); }\n  function showToast(msg) { let t=document.createElement('div'); t.innerText=msg; t.style.position='fixed'; t.style.bottom='20px'; t.style.right='20px'; t.style.background='#1e293b'; t.style.color='white'; t.style.padding='12px 20px'; t.style.borderRadius='40px'; t.style.zIndex='9999'; document.body.appendChild(t); setTimeout(()=>t.remove(),2500); }\n\n  \/\/ Unique view + credit wallet\n  function recordUniqueView(postId) {\n    const post = posts.find(p => p.id === postId);\n    if(!post || !post.approved) return false;\n    const storageKey = `viewed_post_${postId}`;\n    if(localStorage.getItem(storageKey)) return false;\n    localStorage.setItem(storageKey, 'true');\n    if(!post.uniqueViewers) post.uniqueViewers = [];\n    post.uniqueViewers.push(Date.now());\n    const sponsor = advertisers.find(u => u.id === post.userId);\n    if(sponsor && sponsor.approved && sponsor.role === 'advertiser') {\n      sponsor.walletBalance = (sponsor.walletBalance || 0) + 3;\n      saveData();\n    }\n    saveData();\n    return true;\n  }\n\n  function renderStrip() {\n    const now = Date.now();\n    const activePosts = posts.filter(p => p.approved === true && p.expiresAt > now);\n    if(activePosts.length === 0) { document.getElementById('stripTrack').innerHTML = `<div style=\"color:white; padding:20px;\">No active sponsored posts<\/div>`; return; }\n    let html = '';\n    for(let rep=0; rep<3; rep++) {\n      activePosts.forEach(post => {\n        recordUniqueView(post.id);\n        const sponsorHtml = (post.sponsorUrlsArray || []).map(url => `<div class=\"sponsor-badge\"><img loading=\"lazy\" decoding=\"async\" src=\"${escapeHtml(url)}\" width=\"24\" height=\"24\" onerror=\"this.style.display='none'\"> Sponsor<\/div>`).join('');\n        html += `<div class=\"strip-card\"><img decoding=\"async\" class=\"strip-img\" src=\"${escapeHtml(post.activityImageUrl)}\" onerror=\"this.src='https:\/\/placehold.co\/400x250'\"><div class=\"strip-info\"><div class=\"sponsor-logos\">${sponsorHtml}<\/div><div class=\"caption-text\">${escapeHtml(post.caption)}<\/div><div class=\"user-name\"><i class=\"fas fa-coins\"><\/i> UGX 3\/view<\/div><\/div><\/div>`;\n      });\n    }\n    document.getElementById('stripTrack').innerHTML = html;\n  }\n\n  \/\/ Admin panel rendering\n  function renderAdminPanel() {\n    const approvedUsers = advertisers.filter(u => u.approved === true && u.role === 'advertiser');\n    let tableHtml = `<table><thead><tr><th>Name<\/th><th>Email<\/th><th>Wallet (UGX)<\/th><\/tr><\/thead><tbody>`;\n    approvedUsers.forEach(u => { tableHtml += `<tr><td>${escapeHtml(u.name)}<\/td><td>${escapeHtml(u.email)}<\/td><td>${u.walletBalance || 0} UGX<\/td><\/tr>`; });\n    tableHtml += `<\/tbody><\/table>`;\n    document.getElementById('approvedUsersTableContainer').innerHTML = tableHtml;\n    document.getElementById('adminWalletSpan').innerText = adminWallet;\n\n    const pendingPosts = posts.filter(p => !p.approved);\n    const pendingGrid = document.getElementById('pendingPostsGrid');\n    if(pendingPosts.length === 0) pendingGrid.innerHTML = '<div class=\"badge\">No pending posts<\/div>';\n    else {\n      pendingGrid.innerHTML = pendingPosts.map(post => {\n        const owner = advertisers.find(u => u.id === post.userId);\n        return `<div class=\"post-preview-card\"><img decoding=\"async\" src=\"${escapeHtml(post.activityImageUrl)}\" width=\"100%\" height=\"150\" style=\"object-fit:cover\"><div style=\"padding:12px\"><div><strong>\ud83d\udc64 ${escapeHtml(owner?.name || 'user')}<\/strong><\/div><div>\ud83d\udcc5 Days: ${Math.ceil((post.expiresAt - Date.now())\/86400000)}<\/div><div>\ud83d\udcac ${escapeHtml(post.caption)}<\/div><div style=\"margin-top:12px\"><button onclick=\"approvePostAdmin(${post.id})\">\u2705 Approve<\/button> <button onclick=\"declinePostAdmin(${post.id})\">\u274c Decline<\/button><\/div><\/div><\/div>`;\n      }).join('');\n    }\n\n    const pendingWithdraws = withdrawalRequests.filter(w => w.status === 'pending');\n    const withdrawDiv = document.getElementById('withdrawRequestsList');\n    if(pendingWithdraws.length === 0) withdrawDiv.innerHTML = '<div>No pending requests<\/div>';\n    else {\n      withdrawDiv.innerHTML = pendingWithdraws.map(req => {\n        const u = advertisers.find(a => a.id === req.userId);\n        return `<div style=\"background:#fef9e3; padding:12px; border-radius:20px; margin-bottom:8px;\"><strong>${u?.name}<\/strong> (${u?.email}) requests ${req.amount} UGX <button onclick=\"approveWithdraw(${req.id})\">\u2705 Approve<\/button> <button onclick=\"rejectWithdraw(${req.id})\">\u274c Reject<\/button><\/div>`;\n      }).join('');\n    }\n    const activeApproved = posts.filter(p => p.approved && p.expiresAt > Date.now());\n    document.getElementById('approvedPostsAdminList').innerHTML = activeApproved.map(p => `<div style=\"border-bottom:1px solid #ccc; padding:6px;\">\ud83d\udccc ${escapeHtml(p.caption)} | expires: ${new Date(p.expiresAt).toLocaleDateString()} | views: ${p.uniqueViewers?.length || 0} (${(p.uniqueViewers?.length || 0)*3} UGX earned)<\/div>`).join('') || 'None';\n  }\n\n  window.approvePostAdmin = function(postId) {\n    const post = posts.find(p => p.id === postId);\n    if(post) { post.approved = true; saveData(); renderAdminPanel(); renderStrip(); showToast('Post approved'); }\n  };\n  window.declinePostAdmin = function(postId) {\n    posts = posts.filter(p => p.id !== postId);\n    saveData(); renderAdminPanel(); renderStrip(); showToast('Post declined');\n  };\n  window.approveWithdraw = function(reqId) {\n    const req = withdrawalRequests.find(r => r.id === reqId);\n    if(!req || req.status !== 'pending') return;\n    const user = advertisers.find(u => u.id === req.userId);\n    if(user && user.walletBalance >= req.amount && req.amount > 0) {\n      user.walletBalance -= req.amount;\n      adminWallet += req.amount;\n      req.status = 'approved';\n      saveData();\n      renderAdminPanel();\n      if(currentLoggedInUser && currentUserRole === 'advertiser' && currentLoggedInUser === user.email) renderDashboard();\n      showToast(`Approved ${req.amount} UGX`);\n    } else showToast('Insufficient balance');\n  };\n  window.rejectWithdraw = function(reqId) {\n    const req = withdrawalRequests.find(r => r.id === reqId);\n    if(req) { req.status = 'rejected'; saveData(); renderAdminPanel(); showToast('Rejected'); }\n  };\n\n  function renderDashboard() {\n    if(!currentLoggedInUser || currentUserRole !== 'advertiser') return;\n    const user = advertisers.find(u => u.email === currentLoggedInUser);\n    if(!user) return;\n    document.getElementById('dashboardWallet').innerText = user.walletBalance || 0;\n    const myPosts = posts.filter(p => p.userId === user.id);\n    const activePosts = myPosts.filter(p => p.approved && p.expiresAt > Date.now());\n    document.getElementById('activePostsCount').innerText = activePosts.length;\n    const totalViews = myPosts.reduce((sum, p) => sum + (p.uniqueViewers?.length || 0), 0);\n    document.getElementById('totalViewsCount').innerText = totalViews;\n    document.getElementById('totalEarned').innerText = totalViews * 3;\n    document.getElementById('myPostsList').innerHTML = myPosts.map(p => `<div style=\"border-bottom:1px solid #ddd; padding:12px;\"><img loading=\"lazy\" decoding=\"async\" src=\"${escapeHtml(p.activityImageUrl)}\" width=\"50\" height=\"50\" style=\"object-fit:cover; border-radius:12px;\"> <strong>${escapeHtml(p.caption)}<\/strong> | ${p.approved ? '\u2705 Approved' : '\u23f3 Pending'} | Views: ${p.uniqueViewers?.length || 0}<\/div>`).join('') || '<div>No posts.<\/div>';\n    const myWithdrawals = withdrawalRequests.filter(w => w.userId === user.id);\n    document.getElementById('myWithdrawalsList').innerHTML = myWithdrawals.map(w => `<div>\ud83d\udcb0 ${w.amount} UGX - ${w.status.toUpperCase()} (${new Date(w.requestedAt).toLocaleDateString()})<\/div>`).join('') || '<div>None.<\/div>';\n  }\n\n  function requestWithdrawalDashboard() {\n    if(!currentLoggedInUser || currentUserRole !== 'advertiser') return;\n    const user = advertisers.find(u => u.email === currentLoggedInUser);\n    if(!user || !user.approved) { showToast('Account not approved'); return; }\n    const amount = parseFloat(document.getElementById('dashboardWithdrawAmount').value);\n    if(isNaN(amount) || amount <= 0) { document.getElementById('dashboardWithdrawMsg').innerText = 'Valid amount'; return; }\n    if(amount > (user.walletBalance || 0)) { document.getElementById('dashboardWithdrawMsg').innerText = 'Insufficient balance'; return; }\n    withdrawalRequests.push({ id: Date.now(), userId: user.id, amount: amount, status: 'pending', requestedAt: Date.now() });\n    saveData();\n    document.getElementById('dashboardWithdrawAmount').value = '';\n    document.getElementById('dashboardWithdrawMsg').innerHTML = '\u2705 Request sent to admin.';\n    renderDashboard();\n    if(document.getElementById('adminPanel').style.display !== 'none' && currentUserRole === 'admin') renderAdminPanel();\n    showToast('Withdrawal request sent');\n  }\n\n  function createPost() {\n    if(!currentLoggedInUser || currentUserRole !== 'advertiser') { showToast('Only advertisers can post'); return; }\n    const user = advertisers.find(u => u.email === currentLoggedInUser);\n    if(!user || !user.approved) { document.getElementById('postMsg').innerText = 'Account not approved.'; return; }\n    let imageUrl = document.getElementById('activityImgUrl').value.trim();\n    const fileInput = document.getElementById('activityImgFile');\n    const days = parseInt(document.getElementById('visibilityDays').value);\n    if(isNaN(days) || days < 1) { document.getElementById('postMsg').innerText = 'Valid days'; return; }\n    const expiresAt = Date.now() + (days * 86400000);\n    const processPost = (finalUrl) => {\n      const sponsorUrlsRaw = document.getElementById('sponsorUrls').value;\n      let sponsorUrlsArray = sponsorUrlsRaw.split(',').map(s=>s.trim()).filter(s=>s);\n      const caption = document.getElementById('captionText').value.trim();\n      if(!caption || !finalUrl) { document.getElementById('postMsg').innerText = 'Image & caption required'; return; }\n      posts.push({ id: Date.now(), userId: user.id, activityImageUrl: finalUrl, sponsorUrlsArray, caption, approved: false, createdAt: Date.now(), expiresAt, uniqueViewers: [] });\n      saveData();\n      document.getElementById('postMsg').innerHTML = 'Post submitted for admin approval.';\n      document.getElementById('activityImgUrl').value = ''; document.getElementById('sponsorUrls').value = ''; document.getElementById('captionText').value = ''; document.getElementById('activityImgFile').value = '';\n      if(currentUserRole === 'admin') renderAdminPanel();\n      renderDashboard();\n    };\n    if(fileInput.files && fileInput.files[0]) {\n      const reader = new FileReader();\n      reader.onload = e => processPost(e.target.result);\n      reader.readAsDataURL(fileInput.files[0]);\n    } else { if(!imageUrl) { document.getElementById('postMsg').innerText = 'Provide image'; return; } processPost(imageUrl); }\n  }\n\n  \/\/ Authentication functions\n  function advertiserLogin(email, password) {\n    const user = advertisers.find(a => a.email === email && a.password === password && a.role === 'advertiser');\n    if(!user) { document.getElementById('advLoginError').innerText = 'Invalid credentials'; return false; }\n    if(!user.approved) { document.getElementById('advLoginError').innerText = 'Account pending admin approval.'; return false; }\n    currentLoggedInUser = user.email;\n    currentUserRole = 'advertiser';\n    return true;\n  }\n  function adminLogin(email, password) {\n    if(email === ADMIN_EMAIL && password === ADMIN_PASS) {\n      currentLoggedInUser = ADMIN_EMAIL;\n      currentUserRole = 'admin';\n      return true;\n    }\n    document.getElementById('adminLoginError').innerText = 'Invalid admin credentials';\n    return false;\n  }\n\n  function showMainApp() {\n    document.getElementById('dualLoginContainer').style.display = 'none';\n    document.getElementById('app').style.display = 'block';\n    document.getElementById('currentUserEmail').innerText = currentLoggedInUser + (currentUserRole === 'admin' ? ' (Admin)' : '');\n    renderStrip();\n    if(currentUserRole === 'advertiser') renderDashboard();\n    if(currentUserRole === 'admin') renderAdminPanel();\n    setInterval(() => renderStrip(), 6000);\n  }\n\n  function logout() {\n    currentLoggedInUser = null;\n    currentUserRole = null;\n    document.getElementById('dualLoginContainer').style.display = 'flex';\n    document.getElementById('app').style.display = 'none';\n    document.getElementById('advLoginError').innerText = '';\n    document.getElementById('adminLoginError').innerText = '';\n    document.getElementById('advLoginEmail').value = '';\n    document.getElementById('advLoginPassword').value = '';\n    document.getElementById('adminLoginEmail').value = '';\n    document.getElementById('adminLoginPassword').value = '';\n  }\n\n  \/\/ Signup logic\n  function showSignupModal() { document.getElementById('signupModal').style.display = 'flex'; }\n  function hideSignupModal() { document.getElementById('signupModal').style.display = 'none'; }\n  function advertiserSignup() {\n    const name = document.getElementById('signupName').value.trim();\n    const email = document.getElementById('signupEmail').value.trim();\n    const pwd = document.getElementById('signupPassword').value;\n    if(!name || !email || !pwd) { document.getElementById('signupMsg').innerHTML = '<span style=\"color:red;\">All fields required<\/span>'; return; }\n    if(advertisers.find(a => a.email === email)) { document.getElementById('signupMsg').innerHTML = '<span style=\"color:red;\">Email already exists<\/span>'; return; }\n    advertisers.push({ id: Date.now(), name, email, password: pwd, approved: false, walletBalance: 0, role: \"advertiser\" });\n    saveData();\n    document.getElementById('signupMsg').innerHTML = '<span style=\"color:green;\">Signup successful! Wait for admin approval.<\/span>';\n    setTimeout(() => hideSignupModal(), 1500);\n    document.getElementById('signupName').value = '';\n    document.getElementById('signupEmail').value = '';\n    document.getElementById('signupPassword').value = '';\n  }\n\n  \/\/ Export functions\n  function exportToExcel() {\n    const approvedUsers = advertisers.filter(u => u.approved && u.role === 'advertiser').map(u => ({ Name: u.name, Email: u.email, Wallet_UGX: u.walletBalance || 0 }));\n    const ws = XLSX.utils.json_to_sheet(approvedUsers);\n    const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, \"Advertisers\");\n    XLSX.writeFile(wb, `advertisers_${Date.now()}.xlsx`);\n  }\n  async function exportToPDF() {\n    const { jsPDF } = window.jspdf;\n    const doc = new jsPDF();\n    doc.text(\"Approved Advertisers\", 14, 10);\n    const data = advertisers.filter(u => u.approved && u.role === 'advertiser').map(u => [u.name, u.email, (u.walletBalance || 0) + \" UGX\"]);\n    doc.autoTable({ head: [[\"Name\", \"Email\", \"Wallet\"]], body: data, startY: 20 });\n    doc.save(`advertisers_${Date.now()}.pdf`);\n  }\n\n  function initTabs() {\n    const btns = document.querySelectorAll('.tab-btn');\n    const panels = {\n      public: document.getElementById('publicPanel'),\n      dashboard: document.getElementById('dashboardPanel'),\n      upload: document.getElementById('uploadPanel'),\n      admin: document.getElementById('adminPanel')\n    };\n    btns.forEach(btn => {\n      btn.addEventListener('click', () => {\n        const tab = btn.dataset.tab;\n        btns.forEach(b=>b.classList.remove('active'));\n        btn.classList.add('active');\n        Object.values(panels).forEach(p=>p.style.display='none');\n        if(tab==='public') panels.public.style.display='block';\n        if(tab==='dashboard') { \n          if(currentUserRole === 'advertiser') { panels.dashboard.style.display='block'; renderDashboard(); }\n          else { showToast('Only advertisers can access dashboard'); document.querySelector('[data-tab=\"public\"]').click(); }\n        }\n        if(tab==='upload') { panels.upload.style.display='block'; }\n        if(tab==='admin') {\n          if(currentUserRole === 'admin') { panels.admin.style.display='block'; renderAdminPanel(); }\n          else { alert('Admin access only'); document.querySelector('[data-tab=\"public\"]').click(); }\n        }\n      });\n    });\n  }\n\n  \/\/ Event listeners\n  document.getElementById('advLoginBtn')?.addEventListener('click', () => {\n    const email = document.getElementById('advLoginEmail').value;\n    const pwd = document.getElementById('advLoginPassword').value;\n    if(advertiserLogin(email, pwd)) showMainApp();\n  });\n  document.getElementById('adminLoginBtn')?.addEventListener('click', () => {\n    const email = document.getElementById('adminLoginEmail').value;\n    const pwd = document.getElementById('adminLoginPassword').value;\n    if(adminLogin(email, pwd)) showMainApp();\n  });\n  document.getElementById('showAdvertiserSignup')?.addEventListener('click', showSignupModal);\n  document.getElementById('closeSignupModal')?.addEventListener('click', hideSignupModal);\n  document.getElementById('confirmSignupBtn')?.addEventListener('click', advertiserSignup);\n  document.getElementById('createPostBtn')?.addEventListener('click', createPost);\n  document.getElementById('dashboardWithdrawBtn')?.addEventListener('click', requestWithdrawalDashboard);\n  document.getElementById('exportExcelBtn')?.addEventListener('click', exportToExcel);\n  document.getElementById('exportPdfBtn')?.addEventListener('click', exportToPDF);\n  document.getElementById('logoutMainBtn')?.addEventListener('click', logout);\n\n  loadData();\n  initTabs();\n  \/\/ Show dual login panel initially\n  document.getElementById('dualLoginContainer').style.display = 'flex';\n  document.getElementById('app').style.display = 'none';\n<\/script>\n<\/body>\n<\/html>\n\n\n\n<!--nextpage-->\n\n\n\n<meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n  <title>CashChat Strip | Earn per View &amp; Admin Wallet<\/title>\n  <!-- Font Awesome 6 -->\n  <link rel=\"stylesheet\" href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/font-awesome\/6.0.0-beta3\/css\/all.min.css\">\n  <!-- SheetJS for Excel\/PDF export -->\n  <script src=\"https:\/\/cdn.sheetjs.com\/xlsx-0.20.2\/package\/dist\/xlsx.full.min.js\"><\/script>\n  <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jspdf\/2.5.1\/jspdf.umd.min.js\"><\/script>\n  <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jspdf-autotable\/3.8.2\/jspdf.plugin.autotable.min.js\"><\/script>\n  <style>\n    * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Roboto, sans-serif; }\n    body { background: #f0f2f5; padding: 20px; }\n    .app { max-width: 1600px; margin: 0 auto; }\n    .login-overlay {\n      position: fixed; top:0; left:0; width:100%; height:100%; background: rgba(0,0,0,0.85);\n      display: flex; align-items: center; justify-content: center; z-index: 1000;\n      backdrop-filter: blur(6px);\n    }\n    .login-card {\n      background: white; border-radius: 32px; padding: 32px; width: 360px; box-shadow: 0 20px 40px rgba(0,0,0,0.3);\n    }\n    .login-card h2 { margin-bottom: 20px; }\n    .form-group { margin-bottom: 16px; }\n    input { width: 100%; padding: 12px; border-radius: 40px; border: 1px solid #ccc; }\n    button { background: #2c6e9e; color: white; border: none; padding: 10px 20px; border-radius: 40px; cursor: pointer; font-weight: bold; }\n    .tabs { display: flex; gap: 12px; margin-bottom: 24px; flex-wrap: wrap; }\n    .tab-btn { background: #e2e8f0; color: #1a2a3a; padding: 8px 20px; border-radius: 40px; }\n    .tab-btn.active { background: #2c6e9e; color: white; }\n    .panel { background: white; border-radius: 28px; padding: 24px; margin-bottom: 24px; box-shadow: 0 8px 20px rgba(0,0,0,0.05); }\n    .strip-container { background: #0f172a; border-radius: 32px; padding: 20px 12px; overflow-x: hidden; }\n    .strip-track { display: flex; gap: 24px; animation: scrollLeftToRight 35s linear infinite; width: max-content; }\n    .strip-container:hover .strip-track { animation-play-state: paused; }\n    @keyframes scrollLeftToRight { 0% { transform: translateX(0%); } 100% { transform: translateX(-50%); } }\n    .strip-card { width: 270px; background: white; border-radius: 24px; overflow: hidden; box-shadow: 0 10px 20px rgba(0,0,0,0.2); flex-shrink: 0; }\n    .strip-img { width: 100%; height: 180px; object-fit: cover; }\n    .strip-info { padding: 12px; }\n    .sponsor-badge { background: #f1f5f9; border-radius: 40px; padding: 4px 12px; font-size: 0.7rem; display: inline-flex; align-items: center; gap: 6px; margin: 2px; }\n    .grid { display: flex; flex-wrap: wrap; gap: 20px; margin-top: 20px; }\n    .post-preview-card { width: 280px; background: #fefce8; border-radius: 24px; border: 1px solid #ddd; overflow: hidden; }\n    .wallet-info { background: #e6f7e6; border-radius: 20px; padding: 12px; margin-bottom: 16px; }\n    .export-buttons { margin: 10px 0; display: flex; gap: 12px; }\n    .badge { background: #e2e8f0; padding: 4px 12px; border-radius: 30px; font-size: 0.7rem; }\n    hr { margin: 16px 0; }\n    .flex-between { display: flex; justify-content: space-between; align-items: center; }\n  <\/style>\n\n\n<div class=\"app\" id=\"app\" style=\"display: none;\">\n  <div class=\"tabs\">\n    <button class=\"tab-btn active\" data-tab=\"public\">\ud83d\udcf8 Moving Strip<\/button>\n    <button class=\"tab-btn\" data-tab=\"upload\">\ud83d\udce4 Upload Post<\/button>\n    <button class=\"tab-btn\" data-tab=\"admin\">\ud83d\udee1\ufe0f Admin Panel<\/button>\n  <\/div>\n  <!-- public strip -->\n  <div id=\"publicPanel\" class=\"panel\"><h2><i class=\"fas fa-images\"><\/i> Activity Strip (Left \u2192 Right)<\/h2><div class=\"strip-container\"><div class=\"strip-track\" id=\"stripTrack\"><\/div><\/div><p class=\"badge\">\ud83d\udc41\ufe0f Each unique view = UGX 3 to sponsor&#8217;s wallet<\/p><\/div>\n  <!-- upload panel -->\n  <div id=\"uploadPanel\" class=\"panel\" style=\"display:none;\"><h2>\ud83d\udce4 Create Sponsored Post<\/h2><div id=\"userWalletDisplay\" class=\"wallet-info\"><\/div>\n    <div class=\"form-group\"><label>Your Email (approved user)<\/label><select id=\"userSelectUpload\"><\/select><\/div>\n    <div class=\"form-group\"><label>Activity Image (URL or file)<\/label><input type=\"file\" id=\"activityImgFile\" accept=\"image\/*\"><input type=\"text\" id=\"activityImgUrl\" placeholder=\"https:\/\/...\"><\/div>\n    <div class=\"form-group\"><label>Sponsor Logos (URLs, comma separated)<\/label><input type=\"text\" id=\"sponsorUrls\" placeholder=\"https:\/\/logo1.png, https:\/\/logo2.png\"><\/div>\n    <div class=\"form-group\"><label>Caption<\/label><textarea id=\"captionText\" rows=\"2\"><\/textarea><\/div>\n    <div class=\"form-group\"><label>\ud83d\udcc5 Visibility (number of days this post stays active)<\/label><input type=\"number\" id=\"visibilityDays\" min=\"1\" max=\"90\" value=\"7\"><\/div>\n    <button id=\"createPostBtn\">\ud83d\ude80 Submit Post (needs admin approval)<\/button><div id=\"postMsg\" style=\"margin-top:10px;\"><\/div>\n    <hr><h3>\ud83d\udcb0 My Wallet<\/h3><div id=\"userWalletDetails\"><\/div><div class=\"form-group\"><label>Withdraw amount (UGX)<\/label><input type=\"number\" id=\"withdrawAmount\" placeholder=\"Enter amount\"><button id=\"requestWithdrawBtn\">\ud83d\udcb8 Request Withdrawal<\/button><\/div><div id=\"withdrawMsg\"><\/div>\n  <\/div>\n  <!-- admin panel -->\n  <div id=\"adminPanel\" class=\"panel\" style=\"display:none;\"><h2>Admin Dashboard<\/h2><div class=\"flex-between\"><div><strong>\ud83d\udc51 Admin Wallet Balance: <span id=\"adminWalletSpan\">0<\/span> UGX<\/strong><\/div><div class=\"export-buttons\"><button id=\"exportExcelBtn\"><i class=\"fas fa-file-excel\"><\/i> Excel<\/button><button id=\"exportPdfBtn\"><i class=\"fas fa-file-pdf\"><\/i> PDF<\/button><\/div><\/div>\n    <h3>\ud83d\udc65 Approved Users + Wallets<\/h3><div id=\"approvedUsersTableContainer\"><\/div><h3>\ud83d\udcdd Pending Posts (preview &amp; approve\/decline)<\/h3><div id=\"pendingPostsGrid\" class=\"grid\"><\/div><h3>\ud83d\udcb8 Withdrawal Requests<\/h3><div id=\"withdrawRequestsList\"><\/div><h3>\u2705 Active Approved Posts<\/h3><div id=\"approvedPostsAdminList\"><\/div>\n  <\/div>\n<\/div>\n<div id=\"loginModal\" class=\"login-overlay\"><div class=\"login-card\"><h2><i class=\"fas fa-lock\"><\/i> Admin Login<\/h2><div class=\"form-group\"><label>Email<\/label><input type=\"email\" id=\"adminEmail\" placeholder=\"ads@cashchat.se\"><\/div><div class=\"form-group\"><label>Password<\/label><input type=\"password\" id=\"adminPassword\" placeholder=\"********\"><\/div><button id=\"loginBtn\">Access Admin Panel<\/button><p id=\"loginError\" style=\"color:red; margin-top:10px;\"><\/p><\/div><\/div>\n<script>\n  \/\/ ---------- DATA MODELS ----------\n  let users = [];        \/\/ { id, name, email, approved, walletBalance }\n  let posts = [];        \/\/ { id, userId, activityImageUrl, sponsorUrlsArray, caption, approved, createdAt, expiresAt, uniqueViewers }\n  let withdrawalRequests = []; \/\/ { id, userId, amount, status, requestedAt }\n  let adminWallet = 0;\n\n  \/\/ Helper: save to localStorage (hide sensitive)\n  function saveData() {\n    localStorage.setItem('cc_users', JSON.stringify(users));\n    localStorage.setItem('cc_posts', JSON.stringify(posts));\n    localStorage.setItem('cc_withdrawals', JSON.stringify(withdrawalRequests));\n    localStorage.setItem('cc_adminWallet', adminWallet);\n  }\n  function loadData() {\n    const u = localStorage.getItem('cc_users'); if(u) users = JSON.parse(u);\n    const p = localStorage.getItem('cc_posts'); if(p) posts = JSON.parse(p);\n    const w = localStorage.getItem('cc_withdrawals'); if(w) withdrawalRequests = JSON.parse(w);\n    const aw = localStorage.getItem('cc_adminWallet'); if(aw) adminWallet = Number(aw);\n    if(users.length === 0) { \/\/ seed demo approved users + pending\n      users.push({ id: 1, name: \"Alice Sponsor\", email: \"alice@chat.com\", approved: true, walletBalance: 12000 });\n      users.push({ id: 2, name: \"Bob Marketer\", email: \"bob@cashchat.se\", approved: true, walletBalance: 5000 });\n      users.push({ id: 3, name: \"Charlie Pending\", email: \"charlie@test.com\", approved: false, walletBalance: 0 });\n      adminWallet = 25000;\n      posts.push({ id: 101, userId: 1, activityImageUrl: \"https:\/\/picsum.photos\/id\/20\/400\/250\", sponsorUrlsArray: [\"https:\/\/picsum.photos\/id\/1\/40\/40\"], caption: \"Charity run 2025\", approved: true, createdAt: Date.now(), expiresAt: Date.now() + 30*86400000, uniqueViewers: [] });\n      posts.push({ id: 102, userId: 2, activityImageUrl: \"https:\/\/picsum.photos\/id\/29\/400\/250\", sponsorUrlsArray: [\"https:\/\/picsum.photos\/id\/13\/40\/40\"], caption: \"Bike festival\", approved: true, createdAt: Date.now(), expiresAt: Date.now() + 15*86400000, uniqueViewers: [] });\n      saveData();\n    }\n    \/\/ ensure each user has walletBalance\n    users.forEach(u => { if(u.walletBalance === undefined) u.walletBalance = 0; });\n    posts.forEach(p => { if(!p.uniqueViewers) p.uniqueViewers = []; if(!p.expiresAt) p.expiresAt = Date.now() + 7*86400000; });\n    saveData();\n  }\n\n  \/\/ Helper functions\n  function escapeHtml(str) { if(!str) return ''; return str.replace(\/[&<>]\/g, function(m){if(m==='&') return '&amp;'; if(m==='<') return '&lt;'; if(m==='>') return '&gt;'; return m;}); }\n\n  \/\/ Unique view tracking: when a post is shown to a user (by session per day approx, using localStorage + postId)\n  function recordUniqueView(postId) {\n    const post = posts.find(p => p.id === postId);\n    if(!post || !post.approved) return false;\n    const storageKey = `viewed_post_${postId}`;\n    const alreadyViewed = localStorage.getItem(storageKey);\n    if(alreadyViewed) return false;\n    \/\/ new unique view\n    localStorage.setItem(storageKey, 'true');\n    if(!post.uniqueViewers) post.uniqueViewers = [];\n    post.uniqueViewers.push(Date.now());\n    \/\/ Credit sponsor wallet UGX 3 per unique view\n    const sponsorUser = users.find(u => u.id === post.userId);\n    if(sponsorUser && sponsorUser.approved) {\n      sponsorUser.walletBalance = (sponsorUser.walletBalance || 0) + 3;\n      saveData();\n    }\n    saveData();\n    return true;\n  }\n\n  \/\/ render moving strip (filter only approved & not expired)\n  function renderStrip() {\n    const now = Date.now();\n    const activePosts = posts.filter(p => p.approved === true && p.expiresAt > now);\n    if(activePosts.length === 0) { document.getElementById('stripTrack').innerHTML = `<div style=\"color:white; padding:20px;\">No active sponsored posts<\/div>`; return; }\n    \/\/ duplicate for seamless\n    let html = '';\n    for(let rep=0; rep<3; rep++) {\n      activePosts.forEach(post => {\n        \/\/ register view attempt (unique)\n        recordUniqueView(post.id);\n        html += createStripCardHTML(post);\n      });\n    }\n    document.getElementById('stripTrack').innerHTML = html;\n  }\n\n  function createStripCardHTML(post) {\n    const sponsorHtml = (post.sponsorUrlsArray || []).map(url => `<div class=\"sponsor-badge\"><img loading=\"lazy\" decoding=\"async\" src=\"${escapeHtml(url)}\" width=\"24\" height=\"24\" onerror=\"this.style.display='none'\"> Sponsor<\/div>`).join('');\n    return `<div class=\"strip-card\"><img decoding=\"async\" class=\"strip-img\" src=\"${escapeHtml(post.activityImageUrl)}\" onerror=\"this.src='https:\/\/placehold.co\/400x250'\"><div class=\"strip-info\"><div class=\"sponsor-logos\">${sponsorHtml}<\/div><div class=\"caption-text\">${escapeHtml(post.caption)}<\/div><div class=\"user-name\"><i class=\"fas fa-coins\"><\/i> UGX 3\/view<\/div><\/div><\/div>`;\n  }\n\n  \/\/ Admin Panel Rendering\n  function renderAdminPanel() {\n    const approvedUsers = users.filter(u => u.approved === true);\n    let userTableHtml = `<table border=\"1\" cellpadding=\"8\" style=\"width:100%; border-collapse: collapse;\"><thead><tr><th>Name<\/th><th>Email<\/th><th>Wallet (UGX)<\/th><\/tr><\/thead><tbody>`;\n    approvedUsers.forEach(u => { userTableHtml += `<tr><td>${escapeHtml(u.name)}<\/td><td>${escapeHtml(u.email)}<\/td><td>${u.walletBalance || 0} UGX<\/td><\/tr>`; });\n    userTableHtml += `<\/tbody><\/table>`;\n    document.getElementById('approvedUsersTableContainer').innerHTML = userTableHtml;\n    document.getElementById('adminWalletSpan').innerText = adminWallet;\n\n    \/\/ pending posts (not approved)\n    const pendingPosts = posts.filter(p => !p.approved);\n    const pendingGrid = document.getElementById('pendingPostsGrid');\n    if(pendingPosts.length === 0) pendingGrid.innerHTML = '<div class=\"badge\">No pending posts<\/div>';\n    else {\n      pendingGrid.innerHTML = pendingPosts.map(post => `\n        <div class=\"post-preview-card\"><img decoding=\"async\" src=\"${escapeHtml(post.activityImageUrl)}\" width=\"100%\" height=\"150\" style=\"object-fit:cover\"><div style=\"padding:12px\"><div><strong>\ud83d\udc64 ${escapeHtml(users.find(u=>u.id===post.userId)?.name || 'user')}<\/strong><\/div><div>\ud83d\udcc5 Days: ${Math.ceil((post.expiresAt - Date.now())\/86400000)} days left<\/div><div>\ud83d\udcac ${escapeHtml(post.caption)}<\/div><div class=\"actions\" style=\"margin-top:12px\"><button class=\"success\" onclick=\"approvePostAdmin(${post.id})\">\u2705 Approve<\/button><button class=\"danger\" onclick=\"declinePostAdmin(${post.id})\">\u274c Decline<\/button><\/div><\/div><\/div>\n      `).join('');\n    }\n\n    \/\/ withdrawal requests pending\n    const pendingWithdraws = withdrawalRequests.filter(w => w.status === 'pending');\n    const withdrawDiv = document.getElementById('withdrawRequestsList');\n    if(pendingWithdraws.length === 0) withdrawDiv.innerHTML = '<div>No pending withdrawal requests<\/div>';\n    else {\n      withdrawDiv.innerHTML = pendingWithdraws.map(req => {\n        const userReq = users.find(u => u.id === req.userId);\n        return `<div style=\"background:#fef9e3; padding:12px; border-radius:20px; margin-bottom:8px;\"><strong>${userReq?.name}<\/strong> (${userReq?.email}) requests ${req.amount} UGX <button onclick=\"approveWithdraw(${req.id})\">\u2705 Approve Withdraw<\/button> <button onclick=\"rejectWithdraw(${req.id})\">\u274c Reject<\/button><\/div>`;\n      }).join('');\n    }\n\n    const activeApproved = posts.filter(p => p.approved && p.expiresAt > Date.now());\n    document.getElementById('approvedPostsAdminList').innerHTML = activeApproved.map(p => `<div style=\"border-bottom:1px solid #ccc; padding:6px;\">\ud83d\udccc ${escapeHtml(p.caption)} | expires: ${new Date(p.expiresAt).toLocaleDateString()} | views: ${p.uniqueViewers?.length || 0} (${(p.uniqueViewers?.length || 0)*3} UGX earned)<\/div>`).join('') || 'None';\n  }\n\n  window.approvePostAdmin = function(postId) {\n    const post = posts.find(p => p.id === postId);\n    if(post) { post.approved = true; saveData(); renderAdminPanel(); renderStrip(); showToast('Post approved & active on strip!'); }\n  };\n  window.declinePostAdmin = function(postId) {\n    posts = posts.filter(p => p.id !== postId);\n    saveData(); renderAdminPanel(); renderStrip(); showToast('Post declined');\n  };\n  window.approveWithdraw = async function(reqId) {\n    const req = withdrawalRequests.find(r => r.id === reqId);\n    if(!req || req.status !== 'pending') return;\n    const user = users.find(u => u.id === req.userId);\n    if(!user) return;\n    if(user.walletBalance >= req.amount && req.amount > 0) {\n      user.walletBalance -= req.amount;\n      adminWallet += req.amount;\n      req.status = 'approved';\n      saveData();\n      renderAdminPanel();\n      if(document.getElementById('uploadPanel').style.display !== 'none') updateUserWalletUI();\n      showToast(`Withdrawal approved: ${req.amount} UGX deducted from user, added to admin wallet`);\n    } else { showToast('Insufficient user wallet balance'); }\n  };\n  window.rejectWithdraw = function(reqId) {\n    const req = withdrawalRequests.find(r => r.id === reqId);\n    if(req) { req.status = 'rejected'; saveData(); renderAdminPanel(); showToast('Withdrawal rejected'); }\n  };\n  window.deletePostGlobal = function(postId) {\n    posts = posts.filter(p => p.id !== postId);\n    saveData(); renderStrip(); renderAdminPanel(); showToast('Post deleted');\n  };\n\n  \/\/ User wallet & withdraw request\n  function updateUserWalletUI() {\n    const selectedEmail = document.getElementById('userSelectUpload').value;\n    if(!selectedEmail) return;\n    const user = users.find(u => u.email === selectedEmail);\n    if(user) {\n      document.getElementById('userWalletDetails').innerHTML = `\ud83d\udcb0 Balance: <strong>${user.walletBalance || 0} UGX<\/strong>`;\n      document.getElementById('userWalletDisplay').innerHTML = `<i class=\"fas fa-wallet\"><\/i> Your Wallet: ${user.walletBalance || 0} UGX (Earn 3 UGX per unique view on your post)`;\n    } else { document.getElementById('userWalletDetails').innerHTML = 'Select user'; }\n  }\n\n  function requestWithdrawal() {\n    const selectedEmail = document.getElementById('userSelectUpload').value;\n    if(!selectedEmail) { document.getElementById('withdrawMsg').innerText = 'Select user first'; return; }\n    const user = users.find(u => u.email === selectedEmail);\n    if(!user || !user.approved) { document.getElementById('withdrawMsg').innerText = 'User not approved'; return; }\n    const amount = parseFloat(document.getElementById('withdrawAmount').value);\n    if(isNaN(amount) || amount <= 0) { document.getElementById('withdrawMsg').innerText = 'Enter valid amount'; return; }\n    if(amount > (user.walletBalance || 0)) { document.getElementById('withdrawMsg').innerText = 'Insufficient wallet balance'; return; }\n    const newReq = { id: Date.now(), userId: user.id, amount: amount, status: 'pending', requestedAt: Date.now() };\n    withdrawalRequests.push(newReq);\n    saveData();\n    document.getElementById('withdrawAmount').value = '';\n    document.getElementById('withdrawMsg').innerHTML = '\u2705 Withdrawal request sent to admin for approval.';\n    if(document.getElementById('adminPanel').style.display !== 'none') renderAdminPanel();\n    showToast('Request submitted');\n  }\n\n  \/\/ Create post with days visibility\n  function createPost() {\n    const selectedEmail = document.getElementById('userSelectUpload').value;\n    if(!selectedEmail) { document.getElementById('postMsg').innerText = 'Select user email'; return; }\n    const user = users.find(u => u.email === selectedEmail);\n    if(!user || !user.approved) { document.getElementById('postMsg').innerText = 'Account not approved by admin.'; return; }\n    let imageUrl = document.getElementById('activityImgUrl').value.trim();\n    const fileInput = document.getElementById('activityImgFile');\n    const days = parseInt(document.getElementById('visibilityDays').value);\n    if(isNaN(days) || days < 1) { document.getElementById('postMsg').innerText = 'Valid days required'; return; }\n    const expiresAt = Date.now() + (days * 86400000);\n    const processPost = (finalUrl) => {\n      const sponsorUrlsRaw = document.getElementById('sponsorUrls').value;\n      let sponsorUrlsArray = sponsorUrlsRaw.split(',').map(s=>s.trim()).filter(s=>s);\n      const caption = document.getElementById('captionText').value.trim();\n      if(!caption || !finalUrl) { document.getElementById('postMsg').innerText = 'Image and caption required'; return; }\n      const newPost = { id: Date.now(), userId: user.id, activityImageUrl: finalUrl, sponsorUrlsArray, caption, approved: false, createdAt: Date.now(), expiresAt, uniqueViewers: [] };\n      posts.push(newPost);\n      saveData();\n      document.getElementById('postMsg').innerHTML = `\ud83d\udce4 Post submitted! Admin will review. Visible for ${days} days after approval.`;\n      document.getElementById('activityImgUrl').value = ''; document.getElementById('sponsorUrls').value = ''; document.getElementById('captionText').value = ''; document.getElementById('activityImgFile').value = '';\n      renderAdminPanel();\n    };\n    if(fileInput.files && fileInput.files[0]) {\n      const reader = new FileReader();\n      reader.onload = function(e) { processPost(e.target.result); };\n      reader.readAsDataURL(fileInput.files[0]);\n    } else { if(!imageUrl) { document.getElementById('postMsg').innerText = 'Provide image URL or file'; return; } processPost(imageUrl); }\n  }\n\n  function populateUserSelectUpload() {\n    const select = document.getElementById('userSelectUpload');\n    select.innerHTML = '<option value=\"\">-- Choose your email --<\/option>';\n    users.forEach(u => { select.innerHTML += `<option value=\"${escapeHtml(u.email)}\">${escapeHtml(u.name)} (${escapeHtml(u.email)}) ${u.approved ? '\u2705' : '\u23f3'}<\/option>`; });\n    select.onchange = () => { updateUserWalletUI(); };\n    if(users.length) updateUserWalletUI();\n  }\n\n  \/\/ Export functions (Excel & PDF)\n  function exportToExcel() {\n    const approvedUsers = users.filter(u => u.approved).map(u => ({ Name: u.name, Email: u.email, Wallet_UGX: u.walletBalance || 0 }));\n    const ws = XLSX.utils.json_to_sheet(approvedUsers);\n    const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, \"ApprovedUsers\");\n    XLSX.writeFile(wb, `approved_users_${new Date().toISOString().slice(0,19)}.xlsx`);\n  }\n  async function exportToPDF() {\n    const { jsPDF } = window.jspdf;\n    const doc = new jsPDF();\n    doc.text(\"Approved Users List with Wallets\", 14, 10);\n    const approvedUsers = users.filter(u => u.approved).map(u => [u.name, u.email, (u.walletBalance || 0) + \" UGX\"]);\n    doc.autoTable({ head: [[\"Name\", \"Email\", \"Wallet\"]], body: approvedUsers, startY: 20 });\n    doc.save(`approved_users_${Date.now()}.pdf`);\n  }\n\n  \/\/ Admin login check\n  function checkAdminLogin(email, pwd) {\n    return (email === 'ads@cashchat.se' && pwd === 'Justtrade81#');\n  }\n\n  \/\/ Session & UI control\n  let adminLoggedIn = false;\n  function showAdminPanelOnlyAfterLogin() {\n    const loginDiv = document.getElementById('loginModal');\n    const appDiv = document.getElementById('app');\n    if(adminLoggedIn) {\n      loginDiv.style.display = 'none';\n      appDiv.style.display = 'block';\n      renderStrip();\n      renderAdminPanel();\n      populateUserSelectUpload();\n      setInterval(() => { renderStrip(); }, 5000);\n    } else {\n      loginDiv.style.display = 'flex';\n      appDiv.style.display = 'none';\n    }\n  }\n\n  document.getElementById('loginBtn')?.addEventListener('click', () => {\n    const email = document.getElementById('adminEmail').value;\n    const pwd = document.getElementById('adminPassword').value;\n    if(checkAdminLogin(email, pwd)) {\n      adminLoggedIn = true;\n      showAdminPanelOnlyAfterLogin();\n    } else { document.getElementById('loginError').innerText = 'Invalid credentials. Access denied.'; }\n  });\n\n  \/\/ Tabs logic\n  function initTabs() {\n    const btns = document.querySelectorAll('.tab-btn');\n    const panels = { public: document.getElementById('publicPanel'), upload: document.getElementById('uploadPanel'), admin: document.getElementById('adminPanel') };\n    btns.forEach(btn => {\n      btn.addEventListener('click', () => {\n        const tab = btn.dataset.tab;\n        btns.forEach(b=>b.classList.remove('active'));\n        btn.classList.add('active');\n        Object.values(panels).forEach(p=>p.style.display='none');\n        if(tab==='public') panels.public.style.display='block';\n        if(tab==='upload') { panels.upload.style.display='block'; populateUserSelectUpload(); updateUserWalletUI(); }\n        if(tab==='admin') { if(adminLoggedIn) { panels.admin.style.display='block'; renderAdminPanel(); } else { alert('Admin access only after login'); document.querySelector('[data-tab=\"public\"]').click(); } }\n      });\n    });\n  }\n\n  \/\/ Event listeners\n  document.getElementById('createPostBtn')?.addEventListener('click', createPost);\n  document.getElementById('requestWithdrawBtn')?.addEventListener('click', requestWithdrawal);\n  document.getElementById('exportExcelBtn')?.addEventListener('click', exportToExcel);\n  document.getElementById('exportPdfBtn')?.addEventListener('click', exportToPDF);\n  window.showToast = function(msg) { let t=document.createElement('div'); t.innerText=msg; t.style.position='fixed'; t.style.bottom='20px'; t.style.right='20px'; t.style.background='#1e293b'; t.style.color='white'; t.style.padding='12px 20px'; t.style.borderRadius='40px'; t.style.zIndex='9999'; document.body.appendChild(t); setTimeout(()=>t.remove(),2500); };\n\n  loadData();\n  initTabs();\n  showAdminPanelOnlyAfterLogin();\n  \/\/ auto refresh strip with view counting\n  setInterval(() => { if(adminLoggedIn) renderStrip(); }, 8000);\n<\/script>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>CashChat Strip | Advertiser + Admin Dual Login | Earn per View Advertiser Login Login as Advertiser New advertiser? Create account Admin Login Login as Admin Demo admin: ads@cashchat.se \/ Justtrade81# Advertiser Signup Sign Up Cancel \ud83d\udcf8 Moving Strip \ud83d\udcca My Dashboard \ud83d\udce4 Create Post \ud83d\udee1\ufe0f Admin Panel Logout Activity Strip (Left \u2192 Right) \ud83d\udc41\ufe0f [&hellip;]<\/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-254","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/cashchat.se\/dir\/wp-json\/wp\/v2\/pages\/254","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=254"}],"version-history":[{"count":5,"href":"https:\/\/cashchat.se\/dir\/wp-json\/wp\/v2\/pages\/254\/revisions"}],"predecessor-version":[{"id":260,"href":"https:\/\/cashchat.se\/dir\/wp-json\/wp\/v2\/pages\/254\/revisions\/260"}],"wp:attachment":[{"href":"https:\/\/cashchat.se\/dir\/wp-json\/wp\/v2\/media?parent=254"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}