๐ฆ My APAR
Sistem Pemeliharaan & Checklist Bulanan Real-Time
๐ Scan Barcode / QR Alat
Arahkan kamera ke stiker QR APAR. Data otomatis muncul dan siap diisi checklist kondisi fisik.
๐ Riwayat Pemeriksaan
๐ Ringkasan Progres
Progres Per Gedung
๐พ Ekspor & Database Cloud
โ Tambah Akun Petugas Baru
Buat akun untuk petugas lapangan baru atau administrator K3. Akun langsung otomatis aktif dan dapat digunakan login.
โ๏ธ Sinkronisasi Firebase Firestore
Langkah Setting Rules di Firebase Console (Hanya 1x):
- Buka console.firebase.google.com > Pilih project apar-fss.
- Di menu kiri: Build > Firestore Database. (Jika belum ada, klik Create database dulu).
- Buka tab Rules (Aturan) di bagian atas.
- Hapus aturan lama, lalu tempel (*paste*) aturan berikut:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /apar_log/{document=**} {
allow read, write: if true;
}
match /users/{document=**} {
allow read, write: if true;
}
match /{document=**} {
allow read, write: if true;
}
}
}
โ ๏ธ Setelah di-paste, klik tombol Publish (Publikasikan) di kanan atas. Tunggu 30 detik lalu coba sinkronkan kembali.
๐ Hubungkan Google Sheets & Drive
Hubungkan aplikasi APAR ke Google Sheets & Google Drive agar laporan inspeksi dan foto eviden otomatis tersimpan ke Cloud Google milik Anda.
Langkah-Langkah Pembuatan di Google Sheets:
- Buka Google Sheets baru di browser.
- Di menu atas, pilih Extensions (Ekstensi) > Apps Script.
- Hapus semua kode lama, lalu paste (tempel) kode di bawah ini:
/**
* =========================================================================
* GOOGLE APPS SCRIPT โ SISTEM PEMELIHARAAN APAR & FSS K3
* Auto-Sync Real-Time, Foto Eviden Otomatis di Sel, & Manajemen Data
* =========================================================================
*/
function doGet(e) {
return handleRequest(e);
}
function doPost(e) {
return handleRequest(e);
}
function handleRequest(e) {
try {
var data = {};
if (e && e.postData && e.postData.contents) {
try {
data = JSON.parse(e.postData.contents);
} catch (jsonErr) {
try {
data = JSON.parse(decodeURIComponent(e.postData.contents));
} catch (e2) {
data = e.parameter || {};
}
}
} else if (e && e.parameter) {
data = e.parameter;
}
if (data && data.payload) {
if (typeof data.payload === "string") {
try { data = JSON.parse(data.payload); } catch(e3) {}
} else if (typeof data.payload === "object") {
data = data.payload;
}
}
var ss = null;
try {
ss = SpreadsheetApp.getActiveSpreadsheet();
} catch (eSs) {}
if (!ss && data.spreadsheetId) {
try {
ss = SpreadsheetApp.openById(data.spreadsheetId);
} catch (eId) {}
}
if (!ss) {
return ContentService.createTextOutput(JSON.stringify({
success: false,
error: "Spreadsheet tidak terdeteksi. Buka menu Extensions > Apps Script langsung di dalam Google Sheets Anda!"
})).setMimeType(ContentService.MimeType.JSON);
}
// Response for simple test GET / ping
if (!data || Object.keys(data).length === 0 || data.action === "ping") {
return ContentService.createTextOutput(JSON.stringify({
success: true,
message: "Google Apps Script Web App APAR K3 aktif & terhubung! ๐ฆ",
spreadsheetName: ss.getName(),
spreadsheetUrl: ss.getUrl(),
timestamp: new Date().toISOString()
})).setMimeType(ContentService.MimeType.JSON);
}
var sheet = ss.getSheetByName("Riwayat_APAR") || ss.getSheets()[0];
if (sheet.getName() === "Sheet1" || sheet.getName() === "Lembar1") {
try { sheet.setName("Riwayat_APAR"); } catch(eName) {}
}
// Inisialisasi Header Kolom jika sheet masih kosong
if (sheet.getLastRow() === 0) {
sheet.appendRow([
"Waktu", "Kode APAR", "Gedung", "Ruangan", "Lantai",
"Merk", "Kapasitas", "Gas", "Kebersihan", "Tekanan",
"Pin", "Selang", "Nozzle", "Tag", "Status",
"Keterangan", "Foto Eviden (Langsung di Sel)", "Pemeriksa"
]);
sheet.getRange(1, 1, 1, 18).setFontWeight("bold").setBackground("#0f172a").setFontColor("#ffffff");
try { sheet.setFrozenRows(1); } catch(eF) {}
try { sheet.setColumnWidth(17, 120); } catch(eW) {}
}
function toSafeCell(val) {
if (val === null || val === undefined || val === "") return "-";
if (val === "good") return "Baik / Sesuai";
if (val === "bad") return "Rusak / Tidak Sesuai";
return String(val).trim();
}
// Helper: Upload Base64 Photo to Google Drive and return Formula =IMAGE(...)
function processPhoto(photoData, kodeLabel, idx) {
if (!photoData || typeof photoData !== "string" || photoData.indexOf("base64,") === -1) {
if (photoData && String(photoData).indexOf("http") === 0) {
return '=IMAGE("' + photoData + '", 1)';
}
return "-";
}
try {
var folderName = "Foto_Eviden_APAR";
var folders = DriveApp.getFoldersByName(folderName);
var folder = folders.hasNext() ? folders.next() : DriveApp.createFolder(folderName);
var base64Content = photoData.split("base64,")[1];
var fileName = "EVIDEN_" + (kodeLabel || "APAR") + "_" + Date.now() + (idx !== undefined ? ("_" + idx) : "") + ".jpg";
var blob = Utilities.newBlob(Utilities.base64Decode(base64Content), "image/jpeg", fileName);
var file = folder.createFile(blob);
file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
var fileId = file.getId();
return '=IMAGE("https://lh3.googleusercontent.com/d/' + fileId + '", 1)';
} catch (eImg) {
return "Err Foto: " + eImg.toString();
}
}
// 1. FITUR SINKRONISASI MASSAL (sync_all / items)
if (data.action === "sync_all" || Array.isArray(data.items)) {
var items = data.items || [];
if (!Array.isArray(items) && typeof items === "object") {
var temp = [];
for (var k in items) temp.push(items[k]);
items = temp;
}
var lastRow = sheet.getLastRow();
if (lastRow > 1) {
sheet.getRange(2, 1, lastRow - 1, 18).clearContent();
}
var rowsToAdd = [];
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (!item) continue;
var photoFormula = processPhoto(item.foto || item.photoUrl, item.kode || item.id, i);
rowsToAdd.push([
toSafeCell(item.tanggal || item.tgl || new Date().toLocaleString("id-ID")),
toSafeCell(item.kode || item.id),
toSafeCell(item.gedung),
toSafeCell(item.ruangan),
toSafeCell(item.lantai),
toSafeCell(item.merk),
toSafeCell(item.kapasitas),
toSafeCell(item.jenisGas),
toSafeCell(item.kebersihanTabung || item.kebersihan),
toSafeCell(item.indikatorTekanan || item.tekanan),
toSafeCell(item.kunciPengaman || item.pin),
toSafeCell(item.selangSemprot || item.selang),
toSafeCell(item.nozzle),
toSafeCell(item.tagLabel || item.tag),
toSafeCell(item.status || "OK"),
toSafeCell(item.keterangan),
photoFormula,
toSafeCell(item.pemeriksa || "Petugas")
]);
}
if (rowsToAdd.length > 0) {
sheet.getRange(2, 1, rowsToAdd.length, 18).setValues(rowsToAdd);
try {
sheet.setColumnWidth(17, 120);
for (var rIdx = 0; rIdx < rowsToAdd.length; rIdx++) {
sheet.setRowHeight(rIdx + 2, 85);
}
} catch(eH) {}
}
return ContentService.createTextOutput(JSON.stringify({
success: true,
message: "Berhasil sinkronisasi massal " + rowsToAdd.length + " data inspeksi ke Google Sheets & Drive!",
totalSynced: rowsToAdd.length,
spreadsheetUrl: ss.getUrl()
})).setMimeType(ContentService.MimeType.JSON);
}
// 2. FITUR HAPUS DATA (delete)
if (data.action === "delete") {
var targetKode = String(data.kode || data.id || "").trim().toLowerCase();
if (targetKode) {
var rows = sheet.getDataRange().getValues();
var deletedCount = 0;
for (var d = rows.length - 1; d >= 1; d--) {
var rowKode = String(rows[d][1] || "").trim().toLowerCase();
if (rowKode === targetKode) {
sheet.deleteRow(d + 1);
deletedCount++;
}
}
return ContentService.createTextOutput(JSON.stringify({
success: true,
message: "Data APAR Kode " + targetKode + " berhasil dihapus dari Google Sheets!",
deletedCount: deletedCount
})).setMimeType(ContentService.MimeType.JSON);
}
}
// 3. SIMPAN / UPDATE REAL-TIME TUNGGAL (UPSERT)
var photoCell = processPhoto(data.foto || data.photoUrl, data.kode || data.id);
var targetKodeNew = String(data.kode || data.id || "").trim().toLowerCase();
var existingRowIndex = -1;
if (targetKodeNew && targetKodeNew !== "test_koneksi" && targetKodeNew !== "test-apar-01") {
var allRows = sheet.getDataRange().getValues();
for (var r = 1; r < allRows.length; r++) {
if (String(allRows[r][1] || "").trim().toLowerCase() === targetKodeNew) {
existingRowIndex = r + 1;
break;
}
}
}
var rowValues = [
toSafeCell(data.tanggal || data.tgl || new Date().toLocaleString("id-ID")),
toSafeCell(data.kode || data.id),
toSafeCell(data.gedung),
toSafeCell(data.ruangan),
toSafeCell(data.lantai),
toSafeCell(data.merk),
toSafeCell(data.kapasitas),
toSafeCell(data.jenisGas),
toSafeCell(data.kebersihanTabung || data.kebersihan),
toSafeCell(data.indikatorTekanan || data.tekanan),
toSafeCell(data.kunciPengaman || data.pin),
toSafeCell(data.selangSemprot || data.selang),
toSafeCell(data.nozzle),
toSafeCell(data.tagLabel || data.tag),
toSafeCell(data.status || "OK"),
toSafeCell(data.keterangan),
photoCell,
toSafeCell(data.pemeriksa || "Petugas")
];
var targetRow = (existingRowIndex > 0) ? existingRowIndex : sheet.getLastRow() + 1;
if (existingRowIndex > 0) {
sheet.getRange(existingRowIndex, 1, 1, 18).setValues([rowValues]);
} else {
sheet.appendRow(rowValues);
}
try {
sheet.setRowHeight(targetRow, 85);
sheet.setColumnWidth(17, 120);
} catch(eDim) {}
return ContentService.createTextOutput(JSON.stringify({
success: true,
message: "Data APAR Kode " + (data.kode || data.id || "") + " berhasil dicatat dan foto ditampilkan langsung di sel Google Sheets!",
spreadsheetUrl: ss.getUrl()
})).setMimeType(ContentService.MimeType.JSON);
} catch (err) {
return ContentService.createTextOutput(JSON.stringify({
success: false,
error: err.toString()
})).setMimeType(ContentService.MimeType.JSON);
}
}
- Klik tombol Deploy (Terapkan) di kanan atas > pilih New deployment.
- Pilih jenis: Web App.
- Ubah Who has access (Siapa saja yang memiliki akses) ke Anyone (Siapa Saja).
- Klik Deploy, copy Web App URL lalu simpan di kolom atas!
๐ Kirim Massal Data ke Google Sheets
Kirim seluruh data hasil pemeriksaan sekaligus ke Google Sheets agar isi Sheet 100% mengikuti progres aplikasi.
๐ฅ Daftar Akun Terdaftar
0 AkunSemua akun yang memiliki hak akses untuk masuk ke sistem inspeksi APAR.