Modules
5
Total available
📚
Completed
0
Modules finished
Points Earned
125
From training
Certificates
0
Earned so far
🏆
Click to Watch Lesson
⏱ 12:00 min

Lesson Title

📹 Video
12 min
⭐ 25 Points

Lesson description will appear here.

Module Lessons

0 of 0 completed

Progress0%
📎 Resources

Quiz

Test your understanding

Question 1 of 5
Question text here
Module Quiz
80%
4 of 5 correct
✅ Passed
Review Your Answers
TAG Markets Success Academy
This is to certify that
Arjun Rathi
Certificate of Completion
has successfully completed
Module 1: Mindset & Vision
Issued: May 30, 2026
🏆
Mr. TAG Leader
Founder & CEO
Academy Director
Training Head

📚 Module Progress

⭐ Points History

🎯
Training: Module 1 Lesson 1
May 28, 2026
+25
🎯
Completed Module 1 Quiz
May 28, 2026
+50
🏆
Contest Participation Bonus
May 25, 2026
+50
Total Points 125

🏅 My Badges

🥉
Bronze Achiever
🥈
Silver Achiever
Complete 2 modules
🥇
Gold Achiever
Complete 4 modules
💎
Diamond Leader
Complete all modules

🏆 My Certificates

Complete a module to earn your first certificate! 🎓
All Lessons
Upload Lesson
Manage Modules
All Lessons
15 lessons across 5 modules
# Lesson Title Module Type Duration Status Actions
Complete a module to earn your first certificate 🎓
⏳ Pending (3)
✅ Reviewed
All
const status = state.submissionStatus[lesson.id] || 'pending'; const isVideo = lesson.assignmentType === 'video'; document.getElementById('assignmentPanelTitle').textContent = isVideo ? '🎬 Video Submission Required' : '📋 Assignment Submission'; document.getElementById('assignmentPanelDesc').textContent = lesson.desc; // Status badge const statusHTML = { pending: `
⏳ Not submitted yet
`, submitted:`
📤 Under Review
`, reviewed: `
✅ Reviewed — feedback ready
`, }; document.getElementById('subStatusWrap').innerHTML = statusHTML[status]; const uploadSection = document.getElementById('uploadSection'); const submittedView = document.getElementById('submittedView'); if (status === 'submitted' || status === 'reviewed') { uploadSection.style.display = 'none'; submittedView.style.display = 'block'; } else { uploadSection.style.display = 'block'; submittedView.style.display = 'none'; document.getElementById('uploadIcon').textContent = isVideo ? '🎬' : '📄'; document.getElementById('uploadText').textContent = isVideo ? 'Click to select your video file' : 'Click to select your document'; document.getElementById('uploadSub').textContent = isVideo ? 'MP4, MOV, AVI — max 500MB' : 'PDF, DOCX — max 50MB'; const fileInfo = document.getElementById('fileSelectedInfo'); if (state.selectedFile && state.selectedFile.lessonId === lesson.id) { fileInfo.style.display = 'block'; fileInfo.innerHTML = `✅ ${state.selectedFile.name} selected — ready to submit`; document.getElementById('uploadArea').style.borderColor = 'var(--success)'; } else { fileInfo.style.display = 'none'; document.getElementById('uploadArea').style.borderColor = ''; } } } function renderAssignmentPanel(lesson) { const status = state.submissionStatus[lesson.id] || 'pending'; const isVideo = lesson.assignmentType === 'video'; document.getElementById('assignmentPanelTitle').textContent = isVideo ? '🎬 Video Submission Required' : '📋 Assignment Submission'; document.getElementById('assignmentPanelDesc').textContent = lesson.desc; const statusHTML = { pending: `
⏳ Not submitted yet
`, submitted: `
📤 Under Review
`, reviewed: `
✅ Reviewed — feedback ready
`, }; document.getElementById('subStatusWrap').innerHTML = statusHTML[status] || statusHTML.pending; const uploadSection = document.getElementById('uploadSection'); const submittedView = document.getElementById('submittedView'); if (status === 'submitted' || status === 'reviewed') { uploadSection.style.display = 'none'; submittedView.style.display = 'block'; } else { uploadSection.style.display = 'block'; submittedView.style.display = 'none'; document.getElementById('uploadIcon').textContent = isVideo ? '🎬' : '📄'; document.getElementById('uploadText').textContent = isVideo ? 'Click to select your video file' : 'Click to select your document'; document.getElementById('uploadSub').textContent = isVideo ? 'MP4, MOV, AVI — max 500MB' : 'PDF, DOCX — max 50MB'; const fileInfo = document.getElementById('fileSelectedInfo'); if (state.selectedFile && state.selectedFile.lessonId === lesson.id) { fileInfo.style.display = 'block'; fileInfo.innerHTML = `✅ ${state.selectedFile.name} — ready to submit`; document.getElementById('uploadArea').style.borderColor = 'var(--success)'; } else { fileInfo.style.display = 'none'; document.getElementById('uploadArea').style.borderColor = ''; } } } function handleFileSelect() { const lesson = state.currentLesson; const isVideo = lesson.assignmentType === 'video'; const fakeNames = isVideo ? ['my_presentation.mp4', 'role_play_recording.mov', 'submission_video.mp4'] : ['prospect_list_100.pdf', 'assignment_submission.pdf', 'completed_task.pdf']; const name = fakeNames[Math.floor(Math.random()*fakeNames.length)]; state.selectedFile = { name, lessonId: lesson.id }; showToast(`📎 File selected: ${name}`); renderAssignmentPanel(lesson); } function submitAssignment() { const lesson = state.currentLesson; if (!state.selectedFile || state.selectedFile.lessonId !== lesson.id) { showToast('⚠️ Please select a file first'); return; } state.submissionStatus[lesson.id] = 'submitted'; state.selectedFile = null; addPoints(25, `Assignment submitted: ${lesson.title}`); showToast('📤 Submitted! Your trainer will review shortly.'); renderAssignmentPanel(lesson); } function switchLesson(lessonId) { const lesson = state.currentModule.lessons.find(l => l.id === lessonId); state.currentLesson = lesson; renderLessonView(); } function playVideo() { const lesson = state.currentLesson; const playerEl = document.getElementById('videoPlayer'); if (lesson.videoUrl) { document.getElementById('videoPlaceholder').style.display = 'none'; const iframe = document.createElement('iframe'); iframe.src = lesson.videoUrl + '?autoplay=1'; iframe.style.cssText = 'width:100%;height:100%;border:none;'; iframe.allowFullscreen = true; iframe.allow = 'autoplay; fullscreen'; playerEl.appendChild(iframe); } else { showToast('▶ Video lesson coming soon! Check back shortly.'); } } function markVideoWatched() { playVideo(); } function markLessonComplete() { const lesson = state.currentLesson; if (state.lessonProgress[lesson.id]) return; state.lessonProgress[lesson.id] = true; addPoints(25, `Lesson complete: ${lesson.title}`); showToast('✅ Lesson complete! +25 points earned'); const mod = state.currentModule; const nonAssign = mod.lessons.filter(l => l.type !== 'assignment'); const allNonAssignDone = nonAssign.every(l => state.lessonProgress[l.id]); const allDone = mod.lessons.every(l => state.lessonProgress[l.id]); if (allNonAssignDone && mod.quiz.length > 0 && !state.completedModules.includes(mod.id)) { setTimeout(() => showToast('📝 All lessons done! Take the quiz to earn your certificate.'), 1300); } else if (allDone && !state.completedModules.includes(mod.id)) { setTimeout(() => completeModule(mod.id), 1300); } renderLessonView(); updateStats(); } function nextLesson() { const mod = state.currentModule; const idx = mod.lessons.findIndex(l => l.id === state.currentLesson.id); if (idx < mod.lessons.length - 1) { state.currentLesson = mod.lessons[idx + 1]; renderLessonView(); } else { showToast('🎉 Last lesson! Take the quiz to complete the module.'); } } function completeModule(modId) { if (state.completedModules.includes(modId)) { openCertificate(modId); return; } state.completedModules.push(modId); const mod = modules.find(m => m.id === modId); addPoints(mod.points, `Module bonus: ${mod.title}`); checkAllBadges(); updateStats(); showModal('🏆 Module Complete!', `
🎓
Congratulations!

