Lompat ke konten Lompat ke sidebar Lompat ke footer

Source Code Administrasi Guru Menggunakan Blogger dan Database Spreadsheet

Manajemen data sekolah, rekap absensi, dan pengolahan nilai seringkali menjadi beban administratif yang menyita banyak waktu guru. Bayangkan jika semua proses tersebut bisa didigitalisasi secara gratis, terpusat, dan dapat diakses dari perangkat mana saja.

Pada artikel ini, kita akan membahas secara tuntas bagaimana membangun Sistem Administrasi Guru Digital (EdAdmin Pro) dengan memanfaatkan ekosistem gratis dari Google: Blogger, Google Apps Script, dan Google Spreadsheet.

Pengenalan Komponen Sistem (Tech Stack)

Aplikasi ini dirancang menggunakan arsitektur Headless modern. Artinya, bagian tampilan pengguna (Front-end) dan bagian pemrosesan data (Back-end) dipisahkan, namun saling berkomunikasi. Berikut adalah komponen utamanya:

  1. Blogger (Sebagai Front-End):
    Blogger adalah platform Content Management System (CMS) gratis dari Google. Dalam sistem ini, Blogger bukan digunakan untuk menulis artikel biasa, melainkan digunakan sebagai "Hosting Gratis" untuk menampung tampilan aplikasi (HTML, CSS Tailwind, dan JavaScript). Ini adalah bagian antarmuka (UI) tempat Anda mengklik tombol, melihat tabel nilai, dan mengisi absensi.
  2. Google Spreadsheet (Sebagai Database):
    Spreadsheet berfungsi sebagai tempat penyimpanan data (Database) dari aplikasi ini. Seluruh data siswa, mata pelajaran, riwayat absensi, daftar nilai, hingga agenda mengajar akan tersimpan rapi berlapis-lapis dalam sheet Excel virtual ini.
  3. Google Apps Script / GAS (Sebagai Back-End):
    GAS adalah bahasa pemrograman berbasis cloud dari Google. Disini GAS bertindak sebagai "Server" atau "Otak" penghubung (API). Saat Anda mengklik "Simpan Nilai" di Blogger, perintah itu dikirim ke GAS. Kemudian GAS akan mengolah data tersebut, mengecek duplikasi, dan menuliskannya secara otomatis ke dalam Google Spreadsheet.

Kelebihan Integrasi Blogger dengan Apps Script

  • 100% Gratis Selamanya: Anda tidak perlu menyewa layanan Hosting (CPanel) atau membeli Domain.
  • Zero Downtime (Anti-Down): Karena menggunakan infrastruktur server Google, aplikasi ini tidak akan pernah down atau error karena kelebihan beban kunjungan.
  • Sangat Aman: Data sekolah sepenuhnya milik Anda dan tersimpan secara privat di Google Drive akun Anda.
  • Real-time & Otomatis: Rekapitulasi nilai akhir (Leger) dan persentase kehadiran siswa dikalkulasi secara otomatis oleh sistem tanpa Anda perlu menghitung manual.
  • Mobile Friendly: Tampilan aplikasi (Blogger Frontend) sudah dioptimalkan agar nyaman saat diakses untuk mengisi absensi dan jurnal saat guru berada di dalam kelas langsung dari HP.

Langkah-Langkah Tutorial Instalasi

Mari kita mulai memasang aplikasi ini ke sekolah Anda. Proses ini hanya memakan waktu sekitar 10 menit jika diikuti dengan seksama.

LANGKAH 1: Setup Backend & Database (Google Apps Script)

  1. Buka Google Drive Anda, buat sebuah file Google Spreadsheet kosong. Beri nama file tersebut (misalnya: Database Administrasi Guru).
  2. Pada menu atas Spreadsheet, klik Ekstensi > pilih Apps Script. Tab baru akan terbuka.
  3. Hapus seluruh kode bawaan yang ada di halaman Apps Script tersebut.
  4. Salin seluruh kode backend.gs di bawah ini, lalu paste (tempel) ke dalam editor Apps Script.
