Cara Membuat Booking Padel dengan Google Script Apps Gratis
Cara membuat booking padel online gratis tanpa aplikasi berbayar
kini menjadi solusi favorit bagi pengelola lapangan dan komunitas olahraga.
Dengan Google Script Apps, sistem booking padel dapat berjalan otomatis,
real-time, dan terintegrasi langsung dengan Google Sheets.
Menariknya, booking padel dengan Google Script Apps gratis
tidak membutuhkan kemampuan coding tingkat lanjut.
Cukup bermodal akun Google, Anda sudah bisa membuat sistem reservasi
yang rapi, profesional, dan mudah digunakan oleh pelanggan. Anda juga bisa membuat Absensi menggunakan GPS agar lebih aman dan flexible.
Kenapa Booking Padel Menggunakan Google Script Apps?
- Gratis tanpa biaya langganan bulanan
- Mudah dikustomisasi sesuai jam operasional lapangan
- Data booking tersimpan otomatis di Google Sheets
- Aman karena berbasis cloud Google
Alur Sistem Booking Padel
Sistem booking padel berbasis Google Script Apps bekerja dengan alur sederhana:
user mengisi form, data masuk ke spreadsheet, script memvalidasi jadwal,
lalu sistem mengirimkan konfirmasi booking secara otomatis.
Langkah-Langkah Membuat Booking Padel Gratis
1. Membuat Google Form Booking
Google Form digunakan sebagai tampilan utama untuk pelanggan.
Field penting meliputi nama, nomor WhatsApp, tanggal main,
jam bermain, dan pilihan lapangan.
2. Menghubungkan Form ke Google Sheets
Setiap data yang masuk dari Google Form akan otomatis tersimpan
ke Google Sheets dan berfungsi sebagai database booking.
$ads={1}
3. Menambahkan Google Apps Script
Dari Google Sheets, buka menu Extensions lalu Apps Script
untuk menambahkan logika pengecekan jadwal dan validasi booking.
4. Mengatur Notifikasi Otomatis
Sistem dapat mengirim email konfirmasi atau notifikasi WhatsApp
agar pelanggan mengetahui status booking mereka.
Code Booking Padel
Code.gs
/* ===================== CONFIG ===================== */
const SPREADSHEET_ID = '1iT0d6a1Mf-tGquAkUefV7Akr4cLu5ZkPiDNoHRFBidQ';
const SHEET_MASTER = 'MasterLapangan';
const SHEET_MENU = 'Menu';
const SHEET_BOOKINGS = 'Bookings';
const OPEN_TIME = 8 * 60;
const CLOSE_TIME = 23 * 60;
const SLOT_MINUTES = 60;
const PENDING_EXPIRE_MINUTES = 30;
const TEMP_LOCK_MINUTES = 5;
/* ===================== CORE ===================== */
function ss(){ return SpreadsheetApp.openById(SPREADSHEET_ID); }
function sh(n){ return ss().getSheetByName(n); }
function toMin(t){
const [h,m]=t.split(':').map(Number);
return h*60+m;
}
function toTime(m){
return String(Math.floor(m/60)).padStart(2,'0')+':'+String(m%60).padStart(2,'0');
}
/* ===================== MASTER ===================== */
function getMasterLapangan(){
return sh(SHEET_MASTER).getDataRange().getValues().slice(1)
.map(r=>({id:r[0],nama:r[1],harga:r[2]}));
}
function getMenuList(){
return sh(SHEET_MENU).getDataRange().getValues().slice(1)
.map(r=>({id:r[0],nama:r[1],harga:r[2]}));
}
/* ===================== BOOKING DATA ===================== */
function getAllBookings(){
return sh(SHEET_BOOKINGS).getDataRange().getValues().slice(1).map(r=>({
booking_id:r[0],
created:r[1],
nama:r[2],
email:r[3],
telepon:r[4],
tanggal:r[5],
jam_mulai:r[6],
durasi:r[7],
lapangan_id:r[8],
total_menu:r[9],
total_lap:r[10],
total:r[11],
status:r[12],
payment_status:r[15],
checkin_status:r[17]
}));
}
/* ===================== AUTO RELEASE ===================== */
function autoReleaseExpiredBookings(){
const sheet=sh(SHEET_BOOKINGS);
const data=sheet.getDataRange().getValues();
const now=new Date();
for(let i=1;i<data.length;i++){
if(data[i][12]=='pending'){
const diff=(now-new Date(data[i][1]))/60000;
if(diff>PENDING_EXPIRE_MINUTES){
sheet.getRange(i+1,13).setValue('expired');
sheet.getRange(i+1,16).setValue('expired');
}
}
}
}
/* ===================== AVAILABILITY ===================== */
function getHourlyAvailability(lapanganId,tanggal){
autoReleaseExpiredBookings();
const bookings=getAllBookings().filter(b=>
b.lapangan_id==lapanganId &&
b.tanggal==tanggal &&
['pending','approved'].includes(b.status)
);
let result=[];
for(let t=OPEN_TIME;t<CLOSE_TIME;t+=SLOT_MINUTES){
let available=true;
bookings.forEach(b=>{
const s=toMin(b.jam_mulai);
const e=s+(b.durasi*60);
if(t<e && (t+SLOT_MINUTES)>s) available=false;
});
result.push({time:toTime(t),available});
}
return result;
}
/* ===================== BOOKING STEP 1 ===================== */
function createBookingStep1(d){
autoReleaseExpiredBookings();
const slot=getHourlyAvailability(d.lapangan_id,d.tanggal)
.find(s=>s.time==d.jam_mulai);
if(!slot||!slot.available){
return {success:false,message:'Lapangan tidak tersedia'};
}
const lap=getMasterLapangan().find(l=>l.id==d.lapangan_id);
const totalLap=lap.harga*d.durasi_jam;
const id='B'+Date.now();
sh(SHEET_BOOKINGS).appendRow([
id,new Date(),d.nama,d.email,d.telepon,
d.tanggal,d.jam_mulai,d.durasi_jam,d.lapangan_id,
0,totalLap,totalLap,'pending','LOCK',
'', 'unpaid','', 'not_checked',''
]);
return {success:true,bookingId:id};
}
function setPaymentMethod(bookingId, method) {
const sheet = sh(SHEET_BOOKINGS);
const data = sheet.getDataRange().getValues();
for (let i = 1; i < data.length; i++) {
if (data[i][0] == bookingId) {
sheet.getRange(i + 1, 15).setValue(method); // payment_method
sheet.getRange(i + 1, 16).setValue('waiting'); // payment_status
sheet.getRange(i + 1, 14).setValue('Menunggu konfirmasi admin'); // note
return true;
}
}
return false;
}
/* ===================== MENU ORDER ===================== */
function addMenuOrder(bookingId,items){
let total=0;
items.forEach(i=>total+=i.qty*i.harga);
const sheet=sh(SHEET_BOOKINGS);
const data=sheet.getDataRange().getValues();
for(let i=1;i<data.length;i++){
if(data[i][0]==bookingId){
sheet.getRange(i+1,10).setValue(total);
sheet.getRange(i+1,12).setValue(data[i][10]+total);
}
}
}
/* ===================== PAYMENT ===================== */
function adminConfirmPayment(bookingId,method='QRIS'){
const sheet=sh(SHEET_BOOKINGS);
const data=sheet.getDataRange().getValues();
for(let i=1;i<data.length;i++){
if(data[i][0]==bookingId){
sheet.getRange(i+1,13).setValue('approved');
sheet.getRange(i+1,15).setValue(method);
sheet.getRange(i+1,16).setValue('paid');
sendBookingEmail(bookingId);
}
}
}
function adminApproveBooking(bookingId) {
const sheet = sh(SHEET_BOOKINGS);
const data = sheet.getDataRange().getValues();
for (let i = 1; i < data.length; i++) {
if (data[i][0] == bookingId) {
sheet.getRange(i + 1, 13).setValue('approved'); // status
sheet.getRange(i + 1, 16).setValue('paid'); // payment_status
sheet.getRange(i + 1, 14).setValue('Pembayaran dikonfirmasi admin');
// kirim email + QR
sendBookingEmail(bookingId);
return true;
}
}
return false;
}
/* ===================== EMAIL + QR ===================== */
function getBookingQRCode(id){
const url=ScriptApp.getService().getUrl();
return 'https://chart.googleapis.com/chart?chs=300x300&cht=qr&chl='+
encodeURIComponent(url+'?action=checkin&id='+id);
}
function sendBookingEmail(id){
const b=getAllBookings().find(x=>x.booking_id==id);
const qr=getBookingQRCode(id);
MailApp.sendEmail({
to:b.email,
subject:'Booking Padel Approved',
htmlBody:`
<h3>Booking Padel</h3>
<p>Kode: <b>${id}</b></p>
<p>${b.tanggal} ${b.jam_mulai}</p>
<img src="${qr}">
`
});
}
/* ===================== CHECK-IN ===================== */
function doGet(e) {
e = e || {};
e.parameter = e.parameter || {};
// ===== QR CHECK-IN =====
if (e.parameter.action === 'checkin') {
return checkin(e.parameter.id);
}
// ===== PAGE ROUTING =====
const page = e.parameter.page || 'Index';
const tpl = HtmlService.createTemplateFromFile(page);
tpl.baseUrl = ScriptApp.getService().getUrl();
return tpl.evaluate()
.setTitle('Booking Padel')
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
function checkin(id){
const sheet=sh(SHEET_BOOKINGS);
const data=sheet.getDataRange().getValues();
for(let i=1;i<data.length;i++){
if(data[i][0]==id){
if(data[i][16]!='paid')
return HtmlService.createHtmlOutput('❌ Belum bayar');
if(data[i][17]=='checked')
return HtmlService.createHtmlOutput('⚠️ Sudah check-in');
sheet.getRange(i+1,18).setValue('checked');
sheet.getRange(i+1,19).setValue(new Date());
return HtmlService.createHtmlOutput('✅ Check-in berhasil');
}
}
return HtmlService.createHtmlOutput('❌ Booking tidak ditemukan');
}
Index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<title>Booking Padel</title>
</head>
<body>
<h2>🏓 Sistem Booking Padel</h2>
<ul>
<li><a href="<?= baseUrl ?>?page=Pelanggan">Booking Lapangan</a></li>
<li><a href="<?= baseUrl ?>?page=Checkin">Check-in</a></li>
<li><a href="<?= baseUrl ?>?page=Admin">Admin</a></li>
<li><a href="<?= baseUrl ?>?page=Pemilik">Pemilik</a></li>
</ul>
</body>
</html>
Admin.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<h3>🛠 Admin – Konfirmasi Pembayaran</h3>
<input id="bookingId" placeholder="Masukkan Booking ID">
<br><br>
<button onclick="approve()">Approve Pembayaran</button>
<script>
function approve(){
if(!bookingId.value){
alert('Booking ID wajib diisi');
return;
}
google.script.run.withSuccessHandler(()=>{
alert('Pembayaran dikonfirmasi.\nEmail & QR Code telah dikirim ke pelanggan.');
}).adminApproveBooking(bookingId.value);
}
</script>
</body>
</html>
$ads={1}
Pelanggan.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<style>
body { font-family: Arial; padding: 15px; }
h3 { margin-top: 25px; }
input, select, button { margin: 5px 0; padding: 6px; }
table { border-collapse: collapse; }
td { padding: 5px; }
</style>
</head>
<body>
<h2>🎾 Booking Lapangan Padel</h2>
<table>
<tr><td>Nama</td><td><input id="nama"></td></tr>
<tr><td>Email</td><td><input id="email"></td></tr>
<tr><td>No HP</td><td><input id="hp"></td></tr>
<tr><td>Tanggal</td><td><input type="date" id="tanggal"></td></tr>
<tr>
<td>Lapangan</td>
<td>
<select id="lapangan"></select>
</td>
</tr>
<tr>
<td>Jam Mulai</td>
<td>
<select id="jam"></select>
<button onclick="cekJam()">Cek Jam</button>
</td>
</tr>
<tr>
<td>Durasi</td>
<td>
<select id="durasi">
<option value="1">1 Jam</option>
<option value="2">2 Jam</option>
<option value="3">3 Jam</option>
</select>
</td>
</tr>
</table>
<button onclick="booking()">Booking</button>
<hr>
<h3>🍜 Order Menu</h3>
<div id="menu"></div>
<button onclick="simpanMenu()">Simpan Menu</button>
<hr>
<h3>💳 Pembayaran</h3>
<select id="payment">
<option value="">-- Pilih Metode Pembayaran --</option>
<option value="QRIS">QRIS</option>
<option value="Transfer BCA">Transfer BCA</option>
<option value="Transfer Mandiri">Transfer Mandiri</option>
</select>
<br><br>
<button onclick="bayar()">Bayar & Konfirmasi via WhatsApp</button>
<script>
let bookingId = '';
/* ========= INIT ========= */
google.script.run.withSuccessHandler(loadInit).getInitData();
function loadInit(res){
lapangan.innerHTML = res.lapangan.map(l=>`<option>${l}</option>`).join('');
jam.innerHTML = res.jam.map(j=>`<option>${j}</option>`).join('');
let html = '';
res.menu.forEach(m=>{
html += `
<div>
<input type="number" min="0" id="menu_${m.id}" value="0">
${m.nama} (${m.harga})
</div>`;
});
menu.innerHTML = html;
}
/* ========= CEK JAM ========= */
function cekJam(){
google.script.run.withSuccessHandler(res=>{
alert(res ? 'Jam tersedia ✅' : 'Jam sudah dibooking ❌');
}).checkAvailability(
tanggal.value,
lapangan.value,
jam.value,
durasi.value
);
}
/* ========= BOOKING ========= */
function booking(){
if(!nama.value || !email.value || !tanggal.value){
alert('Lengkapi data booking');
return;
}
google.script.run.withSuccessHandler(id=>{
bookingId = id;
alert('Booking berhasil.\nID: ' + id);
}).createBooking({
nama: nama.value,
email: email.value,
hp: hp.value,
tanggal: tanggal.value,
lapangan: lapangan.value,
jam: jam.value,
durasi: durasi.value
});
}
/* ========= SIMPAN MENU ========= */
function simpanMenu(){
if(!bookingId){
alert('Lakukan booking terlebih dahulu');
return;
}
let items = [];
document.querySelectorAll('[id^=menu_]').forEach(i=>{
if(i.value > 0){
items.push({id:i.id.replace('menu_',''), qty:i.value});
}
});
google.script.run.withSuccessHandler(()=>{
alert('Menu tersimpan');
}).saveMenu(bookingId, items);
}
/* ========= BAYAR ========= */
function bayar(){
if(!bookingId){
alert('Booking belum ada');
return;
}
if(!payment.value){
alert('Pilih metode pembayaran');
return;
}
google.script.run.withSuccessHandler(()=>{
const pesan = `
Halo Admin,
Saya sudah melakukan pembayaran booking padel.
Booking ID : ${bookingId}
Metode : ${payment.value}
Mohon dicek ya 🙏
`;
const waAdmin = '6281234567890'; // 🔴 GANTI NOMOR ADMIN
window.open(
'https://wa.me/' + waAdmin + '?text=' + encodeURIComponent(pesan),
'_blank'
);
alert('Silakan kirim bukti pembayaran via WhatsApp');
}).setPaymentMethod(bookingId, payment.value);
}
</script>
</body>
</html>
Pemilik.html
<!DOCTYPE html>
<html>
<head><base target="_top"></head>
<body>
<h3>📈 Dashboard Pemilik</h3>
<button onclick="load()">Refresh</button>
<pre id="out"></pre>
<script>
function load(){
google.script.run.withSuccessHandler(d=>{
out.textContent=JSON.stringify(d,null,2);
}).getAllBookings();
}
</script>
</body>
</html>
Kelebihan Sistem Booking Padel Ini
- Tidak memerlukan server hosting
- Cocok untuk UMKM dan komunitas padel
- Mudah dikembangkan ke sistem yang lebih besar
Dengan mengikuti panduan cara membuat booking padel dengan Google Script Apps gratis,
Anda bisa membangun sistem reservasi yang efisien, hemat biaya,
dan siap digunakan kapan saja. Solusi ini sangat ideal untuk
pengelola lapangan padel yang ingin beralih ke sistem digital.