/**
* Learn Module - JavaScript Controller
* Learning Management System for General Bots
*/
// State management
const LearnState = {
courses: [],
myCourses: [],
mandatoryAssignments: [],
certificates: [],
categories: [],
currentCourse: null,
currentLesson: null,
currentQuiz: null,
quizAnswers: {},
quizTimer: null,
quizTimeRemaining: 0,
currentQuestionIndex: 0,
filters: {
category: "all",
difficulty: ["beginner", "intermediate", "advanced"],
search: "",
sort: "recent",
},
pagination: {
offset: 0,
limit: 12,
hasMore: true,
},
userStats: {
coursesCompleted: 0,
coursesInProgress: 0,
certificates: 0,
timeSpent: 0,
},
};
// API Base URL
const LEARN_API = "/api/learn";
// ============================================================================
// INITIALIZATION
// ============================================================================
document.addEventListener("DOMContentLoaded", () => {
initLearn();
});
function initLearn() {
loadUserStats();
loadCategories();
loadCourses();
loadMyCourses();
loadMandatoryAssignments();
loadCertificates();
loadRecommendations();
bindEvents();
}
function bindEvents() {
// Search input
const searchInput = document.getElementById("searchCourses");
if (searchInput) {
let searchTimeout;
searchInput.addEventListener("input", (e) => {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(() => {
LearnState.filters.search = e.target.value;
LearnState.pagination.offset = 0;
loadCourses();
}, 300);
});
}
// Sort select
const sortSelect = document.getElementById("sortCourses");
if (sortSelect) {
sortSelect.addEventListener("change", (e) => {
LearnState.filters.sort = e.target.value;
LearnState.pagination.offset = 0;
loadCourses();
});
}
// Category filters
document.querySelectorAll(".category-item").forEach((item) => {
item.addEventListener("click", () => {
document
.querySelectorAll(".category-item")
.forEach((i) => i.classList.remove("active"));
item.classList.add("active");
LearnState.filters.category = item.dataset.category;
LearnState.pagination.offset = 0;
loadCourses();
});
});
// Difficulty filters
document.querySelectorAll("[data-difficulty]").forEach((checkbox) => {
checkbox.addEventListener("change", () => {
LearnState.filters.difficulty = Array.from(
document.querySelectorAll("[data-difficulty]:checked"),
).map((cb) => cb.dataset.difficulty);
LearnState.pagination.offset = 0;
loadCourses();
});
});
// Close modals on background click
document.querySelectorAll(".modal").forEach((modal) => {
modal.addEventListener("click", (e) => {
if (e.target === modal) {
modal.classList.add("hidden");
}
});
});
// Keyboard shortcuts
document.addEventListener("keydown", (e) => {
if (e.key === "Escape") {
closeAllModals();
}
});
}
// ============================================================================
// API CALLS
// ============================================================================
async function apiCall(endpoint, options = {}) {
try {
const response = await fetch(`${LEARN_API}${endpoint}`, {
headers: {
"Content-Type": "application/json",
...options.headers,
},
...options,
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || "API request failed");
}
return data;
} catch (error) {
console.error("API Error:", error);
showNotification("error", "Erro ao carregar dados. Tente novamente.");
throw error;
}
}
async function loadUserStats() {
try {
const response = await apiCall("/stats/user");
if (response.success) {
LearnState.userStats = response.data;
updateUserStatsUI();
}
} catch (error) {
// Use mock data if API fails
updateUserStatsUI();
}
}
async function loadCategories() {
try {
const response = await apiCall("/categories");
if (response.success) {
LearnState.categories = response.data;
updateCategoryCounts();
}
} catch (error) {
// Categories loaded from HTML
}
}
async function loadCourses() {
try {
const params = new URLSearchParams({
limit: LearnState.pagination.limit,
offset: LearnState.pagination.offset,
});
if (LearnState.filters.category !== "all") {
params.append("category", LearnState.filters.category);
}
if (LearnState.filters.search) {
params.append("search", LearnState.filters.search);
}
if (LearnState.filters.difficulty.length < 3) {
params.append("difficulty", LearnState.filters.difficulty.join(","));
}
const response = await apiCall(`/courses?${params}`);
if (response.success) {
if (LearnState.pagination.offset === 0) {
LearnState.courses = response.data;
} else {
LearnState.courses = [...LearnState.courses, ...response.data];
}
LearnState.pagination.hasMore =
response.data.length >= LearnState.pagination.limit;
renderCourses();
}
} catch (error) {
// Render mock courses for demo
renderMockCourses();
}
}
async function loadMyCourses() {
try {
const response = await apiCall("/progress");
if (response.success) {
LearnState.myCourses = response.data;
renderMyCourses();
}
} catch (error) {
renderMockMyCourses();
}
}
async function loadMandatoryAssignments() {
try {
const response = await apiCall("/assignments/pending");
if (response.success) {
LearnState.mandatoryAssignments = response.data;
renderMandatoryAssignments();
updateMandatoryAlert();
}
} catch (error) {
renderMockMandatory();
}
}
async function loadCertificates() {
try {
const response = await apiCall("/certificates");
if (response.success) {
LearnState.certificates = response.data;
renderCertificates();
}
} catch (error) {
renderMockCertificates();
}
}
async function loadRecommendations() {
try {
const response = await apiCall("/recommendations");
if (response.success) {
renderRecommendations(response.data);
}
} catch (error) {
renderMockRecommendations();
}
}
async function loadCourseDetail(courseId) {
try {
const response = await apiCall(`/courses/${courseId}`);
if (response.success) {
LearnState.currentCourse = response.data;
renderCourseModal(response.data);
}
} catch (error) {
showMockCourseDetail(courseId);
}
}
async function startCourseAPI(courseId) {
try {
const response = await apiCall(`/progress/${courseId}/start`, {
method: "POST",
});
if (response.success) {
showNotification("success", "Curso iniciado com sucesso!");
loadMyCourses();
return response.data;
}
} catch (error) {
showNotification("error", "Erro ao iniciar o curso.");
}
}
async function completeLessonAPI(lessonId) {
try {
const response = await apiCall(`/progress/${lessonId}/complete`, {
method: "POST",
});
if (response.success) {
showNotification("success", "Aula concluída!");
return response.data;
}
} catch (error) {
showNotification("error", "Erro ao marcar aula como concluída.");
}
}
async function submitQuizAPI(courseId, answers) {
try {
const response = await apiCall(`/courses/${courseId}/quiz`, {
method: "POST",
body: JSON.stringify({ answers }),
});
if (response.success) {
return response.data;
}
} catch (error) {
showNotification("error", "Erro ao enviar respostas.");
}
}
// ============================================================================
// UI RENDERING
// ============================================================================
function updateUserStatsUI() {
document.getElementById("statCoursesCompleted").textContent =
LearnState.userStats.courses_completed || 0;
document.getElementById("statCoursesInProgress").textContent =
LearnState.userStats.courses_in_progress || 0;
document.getElementById("statCertificates").textContent =
LearnState.userStats.certificates_earned || 0;
document.getElementById("statTimeSpent").textContent =
`${LearnState.userStats.total_time_spent_hours || 0}h`;
}
function updateCategoryCounts() {
// Update category counts based on courses
const counts = {};
LearnState.courses.forEach((course) => {
counts[course.category] = (counts[course.category] || 0) + 1;
});
document.getElementById("countAll").textContent = LearnState.courses.length;
document.getElementById("countMandatory").textContent =
LearnState.mandatoryAssignments.length;
}
function renderCourses() {
const grid = document.getElementById("coursesGrid");
const countLabel = document.getElementById("coursesCountLabel");
if (!grid) return;
if (LearnState.pagination.offset === 0) {
grid.innerHTML = "";
}
if (LearnState.courses.length === 0) {
grid.innerHTML = `
📚
Nenhum curso encontrado
Tente ajustar os filtros de busca.
`;
countLabel.textContent = "0 cursos";
return;
}
LearnState.courses.forEach((course) => {
grid.appendChild(createCourseCard(course));
});
countLabel.textContent = `${LearnState.courses.length} cursos`;
// Show/hide load more button
const loadMore = document.getElementById("loadMore");
if (loadMore) {
loadMore.style.display = LearnState.pagination.hasMore ? "block" : "none";
}
}
function createCourseCard(course) {
const card = document.createElement("div");
card.className = "course-card";
card.onclick = () => openCourseModal(course.id);
const difficultyClass = (course.difficulty || "beginner").toLowerCase();
const progress = course.user_progress || 0;
card.innerHTML = `
${
course.thumbnail_url
? `

`
: `
📖`
}
${course.is_mandatory ? '
Obrigatório' : ""}
${progress > 0 ? `
${progress}%` : ""}
${escapeHtml(course.title)}
${
progress > 0
? `
`
: ""
}
`;
return card;
}
function renderMyCourses() {
const continueLearning = document.getElementById("continueLearning");
const completedCourses = document.getElementById("completedCourses");
if (!continueLearning || !completedCourses) return;
const inProgress = LearnState.myCourses.filter(
(c) => c.status === "in_progress",
);
const completed = LearnState.myCourses.filter(
(c) => c.status === "completed",
);
// Update badge
document.getElementById("myCoursesCount").textContent = inProgress.length;
// Render in-progress courses
if (inProgress.length === 0) {
continueLearning.innerHTML = `
📚
Nenhum curso em andamento
`;
} else {
continueLearning.innerHTML = inProgress
.map((course) => createCourseListItem(course))
.join("");
}
// Render completed courses
if (completed.length === 0) {
completedCourses.innerHTML = `
✅
Nenhum curso concluído ainda
`;
} else {
completedCourses.innerHTML = completed
.map((course) => createCourseListItem(course, true))
.join("");
}
}
function createCourseListItem(course, isCompleted = false) {
return `
📖
${escapeHtml(course.course_title || course.title || "Curso")}
${formatDuration(course.duration_minutes || 30)}
${course.completion_percentage || (isCompleted ? 100 : 0)}% completo
`;
}
function renderMandatoryAssignments() {
const list = document.getElementById("mandatoryList");
const badge = document.getElementById("mandatoryCount");
if (!list) return;
badge.textContent = LearnState.mandatoryAssignments.length;
if (LearnState.mandatoryAssignments.length === 0) {
list.innerHTML = `
🎉
Tudo em dia!
Você não possui treinamentos obrigatórios pendentes.
`;
return;
}
list.innerHTML = LearnState.mandatoryAssignments
.map((assignment) => {
const isOverdue =
assignment.due_date && new Date(assignment.due_date) < new Date();
const daysUntilDue = assignment.due_date
? Math.ceil(
(new Date(assignment.due_date) - new Date()) /
(1000 * 60 * 60 * 24),
)
: null;
const isUrgent =
daysUntilDue !== null && daysUntilDue <= 7 && daysUntilDue > 0;
return `
${isOverdue ? "⚠️" : isUrgent ? "⏰" : "📋"}
${escapeHtml(assignment.course_title || "Treinamento Obrigatório")}
${
isOverdue
? "⚠️ Prazo vencido!"
: daysUntilDue !== null
? `Prazo: ${daysUntilDue} dias`
: "Sem prazo definido"
}
`;
})
.join("");
}
function updateMandatoryAlert() {
const alert = document.getElementById("mandatoryAlert");
const alertText = document.getElementById("mandatoryAlertText");
if (!alert) return;
const overdueCount = LearnState.mandatoryAssignments.filter(
(a) => a.due_date && new Date(a.due_date) < new Date(),
).length;
const urgentCount = LearnState.mandatoryAssignments.filter((a) => {
if (!a.due_date) return false;
const days = Math.ceil(
(new Date(a.due_date) - new Date()) / (1000 * 60 * 60 * 24),
);
return days > 0 && days <= 7;
}).length;
if (overdueCount > 0 || urgentCount > 0) {
alert.style.display = "flex";
if (overdueCount > 0) {
alertText.textContent = `Você possui ${overdueCount} treinamento(s) com prazo vencido!`;
} else {
alertText.textContent = `Você possui ${urgentCount} treinamento(s) com prazo próximo.`;
}
} else {
alert.style.display = "none";
}
}
function renderCertificates() {
const grid = document.getElementById("certificatesGrid");
const preview = document.getElementById("certificatesPreview");
if (!grid) return;
if (LearnState.certificates.length === 0) {
grid.innerHTML = `
🏆
Nenhum certificado ainda
Complete seus cursos para ganhar certificados.
`;
if (preview) {
preview.innerHTML = `
🏆
Nenhum certificado ainda
`;
}
return;
}
grid.innerHTML = LearnState.certificates
.map(
(cert) => `
${cert.score}%
${formatDate(cert.issued_at)}
`,
)
.join("");
// Update sidebar preview
if (preview) {
preview.innerHTML = LearnState.certificates
.slice(0, 3)
.map(
(cert) => `
🎓
${escapeHtml(cert.course_title || "Curso")}
${formatDate(cert.issued_at)}
`,
)
.join("");
}
}
function renderRecommendations(courses) {
const carousel = document.getElementById("recommendedCourses");
if (!carousel) return;
if (!courses || courses.length === 0) {
carousel.innerHTML =
'Explore o catálogo para encontrar cursos.
';
return;
}
carousel.innerHTML = courses
.slice(0, 6)
.map((course) => {
const card = createCourseCard(course);
card.style.minWidth = "280px";
return card.outerHTML;
})
.join("");
}
// ============================================================================
// MODALS
// ============================================================================
function openCourseModal(courseId) {
loadCourseDetail(courseId);
document.getElementById("courseModal").classList.remove("hidden");
}
function closeCourseModal() {
document.getElementById("courseModal").classList.add("hidden");
LearnState.currentCourse = null;
}
function renderCourseModal(data) {
const { course, lessons, quiz } = data;
document.getElementById("modalCourseTitle").textContent = course.title;
document.getElementById("modalDescription").textContent =
course.description || "Sem descrição disponível.";
document.getElementById("modalDifficulty").textContent = formatDifficulty(
course.difficulty,
);
document.getElementById("modalDifficulty").className =
`difficulty-badge ${(course.difficulty || "beginner").toLowerCase()}`;
document.getElementById("modalDuration").querySelector("span").textContent =
formatDuration(course.duration_minutes);
document
.getElementById("modalLessonsCount")
.querySelector("span").textContent = `${lessons?.length || 0} aulas`;
// Render lessons
const lessonsList = document.getElementById("modalLessonsList");
if (lessons && lessons.length > 0) {
lessonsList.innerHTML = lessons
.map(
(lesson, index) => `
${lesson.is_completed ? "✓" : index + 1}
${escapeHtml(lesson.title)}
${formatDuration(lesson.duration_minutes)}
`,
)
.join("");
} else {
lessonsList.innerHTML =
'Nenhuma aula disponível.
';
}
// Render quiz section
const quizSection = document.getElementById("modalQuizSection");
if (quiz) {
quizSection.style.display = "block";
const questions =
typeof quiz.questions === "string"
? JSON.parse(quiz.questions)
: quiz.questions || [];
document.getElementById("modalQuizQuestions").textContent =
`${questions.length} questões`;
document.getElementById("modalQuizTime").textContent =
quiz.time_limit_minutes ? `${quiz.time_limit_minutes} min` : "Sem limite";
document.getElementById("modalQuizPassing").textContent =
`${quiz.passing_score}% para aprovação`;
LearnState.currentQuiz = quiz;
} else {
quizSection.style.display = "none";
}
// Update button text based on progress
const startBtn = document.getElementById("startCourseBtn");
if (data.user_progress) {
const progress = data.user_progress;
if (progress.status === "completed") {
startBtn.innerHTML = `
Revisar Curso
`;
} else if (progress.status === "in_progress") {
startBtn.innerHTML = `
Continuar
`;
}
// Show progress
document.getElementById("modalProgress").style.display = "block";
document.getElementById("modalProgressFill").style.width =
`${progress.completion_percentage || 0}%`;
document.getElementById("modalProgressText").textContent =
`${progress.completion_percentage || 0}% completo`;
} else {
document.getElementById("modalProgress").style.display = "none";
startBtn.innerHTML = `
Iniciar Curso
`;
}
}
function startCourse() {
if (!LearnState.currentCourse) return;
const course = LearnState.currentCourse.course || LearnState.currentCourse;
const lessons = LearnState.currentCourse.lessons || [];
// Start course via API
startCourseAPI(course.id);
// Open first lesson
if (lessons.length > 0) {
openLesson(lessons[0].id, 0);
} else {
showNotification("info", "Este curso ainda não possui aulas.");
}
}
function openLesson(lessonId, index) {
const lessons = LearnState.currentCourse?.lessons || [];
const lesson = lessons.find((l) => l.id === lessonId) || lessons[index];
if (!lesson) {
showNotification("error", "Aula não encontrada.");
return;
}
LearnState.currentLesson = lesson;
LearnState.currentLessonIndex = index;
// Close course modal, open lesson modal
closeCourseModal();
document.getElementById("lessonModal").classList.remove("hidden");
// Update lesson UI
document.getElementById("lessonTitle").textContent = lesson.title;
document.getElementById("lessonNavTitle").textContent =
`Aula ${index + 1} de ${lessons.length}`;
// Render content based on type
const contentDiv = document.getElementById("lessonContent");
if (lesson.video_url) {
contentDiv.innerHTML = `
${lesson.content || ""}
`;
} else {
contentDiv.innerHTML = `
${lesson.content || "
Conteúdo da aula será exibido aqui.
"}
`;
}
// Update sidebar list
const sidebar = document.getElementById("lessonListSidebar");
sidebar.innerHTML = lessons
.map(
(l, i) => `
${l.is_completed ? "✓" : i + 1}
${escapeHtml(l.title)}
`,
)
.join("");
// Update navigation buttons
document.getElementById("prevLessonBtn").disabled = index === 0;
document.getElementById("nextLessonBtn").disabled =
index >= lessons.length - 1;
// Update progress
const progress = ((index + 1) / lessons.length) * 100;
document.getElementById("lessonProgressFill").style.width = `${progress}%`;
}
function closeLessonModal() {
document.getElementById("lessonModal").classList.add("hidden");
LearnState.currentLesson = null;
// Reopen course modal
if (LearnState.currentCourse) {
openCourseModal(LearnState.currentCourse.id);
}
}
function toggleLearnSidebar() {
const sidebar = document.querySelector(".learn-sidebar");
if (sidebar) {
sidebar.classList.toggle("collapsed");
}
}
function switchTab(tabId) {
document.querySelectorAll(".tab-btn, .learn-tab-btn").forEach((btn) => {
btn.classList.remove("active");
if (
btn.dataset.tab === tabId ||
btn.getAttribute("onclick")?.includes(tabId)
) {
btn.classList.add("active");
}
});
document
.querySelectorAll(".tab-content, .learn-tab-content")
.forEach((content) => {
content.classList.add("hidden");
if (content.id === tabId || content.id === `${tabId}-tab`) {
content.classList.remove("hidden");
}
});
}
function showAllCertificates() {
switchTab("certificates");
}
function loadMoreCourses() {
const currentCount = document.querySelectorAll(".course-card").length;
fetch(`/api/learn/courses?offset=${currentCount}&limit=12`)
.then((r) => r.json())
.then((data) => {
const grid = document.querySelector(".courses-grid");
if (grid && data.courses) {
data.courses.forEach((course) => {
grid.insertAdjacentHTML("beforeend", createCourseCard(course));
});
}
if (!data.hasMore) {
const btn = document.querySelector('[onclick="loadMoreCourses()"]');
if (btn) btn.style.display = "none";
}
})
.catch((err) => console.error("Error loading more courses:", err));
}
function startQuiz() {
if (!LearnState.currentCourse) return;
LearnState.quizState = {
questions: LearnState.currentCourse.quiz || [],
currentIndex: 0,
answers: {},
startTime: Date.now(),
};
document.getElementById("courseModal").classList.add("hidden");
document.getElementById("quizModal").classList.remove("hidden");
renderQuizQuestion();
}
function renderQuizQuestion() {
const { questions, currentIndex } = LearnState.quizState;
if (!questions || questions.length === 0) return;
const question = questions[currentIndex];
const container = document.getElementById("quizQuestionContainer");
container.innerHTML = `
`;
document.getElementById("quizProgress").textContent =
`${currentIndex + 1}/${questions.length}`;
document.getElementById("quizProgressFill").style.width =
`${((currentIndex + 1) / questions.length) * 100}%`;
}
function selectAnswer(index) {
LearnState.quizState.answers[LearnState.quizState.currentIndex] = index;
}
function prevQuestion() {
if (LearnState.quizState.currentIndex > 0) {
LearnState.quizState.currentIndex--;
renderQuizQuestion();
}
}
function nextQuestion() {
if (
LearnState.quizState.currentIndex <
LearnState.quizState.questions.length - 1
) {
LearnState.quizState.currentIndex++;
renderQuizQuestion();
}
}
function submitQuiz() {
const { questions, answers, startTime } = LearnState.quizState;
let correct = 0;
questions.forEach((q, i) => {
if (answers[i] === q.correctIndex) correct++;
});
const score = Math.round((correct / questions.length) * 100);
const passed = score >= 70;
const duration = Math.round((Date.now() - startTime) / 1000);
LearnState.quizResult = {
score,
correct,
total: questions.length,
passed,
duration,
};
document.getElementById("quizModal").classList.add("hidden");
document.getElementById("quizResultModal").classList.remove("hidden");
document.getElementById("quizScore").textContent = `${score}%`;
document.getElementById("quizCorrect").textContent =
`${correct}/${questions.length}`;
document.getElementById("quizStatus").textContent = passed
? "Passed!"
: "Not Passed";
document.getElementById("quizStatus").className = passed
? "status-passed"
: "status-failed";
if (passed && LearnState.currentCourse) {
fetch(`/api/learn/courses/${LearnState.currentCourse.id}/complete`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ score, duration }),
}).catch((err) => console.error("Error completing course:", err));
}
}
function closeQuizResult() {
document.getElementById("quizResultModal").classList.add("hidden");
LearnState.quizState = null;
LearnState.quizResult = null;
renderMyCourses();
renderCertificates();
}
function reviewAnswers() {
document.getElementById("quizResultModal").classList.add("hidden");
document.getElementById("quizModal").classList.remove("hidden");
LearnState.quizState.currentIndex = 0;
renderQuizQuestion();
}
function confirmExitQuiz() {
if (confirm("Are you sure you want to exit? Your progress will be lost.")) {
document.getElementById("quizModal").classList.add("hidden");
LearnState.quizState = null;
if (LearnState.currentCourse) {
openCourseModal(LearnState.currentCourse.id);
}
}
}
function downloadCertificate() {
if (!LearnState.currentCourse) return;
window.open(
`/api/learn/certificates/${LearnState.currentCourse.id}/download`,
"_blank",
);
}
function downloadCertificateById(certId) {
window.open(`/api/learn/certificates/${certId}/download`, "_blank");
}
function closeCertificateModal() {
document.getElementById("certificateModal").classList.add("hidden");
}
function prevLesson() {
if (!LearnState.currentCourse || !LearnState.currentLesson) return;
const lessons = LearnState.currentCourse.lessons || [];
const currentIndex = lessons.findIndex(
(l) => l.id === LearnState.currentLesson.id,
);
if (currentIndex > 0) {
openLesson(lessons[currentIndex - 1].id, currentIndex - 1);
}
}
function nextLesson() {
if (!LearnState.currentCourse || !LearnState.currentLesson) return;
const lessons = LearnState.currentCourse.lessons || [];
const currentIndex = lessons.findIndex(
(l) => l.id === LearnState.currentLesson.id,
);
if (currentIndex < lessons.length - 1) {
openLesson(lessons[currentIndex + 1].id, currentIndex + 1);
}
}
function completeLesson() {
if (!LearnState.currentLesson || !LearnState.currentCourse) return;
fetch(`/api/learn/lessons/${LearnState.currentLesson.id}/complete`, {
method: "POST",
})
.then(() => {
LearnState.currentLesson.completed = true;
closeLessonModal();
})
.catch((err) => console.error("Error completing lesson:", err));
}
// Export functions to window
window.toggleLearnSidebar = toggleLearnSidebar;
window.showAllCertificates = showAllCertificates;
window.loadMoreCourses = loadMoreCourses;
window.startQuiz = startQuiz;
window.prevQuestion = prevQuestion;
window.nextQuestion = nextQuestion;
window.submitQuiz = submitQuiz;
window.closeQuizResult = closeQuizResult;
window.reviewAnswers = reviewAnswers;
window.confirmExitQuiz = confirmExitQuiz;
window.downloadCertificate = downloadCertificate;
window.downloadCertificateById = downloadCertificateById;
window.closeCertificateModal = closeCertificateModal;
window.prevLesson = prevLesson;
window.nextLesson = nextLesson;
window.completeLesson = completeLesson;
window.selectAnswer = selectAnswer;
window.switchTab = switchTab;
window.openCourseModal = openCourseModal;
window.closeCourseModal = closeCourseModal;
window.startCourse = startCourse;
window.openLesson = openLesson;
window.closeLessonModal = closeLessonModal;