📄 backend.gs (Google Apps Script)
/**
 * BACKEND SISTEM ADMINISTRASI GURU - EDADMIN PRO (FINAL RELEASE)
 * Logika Database Final dengan Proteksi Anti-Duplikat & Validasi
 */

const SS = SpreadsheetApp.getActiveSpreadsheet();
const TABLES = [
  { name: 'Config', headers: ['Key', 'Value'] },
  { name: 'DataSiswa', headers: ['ID', 'NISN', 'Nama Siswa', 'Kelas'] },
  { name: 'Mapel', headers: ['ID', 'Nama Mapel', 'Semester', 'Tahun Ajaran'] },
  { name: 'Jadwal', headers: ['ID', 'Hari', 'Jam', 'Kelas', 'Mapel'] },
  { name: 'Absensi', headers: ['Waktu', 'Kelas', 'Mapel', 'ID_Siswa', 'Nama Siswa', 'Status', 'Nama Guru', 'Bulan', 'Tahun'] },
  { name: 'Nilai', headers: ['Waktu', 'Jenis', 'Mapel', 'Kelas', 'ID_Siswa', 'Nama Siswa', 'Nilai', 'Nama Guru'] },
  { name: 'Agenda', headers: ['Tanggal', 'Jam', 'Kelas', 'Mapel', 'Materi', 'Status', 'Absen Siswa', 'Ket', 'Nama Guru'] },
  { name: 'SiswaBimbingan', headers: ['ID', 'Nama Siswa', 'Kelas'] },
  { name: 'BimbinganWali', headers: ['Tanggal', 'Nama Siswa', 'Kelas', 'Jenis', 'Kasus', 'Tindak Lanjut', 'Guru Wali'] }
];

// ==========================================
// 1. SETUP DATABASE (Jalankan sekali manual)
// ==========================================
function SETUP_DATABASE() {
  TABLES.forEach(table => {
    let sheet = SS.getSheetByName(table.name);
    if (!sheet) {
      sheet = SS.insertSheet(table.name);
      sheet.appendRow(table.headers);
      sheet.getRange(1, 1, 1, table.headers.length).setFontWeight("bold").setBackground("#2e7d32").setFontColor("white");
    }
  });
  return "Database Berhasil Disiapkan!";
}

// ==========================================
// 2. GET API (Mengirim Data ke Frontend)
// ==========================================
function doGet(e) {
  if (e.parameter.action === 'all') {
    let allData = {};
    TABLES.forEach(table => {
      let sheet = SS.getSheetByName(table.name);
      if (table.name === 'Config') {
        allData[table.name] = getConfigJSON(sheet);
      } else {
        allData[table.name] = sheet ? getSheetDataAsJSON(sheet) : [];
      }
    });

    return ContentService.createTextOutput(JSON.stringify({
      status: 'success',
      data: allData
    })).setMimeType(ContentService.MimeType.JSON);
  }
  return HtmlService.createHtmlOutput("API EdAdmin Pro Aktif.");
}

// ==========================================
// 3. POST API (Menerima Data dari Frontend)
// ==========================================
function doPost(e) {
  const lock = LockService.getScriptLock();
  try {
    lock.waitLock(15000); // 15 detik antrian aman
    let request = JSON.parse(e.postData.contents);
    let action = request.action;
    let payload = request.payload;
    
    if (action === 'saveConfig') handleSaveConfig(payload);
    else if (action === 'saveSiswa') insertRow('DataSiswa', payload);
    else if (action === 'saveSiswaBulk') handleSaveSiswaBulk(payload);
    else if (action === 'saveMapel') insertRow('Mapel', payload);
    else if (action === 'saveJadwal') insertRow('Jadwal', payload);
    else if (action === 'saveAgenda') insertRow('Agenda', payload);
    else if (action === 'saveBimbingan') insertRow('BimbinganWali', payload);
    else if (action === 'saveSiswaBimbingan') insertRow('SiswaBimbingan', payload);
    else if (action === 'saveAbsensi') handleAbsensi(payload);
    else if (action === 'savePenilaian') handlePenilaian(payload);
    else throw new Error("Aksi tidak dikenali");
    
    return ContentService.createTextOutput(JSON.stringify({status: 'success', message: 'Data Tersimpan'}))
      .setMimeType(ContentService.MimeType.JSON);
      
  } catch (error) {
    return ContentService.createTextOutput(JSON.stringify({status: 'error', message: error.toString()}))
      .setMimeType(ContentService.MimeType.JSON);
  } finally {
    lock.releaseLock();
  }
}

