Thursday, 12 November 2026

Aplikasi Ujian Semeteran RPL kelas 12

Ujian Interaktif - SMK Hasan Kafrawi Mayong, Jepara - XII RPL
Logo SMK Hasan Kafrawi Mayong Jepara
SMK HASAN KAFRAWI - MAYONG, JEPARA
Ujian Interaktif • Pemrograman Text, Grafik, dan Multimedia • XII RPL
Logo SMKTAHUN PELAJARAN 2026 / 2027 • KELAS XII RPL 1, 2, 3 • MAYONG, JEPARA

Ujian Interaktif
Pemrograman Text, Grafik, dan Multimedia

40 Soal Pilihan Ganda + 5 Esai • Waktu 90 Menit • SMK Hasan Kafrawi Mayong, Jepara

👨‍🎓 MASUK SISWA

Untuk mengerjakan ujian / latihan. Pilih kelasmu lalu mulai.

👩‍🏫 MASUK GURU

Khusus Guru
Hanya untuk guru pengampu. Masukkan kode guru yang sudah dibagikan secara pribadi.

Di halaman guru tersedia rekap nilai per kelas, detail siswa, penilaian esai, dan unduh rekap Excel.

📚 Ringkasan Materi

1. Pemrograman Text

String, split, replace, len, Regex, JSON, file teks, encoding UTF-8, tokenisasi.

2. Pemrograman Grafik

Bitmap vs Vektor, RGB 0-255, Canvas fillRect, SVG circle, koordinat (0,0) kiri atas, translasi, scaling, rotasi, layer.

3. Multimedia

Codec, fps, WAV/FLAC lossless, tweening, kompresi lossy, tag video, storyboard, onclick, MP4, lip-sync, navigasi non-linear, bitrate, konsistensi UI.

4. Integrasi

Menggabungkan teks + grafik + multimedia dalam satu aplikasi web.

Thursday, 27 August 2026

Membangun web sekolah berbasis Google Apps Script (GAS) dengan Google Sheets sebagai basis datanya.

 Berikut adalah modul panduan langkah demi langkah untuk membangun web sekolah berbasis Google Apps Script (GAS) dengan Google Sheets sebagai basis datanya.

Modul: Pembangunan Web Sekolah Berbasis Google Apps Script & Google Sheets

Deskripsi Modul:

Modul ini dirancang untuk pemula yang ingin membangun sistem informasi web sekolah secara gratis menggunakan ekosistem Google.

Tahap 1: Persiapan Database (Google Sheets)

Langkah pertama adalah menyiapkan struktur data sekolah.

1.Buat Google Sheets Baru:
  1. Buka Google Sheets.

  2. Buat dokumen baru dan beri nama Database Web Sekolah.

2.Buat Sheet 'Pengumuman':

Ubah nama Sheet1 menjadi Pengumuman. Buat header pada baris pertama:

  • Kolom A (A1): ID

  • Kolom B (B1): Judul

  • Kolom C (C1): Tanggal

  • Kolom D (D1): Isi

Contoh Data (Baris 2): 1 | Penerimaan Siswa Baru | 2026-08-01 | Pendaftaran siswa baru telah dibuka.

3.Buat Sheet 'Guru':

Buat sheet baru dengan menekan tombol + di pojok kiri bawah, beri nama Guru. Buat header:

  • Kolom A (A1): NIP

  • Kolom B (B1): Nama

  • Kolom C (C1): Mata Pelajaran

Contoh Data (Baris 2): 19850101 | Budi Santoso, S.Pd. | Matematika

Tahap 2: Menulis Kode Apps Script (Backend & Frontend)

1.Buka Apps Script Editor:
  1. Di Google Sheets yang telah dibuat, klik menu Ekstensi > Apps Script.

  2. Ubah nama proyek menjadi Web Sekolah App.

2.Tulis Kode Backend (Code.gs):

Hapus semua kode yang ada di file Code.gs, lalu masukkan kode berikut:


// Menampilkan halaman web utama (HTML)

