Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //Code.GS
- function doGet() {
- return HtmlService.createTemplateFromFile('Index')
- .evaluate()
- .setTitle(CONFIG.APP_TITLE)
- .addMetaTag('viewport', 'width=device-width, initial-scale=1')
- .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
- }
- function columnToLetter(column) {
- var temp, letter = '';
- while (column > 0) {
- temp = (column - 1) % 26;
- letter = String.fromCharCode(temp + 65) + letter;
- column = (column - temp - 1) / 26;
- }
- return letter;
- }
- // --- BARU: AMBIL DAFTAR KELAS (NAMA SHEET) ---
- function getDaftarKelas() {
- var ss = SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID);
- var sheets = ss.getSheets();
- var daftarKelas = [];
- sheets.forEach(function(sheet) {
- // Ambil nama sheet sebagai nama kelas
- daftarKelas.push(sheet.getName());
- });
- return {
- kelas: daftarKelas,
- config: CONFIG // Kirim config ke frontend juga
- };
- }
- // --- FUNGSI CEK TEMPLATE (DINAMIS PER KELAS) ---
- function cekDanBuatTemplate(namaKelas) {
- var ss = SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID);
- var sheet = ss.getSheetByName(namaKelas);
- if (!sheet) return; // Safety check
- // Security Check 1
- var headerNo = sheet.getRange(CONFIG.HEADER_ROW, 1).getValue();
- if (headerNo === "No") return;
- // Security Check 2
- var lastRow = sheet.getLastRow();
- if (lastRow >= CONFIG.DATA_START_ROW) {
- var dataCheck = sheet.getRange(CONFIG.DATA_START_ROW, 2, Math.min(lastRow, 20), 1).getValues();
- for (var i = 0; i < dataCheck.length; i++) {
- if (dataCheck[i][0] && dataCheck[i][0].toString().trim() !== "") return;
- }
- }
- // --- INSTALASI TEMPLATE ---
- sheet.clear();
- var headerInfos = [
- { row: 1, text: CONFIG.SCHOOL_NAME, size: 14 },
- { row: 2, text: "KELAS: " + namaKelas, size: 12 }, // Dinamis dari Nama Sheet
- { row: 3, text: CONFIG.SUBJECT_INFO, size: 12 },
- { row: 4, text: CONFIG.SEMESTER_INFO, size: 12 }
- ];
- headerInfos.forEach(function(info) {
- sheet.getRange(info.row, 1, 1, 9).merge()
- .setValue(info.text)
- .setHorizontalAlignment("center").setVerticalAlignment("middle")
- .setFontWeight("bold").setFontSize(info.size);
- });
- var headerColor = "#0f515e"; var fontColor = "#ffffff";
- sheet.getRange(CONFIG.HEADER_ROW - 1, 1, 2, 1).merge().setValue("No");
- sheet.getRange(CONFIG.HEADER_ROW - 1, 2, 2, 1).merge().setValue("Nama Siswa");
- sheet.getRange(CONFIG.HEADER_ROW - 1, 1, 2, 2)
- .setFontWeight("bold").setHorizontalAlignment("center").setVerticalAlignment("middle")
- .setBackground(headerColor).setFontColor(fontColor)
- .setBorder(true, true, true, true, true, true, "black", SpreadsheetApp.BorderStyle.SOLID);
- sheet.getRange(CONFIG.HEADER_ROW - 1, 3).setValue("Pertemuan").setFontColor(headerColor);
- sheet.getRange(CONFIG.HEADER_ROW - 1, 4, 1, 6).merge().setValue("Total")
- .setFontWeight("bold").setHorizontalAlignment("center").setVerticalAlignment("middle")
- .setBackground(headerColor).setFontColor(fontColor)
- .setBorder(true, true, true, true, true, true, "black", SpreadsheetApp.BorderStyle.SOLID);
- var subHeaders = ["H", "A", "I", "S", "T", "B"];
- sheet.getRange(CONFIG.HEADER_ROW, 4, 1, 6).setValues([subHeaders])
- .setFontWeight("bold").setHorizontalAlignment("center").setVerticalAlignment("middle")
- .setBackground(headerColor).setFontColor(fontColor)
- .setBorder(true, true, true, true, true, true, "black", SpreadsheetApp.BorderStyle.SOLID);
- sheet.setColumnWidth(1, 40); sheet.setColumnWidth(2, 250); sheet.setColumnWidth(3, 10);
- for (var c = 4; c <= 9; c++) sheet.setColumnWidth(c, 40);
- sheet.hideColumns(3);
- var startRow = CONFIG.DATA_START_ROW;
- sheet.getRange(startRow, 1).setValue(1).setHorizontalAlignment("center");
- sheet.getRange(startRow, 2).setValue("Siswa Contoh (Ganti Nama Ini)");
- var totalHLetter = columnToLetter(4);
- var formulas = [[
- '=COUNTIF($C7:OFFSET($'+totalHLetter+'7;0;-1); "H")',
- '=COUNTIF($C7:OFFSET($'+totalHLetter+'7;0;-1); "A")',
- '=COUNTIF($C7:OFFSET($'+totalHLetter+'7;0;-1); "I")',
- '=COUNTIF($C7:OFFSET($'+totalHLetter+'7;0;-1); "S")',
- '=COUNTIF($C7:OFFSET($'+totalHLetter+'7;0;-1); "T")',
- '=COUNTIF($C7:OFFSET($'+totalHLetter+'7;0;-1); "B")'
- ]];
- sheet.getRange(startRow, 4, 1, 6).setFormulas(formulas).setHorizontalAlignment("center");
- sheet.getRange(startRow, 1, 1, 9).setBorder(true, true, true, true, true, true, "black", SpreadsheetApp.BorderStyle.SOLID).setVerticalAlignment("middle");
- }
- // --- AMBIL SISWA BERDASARKAN KELAS (REVISI FIX REKAP) ---
- function getDaftarSiswa(namaKelas) {
- // Cek template dulu untuk kelas yg dipilih
- cekDanBuatTemplate(namaKelas);
- var ss = SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID);
- var sheet = ss.getSheetByName(namaKelas);
- if (!sheet) return [];
- var lastRow = sheet.getLastRow();
- if (lastRow < CONFIG.DATA_START_ROW) return [];
- // Ambil semua data dari baris pertama siswa sampai baris paling bawah sheet
- var data = sheet.getRange(CONFIG.DATA_START_ROW, 2, lastRow - CONFIG.DATA_START_ROW + 1, 1).getValues();
- var siswa = [];
- // Daftar kata kunci yang menandakan batas akhir data siswa
- var stopKeywords = [
- "REKAPITULASI HARIAN",
- "Total Hadir", "Total Hadir (H)",
- "Total Sakit", "Total Sakit (S)",
- "Total Izin", "Total Izin (I)",
- "Total Alpa", "Total Alpa (A)",
- "Total Terlambat", "Total Terlambat (T)",
- "Total Bolos", "Total Bolos (B)"
- ];
- for (var i = 0; i < data.length; i++) {
- var cellValue = data[i][0];
- // Validasi: Pastikan cell tidak kosong
- if (cellValue && cellValue.toString().trim() !== "") {
- var namaStr = cellValue.toString().trim();
- if (stopKeywords.indexOf(namaStr) > -1 || namaStr.indexOf("Total ") === 0) {
- break; // BERHENTI loop (stop scan ke bawah)
- }
- // Jika bukan rekap, masukkan ke daftar siswa
- siswa.push(namaStr);
- }
- }
- return siswa;
- }
- // --- FUNGSI UTAMA: SIMPAN PRESENSI (VERSI DINAMIS & RAPI) ---
- function simpanPresensi(dataPresensi, namaKelas) {
- var ss = SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID);
- var sheet = ss.getSheetByName(namaKelas);
- // 1. BERSIHKAN AREA SISWA (Hapus baris kosong/siswa keluar)
- bersihkanAreaSiswa(sheet);
- // 2. DETEKSI POSISI & STRUKTUR
- var lastRowTotal = sheet.getLastRow();
- var startRekapRow = -1;
- var dataKolomB = sheet.getRange(1, 2, lastRowTotal, 1).getValues();
- for (var i = 0; i < lastRowTotal; i++) {
- if (dataKolomB[i][0] === "REKAPITULASI HARIAN") {
- startRekapRow = i + 1;
- break;
- }
- }
- // Tentukan baris siswa terakhir
- var lastStudentRow = CONFIG.DATA_START_ROW - 1;
- if (startRekapRow !== -1) {
- for (var r = startRekapRow - 1; r >= CONFIG.DATA_START_ROW; r--) {
- var val = sheet.getRange(r, 2).getValue();
- if (val !== "" && val !== null) {
- lastStudentRow = r;
- break;
- }
- }
- } else {
- lastStudentRow = sheet.getLastRow();
- }
- // Jaga jarak 1 baris (Gap)
- if (startRekapRow !== -1 && (startRekapRow - lastStudentRow) <= 1) {
- sheet.insertRowAfter(lastStudentRow);
- startRekapRow++;
- }
- // Bersihkan Baris Gap
- var gapRow = lastStudentRow + 1;
- var maxCols = sheet.getLastColumn();
- sheet.getRange(gapRow, 1, 1, maxCols).clearContent().clearFormat().setBackground("white");
- // 3. INSERT KOLOM TANGGAL BARU
- var lastCol = sheet.getLastColumn();
- var headers = sheet.getRange(CONFIG.HEADER_ROW, 1, 1, lastCol).getValues()[0];
- var targetColIndex = -1;
- for (var i = 0; i < headers.length; i++) {
- if (headers[i] === CONFIG.HEADER_TOTAL_START || headers[i] === "Total") { targetColIndex = i + 1; break; }
- }
- if (targetColIndex === -1) targetColIndex = lastCol - 5;
- sheet.insertColumnBefore(targetColIndex);
- sheet.setColumnWidth(targetColIndex, 35);
- // Header Pertemuan & Tanggal
- var superHeaderRow = CONFIG.HEADER_ROW - 1;
- var colsToMerge = targetColIndex - 3 + 1;
- try { sheet.getRange(superHeaderRow, 3, 1, colsToMerge - 1).breakApart(); } catch(e){}
- sheet.getRange(superHeaderRow, 3, 1, colsToMerge).merge().setValue("Pertemuan")
- .setHorizontalAlignment("center").setVerticalAlignment("middle")
- .setFontWeight("bold").setFontColor("white").setBackground("#0f515e")
- .setBorder(true, true, true, true, true, true, "black", SpreadsheetApp.BorderStyle.SOLID);
- var today = new Date();
- var dateString = Utilities.formatDate(today, "GMT+8", "dd/MM");
- sheet.getRange(CONFIG.HEADER_ROW, targetColIndex).setValue(dateString)
- .setFontWeight("bold").setHorizontalAlignment("center")
- .setBackground("#0f515e").setFontColor("white")
- .setBorder(true, true, true, true, true, true, "black", SpreadsheetApp.BorderStyle.SOLID);
- // 4. TULIS DATA ABSENSI
- var numSiswa = lastStudentRow - CONFIG.DATA_START_ROW + 1;
- if (numSiswa > 0) {
- var dataToWrite = [];
- for (var j = 0; j < numSiswa; j++) {
- dataToWrite.push([ (j < dataPresensi.length) ? dataPresensi[j] : "" ]);
- }
- sheet.getRange(CONFIG.DATA_START_ROW, targetColIndex, numSiswa, 1)
- .setValues(dataToWrite).setHorizontalAlignment("center");
- // --- BAGIAN INI DIMODIFIKASI UNTUK UPDATE FORMULA & WARNA ---
- var colTotalStart = targetColIndex + 1;
- var totalHLetter = columnToLetter(colTotalStart);
- // A. UPDATE HEADER KECIL (Agar sesuai urutan H, A, I, S, T, B)
- var subHeaders = [["H", "A", "I", "S", "T", "B"]];
- sheet.getRange(CONFIG.HEADER_ROW, colTotalStart, 1, 6)
- .setValues(subHeaders)
- .setHorizontalAlignment("center").setFontWeight("bold")
- .setBackground("#0f515e").setFontColor("white"); // Samakan style header
- var formulasTotal = [];
- var nomorUrut = [];
- for(var k = 0; k < numSiswa; k++) {
- var r = CONFIG.DATA_START_ROW + k;
- // B. RUMUS AKURAT SESUAI URUTAN HEADER (H, A, I, S, T, B)
- formulasTotal.push([
- '=COUNTIF($C'+r+':OFFSET($'+totalHLetter+r+';0;-1); "H")', // Kolom 1: H
- '=COUNTIF($C'+r+':OFFSET($'+totalHLetter+r+';0;-1); "A")', // Kolom 2: A (Alpa)
- '=COUNTIF($C'+r+':OFFSET($'+totalHLetter+r+';0;-1); "I")', // Kolom 3: I
- '=COUNTIF($C'+r+':OFFSET($'+totalHLetter+r+';0;-1); "S")', // Kolom 4: S
- '=COUNTIF($C'+r+':OFFSET($'+totalHLetter+r+';0;-1); "T")', // Kolom 5: T
- '=COUNTIF($C'+r+':OFFSET($'+totalHLetter+r+';0;-1); "B")' // Kolom 6: B
- ]);
- nomorUrut.push([k + 1]);
- }
- // Terapkan Rumus
- var rangeTotal = sheet.getRange(CONFIG.DATA_START_ROW, colTotalStart, numSiswa, 6);
- rangeTotal.setFormulas(formulasTotal)
- .setHorizontalAlignment("center") // C. AUTO CENTER
- .setVerticalAlignment("middle");
- sheet.getRange(CONFIG.DATA_START_ROW, 1, numSiswa, 1).setValues(nomorUrut).setHorizontalAlignment("center");
- // Border Rapi
- sheet.getRange(CONFIG.DATA_START_ROW, 1, numSiswa, sheet.getLastColumn())
- .setBorder(true, true, true, true, true, true, "black", SpreadsheetApp.BorderStyle.SOLID);
- // --- D. CONDITIONAL FORMATTING (WARNA ALPA OTOMATIS) ---
- var colAlpaIndex = colTotalStart + 1;
- var rangeAlpa = sheet.getRange(CONFIG.DATA_START_ROW, colAlpaIndex, numSiswa, 1);
- // Hapus aturan lama agar tidak menumpuk
- rangeAlpa.clearFormat(); // Hapus warna manual
- // Kembalikan border & align karena clearFormat menghapusnya
- rangeAlpa.setBorder(true, true, true, true, true, true, "black", SpreadsheetApp.BorderStyle.SOLID)
- .setHorizontalAlignment("center").setVerticalAlignment("middle");
- var rules = sheet.getConditionalFormatRules();
- var newRules = [];
- // Aturan 1: Merah (10-20 Alpa)
- var ruleMerah = SpreadsheetApp.newConditionalFormatRule()
- .whenNumberBetween(10, 20)
- .setBackground("#ea9999") // Merah
- .setRanges([rangeAlpa])
- .build();
- // Aturan 2: Orange (5-9 Alpa)
- var ruleOrange = SpreadsheetApp.newConditionalFormatRule()
- .whenNumberBetween(5, 9)
- .setBackground("#f9cb9c") // Orange
- .setRanges([rangeAlpa])
- .build();
- // Aturan 3: Kuning (3-4 Alpa)
- var ruleKuning = SpreadsheetApp.newConditionalFormatRule()
- .whenNumberBetween(3, 4)
- .setBackground("#ffe599") // Kuning
- .setRanges([rangeAlpa])
- .build();
- sheet.clearConditionalFormatRules();
- newRules.push(ruleMerah);
- newRules.push(ruleOrange);
- newRules.push(ruleKuning);
- sheet.setConditionalFormatRules(newRules);
- }
- // Update Header Info
- var currentLastCol = sheet.getLastColumn();
- var headerInfos = [
- { row: 1, text: CONFIG.SCHOOL_NAME }, { row: 2, text: "KELAS: " + namaKelas },
- { row: 3, text: CONFIG.SUBJECT_INFO }, { row: 4, text: CONFIG.SEMESTER_INFO }
- ];
- headerInfos.forEach(function(info) {
- try { sheet.getRange(info.row, 1, 1, currentLastCol).breakApart(); } catch(e){}
- sheet.getRange(info.row, 1, 1, currentLastCol).merge().setValue(info.text).setHorizontalAlignment("center");
- });
- updateRekapHarian(sheet, targetColIndex, lastStudentRow);
- return CONFIG.SUCCESS_MSG;
- }
- function bersihkanBarisSiswaKosong(sheet) {
- var lastRow = sheet.getLastRow();
- var limitRow = lastRow;
- var dataB = sheet.getRange(CONFIG.DATA_START_ROW, 2, lastRow - CONFIG.DATA_START_ROW + 1, 1).getValues();
- for(var i=0; i<dataB.length; i++) {
- if (dataB[i][0] === "REKAPITULASI HARIAN") {
- limitRow = CONFIG.DATA_START_ROW + i - 1;
- break;
- }
- }
- for (var r = limitRow; r >= CONFIG.DATA_START_ROW; r--) {
- var nama = sheet.getRange(r, 2).getValue();
- if (nama === "" || nama === null || nama.toString().trim() === "") {
- sheet.deleteRow(r);
- }
- }
- }
- function hapusRekapHarian(sheet) {
- var lastRow = sheet.getLastRow();
- var keywords = [
- "REKAPITULASI HARIAN",
- "Total Hadir", "Total Hadir (H)",
- "Total Sakit", "Total Sakit (S)",
- "Total Izin", "Total Izin (I)",
- "Total Alpa", "Total Alpa (A)",
- "Total Terlambat", "Total Terlambat (T)",
- "Total Bolos", "Total Bolos (B)"
- ];
- // Loop mundur dari bawah ke atas
- for (var i = lastRow; i >= CONFIG.DATA_START_ROW; i--) {
- var cellValue = sheet.getRange(i, 2).getValue();
- var stringValue = cellValue ? cellValue.toString().trim() : "";
- if (keywords.indexOf(stringValue) > -1 || (stringValue.indexOf("Total ") === 0 && stringValue.length < 25)) {
- // Hapus baris ini
- sheet.deleteRow(i);
- }
- }
- }
- // --- HELPER 2: BUAT REKAP HARIAN BARU ---
- function buatRekapHarian(sheet, colIndexDate) {
- var lastRowSiswa = sheet.getLastRow();
- var startRow = lastRowSiswa + 2; // Beri jarak 1 baris kosong dari data siswa terakhir
- // Label Rekap Lengkap (H, S, I, A, T, B)
- var labels = [
- ["REKAPITULASI HARIAN"],
- ["Total Hadir (H)"],
- ["Total Sakit (S)"],
- ["Total Izin (I)"],
- ["Total Alpa (A)"],
- ["Total Terlambat (T)"], // Tambahan
- ["Total Bolos (B)"] // Tambahan
- ];
- // Set Label di Kolom B (Nama Siswa)
- sheet.getRange(startRow, 2, labels.length, 1).setValues(labels)
- .setFontWeight("bold").setHorizontalAlignment("right");
- // Format Judul Rekap (agar lebih menonjol)
- sheet.getRange(startRow, 2).setFontColor("#0f515e").setFontSize(11);
- // Buat Range Data untuk dihitung (Dari baris pertama data s.d. baris terakhir siswa)
- var colLetter = columnToLetter(colIndexDate);
- var rangeData = colLetter + CONFIG.DATA_START_ROW + ":" + colLetter + lastRowSiswa;
- // Rumus Lengkap
- var formulas = [
- [""], // Baris judul kosong
- ["=COUNTIF(" + rangeData + "; \"H\")"],
- ["=COUNTIF(" + rangeData + "; \"S\")"],
- ["=COUNTIF(" + rangeData + "; \"I\")"],
- ["=COUNTIF(" + rangeData + "; \"A\")"],
- ["=COUNTIF(" + rangeData + "; \"T\")"], // Rumus T
- ["=COUNTIF(" + rangeData + "; \"B\")"] // Rumus B
- ];
- // Tulis Formula ke Sheet
- sheet.getRange(startRow, colIndexDate, formulas.length, 1).setFormulas(formulas)
- .setFontWeight("bold").setHorizontalAlignment("center")
- .setBorder(true, true, true, true, true, true, "black", SpreadsheetApp.BorderStyle.SOLID)
- .setBackground("#f3f3f3"); // Background abu muda
- }
- function updateRekapHarian(sheet, colIndexDate, lastStudentRow) {
- // --- KONFIGURASI TAMPILAN (DISESUAIKAN DENGAN URUTAN H, A, I, S, T, B) ---
- var labels = [
- "REKAPITULASI HARIAN",
- "Total Hadir (H)",
- "Total Alpa (A)", // Alpa naik ke urutan kedua
- "Total Izin (I)",
- "Total Sakit (S)",
- "Total Terlambat (T)",
- "Total Bolos (B)"
- ];
- // Kode Rumus (Harus urut sesuai labels di atas)
- var codes = ["", "H", "A", "I", "S", "T", "B"];
- // Palette Warna
- var styles = [
- { bg: "#ffffff", text: "#0f515e" }, // Header
- { bg: "#e6f4ea", text: "#137333" }, // Hadir (Hijau)
- { bg: "#fce8e6", text: "#c5221f" }, // Alpa (Merah)
- { bg: "#fef7e0", text: "#b06000" }, // Izin (Kuning)
- { bg: "#e8f0fe", text: "#1967d2" }, // Sakit (Biru)
- { bg: "#fff8e1", text: "#e37400" }, // Terlambat (Oranye)
- { bg: "#fce8e6", text: "#c5221f" } // Bolos (Merah)
- ];
- // 1. CARI POSISI REKAP
- var startRow = -1;
- var lastRowSheet = sheet.getLastRow();
- for (var i = Math.max(CONFIG.DATA_START_ROW, lastRowSheet - 20); i <= lastRowSheet; i++) {
- var val = sheet.getRange(i, 2).getValue();
- if (val == "REKAPITULASI HARIAN") {
- startRow = i;
- break;
- }
- }
- if (startRow === -1) startRow = lastStudentRow + 2;
- // 2. UPDATE LABEL
- for (var k = 0; k < labels.length; k++) {
- var currentRow = startRow + k;
- var cellLabel = sheet.getRange(currentRow, 2);
- cellLabel.setValue(labels[k]);
- if (k === 0) {
- cellLabel.setFontWeight("bold").setFontSize(11)
- .setFontColor("#0f515e").setHorizontalAlignment("left")
- .setBackground(null)
- .setBorder(null, null, true, null, null, null, "#0f515e", SpreadsheetApp.BorderStyle.SOLID_MEDIUM);
- } else {
- cellLabel.setFontWeight("bold").setFontSize(9)
- .setFontColor("#5f6368").setHorizontalAlignment("right").setVerticalAlignment("middle");
- }
- }
- // 3. UPDATE DATA
- var colLetter = columnToLetter(colIndexDate);
- var rangeFormula = colLetter + CONFIG.DATA_START_ROW + ":" + colLetter + lastStudentRow;
- for (var k = 0; k < labels.length; k++) {
- if (k === 0) continue;
- var currentRow = startRow + k;
- var cellData = sheet.getRange(currentRow, colIndexDate);
- var formula = "=COUNTIF(" + rangeFormula + "; \"" + codes[k] + "\")";
- cellData.setFormula(formula)
- .setFontWeight("bold")
- .setHorizontalAlignment("center").setVerticalAlignment("middle")
- .setBackground(styles[k].bg)
- .setFontColor(styles[k].text)
- .setBorder(true, true, true, true, null, null, "#dadce0", SpreadsheetApp.BorderStyle.SOLID);
- }
- }
- function bersihkanAreaSiswa(sheet) {
- var lastRow = sheet.getLastRow();
- var dataB = sheet.getRange(1, 2, lastRow, 1).getValues();
- var limitRow = lastRow;
- for(var i=0; i<dataB.length; i++) {
- if (dataB[i][0] === "REKAPITULASI HARIAN") {
- limitRow = i + 1;
- break;
- }
- }
- for (var r = limitRow - 1; r >= CONFIG.DATA_START_ROW; r--) {
- var nama = sheet.getRange(r, 2).getValue();
- var isGapRow = (r === limitRow - 1);
- if (nama === "" || nama === null || nama.toString().trim() === "") {
- sheet.deleteRow(r);
- limitRow--;
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment