Prompt dan Download Source Code Aplikasi Administrasi Guru Menggunakan Appscript dan Blogger
Di era digitalisasi pendidikan, efisiensi kerja guru bukan lagi sebuah pilihan, melainkan keharusan. Postingan ini akan membagikan secara gratis Source Code Aplikasi Administrasi Guru berdesain Ultra Platinum, lengkap dengan panduan penggunaan menggunakan teknologi gratis dari Google.
Apa itu Administrasi Guru?
Administrasi guru adalah segala bentuk kegiatan pendataan, pencatatan, dan pelaporan yang berkaitan dengan proses Kegiatan Belajar Mengajar (KBM). Ini meliputi pembuatan Rencana Pelaksanaan Pembelajaran (RPP), absensi siswa, buku leger nilai, jurnal agenda harian mengajar, hingga catatan bimbingan konseling (Wali Kelas).
Perbedaan Administrasi Manual vs Aplikasi
| Aspek | Administrasi Manual (Buku/Kertas) | Aplikasi Administrasi (Digital) |
|---|---|---|
| Penyimpanan | Membutuhkan banyak rak buku, rawan rusak/hilang. | Tersimpan aman di Cloud, bisa diakses dari perangkat manapun. |
| Pencarian Data | Lama, harus membolak-balik halaman. | Cepat, hitungan detik dengan fitur filter/search. |
| Rekapitulasi | Dihitung manual menggunakan kalkulator, rawan *human error*. | Otomatis. Rata-rata nilai dan persentase kehadiran langsung tampil. |
| Pelaporan | Harus mengetik ulang atau menulis di raport. | Tinggal tekan tombol, format PDF langsung tercetak siap setor. |
Mengenal Google Apps Script & Blogger
Google Apps Script (GAS) adalah bahasa *scripting* berbasis cloud buatan Google (mirip JavaScript). Manfaat utamanya adalah kita bisa membuat backend/API gratis selamanya dengan menjadikan Google Spreadsheet sebagai databasenya. Tidak perlu menyewa server/hosting mahal!
Blogger adalah platform *Content Management System* (CMS) gratis dari Google. Selain untuk menulis artikel, Blogger sangat powerfull digunakan sebagai *Frontend* (Tampilan Antarmuka) aplikasi karena gratis hosting dan memiliki *bandwidth* tanpa batas.
Prompt Membuat Aplikasi Administrasi Guru
Jika Anda ingin berlatih membuat aplikasi ini sendiri menggunakan bantuan AI (seperti ChatGPT atau Gemini), Anda bisa menggunakan Prompt Engineer detail berikut ini:
Spesifikasi Frontend: Buatkan desain UI/UX tema 'Ultra Platinum Modern' (Dark mode, glassmorphism, sudut melengkung, gradasi warna kontras). Aplikasi ini bersifat Single User (Langsung masuk ke dashboard sebagai Super Admin tanpa login). Buat sidebar menu yang berisi: Dashboard, Input Absensi, Olah Penilaian, Jadwal Mengajar, Jurnal Agenda, Guru Wali (Bimbingan), Kelola Data Induk, Import Siswa, dan Konfigurasi. Sediakan fitur cetak ke PDF dengan jsPDF untuk setiap laporan.
Spesifikasi Backend (Code.gs): Gunakan Google Spreadsheet sebagai database dengan sheet: Config, Kelas, Mapel, DataSiswa, Absensi, Nilai, Agenda, BimbinganWali, SiswaBimbingan, JadwalMengajar. Buat fungsi doPost untuk menangani API request (JSON) untuk Read, Insert, dan Delete data dari frontend. Hilangkan semua validasi nama/user guru karena ini adalah aplikasi versi single-user global."
Download Tema Frontend Blogger (HTML)
Klik tombol dengan warna kontras di bawah ini untuk mengunduh file HTML (Frontend) yang tinggal Anda copy-paste ke tema Blogger Anda. Unduhan ini menggunakan metode Direct Download.
Download Tema HTMLLangkah-Langkah Pemasangan & Setting URL
- Buat Spreadsheet Baru: Buka Google Spreadsheet, beri nama bebas (misal: "DB Admin Guru"). Buka menu Ekstensi > Apps Script.
- Paste Kode Backend: Hapus kode bawaan di
Code.gs, lalu paste Source Code Backend yang ada di bagian bawah artikel ini. Simpan project tersebut. - Deploy Apps Script: Klik tombol Terapkan (Deploy) > Deployment Baru. Pilih jenis "Aplikasi Web". Akses: "Siapa saja (Anyone)". Izinkan semua akses akun Google yang diminta. Salin URL Aplikasi Web (Web App URL) yang diberikan.
- Pasang di Blogger: Buka Dasbor Blogger > Tema > Edit HTML. Ganti seluruh isi HTML dengan file yang sudah Anda download di atas.
- Hubungkan Aplikasi: Di dalam kode HTML Blogger yang sudah Anda paste, cari baris kode ini:
const GAS_URL = "https://script.google.com/macros/s/AKfycbzQy_xxxxx_GANTI_URL_INI/exec";
Ganti teks URL tersebut dengan URL Aplikasi Web (Web App URL) yang Anda dapatkan di Langkah 3. Simpan Tema Blogger Anda. Aplikasi siap digunakan!
Source Code Backend (Google Apps Script)
Silakan copy kode di bawah ini tanpa mengubah satu karakter pun dan paste ke dalam file Code.gs di project Google Apps Script Anda.
/**
* SISTEM ADMINISTRASI TERPADU (Single User - Ultra Platinum)
* Update: Penghapusan limitasi berdasar nama guru & Sistem Single User Global
*/
function doPost(e) {
var response = { status: 'error', message: 'Unknown action' };
try {
var request = JSON.parse(e.postData.contents);
var action = request.action;
var p = request.payload || {};
// Semua fungsi langsung berjalan tanpa perlu verifikasi Login User
if (action === 'init') response = { status: 'success', data: getInitData() };
else if (action === 'getSiswa') response = { status: 'success', data: getSiswa(p.kelas) };
else if (action === 'simpanAbsen') response = { status: 'success', message: simpanAbsen(p.kelas, p.mapel, p.guru, p.data) };
else if (action === 'getLaporanAbsen') response = { status: 'success', data: getLaporanAbsen(p.kelas, p.mapel, p.bln, p.thn) };
else if (action === 'simpanNilai') response = { status: 'success', message: simpanNilai(p.jns, p.mapel, p.kelas, p.guru, p.data) };
else if (action === 'getLeger') response = { status: 'success', data: getLeger(p.kelas, p.mapel) };
else if (action === 'getRiwayatNilai') response = { status: 'success', data: getRiwayatNilai(p.kelas, p.mapel) };
else if (action === 'saveAgenda') response = { status: 'success', message: saveAgenda(p.data) };
else if (action === 'getAgenda') response = { status: 'success', data: getAgenda() };
else if (action === 'simpanSiswaBimbingan') { simpanSiswaBimbingan(p.nama, p.kelas, p.guru); response = { status: 'success', message: 'Disimpan' }; }
else if (action === 'getSiswaBimbingan') response = { status: 'success', data: getSiswaBimbingan() };
else if (action === 'simpanCatatanBimbingan') { simpanCatatanBimbingan(p.data); response = { status: 'success', message: 'Disimpan' }; }
else if (action === 'getRiwayatBimbingan') response = { status: 'success', data: getRiwayatBimbingan() };
else if (action === 'getAllBimbingan') response = { status: 'success', data: getAllBimbingan() };
else if (action === 'saveConfig') response = { status: 'success', message: saveConfig(p.key, p.val) };
else if (action === 'manageItem') response = { status: 'success', message: manageItem(p.type, p.action, p.v1) };
else if (action === 'importSiswa') response = { status: 'success', message: importSiswa(p.arr, p.kls) };
else if (action === 'simpanJadwal') response = { status: 'success', message: simpanJadwal(p.data) };
else if (action === 'getJadwal') response = { status: 'success', data: getJadwal() };
else if (action === 'hapusJadwal') response = { status: 'success', message: hapusJadwal(p.hari, p.jam) };
} catch (err) { response = { status: 'error', message: err.message }; }
return ContentService.createTextOutput(JSON.stringify(response)).setMimeType(ContentService.MimeType.JSON);
}
function doGet(e) { return ContentService.createTextOutput("API Aktif. (Single User Version)."); }
function openDatabase() { return SpreadsheetApp.getActiveSpreadsheet(); }
function setupStructure(ss) {
const struct = [
{ name: "Config", headers: ["Key", "Value"] },
{ name: "Kelas", headers: ["Nama Kelas"] }, { name: "Mapel", headers: ["Nama Mapel"] }, { name: "DataSiswa", headers: ["No", "Nama Siswa", "Kelas"] },
{ name: "Absensi", headers: ["Waktu", "Kelas", "Mapel", "Nama Siswa", "Status", "Nama Guru", "Bulan", "Tahun"] }, { name: "Nilai", headers: ["Waktu", "Jenis", "Mapel", "Kelas", "Nama Siswa", "Nilai", "Keterangan", "Nama Guru"] },
{ name: "Agenda", headers: ["Hari/Tgl", "Jam", "Kelas", "Mapel", "Materi", "Status", "Absen Siswa", "Ket", "Nama Guru"] }, { name: "BimbinganWali", headers: ["Tanggal", "Nama Siswa", "Kelas", "Jenis", "Masalah", "Solusi", "Guru Wali"] },
{ name: "SiswaBimbingan", headers: ["Nama Siswa", "Kelas", "Guru Wali"] }, { name: "JadwalMengajar", headers: ["Nama Guru", "Hari", "Jam Ke", "Kelas", "Mapel"] }
];
struct.forEach(s => {
let sheet = ss.getSheetByName(s.name);
if (!sheet) { sheet = ss.insertSheet(s.name); sheet.appendRow(s.headers); sheet.getRange(1, 1, 1, s.headers.length).setFontWeight("bold").setBackground("#2c3e50").setFontColor("white"); }
});
const cS = ss.getSheetByName("Config");
if(cS.getLastRow() < 2) {
const defs = [ ["Nama Pemerintah", "PEMERINTAH KABUPATEN/PROVINSI"], ["Nama Sekolah", "SEKOLAH MODERN PLATINUM"], ["Alamat Sekolah", "Jalan Utama No.1"], ["Nama Kepala Sekolah", "NAMA KEPSEK, M.Pd"], ["NIP Kepala Sekolah", "1980..."], ["Logo Kiri", ""], ["Logo Kanan", ""], ["Link Spreadsheet", ss.getUrl()], ["Tempat Tanda Tangan", "Lokasi"] ];
defs.forEach(d=>cS.appendRow(d));
}
}
function getInitData() {
const ss = openDatabase(); setupStructure(ss);
const conf = {}; ss.getSheetByName("Config").getDataRange().getDisplayValues().forEach(r => { if(r[0]) conf[r[0]] = r[1]; });
const mapel = ss.getSheetByName("Mapel").getDataRange().getDisplayValues().slice(1).flat().filter(String);
const kelas = ss.getSheetByName("Kelas").getDataRange().getDisplayValues().slice(1).flat().filter(String);
const jmlSiswa = ss.getSheetByName("DataSiswa").getLastRow() - 1;
return { config: conf, mapel: mapel, kelas: kelas, stats: {siswa: jmlSiswa > 0 ? jmlSiswa : 0, rombel: kelas.length} };
}
function saveConfig(key, val) {
const lock = LockService.getScriptLock();
try {
lock.waitLock(10000); const s = openDatabase().getSheetByName("Config"); const data = s.getDataRange().getValues(); let found = false;
for(let i=0; i=0; i--){ if(data[i][0] == v1) { sheet.deleteRow(i+1); break; } }
}
return "Sukses";
} catch(e) { return "Gagal menyimpan"; } finally { lock.releaseLock(); }
}
function importSiswa(arr, kls) {
const lock = LockService.getScriptLock();
try {
lock.waitLock(10000);
const s = openDatabase().getSheetByName("DataSiswa");
let last = s.getLastRow(); let rows = [];
arr.forEach(n=>{
if(n && n.trim()) {
let cleanName = n.trim().replace(/\s+/g, ' ');
rows.push([last, cleanName, kls]);
last++;
}
});
if(rows.length) s.getRange(s.getLastRow()+1,1,rows.length,3).setValues(rows);
return rows.length + " Data Masuk";
} catch(e) { return "Gagal Import"; } finally { lock.releaseLock(); }
}
function simpanJadwal(d) { const lock = LockService.getScriptLock(); try { lock.waitLock(10000); openDatabase().getSheetByName("JadwalMengajar").appendRow(d); return "Tersimpan"; } finally { lock.releaseLock(); } }
function getJadwal() {
const s = openDatabase().getSheetByName("JadwalMengajar"); if(s.getLastRow() < 2) return [];
// Tampilkan semua jadwal tanpa filter user
return s.getRange(2,1,s.getLastRow()-1,5).getDisplayValues();
}
function hapusJadwal(hari, jam) {
const lock = LockService.getScriptLock();
try {
lock.waitLock(10000); const s = openDatabase().getSheetByName("JadwalMengajar"); const data = s.getDataRange().getDisplayValues();
// Hapus tanpa verifikasi guru
for(let i=data.length-1; i>=1; i--) { if(data[i][1] == hari && data[i][2] == jam) { s.deleteRow(i+1); return "Dihapus"; } }
return "Gagal Menghapus";
} finally { lock.releaseLock(); }
}
function getSiswa(k) {
const s = openDatabase().getSheetByName("DataSiswa"); if(s.getLastRow()<2) return [];
return s.getRange(2,2,s.getLastRow()-1,2).getDisplayValues().filter(r=>r[1]==k).map(r=>r[0]).sort();
}
function simpanAbsen(kls, mapel, guru, data) {
const lock = LockService.getScriptLock();
try {
lock.waitLock(10000); const s = openDatabase().getSheetByName("Absensi"); const time = new Date();
const rows = data.map(d=>[time, kls, mapel, d.nama, d.sts, guru, time.getMonth()+1, time.getFullYear()]);
s.getRange(s.getLastRow()+1,1,rows.length,8).setValues(rows); return "Tersimpan";
} catch(e) { return "Error: " + e.message; } finally { lock.releaseLock(); }
}
function getLaporanAbsen(kls, mapel, bln, thn) {
const s = openDatabase().getSheetByName("Absensi"); const sList = getSiswa(kls); if(sList.length==0) return { error: "Siswa Kosong" }; let data = [];
if(s.getLastRow() > 1) {
const raw = s.getRange(2,1,s.getLastRow()-1,8).getValues();
// Tanpa Filter Guru
if(bln === "ALL") data = raw.filter(r => r[1]==kls && r[2]==mapel && r[7]==thn); else data = raw.filter(r => r[1]==kls && r[2]==mapel && r[6]==bln && r[7]==thn);
}
let rekap = sList.map((nama, i) => {
let st = {H:0, S:0, I:0, A:0};
data.filter(r=>r[3]==nama).forEach(r=>{ let s=r[4].charAt(0); if(s=='S') st.S++; else if(s=='I') st.I++; else if(s=='A') st.A++; else st.H++; });
let tot = st.H+st.S+st.I+st.A; let pct = tot>0 ? Math.round((st.H/tot)*100) : 0; return { no:i+1, nama:nama, s:st.S, i:st.I, a:st.A, h:st.H, pct:pct };
});
let harian = data.map((r,i) => ({ no: i+1, waktu: Utilities.formatDate(new Date(r[0]), "Asia/Jakarta", "dd/MM/yyyy HH:mm"), nama: r[3], status: r[4] }));
return { rekap: rekap, harian: harian };
}
function simpanNilai(jns, mapel, kls, guru, data) {
const lock = LockService.getScriptLock();
try {
lock.waitLock(10000); const s = openDatabase().getSheetByName("Nilai"); const time = new Date();
const rows = data.map(d=>[time, jns, mapel, kls, d.nama, d.val, '', guru]);
s.getRange(s.getLastRow()+1,1,rows.length,8).setValues(rows); return "Tersimpan";
} catch(e) { return "Error"; } finally { lock.releaseLock(); }
}
function getLeger(kls, mapel) {
const s = openDatabase().getSheetByName("Nilai"); const sList = getSiswa(kls); if(sList.length==0) return { error: "Siswa Kosong" }; let data = [];
// Tanpa filter Guru
if(s.getLastRow() > 1) data = s.getRange(2,1,s.getLastRow()-1,8).getValues().filter(r => r[3]==kls && r[2]==mapel);
let headers = [...new Set(data.map(r=>r[1]))].sort();
let res = sList.map((nama,i) => {
let row = { no:i+1, nama:nama, tot:0, cnt:0 };
headers.forEach(h => {
let f = data.filter(r => r[4]==nama && r[1]==h); let val = parseFloat(f.length ? String(f[f.length-1][5]).replace(',','.') : 0) || 0;
row[h] = val; if(val !== null) { row.tot+=val; row.cnt++; }
});
row.avg = (row.tot / (headers.length > 0 ? headers.length : 1)).toFixed(1); row.tot = row.tot.toFixed(0); return row;
});
return { headers: headers, data: res };
}
function getRiwayatNilai(kls, mapel) {
const s = openDatabase().getSheetByName("Nilai"); if(s.getLastRow() < 2) return [];
// Tanpa filter Guru
return s.getRange(2,1,s.getLastRow()-1,8).getValues().filter(r => r[3]==kls && r[2]==mapel).sort((a,b) => new Date(b[0]) - new Date(a[0])).map(r => {
r[0] = Utilities.formatDate(new Date(r[0]), "Asia/Jakarta", "dd/MM/yyyy HH:mm"); return r;
});
}
function saveAgenda(d) { const lock = LockService.getScriptLock(); try { lock.waitLock(10000); openDatabase().getSheetByName("Agenda").appendRow(d); return "Tersimpan"; } finally { lock.releaseLock(); } }
function getAgenda() {
const s = openDatabase().getSheetByName("Agenda"); if (s.getLastRow() < 2) return [];
// Tampilkan semua agenda
return s.getRange(2, 1, s.getLastRow() - 1, 9).getDisplayValues();
}
function simpanSiswaBimbingan(n, k, g) { const lock = LockService.getScriptLock(); try { lock.waitLock(10000); openDatabase().getSheetByName("SiswaBimbingan").appendRow([n, k, g]); } finally { lock.releaseLock(); } }
function getSiswaBimbingan() {
const s = openDatabase().getSheetByName("SiswaBimbingan"); if (s.getLastRow() < 2) return [];
// Tampilkan semua siswa bimbingan
return s.getRange(2, 1, s.getLastRow() - 1, 3).getDisplayValues().map(r => ({ nama: r[0], kelas: r[1] }));
}
function simpanCatatanBimbingan(d) { const lock = LockService.getScriptLock(); try { lock.waitLock(10000); openDatabase().getSheetByName("BimbinganWali").appendRow(d); } finally { lock.releaseLock(); } }
function getRiwayatBimbingan() {
const s = openDatabase().getSheetByName("BimbinganWali"); if(s.getLastRow()<2) return [];
// Tampilkan semua riwayat
return s.getRange(2,1,s.getLastRow()-1,7).getValues().map(r => { r[0] = Utilities.formatDate(new Date(r[0]), "Asia/Jakarta", "dd/MM/yyyy"); return r; });
}
function getAllBimbingan() {
const s = openDatabase().getSheetByName("BimbinganWali"); if(s.getLastRow() < 2) return [];
return s.getRange(2, 1, s.getLastRow()-1, 7).getValues().map(r => { r[0] = Utilities.formatDate(new Date(r[0]), "Asia/Jakarta", "dd/MM/yyyy"); return r; });
}