function doGet() {

  return HtmlService.createTemplateFromFile('Index')

    .evaluate()

    .setTitle('Web Resmi Sekolah')

    .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);

}


// Mengambil data dari Sheet tertentu

function getDataSheet(namaSheet) {

  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(namaSheet);

  var data = sheet.getDataRange().getValues();

  var header = data[0];

  var result = [];


  // Mengubah baris array menjadi array of objects

  for (var i = 1; i < data.length; i++) {

    var row = data[i];

    var obj = {};

    for (var j = 0; j < header.length; j++) {

      obj[header[j]] = row[j];

    }

    result.push(obj);

  }

  return result;

}


3.Buat File Tampilan Frontend (Index.html):
  1. Klik tanda + di sebelah kiri Apps Script > pilih HTML.

  2. Beri nama file Index (tanpa ekstensi .html).

  3. Isi dengan kode HTML + CSS Framework (Bootstrap) berikut: 

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Web Sekolah</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.min.css" rel="stylesheet">
  </head>
  <body>
    <!-- Navbar -->
    <nav class="navbar navbar-expand-lg navbar-dark bg-primary">
      <div class="container">
        <a class="navbar-brand fw-bold" href="#">SMA NEGERI 1</a>
      </div>
    </nav>

    <div class="container my-5">
      <!-- Pengumuman Section -->
      <h2 class="mb-3">Pengumuman Terbaru</h2>
      <div id="loading-pengumuman" class="spinner-border text-primary" role="status"></div>
      <div class="row" id="container-pengumuman"></div>

      <hr class="my-5">

      <!-- Daftar Guru Section -->
      <h2 class="mb-3">Daftar Guru</h2>
      <div id="loading-guru" class="spinner-border text-primary" role="status"></div>
      <table class="table table-striped d-none" id="tabel-guru">
        <thead>
          <tr>
            <th>NIP</th>
            <th>Nama</th>
            <th>Mata Pelajaran</th>
          </tr>
        </thead>
        <tbody id="body-guru"></tbody>
      </table>
    </div>

    <script>
      // Ambil Data Pengumuman saat halaman dimuat
      google.script.run.withSuccessHandler(renderPengumuman).getDataSheet('Pengumuman');
      // Ambil Data Guru saat halaman dimuat
      google.script.run.withSuccessHandler(renderGuru).getDataSheet('Guru');

      function renderPengumuman(data) {
        document.getElementById('loading-pengumuman').classList.add('d-none');
        let html = '';
        data.forEach(item => {
          html += `
            <div class="col-md-6 mb-3">
              <div class="card h-100">
                <div class="card-body">
                  <h5 class="card-title">${item.Judul}</h5>
                  <h6 class="card-subtitle mb-2 text-muted">${item.Tanggal}</h6>
                  <p class="card-text">${item.Isi}</p>
                </div>
              </div>
            </div>`;
        });
        document.getElementById('container-pengumuman').innerHTML = html;
      }

      function renderGuru(data) {
        document.getElementById('loading-guru').classList.add('d-none');
        let html = '';
        data.forEach(item => {
          html += `
            <tr>
              <td>${item.NIP}</td>
              <td>${item.Nama}</td>
              <td>${item['Mata Pelajaran']}</td>
            </tr>`;
        });
        document.getElementById('body-guru').innerHTML = html;
        document.getElementById('tabel-guru').classList.remove('d-none');
      }
    </script>
  </body>
</html>

Tahap 3: Peluncuran / Deployment Web

1.Lakukan Deployment Proyek:
  1. Di pojok kanan atas Apps Script Editor, klik Terapkan (Deploy) > Deploy Baru (New Deployment).

  2. Klik ikon roda gigi (Select type) > pilih Aplikasi Web (Web app).

2.Atur Akses Pengguna:
  • Deskripsi: Versi 1 - Web Sekolah

  • Jalankan sebagai (Execute as): Saya (Me)

  • Yang memiliki akses (Who has access): Siapa saja (Anyone) (Agar web dapat diakses umum tanpa harus login).

