Transformasi Digital Pendidikan Karakter dengan Aplikasi Pelaporan G7Kaih

Pendidikan karakter adalah jantung dari proses belajar mengajar di satuan pendidikan. Kita sepakat bahwa menanamkan kebiasaan baik—seperti bangun pagi, rajin beribadah, hingga gemar belajar—sama pentingnya dengan pencapaian akademik siswa. Namun, kita juga tahu bahwa memantau dan merekap kebiasaan ratusan siswa setiap harinya secara manual adalah tugas yang sangat menyita waktu dan energi. Kertas laporan yang menumpuk, data yang terselip, hingga proses rekapitulasi akhir bulan seringkali menjadi beban tambahan di tengah kesibukan mengajar.

Aplikasi ini bukanlah sistem rumit yang membutuhkan biaya server mahal, sewa hosting maupun beli domain atau keahlian coding tingkat dewa. Kita akan membangunnya dengan memanfaatkan ekosistem yang sudah sangat akrab dengan kita, yaitu Google Workspace (Google Sheets dan Google Apps Script) yang sudah bisa kita manfaatkan secara gratis dan di bantu oleh kecerdasan artifisial yaitu Gemini.

Mengapa Aplikasi G7Kaih Ini Sangat Membantu?

  • Bebas Kertas & Otomatis: Siswa dapat mengisi laporan dari HP mereka sendiri di rumah. Data tersebut akan otomatis masuk dan tersusun rapi di Google Sheets kita.
  • Ramah Pengguna (Mobile-First): Tampilan aplikasi dirancang khusus untuk layar HP. Tombolnya besar, mudah ditekan, dan anti-zoom, sehingga sangat nyaman digunakan oleh siswa, guru, maupun orang tua.
  • Pemantauan Cepat (Satu Klik): Bapak/Ibu tidak perlu lagi merekap satu per satu. Dengan fitur dashboard guru, Anda bisa memfilter laporan berdasarkan kelas atau tanggal, memberikan catatan, dan menyetujui laporan secara massal.
  • Keterlibatan Orang Tua: Wali murid memiliki portal login sendiri untuk memantau langsung perkembangan anak mereka dari minggu ke minggu, menciptakan sinergi yang transparan antara sekolah dan rumah.

Apa yang Akan Kita Lakukan Hari Ini? Dalam sesi tutorial ini, kita akan belajar langkah demi langkah mulai dari:

  1. Menyiapkan database gratis menggunakan Google Sheets.
  2. Menyematkan kode backend (Google Apps Script) dan antarmuka (Frontend HTML/CSS) yang sudah disediakan.
  3. Melakukan Deployment agar aplikasi bisa langsung diakses melalui link (URL) di HP siswa dan orang tua.

Bapak/Ibu tidak perlu khawatir jika belum pernah membuat aplikasi sebelumnya. Cukup ikuti langkah-langkahnya, praktikkan secara bertahap, dan dalam waktu singkat, satuan pendidikan kota Semarang akan memiliki sistem pelaporan karakter yang modern, efisien, dan mandiri.

Mari kita mulai langkah pertama menuju digitalisasi pendidikan karakter yang lebih baik! kita mulai yuk…!

Langkah 0: Meminta Gemini Membuatkan Aplikasi

Prompt :

“Bertindaklah sebagai Ahli Web Developer dan spesialis Google Apps Script. Buatkan kode untuk aplikasi Web App berbasis Google Apps Script (HTML, CSS, JavaScript) dengan database Google Sheets yang dirancang khusus untuk layar HP (Mobile-First Responsif) menggunakan Bootstrap 5.

Nama Aplikasi: Aplikasi Pembiasaan Baik SD Negeri Kota Semarang

DESAIN ANTARMUKA:

Aplikasi memiliki 3 Tab Utama menggunakan Navigasi Bawah (Bottom Navigation) ala aplikasi mobile asli. Gunakan ukuran font besar (minimal 16px untuk input/select agar anti-zoom di HP), tombol tebal (padding longgar), loading spinner (overlay), dan layout bersih.

SISTEM DATABASE (Google Sheets) WAJIB SESUAI URUTAN KOLOM BERIKUT:

1. Sheet “Data Siswa” (6 Kolom): [NIS, Nama Siswa, Kelas, Jenis Kelamin, Nama Bapak, Password]

2. Sheet “Guru” (2 Kolom): [Nama Guru, Password]

3. Sheet “Laporan” (16 Kolom): [Timestamp, Tanggal, NIS, Nama Siswa, Kelas, Jenis Kelamin, Bangun Pagi, Taat Beribadah, Berolahraga, Makan Sehat Bergizi, Gemar Belajar, Aktif Bermasyarakat, Tidur Cepat, Keterangan, Status, Catatan Guru]

FITUR PADA TIAP HALAMAN (TAB):

1. HALAMAN SISWA (Tab 1 – Default)

– Dropdown “Pilih Kelas” (data unik dari sheet Data Siswa).

– Dropdown “Pilih Nama Anda” (otomatis muncul sesuai kelas yang dipilih, disabled sebelum kelas dipilih).

– 7 Checkbox indikator kebiasaan (Bangun Pagi, Taat Beribadah, Berolahraga, Makan Sehat Bergizi, Gemar Belajar, Aktif Bermasyarakat, Tidur Cepat). Value jika dicentang = “Dilakukan”.

– Textarea untuk Keterangan Opsional.

– Tombol Simpan Laporan. Kolom ‘Tanggal’ di-generate format yyyy-MM-dd di sisi klien. Kolom Jenis Kelamin harus ditarik dari cache data siswa. Kolom Status default “Menunggu”, Catatan Guru default “-“.

2. HALAMAN GURU (Tab 2)

– Form Login Guru (mencocokkan Nama & Password di sheet Guru).

– Jika sukses, tampilkan Dashboard dengan Navigasi “Pills” untuk 2 Sub-Menu: “Daftar Harian” dan “Rekap Bulanan”.

– [Sub-Menu: Daftar Harian] Berisi tombol “✓ Setuju Semua” (ubah status Menunggu jadi Selesai). Terdapat filter Kelas dan Tanggal real-time. Tiap kartu laporan menampilkan identitas (Nama, Kelas, Jenis Kelamin), badge warna-warni untuk status 7 kebiasaan, serta form inline untuk ubah Status, input Catatan Guru, dan tombol “Simpan”.

– [Sub-Menu: Rekap Bulanan] Berisi dropdown Kelas, input type=”month” (Bulan), dan tombol Proses.

– [Logika Rekap Bulanan]: Tarik data siswa dari sheet Data Siswa berdasarkan kelas untuk mengetahui total laki-laki dan perempuan. Hitung centang kebiasaan di bulan terkait. Threshold: >=18 (Terbiasa/Sudah Terbiasa), 10-17 (Cukup Terbiasa), <10 (Belum Terbiasa).

– [Output Rekap Bulanan] Hasilkan 2 Tabel bergaris (border 1px hitam):

  a. Tabel “Rekap Jenis Kelamin”: Menampilkan baris Perempuan, Laki-laki, dan Total. Kolom: Jenis Kelamin, Jml Siswa, Terbiasa, Cukup Terbiasa, Belum Terbiasa.

  b. Tabel “Rekap Pembiasaan”: Menampilkan 7 baris kebiasaan. Kolom untuk tiap status (Sudah, Cukup, Belum) dibagi 2: “Jumlah Anak” dan “Persentase (%)”. Beri warna hijau untuk Sudah Terbiasa, Kuning untuk Cukup, Merah untuk Belum.

3. HALAMAN ORANG TUA (Tab 3)

– Form Login (NIS Anak & Password dari sheet Data Siswa).

– Jika sukses, tampilkan nama & kelas anak, dan riwayat laporan milik NIS tersebut (urut dari terbaru).

– Terdapat Filter Per Minggu menggunakan Input type=”week” (Format YYYY-Www).

– Tiap riwayat laporan menampilkan badge kebiasaan, teks keterangan anak, dan kotak alert khusus untuk Respons/Catatan Guru.

INSTRUKSI KODE:

Berikan struktur kode utuh, bersih, dan menggunakan try-catch untuk anti-error:

1. File ‘Kode.gs’ (Seluruh fungsi backend GAS, pastikan format tanggal aman dari bug timezone GAS).

2. File ‘Index.html’ (Satu file utuh gabungan HTML, CSS, dan JS Frontend).

akan menghasilkan :

1. File Backend: Kode.gs

Kode ini menangani routing, integrasi database dengan Google Sheets, dan memvalidasi setiap proses menggunakan blok try-catch. Pastikan Anda telah membuat spreadsheet dengan nama Sheet yang sama persis (Data Siswa, Guru, Laporan).

Code :

JavaScript

// ==========================================

// 1. SETUP & ROUTING

// ==========================================

function doGet(e) {

  return HtmlService.createHtmlOutputFromFile(‘Index’)

    .setTitle(‘G7Kaih SMPN 33’)

    .addMetaTag(‘viewport’, ‘width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0’);

}

// Helper: Mendapatkan aktif spreadsheet

function getDB() {

  return SpreadsheetApp.getActiveSpreadsheet();

}

// Helper: Format tanggal untuk menghindari error JSON serialization

function formatDataDate(data) {

  return data.map(row =>

    row.map(cell => (cell instanceof Date) ? cell.toISOString() : cell)

  );

}

// ==========================================

// 2. FUNGSI UNTUK HALAMAN SISWA

// ==========================================

function getClasses() {

  try {

    const sheet = getDB().getSheetByName(“Data Siswa”);

    if (!sheet) return { error: “Sheet ‘Data Siswa’ tidak ditemukan” };

    const lastRow = sheet.getLastRow();

    if (lastRow < 2) return { success: true, data: [] };

    const data = sheet.getRange(2, 3, lastRow – 1, 1).getValues(); // Kolom Kelas

    const classes = […new Set(data.map(r => r[0]).filter(String))];

    return { success: true, data: classes.sort() };

  } catch (e) {

    return { error: e.message };

  }

}

function getStudentsByClass(kelas) {

  try {

    const sheet = getDB().getSheetByName(“Data Siswa”);

    const lastRow = sheet.getLastRow();

    if (lastRow < 2) return { success: true, data: [] };

    const data = sheet.getRange(2, 1, lastRow – 1, 3).getValues(); // NIS, Nama, Kelas

    const students = data

      .filter(r => r[2] === kelas)

      .map(r => ({ nis: r[0], nama: r[1] }));

    return { success: true, data: students };

  } catch (e) {

    return { error: e.message };

  }

}

function submitSiswaReport(payload) {

  try {

    const sheet = getDB().getSheetByName(“Laporan”);

    if (!sheet) return { error: “Sheet ‘Laporan’ tidak ditemukan” };

    const timestamp = new Date();

    // Kolom: Timestamp, Tanggal, NIS, Nama Siswa, Kelas, 7 Kebiasaan, Keterangan, Status, Catatan Guru

    const rowData = [

      timestamp,

      payload.tanggal, // Format: yyyy-MM-dd dari frontend

      payload.nis,

      payload.nama,

      payload.kelas,

      payload.h1, payload.h2, payload.h3, payload.h4, payload.h5, payload.h6, payload.h7,

      payload.keterangan || “”,

      “Menunggu”,

      “-“

    ];

    sheet.appendRow(rowData);

    return { success: true };

  } catch (e) {

    return { error: e.message };

  }

}

// ==========================================

// 3. FUNGSI UNTUK HALAMAN GURU

// ==========================================

function loginGuru(nama, password) {

  try {

    const sheet = getDB().getSheetByName(“Guru”);

    if (!sheet) return { error: “Sheet ‘Guru’ tidak ditemukan” };

    const lastRow = sheet.getLastRow();

    if (lastRow < 2) return { error: “Data Guru kosong” };

    const data = sheet.getRange(2, 1, lastRow – 1, 2).getValues();

    const isValid = data.some(r => r[0] == nama && r[1] == password);

    if (isValid) return { success: true };

    return { error: “Kredensial Guru tidak valid” };

  } catch (e) {

    return { error: e.message };

  }

}

function getLaporanGuru() {

  try {

    const sheet = getDB().getSheetByName(“Laporan”);

    const lastRow = sheet.getLastRow();

    if (lastRow < 2) return { success: true, data: [] };

    // Ambil data dan kembalikan dengan nomor baris (untuk mempermudah update)

    let rawData = sheet.getRange(2, 1, lastRow – 1, 15).getValues();

    rawData = formatDataDate(rawData);

    const reports = rawData.map((r, i) => ({

      rowIndex: i + 2,

      timestamp: r[0], tanggal: r[1], nis: r[2], nama: r[3], kelas: r[4],

      h1: r[5], h2: r[6], h3: r[7], h4: r[8], h5: r[9], h6: r[10], h7: r[11],

      keterangan: r[12], status: r[13], catatan: r[14]

    }));

    // Reverse agar data terbaru tampil di atas

    return { success: true, data: reports.reverse() };

  } catch (e) {

    return { error: e.message };

  }

}

function updateLaporanStatus(rowIndex, status, catatan) {

  try {

    const sheet = getDB().getSheetByName(“Laporan”);

    // Kolom 14 = Status, Kolom 15 = Catatan Guru

    sheet.getRange(rowIndex, 14).setValue(status);

    sheet.getRange(rowIndex, 15).setValue(catatan || “-“);

    return { success: true };

  } catch (e) {

    return { error: e.message };

  }

}

function approveAllLaporan() {

  try {

    const sheet = getDB().getSheetByName(“Laporan”);

    const lastRow = sheet.getLastRow();

    if (lastRow < 2) return { success: true, updated: 0 };

    const statusRange = sheet.getRange(2, 14, lastRow – 1, 1);

    const statuses = statusRange.getValues();

    let updatedCount = 0;

    const newStatuses = statuses.map(r => {

      if (r[0] === “Menunggu”) {

        updatedCount++;

        return [“Selesai”];

      }

      return [r[0]];

    });

    if (updatedCount > 0) {

      statusRange.setValues(newStatuses);

    }

    return { success: true, updated: updatedCount };

  } catch (e) {

    return { error: e.message };

  }

}

// ==========================================

// 4. FUNGSI UNTUK HALAMAN ORANG TUA

