document.addEventListener('DOMContentLoaded', function() { const startExamBtn = document.getElementById('startExamBtn'); const pageLoader = document.getElementById('pageLoader'); const loaderMessage = document.getElementById('loaderMessage'); const examSelectionModal = new bootstrap.Modal(document.getElementById('examSelectionModal')); // Form elements const examCategorySelect = document.getElementById('examCategory'); const examNameSelect = document.getElementById('examName'); const examDescription = document.getElementById('examDescription'); const startSelectedExamBtn = document.getElementById('startSelectedExam'); const viewPastResultsBtn = document.getElementById('viewPastResultsBtn'); // Hide View Results button by default - only show when current exam is EVALUATING or EVALUATED if (viewPastResultsBtn) { viewPastResultsBtn.closest('li').style.display = 'none'; } let labs = []; // Will store all labs fetched from the API let selectedLab = null; // Will store the currently selected lab // Check for current exam status on page load checkCurrentExamStatus(); console.log('Loading labs on page load...'); // Load labs data when the page loads fetchLabs(false); // Function to check current exam status function checkCurrentExamStatus() { fetch('/facilitator/api/v1/exams/current') .then(response => { if (!response.ok) { if (response.status !== 404) { console.error('Error checking current exam status:', response.status); } return null; } return response.json(); }) .then(data => { if (data && data.id) { // Store current exam data in localStorage for the View Results functionality localStorage.setItem('currentExamData', JSON.stringify(data)); // Show View Results button only if status is EVALUATING or EVALUATED if (data.status === 'EVALUATING' || data.status === 'EVALUATED') { if (viewPastResultsBtn) { viewPastResultsBtn.closest('li').style.display = 'block'; } } // If exam is in PREPARING state, show loading overlay and start polling if (data.status === 'PREPARING') { console.log('Exam is in PREPARING state, showing loading overlay'); showLoadingOverlay(); updateLoadingMessage('Preparing lab environment...'); updateExamInfo(data.info?.name || 'Unknown Exam'); // Start polling for status pollExamStatus(data.id).then(statusData => { if (statusData.status === 'READY') { // Redirect to exam page when ready window.location.href = `/exam.html?id=${data.id}`; } }); } } }) .catch(error => { console.error('Error checking current exam status:', error); }); } // Event listener for the "Start Exam" button startExamBtn.addEventListener('click', function(e) { e.preventDefault(); console.log('Checking for active exam sessions...'); // First check if there's any active exam fetch('/facilitator/api/v1/exams/current') .then(response => { if (response.status === 404) { console.log('No active exam found, proceeding with new exam'); // No active exam, proceed as normal if (labs.length > 0) { console.log('Using pre-loaded labs data'); examSelectionModal.show(); } else { console.log('No pre-loaded labs data available, fetching now...'); fetchLabs(true); } return null; } if (!response.ok) { console.error('Error checking current exam status:', response.status); return null; } return response.json(); }) .then(data => { if (data && data.id) { console.log('Active exam found:', data.id, 'Status:', data.status); // Active exam found, show warning modal showActiveExamWarningModal(data); } }) .catch(error => { console.error('Error checking for active exam:', error); // Proceed anyway in case of error if (labs.length > 0) { examSelectionModal.show(); } else { fetchLabs(true); } }); }); // Function to show warning modal for active exam function showActiveExamWarningModal(examData) { // Create modal HTML const modalHTML = ` `; // Add modal to DOM if it doesn't exist if (!document.getElementById('activeExamWarningModal')) { document.body.insertAdjacentHTML('beforeend', modalHTML); } // Get modal element and create Bootstrap modal const modalElement = document.getElementById('activeExamWarningModal'); const warningModal = new bootstrap.Modal(modalElement); // Show the modal warningModal.show(); // Remove any existing event listeners by cloning and replacing the buttons const oldTerminateBtn = document.getElementById('terminateAndProceedBtn'); const newTerminateBtn = oldTerminateBtn.cloneNode(true); oldTerminateBtn.parentNode.replaceChild(newTerminateBtn, oldTerminateBtn); const oldContinueBtn = document.getElementById('continueSessionBtn'); const newContinueBtn = oldContinueBtn.cloneNode(true); oldContinueBtn.parentNode.replaceChild(newContinueBtn, oldContinueBtn); // Add event listener for continue session button document.getElementById('continueSessionBtn').addEventListener('click', function() { // Redirect to the current exam window.location.href = `/exam.html?id=${examData.id}`; }); // Add event listener for terminate and proceed button document.getElementById('terminateAndProceedBtn').addEventListener('click', function() { // Update button to show progress const terminateBtn = document.getElementById('terminateAndProceedBtn'); terminateBtn.disabled = true; terminateBtn.innerHTML = '
TERMINATING...
'; // Show loading overlay showLoadingOverlay(); updateLoadingMessage('Terminating active session...'); console.log('Attempting to terminate exam:', examData.id); // Call API to terminate the active exam fetch(`/facilitator/api/v1/exams/${examData.id}/terminate`, { method: 'POST' }) .then(response => { if (!response.ok) { console.error('Termination failed with status:', response.status); throw new Error('Failed to terminate exam. Status: ' + response.status); } return response.json(); }) .then((data) => { console.log('Exam terminated successfully:', examData.id); // Hide the warning modal warningModal.hide(); // Clear any stored exam data localStorage.removeItem('currentExamData'); localStorage.removeItem('currentExamId'); // Proceed with starting a new exam hideLoadingOverlay(); if (labs.length > 0) { examSelectionModal.show(); } else { fetchLabs(true); } }) .catch(error => { console.error('Error terminating exam:', error); hideLoadingOverlay(); // Reset button state terminateBtn.disabled = false; terminateBtn.innerHTML = 'Terminate and Proceed'; alert('Failed to terminate the active exam. Please try again later.'); }); // Clean up the modal when it's hidden modalElement.addEventListener('hidden.bs.modal', function() { console.log('Modal hidden, cleaning up event listeners'); // Remove event listeners by replacing buttons with clones if they exist if (document.getElementById('terminateAndProceedBtn')) { const oldTerminateBtn = document.getElementById('terminateAndProceedBtn'); const newTerminateBtn = oldTerminateBtn.cloneNode(true); oldTerminateBtn.parentNode.replaceChild(newTerminateBtn, oldTerminateBtn); } if (document.getElementById('continueSessionBtn')) { const oldContinueBtn = document.getElementById('continueSessionBtn'); const newContinueBtn = oldContinueBtn.cloneNode(true); oldContinueBtn.parentNode.replaceChild(newContinueBtn, oldContinueBtn); } }); }); } // Fetch labs from the facilitator API function fetchLabs(showLoader = true) { console.log('Fetching labs, showLoader:', showLoader); if (showLoader) { pageLoader.style.display = 'flex'; loaderMessage.textContent = 'Loading labs...'; } fetch('/facilitator/api/v1/assements/') .then(response => { if (!response.ok) { throw new Error('Failed to fetch labs. Status: ' + response.status); } return response.json(); }) .then(data => { labs = data; console.log('Labs loaded successfully, count:', labs.length); if (showLoader) { pageLoader.style.display = 'none'; examSelectionModal.show(); } populateLabCategories(); }) .catch(error => { console.error('Error fetching labs:', error); if (showLoader) { pageLoader.style.display = 'none'; alert('Failed to load labs. Please try again later.'); } }); } // Populate the lab categories dropdown function populateLabCategories() { // Get unique categories const categories = [...new Set(labs.map(lab => lab.category))]; // If CKAD is available, select it by default if (categories.includes('CKAD')) { examCategorySelect.value = 'CKAD'; filterLabsByCategory('CKAD'); } else if (categories.length > 0) { examCategorySelect.value = categories[0]; filterLabsByCategory(categories[0]); } } // Premium exam configuration const premiumExams = { 'CKA': { url: 'https://sailor.sh/certified-kubernetes-administrator-cka-certification-ready-mock-exam-bundle/', name: 'CKA Premium Mock Exam Bundle' }, 'CKAD': { url: 'https://sailor.sh/certified-kubernetes-application-developer-ckad-certification-ready-mock-exam-bundle/', name: 'CKAD Premium Mock Exam Bundle' }, 'CKS': { url: 'https://sailor.sh/certified-kubernetes-security-specialist-cks-certification-ready-mock-exam-bundle/', name: 'CKS Premium Mock Exam Bundle' }, 'KCNA': { url: 'https://sailor.sh/kubernetes-and-cloud-native-associate-kcna-certification-ready-mock-exam-bundle/', name: 'KCNA Premium Mock Exam Bundle' }, 'KCSA': { url: 'https://sailor.sh/kubernetes-and-cloud-native-security-associate-kcsa-certification-ready-mock-exam-bundle/', name: 'KCSA Premium Mock Exam Bundle' } }; // Filter labs by category and populate the labs dropdown function filterLabsByCategory(category) { const filteredLabs = labs.filter(lab => lab.category === category); // Clear existing options examNameSelect.innerHTML = ''; let hasPremium = false; // Add premium exam option if available for this category if (premiumExams[category]) { const premiumOption = document.createElement('option'); premiumOption.value = `premium_${category}`; premiumOption.textContent = `🏆 ${premiumExams[category].name}`; premiumOption.setAttribute('data-premium', 'true'); premiumOption.setAttribute('data-url', premiumExams[category].url); examNameSelect.appendChild(premiumOption); hasPremium = true; } // Add filtered labs to the dropdown with FREE badge filteredLabs.forEach(lab => { const option = document.createElement('option'); option.value = lab.id; option.textContent = `🆓 ${lab.name}`; option.setAttribute('data-premium', 'false'); examNameSelect.appendChild(option); }); // Enable the lab name select examNameSelect.disabled = false; // Select premium exam by default if available, otherwise select first free lab if (hasPremium) { examNameSelect.value = `premium_${category}`; // Trigger the change event to show premium description const selectedOption = examNameSelect.options[examNameSelect.selectedIndex]; const premiumUrl = selectedOption.getAttribute('data-url'); showPremiumDescription(category, premiumUrl); } else if (filteredLabs.length > 0) { examNameSelect.value = filteredLabs[0].id; updateLabDescription(filteredLabs[0]); } else { examDescription.textContent = 'No labs available for this category.'; startSelectedExamBtn.disabled = true; } } // Update the lab description when a lab is selected function updateLabDescription(lab) { // Hide premium info for free labs const premiumInfo = document.getElementById('premiumInfo'); const examDescription = document.getElementById('examDescription'); if (premiumInfo) { premiumInfo.style.display = 'none'; } // Show the exam description box for free labs if (examDescription) { examDescription.style.display = 'block'; } // Create a nicely formatted description const difficultyText = lab.difficulty || 'Medium'; const examTimeText = lab.examDurationInMinutes || lab.estimatedTime || '30'; const descriptionHTML = `

${lab.description || 'No description available.'}

Difficulty: ${difficultyText}
Exam Time: ${examTimeText} minutes
`; // No need to add styles dynamically anymore since they're in the CSS file // Use innerHTML to render the HTML content examDescription.innerHTML = descriptionHTML; selectedLab = lab; startSelectedExamBtn.disabled = false; } // Event listener for the exam category select examCategorySelect.addEventListener('change', function() { filterLabsByCategory(this.value); }); // Helper function to show premium description function showPremiumDescription(category, premiumUrl) { const premiumInfo = document.getElementById('premiumInfo'); const examDescription = document.getElementById('examDescription'); // Hide the blue description box for premium exams examDescription.style.display = 'none'; premiumInfo.style.display = 'block'; selectedLab = { isPremium: true, url: premiumUrl, category: category }; startSelectedExamBtn.disabled = false; startSelectedExamBtn.textContent = 'GET INSTANT ACCESS →'; } // Event listener for the exam name select examNameSelect.addEventListener('change', function() { const premiumInfo = document.getElementById('premiumInfo'); const examDescription = document.getElementById('examDescription'); if (this.value) { const selectedOption = this.options[this.selectedIndex]; const isPremium = selectedOption.getAttribute('data-premium') === 'true'; if (isPremium) { // Premium exam selected const premiumUrl = selectedOption.getAttribute('data-url'); const category = examCategorySelect.value; showPremiumDescription(category, premiumUrl); } else { // Free exam selected const lab = labs.find(lab => lab.id === this.value); if (lab) { updateLabDescription(lab); premiumInfo.style.display = 'none'; examDescription.style.display = 'block'; startSelectedExamBtn.textContent = 'START EXAM'; } } } else { examDescription.textContent = 'No lab selected.'; examDescription.style.display = 'block'; premiumInfo.style.display = 'none'; selectedLab = null; startSelectedExamBtn.disabled = true; startSelectedExamBtn.textContent = 'START EXAM'; } }); // Event listener for the start selected exam button startSelectedExamBtn.addEventListener('click', function() { if (selectedLab) { // Check if it's a premium exam if (selectedLab.isPremium) { // Open premium exam URL in new tab window.open(selectedLab.url, '_blank'); examSelectionModal.hide(); return; } // Regular free exam flow examSelectionModal.hide(); showLoadingOverlay(); // Show the loading overlay instead of pageLoader updateLoadingMessage('Starting lab environment...'); updateExamInfo(`Lab: ${selectedLab.name} | Difficulty: ${selectedLab.difficulty || 'Medium'}`); let userAgent = ''; try { userAgent = navigator.userAgent; } catch (error) { console.error('Error getting user agent:', error); } selectedLab.userAgent = userAgent; // Make a POST request to the facilitator API - using exams endpoint for POST fetch('/facilitator/api/v1/exams/', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(selectedLab) }) .then(response => { if (!response.ok) { throw new Error('Failed to start lab. Status: ' + response.status); } return response.json(); }) .then(data => { // Store exam ID in localStorage localStorage.setItem('currentExamId', data.id); // Start polling for status const warmUpTime = data.warmUpTimeInSeconds || 30; updateLoadingMessage(`Preparing your lab environment (${warmUpTime}s estimated)`); // Poll for exam status until it's ready return pollExamStatus(data.id); }) .then(() => { // Redirect to the lab page after status is READY const examId = localStorage.getItem('currentExamId'); window.location.href = `/exam.html?id=${examId}`; }) .catch(error => { console.error('Error starting lab:', error); hideLoadingOverlay(); alert('Failed to start the lab. Please try again later.'); }); } }); // Add new functions for exam status handling function showLoadingOverlay() { document.getElementById('loadingOverlay').style.display = 'flex'; } function hideLoadingOverlay() { document.getElementById('loadingOverlay').style.display = 'none'; } function updateProgressBar(progress) { document.getElementById('progressBar').style.width = `${progress}%`; } function updateLoadingMessage(message) { document.getElementById('loadingMessage').textContent = message; } function updateExamInfo(info) { document.getElementById('examInfo').textContent = info; } async function pollExamStatus(examId) { const startTime = Date.now(); const pollInterval = 1000; // Poll every 1 second return new Promise((resolve, reject) => { const poll = async () => { try { const response = await fetch(`/facilitator/api/v1/exams/${examId}/status`); const data = await response.json(); // set warmup time in seconds const warmUpTimeInSeconds = data.warmUpTimeInSeconds || 30; if (data.status === 'READY') { // Set progress to 100% when ready updateProgressBar(100); updateLoadingMessage('Lab environment is ready! Redirecting...'); // Wait a moment for the user to see the 100% progress setTimeout(() => resolve(data), 1000); return; } // Calculate progress based on warm-up time const elapsedTime = (Date.now() - startTime) / 1000; const progress = Math.min((elapsedTime / warmUpTimeInSeconds) * 100, 95); updateProgressBar(progress); updateLoadingMessage(data.message || 'Preparing lab environment...'); // Continue polling setTimeout(poll, pollInterval); } catch (error) { console.error('Error polling exam status:', error); // Show error in the loading overlay updateLoadingMessage(`Error: ${error.message}. Retrying...`); // Continue polling despite errors setTimeout(poll, pollInterval); } }; poll(); }); } // Modify the existing startExam function async function startExam(examId) { try { showLoadingOverlay(); updateLoadingMessage('Starting exam environment...'); const response = await fetch('/facilitator/api/v1/exams', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ examId }) }); const data = await response.json(); if (!response.ok) { throw new Error(data.message || 'Failed to start exam'); } // Store exam ID in localStorage localStorage.setItem('currentExamId', data.id); // Start polling for status await pollExamStatus(data.id, data.warmUpTimeInSeconds || 30); // Redirect to exam page when ready window.location.href = `/exam.html?id=${data.id}`; } catch (error) { console.error('Error starting exam:', error); hideLoadingOverlay(); alert('Failed to start exam: ' + error.message); } } // Modify the dropdown population to include difficulty info function populateDropdown(labs) { const dropdown = document.getElementById('examDropdown'); dropdown.innerHTML = ''; labs.forEach(lab => { const option = document.createElement('option'); option.value = lab.id; option.textContent = `${lab.name} (${lab.difficulty || 'Medium'})`; option.title = `${lab.description}\nDifficulty: ${lab.difficulty || 'Medium'}\nEstimated Time: ${lab.estimatedTime || '30'} minutes`; dropdown.appendChild(option); }); } // Add event listener for View Past Results button viewPastResultsBtn.addEventListener('click', function() { // Check if we have current exam data const currentExamDataStr = localStorage.getItem('currentExamData'); if (currentExamDataStr) { try { const currentExamData = JSON.parse(currentExamDataStr); // If the current exam is evaluated or being evaluated, go directly to results if (currentExamData.status === 'EVALUATED' || currentExamData.status === 'EVALUATING') { window.location.href = `/results?id=${currentExamData.id}`; return; } else { // If the exam exists but isn't in the right state, show an alert alert('Exam results are not available yet. The exam must be evaluated first.'); return; } } catch (error) { console.error('Error parsing current exam data:', error); } } // If there's no current exam at all, inform the user alert('No active exam found. Please start an exam first.'); }); });