SMP FAJAR, SMK FAJAR dan SMK MUTIARA INSANI
1WSr7jCeBuaEbMOQwU2Jl7e1uuJwlSABFRs4AnoEDQqUhttps://script.google.com/macros/s/AKfycbx3OiMI91HVfuThne4XmmMK0dE58uVP97UMrszf51bMkPUwz_U7kJFrAt5AJBF96Nlz/exec// Google Apps Script untuk Data Pegawai Real-time - VERSI TERBARU
// URL: https://script.google.com/macros/s/AKfycbx3OiMI91HVfuThne4XmmMK0dE58uVP97UMrszf51bMkPUwz_U7kJFrAt5AJBF96Nlz/exec
// Sheet ID: 1WSr7jCeBuaEbMOQwU2Jl7e1uuJwlSABFRs4AnoEDQqU
// Real-time sync dengan auto-refresh setiap 45 detik
function doGet(e) {
return handleRequest(e);
}
function doPost(e) {
return handleRequest(e);
}
function handleRequest(e) {
try {
// SHEET ID PERMANENT - JANGAN DIUBAH
const SHEET_ID = '1WSr7jCeBuaEbMOQwU2Jl7e1uuJwlSABFRs4AnoEDQqU';
// Log request untuk debugging
console.log('π₯ Request received:', {
method: e ? (e.postData ? 'POST' : 'GET') : 'UNKNOWN',
parameters: e ? e.parameter : {},
postData: e && e.postData ? e.postData.contents : null,
timestamp: new Date().toISOString()
});
// Buka spreadsheet
const spreadsheet = SpreadsheetApp.openById(SHEET_ID);
let sheet = spreadsheet.getSheetByName('Data Pegawai');
// Jika sheet tidak ada, buat sheet baru dengan header lengkap
if (!sheet) {
console.log('π Creating new sheet: Data Pegawai');
sheet = spreadsheet.insertSheet('Data Pegawai');
// Header lengkap sesuai form aplikasi (tanpa NIP)
const headers = [
'Nama Lengkap', 'NUPTK', 'NIK', 'Tempat Lahir', 'Tanggal Lahir',
'Jenis Kelamin', 'Agama', 'Status Perkawinan', 'Alamat', 'No Telepon', 'Email',
'Sekolah', 'Status Kepegawaian', 'Jenis PTK', 'Pendidikan Terakhir', 'Jurusan',
'Tahun Lulus', 'Bidang Studi Ijazah', 'Status Sertifikasi', 'Nomor Sertifikasi',
'Bidang Studi Sertifikasi', 'NRG', 'TMT Sertifikasi', 'TMT Mengajar',
'Mata Pelajaran', 'SK Mengajar', 'TMT SK Mengajar', 'SK GTY', 'TMT SK GTY',
'SK Tugas Pertama', 'TMT SK Tugas Pertama', 'Tanggal Input', 'Terakhir Update'
];
// Set header dengan formatting yang lebih mewah
const headerRange = sheet.getRange(1, 1, 1, headers.length);
headerRange.setValues([headers]);
headerRange.setFontWeight('bold');
headerRange.setBackground('#0f172a');
headerRange.setFontColor('white');
headerRange.setHorizontalAlignment('center');
headerRange.setFontSize(11);
// Auto resize columns
sheet.autoResizeColumns(1, headers.length);
console.log('β
Sheet created with headers');
// Set up real-time trigger
setupRealTimeTrigger();
}
// Ambil parameter action
let action = 'getData';
let postData = {};
if (e && e.parameter && e.parameter.action) {
action = e.parameter.action;
postData = e.parameter;
} else if (e && e.postData) {
try {
const parsedData = JSON.parse(e.postData.contents);
action = parsedData.action || 'getData';
postData = parsedData;
} catch (parseError) {
console.log('β οΈ Failed to parse POST data, using GET parameters');
action = 'getData';
postData = e.parameter || {};
}
}
console.log('π― Action:', action);
// Handle GET DATA
if (action === 'getData' || action === 'ping') {
const lastRow = sheet.getLastRow();
console.log('π Last row:', lastRow);
if (lastRow <= 1) {
const response = {
status: 'success',
data: [],
count: 0,
message: 'Sheet kosong - siap menerima data baru',
timestamp: new Date().toISOString(),
sheetId: SHEET_ID,
sheetName: sheet.getName()
};
console.log('π€ Sending empty response:', response);
return ContentService
.createTextOutput(JSON.stringify(response))
.setMimeType(ContentService.MimeType.JSON);
}
// Ambil data dari baris 2 sampai terakhir
const dataRange = sheet.getRange(2, 1, lastRow - 1, sheet.getLastColumn());
const values = dataRange.getValues();
const headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
console.log('π Headers:', headers);
console.log('π Data rows:', values.length);
// Konversi ke format JSON
const data = values.map((row, rowIndex) => {
const obj = {};
headers.forEach((header, colIndex) => {
let value = row[colIndex];
// Konversi header ke snake_case dan handle special cases
let key = header.toLowerCase()
.replace(/\s+/g, '_')
.replace(/[^\w]/g, '')
.replace('mata_pelajaran', 'mapel_diampu')
.replace('tanggal_input', 'created_at')
.replace('terakhir_update', 'last_updated');
// Handle special data types
if (header === 'Status Sertifikasi') {
obj['is_sertifikasi'] = (value === 'Sudah Sertifikasi' || value === true || value === 'true');
} else if (header === 'Tahun Lulus') {
obj['tahun_lulus'] = value ? parseInt(value) : 0;
} else if (header.includes('Tanggal') || header.includes('TMT')) {
// Handle date fields
if (value instanceof Date) {
obj[key] = value.toISOString().split('T')[0];
} else if (value) {
obj[key] = value.toString();
} else {
obj[key] = '';
}
} else {
obj[key] = value ? value.toString() : '';
}
});
// Add unique identifier for Data SDK
obj['__backendId'] = 'gs_' + (rowIndex + 2);
return obj;
});
const response = {
status: 'success',
data: data,
count: data.length,
message: `Berhasil mengambil ${data.length} data guru`,
timestamp: new Date().toISOString(),
sheetId: SHEET_ID,
sheetName: sheet.getName()
};
console.log('π€ Sending data response:', { count: data.length, message: response.message });
return ContentService
.createTextOutput(JSON.stringify(response))
.setMimeType(ContentService.MimeType.JSON);
}
// Handle ADD DATA
else if (action === 'addData') {
console.log('πΎ Adding new data:', postData);
const timestamp = new Date().toISOString();
const dateOnly = timestamp.split('T')[0];
// Validasi data wajib
if (!postData.nama_lengkap || !postData.sekolah) {
const errorResponse = {
status: 'error',
message: 'Nama lengkap dan sekolah wajib diisi',
timestamp: timestamp
};
console.log('β Validation error:', errorResponse);
return ContentService
.createTextOutput(JSON.stringify(errorResponse))
.setMimeType(ContentService.MimeType.JSON);
}
// Susun data sesuai urutan header (tanpa NIP)
const rowData = [
postData.nama_lengkap || '',
postData.nuptk || '',
postData.nik || '',
postData.tempat_lahir || '',
postData.tanggal_lahir || '',
postData.jenis_kelamin || '',
postData.agama || '',
postData.status_perkawinan || '',
postData.alamat || '',
postData.no_telepon || '',
postData.email || '',
postData.sekolah || '',
postData.status_kepegawaian || '',
postData.jenis_ptk || '',
postData.pendidikan_terakhir || '',
postData.jurusan || '',
postData.tahun_lulus || '',
postData.bidang_studi_ijazah || '',
(postData.is_sertifikasi === true || postData.is_sertifikasi === 'true') ? 'Sudah Sertifikasi' : 'Belum Sertifikasi',
postData.nomor_sertifikasi || '',
postData.bidang_studi_sertifikasi || '',
postData.nrg || '',
postData.tmt_sertifikasi || '',
postData.tmt_mengajar || '',
postData.mapel_diampu || '',
postData.sk_mengajar || '',
postData.tmt_sk_mengajar || '',
postData.sk_gty || '',
postData.tmt_sk_gty || '',
postData.sk_tugas_pertama || '',
postData.tmt_sk_tugas_pertama || '',
dateOnly,
timestamp
];
console.log('π Row data prepared:', rowData.length, 'columns');
// Tambahkan data ke sheet
const lastRow = sheet.getLastRow();
const newRowRange = sheet.getRange(lastRow + 1, 1, 1, rowData.length);
newRowRange.setValues([rowData]);
// Format baris baru
if (lastRow % 2 === 0) {
newRowRange.setBackground('#f8fafc');
}
const successResponse = {
status: 'success',
message: `Data ${postData.nama_lengkap} berhasil disimpan ke Google Sheets`,
timestamp: timestamp,
rowNumber: lastRow + 1,
sheetId: SHEET_ID,
sheetName: sheet.getName()
};
console.log('β
Data saved successfully:', successResponse);
return ContentService
.createTextOutput(JSON.stringify(successResponse))
.setMimeType(ContentService.MimeType.JSON);
}
// Action tidak dikenali
const unknownActionResponse = {
status: 'error',
message: 'Action tidak dikenali: ' + action,
availableActions: ['getData', 'addData', 'ping'],
timestamp: new Date().toISOString()
};
console.log('β Unknown action:', unknownActionResponse);
return ContentService
.createTextOutput(JSON.stringify(unknownActionResponse))
.setMimeType(ContentService.MimeType.JSON);
} catch (error) {
// Error handling
const errorResponse = {
status: 'error',
message: 'Server error: ' + error.toString(),
stack: error.stack,
timestamp: new Date().toISOString(),
sheetId: '1WSr7jCeBuaEbMOQwU2Jl7e1uuJwlSABFRs4AnoEDQqU'
};
console.error('π₯ Server error:', errorResponse);
return ContentService
.createTextOutput(JSON.stringify(errorResponse))
.setMimeType(ContentService.MimeType.JSON);
}
}
// Test function untuk debugging
function testConnection() {
const SHEET_ID = '1WSr7jCeBuaEbMOQwU2Jl7e1uuJwlSABFRs4AnoEDQqU';
try {
const spreadsheet = SpreadsheetApp.openById(SHEET_ID);
console.log('β
Koneksi ke spreadsheet berhasil');
console.log('π Nama spreadsheet:', spreadsheet.getName());
console.log('π URL spreadsheet:', spreadsheet.getUrl());
// Test sheet creation
let sheet = spreadsheet.getSheetByName('Data Guru');
if (!sheet) {
console.log('π Sheet "Data Guru" tidak ditemukan, akan dibuat otomatis saat request pertama');
} else {
console.log('π Sheet "Data Guru" sudah ada');
console.log('π Jumlah baris:', sheet.getLastRow());
console.log('π Jumlah kolom:', sheet.getLastColumn());
}
return true;
} catch (error) {
console.log('β Koneksi gagal:', error.toString());
return false;
}
}
// Setup Real-time Trigger untuk auto-sync
function setupRealTimeTrigger() {
try {
// Hapus trigger lama jika ada
const triggers = ScriptApp.getProjectTriggers();
triggers.forEach(trigger => {
if (trigger.getHandlerFunction() === 'onSheetChange') {
ScriptApp.deleteTrigger(trigger);
}
});
// Buat trigger baru untuk perubahan spreadsheet
const SHEET_ID = '1WSr7jCeBuaEbMOQwU2Jl7e1uuJwlSABFRs4AnoEDQqU';
const spreadsheet = SpreadsheetApp.openById(SHEET_ID);
ScriptApp.newTrigger('onSheetChange')
.timeBased()
.everyMinutes(1) // Check setiap menit
.create();
console.log('β
Real-time trigger setup completed');
} catch (error) {
console.error('β Failed to setup trigger:', error);
}
}
// Handler untuk perubahan sheet
function onSheetChange() {
try {
const SHEET_ID = '1WSr7jCeBuaEbMOQwU2Jl7e1uuJwlSABFRs4AnoEDQqU';
const spreadsheet = SpreadsheetApp.openById(SHEET_ID);
const sheet = spreadsheet.getSheetByName('Data Pegawai');
if (sheet) {
// Update timestamp untuk menandai perubahan
const lastRow = sheet.getLastRow();
if (lastRow > 1) {
const lastUpdateCol = sheet.getLastColumn();
sheet.getRange(lastRow, lastUpdateCol).setValue(new Date().toISOString());
}
console.log('π Sheet change detected and processed');
}
} catch (error) {
console.error('β Error in onSheetChange:', error);
}
}
// Function untuk download template Excel (updated tanpa NIP)
function downloadTemplate() {
const headers = [
'Nama Lengkap', 'NUPTK', 'NIK', 'Tempat Lahir', 'Tanggal Lahir',
'Jenis Kelamin', 'Agama', 'Status Perkawinan', 'Alamat', 'No Telepon', 'Email',
'Sekolah', 'Status Kepegawaian', 'Jenis PTK', 'Pendidikan Terakhir', 'Jurusan',
'Tahun Lulus', 'Bidang Studi Ijazah', 'Status Sertifikasi', 'Nomor Sertifikasi',
'Bidang Studi Sertifikasi', 'NRG', 'TMT Sertifikasi', 'TMT Mengajar',
'Mata Pelajaran', 'SK Mengajar', 'TMT SK Mengajar', 'SK GTY', 'TMT SK GTY',
'SK Tugas Pertama', 'TMT SK Tugas Pertama'
];
// Sample data (tanpa NIP)
const sampleData = [
[
'Dr. Siti Nurhaliza, S.Pd., M.Pd.', '1234567890123456', '3201234567890123',
'Jakarta', '1968-01-05', 'Perempuan', 'Islam', 'Kawin', 'Jl. Pendidikan No. 123, Jakarta Selatan',
'081234567890', 'siti.nurhaliza@email.com', 'SMP Fajar', 'PNS', 'Guru Mata Pelajaran',
'S2', 'Pendidikan Matematika', '2015', 'Pendidikan Matematika', 'Sudah Sertifikasi',
'12345678901234567890', 'Matematika', '12345678901234567890', '2018-07-01', '1990-07-01',
'Matematika', 'SK/001/2023', '2023-01-01', '', '', 'SK/001/1990', '1990-07-01'
],
[
'Ahmad Hidayat, S.Pd.', '9876543210987654', '3201987654321098',
'Bandung', '1985-03-15', 'Laki-laki', 'Islam', 'Kawin', 'Jl. Guru No. 456, Bandung',
'081987654321', 'ahmad.hidayat@email.com', 'SMK Fajar', 'GTY', 'Guru Mata Pelajaran',
'S1', 'Teknik Informatika', '2008', 'Teknik Informatika', 'Belum Sertifikasi',
'', '', '', '', '2010-08-01',
'Pemrograman Dasar', 'SK/002/2023', '2023-01-01', 'SK/GTY/001/2010', '2010-08-01', 'SK/002/2010', '2010-08-01'
]
];
return {
headers: headers,
sampleData: sampleData
};
}