You've completed Module ${modId}: ${mod.title}!

BONUS POINTS EARNED
+${mod.points}
`, [{ label:'🏆 View Certificate', action:`closeModal();openCertificate(${modId})`, cls:'btn-gold' }, { label:'Continue', action:'closeModal();showView("academy")', cls:'btn-outline' }] ); } // ═══════════════════════════════════════════════ // QUIZ SYSTEM // ═══════════════════════════════════════════════ function showQuizView() { const mod = state.currentModule; document.getElementById('quizBreadMod').textContent = `Module ${mod.id}`; document.getElementById('quizPageTitle').textContent = `Module ${mod.id} Quiz`; document.getElementById('quizTitle').textContent = `Module ${mod.id}: ${mod.title} — Quiz`; document.getElementById('quizSubTitle').textContent = `${mod.quiz.length} questions · Pass with 70%+`; state.currentQuestionIdx = 0; state.quizAnswers = new Array(mod.quiz.length).fill(null); renderQuizQuestion(); } function renderQuizQuestion() { const mod = state.currentModule; const idx = state.currentQuestionIdx; const q = mod.quiz[idx]; const strip = document.getElementById('quizStrip'); strip.innerHTML = ''; mod.quiz.forEach((_,i) => { strip.innerHTML += `
`; }); document.getElementById('quizQNum').textContent = `Question ${idx+1} of ${mod.quiz.length}`; document.getElementById('quizQuestion').textContent = q.q; const optContainer = document.getElementById('quizOptions'); optContainer.innerHTML = ''; q.opts.forEach((opt, oi) => { const selected = state.quizAnswers[idx] === oi; optContainer.innerHTML += `
${selected?'●':''}
${opt}
`; }); document.getElementById('prevQBtn').style.visibility = idx===0?'hidden':'visible'; const isLast = idx === mod.quiz.length - 1; const nextBtn = document.getElementById('nextQBtn'); nextBtn.textContent = isLast ? 'Submit Quiz ✓' : 'Next →'; nextBtn.className = `btn ${isLast?'btn-gold':'btn-primary'}`; } function selectAnswer(oi) { state.quizAnswers[state.currentQuestionIdx] = oi; renderQuizQuestion(); } function nextQuestion() { if (state.quizAnswers[state.currentQuestionIdx] === null) { showToast('⚠️ Please select an answer first'); return; } if (state.currentQuestionIdx < state.currentModule.quiz.length - 1) { state.currentQuestionIdx++; renderQuizQuestion(); } else submitQuiz(); } function prevQuestion() { if (state.currentQuestionIdx > 0) { state.currentQuestionIdx--; renderQuizQuestion(); } } function submitQuiz() { const mod = state.currentModule; const correct = state.quizAnswers.filter((a,i) => a === mod.quiz[i].ans).length; const total = mod.quiz.length; const pct = Math.round((correct / total) * 100); const passed = pct >= 70; // Award points addPoints(passed ? 50 : 10, `Quiz ${passed?'passed':'attempted'}: Module ${mod.id}`); if (pct === 100) { checkBadge('quiz'); setTimeout(()=>showToast('🎯 Perfect score! Quiz Champion badge unlocked!'), 800); } // Build review screen document.getElementById('reviewBreadMod').textContent = `Module ${mod.id}`; document.getElementById('reviewModTitle').textContent = `Module ${mod.id}: ${mod.title} — Quiz`; const scoreEl = document.getElementById('reviewScorePct'); scoreEl.textContent = pct + '%'; scoreEl.style.color = passed ? 'var(--gold-light)' : '#ff9999'; document.getElementById('reviewCorrectCount').textContent = `${correct} of ${total} correct`; const passBadge = document.getElementById('reviewPassBadge'); passBadge.textContent = passed ? '✅ Passed! Certificate Unlocked' : '❌ Not yet — 70% required'; passBadge.style.background = passed ? 'rgba(34,197,94,0.2)' : 'rgba(239,68,68,0.2)'; passBadge.style.color = passed ? '#86efac' : '#fca5a5'; const nextBtn = document.getElementById('reviewNextBtn'); nextBtn.style.display = passed ? 'inline-flex' : 'none'; // Answer review list const reviewList = document.getElementById('reviewAnswersList'); reviewList.innerHTML = ''; mod.quiz.forEach((q, i) => { const userAns = state.quizAnswers[i]; const isCorrect = userAns === q.ans; reviewList.innerHTML += `
${isCorrect?'✓':'✗'}
Q${i+1}. ${q.q}
${q.opts.map((opt, oi) => { const isCorrectOpt = oi === q.ans; const isUserWrong = oi === userAns && !isCorrect; let style = 'background:var(--gray-50);border:1px solid var(--gray-200);color:var(--gray-700);'; let icon = '○'; if (isCorrectOpt) { style='background:rgba(34,197,94,0.08);border:1px solid rgba(34,197,94,0.4);color:#16a34a;font-weight:600;'; icon='✓'; } else if (isUserWrong) { style='background:rgba(239,68,68,0.08);border:1px solid rgba(239,68,68,0.4);color:var(--danger);'; icon='✗'; } return `
${icon}${opt}
`; }).join('')}
💡 Explanation: ${q.exp || 'This is the correct answer.'}
`; }); showView('quizreview'); } function retryQuiz() { state.currentQuestionIdx = 0; state.quizAnswers = new Array(state.currentModule.quiz.length).fill(null); showView('quiz'); } function afterQuizPass() { completeModule(state.currentModule.id); } // ═══════════════════════════════════════════════ // CERTIFICATE // ═══════════════════════════════════════════════ function openCertificate(modId) { const mod = modules.find(m => m.id === modId); document.getElementById('certModuleName').textContent = `Module ${mod.id}: ${mod.title}`; document.getElementById('certDate').textContent = `Issued: ${new Date().toLocaleDateString('en-US', {year:'numeric',month:'long',day:'numeric'})}`; showView('certificate'); } // ═══════════════════════════════════════════════ // POINTS & BADGES // ═══════════════════════════════════════════════ function addPoints(pts, action) { state.totalPoints += pts; state.pointsHistory.unshift({ action, pts, date: new Date().toLocaleDateString('en-US',{month:'short',day:'numeric',year:'numeric'}), icon:'🎯' }); // Update all points displays ['navPoints','totalPointsDisplay'].forEach(id => { const el = document.getElementById(id); if (el) el.textContent = state.totalPoints; }); checkBadge('star'); } function checkAllBadges() { const n = state.completedModules.length; BADGE_DEFS.forEach(b => { if (b.modReq > 0 && n >= b.modReq) checkBadge(b.id); if (b.ptsReq > 0 && state.totalPoints >= b.ptsReq) checkBadge(b.id); }); } function checkBadge(badgeId) { if (state.earnedBadges.includes(badgeId)) return; const badge = BADGE_DEFS.find(b => b.id === badgeId); if (!badge) return; if (badge.ptsReq > 0 && state.totalPoints < badge.ptsReq) return; if (badge.modReq > 0 && state.completedModules.length < badge.modReq) return; state.earnedBadges.push(badgeId); setTimeout(() => showToast(`🏅 Badge Unlocked: ${badge.icon} ${badge.name}!`), 600); } // ═══════════════════════════════════════════════ // PROGRESS VIEW // ═══════════════════════════════════════════════ function renderProgressView() { const container = document.getElementById('moduleProgressList'); container.innerHTML = ''; modules.forEach(mod => { const done = mod.lessons.filter(l => state.lessonProgress[l.id]).length; const pct = Math.round((done / mod.lessons.length) * 100); container.innerHTML += `
${mod.icon}
M${mod.id}: ${mod.title} ${state.completedModules.includes(mod.id)?'':''}
${pct}%
`; }); // Points history const phList = document.getElementById('pointsHistoryList'); phList.innerHTML = state.pointsHistory.slice(0,6).map(h => `
${h.icon}
${h.action}
${h.date}
+${h.pts}
`).join(''); document.getElementById('totalPointsProg').textContent = state.totalPoints; // Badges const badgesGrid = document.getElementById('badgesGrid'); badgesGrid.innerHTML = BADGE_DEFS.map(b => { const earned = state.earnedBadges.includes(b.id); return `
${b.icon}
${b.name}
${earned ? '✓ Earned' : b.req}
`; }).join(''); // Certificates const certsList = document.getElementById('myCertsList'); if (state.completedModules.length === 0) { certsList.innerHTML = '
Complete a module to earn your first certificate 🎓
'; } else { certsList.innerHTML = state.completedModules.map(modId => { const mod = modules.find(m => m.id === modId); return `
🏆
Module ${mod.id}: ${mod.title}
Issued: ${new Date().toLocaleDateString('en-US',{month:'long',day:'numeric',year:'numeric'})}
`; }).join(''); } } // ═══════════════════════════════════════════════ // ADMIN — LESSONS TABLE // ═══════════════════════════════════════════════ function renderAdminLessons() { const tbody = document.getElementById('adminLessonsBody'); tbody.innerHTML = ''; let rowIdx = 1; modules.forEach(mod => { mod.lessons.forEach(lesson => { const tc = { video:'video', pdf:'pdf', assignment:'assignment', quiz:'quiz' }; tbody.innerHTML += ` ${rowIdx++}
${lesson.title}
${lesson.desc.substring(0,55)}...
M${mod.id} ${lesson.type} ${lesson.duration} ● Published
`; }); }); } function renderAdminModules() { const container = document.getElementById('adminModulesGrid'); container.innerHTML = ''; modules.forEach(mod => { container.innerHTML += `
${mod.id}
${mod.icon}
Published
Module ${mod.id}: ${mod.title}
${mod.lessons.length} lessons · ${mod.quiz.length} quiz questions
⭐ ${mod.points} pts
`; }); } function switchAdminTab(tab, el) { document.querySelectorAll('#view-admin .admin-tab').forEach(t => t.classList.remove('active')); el.classList.add('active'); ['lessons','upload','modules'].forEach(t => { document.getElementById(`adminTab-${t}`).style.display = t===tab?'block':'none'; }); if (tab==='lessons') renderAdminLessons(); if (tab==='modules') renderAdminModules(); } // ═══════════════════════════════════════════════ // QUIZ BUILDER // ═══════════════════════════════════════════════ function openQuizBuilder(modId) { showView('quizbuilder'); document.getElementById('qbModSelect').value = modId; loadQuizBuilder(modId); } function loadQuizBuilder(modId) { const container = document.getElementById('quizBuilderContent'); if (!modId) { container.innerHTML = '
Select a module above to manage its quiz questions.
'; return; } const mod = modules.find(m => m.id == modId); let qHTML = mod.quiz.map((q, i) => `
Question ${i+1}
${q.q}
${q.opts.map((opt,oi) => `${oi===q.ans?'✓ ':''}${opt}`).join('')}
💡 ${q.exp||'No explanation added.'}
`).join(''); container.innerHTML = `
Module ${mod.id}: ${mod.title}
${mod.quiz.length} questions · Pass score: 70%
${qHTML || '
No questions yet — click "+ Add Question" to start building.
'}
`; } function showAddQuestionModal(modId) { showModal(`Add Question — Module ${modId}`, `
`, [{ label:'Save Question', action:`saveNewQuestion(${modId})`, cls:'btn-primary' }, { label:'Cancel', action:'closeModal()', cls:'btn-outline' }] ); } function saveNewQuestion(modId) { const q = document.getElementById('newQ').value.trim(); const opts = [0,1,2,3].map(i => document.getElementById(`newOpt${i}`).value.trim()).filter(Boolean); const ans = parseInt(document.getElementById('newAns').value); const exp = document.getElementById('newExp').value.trim(); if (!q || opts.length < 2) { showToast('⚠️ Question and at least 2 options are required'); return; } const mod = modules.find(m => m.id == modId); mod.quiz.push({ q, opts, ans, exp: exp || 'Correct!' }); closeModal(); showToast('✅ Question added to Module ' + modId); loadQuizBuilder(modId); } function editQuestion(modId, idx) { const mod = modules.find(m => m.id == modId); const q = mod.quiz[idx]; showModal(`Edit Question ${idx+1}`, `
${q.opts.map((opt,oi) => `
`).join('')}
`, [{ label:'Save Changes', action:`saveEditQuestion(${modId},${idx})`, cls:'btn-primary' }, { label:'Cancel', action:'closeModal()', cls:'btn-outline' }] ); } function saveEditQuestion(modId, idx) { const mod = modules.find(m => m.id == modId); mod.quiz[idx].q = document.getElementById('editQ').value.trim(); mod.quiz[idx].opts = mod.quiz[idx].opts.map((_,oi) => { const el = document.getElementById(`editOpt${oi}`); return el ? el.value.trim() : _; }); mod.quiz[idx].ans = parseInt(document.getElementById('editAns').value); mod.quiz[idx].exp = document.getElementById('editExp').value.trim(); closeModal(); showToast('✅ Question updated'); loadQuizBuilder(modId); } function deleteQuestion(modId, idx) { if (!confirm('Delete this question?')) return; modules.find(m => m.id == modId).quiz.splice(idx,1); showToast('🗑 Question deleted'); loadQuizBuilder(modId); } // ═══════════════════════════════════════════════ // SUBMISSIONS (ADMIN) // ═══════════════════════════════════════════════ function renderSubmissions(filter) { const container = document.getElementById('submissionsContent'); const list = filter === 'all' ? FAKE_SUBMISSIONS : FAKE_SUBMISSIONS.filter(s => s.status === filter); if (!list.length) { container.innerHTML = '
No submissions in this category.
'; return; } container.innerHTML = list.map(s => `
${s.initials}
${s.name} ${s.status==='pending'?'⏳ Pending':'✅ Reviewed'}
${s.module} · ${s.lesson}
Submitted: ${s.date}
${s.notes?`
"${s.notes}"
`:''}
${s.status==='pending'?` `:``}
`).join(''); } function approveSubmission(id) { const sub = FAKE_SUBMISSIONS.find(s => s.id === id); sub.status = 'reviewed'; showToast(`✅ ${sub.name}'s submission approved! +100 pts awarded.`); renderSubmissions('pending'); } function switchSubTab(tab, el) { document.querySelectorAll('#view-submissions .admin-tab').forEach(t => t.classList.remove('active')); el.classList.add('active'); renderSubmissions(tab); } // ═══════════════════════════════════════════════ // NAVIGATION // ═══════════════════════════════════════════════ function showView(viewId) { document.querySelectorAll('.view').forEach(v => v.classList.remove('active')); document.getElementById('view-' + viewId).classList.add('active'); document.querySelectorAll('.sidebar-item').forEach(i => i.classList.remove('active')); if (viewId === 'academy') { renderModulesGrid('modulesGrid'); document.querySelectorAll('.sidebar-item')[0].classList.add('active'); } if (viewId === 'progress') { renderProgressView(); document.querySelectorAll('.sidebar-item')[1].classList.add('active'); } if (viewId === 'certificates'){ renderCertificatesPage(); document.querySelectorAll('.sidebar-item')[2].classList.add('active'); } if (viewId === 'quiz') showQuizView(); if (viewId === 'admin') renderAdminLessons(); if (viewId === 'submissions') renderSubmissions('pending'); state.currentView = viewId; } function renderCertificatesPage() { const container = document.getElementById('certsPageList'); if (!state.completedModules.length) { container.innerHTML = '
Complete a module to earn your first certificate 🎓
'; return; } container.innerHTML = state.completedModules.map(modId => { const mod = modules.find(m => m.id === modId); return `
🏆
Certificate of Completion
Module ${mod.id}: ${mod.title}
`; }).join(''); } function toggleRole() { state.role = state.role === 'member' ? 'admin' : 'member'; document.getElementById('roleLabel').textContent = state.role === 'admin' ? 'Admin' : 'Member'; const show = state.role === 'admin' ? 'flex' : 'none'; ['adminSidebarItem','adminQBItem','adminSubItem'].forEach(id => { const el = document.getElementById(id); if (el) el.style.display = show; }); showToast(`👤 Switched to ${state.role === 'admin' ? 'Admin' : 'Member'} view`); } // ═══════════════════════════════════════════════ // STATS // ═══════════════════════════════════════════════ function updateStats() { const completed = state.completedModules.length; document.getElementById('completedCount').textContent = completed; document.getElementById('certCount').textContent = completed; document.getElementById('totalPointsDisplay').textContent = state.totalPoints; document.getElementById('navPoints').textContent = state.totalPoints; const totalLessons = modules.reduce((a,m) => a + m.lessons.length, 0); const doneLessons = Object.keys(state.lessonProgress).filter(k => state.lessonProgress[k]).length; const pct = Math.round((doneLessons / totalLessons) * 100); document.getElementById('overallPct').textContent = pct + '%'; document.getElementById('overallBar').style.width = pct + '%'; document.getElementById('sidebarSub').textContent = `${doneLessons} of ${totalLessons} lessons done`; } // ═══════════════════════════════════════════════ // MODAL // ═══════════════════════════════════════════════ function showModal(title, body, buttons=[]) { document.getElementById('modalTitle').textContent = title; document.getElementById('modalBody').innerHTML = body; const footer = document.getElementById('modalFooter'); footer.innerHTML = ''; buttons.forEach(b => { footer.innerHTML += ``; }); document.getElementById('modalOverlay').classList.add('open'); } function closeModal() { document.getElementById('modalOverlay').classList.remove('open'); } // ═══════════════════════════════════════════════ // TOAST // ═══════════════════════════════════════════════ function showToast(msg) { const wrap = document.getElementById('toastWrap'); const toast = document.createElement('div'); toast.className = 'toast'; toast.innerHTML = `${msg}`; wrap.appendChild(toast); setTimeout(() => { toast.style.opacity='0'; toast.style.transition='opacity 0.4s'; setTimeout(()=>toast.remove(),400); }, 3200); } // ═══════════════════════════════════════════════ // INIT // ═══════════════════════════════════════════════ renderModulesGrid('modulesGrid'); updateStats();