/**
* E-Voting System 2026 - Code.gs
* Backend logic for Google Apps Script
*/
// --- KONFIGURASI NAMA SHEET ---
const DB_CONFIG = "Konfigurasi";
const DB_SISWA = "Data Siswa";
const DB_KANDIDAT = "Data Kandidat";
function doGet() {
setupDatabase(); // Auto-setup saat dibuka
return HtmlService.createTemplateFromFile('index')
.evaluate()
.setTitle('E-Voting OSIS Premium')
.addMetaTag('viewport', 'width=device-width, initial-scale=1')
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
// --- FUNGSI DATABASE AUTO-SETUP ---
function setupDatabase() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
// 1. Setup Sheet Konfigurasi
let sheetConfig = ss.getSheetByName(DB_CONFIG);
if (!sheetConfig) {
sheetConfig = ss.insertSheet(DB_CONFIG);
sheetConfig.appendRow(["Key", "Value"]);
sheetConfig.appendRow(["Nama Sekolah", "SMA Negeri Future"]);
sheetConfig.appendRow(["Status Voting", "BUKA"]);
}
// 2. Setup Sheet Data Siswa
let sheetSiswa = ss.getSheetByName(DB_SISWA);
if (!sheetSiswa) {
sheetSiswa = ss.insertSheet(DB_SISWA);
sheetSiswa.appendRow(["NIS", "Nama", "Kelas", "Status Pemilihan"]);
}
// 3. Setup Sheet Kandidat
let sheetKandidat = ss.getSheetByName(DB_KANDIDAT);
if (!sheetKandidat) {
sheetKandidat = ss.insertSheet(DB_KANDIDAT);
sheetKandidat.appendRow(["ID", "Ketua", "Wakil", "Visi", "Misi", "FotoURL", "Jumlah Suara"]);
}
}
// --- FUNGSI API (Dipanggil dari HTML) ---
function getInitialData() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const config = ss.getSheetByName(DB_CONFIG).getDataRange().getValues();
const sheetKandidat = ss.getSheetByName(DB_KANDIDAT);
// Handle jika sheet kosong
let candidates = [];
if (sheetKandidat.getLastRow() > 1) {
candidates = sheetKandidat.getDataRange().getValues();
}
let candidateList = [];
if (candidates.length > 1) {
for (let i = 1; i < candidates.length; i++) {
candidateList.push({
id: candidates[i][0],
ketua: candidates[i][1],
wakil: candidates[i][2],
visi: candidates[i][3],
misi: candidates[i][4],
foto: candidates[i][5],
suara: candidates[i][6]
});
}
}
let schoolName = "Sekolah";
config.forEach(row => {
if(row[0] === "Nama Sekolah") schoolName = row[1];
});
return { schoolName, candidates: candidateList };
}
function saveSchoolConfig(name) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName(DB_CONFIG);
const data = sheet.getDataRange().getValues();
for(let i=0; i 1 ? sheet.getRange(2, 1, lastRow-1).getValues().flat() : [];
let newRows = [];
let duplicateCount = 0;
studentList.forEach(s => {
// Validasi NIS harus string agar tidak error match
if (!existingNIS.includes(String(s.nis)) && !existingNIS.includes(Number(s.nis))) {
newRows.push([s.nis, s.nama, s.kelas, "BELUM"]);
} else {
duplicateCount++;
}
});
if (newRows.length > 0) {
sheet.getRange(lastRow + 1, 1, newRows.length, 4).setValues(newRows);
}
return { success: true, added: newRows.length, skipped: duplicateCount };
}
// REVISI: Fungsi Tambah Kandidat (Sekarang menerima String URL, bukan upload file)
function addCandidate(data) {
try {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName(DB_KANDIDAT);
const newId = new Date().getTime(); // ID unik pakai timestamp
// Simpan URL langsung dari input user
sheet.appendRow([
newId,
data.namaKetua,
data.namaWakil,
data.visi,
data.misi,
data.fotoUrl, // URL Link
0 // Suara awal 0
]);
return { success: true };
} catch (e) {
return { success: false, error: e.toString() };
}
}
function submitVote(nis, candidateId) {
const lock = LockService.getScriptLock();
try {
// Lock untuk mencegah race condition (vote ganda dalam milidetik yang sama)
lock.waitLock(10000);
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheetSiswa = ss.getSheetByName(DB_SISWA);
const sheetKandidat = ss.getSheetByName(DB_KANDIDAT);
// Cek Siswa
const dataSiswa = sheetSiswa.getDataRange().getValues();
let studentRow = -1;
for (let i = 1; i < dataSiswa.length; i++) {
if (String(dataSiswa[i][0]) === String(nis)) { // Compare as string
studentRow = i + 1;
if (dataSiswa[i][3] === "SUDAH") {
return { success: false, message: "Anda sudah menggunakan hak pilih sebelumnya." };
}
break;
}
}
if (studentRow === -1) {
return { success: false, message: "NIS tidak ditemukan dalam daftar pemilih." };
}
// Update Suara Kandidat
const dataKandidat = sheetKandidat.getDataRange().getValues();
let candidateRow = -1;
for (let i = 1; i < dataKandidat.length; i++) {
// Compare ID
if (String(dataKandidat[i][0]) === String(candidateId)) {
candidateRow = i + 1;
break;
}
}
if (candidateRow !== -1) {
// 1. Tandai Siswa Sudah Memilih
sheetSiswa.getRange(studentRow, 4).setValue("SUDAH");
// 2. Tambah Hitungan Suara
const currentVote = sheetKandidat.getRange(candidateRow, 7).getValue();
sheetKandidat.getRange(candidateRow, 7).setValue(currentVote + 1);
return { success: true, message: "Voting Berhasil! Terima kasih." };
} else {
return { success: false, message: "Data kandidat error." };
}
} catch (e) {
return { success: false, message: "Error: " + e.toString() };
} finally {
lock.releaseLock();
}
}
function resetVotes() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheetSiswa = ss.getSheetByName(DB_SISWA);
const sheetKandidat = ss.getSheetByName(DB_KANDIDAT);
const lastRowSiswa = sheetSiswa.getLastRow();
if(lastRowSiswa > 1) {
sheetSiswa.getRange(2, 4, lastRowSiswa - 1, 1).setValue("BELUM");
}
const lastRowKandidat = sheetKandidat.getLastRow();
if(lastRowKandidat > 1) {
sheetKandidat.getRange(2, 7, lastRowKandidat - 1, 1).setValue(0);
}
return "Sistem berhasil di-reset ke 0.";
}
E-Voting OSIS Premium
Memproses...
E-Voting System
Suara Anda Menentukan Masa Depan
Memuat Kandidat...
Hasil Perolehan Suara
🏫 Konfigurasi Sekolah
👥 Data Pemilih (Siswa)
Format: NIS, Nama, Kelas (Enter per baris)
Posting Komentar untuk " "