function doGet(e) {
var lock = LockService.getScriptLock();
lock.tryLock(10000);
try {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var params = (e && e.parameter) ? e.parameter : {};
var action = params.action || "";
// 1. Baca Data Tamu
var shGuests = ss.getSheetByName("Daftar_Tamu");
var guests = [];
if (shGuests && shGuests.getLastRow() > 1) {
var gVals = shGuests.getRange(2, 1, shGuests.getLastRow() - 1, 11).getValues();
guests = gVals.filter(function(r) { return String(r[0]).trim() !== ""; }).map(function(r) {
return {
id: String(r[0]).trim(),
name: String(r[1]).trim(),
category: String(r[2]).trim() || "Keluarga",
phone: String(r[3]).replace(/^'/, "").trim(),
pax: Number(r[4]) || 1,
rsvp: String(r[5]).trim() || "Belum Konfirmasi",
checkedIn: String(r[6]).trim().toUpperCase() === "SUDAH HADIR",
checkInTime: String(r[7]).trim() || "-",
waSent: String(r[8]).trim().toLowerCase() === "terkirim",
wish: String(r[9] || "").trim(),
uniqueUrl: String(r[10] || "").trim()
};
});
}
// 2. Baca Data Pengguna
var shUsers = ss.getSheetByName("Data_Pengguna");
var appUsers = [];
if (shUsers && shUsers.getLastRow() > 1) {
var uVals = shUsers.getRange(2, 1, shUsers.getLastRow() - 1, 7).getValues();
appUsers = uVals.filter(function(r) { return String(r[0]).trim() !== ""; }).map(function(r) {
return {
id: String(r[0]).trim(),
name: String(r[1]).trim(),
username: String(r[2]).trim(),
role: String(r[3]).trim() || "Panitia Check-In",
pin: String(r[4]).replace(/^'/, "").trim(),
phone: String(r[5]).replace(/^'/, "").trim(),
active: String(r[6]).trim().toUpperCase() === "AKTIF"
};
});
}
// 3. Baca Pengaturan Acara & Foto Anak
var shEvent = ss.getSheetByName("Pengaturan_Acara");
var eventData = {
babyName: "Muhammad Zayn Al-Fatih",
birthInfo: "Lahir pada: Jumat, 6 November 2026 • Pukul 08.15 WIB",
parents: "Bpk. Ahmad Fauzi & Ibu Siti Aminah",
eventDate: "Minggu, 15 November 2026",
eventTime: "Pukul 09.00 - 12.00 WIB",
venue: "Kediaman Keluarga Bpk. Ahmad Fauzi",
address: "Jl. Ketintang Baru No. 45, Gayungan, Surabaya",
publicBaseUrl: "",
babyPhoto: ""
};
if (shEvent && shEvent.getLastRow() > 1) {
var sVals = shEvent.getRange(2, 1, shEvent.getLastRow() - 1, 2).getValues();
var mapP = {};
sVals.forEach(function(r) { mapP[String(r[0]).trim()] = String(r[1] || "").trim(); });
if (mapP["Nama Bayi"]) eventData.babyName = mapP["Nama Bayi"];
if (mapP["Info Kelahiran"]) eventData.birthInfo = mapP["Info Kelahiran"];
if (mapP["Nama Orang Tua"]) eventData.parents = mapP["Nama Orang Tua"];
if (mapP["Tanggal Acara"]) eventData.eventDate = mapP["Tanggal Acara"];
if (mapP["Waktu Acara"]) eventData.eventTime = mapP["Waktu Acara"];
if (mapP["Tempat / Venue"]) eventData.venue = mapP["Tempat / Venue"];
if (mapP["Alamat Lengkap"]) eventData.address = mapP["Alamat Lengkap"];
if (mapP["Domain Web Online"]) eventData.publicBaseUrl = mapP["Domain Web Online"];
var fullPhoto = (mapP["Data Foto Anak"] || "") + (mapP["Data Foto Anak (Bagian 2)"] || "");
if (fullPhoto.indexOf("data:image") === 0) eventData.babyPhoto = fullPhoto;
}
// Jika dibuka oleh Tamu dari Link WA (tanpa action=GET_ALL), tampilkan Halaman Undangan Online
if (action !== "GET_ALL" && (params.code || params.to || params.inv)) {
var codeP = String(params.code || "AQQ-001").trim();
var guest = guests.find(function(g) { return g.id.toUpperCase() === codeP.toUpperCase(); }) || {
id: codeP,
name: params.to || "Tamu Kehormatan",
category: params.cat || "Tamu Kehormatan",
pax: Number(params.pax) || 2,
rsvp: "Hadir",
wish: ""
};
var scriptUrl = ScriptApp.getService().getUrl();
var html = '<!DOCTYPE html><html lang="id"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0">'
+ '<title>Undangan Walimatul Aqiqoh - ' + eventData.babyName + '</title>'
+ '<script src="https://cdn.tailwindcss.com"></script>'
+ '<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@600;700&family=Plus+Jakarta+Sans:wght@400;600;700&family=Amiri:wght@400;700&display=swap" rel="stylesheet">'
+ '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">'
+ '<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"></script>'
+ '<style>body{font-family:"Plus Jakarta Sans",sans-serif;background:#fdfbf7}.font-serif-title{font-family:"Cormorant Garamond",serif}.font-arabic{font-family:"Amiri",serif}</style></head>'
+ '<body class="text-slate-800">'
+ '<div id="cover" class="fixed inset-0 z-50 bg-[#043927] flex items-center justify-center p-5 text-center text-white transition-all duration-700">'
+ '<div class="max-w-md w-full bg-[#022c22]/90 border border-[#d4af37]/40 rounded-3xl p-8 shadow-2xl">'
+ '<p class="text-xs uppercase tracking-[0.25em] text-[#f3e5ab] mb-3">Walimatul Aqiqoh</p>'
+ '<div class="w-24 h-24 mx-auto mb-4 rounded-full border-2 border-[#d4af37] p-1 bg-[#043927] flex items-center justify-center overflow-hidden">'
+ (eventData.babyPhoto ? '<img src="' + eventData.babyPhoto + '" class="w-full h-full object-cover rounded-full">' : '<i class="fa-solid fa-baby text-4xl text-[#f3e5ab]"></i>')
+ '</div>'
+ '<h1 class="font-serif-title text-3xl font-bold text-[#f3e5ab] mb-1">' + eventData.babyName + '</h1>'
+ '<p class="text-xs text-emerald-100/80 mb-6">Putra dari ' + eventData.parents + '</p>'
+ '<div class="bg-[#043927] border border-[#d4af37]/30 rounded-2xl p-4 mb-6">'
+ '<p class="text-xs text-emerald-200 mb-1">Kepada Yth. Bapak/Ibu/Saudara/i:</p>'
+ '<h2 class="font-serif-title text-2xl font-bold text-white">' + guest.name + '</h2>'
+ '<p class="text-xs text-[#f3e5ab] mt-1">' + guest.category + ' • Berlaku ' + guest.pax + ' Orang</p></div>'
+ '<button onclick="document.getElementById(\'cover\').style.display=\'none\'" class="w-full py-3.5 px-6 rounded-xl bg-[#d4af37] text-[#022c22] font-bold text-sm shadow-lg">Buka Undangan</button>'
+ '</div></div>'
+ '<main class="max-w-2xl mx-auto px-4 py-8 space-y-6">'
+ '<section class="bg-white rounded-3xl p-6 sm:p-10 shadow-xl border border-[#d4af37]/30 text-center">'
+ '<p class="font-arabic text-2xl text-[#043927] mb-2">بِسْمِ اللّٰهِ الرَّحْمٰنِ الرَّحِيْمِ</p>'
+ '<p class="text-xs font-bold uppercase tracking-widest text-[#aa8c2c] mb-3">Assalamu\'alaikum Warahmatullahi Wabarakatuh</p>'
+ '<div class="w-32 h-32 mx-auto my-4 rounded-full p-1.5 bg-[#043927] shadow-lg overflow-hidden flex items-center justify-center">'
+ (eventData.babyPhoto ? '<img src="' + eventData.babyPhoto + '" class="w-full h-full object-cover rounded-full">' : '<i class="fa-solid fa-baby text-5xl text-[#f3e5ab]"></i>')
+ '</div>'
+ '<h2 class="font-serif-title text-3xl sm:text-4xl font-bold text-[#022c22]">' + eventData.babyName + '</h2>'
+ '<p class="text-xs text-[#aa8c2c] font-semibold mt-1 mb-2">' + eventData.birthInfo + '</p>'
+ '<p class="text-xs text-slate-600">Putra dari: <strong>' + eventData.parents + '</strong></p>'
+ '<div class="mt-6 grid sm:grid-cols-2 gap-3 text-left">'
+ '<div class="bg-[#043927] text-white rounded-2xl p-4"><p class="text-xs text-[#f3e5ab]">Hari & Tanggal</p><p class="font-serif-title text-xl font-bold">' + eventData.eventDate + '</p><p class="text-xs text-emerald-200">' + eventData.eventTime + '</p></div>'
+ '<div class="bg-[#043927] text-white rounded-2xl p-4"><p class="text-xs text-[#f3e5ab]">Lokasi Acara</p><p class="font-serif-title text-xl font-bold">' + eventData.venue + '</p><p class="text-xs text-emerald-200">' + eventData.address + '</p></div>'
+ '</div></section>'
+ '<section class="bg-white rounded-3xl p-6 shadow-xl border border-[#d4af37]/30 text-center">'
+ '<h3 class="font-serif-title text-2xl font-bold text-[#022c22] mb-2">Tiket QR Code Check-In</h3>'
+ '<div id="qrcode-box" class="inline-block bg-white p-3 rounded-xl border my-2"></div>'
+ '<p class="font-mono text-xs font-bold text-[#043927]">' + guest.id + ' • ' + guest.name + '</p>'
+ '</section>'
+ '<footer class="text-center text-xs text-slate-500 py-4">CREATED BY ZAINAL ARIFIN • WA. 082132008882 • VER. 4.4.4</footer>'
+ '</main>'
+ '<script>new QRCode(document.getElementById("qrcode-box"),{text:JSON.stringify({event:"AQIQOH",code:"' + guest.id + '",name:"' + guest.name + '"}),width:148,height:148,colorDark:"#043927",colorLight:"#ffffff"});<\/script>'
+ '</body></html>';
return HtmlService.createHtmlOutput(html)
.setTitle("Undangan Walimatul Aqiqoh - " + eventData.babyName)
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
// Jika dipanggil oleh tombol Tarik Data dari Sheet (PULL)
return ContentService.createTextOutput(JSON.stringify({
status: "ok",
guests: guests,
appUsers: appUsers,
eventData: eventData
})).setMimeType(ContentService.MimeType.JSON);
} finally {
lock.releaseLock();
}
}
function doPost(e) {
var lock = LockService.getScriptLock();
lock.tryLock(10000);
try {
var payload = JSON.parse(e.postData.contents);
var ss = SpreadsheetApp.getActiveSpreadsheet();
// 1. OTOMATIS BENTUK SHEET: Daftar_Tamu
var shGuests = ss.getSheetByName("Daftar_Tamu") || ss.insertSheet("Daftar_Tamu");
shGuests.clearContents();
var guestHeaders = [
"Kode_QR", "Nama_Tamu", "Kategori", "No_WhatsApp",
"Kuota_Pax", "Status_RSVP", "Status_CheckIn",
"Waktu_CheckIn", "WA_Terkirim", "Ucapan_Doa", "Link_Unik"
];
shGuests.getRange(1, 1, 1, guestHeaders.length).setValues([guestHeaders])
.setBackground("#043927").setFontColor("#f3e5ab").setFontWeight("bold");
if (payload.guests && payload.guests.length > 0) {
var rows = payload.guests.map(function(g) {
return [
g.id, g.name, g.category, "'" + g.phone,
g.pax, g.rsvp, g.checkedIn ? "SUDAH HADIR" : "BELUM HADIR",
g.checkInTime || "-", g.waSent ? "Terkirim" : "Belum",
g.wish || "", g.uniqueUrl || ""
];
});
shGuests.getRange(2, 1, rows.length, guestHeaders.length).setValues(rows);
}
// 2. OTOMATIS BENTUK SHEET: Log_CheckIn
var shLog = ss.getSheetByName("Log_CheckIn") || ss.insertSheet("Log_CheckIn");
shLog.clearContents();
var logHeaders = ["No", "Waktu_CheckIn", "Kode_QR", "Nama_Tamu", "Kategori", "Jumlah_Pax", "Status"];
shLog.getRange(1, 1, 1, logHeaders.length).setValues([logHeaders])
.setBackground("#064e3b").setFontColor("#ffffff").setFontWeight("bold");
var presentGuests = (payload.guests || []).filter(function(g) { return g.checkedIn; });
if (presentGuests.length > 0) {
var logRows = presentGuests.map(function(g, idx) {
return [idx + 1, g.checkInTime, g.id, g.name, g.category, g.pax, "Terverifikasi QR"];
});
shLog.getRange(2, 1, logRows.length, logHeaders.length).setValues(logRows);
}
// 3. OTOMATIS BENTUK SHEET: Pengaturan_Acara (Termasuk Data Foto Anak)
var shEvent = ss.getSheetByName("Pengaturan_Acara") || ss.insertSheet("Pengaturan_Acara");
shEvent.clearContents();
shEvent.getRange(1, 1, 1, 2).setValues([["Parameter_Menu", "Nilai_Pengaturan"]])
.setBackground("#043927").setFontColor("#f3e5ab").setFontWeight("bold");
var ev = payload.eventData || {};
var rawPhoto = String(ev.babyPhoto || "");
var photoChunk1 = rawPhoto.substring(0, 45000);
var photoChunk2 = rawPhoto.length > 45000 ? rawPhoto.substring(45000, 90000) : "";
var evRows = [
["Nama Bayi", ev.babyName || ""],
["Info Kelahiran", ev.birthInfo || ""],
["Nama Orang Tua", ev.parents || ""],
["Tanggal Acara", ev.eventDate || ""],
["Waktu Acara", ev.eventTime || ""],
["Tempat / Venue", ev.venue || ""],
["Alamat Lengkap", ev.address || ""],
["Domain Web Online", ev.publicBaseUrl || ""],
["Status Foto Anak", rawPhoto ? "Foto Tersimpan di Sheet" : "Default Ilustrasi"],
["Data Foto Anak", photoChunk1],
["Data Foto Anak (Bagian 2)", photoChunk2],
["Terakhir Disinkronkan", new Date().toLocaleString("id-ID")]
];
shEvent.getRange(2, 1, evRows.length, 2).setValues(evRows);
// 4. OTOMATIS BENTUK SHEET: Data_Pengguna (Manajemen Pengguna & Wewenang)
var shUsers = ss.getSheetByName("Data_Pengguna") || ss.insertSheet("Data_Pengguna");
shUsers.clearContents();
var userHeaders = [
"ID_User", "Nama_Lengkap", "Username", "Wewenang_Role",
"PIN_Akses", "No_WhatsApp", "Status_Aktif"
];
shUsers.getRange(1, 1, 1, userHeaders.length).setValues([userHeaders])
.setBackground("#0c4a6e").setFontColor("#ffffff").setFontWeight("bold");
if (payload.appUsers && payload.appUsers.length > 0) {
var userRows = payload.appUsers.map(function(u) {
return [
u.id, u.name, u.username, u.role,
"'" + u.pin, "'" + (u.phone || "-"),
u.active ? "AKTIF" : "STANDBY"
];
});
shUsers.getRange(2, 1, userRows.length, userHeaders.length).setValues(userRows);
}
return ContentService.createTextOutput(JSON.stringify({
status: "ok",
guestsCount: (payload.guests || []).length,
usersCount: (payload.appUsers || []).length
})).setMimeType(ContentService.MimeType.JSON);
} finally {
lock.releaseLock();
}
}