// ==========================================
// 4. FUNGSI LOGIKA DATA
// ==========================================
function getSheetDataAsJSON(sheet) {
  let data = sheet.getDataRange().getValues();
  if (data.length <= 1) return [];
  let headers = data[0];
  let result = [];
  for (let i = 1; i < data.length; i++) {
    let obj = {};
    for (let j = 0; j < headers.length; j++) {
      let cellData = data[i][j];
      if (cellData instanceof Date) {
         let mm = cellData.getMonth() + 1;
         let dd = cellData.getDate();
         // FIX: Menggunakan metode padding aman untuk mengatasi masalah '010' di bulan Oktober
         cellData = [cellData.getFullYear(), ('0' + mm).slice(-2), ('0' + dd).slice(-2)].join('-');
      }
      obj[headers[j]] = cellData;
    }
    result.push(obj);
  }
  return result;
}

function getConfigJSON(sheet) {
  if (!sheet) return {};
  let data = sheet.getDataRange().getValues();
  let conf = {};
  for(let i=1; i obj[header] !== undefined ? obj[header] : "");
  sheet.appendRow(rowData);
}

// Proteksi agar import excel massal tidak ganda
function handleSaveSiswaBulk(payload) {
  let sheet = SS.getSheetByName('DataSiswa');
  let data = sheet.getDataRange().getValues();
  let existingNISN = new Set();
  
  // Asumsi header NISN ada di indeks 1 (sesuai struktur array TABLES)
  for(let i = 1; i < data.length; i++) {
      existingNISN.add(String(data[i][1]));
  }
  
  let rowsToAppend = [];
  let headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
  
  payload.forEach(obj => {
      if (!existingNISN.has(String(obj['NISN']))) {
          let rowData = headers.map(header => obj[header] !== undefined ? obj[header] : "");
          rowsToAppend.push(rowData);
          existingNISN.add(String(obj['NISN']));
      }
  });
  
  if (rowsToAppend.length > 0) {
      sheet.getRange(sheet.getLastRow() + 1, 1, rowsToAppend.length, headers.length).setValues(rowsToAppend);
  }
}

function handleAbsensi(payload) {
  let sheet = SS.getSheetByName('Absensi');
  let data = sheet.getDataRange().getValues();
  
  // FIX: Memecah string langsung tanpa mengubah ke Date object untuk menghindari bias zona waktu UTC
  let parts = payload.tanggal.split('-');
  let thn = parts[0];
  let bln = parts[1];
  
  payload.records.forEach(rec => {
    let rowIndex = -1;
    for (let i = 1; i < data.length; i++) {
      let tglData = data[i][0] instanceof Date ? 
         [data[i][0].getFullYear(), ('0'+(data[i][0].getMonth()+1)).slice(-2), ('0'+data[i][0].getDate()).slice(-2)].join('-') : 
         data[i][0];
      if (tglData === payload.tanggal && data[i][1] == payload.kelas && data[i][2] == payload.mapel && data[i][3] == rec.ID_Siswa) {
        rowIndex = i + 1; break;
      }
    }

    let rowData = [payload.tanggal, payload.kelas, payload.mapel, rec.ID_Siswa, rec.Nama_Siswa, rec.Status, payload.guru, bln, thn];

    if (rowIndex > -1) sheet.getRange(rowIndex, 1, 1, rowData.length).setValues([rowData]);
    else sheet.appendRow(rowData);
  });
}

