Pipeline: sponsor follow-up board (stages from the ledger, notes/follow-ups/tags, stage messages into chat), gated by pipelineMode with a coming-soon card until launch; training cards can wait on the same switch
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -796,7 +796,7 @@
|
||||
}));
|
||||
|
||||
// site settings: key / value rows; booleans as checkboxes, numbers stay numbers
|
||||
const SITE_META = { noPayoutIds: 'No-payout positions (member #s, comma): linkage only, no buys from them, no joins routed under them', siteName: 'Site name', tagline: 'Tagline', rehearsal: 'Testnet rehearsal banner', defaultSponsorId: 'Fallback sponsor (member #)', walletConnectProjectId: 'WalletConnect project id', moonpayPublicKey: 'MoonPay public key', telegramBotToken: 'Telegram proof feed: bot token', telegramChatId: 'Telegram proof feed: chat id', telegramTopicId: 'Telegram proof feed: topic id (optional)', telegramEvents: 'Telegram proof feed: events (payouts | payouts+purchases | all)', telegramCtaUrl: 'Telegram proof feed: join link under each post', aiCreditsPerGen: 'AI Copy Engine: credits per generation after the free allowance', aiFreeSurge: 'AI Copy Engine: free generations a month at Surge', aiFreeCircuit: 'AI Copy Engine: free generations a month at Circuit', aiFreeNexus: 'AI Copy Engine: free generations a month at Nexus', memberWeeklyEmail: 'Weekly member email to everyone active (1) or only sponsors with a line (0)', leaderboardWeeklyPrize: 'Leaderboard: weekly prize text (optional; blank shows the credit ladder)', leaderboardMonthlyPrize: 'Leaderboard: monthly prize text (optional)', leaderboardWeeklyCredits: 'Leaderboard: weekly credits for 1st,2nd,3rd… (e.g. 1000,500,250; blank = none)', leaderboardMonthlyCredits: 'Leaderboard: monthly credits for 1st,2nd,3rd… (e.g. 5000,2500,1000)', leaderboardAnnounceGeneral: 'Leaderboard: announce winners in the main group too (1/0)', telegramEchoChatId: 'Telegram echo (shared payments topic): chat id', telegramEchoTopicId: 'Telegram echo: topic id', telegramEchoEvents: 'Telegram echo: events (payouts | payouts+purchases | all)', legacyCreditsAdvertiser: 'Legacy welcome credits: former advertisers', legacyCreditsEarner: 'Legacy welcome credits: former earners', pnlFixedMonthlyUsd: 'P&L: fixed monthly cost (USD)' };
|
||||
const SITE_META = { noPayoutIds: 'No-payout positions (member #s, comma): linkage only, no buys from them, no joins routed under them', siteName: 'Site name', tagline: 'Tagline', rehearsal: 'Testnet rehearsal banner', defaultSponsorId: 'Fallback sponsor (member #)', walletConnectProjectId: 'WalletConnect project id', moonpayPublicKey: 'MoonPay public key', telegramBotToken: 'Telegram proof feed: bot token', telegramChatId: 'Telegram proof feed: chat id', telegramTopicId: 'Telegram proof feed: topic id (optional)', telegramEvents: 'Telegram proof feed: events (payouts | payouts+purchases | all)', telegramCtaUrl: 'Telegram proof feed: join link under each post', aiCreditsPerGen: 'AI Copy Engine: credits per generation after the free allowance', aiFreeSurge: 'AI Copy Engine: free generations a month at Surge', aiFreeCircuit: 'AI Copy Engine: free generations a month at Circuit', aiFreeNexus: 'AI Copy Engine: free generations a month at Nexus', pipelineMode: 'Pipeline board: off (coming soon card) | preview (admin account only) | on (everyone)', pipelineEta: 'Pipeline: opening date shown on the coming-soon card (e.g. Sep 28)', memberWeeklyEmail: 'Weekly member email to everyone active (1) or only sponsors with a line (0)', leaderboardWeeklyPrize: 'Leaderboard: weekly prize text (optional; blank shows the credit ladder)', leaderboardMonthlyPrize: 'Leaderboard: monthly prize text (optional)', leaderboardWeeklyCredits: 'Leaderboard: weekly credits for 1st,2nd,3rd… (e.g. 1000,500,250; blank = none)', leaderboardMonthlyCredits: 'Leaderboard: monthly credits for 1st,2nd,3rd… (e.g. 5000,2500,1000)', leaderboardAnnounceGeneral: 'Leaderboard: announce winners in the main group too (1/0)', telegramEchoChatId: 'Telegram echo (shared payments topic): chat id', telegramEchoTopicId: 'Telegram echo: topic id', telegramEchoEvents: 'Telegram echo: events (payouts | payouts+purchases | all)', legacyCreditsAdvertiser: 'Legacy welcome credits: former advertisers', legacyCreditsEarner: 'Legacy welcome credits: former earners', pnlFixedMonthlyUsd: 'P&L: fixed monthly cost (USD)' };
|
||||
function drawSite() {
|
||||
const wrap = $('siteForm');
|
||||
wrap.innerHTML = Object.entries(siteObj).map(([k, v]) => '<div class="kv-row"><span class="k" title="' + esc(k) + '">' + esc(SITE_META[k] || humanize(k)) + '</span>'
|
||||
|
||||
+76
-2
@@ -632,8 +632,8 @@
|
||||
}
|
||||
|
||||
// ── back-office menu: hash-routed panes ───────────────
|
||||
const PANES = ['overview', 'line', 'buy', 'campaigns', 'earn', 'earnings', 'promo', 'training', 'wallet', 'profile'];
|
||||
const TITLES = { overview: 'Overview', line: 'My line', buy: 'Buy packages', campaigns: 'Campaigns',
|
||||
const PANES = ['overview', 'line', 'pipeline', 'buy', 'campaigns', 'earn', 'earnings', 'promo', 'training', 'wallet', 'profile'];
|
||||
const TITLES = { overview: 'Overview', line: 'My line', pipeline: 'Pipeline', buy: 'Buy packages', campaigns: 'Campaigns',
|
||||
earn: 'Earn credits', earnings: 'Earnings', promo: 'Promo tools', training: 'Training',
|
||||
wallet: 'Wallet & account', profile: 'Profile' };
|
||||
function setPane(name) {
|
||||
@@ -654,6 +654,7 @@
|
||||
if (name === 'line') { loadLineage(); loadUplineMessages(); loadCoach(); loadLinkStats(); loadProspects(); }
|
||||
if (name === 'campaigns') ['cTarget', 'cImage', 'cVideoUrl'].forEach(id => { if ($(id)) $(id).value = ''; }); // no residual URL between visits
|
||||
if (name === 'training') loadTraining();
|
||||
if (name === 'pipeline') loadPipeline();
|
||||
document.getElementById('memberArea').classList.remove('side-open'); // close mobile drawer
|
||||
if (location.hash !== '#' + name) history.replaceState(null, '', '#' + name);
|
||||
}
|
||||
@@ -1336,6 +1337,79 @@
|
||||
}
|
||||
// ── prospects: the member's own follow-up list ──
|
||||
let PP_STATUSES = ['new', 'contacted', 'interested', 'joined', 'bought', 'not now'];
|
||||
// ── Pipeline: the follow-up board (Marty, 2026-09-15). Stages come from the server; cards never move by hand ──
|
||||
let PIPE = null, PIPE_KEY = null;
|
||||
const pipeAgo = ts => { const d = Math.floor((Date.now() - ts) / 86400000); return d <= 0 ? 'today' : d === 1 ? 'yesterday' : d < 30 ? d + 'd ago' : new Date(ts).toLocaleDateString(undefined, { month: 'short', day: 'numeric' }); };
|
||||
const pipeInvite = () => { const t = ($('inviteLine') && $('inviteLine').textContent) || ''; return /^https?:\/\//.test(t) ? t : ''; };
|
||||
function pipeCardHtml(c, showStage) {
|
||||
const today = new Date(); today.setHours(23, 59, 59, 999);
|
||||
const due = c.followUp && c.followUp <= today.getTime();
|
||||
const chips = [];
|
||||
if (c.stalled) chips.push('<span class="chip warm">stalled ' + c.quietDays + 'd</span>');
|
||||
if (due) chips.push('<span class="chip warm">follow up</span>');
|
||||
else if (c.followUp) chips.push('<span class="chip flat">' + new Date(c.followUp).toLocaleDateString(undefined, { month: 'short', day: 'numeric' }) + '</span>');
|
||||
if (c.tag) chips.push('<span class="chip flat">' + esc(c.tag) + '</span>');
|
||||
if (c.kind === 'member' && c.buyers) chips.push('<span class="chip flat">' + c.buyers + ' buyer' + (c.buyers === 1 ? '' : 's') + '</span>');
|
||||
const stage = showStage ? (PIPE.stages.find(s => s.key === c.stage) || {}).label || '' : '';
|
||||
return '<div class="pipe-card' + (c.key === PIPE_KEY ? ' on' : '') + '" tabindex="0" data-pk="' + esc(c.key) + '">'
|
||||
+ '<div class="nm"><span>' + esc(c.name) + '</span><small>' + (c.kind === 'prospect' ? 'prospect' : (c.lastSeen ? 'seen ' + pipeAgo(c.lastSeen) : 'never signed in')) + '</small></div>'
|
||||
+ '<div class="sub">' + esc(stage ? stage + ' · ' + c.next : c.next) + '</div>'
|
||||
+ (c.note ? '<div class="note" title="' + esc(c.note) + '">' + esc(c.note) + '</div>' : '')
|
||||
+ (chips.length ? '<div>' + chips.join('') + '</div>' : '') + '</div>';
|
||||
}
|
||||
async function loadPipeline() {
|
||||
try {
|
||||
const r = await (await fetch('/api/my/pipeline')).json();
|
||||
if (r.error) { IAP.status(r.error, 'bad'); return; }
|
||||
if (!r.live) {
|
||||
$('pipeSoon').hidden = false; $('pipeLive').hidden = true;
|
||||
$('pipeSoonEta').textContent = r.eta ? 'Opens ' + r.eta + '. It is on the roadmap so you can see what is coming and when.' : 'It is on the roadmap so you can see what is coming and when.';
|
||||
return;
|
||||
}
|
||||
PIPE = r; $('pipeSoon').hidden = true; $('pipeLive').hidden = false;
|
||||
const badge = $('pipeBadge'); if (badge) { const n = r.counts.due + r.counts.stalled; badge.hidden = !n; badge.textContent = n > 9 ? '9+' : n; }
|
||||
$('pipeDueCount').hidden = !r.due.length; $('pipeDueCount').textContent = r.due.length;
|
||||
$('pipeDue').innerHTML = r.due.length ? '<div class="pipe-due">' + r.due.map(c => pipeCardHtml(c, true)).join('') + '</div>' : '<p class="muted small">Nothing due. Set a follow-up date on any card and it shows up here.</p>';
|
||||
$('pipeSummary').textContent = r.counts.total ? r.counts.total + ' people on your board' + (r.counts.stalled ? ', ' + r.counts.stalled + ' stalled' : '') + '. Cards move on their own when something happens on the ledger; open one to add a note, a follow-up date or a tag.' : 'Nobody on your board yet. Add prospects on My line, and everyone who joins through your link appears here on their own.';
|
||||
$('pipeBoard').innerHTML = r.columns.map(col => '<div class="pipe-col"><h4>' + esc(col.label) + '<span>' + col.cards.length + '</span></h4><p class="hint">' + esc(col.hint) + '</p>' + col.cards.map(c => pipeCardHtml(c, false)).join('') + '</div>').join('');
|
||||
$('pipeLive').querySelectorAll('[data-pk]').forEach(el => {
|
||||
el.addEventListener('click', () => openPipeCard(el.dataset.pk));
|
||||
el.addEventListener('keydown', e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openPipeCard(el.dataset.pk); } });
|
||||
});
|
||||
if (PIPE_KEY) { const c = allPipeCards().find(x => x.key === PIPE_KEY); if (c) fillPipeCard(c); else { PIPE_KEY = null; $('pipeCard').hidden = true; } }
|
||||
} catch (e) { IAP.status('Could not load your pipeline.', 'bad'); }
|
||||
}
|
||||
function allPipeCards() { return PIPE ? PIPE.columns.flatMap(c => c.cards) : []; }
|
||||
function fillPipeCard(c) {
|
||||
$('pipeCardName').textContent = c.name;
|
||||
const stage = (PIPE.stages.find(s => s.key === c.stage) || {}).label || '';
|
||||
$('pipeCardMeta').textContent = stage + (c.kind === 'member' ? ' · joined ' + new Date(c.since).toLocaleDateString() + (c.lastSeen ? ' · last seen ' + pipeAgo(c.lastSeen) : ' · never signed in') + (c.bought ? ' · your qualifying buyer' : '') : ' · prospect' + (c.contact ? ' · ' + c.contact : ''));
|
||||
$('pipeCardNext').innerHTML = '<b>Next for them:</b> ' + esc(c.next) + (c.stalled ? ' <span class="chip warm">quiet ' + c.quietDays + ' days</span>' : '');
|
||||
const say = (c.say || '').replace('{{link}}', pipeInvite());
|
||||
$('pipeSayBlock').hidden = !say; $('pipeSay').textContent = say;
|
||||
$('pipeSend').hidden = c.kind !== 'member' || !c.email;
|
||||
$('pipeFollow').value = c.followUp ? new Date(c.followUp).toISOString().slice(0, 10) : '';
|
||||
const sel = $('pipeTag'); sel.innerHTML = PIPE.tags.map(t => '<option value="' + t + '"' + (t === c.tag ? ' selected' : '') + '>' + (t || 'no tag') + '</option>').join('');
|
||||
$('pipeNote').value = c.note || ''; $('pipeSaved').textContent = '';
|
||||
$('pipeCard').hidden = false;
|
||||
}
|
||||
function openPipeCard(key) {
|
||||
const c = allPipeCards().find(x => x.key === key); if (!c) return;
|
||||
PIPE_KEY = key; $('pipeLive').querySelectorAll('[data-pk]').forEach(el => el.classList.toggle('on', el.dataset.pk === key));
|
||||
fillPipeCard(c); $('pipeCard').scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
}
|
||||
if ($('pipeCardClose')) $('pipeCardClose').addEventListener('click', () => { PIPE_KEY = null; $('pipeCard').hidden = true; $('pipeLive').querySelectorAll('[data-pk]').forEach(el => el.classList.remove('on')); });
|
||||
if ($('pipeSave')) $('pipeSave').addEventListener('click', async () => {
|
||||
if (!PIPE_KEY) return;
|
||||
try { await api('/api/my/pipeline/note', { key: PIPE_KEY, note: $('pipeNote').value, followUp: $('pipeFollow').value || null, tag: $('pipeTag').value }); $('pipeSaved').textContent = 'Saved.'; loadPipeline(); }
|
||||
catch (e) { IAP.status(e.message, 'bad'); }
|
||||
});
|
||||
if ($('pipeCopy')) $('pipeCopy').addEventListener('click', async () => { try { await navigator.clipboard.writeText($('pipeSay').textContent); $('pipeCopy').textContent = 'Copied'; setTimeout(() => { $('pipeCopy').textContent = 'Copy message'; }, 1500); } catch (e) { IAP.status('Copy failed; select the text by hand.', 'bad'); } });
|
||||
if ($('pipeSend')) $('pipeSend').addEventListener('click', () => {
|
||||
const c = allPipeCards().find(x => x.key === PIPE_KEY); if (!c || !c.email) return;
|
||||
openConvo(c.email, c.name);
|
||||
const ta = $('chatInput'); if (ta) { ta.value = $('pipeSay').textContent; ta.dispatchEvent(new Event('input')); ta.focus(); }
|
||||
});
|
||||
async function loadProspects() {
|
||||
try {
|
||||
const r = await (await fetch('/api/my/prospects')).json();
|
||||
|
||||
@@ -730,3 +730,21 @@ img{max-width:100%}
|
||||
.camp-table td.act{display:flex;flex-wrap:wrap;justify-content:flex-start;gap:8px;margin-top:8px;padding-top:10px;border-top:1px solid var(--line)} .camp-table td.act::before{display:none}
|
||||
.camp-table td.act .btn{flex:1 1 auto;text-align:center}
|
||||
}
|
||||
|
||||
/* Pipeline board (Marty, 2026-09-15): columns scroll sideways inside their own box, never the page */
|
||||
.pipe-board{display:flex;gap:10px;overflow-x:auto;padding:4px 2px 10px;scroll-snap-type:x proximity}
|
||||
.pipe-col{flex:0 0 220px;scroll-snap-align:start;background:rgba(4,8,7,.45);border:1px solid var(--line);border-radius:12px;padding:10px;min-height:120px}
|
||||
.pipe-col h4{margin:0 0 2px;font-size:13px;letter-spacing:.06em;text-transform:uppercase;color:var(--mint);display:flex;justify-content:space-between;gap:8px}
|
||||
.pipe-col h4 span{color:var(--muted);font-variant-numeric:tabular-nums}
|
||||
.pipe-col .hint{font-size:11.5px;color:var(--muted);margin:0 0 8px;line-height:1.35}
|
||||
.pipe-card{background:rgba(255,255,255,.04);border:1px solid var(--line);border-radius:10px;padding:8px 10px;margin:0 0 8px;cursor:pointer;transition:border-color .15s}
|
||||
.pipe-card:hover,.pipe-card:focus-visible{border-color:var(--mint);outline:none}
|
||||
.pipe-card.on{border-color:var(--gold,#e6c15a)}
|
||||
.pipe-card .nm{font-weight:700;font-size:14px;display:flex;justify-content:space-between;gap:6px;align-items:baseline}
|
||||
.pipe-card .nm small{font-weight:400;font-size:11px;color:var(--muted);white-space:nowrap}
|
||||
.pipe-card .sub{font-size:12px;color:var(--muted);margin-top:3px;line-height:1.35}
|
||||
.pipe-card .note{font-size:12px;color:var(--ink);margin-top:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||
.pipe-card .chip{margin-top:5px;margin-right:4px}
|
||||
.pipe-due{display:grid;gap:6px}
|
||||
.pipe-due .pipe-card{margin:0}
|
||||
@media (max-width:640px){.pipe-form{grid-template-columns:1fr !important}.pipe-col{flex-basis:200px}}
|
||||
|
||||
+39
-2
@@ -5,7 +5,7 @@
|
||||
<title>Member area | InstantAdPay</title>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Sora:wght@600;700;800&display=swap">
|
||||
<link rel="icon" type="image/png" href="/logo-icon.png">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=20260915b">
|
||||
<link rel="stylesheet" href="/assets/site.css?v=20260915c">
|
||||
</head>
|
||||
<body class="bo-body">
|
||||
|
||||
@@ -130,6 +130,7 @@
|
||||
<nav class="bo-menu" aria-label="Member menu">
|
||||
<button data-pane="overview" class="on" type="button"><svg viewBox="0 0 24 24"><rect x="3" y="3" width="8" height="8" rx="2"/><rect x="13" y="3" width="8" height="8" rx="2"/><rect x="3" y="13" width="8" height="8" rx="2"/><rect x="13" y="13" width="8" height="8" rx="2"/></svg>Overview</button>
|
||||
<button data-pane="line" type="button"><svg viewBox="0 0 24 24"><circle cx="12" cy="7" r="3.2"/><circle cx="5" cy="17" r="2.6"/><circle cx="19" cy="17" r="2.6"/><path d="M12 10v3M12 13l-5 2M12 13l5 2"/></svg>My line</button>
|
||||
<button data-pane="pipeline" type="button"><svg viewBox="0 0 24 24"><rect x="3" y="4" width="5" height="16" rx="1.2"/><rect x="9.5" y="4" width="5" height="11" rx="1.2"/><rect x="16" y="4" width="5" height="7" rx="1.2"/></svg>Pipeline<span class="pill" id="pipeBadge" hidden></span></button>
|
||||
<button data-pane="buy" type="button"><svg viewBox="0 0 24 24"><circle cx="9" cy="20" r="1.6"/><circle cx="17" cy="20" r="1.6"/><path d="M3 4h2l2.4 11.2A2 2 0 0 0 9.4 17h8.2a2 2 0 0 0 2-1.6L21 8H6"/></svg>Buy packages</button>
|
||||
<button data-pane="campaigns" type="button"><svg viewBox="0 0 24 24"><path d="M3 11l14-5v12L3 13v-2z"/><path d="M17 8a4 4 0 0 1 0 8M7 13v5a2 2 0 0 0 4 0v-3"/></svg>Campaigns</button>
|
||||
<button data-pane="earn" type="button"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="8.5"/><path d="M12 7.5v9M9 10c0-1.1 1.3-1.8 3-1.8s3 .7 3 1.8-1.3 1.6-3 1.8-3 .7-3 1.8 1.3 1.8 3 1.8 3-.7 3-1.8"/></svg>Earn credits<span class="pill" id="inboxBadge" hidden></span></button>
|
||||
@@ -374,6 +375,42 @@
|
||||
<div class="card ad-foot" id="adSlotPane-line" hidden></div>
|
||||
</div>
|
||||
|
||||
<div class="pane" id="pane-pipeline" hidden>
|
||||
<div class="card" id="pipeSoon" hidden>
|
||||
<h3>Pipeline: coming soon</h3>
|
||||
<p class="muted small" id="pipeSoonText">One board for everyone you are working with. Every prospect and every direct sits in a column by what they have actually done: talking, joined, wallet linked, payouts on, bought, building. The site moves the cards when the event happens on the ledger. You add the part it cannot know: a note, a follow-up date, and the message for that exact stage, sent through the chat you already have.</p>
|
||||
<p class="muted small" id="pipeSoonEta"></p>
|
||||
<p><a class="btn small sec" href="/whats-new" target="_blank" rel="noopener">See the roadmap</a></p>
|
||||
</div>
|
||||
<div id="pipeLive" hidden>
|
||||
<div class="card" id="pipeDueCard">
|
||||
<h3>Follow up today <span class="pill" id="pipeDueCount" hidden></span></h3>
|
||||
<p class="muted small">Cards with a follow-up date of today or earlier. Cards flagged <b>stalled</b> have been quiet for three days or more.</p>
|
||||
<div id="pipeDue"><p class="muted small">Nothing due. Set a follow-up date on any card and it shows up here.</p></div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Your board</h3>
|
||||
<p class="muted small" id="pipeSummary">Every column is a stage the site can prove. Cards move on their own; open one to add a note, a follow-up date or a tag.</p>
|
||||
<div class="pipe-board" id="pipeBoard"></div>
|
||||
</div>
|
||||
<div class="card" id="pipeCard" hidden>
|
||||
<div style="display:flex;justify-content:space-between;gap:10px;align-items:flex-start;flex-wrap:wrap">
|
||||
<div><h3 id="pipeCardName" style="margin:0 0 4px"></h3><p class="muted small" id="pipeCardMeta" style="margin:0"></p></div>
|
||||
<button class="btn small sec" type="button" id="pipeCardClose">Close</button>
|
||||
</div>
|
||||
<p class="small" id="pipeCardNext" style="margin:10px 0 6px"></p>
|
||||
<div class="promo-block" id="pipeSayBlock" hidden><div class="pb-head"><span class="pb-net">Message for this stage</span></div><div class="pb-text" id="pipeSay"></div>
|
||||
<p style="margin:8px 0 0"><button class="btn small" type="button" id="pipeSend">Open chat with this message</button> <button class="btn small sec" type="button" id="pipeCopy">Copy message</button></p></div>
|
||||
<div style="display:grid;gap:10px;grid-template-columns:1fr 1fr;margin-top:12px" class="pipe-form">
|
||||
<label class="small muted">Follow up on<br><input type="date" id="pipeFollow" class="small" style="width:100%"></label>
|
||||
<label class="small muted">Tag<br><select id="pipeTag" class="small" style="width:100%"></select></label>
|
||||
</div>
|
||||
<label class="small muted" style="display:block;margin-top:10px">Your note<br><textarea id="pipeNote" rows="3" style="width:100%" maxlength="1000" placeholder="What you talked about, what they said, what is next."></textarea></label>
|
||||
<p style="margin:10px 0 0"><button class="btn small" type="button" id="pipeSave">Save</button> <span class="muted small" id="pipeSaved"></span></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card ad-foot" id="adSlotPane-pipeline" hidden></div>
|
||||
</div>
|
||||
<div class="pane" id="pane-buy" hidden>
|
||||
<div class="card">
|
||||
<h3>Buy an ad package</h3>
|
||||
@@ -984,7 +1021,7 @@
|
||||
<script src="/assets/common.js?v=20260914a"></script>
|
||||
<script src="/assets/wallet.js?v=20260911a"></script>
|
||||
<script src="/assets/promo.js?v=20260911a"></script>
|
||||
<script src="/assets/my.js?v=20260915b"></script>
|
||||
<script src="/assets/my.js?v=20260915c"></script>
|
||||
<script src="/assets/chat.js?v=20260907l"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user