// ==========================================

function loginOrangTua(nis, password) {

  try {

    const sheet = getDB().getSheetByName(“Data Siswa”);

    if (!sheet) return { error: “Sheet ‘Data Siswa’ tidak ditemukan” };

    const lastRow = sheet.getLastRow();

    if (lastRow < 2) return { error: “Data Siswa kosong” };

    const data = sheet.getRange(2, 1, lastRow – 1, 5).getValues(); // [NIS, Nama, Kelas, Bapak, Password]

    const user = data.find(r => r[0] == nis && r[4] == password);

    if (user) {

      return { success: true, data: { nis: user[0], nama: user[1], kelas: user[2] } };

    }

    return { error: “NIS atau Password tidak valid” };

  } catch (e) {

    return { error: e.message };

  }

}

function getLaporanSiswa(nis) {

  try {

    const sheet = getDB().getSheetByName(“Laporan”);

    const lastRow = sheet.getLastRow();

    if (lastRow < 2) return { success: true, data: [] };

    let rawData = sheet.getRange(2, 1, lastRow – 1, 15).getValues();

    rawData = formatDataDate(rawData);

    const reports = rawData

      .filter(r => r[2] == nis)

      .map(r => ({

        tanggal: r[1],

        h1: r[5], h2: r[6], h3: r[7], h4: r[8], h5: r[9], h6: r[10], h7: r[11],

        keterangan: r[12], status: r[13], catatan: r[14]

      }));

    return { success: true, data: reports.reverse() };

  } catch (e) {

    return { error: e.message };

  }

}

2. File Frontend: Index.html

Kode HTML ini sudah merangkum elemen struktur, styling antarmuka, dan seluruh proses manipulasi client-side seperti login, filter real-time, hingga rendering badge dinamis secara efisien di perangkat seluler.

Code :

HTML

<!DOCTYPE html>

<html lang=”id”>

<head>

  <meta charset=”UTF-8″>

  <meta name=”viewport” content=”width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no”>

  <title>G7Kaih SMPN 33</title>

  <link href=”https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css” rel=”stylesheet”>

  <link href=”https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css” rel=”stylesheet”>

  <style>

    :root {

      –primary: #0d6efd;

      –nav-bg: #ffffff;

      –bg-color: #f8f9fa;

    }

    body {

      background-color: var(–bg-color);

      /* Padding bawah untuk menyisakan ruang bagi Bottom Nav */

      padding-bottom: 80px;

      font-family: -apple-system, BlinkMacSystemFont, “Segoe UI”, Roboto, Helvetica, Arial, sans-serif;

    }

    /* MOBILE-FIRST: Input & Select wajib besar */

    input, select, textarea {

      font-size: 16px !important;

      padding: 12px !important;

      border-radius: 8px !important;

    }

    /* Tombol empuk/tebal */

    .btn {

      padding: 12px 20px !important;

      border-radius: 8px !important;

      font-size: 16px !important;

      font-weight: 600 !important;

    }

    /* Navbar Atas */

    .top-navbar {

      background-color: var(–primary);

      color: white;

      padding: 15px;

      text-align: center;

      font-weight: bold;

      font-size: 18px;

      box-shadow: 0 2px 4px rgba(0,0,0,0.1);

      position: sticky;

      top: 0;

      z-index: 1020;

    }

    /* Bottom Navigation */

    .bottom-nav {

      position: fixed;

      bottom: 0;

      left: 0;

      width: 100%;

      background-color: var(–nav-bg);

      display: flex;

      justify-content: space-around;

      box-shadow: 0 -2px 10px rgba(0,0,0,0.05);

      z-index: 1030;

      padding: 8px 0;

    }

    .nav-item {

      text-align: center;

      color: #6c757d;

      flex-grow: 1;

      padding: 10px 0;

      cursor: pointer;

      text-decoration: none;

      font-size: 14px;

    }

    .nav-item i {

      display: block;

      font-size: 20px;

      margin-bottom: 4px;

    }

    .nav-item.active {

      color: var(–primary);

      font-weight: bold;

    }

    /* Checkbox & Card Styling */

    .form-check { margin-bottom: 15px; }

    .form-check-input { width: 1.5em; height: 1.5em; margin-top: 0; }

    .form-check-label { padding-left: 10px; padding-top: 3px; font-size: 16px; }

    .card-report {

      background: white;

      border-radius: 12px;

      padding: 15px;

      margin-bottom: 15px;

      box-shadow: 0 2px 5px rgba(0,0,0,0.05);

      border: 1px solid #dee2e6;

    }

    .badge-habit {

      margin: 2px;

      font-size: 12px;

      padding: 6px 10px;

    }

    .spinner-overlay {

      display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%;

      background: rgba(255,255,255,0.8); z-index: 9999;

      justify-content: center; align-items: center;

    }

  </style>

</head>