3.Dapatkan URL Web:
  1. Klik Terapkan (Deploy).

  2. Berikan izin akses (Authorize Access) jika diminta oleh sistem Google.

  3. Salin URL Aplikasi Web yang diberikan. Buka URL tersebut di browser untuk melihat hasilnya.

Pembahasan & Penjelasan Konsep Teknis

1. Arsitektur Aplikasi (Client-Server Model)

  • Backend (Code.gs): Bertindak sebagai server. Fungsi doGet() otomatis dipanggil saat seseorang membuka URL web. Fungsi getDataSheet() bertugas membaca data dari spreadsheet lalu mengubah data mentah berbentuk baris/kolom menjadi format objek JSON agar mudah diproses di browser.

  • Database (Google Sheets): Menyimpan data secara persisten. Saat Anda menambah baris data baru di Sheet Pengumuman atau Guru, tampilan halaman web akan langsung terbarui secara otomatis saat dimuat ulang.

  • Frontend (Index.html): Berisi struktur tampilan web. Bootstrap digunakan melalui CDN untuk menyajikan antarmuka yang responsif (rapi di HP maupun komputer).

2. Jembatan Komunikasi (google.script.run) Di dalam file HTML, perintah google.script.run.withSuccessHandler(callback).namaFungsiBackend() adalah kunci utama. Perintah ini mengeksekusi fungsi getDataSheet() di server Google secara asinkron (tanpa reload halaman), lalu mengirimkan hasilnya ke fungsi JavaScript renderPengumuman atau renderGuru di browser untuk ditampilkan.

Membangun aplikasi Kasir (Point of Sale) sederhana

Berikut adalah modul pembelajaran lengkap untuk membangun aplikasi Kasir (Point of Sale) sederhana berbasis JavaScript/Web, lengkap dengan persiapan, struktur kode, dan alur logika di balik layarnya.

Modul 1: Persiapan & Struktur Project

1. Tools yang Dibutuhkan

  • Editor Kode: Visual Studio Code.

  • Browser: Google Chrome / Firefox.

2. Struktur Folder & File Buat folder baru bernama aplikasi-kasir dan siapkan 3 file utama:

  • index.html (Tampilan antarmuka)

  • style.css (Desain/tata letak)

  • app.js (Logika & pemrosesan data)

Modul 2: Desain Antarmuka (index.html & style.css)

Buat struktur HTML dengan dua area utama: Daftar Produk (kiri) dan Keranjang/Struk (kanan).

index.html

<!DOCTYPE html>

<html lang="id">

<head>

  <meta charset="UTF-8">

  <title>Aplikasi Kasir Sederhana</title>

  <link rel="stylesheet" href="style.css">

</head>

<body>

  <div class="container">

    <!-- Bagian Produk -->

    <div class="produk-section">

      <h2>Daftar Produk</h2>

      <div id="daftar-produk" class="grid-produk"></div>

    </div>


    <!-- Bagian Keranjang & Pembayaran -->

    <div class="kasir-section">

      <h2>Keranjang Belanja</h2>

      <ul id="daftar-keranjang"></ul>

      <hr>

      <p><strong>Total: Rp <span id="total-harga">0</span></strong></p>


      <div class="pembayaran">

        <label>Bayar: Rp</label>

        <input type="number" id="input-bayar" placeholder="Masukkan nominal">

        <button onclick="prosesBayar()">Proses Transaksi</button>

      </div>

      <p id="pesan-kembalian"></p>

    </div>

  </div>


  <script src="app.js"></script>

</body>

</html>


style.css

