{"id":266,"date":"2026-06-12T04:55:37","date_gmt":"2026-06-12T04:55:37","guid":{"rendered":"https:\/\/cashchat.se\/dir\/?page_id=266"},"modified":"2026-06-12T04:55:37","modified_gmt":"2026-06-12T04:55:37","slug":"v5","status":"publish","type":"page","link":"https:\/\/cashchat.se\/dir\/v5\/","title":{"rendered":"v5"},"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 | Approve Users<\/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, .user-preview-card {\n      width: 280px;\n      background: #fefce8;\n      border-radius: 24px;\n      border: 1px solid #ddd;\n      overflow: hidden;\n    }\n    .user-preview-card {\n      background: #fff3e0;\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    .section-title {\n      margin: 20px 0 12px 0;\n      padding-bottom: 8px;\n      border-bottom: 2px solid #2c6e9e;\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 - Password is hidden (no placeholder, no demo text showing the password) -->\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=\"Admin Email\">\n    <\/div>\n    <div class=\"form-group\">\n      <input type=\"password\" id=\"adminLoginPassword\" placeholder=\"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\">\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    <!-- Removed the demo text that showed the password -->\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 - Updated with Pending Users Approval Section -->\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    \n    <!-- NEW SECTION: Pending User Approvals -->\n    <div class=\"section-title\"><i class=\"fas fa-user-check\"><\/i> \ud83d\udccb Pending User Approvals<\/div>\n    <div id=\"pendingUsersList\" class=\"grid\" style=\"background:#f9fafb; padding:16px; border-radius:24px; margin-bottom:20px;\"><\/div>\n    \n    <div class=\"section-title\"><i class=\"fas fa-users\"><\/i> \ud83d\udc65 Approved Advertisers<\/div>\n    <div id=\"approvedUsersTableContainer\" style=\"overflow-x:auto;\"><\/div>\n    \n    <div class=\"section-title\"><i class=\"fas fa-clock\"><\/i> \ud83d\udcdd Pending Posts<\/div>\n    <div id=\"pendingPostsGrid\" class=\"grid\"><\/div>\n    \n    <div class=\"section-title\"><i class=\"fas fa-money-bill-wave\"><\/i> \ud83d\udcb8 Withdrawal Requests<\/div>\n    <div id=\"withdrawRequestsList\"><\/div>\n    \n    <div class=\"section-title\"><i class=\"fas fa-check-circle\"><\/i> \u2705 Active Approved Posts<\/div>\n    <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  \/\/ Admin credentials are hardcoded in the backend only - NOT displayed anywhere in UI\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      advertisers.push({ id: 4, name: \"Diana New\", email: \"diana@new.com\", password: \"diana123\", approved: false, walletBalance: 0, role: \"advertiser\" });\n      \/\/ Admin user (stored in localStorage but password is never shown)\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 with Pending Users section\n  function renderAdminPanel() {\n    \/\/ Pending users (not approved, role advertiser, exclude admin)\n    const pendingUsers = advertisers.filter(u => u.approved === false && u.role === 'advertiser');\n    const pendingUsersDiv = document.getElementById('pendingUsersList');\n    if(pendingUsers.length === 0) {\n      pendingUsersDiv.innerHTML = '<div class=\"badge\">\u2705 No pending user approvals<\/div>';\n    } else {\n      pendingUsersDiv.innerHTML = pendingUsers.map(user => `\n        <div class=\"user-preview-card\" style=\"padding:16px;\">\n          <div><strong><i class=\"fas fa-user\"><\/i> ${escapeHtml(user.name)}<\/strong><\/div>\n          <div><i class=\"fas fa-envelope\"><\/i> ${escapeHtml(user.email)}<\/div>\n          <div><i class=\"fas fa-calendar-alt\"><\/i> Registered: ${new Date(user.id).toLocaleDateString()}<\/div>\n          <div style=\"margin-top:12px; display:flex; gap:8px;\">\n            <button onclick=\"approveUserAdmin(${user.id})\" style=\"background:#0f7b3a; padding:8px 16px;\">\u2705 Approve User<\/button>\n            <button onclick=\"declineUserAdmin(${user.id})\" style=\"background:#b91c1c; padding:8px 16px;\">\u274c Reject<\/button>\n          <\/div>\n        <\/div>\n      `).join('');\n    }\n\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><th>Status<\/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><td>\u2705 Active<\/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  \/\/ New admin functions for user approval\/rejection\n  window.approveUserAdmin = function(userId) {\n    const user = advertisers.find(u => u.id === userId);\n    if(user && user.role === 'advertiser') {\n      user.approved = true;\n      saveData();\n      renderAdminPanel();\n      showToast(`User ${user.name} has been approved!`);\n    }\n  };\n  window.declineUserAdmin = function(userId) {\n    const index = advertisers.findIndex(u => u.id === userId);\n    if(index !== -1 && advertisers[index].role === 'advertiser') {\n      const removed = advertisers.splice(index, 1)[0];\n      saveData();\n      renderAdminPanel();\n      showToast(`User ${removed.name} has been rejected and removed.`);\n    }\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 - admin password is verified but never displayed\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    \/\/ Password is checked against hidden constant - never exposed in UI\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","protected":false},"excerpt":{"rendered":"<p>CashChat Strip | Advertiser + Admin Dual Login | Approve Users Advertiser Login Login as Advertiser New advertiser? Create account Admin Login Login as Admin 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 Each unique view = UGX 3 [&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-266","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/cashchat.se\/dir\/wp-json\/wp\/v2\/pages\/266","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=266"}],"version-history":[{"count":1,"href":"https:\/\/cashchat.se\/dir\/wp-json\/wp\/v2\/pages\/266\/revisions"}],"predecessor-version":[{"id":267,"href":"https:\/\/cashchat.se\/dir\/wp-json\/wp\/v2\/pages\/266\/revisions\/267"}],"wp:attachment":[{"href":"https:\/\/cashchat.se\/dir\/wp-json\/wp\/v2\/media?parent=266"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}