<body>

  <div class=”top-navbar”>

    Aplikasi G7Kaih SMPN 33

  </div>

  <div class=”spinner-overlay” id=”loading”>

    <div class=”spinner-border text-primary” role=”status”>

      <span class=”visually-hidden”>Loading…</span>

    </div>

  </div>

  <div class=”container mt-3″>

    <div id=”tab-siswa” class=”view-section”>

      <div class=”card card-report border-0 shadow-sm”>

        <h5 class=”mb-4″>Form Laporan Kebiasaan Baik</h5>

        <form id=”form-siswa”>

          <div class=”mb-3″>

            <label class=”form-label text-muted”>Pilih Kelas</label>

            <select class=”form-select” id=”siswa-kelas” required>

              <option value=”” disabled selected>Memuat kelas…</option>

            </select>

          </div>

          <div class=”mb-4″>

            <label class=”form-label text-muted”>Pilih Nama Anda</label>

            <select class=”form-select” id=”siswa-nama” required disabled>

              <option value=”” disabled selected>Pilih kelas terlebih dahulu</option>

            </select>

          </div>

          <p class=”fw-bold mb-3″>Centang kebiasaan yang Anda lakukan hari ini:</p>

          <div class=”form-check”>

            <input class=”form-check-input” type=”checkbox” id=”h1″ value=”Dilakukan”>

            <label class=”form-check-label” for=”h1″>1. Bangun pagi</label>

          </div>

          <div class=”form-check”>

            <input class=”form-check-input” type=”checkbox” id=”h2″ value=”Dilakukan”>

            <label class=”form-check-label” for=”h2″>2. Beribadah</label>

          </div>

          <div class=”form-check”>

            <input class=”form-check-input” type=”checkbox” id=”h3″ value=”Dilakukan”>

            <label class=”form-check-label” for=”h3″>3. Berolah Raga</label>

          </div>

          <div class=”form-check”>

            <input class=”form-check-input” type=”checkbox” id=”h4″ value=”Dilakukan”>

            <label class=”form-check-label” for=”h4″>4. Makan Sehat & Bergizi</label>

          </div>

          <div class=”form-check”>

            <input class=”form-check-input” type=”checkbox” id=”h5″ value=”Dilakukan”>

            <label class=”form-check-label” for=”h5″>5. Gemar Belajar</label>

          </div>

          <div class=”form-check”>

            <input class=”form-check-input” type=”checkbox” id=”h6″ value=”Dilakukan”>

            <label class=”form-check-label” for=”h6″>6. Bermasyarakat</label>

          </div>

          <div class=”form-check”>

            <input class=”form-check-input” type=”checkbox” id=”h7″ value=”Dilakukan”>

            <label class=”form-check-label” for=”h7″>7. Tidur Cepat</label>

          </div>

          <div class=”mb-4 mt-3″>

            <label class=”form-label text-muted”>Keterangan Tambahan (Opsional)</label>

            <textarea class=”form-control” id=”siswa-ket” rows=”3″ placeholder=”Tuliskan jika ada kegiatan spesifik…”></textarea>

          </div>

          <button type=”submit” class=”btn btn-primary w-100″>

            <i class=”fas fa-paper-plane me-2″></i> Simpan Laporan

          </button>

        </form>

      </div>

    </div>

    <div id=”tab-guru” class=”view-section d-none”>

      <div id=”login-guru-wrapper” class=”card card-report border-0 shadow-sm”>

        <h5 class=”mb-4″>Login Guru</h5>

        <form id=”form-login-guru”>

          <div class=”mb-3″>

            <input type=”text” id=”lg-nama” class=”form-control” placeholder=”Nama Guru” required>

          </div>

          <div class=”mb-4″>

            <input type=”password” id=”lg-pass” class=”form-control” placeholder=”Password” required>

          </div>

          <button type=”submit” class=”btn btn-primary w-100″>Masuk</button>

        </form>

      </div>

      <div id=”dash-guru-wrapper” class=”d-none”>

        <div class=”d-flex justify-content-between align-items-center mb-3″>

          <h5 class=”m-0″>Rekap Laporan</h5>

          <button class=”btn btn-success btn-sm px-3 py-2″ onclick=”approveAll()”>

            <i class=”fas fa-check-double”></i> Setuju Semua

          </button>

        </div>

        <div class=”card card-report bg-light border-0 mb-3″>

          <div class=”row g-2″>

            <div class=”col-12 col-md-4″>

              <select class=”form-select” id=”filter-kelas-guru”>

                <option value=””>Semua Kelas</option>

              </select>

            </div>

            <div class=”col-8 col-md-5″>

              <input type=”date” class=”form-control” id=”filter-tgl-guru”>

            </div>

            <div class=”col-4 col-md-3″>

              <button class=”btn btn-secondary w-100 h-100 py-0″ onclick=”resetFilterGuru()”>Reset</button>

            </div>

          </div>

        </div>

        <div id=”guru-list-laporan”></div>

      </div>

    </div>

    <div id=”tab-ortu” class=”view-section d-none”>

      <div id=”login-ortu-wrapper” class=”card card-report border-0 shadow-sm”>

        <h5 class=”mb-4″>Akses Wali Murid</h5>

        <form id=”form-login-ortu”>

          <div class=”mb-3″>

            <input type=”number” id=”lo-nis” class=”form-control” placeholder=”NIS Anak” required>

          </div>

          <div class=”mb-4″>

            <input type=”password” id=”lo-pass” class=”form-control” placeholder=”Password” required>

          </div>

          <button type=”submit” class=”btn btn-primary w-100″>Lihat Riwayat</button>

        </form>

      </div>

      <div id=”dash-ortu-wrapper” class=”d-none”>

        <div class=”alert alert-primary mb-3″>

          <h6 class=”m-0 fw-bold” id=”ortu-nama-anak”>Nama Anak</h6>

          <small id=”ortu-kelas-anak”>Kelas</small>

        </div>

        <div class=”card card-report bg-light border-0 mb-3″>

          <label class=”form-label text-muted small mb-1″>Filter Per Minggu</label>

          <input type=”week” class=”form-control mb-2″ id=”filter-minggu-ortu”>

          <button class=”btn btn-outline-secondary btn-sm w-100″ onclick=”resetFilterOrtu()”>Tampilkan Semua Minggu</button>

        </div>

        <div id=”ortu-list-laporan”></div>

      </div>

    </div>

  </div>

  <div class=”bottom-nav”>

    <div class=”nav-item active” onclick=”switchTab(‘siswa’, this)”>

      <i class=”fas fa-user-graduate”></i>Siswa

    </div>

    <div class=”nav-item” onclick=”switchTab(‘guru’, this)”>

      <i class=”fas fa-chalkboard-teacher”></i>Guru

    </div>

    <div class=”nav-item” onclick=”switchTab(‘ortu’, this)”>

      <i class=”fas fa-home”></i>Orang Tua

    </div>

  </div>

  <script>

    // Data Globals

    let studentsCache = [];

    let guruLaporanCache = [];

    let ortuLaporanCache = [];

    let currentUserOrtu = null;

    // Helpers

    const showLoader = () => document.getElementById(‘loading’).style.display = ‘flex’;

    const hideLoader = () => document.getElementById(‘loading’).style.display = ‘none’;

    // Format Tanggal untuk tampilan

    function formatDateView(dateStr) {

      if (!dateStr) return “-“;

      const d = new Date(dateStr);

      return `${d.getDate().toString().padStart(2,’0′)}/${(d.getMonth()+1).toString().padStart(2,’0′)}/${d.getFullYear()}`;

    }

    // Mendapatkan ISO Week String (YYYY-Www) dari tanggal untuk filter

    function getWeekString(dateStr) {

      const date = new Date(dateStr);

      date.setHours(0, 0, 0, 0);

      date.setDate(date.getDate() + 3 – (date.getDay() + 6) % 7);

      const week1 = new Date(date.getFullYear(), 0, 4);

      const weekNum = 1 + Math.round(((date.getTime() – week1.getTime()) / 86400000 – 3 + (week1.getDay() + 6) % 7) / 7);

      return `${date.getFullYear()}-W${weekNum.toString().padStart(2, ‘0’)}`;

    }

    // Logic Pindah Tab

    function switchTab(tabId, el) {

      document.querySelectorAll(‘.view-section’).forEach(sec => sec.classList.add(‘d-none’));

      document.getElementById(`tab-${tabId}`).classList.remove(‘d-none’);

      document.querySelectorAll(‘.nav-item’).forEach(nav => nav.classList.remove(‘active’));

      el.classList.add(‘active’);

      if (tabId === ‘guru’ && !document.getElementById(‘dash-guru-wrapper’).classList.contains(‘d-none’)) {

        loadGuruData(); // Refresh on enter

      }

    }

    // ================== INIT SISWA ==================

    window.onload = function() {

      showLoader();

      google.script.run.withSuccessHandler(res => {

        hideLoader();

        if(res.error) return alert(“Gagal memuat kelas: ” + res.error);

        const sel = document.getElementById(‘siswa-kelas’);

        const selG = document.getElementById(‘filter-kelas-guru’);

        sel.innerHTML = ‘<option value=”” disabled selected>– Pilih Kelas –</option>’;

        res.data.forEach(c => {

          sel.innerHTML += `<option value=”${c}”>${c}</option>`;

          selG.innerHTML += `<option value=”${c}”>${c}</option>`; // untuk filter guru

        });

      }).getClasses();

    };

    document.getElementById(‘siswa-kelas’).addEventListener(‘change’, function() {

      const kelas = this.value;

      const selSiswa = document.getElementById(‘siswa-nama’);

      selSiswa.innerHTML = ‘<option value=”” disabled selected>Memuat data…</option>’;

      selSiswa.disabled = true;

      showLoader();

      google.script.run.withSuccessHandler(res => {

        hideLoader();

        selSiswa.innerHTML = ‘<option value=”” disabled selected>– Pilih Nama –</option>’;

        if(res.error) return alert(“Gagal memuat siswa: ” + res.error);

        studentsCache = res.data;

        res.data.forEach(s => {

          selSiswa.innerHTML += `<option value=”${s.nis}”>${s.nama} (${s.nis})</option>`;

        });

        selSiswa.disabled = false;

      }).getStudentsByClass(kelas);

    });

    document.getElementById(‘form-siswa’).addEventListener(‘submit’, function(e) {

      e.preventDefault();

      const nis = document.getElementById(‘siswa-nama’).value;

      const nama = studentsCache.find(s => s.nis == nis).nama;

      const kelas = document.getElementById(‘siswa-kelas’).value;

      const tzoffset = (new Date()).getTimezoneOffset() * 60000;

      const todayIso = (new Date(Date.now() – tzoffset)).toISOString().split(‘T’)[0];

      const payload = {

        tanggal: todayIso, nis, nama, kelas,

        h1: document.getElementById(‘h1’).checked ? “Dilakukan” : “Tidak”,

        h2: document.getElementById(‘h2’).checked ? “Dilakukan” : “Tidak”,

        h3: document.getElementById(‘h3’).checked ? “Dilakukan” : “Tidak”,

        h4: document.getElementById(‘h4’).checked ? “Dilakukan” : “Tidak”,

        h5: document.getElementById(‘h5’).checked ? “Dilakukan” : “Tidak”,

        h6: document.getElementById(‘h6’).checked ? “Dilakukan” : “Tidak”,

        h7: document.getElementById(‘h7’).checked ? “Dilakukan” : “Tidak”,

        keterangan: document.getElementById(‘siswa-ket’).value

      };

      showLoader();

      google.script.run.withSuccessHandler(res => {

        hideLoader();

        if(res.error) return alert(“Error: ” + res.error);

        alert(“Laporan berhasil dikirim!”);

        document.getElementById(‘form-siswa’).reset();

        document.getElementById(‘siswa-nama’).disabled = true;

      }).submitSiswaReport(payload);

    });

    // ================== INIT GURU ==================

    document.getElementById(‘form-login-guru’).addEventListener(‘submit’, function(e) {

      e.preventDefault();

      showLoader();

      google.script.run.withSuccessHandler(res => {

        if(res.error) {

          hideLoader();

          return alert(res.error);

        }

        document.getElementById(‘login-guru-wrapper’).classList.add(‘d-none’);

        document.getElementById(‘dash-guru-wrapper’).classList.remove(‘d-none’);

        loadGuruData();

      }).loginGuru(document.getElementById(‘lg-nama’).value, document.getElementById(‘lg-pass’).value);

    });

    function loadGuruData() {

      showLoader();

      google.script.run.withSuccessHandler(res => {

        hideLoader();

        if(res.error) return alert(res.error);

        guruLaporanCache = res.data;

        renderGuruCards();

      }).getLaporanGuru();

    }

    function renderGuruCards() {

      const cList = document.getElementById(‘guru-list-laporan’);

      cList.innerHTML = “”;

      const fKelas = document.getElementById(‘filter-kelas-guru’).value;

      const fTgl = document.getElementById(‘filter-tgl-guru’).value; // format yyyy-mm-dd

      const filtered = guruLaporanCache.filter(r => {

        const tglStr = r.tanggal ? r.tanggal.split(‘T’)[0] : “”;

        if (fKelas && r.kelas !== fKelas) return false;

        if (fTgl && tglStr !== fTgl) return false;

        return true;

      });

      if (filtered.length === 0) {

        cList.innerHTML = `<div class=”alert alert-secondary text-center”>Belum ada laporan untuk filter ini.</div>`;

        return;

      }

      filtered.forEach(r => {

        const badges = [r.h1, r.h2, r.h3, r.h4, r.h5, r.h6, r.h7]

          .map((v, i) => `<span class=”badge badge-habit ${v === ‘Dilakukan’ ? ‘bg-success’ : ‘bg-light text-muted border’}”>H${i+1}</span>`)

          .join(‘ ‘);

        const bgStatus = r.status === “Selesai” ? “bg-success text-white” : (r.status === “Diproses” ? “bg-warning” : “bg-secondary text-white”);

        cList.innerHTML += `

          <div class=”card card-report”>

            <div class=”d-flex justify-content-between mb-2″>

              <span class=”fw-bold”>${r.nama} <small class=”text-muted”>(${r.kelas})</small></span>

              <span class=”badge ${bgStatus}”>${r.status || ‘Menunggu’}</span>

            </div>

            <div class=”small text-muted mb-2″><i class=”far fa-calendar-alt”></i> ${formatDateView(r.tanggal)}</div>

            <div class=”mb-2″>${badges}</div>

            <div class=”small mb-3″><strong>Ket:</strong> ${r.keterangan || “-“}</div>

            <hr class=”mt-0 mb-2″>

            <div class=”row g-2″>

              <div class=”col-12 col-md-4″>

                <select class=”form-select form-select-sm” id=”g-stat-${r.rowIndex}”>

                  <option value=”Menunggu” ${r.status===’Menunggu’?’selected’:”}>Menunggu</option>

                  <option value=”Diproses” ${r.status===’Diproses’?’selected’:”}>Diproses</option>

                  <option value=”Selesai” ${r.status===’Selesai’?’selected’:”}>Selesai</option>

                </select>

              </div>

              <div class=”col-8 col-md-5″>

                <input type=”text” class=”form-control form-control-sm” id=”g-cat-${r.rowIndex}” value=”${r.catatan === ‘-‘ ? ” : r.catatan}” placeholder=”Catatan Guru”>

              </div>

              <div class=”col-4 col-md-3″>

                <button class=”btn btn-primary btn-sm w-100 h-100 py-0″ onclick=”updateLaporan(${r.rowIndex})”>Perbarui</button>

              </div>

            </div>

          </div>

        `;

      });

    }

    document.getElementById(‘filter-kelas-guru’).addEventListener(‘change’, renderGuruCards);

    document.getElementById(‘filter-tgl-guru’).addEventListener(‘change’, renderGuruCards);

    function resetFilterGuru() {

      document.getElementById(‘filter-kelas-guru’).value = “”;

      document.getElementById(‘filter-tgl-guru’).value = “”;

      renderGuruCards();

    }

    function updateLaporan(rowIndex) {

      const status = document.getElementById(`g-stat-${rowIndex}`).value;

      const catatan = document.getElementById(`g-cat-${rowIndex}`).value;

      showLoader();

      google.script.run.withSuccessHandler(res => {

        if(res.error) { hideLoader(); return alert(“Gagal update: ” + res.error); }

        loadGuruData(); // reload all

      }).updateLaporanStatus(rowIndex, status, catatan);

    }

    function approveAll() {

      if(!confirm(“Yakin menyetujui (Selesai) SEMUA laporan berstatus Menunggu?”)) return;

      showLoader();

      google.script.run.withSuccessHandler(res => {

        if(res.error) { hideLoader(); return alert(“Gagal: ” + res.error); }

        alert(`Berhasil memperbarui ${res.updated} laporan!`);

        loadGuruData();

      }).approveAllLaporan();

    }

    // ================== INIT ORANG TUA ==================

    document.getElementById(‘form-login-ortu’).addEventListener(‘submit’, function(e) {

      e.preventDefault();

      showLoader();

      const nis = document.getElementById(‘lo-nis’).value;

      google.script.run.withSuccessHandler(res => {

        if(res.error) { hideLoader(); return alert(res.error); }

        currentUserOrtu = res.data;

        document.getElementById(‘ortu-nama-anak’).innerText = res.data.nama;

        document.getElementById(‘ortu-kelas-anak’).innerText = `Kelas: ${res.data.kelas} | NIS: ${res.data.nis}`;

        document.getElementById(‘login-ortu-wrapper’).classList.add(‘d-none’);

        document.getElementById(‘dash-ortu-wrapper’).classList.remove(‘d-none’);

        loadOrtuData();

      }).loginOrangTua(nis, document.getElementById(‘lo-pass’).value);

    });

    function loadOrtuData() {

      google.script.run.withSuccessHandler(res => {

        hideLoader();

        if(res.error) return alert(res.error);

        ortuLaporanCache = res.data;

        renderOrtuCards();

      }).getLaporanSiswa(currentUserOrtu.nis);

    }

    function renderOrtuCards() {

      const cList = document.getElementById(‘ortu-list-laporan’);

      cList.innerHTML = “”;

      const fMinggu = document.getElementById(‘filter-minggu-ortu’).value; // format YYYY-Www

      const filtered = ortuLaporanCache.filter(r => {

        if (!fMinggu) return true;

        return getWeekString(r.tanggal) === fMinggu;

      });

      if (filtered.length === 0) {

        cList.innerHTML = `<div class=”alert alert-secondary text-center”>Belum ada riwayat laporan untuk periode ini.</div>`;

        return;

      }

      filtered.forEach(r => {

        const badges = [r.h1, r.h2, r.h3, r.h4, r.h5, r.h6, r.h7]

          .map((v, i) => `<span class=”badge badge-habit ${v === ‘Dilakukan’ ? ‘bg-success’ : ‘bg-light text-muted border’}”>H${i+1}</span>`)

          .join(‘ ‘);

        const bgStatus = r.status === “Selesai” ? “bg-success text-white” : (r.status === “Diproses” ? “bg-warning” : “bg-secondary text-white”);

        cList.innerHTML += `

          <div class=”card card-report”>

            <div class=”d-flex justify-content-between mb-2″>

              <span class=”fw-bold text-primary”><i class=”far fa-calendar-check”></i> ${formatDateView(r.tanggal)}</span>

              <span class=”badge ${bgStatus}”>${r.status || ‘Menunggu’}</span>

            </div>

            <div class=”mb-2″>${badges}</div>

            <div class=”small mb-3″><strong>Keterangan Anak:</strong> ${r.keterangan || “-“}</div>

            <div class=”alert alert-success m-0 p-2 small”>

              <i class=”fas fa-comment-dots text-success me-2″></i><strong>Catatan Guru:</strong><br>

              ${r.catatan && r.catatan !== ‘-‘ ? r.catatan : ‘<i>Belum ada tanggapan</i>’}

            </div>

          </div>

        `;

      });

    }

    document.getElementById(‘filter-minggu-ortu’).addEventListener(‘change’, renderOrtuCards);

    function resetFilterOrtu() {

      document.getElementById(‘filter-minggu-ortu’).value = “”;

      renderOrtuCards();

    }

  </script>