function handlePenilaian(payload) {
  let sheet = SS.getSheetByName('Nilai');
  let data = sheet.getDataRange().getValues();
  let timeStr = new Date().toISOString().split('T')[0];

  payload.records.forEach(rec => {
    let rowIndex = -1;
    for (let i = 1; i < data.length; i++) {
      if (data[i][1] == payload.jenis && data[i][2] == payload.mapel && data[i][3] == payload.kelas && data[i][4] == rec.ID_Siswa) {
        rowIndex = i + 1; break;
      }
    }
    let rowData = [timeStr, payload.jenis, payload.mapel, payload.kelas, rec.ID_Siswa, rec.Nama_Siswa, rec.Nilai, payload.guru];
    if (rowIndex > -1) sheet.getRange(rowIndex, 1, 1, rowData.length).setValues([rowData]);
    else sheet.appendRow(rowData);
  });
}
  1. Pilih dropdown fungsi di bagian atas dari doGet menjadi SETUP_DATABASE, lalu klik tombol Jalankan (Run). (Beri otorisasi/izin jika diminta Google).
  2. Buka kembali file Spreadsheet Anda. Anda akan melihat banyak sheet baru (Config, DataSiswa, Nilai, dll) dibuat secara otomatis.
  3. Kembali ke tab Apps Script, klik tombol biru Terapkan (Deploy) di pojok kanan atas > Deployment baru (New Deployment).
  4. Pilih roda gigi ⚙️, centang Aplikasi Web.
  5. Atur konfigurasinya:
    - Deskripsi: Bebas
    - Jalankan sebagai: Saya / Me
    - Siapa yang memiliki akses: Siapa saja / Anyone (Ini sangat penting agar API tidak terblokir!)
  6. Klik Terapkan, lalu Salin URL Aplikasi Web (Web App URL) yang diberikan. Simpan URL ini, kita akan membutuhkannya di langkah berikutnya.

LANGKAH 2: Mengunduh Template Frontend XML

Tahap selanjutnya adalah mendapatkan file tema (Frontend) yang akan kita pasang ke dalam Blogger.

LANGKAH 3: Memasang Tema XML di Blogger

PENTING: Kami sangat menyarankan Anda membuat Blog Baru yang masih kosong di akun Blogger Anda khusus untuk aplikasi ini, agar tidak merusak tema blog utama Anda yang sudah ada.
  1. Login ke dashboard Blogger Anda.
  2. Buka menu Tema (Theme) di sidebar kiri.
  3. Klik ikon tanda panah bawah (▼) di sebelah tombol "Sesuaikan" (Customize), lalu pilih Pulihkan (Restore).
  4. Klik tombol Upload, lalu pilih file .xml yang baru saja Anda download di Langkah 2. Tunggu hingga proses upload selesai.

LANGKAH 4: Mengganti URL API (GAS_URL)

Agar tampilan Blogger yang baru Anda pasang bisa berkomunikasi dengan Spreadsheet Database Anda, Anda harus memasukkan URL API yang Anda dapatkan di Langkah 1.

  1. Di menu Tema Blogger, klik tanda panah bawah (▼), lalu pilih Edit HTML.
  2. Tekan tombol Ctrl + F (atau Cmd+F) di dalam area kode, lalu cari tulisan: GAS_URL
  3. Anda akan menemukan baris kode seperti ini:
    const GAS_URL = "PASTEKAN LINK APPSCRIPT ANDA DISINI";
  4. Hapus teks "PASTEKAN LINK APPSCRIPT ANDA DISINI" dan ganti dengan URL Web App yang Anda salin dari Google Apps Script di Langkah 1.
    *Pastikan URL masih berada di dalam tanda kutip dua " ".
  5. Klik ikon Simpan (Save) di pojok kanan atas editor.
Selamat! Aplikasi Administrasi Guru Anda sudah siap digunakan. Buka URL blog Anda, lalu pergi ke menu Pengaturan di dalam aplikasi untuk mengisi nama guru, sekolah, dan menyisipkan logo dinas/sekolah untuk laporan PDF Anda.