Prompt dan Source Code Aplikasi Pengumuman Kelulusan Siswa
Menjelang akhir tahun ajaran, pengumuman kelulusan menjadi momen yang paling ditunggu oleh para siswa. Agar proses pengumuman berjalan lancar, aman, dan tidak memicu kerumunan, sistem kelulusan *online* menjadi solusi terbaik. Pada artikel ini, kita akan membagikan *Source Code* Aplikasi Pengumuman Kelulusan Siswa berbasis web yang responsif, elegan, dan siap cetak PDF ukuran A4 secara presisi.
Apa itu Google Apps Script?
Google Apps Script (AppScript) adalah platform *scripting* berbasis *cloud* gratis yang disediakan oleh Google. Bahasa pemrograman yang digunakan sangat mirip dengan JavaScript modern. Dengan AppScript, Anda bisa membuat aplikasi web ringan tanpa perlu menyewa *hosting* (Cpanel) atau *domain*.
Manfaat AppScript untuk Keperluan Sekolah:
- 100% Gratis & Server Tangguh: Di-host langsung di server Google, sehingga kebal *down* saat diakses ratusan siswa secara bersamaan.
- Database Google Sheets: Anda tidak perlu pusing mengurus SQL. Semua data siswa, NISN, dan status kelulusan cukup diketik atau *copy-paste* ke dalam file Google Sheets layaknya Ms. Excel.
- Pemeliharaan Mudah: Admin TU sekolah bisa mengedit data kelulusan kapan saja melalui HP atau Laptop tanpa harus paham bahasa pemrograman.
Prompt AI untuk Membuat Aplikasi Serupa
Bagi Anda yang ingin belajar memodifikasi atau membuat aplikasi serupa menggunakan kecerdasan buatan (seperti ChatGPT atau Gemini), Anda bisa menggunakan perintah (prompt) komprehensif berikut ini:
1. Gunakan Google Sheets sebagai database. Jika file sheets belum ada, buat script auto-generate spreadsheet beserta data dummy ketika aplikasi pertama kali di-deploy.
2. Format database: NISN (sebagai kata kunci pencarian, amankan awalan angka nol), Nama Lengkap, Kelas, Tempat & Tgl Lahir, dan Status Kelulusan (LULUS/TIDAK LULUS).
3. Tampilan web UI (index.html) harus menggunakan Bootstrap 5, responsif, elegan dengan warna gradasi, dan ada efek loading *spinner* saat mencari data.
4. Alur pengguna: Masukkan NISN -> Muncul Kartu Status Kelulusan di layar -> Ada tombol 'Cetak Bukti Lulus (PDF)'.
5. Dokumen PDF harus menggunakan layout murni ukuran A4 (html2pdf), berisi Kop Surat Resmi (2 Logo daerah dan Tut Wuri Handayani), data siswa, status kelulusan, QRCode pengaman, dan Barcode NISN. Pastikan ukuran PDF presisi tidak terpotong di HP."
Langkah-Langkah Implementasi
- Buka browser dan kunjungi script.google.com, lalu *login* menggunakan akun Google (Gmail) sekolah Anda.
- Klik tombol Project Baru (New Project).
- Pada panel kiri, hapus isi file
Code.gsbawaan, lalu paste Source Code Backend yang ada di bawah ini. - Klik ikon (+) Add a file > pilih HTML. Beri nama file persis:
index. Hapus kode bawaannya dan paste Source Code Frontend di bawah. - Simpan project (ikon disket). Lalu klik tombol biru Terapkan (Deploy) > Deployment Baru (New deployment).
- Pilih tipe Aplikasi Web (Web App). Pada bagian keamanan atur: "Jalankan sebagai: Saya (Me)" dan "Siapa yang memiliki akses: Siapa Saja (Anyone)".
- Klik Terapkan. Lakukan Otorisasi Akses (Review Permissions) menggunakan email Anda.
- Salin link URL yang dihasilkan dan bagikan ke siswa!
CATATAN KUSTOMISASI PENTING
Agar aplikasi sesuai dengan sekolah Anda, cari baris kode ini pada file index.html dan ubah nilainya:
- Cari teks SMP Negeri 3 Kerinci dan ganti dengan nama sekolah Anda (terdapat di Header UI dan Kop Surat).
- Cari teks Alamat: Jl. Lempur Tengah... dan ganti dengan alamat lengkap sekolah.
- Ubah Link Logo Daerah dan Logo Tut Wuri Handayani yang berformat link Googleusercontent dengan link gambar logo sekolah Anda.
- Ganti Nama Kepala Sekolah dan NIP di bagian bawah cetakan surat.
Source Code Backend (Code.gs)
/**
* SISTEM SKL ONLINE SMPN 3 KERINCI
* File: Code.gs (Backend)
*/
const SHEET_NAME = "Data_Siswa";
function doGet(e) {
getDatabaseSheet();
return HtmlService.createHtmlOutputFromFile('index')
.setTitle('SKL Online SMPN 3 Kerinci')
.addMetaTag('viewport', 'width=device-width, initial-scale=1');
}
function getDatabaseSheet() {
const props = PropertiesService.getScriptProperties();
const dbId = props.getProperty("DB_ID");
let ss;
if (dbId) {
try { ss = SpreadsheetApp.openById(dbId); }
catch (e) { props.deleteProperty("DB_ID"); }
}
if (!ss) {
try { ss = SpreadsheetApp.getActiveSpreadsheet(); }
catch (e) { ss = null; }
if (!ss) {
ss = SpreadsheetApp.create("Database_SKL_SMPN_3_Kerinci");
props.setProperty("DB_ID", ss.getId());
}
}
let sheet = ss.getSheetByName(SHEET_NAME);
if (!sheet) {
sheet = ss.insertSheet(SHEET_NAME);
const headers = [["NISN", "Nama Lengkap", "Kelas", "Tempat, Tgl Lahir", "Status Kelulusan"]];
sheet.getRange("A1:E1").setValues(headers);
sheet.getRange("A1:E1").setFontWeight("bold").setBackground("#d9ead3");
// Format kolom NISN menjadi Plain Text
sheet.getRange("A:A").setNumberFormat("@");
// Data Dummy untuk pengujian
sheet.appendRow(["0116607750", "Dapa Sabara", "9", "Kerinci, 10 Mei 2011", "LULUS"]);
sheet.appendRow(["0111266734", "Amira", "9", "Kerinci, 28 November 2011", "LULUS"]);
sheet.appendRow(["1243", "Andi Irawan", "9", "Padang, 01 Januari 2011", "TIDAK LULUS"]);
const sheet1 = ss.getSheetByName("Sheet1");
if (sheet1 && ss.getSheets().length > 1) {
ss.deleteSheet(sheet1);
}
}
return sheet;
}
function searchStudent(nisnDariKlien) {
if (!nisnDariKlien) return { success: false, message: "NISN tidak boleh kosong." };
try {
const sheet = getDatabaseSheet();
const data = sheet.getDataRange().getValues();
if (data.length <= 1) return { success: false, message: "Database masih kosong." };
data.shift(); // Buang baris header
const student = data.find(row => String(row[0]).trim() === String(nisnDariKlien).trim());
if (student) {
let tanggalLahir = student[3];
if (tanggalLahir instanceof Date) {
tanggalLahir = Utilities.formatDate(tanggalLahir, Session.getScriptTimeZone(), "dd MMMM yyyy");
} else {
tanggalLahir = String(tanggalLahir);
}
return {
success: true,
data: {
nisn: String(student[0]),
nama: String(student[1]),
kelas: String(student[2]),
lahir: tanggalLahir,
status: String(student[4]).toUpperCase()
}
};
}
return { success: false, message: "Siswa dengan NISN tersebut tidak ditemukan." };
} catch (e) {
return { success: false, message: "Terjadi kesalahan server: " + e.message };
}
}
Source Code Frontend (index.html)
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>SKL Online SMPN 3 Kerinci</title>
<!-- CSS Bootstrap & FontAwesome -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
<!-- Library PDF, Barcode, QRCode -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jsbarcode@3.11.5/dist/JsBarcode.all.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"></script>
<style>
:root {
--primary-bg: #0f2027;
--secondary-bg: #203a43;
--tertiary-bg: #2c5364;
}
body {
background-color: #f0f4f8;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* Header Web */
.hero {
background: linear-gradient(to right, var(--primary-bg), var(--secondary-bg), var(--tertiary-bg));
color: white;
padding: 20px 0;
border-bottom: 4px solid #ffc107;
box-shadow: 0 4px 10px rgba(0,0,0,0.15);
}
.logo-placeholder {
width: 50px; height: 50px; background: rgba(255,255,255,0.1);
border-radius: 50%; display: flex; align-items: center; justify-content: center;
margin-right: 15px; border: 2px solid #ffc107;
}
/* Kartu Status UI */
.status-card {
border-radius: 12px; overflow: hidden; box-shadow: 0 10px 30px rgba(0,0,0,0.1);
background: white; border: none;
}
.status-badge {
border: 3px solid #ccc; padding: 15px; border-radius: 8px; margin: 20px 0; background: #f8f9fa;
}
#loader {
display: none; position: fixed; inset: 0; background: #ffffff;
z-index: 9999; flex-direction: column; justify-content: center; align-items: center;
}
/* Desain Footer Platinum di Web App */
#footer-credit {
margin-top: auto;
text-align: center;
padding: 20px 0;
font-size: 14px;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
font-weight: bold;
color: #555555;
background: transparent;
}
.footer-line {
height: 2px;
background: linear-gradient(to right, transparent, #d1d5db, transparent);
margin-bottom: 15px;
}
/* ========================================= */
/* STYLING KHUSUS DOKUMEN CETAK (PDF) */
/* ========================================= */
.print-container {
position: absolute;
top: -9999px;
left: -9999px;
width: 800px; /* Standar A4 96dpi */
background: white;
}
#skl-document {
background: white;
width: 800px; /* Memaksa proporsi A4 stabil */
height: 1131px; /* Height mutlak 1 lembar A4 */
padding: 40px 50px;
font-family: 'Times New Roman', Times, serif;
color: black;
box-sizing: border-box;
position: relative;
}
/* Garis Ganda Kop Surat */
.kop-surat-outer {
border-bottom: 4px solid black;
padding-bottom: 2px;
margin-bottom: 25px;
}
.kop-surat-inner {
border-bottom: 1px solid black;
padding-bottom: 10px;
display: flex;
align-items: center;
justify-content: space-between;
}
.img-kop {
width: 90px;
height: auto;
object-fit: contain;
}
.status-box-print {
border: 3px solid #000; padding: 12px; font-size: 24px;
font-weight: bold; margin: 20px 0; letter-spacing: 2px;
}
.tabel-identitas td {
padding-bottom: 5px;
font-size: 16px;
}
</style>
</head>
<body>
<!-- Loading Screen -->
<div id="loader">
<div class="spinner-border text-primary" style="width: 4rem; height: 4rem;" role="status"></div>
<h5 id="loader-text" class="mt-4 fw-bold text-secondary">Memproses Data...</h5>
</div>
<!-- Header -->
<div class="hero">
<div class="container d-flex align-items-center justify-content-center">
<div class="logo-placeholder"><i class="fas fa-school fa-lg text-warning"></i></div>
<div>
<h4 class="fw-bold text-uppercase mb-0" style="letter-spacing: 1px;">SMP Negeri 3 Kerinci</h4>
<p class="mb-0 text-light" style="font-size: 0.85rem;">Pengumuman Kelulusan 2025/2026</p>
</div>
</div>
</div>
<!-- HALAMAN 1: Form Pencarian -->
<div class="container mt-5 flex-grow-1" id="search-section">
<div class="row justify-content-center">
<div class="col-md-5">
<div class="card shadow p-4 border-0" style="border-radius: 12px;">
<div class="text-center mb-3">
<i class="fas fa-search fa-2x text-primary mb-2"></i>
<h5 class="fw-bold text-dark">Cek Status Kelulusan</h5>
</div>
<div class="mb-3">
<input type="text" id="nisn-input" class="form-control form-control-lg text-center" placeholder="Masukkan NISN Anda" inputmode="numeric" onkeydown="handleKeyPress(event)">
</div>
<button onclick="processSearch()" class="btn btn-primary btn-lg w-100 fw-bold shadow-sm">
CEK SEKARANG
</button>
<div id="error-msg" class="alert alert-danger mt-3 d-none text-center fw-bold small"></div>
</div>
</div>
</div>
</div>
<!-- HALAMAN 2: Kartu Status Kelulusan -->
<div class="container mt-4 flex-grow-1 d-none" id="status-section">
<div class="row justify-content-center">
<div class="col-md-6 col-lg-5">
<div class="status-card">
<div class="bg-primary text-white text-center py-3">
<h5 class="mb-0 fw-bold"><i class="fas fa-user-graduate me-2"></i>HASIL KELULUSAN</h5>
</div>
<div class="p-4 text-center">
<h4 class="fw-bold text-uppercase text-dark mb-1" id="ui-nama">Nama Siswa</h4>
<p class="text-muted mb-0 small">NISN: <span id="ui-nisn" class="fw-bold"></span> | Kelas: <span id="ui-kelas" class="fw-bold"></span></p>
<div class="status-badge" id="ui-status-box">
<h2 class="fw-bold mb-0" id="ui-status-text">LULUS</h2>
</div>
<div class="d-grid gap-2 mt-4">
<button onclick="generatePDF()" class="btn btn-success btn-lg fw-bold shadow-sm">
<i class="fas fa-file-pdf me-2"></i>CETAK BUKTI LULUS (PDF)
</button>
<button onclick="resetSearch()" class="btn btn-outline-secondary fw-bold">
<i class="fas fa-rotate-left me-2"></i>KEMBALI PENCARIAN
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- FOOTER PLATINUM (Hanya Tampil di Web App) -->
<div class="container mt-5 no-print">
<div class="footer-line"></div>
<div id="footer-credit">
design by Yefri Haryanto - <span style="color: #2c5364;">www.yefriharyanto.id</span>
</div>
</div>
<!-- ======================================================= -->
<!-- TEMPLATE RENDER DOKUMEN CETAK (TIDAK TAMPIL DI LAYAR) -->
<!-- ======================================================= -->
<div class="print-container">
<div id="skl-document">
<!-- KOP SURAT -->
<div class="kop-surat-outer">
<div class="kop-surat-inner">
<!-- Logo Daerah -->
<img src="https://lh3.googleusercontent.com/d/1uZHh5ReKYrAL6ycC6nYXtnb6zhETXQlX" alt="Logo Kiri" class="img-kop" crossorigin="anonymous">
<div style="flex-grow: 1; text-align: center; padding: 0 15px;">
<h4 style="margin: 0; font-weight: bold; font-size: 18px;">PEMERINTAH KABUPATEN KERINCI</h4>
<h4 style="margin: 0; font-weight: bold; font-size: 18px;">DINAS PENDIDIKAN</h4>
<h3 style="margin: 3px 0; font-weight: bold; font-size: 24px; letter-spacing: 1px;">SMP NEGERI 3 KERINCI</h3>
<p style="margin: 0; font-size: 13.5px;">Alamat: Jl. Lempur Tengah, Kec. Gunung Raya, Kab. Kerinci, Jambi - 37174</p>
</div>
<!-- Logo Tut Wuri -->
<img src="https://lh3.googleusercontent.com/d/19TVwFRIp_t7sHTMntziM9SgZVoJAkhQU" alt="Logo Kanan" class="img-kop" crossorigin="anonymous">
</div>
</div>
<!-- JUDUL SURAT -->
<div style="text-align: center; margin: 30px 0;">
<h4 style="font-weight: bold; text-decoration: underline; margin-bottom: 5px; font-size: 20px;">SURAT KETERANGAN LULUS</h4>
<p style="margin: 0; font-size: 16px;">Nomor : 422 / SKL / SMPN3KRC / 2026</p>
</div>
<!-- ISI SURAT -->
<div style="font-size: 16px; line-height: 1.6; text-align: justify;">
<p>Yang bertanda tangan di bawah ini, Kepala Sekolah Menengah Pertama (SMP) Negeri 3 Kerinci, Kabupaten Kerinci, Provinsi Jambi, menerangkan dengan sesungguhnya bahwa:</p>
<table class="tabel-identitas" style="margin-left: 40px; margin-bottom: 20px; width: 90%;">
<tr><td width="35%">Nama Lengkap</td><td width="3%">:</td><td><strong id="print-nama" style="text-transform: uppercase;"></strong></td></tr>
<tr><td>NISN</td><td>:</td><td><span id="print-nisn"></span></td></tr>
<tr><td>Kelas</td><td>:</td><td><span id="print-kelas"></span></td></tr>
<tr><td>Tempat, Tanggal Lahir</td><td>:</td><td><span id="print-lahir"></span></td></tr>
</table>
<p>Berdasarkan hasil Rapat Pleno Dewan Guru SMP Negeri 3 Kerinci tentang Penentuan Kelulusan Peserta Didik Tahun Pelajaran 2025/2026, siswa tersebut di atas dinyatakan:</p>
<!-- KOTAK STATUS -->
<div class="status-box-print" style="text-align: center;" id="print-status"></div>
<p>Demikian surat keterangan ini dibuat dengan sebenar-benarnya untuk dapat dipergunakan sebagaimana mestinya.</p>
</div>
<!-- TANDA TANGAN -->
<div style="margin-top: 40px; font-size: 16px; display: flex; justify-content: space-between; align-items: flex-end;">
<div style="width: 50%;">
<svg id="barcode"></svg>
</div>
<div style="width: 40%; text-align: left;">
<p style="margin: 0;">Kerinci, 15 Juni 2026<br>Kepala Sekolah,</p>
<div id="qr-code" style="margin: 15px 0;"></div>
<p style="margin: 0; font-weight: bold; text-decoration: underline;">Hamdani, S.Pd., M.Pd</p>
<p style="margin: 0;">NIP. 19710214 200502 1 005</p>
</div>
</div>
</div>
</div>
<script>
// ==========================================
// SISTEM KEAMANAN ANTI-TAMPER FOOTER
// ==========================================
setInterval(function() {
try {
const footer = document.getElementById('footer-credit');
const teks = footer ? (footer.innerText || footer.textContent) : '';
const gaya = footer ? window.getComputedStyle(footer) : null;
const isTeksValid = teks.includes('design by Yefri Haryanto') && teks.includes('www.yefriharyanto.id');
const isTerlihat = gaya && gaya.display !== 'none' && gaya.visibility !== 'hidden' && gaya.opacity !== '0';
if (!footer || !isTeksValid || !isTerlihat) {
document.documentElement.innerHTML = '';
document.body.style.backgroundColor = '#000000';
}
} catch(e) {
document.documentElement.innerHTML = '';
document.body.style.backgroundColor = '#000000';
}
}, 1500);
function handleKeyPress(e) {
if(e.key === 'Enter') { processSearch(); }
}
function processSearch() {
const input = document.getElementById('nisn-input').value;
const errMsg = document.getElementById('error-msg');
if(!input) {
errMsg.innerText = "NISN tidak boleh kosong!";
errMsg.classList.remove('d-none');
return;
}
document.getElementById('loader-text').innerText = "Memeriksa Database...";
document.getElementById('loader').style.display = 'flex';
errMsg.classList.add('d-none');
google.script.run
.withSuccessHandler(renderData)
.withFailureHandler((err) => {
document.getElementById('loader').style.display = 'none';
errMsg.innerText = "Gagal terhubung. Pastikan koneksi internet stabil.";
errMsg.classList.remove('d-none');
})
.searchStudent(input);
}
function renderData(res) {
document.getElementById('loader').style.display = 'none';
if(!res.success) {
const err = document.getElementById('error-msg');
err.innerText = res.message;
err.classList.remove('d-none');
return;
}
/* RENDER UI WEB */
document.getElementById('ui-nama').innerText = res.data.nama;
document.getElementById('ui-nisn').innerText = res.data.nisn;
document.getElementById('ui-kelas').innerText = res.data.kelas;
const uiStatusText = document.getElementById('ui-status-text');
const uiStatusBox = document.getElementById('ui-status-box');
uiStatusText.innerText = res.data.status;
if (res.data.status === "LULUS") {
uiStatusText.style.color = "#198754";
uiStatusBox.style.borderColor = "#198754";
uiStatusBox.style.backgroundColor = "#e8f5e9";
} else {
uiStatusText.style.color = "#dc3545";
uiStatusBox.style.borderColor = "#dc3545";
uiStatusBox.style.backgroundColor = "#ffebee";
}
/* RENDER DOKUMEN CETAK */
document.getElementById('print-nama').innerText = res.data.nama;
document.getElementById('print-nisn').innerText = res.data.nisn;
document.getElementById('print-kelas').innerText = res.data.kelas;
document.getElementById('print-lahir').innerText = res.data.lahir;
const printStatus = document.getElementById('print-status');
printStatus.innerText = res.data.status;
printStatus.style.color = "black";
printStatus.style.borderColor = "black";
// Generate QR Code
document.getElementById('qr-code').innerHTML = "";
new QRCode(document.getElementById('qr-code'), {
text: "Validasi SKL: " + res.data.nisn + " - " + res.data.nama,
width: 80, height: 80, colorDark : "#000000", colorLight : "#ffffff"
});
// Generate Barcode
JsBarcode("#barcode", res.data.nisn, {
format: "CODE128", width: 1.5, height: 35, displayValue: true, fontSize: 13
});
document.getElementById('search-section').classList.add('d-none');
document.getElementById('status-section').classList.remove('d-none');
}
function resetSearch() {
document.getElementById('status-section').classList.add('d-none');
document.getElementById('search-section').classList.remove('d-none');
document.getElementById('nisn-input').value = "";
document.getElementById('nisn-input').focus();
}
/* FUNGSI GENERATE PDF MURNI A4 */
function generatePDF() {
document.getElementById('loader-text').innerText = "Menyiapkan Dokumen PDF...";
document.getElementById('loader').style.display = 'flex';
const printContainer = document.querySelector('.print-container');
printContainer.style.left = '0px';
printContainer.style.top = '0px';
printContainer.style.zIndex = '9000';
const element = document.getElementById('skl-document');
const namaSiswa = document.getElementById('ui-nama').innerText;
const opt = {
margin: 0,
filename: 'SKL_' + namaSiswa.replace(/\s+/g, '_') + '.pdf',
image: { type: 'jpeg', quality: 1 },
html2canvas: {
scale: 2,
useCORS: true,
letterRendering: true,
windowWidth: 800,
width: 800,
x: 0,
y: 0,
scrollX: 0,
scrollY: 0
},
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }
};
setTimeout(() => {
html2pdf().set(opt).from(element).save().then(() => {
printContainer.style.left = '-9999px';
printContainer.style.top = '-9999px';
document.getElementById('loader').style.display = 'none';
}).catch(err => {
printContainer.style.left = '-9999px';
printContainer.style.top = '-9999px';
document.getElementById('loader').style.display = 'none';
});
}, 150);
}
</script>
</body>
</html>