</body>

</html>

Selanjutnya mulai

Langkah 1: Menyiapkan Database di Google Sheets

Google Sheets akan bertindak sebagai “otak penyimpan” dari aplikasi kita. Semua data siswa, akun login, hingga laporan harian akan bermuara dan tersimpan dengan rapi di sini. Sangat penting bagi kita untuk menamai setiap bagian dengan tepat, karena kode aplikasi nanti akan mencari data berdasarkan nama-nama tersebut.

Mari kita ikuti panduannya langkah demi langkah:

A. Membuat File Google Sheets Baru

  1. Buka browser di laptop/komputer Anda, lalu masuk ke Google Drive (drive.google.com) menggunakan akun Google/Belajar.id Bapak/Ibu.
  2. Klik tombol + Baru (atau + New) di pojok kiri atas.
  3. Pilih Google Spreadsheet > Spreadsheet kosong.
  4. Di pojok kiri atas (tempat yang bertuliskan “Spreadsheet tanpa judul”), klik dan ganti namanya menjadi: Database G7Kaih

B. Membuat 3 Lembar Kerja (Sheet) Utama

Secara otomatis, file Bapak/Ibu hanya akan memiliki satu sheet (lembar) di bagian bawah yang bernama “Sheet1”. Kita perlu mengubah namanya dan menambahkan dua sheet lagi sehingga totalnya ada 3.