body { font-family: Arial, sans-serif; margin: 20px; background: #f4f4f9; }

.container { display: flex; gap: 20px; }

.produk-section, .kasir-section { background: white; padding: 20px; border-radius: 8px; flex: 1; }

.grid-produk { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }

.item-produk { border: 1px solid #ddd; padding: 10px; text-align: center; border-radius: 5px; }

.item-produk button { background: #28a745; color: white; border: none; padding: 5px 10px; cursor: pointer; }

button { cursor: pointer; }


Modul 3: Logika Sistem & Pemrograman (app.js)

app.js

// Data Master Produk

const produkList = [

  { id: 1, nama: "Kopi Hitam", harga: 10000 },

  { id: 2, nama: "Roti Bakar", harga: 15000 },

  { id: 3, nama: "Es Teh Manis", harga: 5000 },

  { id: 4, nama: "Nasi Goreng", harga: 20000 }

];


// State Keranjang

let keranjang = [];


// 1. Tampilkan Daftar Produk ke HTML

function tampilkanProduk() {

  const container = document.getElementById('daftar-produk');

  container.innerHTML = '';

  

  produkList.forEach(item => {

    container.innerHTML += `

      <div class="item-produk">

        <h4>${item.nama}</h4>

        <p>Rp ${item.harga.toLocaleString('id-ID')}</p>

        <button onclick="tambahKeKeranjang(${item.id})">+ Tambah</button>

      </div>

    `;

  });

}


// 2. Tambah Produk ke Keranjang

function tambahKeKeranjang(idProduk) {

  const itemInKeranjang = keranjang.find(item => item.id === idProduk);

  

  if (itemInKeranjang) {

    itemInKeranjang.jumlah += 1;

  } else {

    const produkAwal = produkList.find(p => p.id === idProduk);

    keranjang.push({ ...produkAwal, jumlah: 1 });

  }

  updateTampilanKeranjang();

}


// 3. Update Tampilan Keranjang & Total

function updateTampilanKeranjang() {

  const listKeranjang = document.getElementById('daftar-keranjang');

  const totalHargaEl = document.getElementById('total-harga');

  

  listKeranjang.innerHTML = '';

  let total = 0;


  keranjang.forEach(item => {

    const subtotal = item.harga * item.jumlah;

    total += subtotal;

    

    listKeranjang.innerHTML += `

      <li>${item.nama} x ${item.jumlah} = Rp ${subtotal.toLocaleString('id-ID')}</li>

    `;

  });


  totalHargaEl.innerText = total.toLocaleString('id-ID');

}


// 4. Hitung Pembayaran & Kembalian

function prosesBayar() {

  const total = keranjang.reduce((acc, item) => acc + (item.harga * item.jumlah), 0);

  const nominalBayar = Number(document.getElementById('input-bayar').value);

  const pesanEl = document.getElementById('pesan-kembalian');


  if (total === 0) {

    pesanEl.innerText = "Keranjang masih kosong!";

    return;

  }


  if (nominalBayar < total) {

    pesanEl.innerText = "Uang tidak cukup!";

  } else {

    const kembalian = nominalBayar - total;

    pesanEl.innerText = `Transaksi Berhasil! Kembalian: Rp ${kembalian.toLocaleString('id-ID')}`;

    

    // Reset Kasir

    keranjang = [];

    document.getElementById('input-bayar').value = '';

    updateTampilanKeranjang();

  }

}


// Jalankan saat pertama kali dibuka

tampilkanProduk();


Modul 4: Penjelasan Alur Logika Sistem

1. Konsep State (Penyimpanan Sementara Data)

  • Array produkList: Berfungsi sebagai database lokal yang menyimpan master barang (ID, nama, harga).

  • Array keranjang: Berfungsi menampung transaksi aktif. Ketika tombol "+ Tambah" diklik, sistem mengecek apakah barang sudah ada di keranjang. Jika sudah ada, tambahkan nilai jumlah (+1). Jika belum ada, masukkan objek barang baru dengan jumlah = 1.

2. Alur Pengolahan Data (Data Flow)

  • Aksi Tambah: User klik tombol $\rightarrow$ Fungsi tambahKeKeranjang(id) $\rightarrow$ Update Array keranjang $\rightarrow$ Panggil updateTampilanKeranjang().

  • Kalkulasi Total: Fungsi updateTampilanKeranjang() melakukan kalkulasi subtotal per item ($\text{harga} \times \text{jumlah}$) lalu menjumlahkan seluruh item untuk menghasilkan Total.

3. Logika Validasi Pembayaran

  • Ambil input dari input-bayar.

  • Lakukan percabangan logika (Condition Check):

    • Jika $\text{Bayar} < \text{Total}$: Tampilkan peringatan uang kurang.

    • Jika $\text{Bayar} \ge \text{Total}$: Hitung $\text{Kembalian} = \text{Bayar} - \text{Total}$, tampilkan pesan sukses, lalu kosongkan array keranjang untuk transaksi berikutnya.


Berikut adalah pembaruan kode app.js yang menyertakan fitur hapus/kurangi item dari keranjang serta penyimpanan riwayat transaksi otomatis menggunakan localStorage agar data tidak hilang saat halaman di-refresh.

1. Pembaruan Antarmuka (index.html)

Tambahkan area untuk menampilkan riwayat transaksi di bawah bagian keranjang pada index.html:

<!-- Tambahkan div ini di dalam .container atau di bagian bawah html -->

<div class="riwayat-section" style="margin-top: 20px; background: white; padding: 20px; border-radius: 8px;">

  <h2>Riwayat Transaksi</h2>

  <button onclick="hapusRiwayat()" style="background: #dc3545; color: white; border: none; padding: 5px 10px; margin-bottom: 10px;">Hapus Riwayat</button>

  <ul id="daftar-riwayat"></ul>

</div>

2. Kode Logika Baru (app.js)

Gantikan seluruh isi file app.js Anda dengan kode berikut:

// Data Master Produk

const produkList = [

  { id: 1, nama: "Kopi Hitam", harga: 10000 },

  { id: 2, nama: "Roti Bakar", harga: 15000 },

  { id: 3, nama: "Es Teh Manis", harga: 5000 },

  { id: 4, nama: "Nasi Goreng", harga: 20000 }

];


// State Keranjang & Riwayat Transaksi

let keranjang = [];

// Ambil riwayat dari LocalStorage jika ada, jika tidak set array kosong

let riwayatTransaksi = JSON.parse(localStorage.getItem('riwayat_kasir')) || [];


// 1. Tampilkan Daftar Produk

function tampilkanProduk() {

  const container = document.getElementById('daftar-produk');

  container.innerHTML = '';

  

  produkList.forEach(item => {

    container.innerHTML += `

      <div class="item-produk">

        <h4>${item.nama}</h4>

        <p>Rp ${item.harga.toLocaleString('id-ID')}</p>

        <button onclick="tambahKeKeranjang(${item.id})">+ Tambah</button>

      </div>

    `;

  });

}


// 2. Tambah Produk ke Keranjang

function tambahKeKeranjang(idProduk) {

  const itemInKeranjang = keranjang.find(item => item.id === idProduk);

  

  if (itemInKeranjang) {

    itemInKeranjang.jumlah += 1;

  } else {

    const produkAwal = produkList.find(p => p.id === idProduk);

    keranjang.push({ ...produkAwal, jumlah: 1 });

  }

  updateTampilanKeranjang();

}


// 3. Fitur Baru: Hapus / Kurangi Item dari Keranjang

function kurangiDariKeranjang(idProduk) {

  const index = keranjang.findIndex(item => item.id === idProduk);

  

  if (index !== -1) {

    if (keranjang[index].jumlah > 1) {

      keranjang[index].jumlah -= 1; // Kurangi jumlah jika > 1

    } else {

      keranjang.splice(index, 1); // Hapus dari keranjang jika jumlah tinggal 1

    }

  }

  updateTampilanKeranjang();

}


// 4. Update Tampilan Keranjang

function updateTampilanKeranjang() {

  const listKeranjang = document.getElementById('daftar-keranjang');

  const totalHargaEl = document.getElementById('total-harga');

  

  listKeranjang.innerHTML = '';

  let total = 0;


  keranjang.forEach(item => {

    const subtotal = item.harga * item.jumlah;

    total += subtotal;

    

    listKeranjang.innerHTML += `

      <li style="margin-bottom: 5px;">

        ${item.nama} x ${item.jumlah} = Rp ${subtotal.toLocaleString('id-ID')}

        <button onclick="kurangiDariKeranjang(${item.id})" style="background:#dc3545; color:white; border:none; padding:2px 6px; border-radius:3px; margin-left:8px;">- Hapus</button>

      </li>

    `;

  });


  totalHargaEl.innerText = total.toLocaleString('id-ID');

}


// 5. Fitur Baru: Simpan & Tampilkan Riwayat

function simpanDanTampilkanRiwayat(transaksiBaru) {

  // Tambahkan ke array riwayat

  riwayatTransaksi.push(transaksiBaru);

  

  // Simpan array riwayat ke LocalStorage (diubah ke format string JSON)

  localStorage.setItem('riwayat_kasir', JSON.stringify(riwayatTransaksi));

  

  tampilkanRiwayat();

}


function tampilkanRiwayat() {

  const listRiwayat = document.getElementById('daftar-riwayat');

  if (!listRiwayat) return;

  

  listRiwayat.innerHTML = '';


  if (riwayatTransaksi.length === 0) {

    listRiwayat.innerHTML = '<li>Belum ada transaksi.</li>';

    return;

  }


  riwayatTransaksi.forEach((tx, index) => {

    listRiwayat.innerHTML += `

      <li style="margin-bottom: 8px; border-bottom: 1px #eee solid; padding-bottom: 5px;">

        <strong>[${tx.waktu}]</strong> Total: Rp ${tx.total.toLocaleString('id-ID')} | Bayar: Rp ${tx.bayar.toLocaleString('id-ID')} (Kembali: Rp ${tx.kembalian.toLocaleString('id-ID')})

      </li>

    `;

  });

}


function hapusRiwayat() {

  localStorage.removeItem('riwayat_kasir');

  riwayatTransaksi = [];

  tampilkanRiwayat();

}


// 6. Hitung Pembayaran

function prosesBayar() {

  const total = keranjang.reduce((acc, item) => acc + (item.harga * item.jumlah), 0);

  const nominalBayar = Number(document.getElementById('input-bayar').value);

  const pesanEl = document.getElementById('pesan-kembalian');


  if (total === 0) {

    pesanEl.innerText = "Keranjang masih kosong!";

    return;

  }


  if (nominalBayar < total) {

    pesanEl.innerText = "Uang tidak cukup!";

  } else {

    const kembalian = nominalBayar - total;

    pesanEl.innerText = `Transaksi Berhasil! Kembalian: Rp ${kembalian.toLocaleString('id-ID')}`;

    

    // Buat Objek Transaksi

    const transaksi = {

      waktu: new Date().toLocaleString('id-ID'),

      total: total,

      bayar: nominalBayar,

      kembalian: kembalian

    };


    // Simpan ke LocalStorage

    simpanDanTampilkanRiwayat(transaksi);


    // Reset Kasir

    keranjang = [];

    document.getElementById('input-bayar').value = '';

    updateTampilanKeranjang();

  }

}


// Inisialisasi awal saat halaman dimuat

tampilkanProduk();

tampilkanRiwayat();


3. Penjelasan Logika Fitur Baru

  • localStorage (Penyimpanan Browser): localStorage hanya menerima tipe data string. Oleh karena itu, kita gunakan JSON.stringify() saat menyimpannya dan JSON.parse() saat membaca kembali data objek ke bentuk Javascript Array.

  • Logika Pengurangan/Penghapusan Array (splice): Pada kurangiDariKeranjang(), sistem mengecek nilai jumlah. Jika item lebih dari 1, nilainya cukup dikurangi 1. Jika tersisa 1, kita gunakan splice(index, 1) untuk menghapus elemen tersebut secara penuh dari array keranjang.


Aplikasi Ujian Semeteran RPL kelas 12

Ujian Interaktif - SMK Hasan Kafrawi Mayong, Jepara - XII RPL SMK HASAN KAFRAWI - MAYONG, JEPARA Ujian Interaktif • Pemrograman Text, Gr...