Training center: new members-area pane for videos + materials

New "Training" nav pane lists curated lessons (inline video for mp4/webm, links
for external videos and docs) from /api/training (admin-curated via
data/training.json, with a sensible default).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-09-07 08:41:26 -05:00
parent 42d0e15fea
commit 31a6560e9e
3 changed files with 45 additions and 3 deletions
+23 -2
View File
@@ -285,6 +285,26 @@
if (toast) { IAP.status(toast, kind); if (sound) playSound(sound); liveRefresh(); }
}
// training center: videos + materials (admin-curated via data/training.json)
async function loadTraining() {
try {
const r = await (await fetch('/api/training')).json();
const el = $('trainingList'); if (!el) return;
const items = r.items || [];
if (!items.length) { el.innerHTML = '<p class="muted small">Training materials are being added. Check back soon.</p>'; return; }
el.innerHTML = items.map(it => {
const isVid = it.videoUrl && /\.(mp4|webm)(\?|$)/i.test(it.videoUrl);
const media = isVid ? '<video src="' + esc(it.videoUrl) + '" controls playsinline style="width:100%;max-width:640px;border-radius:12px;background:#000"></video>' : '';
const links = [];
if (it.videoUrl && !isVid) links.push('<a class="btn small sec" href="' + esc(it.videoUrl) + '" target="_blank" rel="noopener">Watch</a>');
if (it.docUrl) links.push('<a class="btn small sec" href="' + esc(it.docUrl) + '" target="_blank" rel="noopener">Open material</a>');
return '<div class="card"><h3>' + esc(it.title || 'Lesson') + '</h3>'
+ (it.desc ? '<p class="muted small">' + esc(it.desc) + '</p>' : '')
+ (media ? '<p style="margin:10px 0">' + media + '</p>' : '')
+ (links.length ? '<p>' + links.join(' ') + '</p>' : '') + '</div>';
}).join('');
} catch (e) {}
}
// visual line tree on the Overview: YOU + three levels, qualified directs in gold
async function loadLineTree(buyerCount) {
try {
@@ -409,9 +429,9 @@
}
// ── back-office menu: hash-routed panes ───────────────
const PANES = ['overview', 'line', 'buy', 'campaigns', 'earn', 'earnings', 'promo', 'wallet', 'profile'];
const PANES = ['overview', 'line', 'buy', 'campaigns', 'earn', 'earnings', 'promo', 'training', 'wallet', 'profile'];
const TITLES = { overview: 'Overview', line: 'My line', buy: 'Buy packages', campaigns: 'Campaigns',
earn: 'Earn credits', earnings: 'Earnings', promo: 'Promo tools',
earn: 'Earn credits', earnings: 'Earnings', promo: 'Promo tools', training: 'Training',
wallet: 'Wallet & account', profile: 'Profile' };
function setPane(name) {
if (!PANES.includes(name)) name = 'overview';
@@ -426,6 +446,7 @@
if (name === 'profile') loadLineBanner();
if (name === 'line') { loadLineage(); loadUplineMessages(); }
if (name === 'campaigns') ['cTarget', 'cImage', 'cVideoUrl'].forEach(id => { if ($(id)) $(id).value = ''; }); // no residual URL between visits
if (name === 'training') loadTraining();
document.getElementById('memberArea').classList.remove('side-open'); // close mobile drawer
if (location.hash !== '#' + name) history.replaceState(null, '', '#' + name);
}