PENTING: Pastikan ejaan, spasi, dan huruf besar/kecilnya sama persis seperti panduan di bawah ini agar aplikasi tidak error.

  1. Klik kanan pada tulisan “Sheet1” di bagian kiri bawah layar, pilih Ganti nama, lalu ketik: Data Siswa (tekan Enter).
  2. Klik tanda + (Tambahkan sheet) di pojok kiri bawah untuk membuat sheet baru. Ganti namanya menjadi: Guru.
  3. Klik tanda + sekali lagi, lalu ganti nama sheet ketiga tersebut menjadi: Laporan.

C. Membuat Kolom (Header) di Masing-masing Sheet

Sekarang, mari kita isi baris pertama pada masing-masing sheet sebagai “Judul Kolom” (Header).

1. Buka sheet “Data Siswa”, lalu ketik judul berikut secara berurutan di Baris 1 (dari kolom A sampai E):

  • Kolom A1: NIS
  • Kolom B1: Nama Siswa
  • Kolom C1: Kelas
  • Kolom D1: Nama Bapak
  • Kolom E1: Password .

2. Buka sheet “Guru”, lalu ketik judul berikut di Baris 1:

  • Kolom A1: Nama Guru
  • Kolom B1: Password (Silakan isi baris kedua dan seterusnya dengan nama dan password rekan-rekan guru yang akan menggunakan aplikasi ini. Contoh: Nama Guru : Nurul Password :  guru123).

3. Buka sheet “Laporan”, lalu ketik judul berikut di Baris 1 (dari kolom A sampai O). Sheet ini akan berisi banyak kolom:

  • Kolom A1: Timestamp
  • Kolom B1: Tanggal
  • Kolom C1: NIS
  • Kolom D1: Nama Siswa
  • Kolom E1: Kelas
  • Kolom F1: Bangun pagi
  • Kolom G1: Beribadah
  • Kolom H1: Berolah Raga
  • Kolom I1: Makan Sehat dan Bergizi
  • Kolom J1: Gemar Belajar
  • Kolom K1: Bermasyarakat
  • Kolom L1: Tidur Cepat
  • Kolom M1: Keterangan
  • Kolom N1: Status
  • Kolom O1: Catatan Guru

💡 Tips Tambahan (Membekukan Baris Header): Agar judul kolom (Baris 1) tidak ikut tergulung (scroll) saat data nantinya sudah ribuan, kita bisa “membekukan” baris tersebut. Caranya: Klik Baris 1 (angka 1 di sebelah kiri) agar tersorot > Klik menu Tampilan di atas > pilih Bekukan > klik 1 baris. Ulangi langkah ini untuk ketiga sheet.

Selamat! Fondasi database aplikasi G7Kaih Bapak/Ibu sudah siap sempurna.

File contoh di : https://docs.google.com/spreadsheets/d/1y5xJkVPmPU6_bGBsViG0TkkI8ls8ZweGOiewKDH77HU/edit?usp=sharing

Langkah 2: Memasukkan Kode Program (Google Apps Script)

Setelah database kita siap, sekarang saatnya kita memasukkan “mesin” dan “tampilan” aplikasi menggunakan Google Apps Script. Di sini kita akan membagi program menjadi dua bagian: Kode.gs (sebagai mesin belakang yang mengatur data) dan Index.html (sebagai wajah aplikasi yang akan dilihat siswa di HP).

Mari kita ikuti langkah-langkah di bawah ini:

A. Membuka Jendela Google Apps Script

  1. Pastikan Bapak/Ibu masih membuka file Google Sheets “Database G7Kaih” yang dibuat pada Langkah 1.
  2. Pada menu bagian atas, klik menu Ekstensi (atau Extensions).
  3. Pilih dan klik Apps Script.
  4. Sebuah tab baru akan terbuka di browser Anda. Ini adalah halaman editor tempat kita menaruh kode aplikasi.
  5. Di pojok kiri atas halaman tersebut, ubah tulisan “Proyek tanpa judul” menjadi: Aplikasi G7Kaih.

B. Mengisi File Kode.gs (Mesin Aplikasi)

Secara default, Google sudah menyiapkan satu file bernama Kode.gs yang berisi beberapa baris kode kosong (function myFunction() { … }).

  1. Hapus seluruh kode bawaan yang ada di dalam file Kode.gs tersebut hingga bersih (kosong sama sekali).
  2. Ambil seluruh kode dari bagian 1. File Backend: Kode.gs – Hasil dari hemini
  3. Copy (Salin) seluruh kode tersebut, lalu Paste (Tempel) ke dalam file Kode.gs yang kosong tadi.

C. Membuat dan Mengisi File Index.html (Tampilan Aplikasi)

Sekarang kita perlu membuat file kedua untuk menampung desain tampilan aplikasi agar responsif di layar HP.

  1. Perhatikan kolom sebelah kiri pada layar Apps Script (di bawah tulisan “File”). Ada tanda plus (+) di samping tulisan “File”.
  2. Klik tanda plus (+) tersebut, lalu pilih HTML.
  3. Kotak kecil akan muncul meminta nama file. Ketik nama: Index (Cukup ketik Index saja dengan huruf ‘I’ besar, tidak perlu menambah tulisan .html karena sistem akan membuatnya otomatis). Tekan Enter.
  4. Sekarang Bapak/Ibu akan melihat file baru bernama Index.html telah terbentuk.
  5. Sama seperti sebelumnya, hapus semua kode bawaan yang ada di dalam file Index.html tersebut sampai bersih.
  6. Ambil seluruh kode dari bagian 2. File Frontend: Index.html (yang berisi kode HTML, Bootstrap, dan JavaScript Hasil dari gemini).
  7. Copy (Salin) seluruh kode tersebut, lalu Paste (Tempel) ke dalam file Index.html yang kosong.

D. Menyimpan Seluruh Kode

Ini adalah bagian krusial agar kode yang sudah kita tempel tidak hilang.

  1. Perhatikan ikon-ikon di bagian atas editor kode.
  2. Klik ikon Cakram Flopi / Save (ikon berbentuk disket hitam) untuk menyimpan proyek Anda. Alternatifnya, Bapak/Ibu bisa menekan tombol Ctrl + S di keyboard (atau Cmd + S jika menggunakan Mac).
  3. Pastikan tanda titik oranye di samping nama file Kode.gs dan Index.html sudah hilang, yang menandakan bahwa semua file telah sukses tersimpan.

⚠️ Catatan Penting untuk Diperiksa Kembali:

  • Pastikan nama file HTML Anda adalah Index (menggunakan huruf ‘I’ kapital). Jika ditulis index (huruf kecil), mesin di Kode.gs tidak akan bisa memanggil tampilannya dan aplikasi akan memunculkan pesan error.
  • Jangan mengubah satu karakter pun dari kode yang ditempel kecuali Bapak/Ibu sudah memahami fungsinya, karena salah menghapus satu tanda koma atau tanda kurung saja bisa membuat aplikasi macet.

Luar biasa! Proses pengodean sudah selesai kita lakukan. Langkah terakhir adalah mengaktifkan aplikasi ini agar bisa menghasilkan link (URL) resmi yang dapat dibuka dari HP siapa saja.

Mari kita lanjut ke

Berikut adalah draf untuk Langkah 3: Meluncurkan Aplikasi (Deployment). Tahap ini sangat penting karena kita akan mengubah barisan kode tadi menjadi sebuah link (URL) yang bisa diakses layaknya aplikasi web pada umumnya.

Langkah 3: Meluncurkan Aplikasi (Deployment)

Selamat, Bapak/Ibu Guru! Kita sudah sampai di langkah terakhir. Ibarat memasak, bahan-bahan dan resepnya sudah kita olah, dan sekarang saatnya kita “menghidangkan” aplikasi ini agar bisa digunakan oleh siswa, guru, dan orang tua melalui smartphone mereka.

Proses ini disebut Deployment (Peluncuran). Mari kita ikuti langkah-langkahnya:

A. Memulai Proses Deployment

  1. Di halaman editor Google Apps Script yang masih terbuka, perhatikan pojok kanan atas layar.
  2. Klik tombol berwarna biru bertuliskan Terapkan (atau Deploy).
  3. Dari menu kecil yang muncul, pilih Deployment baru (atau New deployment).

B. Mengatur Setelan Aplikasi Web

Sebuah kotak dialog bernama “Deployment baru” akan muncul. Di sini kita akan mengatur siapa saja yang boleh mengakses aplikasi kita.

  1. Di sebelah tulisan “Pilih jenis” (Select type), klik ikon roda gigi ⚙️, lalu centang Aplikasi web (Web app).
  1. Pada kolom Deskripsi (Description), Bapak/Ibu bisa mengetik: Versi 1 – G7Kaih.
  2. Di bagian Jalankan sebagai (Execute as), pastikan pilihannya adalah Saya (Me). (Ini sangat krusial agar siswa dan orang tua tidak perlu repot-repot login atau meminta izin akses ke Google Sheet kita).
  3. Di bagian Siapa yang memiliki akses (Who has access), klik kotak pilihannya dan ubah menjadi Siapa saja (Anyone).
  4. Jika sudah sesuai, klik tombol biru Terapkan (Deploy) di pojok kanan bawah kotak dialog.

