Created
April 25, 2026 06:22
-
-
Save HilmiZul/2c2e5f2ba97aa57dd6f3bacbb31229a7 to your computer and use it in GitHub Desktop.
soal tes eng
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>NECTAR CEFR A1 Vocabulary & Grammar Test</title> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.5.31/jspdf.plugin.autotable.min.js"></script> | |
| <style> | |
| :root { | |
| /* Navy Blue Theme */ | |
| --primary: #000080; | |
| --success: #27ae60; | |
| --danger: #e74c3c; | |
| } | |
| * { margin: 0; padding: 0; box-sizing: border-box; } | |
| body { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); | |
| color: #333; | |
| min-height: 100vh; | |
| } | |
| .container { | |
| max-width: 820px; | |
| margin: 20px auto; | |
| background: white; | |
| border-radius: 15px; | |
| box-shadow: 0 10px 30px rgba(0,0,0,0.1); | |
| overflow: hidden; | |
| } | |
| /* Updated Header for Logos */ | |
| header { | |
| background: var(--primary); | |
| color: white; | |
| padding: 15px 25px; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .header-logo { | |
| height: 65px; | |
| width: 65px; | |
| object-fit: contain; | |
| } | |
| .header-title { | |
| text-align: center; | |
| flex-grow: 1; | |
| padding: 0 15px; | |
| } | |
| h1 { font-size: 1.8rem; margin-bottom: 5px; } | |
| .subtitle { font-size: 1rem; opacity: 0.9; } | |
| .tab { | |
| display: flex; | |
| background: #f1f1f1; | |
| } | |
| .tab button { | |
| flex: 1; | |
| padding: 15px; | |
| background: none; | |
| border: none; | |
| font-size: 1.1rem; | |
| cursor: pointer; | |
| } | |
| .tab button.active { | |
| background: white; | |
| border-bottom: 4px solid var(--primary); | |
| font-weight: bold; | |
| } | |
| .warning { | |
| background: #fff3cd; | |
| color: #856404; | |
| padding: 12px; | |
| text-align: center; | |
| font-size: 0.95rem; | |
| } | |
| .form-group { | |
| margin-bottom: 20px; | |
| } | |
| label { | |
| display: block; | |
| margin-bottom: 8px; | |
| font-weight: 600; | |
| color: #444; | |
| } | |
| input[type="text"] { | |
| width: 100%; | |
| padding: 12px 16px; | |
| font-size: 1.05rem; | |
| border: 2px solid #ddd; | |
| border-radius: 8px; | |
| } | |
| .timer { | |
| font-size: 1.3rem; | |
| font-weight: bold; | |
| text-align: center; | |
| padding: 10px; | |
| background: #f8f9fa; | |
| } | |
| .progress-container { | |
| padding: 15px 25px; | |
| background: #f8f9fa; | |
| } | |
| .progress-bar { | |
| height: 10px; | |
| background: var(--primary); | |
| border-radius: 5px; | |
| transition: width 0.3s; | |
| } | |
| .progress-text { | |
| text-align: center; | |
| margin-top: 5px; | |
| font-size: 0.9rem; | |
| color: #666; | |
| } | |
| .question-container { | |
| padding: 30px 40px; | |
| min-height: 280px; | |
| } | |
| .question-number { font-size: 1.1rem; color: var(--primary); margin-bottom: 15px; } | |
| .question { font-size: 1.22rem; margin-bottom: 25px; line-height: 1.5; } | |
| .input-field { | |
| width: 100%; | |
| padding: 14px 18px; | |
| font-size: 1.1rem; | |
| border: 2px solid #ddd; | |
| border-radius: 8px; | |
| margin-top: 10px; | |
| } | |
| .input-field:focus { border-color: var(--primary); outline: none; } | |
| .navigation { | |
| padding: 20px 40px; | |
| display: flex; | |
| justify-content: space-between; | |
| background: #f8f9fa; | |
| border-top: 1px solid #eee; | |
| } | |
| button { | |
| padding: 12px 28px; | |
| font-size: 1.05rem; | |
| border: none; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| } | |
| .btn-prev { background: #95a5a6; color: white; } | |
| .btn-next { background: var(--primary); color: white; } | |
| .btn-submit { background: var(--success); color: white; font-weight: bold; } | |
| .btn-pdf { background: #f39c12; color: white; padding: 12px 25px; font-weight: bold; box-shadow: 0 4px 6px rgba(0,0,0,0.1); } | |
| button:hover { transform: translateY(-2px); } | |
| .results { padding: 40px; display: none; } | |
| .score { | |
| text-align: center; | |
| font-size: 3rem; | |
| font-weight: bold; | |
| margin: 20px 0; | |
| color: var(--primary); | |
| } | |
| .feedback { text-align: center; font-size: 1.3rem; margin: 15px 0; color: #2c3e50; } | |
| .review-item { | |
| margin: 20px 0; | |
| padding: 15px; | |
| background: #f8f9fa; | |
| border-radius: 10px; | |
| border-left: 5px solid var(--primary); | |
| } | |
| .correct { color: var(--success); } | |
| .wrong { color: var(--danger); } | |
| footer { | |
| text-align: center; | |
| padding: 15px; | |
| color: #777; | |
| font-size: 0.9rem; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <header> | |
| <img src="NECTAR LOGO.png" id="nectarLogo" class="header-logo" alt="Nectar Logo"> | |
| <div class="header-title"> | |
| <h1>NECTAR CEFR A1 Test</h1> | |
| <p class="subtitle">Simple Present Tense - Grammar (100 Questions)</p> | |
| </div> | |
| <img src="SMPN3 LOGO.png" id="smpn3Logo" class="header-logo" alt="SMPN 3 Logo"> | |
| </header> | |
| <div class="tab"> | |
| <button class="active" onclick="showTab(0)" id="tab0">Student Information</button> | |
| <button onclick="showTab(1)" id="tab1">Start Test</button> | |
| </div> | |
| <div class="warning"> | |
| <strong>⚠️ Important Rule:</strong> Dilarang menggunakan Google Translate atau aplikasi penerjemah lainnya.<br> | |
| No translation tools allowed. This test checks YOUR own English ability. | |
| </div> | |
| <div id="studentTab"> | |
| <div class="question-container"> | |
| <h2 style="margin-bottom: 25px; color: var(--primary);">Student Identity</h2> | |
| <div class="form-group"> | |
| <label>Full Name</label> | |
| <input type="text" id="studentName" placeholder="Enter your full name"> | |
| </div> | |
| <div class="form-group"> | |
| <label>Class</label> | |
| <input type="text" id="studentClass" placeholder="Example: 7A or 8B"> | |
| </div> | |
| <div class="form-group"> | |
| <label>Nectar Student's Class (UK/US)</label> | |
| <input type="text" id="nectarClass" placeholder="Enter your nectar's class"> | |
| </div> | |
| <div style="text-align: center; margin-top: 30px;"> | |
| <button onclick="startTest()" style="background: var(--primary); color: white; padding: 14px 40px; font-size: 1.1rem;"> | |
| Start the Test → | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="testTab" style="display: none;"> | |
| <div class="timer" id="timer">Time Left: 60:00</div> | |
| <div class="progress-container"> | |
| <div class="progress-bar" id="progressBar" style="width: 0%"></div> | |
| <div class="progress-text">Question <span id="currentQ">1</span> of <span id="totalQ">100</span></div> | |
| </div> | |
| <div class="question-container" id="questionArea"> | |
| <div class="question-number">Question <span id="qNumber">1</span></div> | |
| <div class="question" id="questionText"></div> | |
| <input type="text" class="input-field" id="answerInput" placeholder="Type your answer here..." autocomplete="off" autocapitalize="off"> | |
| </div> | |
| <div class="navigation"> | |
| <button class="btn-prev" id="prevBtn" onclick="prevQuestion()">← Previous</button> | |
| <button class="btn-next" id="nextBtn" onclick="nextQuestion()">Next →</button> | |
| <button class="btn-submit" id="submitBtn" onclick="submitTest()" style="display: none;">Submit Test</button> | |
| </div> | |
| </div> | |
| <div class="results" id="resultsScreen"> | |
| <h2 style="text-align: center;">Your Test Result</h2> | |
| <p style="text-align: center; font-size: 1.2rem; margin: 10px 0;" id="studentInfoResult"></p> | |
| <div class="score" id="scoreDisplay">0/100</div> | |
| <div class="feedback" id="feedbackText"></div> | |
| <div style="text-align: center; margin: 25px 0;"> | |
| <button onclick="saveAsPDF()" class="btn-pdf">📄 Download Official PDF Report</button> | |
| </div> | |
| <h3 style="margin: 30px 0 15px;">Detailed Review</h3> | |
| <div id="reviewContainer"></div> | |
| <div style="text-align: center; margin-top: 40px;"> | |
| <button onclick="restartTest()" style="background: var(--primary); color: white; padding: 12px 35px; border: none; border-radius: 8px; font-size: 1.1rem;"> | |
| Take Test Again | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <footer> | |
| NECTAR CEFR Test • Nectar-SMP Negeri 3 Tasikmalaya All right reserved 2026 | |
| </footer> | |
| <script> | |
| const questions = [ | |
| { id: 1, question: "My family __________ rice and vegetables for lunch every day.", answer: ["eats"] }, | |
| { id: 2, question: "She __________ fried chicken with sambal almost every weekend.", answer: ["eats"] }, | |
| { id: 3, question: "Rian __________ nasi goreng for breakfast on school days.", answer: ["eats"] }, | |
| { id: 4, question: "We __________ fruit salad when it is very hot outside.", answer: ["eat"] }, | |
| { id: 5, question: "My little brother __________ too many sweets and then he feels sick.", answer: ["eats"] }, | |
| { id: 6, question: "Mrs. Lina __________ delicious rendang for special occasions.", answer: ["cooks"] }, | |
| { id: 7, question: "They __________ bakso at the street stall near the school.", answer: ["eat"] }, | |
| { id: 8, question: "He __________ eggs and toast every morning before school.", answer: ["eats"] }, | |
| { id: 9, question: "I __________ spicy food because it makes me sweat.", answer: ["don't like", "dislike"] }, | |
| { id: 10, question: "My sister __________ chocolate cake on her birthday.", answer: ["bakes", "makes"] }, | |
| { id: 11, question: "Indonesian people often __________ teh manis in the afternoon.", answer: ["drink"] }, | |
| { id: 12, question: "She __________ a glass of warm water every morning.", answer: ["drinks"] }, | |
| { id: 13, question: "My brother __________ milk before he goes to bed.", answer: ["drinks"] }, | |
| { id: 14, question: "They __________ es jeruk when the weather is hot.", answer: ["drink"] }, | |
| { id: 15, question: "He usually __________ kopi susu at the coffee shop.", answer: ["drinks"] }, | |
| { id: 16, question: "We __________ fresh coconut water after playing football.", answer: ["drink"] }, | |
| { id: 17, question: "My mother __________ ginger tea when she has a cold.", answer: ["drinks"] }, | |
| { id: 18, question: "I never __________ soft drinks because they have too much sugar.", answer: ["drink"] }, | |
| { id: 19, question: "My father __________ black coffee without sugar every day.", answer: ["drinks"] }, | |
| { id: 20, question: "My sister __________ strawberry smoothie on the weekend.", answer: ["drinks"] }, | |
| { id: 21, question: "I __________ up at 5:30 every morning.", answer: ["wake"] }, | |
| { id: 22, question: "She __________ her teeth twice a day.", answer: ["brushes"] }, | |
| { id: 23, question: "My father __________ to work by motorcycle.", answer: ["goes"] }, | |
| { id: 24, question: "We __________ English lessons every Tuesday and Thursday.", answer: ["have"] }, | |
| { id: 25, question: "He __________ his homework after Maghrib prayer.", answer: ["does"] }, | |
| { id: 26, question: "My mother __________ the house every Saturday morning.", answer: ["cleans"] }, | |
| { id: 27, question: "They __________ football in the field near the mosque.", answer: ["play"] }, | |
| { id: 28, question: "She usually __________ to bed at 9 o'clock.", answer: ["goes"] }, | |
| { id: 29, question: "Rian __________ his bicycle to school every day.", answer: ["rides"] }, | |
| { id: 30, question: "We __________ dinner together as a family.", answer: ["have", "eat"] }, | |
| { id: 31, question: "He often __________ video games after finishing his tasks.", answer: ["plays"] }, | |
| { id: 32, question: "My sister __________ her uniform before she goes to school.", answer: ["wears"] }, | |
| { id: 33, question: "The students __________ the national anthem every Monday.", answer: ["sing"] }, | |
| { id: 34, question: "I sometimes __________ a book before sleeping.", answer: ["read"] }, | |
| { id: 35, question: "In the restaurant, she usually __________ nasi uduk and es teh.", answer: ["orders"] }, | |
| { id: 36, question: "He __________ a plate of sate ayam and rice.", answer: ["orders"] }, | |
| { id: 37, question: "We __________ two glasses of es campur when we go to the warung.", answer: ["order"] }, | |
| { id: 38, question: "My friend always __________ extra spicy sambal with his food.", answer: ["asks for"] }, | |
| { id: 39, question: "The customer __________ mie goreng without vegetables.", answer: ["orders"] }, | |
| { id: 40, question: "They often __________ take-away food when they are busy.", answer: ["order"] }, | |
| { id: 41, question: "We __________ English and Math every morning.", answer: ["study", "have"] }, | |
| { id: 42, question: "She __________ hard before every exam.", answer: ["studies"] }, | |
| { id: 43, question: "The teacher __________ new vocabulary on the board.", answer: ["writes"] }, | |
| { id: 44, question: "My classmates often __________ questions in class.", answer: ["ask"] }, | |
| { id: 45, question: "He __________ to school by angkot.", answer: ["goes"] }, | |
| { id: 46, question: "My family __________ in a small house in Tasikmalaya.", answer: ["lives"] }, | |
| { id: 47, question: "She __________ her grandmother very much.", answer: ["loves"] }, | |
| { id: 48, question: "We often __________ our cousins during holidays.", answer: ["visit"] }, | |
| { id: 49, question: "He always __________ his mother with the housework.", answer: ["helps"] }, | |
| { id: 50, question: "My best friend __________ funny stories every day.", answer: ["tells"] }, | |
| { id: 51, question: "I often __________ football with my friends after school.", answer: ["play"] }, | |
| { id: 52, question: "She __________ books about animals in her free time.", answer: ["reads"] }, | |
| { id: 53, question: "He usually __________ movies on weekends.", answer: ["watches"] }, | |
| { id: 54, question: "We __________ to music while doing homework.", answer: ["listen"] }, | |
| { id: 55, question: "My sister __________ beautiful pictures.", answer: ["draws"] }, | |
| { id: 56, question: "We __________ in a comfortable house with a small garden.", answer: ["live"] }, | |
| { id: 57, question: "My mother __________ the kitchen every morning.", answer: ["cleans"] }, | |
| { id: 58, question: "He usually __________ the dishes after dinner.", answer: ["washes"] }, | |
| { id: 59, question: "She __________ dinner for the family.", answer: ["cooks"] }, | |
| { id: 60, question: "I often __________ my room on Saturday.", answer: ["clean"] }, | |
| { id: 61, question: "My mother often __________ vegetables at the traditional market.", answer: ["buys"] }, | |
| { id: 62, question: "She usually __________ a hijab to school every day.", answer: ["wears"] }, | |
| { id: 63, question: "He __________ new shoes when his old ones are broken.", answer: ["buys"] }, | |
| { id: 64, question: "We __________ to the mall on Sunday afternoons.", answer: ["go"] }, | |
| { id: 65, question: "Students __________ white and blue uniforms on Monday.", answer: ["wear"] }, | |
| { id: 66, question: "It often __________ in the afternoon during the rainy season.", answer: ["rains"] }, | |
| { id: 67, question: "The sun usually __________ brightly in the dry season.", answer: ["shines"] }, | |
| { id: 68, question: "We __________ hot weather because we can swim.", answer: ["like"] }, | |
| { id: 69, question: "She __________ cool weather in the morning.", answer: ["likes"] }, | |
| { id: 70, question: "I usually __________ healthy when I sleep enough.", answer: ["feel"] }, | |
| { id: 71, question: "She __________ a headache when she studies too long.", answer: ["has"] }, | |
| { id: 72, question: "We __________ sports to keep our body strong.", answer: ["do", "play"] }, | |
| { id: 73, question: "He __________ fresh fruits and vegetables every day.", answer: ["eats"] }, | |
| { id: 74, question: "My mother __________ her hands before cooking.", answer: ["washes"] }, | |
| { id: 75, question: "Many people __________ near the city center.", answer: ["live"] }, | |
| { id: 76, question: "We often __________ to the mosque for Friday prayer.", answer: ["go"] }, | |
| { id: 77, question: "She __________ her aunt who lives in the village.", answer: ["visits"] }, | |
| { id: 78, question: "There __________ a big park in the middle of town.", answer: ["is"] }, | |
| { id: 79, question: "Many students __________ smartphones for learning.", answer: ["use"] }, | |
| { id: 80, question: "He often __________ games on his tablet.", answer: ["plays"] }, | |
| { id: 81, question: "She __________ videos to learn English pronunciation.", answer: ["watches"] }, | |
| { id: 82, question: "We __________ messages to our friends every day.", answer: ["send"] }, | |
| { id: 83, question: "My father __________ his laptop for work.", answer: ["uses"] }, | |
| { id: 84, question: "We __________ trees to make the air cleaner.", answer: ["plant"] }, | |
| { id: 85, question: "She always __________ the environment by not littering.", answer: ["protects"] }, | |
| { id: 86, question: "He __________ plastic bottles and paper.", answer: ["recycles"] }, | |
| { id: 87, question: "Our school __________ a garden with many flowers.", answer: ["has"] }, | |
| { id: 88, question: "We __________ nature because it gives us fresh air.", answer: ["love"] }, | |
| { id: 89, question: "My grandmother __________ prayers five times a day.", answer: ["performs", "does", "prays"] }, | |
| { id: 90, question: "The shop __________ school uniforms near my house.", answer: ["sells"] }, | |
| { id: 91, question: "The wind sometimes __________ strongly from the south.", answer: ["blows"] }, | |
| { id: 92, question: "The doctor __________ patients in the morning.", answer: ["sees"] }, | |
| { id: 93, question: "The library __________ many interesting books.", answer: ["has"] }, | |
| { id: 94, question: "People should __________ rivers and lakes clean.", answer: ["keep"] }, | |
| { id: 95, question: "My brother __________ me with my English homework.", answer: ["helps"] }, | |
| { id: 96, question: "The bell __________ at 07:00 every day.", answer: ["rings"] }, | |
| { id: 97, question: "She __________ her notes after the lesson.", answer: ["writes", "copies"] }, | |
| { id: 98, question: "We __________ group projects in Science class.", answer: ["do", "make"] }, | |
| { id: 99, question: "The cat usually __________ on the sofa.", answer: ["sleeps", "sits"] }, | |
| { id: 100, question: "My father __________ fishing with his friends on Sundays.", answer: ["goes"] } | |
| ]; | |
| let currentQuestion = 0; | |
| let answers = new Array(questions.length).fill(""); | |
| let timerInterval; | |
| let timeLeft = 60 * 60; | |
| let studentData = { name: "", class: "", nectarClass: "" }; | |
| function showTab(tabIndex) { | |
| document.getElementById("studentTab").style.display = tabIndex === 0 ? "block" : "none"; | |
| document.getElementById("testTab").style.display = tabIndex === 1 ? "block" : "none"; | |
| document.getElementById("tab0").classList.toggle("active", tabIndex === 0); | |
| document.getElementById("tab1").classList.toggle("active", tabIndex === 1); | |
| } | |
| function startTest() { | |
| const name = document.getElementById("studentName").value.trim(); | |
| const className = document.getElementById("studentClass").value.trim(); | |
| const nClass = document.getElementById("nectarClass").value.trim(); | |
| if (!name || !className || !nClass) { | |
| alert("Please fill in all student information fields before starting."); | |
| return; | |
| } | |
| studentData = { name: name, class: className, nectarClass: nClass }; | |
| showTab(1); | |
| startTimer(); | |
| showQuestion(); | |
| } | |
| function startTimer() { | |
| timerInterval = setInterval(() => { | |
| timeLeft--; | |
| const min = Math.floor(timeLeft / 60); | |
| const sec = timeLeft % 60; | |
| document.getElementById("timer").textContent = `Time Left: ${min}:${sec < 10 ? '0' : ''}${sec}`; | |
| if (timeLeft <= 0) submitTest(); | |
| }, 1000); | |
| } | |
| function showQuestion() { | |
| const q = questions[currentQuestion]; | |
| document.getElementById("qNumber").textContent = q.id; | |
| document.getElementById("questionText").innerHTML = q.question.replace("__________", "<strong>__________</strong>"); | |
| document.getElementById("answerInput").value = answers[currentQuestion] || ""; | |
| document.getElementById("prevBtn").style.display = currentQuestion === 0 ? "none" : "block"; | |
| document.getElementById("nextBtn").style.display = currentQuestion === questions.length - 1 ? "none" : "block"; | |
| document.getElementById("submitBtn").style.display = currentQuestion === questions.length - 1 ? "block" : "none"; | |
| const progress = ((currentQuestion + 1) / questions.length) * 100; | |
| document.getElementById("progressBar").style.width = progress + "%"; | |
| document.getElementById("currentQ").textContent = currentQuestion + 1; | |
| document.getElementById("totalQ").textContent = questions.length; | |
| document.getElementById("answerInput").focus(); | |
| } | |
| function saveAnswer() { | |
| answers[currentQuestion] = document.getElementById("answerInput").value.trim(); | |
| } | |
| function nextQuestion() { | |
| saveAnswer(); | |
| if (currentQuestion < questions.length - 1) { | |
| currentQuestion++; | |
| showQuestion(); | |
| } | |
| } | |
| function prevQuestion() { | |
| saveAnswer(); | |
| if (currentQuestion > 0) { | |
| currentQuestion--; | |
| showQuestion(); | |
| } | |
| } | |
| document.getElementById("answerInput").addEventListener("keypress", function(e) { | |
| if (e.key === "Enter") { | |
| if (currentQuestion === questions.length - 1) { | |
| submitTest(); | |
| } else { | |
| nextQuestion(); | |
| } | |
| } | |
| }); | |
| function normalize(str) { | |
| return str.toLowerCase().trim(); | |
| } | |
| function checkAnswer(userAnswer, correctAnswers) { | |
| if (!userAnswer) return false; | |
| return correctAnswers.some(correct => normalize(correct) === normalize(userAnswer)); | |
| } | |
| function submitTest() { | |
| saveAnswer(); | |
| clearInterval(timerInterval); | |
| let score = 0; | |
| const reviewHTML = []; | |
| questions.forEach((q, index) => { | |
| const userAns = answers[index] || ""; | |
| const isCorrect = checkAnswer(userAns, q.answer); | |
| if (isCorrect) score++; | |
| reviewHTML.push(` | |
| <div class="review-item"> | |
| <strong>Q${q.id}:</strong> ${q.question}<br><br> | |
| <span style="color: ${isCorrect ? 'var(--success)' : 'var(--danger)'}"> | |
| Your answer: <strong>${userAns || "(empty)"}</strong> | |
| </span><br> | |
| ${!isCorrect ? `<span class="correct">Correct answer: <strong>${q.answer[0]}</strong></span>` : ''} | |
| </div> | |
| `); | |
| }); | |
| const percentage = Math.round((score / questions.length) * 100); | |
| document.getElementById("testTab").style.display = "none"; | |
| document.getElementById("resultsScreen").style.display = "block"; | |
| document.getElementById("studentInfoResult").innerHTML = | |
| `<strong>Name:</strong> ${studentData.name} | <strong>Class:</strong> ${studentData.class} | <strong>Nectar:</strong> ${studentData.nectarClass}`; | |
| document.getElementById("scoreDisplay").textContent = `${score}/${questions.length} (${percentage}%)`; | |
| // let feedback = percentage >= 85 ? "Excellent! You are A1 level 🎉" : | |
| // percentage >= 70 ? "Good job! You're doing well for A1" : | |
| // percentage >= 50 ? "Keep practicing Pocket Book and Present Tense." : | |
| // "You need more practice on Simple Present Tense."; | |
| let feedback = percentage >= 70 ? "A1 Level 🎉": "Try again next year." | |
| document.getElementById("feedbackText").textContent = feedback; | |
| document.getElementById("reviewContainer").innerHTML = reviewHTML.join(""); | |
| } | |
| // --- PDF GENERATION WITH LOGOS --- | |
| function saveAsPDF() { | |
| const { jsPDF } = window.jspdf; | |
| const doc = new jsPDF(); | |
| const pageWidth = doc.internal.pageSize.width; | |
| // 1. Draw Navy Blue Header Banner | |
| doc.setFillColor(0, 0, 128); // Navy Blue | |
| doc.rect(0, 0, pageWidth, 35, 'F'); // Made banner taller for logos | |
| // 2. Add Logos to the PDF Header | |
| try { | |
| const nectarImg = document.getElementById('nectarLogo'); | |
| const smpn3Img = document.getElementById('smpn3Logo'); | |
| // addImage(imageElement, format, x, y, width, height) | |
| doc.addImage(nectarImg, 'PNG', 15, 4, 22, 27); // Nectar on Left | |
| doc.addImage(smpn3Img, 'JPEG', pageWidth - 37, 4, 23, 25); // SMPN 3 on Right | |
| } catch (err) { | |
| console.warn("Could not load logos into PDF. Proceeding without them."); | |
| } | |
| // 3. Add Document Title | |
| doc.setTextColor(255, 255, 255); | |
| doc.setFont("helvetica", "bold"); | |
| doc.setFontSize(18); | |
| doc.text("NECTAR CEFR A1 Test Result", pageWidth / 2, 18, { align: "center" }); | |
| doc.setFontSize(11); | |
| doc.setFont("helvetica", "normal"); | |
| doc.text("SMP Negeri 3 Tasikmalaya", pageWidth / 2, 26, { align: "center" }); | |
| // 4. Student Information Section | |
| doc.setTextColor(0, 0, 0); | |
| doc.setFontSize(11); | |
| const today = new Date().toLocaleDateString('id-ID', { year: 'numeric', month: 'long', day: 'numeric' }); | |
| doc.text(`Name: ${studentData.name}`, 14, 48); | |
| doc.text(`School Class: ${studentData.class}`, 14, 55); | |
| doc.text(`Nectar Class: ${studentData.nectarClass}`, 14, 62); | |
| doc.text(`Date: ${today}`, pageWidth - 14, 48, { align: "right" }); | |
| // 5. Highlighted Score Box | |
| doc.setFillColor(240, 245, 250); // Light blue-grey background | |
| doc.rect(14, 70, pageWidth - 28, 28, 'F'); | |
| const scoreText = document.getElementById("scoreDisplay").textContent; | |
| doc.setFontSize(18); | |
| doc.setFont("helvetica", "bold"); | |
| doc.setTextColor(0, 0, 128); // Navy | |
| doc.text(`Final Score: ${scoreText}`, pageWidth / 2, 82, { align: "center" }); | |
| const feedback = document.getElementById("feedbackText").textContent.replace(/[^\x00-\x7F]/g, "").trim(); | |
| doc.setFontSize(11); | |
| doc.setFont("helvetica", "italic"); | |
| doc.setTextColor(80, 80, 80); | |
| doc.text(feedback, pageWidth / 2, 90, { align: "center" }); | |
| // 6. Build AutoTable Data | |
| const tableBody = []; | |
| questions.forEach((q, i) => { | |
| const userAns = answers[i] || "-"; | |
| const isCorrect = checkAnswer(userAns, q.answer); | |
| tableBody.push([ | |
| q.id, | |
| q.question, | |
| userAns, | |
| q.answer[0], | |
| isCorrect ? "Correct" : "Wrong" | |
| ]); | |
| }); | |
| // 7. Generate Professional Table | |
| doc.autoTable({ | |
| startY: 105, | |
| head: [['No.', 'Question', 'Your Answer', 'Correct Answer', 'Result']], | |
| body: tableBody, | |
| theme: 'striped', | |
| headStyles: { fillColor: [0, 0, 128], textColor: 255 }, // Navy header | |
| styles: { fontSize: 9, cellPadding: 3 }, | |
| columnStyles: { | |
| 0: { cellWidth: 12, halign: 'center' }, // Number | |
| 1: { cellWidth: 80 }, // Question | |
| 2: { cellWidth: 35 }, // User Answer | |
| 3: { cellWidth: 35 }, // Correct Answer | |
| 4: { cellWidth: 20, halign: 'center', fontStyle: 'bold' } // Result | |
| }, | |
| didParseCell: function(data) { | |
| if (data.section === 'body' && data.column.index === 4) { | |
| if (data.cell.raw === 'Correct') { | |
| data.cell.styles.textColor = [39, 174, 96]; // Green | |
| } else { | |
| data.cell.styles.textColor = [231, 76, 60]; // Red | |
| } | |
| } | |
| } | |
| }); | |
| // 8. Save Document | |
| doc.save(`NECTAR_A1_${studentData.name.replace(/ /g, "_")}.pdf`); | |
| } | |
| function restartTest() { | |
| location.reload(); | |
| } | |
| document.addEventListener('contextmenu', e => e.preventDefault()); | |
| document.addEventListener("visibilitychange", () => { | |
| if (document.hidden) alert("⚠️ Warning: Please stay on the test page."); | |
| }); | |
| window.onload = () => { | |
| document.getElementById("totalQ").textContent = questions.length; | |
| showTab(0); | |
| }; | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment