<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pengumuman Kelulusan SDN Percobaan Tahun 2025</title>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700&display=swap" rel="stylesheet">
<style>
:root {
/* Warna Primer: Merah yang kuat dan berani */
--primary-color: #D32F2F; /* Merah gelap */
/* Warna Sekunder: Kuning cerah */
--secondary-color: #FFD54F; /* Lighter Yellow for accents */
/* Background: Warna abu-abu kemerahan yang lembut */
--background-color: #FFEBEE; /* Light Reddish-Gray */
/* Card Background: Putih bersih */
--card-background: #FFFFFF;
/* Text Color: Hitam keabuan (lebih gelap dari sebelumnya, mendekati hitam murni) */
--text-color: #212121; /* Warna teks utama */
/* Light Text: Abu-abu lebih muda */
--light-text-color: #757575;
/* Border Color: Sedikit lebih gelap dari background card */
--border-color: #E0E0E0;
/* Hover Button: Merah sedikit gelap */
--button-hover-color: #C62828;
/* Shadow: Bayangan lebih lembut */
--box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
/* Warna Khusus untuk Status Lulus/Tidak Lulus */
--status-blue: #2196F3; /* Biru yang cerah dan modern */
}
body {
font-family: 'Poppins', sans-serif;
margin: 0;
padding: 0;
background-color: var(--background-color);
color: var(--text-color);
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
min-height: 100vh;
box-sizing: border-box;
line-height: 1.6;
}
.container {
background-color: var(--card-background);
border-radius: 16px;
box-shadow: var(--box-shadow);
padding: 40px;
max-width: 900px;
width: calc(100% - 40px);
text-align: center;
animation: fadeIn 0.8s ease-out;
margin: 30px auto;
flex-grow: 1;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(30px); }
to { opacity: 1; transform: translateY(0); }
}
.header {
margin-bottom: 40px;
}
.header img {
max-width: 140px;
margin-bottom: 20px;
border-radius: 50%;
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15);
transition: transform 0.3s ease-in-out;
}
.header img:hover {
transform: scale(1.05);
}
.header h1 {
color: var(--primary-color);
font-size: 3em;
margin-bottom: 15px;
font-weight: 700;
letter-spacing: -0.5px;
}
.header p {
font-size: 1.2em;
color: var(--light-text-color);
line-height: 1.4;
}
.login-section, .result-section {
padding: 30px;
border-top: 1px solid var(--border-color);
margin-top: 40px;
}
.login-section h2, .result-section h2 {
color: var(--text-color); /* Ubah ke hitam */
margin-bottom: 30px;
font-size: 2em;
font-weight: 600;
}
.form-group {
margin-bottom: 25px;
}
.form-group label {
display: block;
margin-bottom: 10px;
font-weight: 600;
color: var(--text-color);
font-size: 1.1em;
}
.form-group input[type="text"] {
width: calc(100% - 24px);
padding: 14px;
border: 1px solid var(--border-color);
border-radius: 10px;
font-size: 1.1em;
transition: all 0.3s ease;
max-width: 450px;
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.08);
}
.form-group input[type="text"]:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 4px rgba(211, 47, 47, 0.25);
outline: none;
}
button {
background-color: var(--primary-color);
color: white;
padding: 15px 30px;
border: none;
border-radius: 10px;
cursor: pointer;
font-size: 1.2em;
font-weight: 600;
transition: background-color 0.3s ease, transform 0.2s ease, box-shadow 0.3s ease;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}
button:hover {
background-color: var(--button-hover-color);
transform: translateY(-3px);
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15);
}
.result-section {
display: none;
text-align: left;
padding-top: 20px;
}
.result-item {
margin-bottom: 18px;
font-size: 1.15em;
display: grid;
grid-template-columns: 240px 1fr;
gap: 8px 15px;
align-items: baseline;
color: var(--text-color); /* Teks nilai (span) menjadi hitam */
}
.result-item strong {
color: var(--text-color); /* Ubah ke hitam untuk label (NISN:, Nama Siswa:, dll.) */
text-align: left;
font-weight: 600;
}
.status-lulus {
color: var(--status-blue); /* Ubah ke biru */
font-weight: 700;
font-size: 1.3em;
animation: pulseBlue 1.5s infinite alternate; /* Animasi baru untuk biru */
}
.status-tidak-lulus {
color: var(--status-blue); /* Ubah ke biru */
font-weight: 700;
font-size: 1.3em;
animation: pulseBlue 1.5s infinite alternate; /* Animasi baru untuk biru */
}
/* Animasi untuk warna biru */
@keyframes pulseBlue {
from { transform: scale(1); opacity: 1; }
to { transform: scale(1.03); opacity: 0.9; }
}
.download-button {
background-color: var(--secondary-color);
color: var(--text-color);
padding: 14px 28px;
border-radius: 10px;
font-size: 1.1em;
font-weight: 600;
margin-top: 30px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}
.download-button:hover {
background-color: #FFC107;
transform: translateY(-3px);
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15);
}
.error-message {
color: var(--primary-color);
margin-top: 20px;
font-weight: 600;
font-size: 1.1em;
}
.footer {
width: 100%;
margin-top: auto;
font-size: 0.95em;
color: var(--light-text-color);
border-top: 1px solid var(--border-color);
padding: 25px 0;
text-align: center;
background-color: var(--card-background);
box-shadow: 0 -4px 15px rgba(0, 0, 0, 0.05);
}
/* RESPONSIFITAS MOBILE */
@media (max-width: 768px) {
.container {
padding: 25px;
margin: 20px 15px;
width: calc(100% - 30px);
}
.header h1 {
font-size: 2em;
}
.header p {
font-size: 1em;
}
.header img {
max-width: 100px;
}
.login-section h2, .result-section h2 {
font-size: 1.6em;
}
.form-group input[type="text"], button {
font-size: 1em;
padding: 12px;
}
.download-button {
padding: 12px 20px;
font-size: 1em;
}
.result-item {
font-size: 1em;
grid-template-columns: 1fr;
gap: 0;
}
.result-item strong {
display: block;
margin-bottom: 5px;
text-align: left;
}
.status-lulus, .status-tidak-lulus {
font-size: 1.1em;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/9c/Logo_of_Ministry_of_Education_and_Culture_of_Republic_of_Indonesia.svg/375px-Logo_of_Ministry_of_Education_and_Culture_of_Republic_of_Indonesia.svg.png" alt="Logo SDN Percobaan">
<h1>Pengumuman Kelulusan Siswa</h1>
<p>SDN Percobaan Tahun Ajaran 2024/2025</p>
</div>
<div class="login-section" id="loginSection">
<h2>Login Siswa</h2>
<div class="form-group">
<label for="nisn">Masukkan NISN Anda:</label>
<input type="text" id="nisn" placeholder="Contoh: 0012345678">
</div>
<button onclick="checkGraduation()">Cek Kelulusan</button>
<p id="errorMessage" class="error-message"></p>
</div>
<div class="result-section" id="resultSection">
<h2>Hasil Kelulusan</h2>
<div class="result-item"><strong>NISN:</strong> <span id="displayNisn"></span></div>
<div class="result-item"><strong>Nama Siswa:</strong> <span id="displayNama"></span></div>
<div class="result-item"><strong>Tempat, Tanggal Lahir:</strong> <span id="displayTTL"></span></div>
<div class="result-item"><strong>Kelas:</strong> <span id="displayKelas"></span></div>
<div class="result-item">
<strong>Status Kelulusan:</strong>
<span id="displayStatus" class=""></span>
</div>
<a href="#" id="downloadSKL" class="download-button" target="_blank" style="display: none;">Download Surat Keterangan Lulus</a>
<button onclick="logout()" style="margin-top: 20px;">Logout</button>
</div>
</div>
<div class="footer">
<p>© 2025 SDN Percobaan. Hak Cipta Dilindungi Undang-Undang.</p>
</div>
<script>
const SPREADSHEET_URL = 'https://docs.google.com/spreadsheets/d/e/2PACX-1vRy16foUiBqirOqH9cEyQthYke_wHGSkdJZBzpGTRyhRu9cVb2go1q8MSwXibFnekrnyTb7ThXQ6VTF/pub?output=csv';
let studentData = [];
let columnMapping = {};
const EXPECTED_COLUMNS = {
'NISN': ['nisn', 'nomor induk siswa nasional', 'id siswa'],
'Nama Siswa': ['nama siswa', 'nama', 'nama lengkap'],
'Tempat, Tanggal Lahir': ['tempat tanggal lahir', 'ttl', 'tempat lahir', 'tanggal lahir'],
'Kelas': ['kelas'],
'Keterangan': ['keterangan', 'status', 'status kelulusan'],
'Download Surat Keterangan Lulus': ['download surat keterangan lulus', 'link skl', 'surat lulus']
};
async function loadStudentData() {
try {
const response = await fetch(SPREADSHEET_URL);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const csvText = await response.text();
if (!csvText || csvText.length < 50) {
throw new Error('CSV data is empty or invalid. Check the spreadsheet URL and publication settings.');
}
studentData = parseCSV(csvText);
console.log("Data loaded:", studentData);
console.log("Column mapping:", columnMapping);
if (studentData.length === 0) {
document.getElementById('errorMessage').innerText = 'Data siswa kosong atau tidak dapat diurai. Pastikan spreadsheet Anda memiliki data dan header yang benar.';
} else if (Object.keys(columnMapping).length < Object.keys(EXPECTED_COLUMNS).length) {
document.getElementById('errorMessage').innerText = 'Peringatan: Beberapa kolom penting mungkin tidak terdeteksi. Pastikan nama kolom di spreadsheet jelas dan sesuai.';
}
} catch (error) {
console.error("Error loading student data:", error);
document.getElementById('errorMessage').innerText = `Gagal memuat data siswa: ${error.message}. Mohon coba lagi nanti. Pastikan spreadsheet sudah dipublikasikan ke web sebagai CSV dan URL-nya benar.`;
}
}
function normalizeString(str) {
return str.replace(/[^a-zA-Z0-9]/g, '').toLowerCase();
}
function parseCSV(csv) {
const lines = csv.split('\n').map(line => line.trim()).filter(line => line.length > 0);
if (lines.length < 2) {
console.error("CSV data is too short to parse (header + at least one data row needed).");
return [];
}
const rawHeaders = parseCSVLine(lines[0]);
columnMapping = {};
const positionMapping = {
0: 'NISN',
1: 'Nama Siswa',
2: 'Kelas',
3: 'Tempat, Tanggal Lahir',
4: 'Keterangan',
5: 'Download Surat Keterangan Lulus'
};
rawHeaders.forEach((header, index) => {
const normalizedHeader = normalizeString(header);
if (positionMapping[index] && !columnMapping[positionMapping[index]]) {
columnMapping[positionMapping[index]] = header;
}
for (const expectedKey in EXPECTED_COLUMNS) {
if (!columnMapping[expectedKey]) {
const aliases = EXPECTED_COLUMNS[expectedKey].map(alias => normalizeString(alias));
if (aliases.includes(normalizedHeader)) {
columnMapping[expectedKey] = header;
break;
}
}
}
});
if (!columnMapping['NISN'] && rawHeaders[0]) columnMapping['NISN'] = rawHeaders[0];
if (!columnMapping['Nama Siswa'] && rawHeaders[1]) columnMapping['Nama Siswa'] = rawHeaders[1];
if (!columnMapping['Keterangan'] && rawHeaders[4]) columnMapping['Keterangan'] = rawHeaders[4];
if (!columnMapping['Tempat, Tanggal Lahir'] && rawHeaders[3]) columnMapping['Tempat, Tanggal Lahir'] = rawHeaders[3];
if (!columnMapping['Kelas'] && rawHeaders[2]) columnMapping['Kelas'] = rawHeaders[2];
if (!columnMapping['Download Surat Keterangan Lulus'] && rawHeaders[5]) columnMapping['Download Surat Keterangan Lulus'] = rawHeaders[5];
console.log("Detected column mapping:", columnMapping);
const data = [];
for (let i = 1; i < lines.length; i++) {
const currentline = parseCSVLine(lines[i]);
if (currentline.length >= rawHeaders.length) {
const obj = {};
rawHeaders.forEach((header, j) => {
obj[header.trim()] = (currentline[j] || '').trim();
});
data.push(obj);
} else {
console.warn(`Skipping malformed row ${i+1} due to insufficient columns: "${lines[i]}"`);
}
}
return data;
}
function parseCSVLine(line) {
const result = [];
let inQuote = false;
let currentField = '';
for (let i = 0; i < line.length; i++) {
const char = line[i];
if (char === '"') {
inQuote = !inQuote;
} else if (char === ',' && !inQuote) {
result.push(currentField);
currentField = '';
} else {
currentField += char;
}
}
result.push(currentField);
return result;
}
function checkGraduation() {
const nisnInput = document.getElementById('nisn').value.trim();
const errorMessage = document.getElementById('errorMessage');
errorMessage.innerText = '';
if (!nisnInput) {
errorMessage.innerText = 'NISN tidak boleh kosong.';
return;
}
if (studentData.length === 0) {
errorMessage.innerText = 'Data siswa belum berhasil dimuat. Mohon coba lagi atau hubungi administrator.';
return;
}
const nisnColumnName = columnMapping['NISN'];
const namaColumnName = columnMapping['Nama Siswa'];
const ttlColumnName = columnMapping['Tempat, Tanggal Lahir'];
const kelasColumnName = columnMapping['Kelas'];
const keteranganColumnName = columnMapping['Keterangan'];
const sklColumnName = columnMapping['Download Surat Keterangan Lulus'];
if (!nisnColumnName || !namaColumnName || !keteranganColumnName) {
errorMessage.innerText = 'Tidak dapat menemukan semua kolom data penting (NISN, Nama Siswa, Keterangan) di spreadsheet. Harap periksa nama kolom atau urutan.';
return;
}
const student = studentData.find(s => (s[nisnColumnName] && s[nisnColumnName].trim()) === nisnInput);
if (student) {
document.getElementById('displayNisn').innerText = student[nisnColumnName] || 'N/A';
document.getElementById('displayNama').innerText = student[namaColumnName] || 'N/A';
document.getElementById('displayTTL').innerText = student[ttlColumnName] || 'N/A';
document.getElementById('displayKelas').innerText = student[kelasColumnName] || 'N/A';
const statusElement = document.getElementById('displayStatus');
const keterangan = (student[keteranganColumnName] || '').trim().toLowerCase();
if (keterangan.includes('lulus') && !keterangan.includes('tidak lulus') && !keterangan.includes('belum lulus')) {
statusElement.innerText = 'LULUS';
statusElement.className = 'status-lulus';
const downloadSKLButton = document.getElementById('downloadSKL');
if (sklColumnName && student[sklColumnName]) {
downloadSKLButton.href = student[sklColumnName];
downloadSKLButton.style.display = 'inline-block';
} else {
downloadSKLButton.style.display = 'none';
}
} else {
statusElement.innerText = 'TIDAK LULUS';
statusElement.className = 'status-tidak-lulus';
document.getElementById('downloadSKL').style.display = 'none';
}
document.getElementById('loginSection').style.display = 'none';
document.getElementById('resultSection').style.display = 'block';
} else {
errorMessage.innerText = 'NISN tidak ditemukan. Pastikan NISN yang Anda masukkan benar dan ada di data.';
}
}
function logout() {
document.getElementById('nisn').value = '';
document.getElementById('errorMessage').innerText = '';
document.getElementById('loginSection').style.display = 'block';
document.getElementById('resultSection').style.display = 'none';
document.getElementById('downloadSKL').style.display = 'none';
}
document.addEventListener('DOMContentLoaded', loadStudentData);
</script>
</body>
</html>