C. Memberikan Izin Akses (Langkah Paling Penting)

Karena aplikasi ini akan membaca dan menulis data ke Google Sheets milik Bapak/Ibu, Google akan meminta konfirmasi keamanan. Jangan panik jika muncul peringatan warna merah/kuning, itu adalah prosedur standar Google.

  1. Kotak dialog akan memunculkan tulisan “Otorisasi diperlukan”. Klik tombol Berikan akses (Authorize access).
  1. Sebuah jendela baru akan muncul. Klik Akun Google/Belajar.id Bapak/Ibu yang sedang digunakan.
  2. Google mungkin akan menampilkan layar peringatan bertuliskan “Google belum memverifikasi aplikasi ini”. Di layar ini, klik tulisan kecil di bagian kiri bawah: Lanjutan (Advanced).
  3. Setelah itu, akan muncul teks tambahan di bawahnya. Klik tulisan: Buka Aplikasi G7Kaih SMPN 33 (tidak aman) / Go to Aplikasi G7Kaih (unsafe).
  4. Terakhir, gulir ke bawah dan klik tombol Izinkan (Allow).

D. Mendapatkan Link (URL) Aplikasi

  1. Setelah Bapak/Ibu mengklik “Izinkan”, layar akan memproses sebentar dan memunculkan tulisan Deployment berhasil diperbarui.
  2. Di bagian bawah kotak dialog tersebut, terdapat tulisan URL Aplikasi web beserta tautan panjang (link).
  3. Klik tombol Salin (Copy) yang ada di bawah link tersebut.
  4. Klik Selesai (Done).

🎉 Selamat! Aplikasi G7Kaih Resmi Berjalan! 🎉

Link yang Bapak/Ibu salin tadi adalah link sakti aplikasi kita. Cobalah buka tab baru di browser laptop atau kirim link tersebut ke WhatsApp Bapak/Ibu sendiri, lalu buka melalui layar HP.

Bapak/Ibu akan melihat tampilan aplikasi G7Kaih yang sudah siap digunakan. Siswa kini bisa memilih nama mereka, mencentang kebiasaan baik, dan menekan tombol simpan, di mana datanya akan langsung mengalir secara real-time ke file Google Sheets yang sudah kita buat di Langkah 1.

Tips Pasca-Peluncuran: Link asli dari Google Apps Script biasanya sangat panjang dan sulit dihafal (contoh: https://script.google.com/macros/s/AKfycbxWOVNNbuF0d0AirNu7iqtyLY27T_UlK9ZsKyV1suL7iQdC3JO7hvr_4MwnVCXhwhKRXQ/exec). Agar terlihat lebih profesional dan mudah dibagikan ke grup WhatsApp paguyuban wali murid atau siswa, Bapak/Ibu bisa memperpendek link tersebut menggunakan layanan gratis seperti bit.ly atau s.id (contoh menjadi: bit.ly/G7Kaih-SMPN33). Tutorial untuk membuat s.id bisa di https://lpmp.net/konsep-otomatis/ 

Dengan cara sederhana, kita bisa membuat aplikasi yang cukup powerfull untuk kemudahan kita. Aplikasi contoh diatas masih sangat sederhana, masih perlu banyak ditambah fungsi, kita bisa membuat ulang dengan memperbaiki prompt dan membuat database google sheet yang baru atau memperbaiki dengan menambahi prompt di percakapan yang ada. Misalkan kita ingin menambahkan fungsi rekap di halaman guru,

masukan di prompt

maka gemini akan menghasilkan script gs dan html yang baru, hapus isi yang lama ganti dengan yang baru. Misalkan aplikasi tidak berjalan jangan sungkan komplain ke gemini

“masih error, buat ulang dari awal”

Selamat, Bapak/Ibu Guru yang luar biasa! Jika Anda telah sampai di halaman ini dan berhasil memunculkan link aplikasi Anda sendiri, maka Anda baru saja melakukan sebuah pencapaian yang hebat. Anda telah berhasil menyulap barisan kode dan lembaran spreadsheet kosong menjadi sebuah sistem Aplikasi G7Kaih (7 Kebiasaan Baik) yang siap pakai.

Proses dari Langkah 1 hingga Langkah 3 mungkin terasa menantang, terutama jika ini adalah pengalaman pertama Bapak/Ibu bersentuhan dengan coding dan Google Apps Script. Namun, dedikasi Bapak/Ibu untuk belajar dan mencoba adalah bukti nyata semangat inovasi demi kemajuan peserta didik kita.

Lebih dari Sekadar Aplikasi Rekapitulasi

Mari kita ingat kembali tujuan utama kita. Aplikasi G7Kaih yang baru saja kita bangun bukanlah sekadar alat untuk menyingkirkan tumpukan kertas. Lebih dari itu, aplikasi ini adalah:

  • Jembatan Sinergi: Membuka transparansi antara sekolah dan rumah. Orang tua kini bisa langsung melihat perkembangan karakter anaknya setiap minggu, sementara guru bisa memberikan apresiasi atau catatan yang langsung terbaca oleh wali murid.
  • Katalisator Kedisiplinan: Dengan kemudahan mengisi dari layar HP (yang sangat ramah pengguna), siswa akan merasa lebih termotivasi dan bertanggung jawab untuk melaporkan kebiasaan baik mereka setiap hari.
  • Asisten Cerdas Guru: Fitur “Rekap Bulanan” yang secara otomatis menghitung persentase kebiasaan berdasarkan jenis kelamin dan indikator pembiasaan akan menghemat berjam-jam waktu rekapitulasi Bapak/Ibu Guru di akhir bulan.

Langkah Selanjutnya: Implementasi dan Konsistensi

Aplikasi yang hebat akan memberikan dampak maksimal jika digunakan secara konsisten. Berikut adalah beberapa langkah tindak lanjut yang bisa Bapak/Ibu lakukan:

  1. Bagikan Link Aplikasi: Distribusikan URL (link) aplikasi yang sudah Bapak/Ibu buat ke grup WhatsApp kelas atau paguyuban orang tua. Jangan lupa berikan panduan singkat cara menggunakannya.
  2. Pantau dan Apresiasi: Luangkan waktu 5-10 menit setiap hari atau di akhir minggu untuk melihat dashboard Guru. Berikan “Catatan Guru” yang menguatkan (misalnya: “Hebat, pertahankan bangun paginya!”) dan klik “Setuju Semua” untuk memvalidasi laporan siswa.
  3. Gunakan Data untuk Evaluasi: Manfaatkan fitur Rekap Bulanan saat rapat evaluasi wali kelas. Data tersebut kini bukan lagi sekadar angka, melainkan peta jalan untuk mengetahui area kebiasaan mana yang perlu ditingkatkan secara klasikal di Sekolah

Pendidikan karakter memang bukan proses instan yang bisa diukur dalam semalam. Ia butuh pembiasaan, pemantauan, dan keteladanan. Dengan G7Kaih, kita telah memodernisasi cara kita memantau proses tersebut, menjadikannya lebih mudah, akurat, dan terukur.(Abi)

Tinggalkan Komentar

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *

Situs ini menggunakan Akismet untuk mengurangi spam. Pelajari bagaimana data komentar Anda diproses