Log pesan otomatis yang dikirim saat siswa scan QR atau tindak lanjut
Administrator Control Panel
Manajemen Akun Login Wali Kelas & User (Sheet `Users`)
Kelola akun login untuk masing-masing Wali Kelas (Kelas 1 – 6) maupun Administrator
Nama Guru / Pengguna
Username
Password
Peran (Role)
Akses Kelas
Status
Aksi
Identitas & Parameter Jam Absensi
Atur nama sekolah, kepala sekolah, serta batas jam terlambat otomatis
Manajemen Database & Sinkronisasi
Cadangkan seluruh data ke file JSON atau lihat kode integrasi Google Sheets
Tambah Data Siswa
Tambah Akun Wali Kelas / User
Kartu QR Siswa
Nama Siswa
NISN: -
Kode Backend `Code.gs` (Multi-User & Sheet `Users`)
Otomatis membuat Sheet Users, DataSiswa, dan LogAbsensi di Google Spreadsheet
💡 Struktur Sheet `Users` Otomatis:
Saat disinkronkan atau dijalankan pertama kali, fungsi ensureDefaultSheets() di bawah ini otomatis membuat tab sheet Users dengan kolom: ID | Username | Password | NamaLengkap | Role | Kelas | Status.
// =========================================================================
// CODE.GS - BACKEND GOOGLE APPS SCRIPT ABSENSI QR, WA GATEWAY & SHEET USERS
// =========================================================================
function doGet() {
ensureDefaultSheets();
return HtmlService.createHtmlOutputFromFile('Index')
.setTitle('Absensi QR — WA Gateway | Sistem Absensi Siswa')
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL)
.addMetaTag('viewport', 'width=device-width, initial-scale=1');
}
function doPost(e) {
var payload = JSON.parse(e.postData.contents);
if (payload.action === 'LOGIN') {
return ContentService.createTextOutput(JSON.stringify(authenticateUserFromSheet(payload.username, payload.password)))
.setMimeType(ContentService.MimeType.JSON);
}
return ContentService.createTextOutput(JSON.stringify(syncAppStateToSheets(JSON.stringify(payload))))
.setMimeType(ContentService.MimeType.JSON);
}
// Otomatis membuat Sheet Users, DataSiswa, dan LogAbsensi jika belum ada
function ensureDefaultSheets() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var userSheet = ss.getSheetByName('Users');
if (!userSheet) {
userSheet = ss.insertSheet('Users');
userSheet.appendRow(['ID', 'Username', 'Password', 'NamaLengkap', 'Role', 'Kelas', 'Status']);
userSheet.appendRow(['U01', 'admin', 'admin123', 'Administrator Sekolah', 'ADMIN', 'ALL', 'Aktif']);
userSheet.appendRow(['U02', 'walikelas1', 'kelas1', 'Ibu Siti Rahma, S.Pd.', 'WALIKELAS', 'Kelas 1', 'Aktif']);
userSheet.appendRow(['U03', 'walikelas2', 'kelas2', 'Ibu Rahmawati, S.Pd.', 'WALIKELAS', 'Kelas 2', 'Aktif']);
userSheet.appendRow(['U04', 'walikelas3', 'kelas3', 'Bpk. Hendra Wijaya, S.Pd.', 'WALIKELAS', 'Kelas 3', 'Aktif']);
userSheet.appendRow(['U05', 'walikelas4', 'kelas4', 'Ibu Dewi Sartika, S.Pd.', 'WALIKELAS', 'Kelas 4', 'Aktif']);
userSheet.appendRow(['U06', 'walikelas5', 'kelas5', 'Bpk. Ahmad Fauzi, S.Pd.', 'WALIKELAS', 'Kelas 5', 'Aktif']);
userSheet.appendRow(['U07', 'walikelas6', 'kelas6', 'Ibu Hj. Nurhayati, S.Pd.', 'WALIKELAS', 'Kelas 6', 'Aktif']);
}
if (!ss.getSheetByName('DataSiswa')) {
var sSheet = ss.insertSheet('DataSiswa');
sSheet.appendRow(['ID', 'NISN', 'Nama', 'Kelas', 'Gender', 'OrangTua', 'NoWA']);
}
if (!ss.getSheetByName('LogAbsensi')) {
var lSheet = ss.insertSheet('LogAbsensi');
lSheet.appendRow(['ID', 'Tanggal', 'Waktu', 'NISN', 'Nama', 'Kelas', 'Tipe', 'Status', 'Keterangan']);
}
}
// Validasi Login User langsung dari Sheet Users
function authenticateUserFromSheet(username, password) {
ensureDefaultSheets();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var rows = ss.getSheetByName('Users').getDataRange().getValues();
for (var i = 1; i < rows.length; i++) {
var u = String(rows[i][1]).trim().toLowerCase();
var p = String(rows[i][2]).trim();
var status = String(rows[i][6]).trim();
if (u === String(username).trim().toLowerCase() && p === String(password).trim()) {
if (status !== 'Aktif') return { ok: false, message: 'Akun sedang dinonaktifkan oleh Administrator.' };
return {
ok: true,
user: {
id: rows[i][0],
username: rows[i][1],
fullName: rows[i][3],
role: rows[i][4],
kelas: rows[i][5],
status: rows[i][6]
}
};
}
}
return { ok: false, message: 'Username atau kata sandi tidak ditemukan di Sheet Users.' };
}
// Sinkronisasi seluruh state aplikasi (Users, DataSiswa, LogAbsensi) ke Google Sheets
function syncAppStateToSheets(payloadJson) {
ensureDefaultSheets();
var data = JSON.parse(payloadJson);
var ss = SpreadsheetApp.getActiveSpreadsheet();
if (data.users && data.users.length) {
var userSheet = ss.getSheetByName('Users');
userSheet.clearContents();
userSheet.appendRow(['ID', 'Username', 'Password', 'NamaLengkap', 'Role', 'Kelas', 'Status']);
data.users.forEach(function(u) {
userSheet.appendRow([u.id, u.username, u.password, u.fullName, u.role, u.kelas, u.status]);
});
}
if (data.students && data.students.length) {
var siswaSheet = ss.getSheetByName('DataSiswa');
siswaSheet.clearContents();
siswaSheet.appendRow(['ID', 'NISN', 'Nama', 'Kelas', 'Gender', 'OrangTua', 'NoWA']);
data.students.forEach(function(s) {
siswaSheet.appendRow([s.id, s.nisn, s.nama, s.kelas, s.gender, s.ortu, s.phone]);
});
}
var logSheet = ss.getSheetByName('LogAbsensi');
logSheet.clearContents();
logSheet.appendRow(['ID', 'Tanggal', 'Waktu', 'NISN', 'Nama', 'Kelas', 'Tipe', 'Status', 'Keterangan']);
(data.attendance || []).forEach(function(r) {
logSheet.appendRow([r.id, r.date, r.time, r.nisn, r.nama, r.kelas, r.type, r.status, r.note || '']);
});
return { ok: true, syncedAt: new Date().toISOString() };
}
function sendWaViaFonnte(target, message, token) {
var res = UrlFetchApp.fetch('https://api.fonnte.com/send', {
method: 'post',
headers: { Authorization: token },
payload: { target: target, message: message }
});
return res.getContentText();
}