Cek Benturan Jadwal sebagai Peringatan, Bukan Penghalang
Formulir peminjaman kini memperingatkan benturan jadwal tanpa memblokir: endpoint baca-saja, SQL irisan inklusif, debounce dan abort.
Ringkasan
Dulu dua pengajuan sewa aula yang tabrak tanggal bisa lolos dua-duanya karena petugas beda nggak saling cek. Sekarang ada endpoint baca-saja yang nandain irisan jadwal, terus formulir munculin kotak peringatan kuning. Tapi tombol kirim tetap bebas, soalnya sistem cuma ngasih tahu, keputusan akhir tetap di tangan petugas.
Dua pengajuan penyewaan untuk aula yang sama, tanggalnya beririsan, dan keduanya lolos karena disetujui petugas berbeda yang saling berturutan tanpa saling mengecek. Formulir lama tidak punya cara memberi tahu bahwa rentang tanggal yang dipilih sudah diambil. Benturan baru terlihat di dasbor, setelah kedua berkas masuk.
Tebakan pertama: blokir tombol kirim saat benturan terdeteksi. Pendekatan itu ditolak. Ketersediaan fasilitas bersifat advisori; pengecualian, negosiasi jadwal ulang, dan keputusan akhir tetap di tangan petugas. Yang dipasang justru kebalikannya: endpoint baca-saja yang menandai irisan jadwal, lalu kotak peringatan di formulir yang tidak pernah memblokir apa pun.
Validasi Parameter di Server
Endpoint availability menerima tiga parameter. Identitas fasilitas wajib berhasil diurai sebagai bilangan bulat positif. Dua parameter tanggal wajib lolos time.Parse dengan layout "2006-01-02": layout Go ditulis terhadap stempel waktu referensi 01/02 03:04:05PM '06 -0700, sehingga 2006-01-02 adalah penulisan baku untuk tanggal YYYY-MM-DD [1]. Bila tanggal mulai melebihi tanggal akhir, server menjawab 400 INVALID_INPUT sebelum menyentuh basis data.
id, err := strconv.ParseInt(rawID, 10, 64)
if err != nil || id <= 0 {
return http.StatusBadRequest // INVALID_INPUT
}
_, err = time.Parse("2006-01-02", rawStart)
if err != nil {
return http.StatusBadRequest // format wajib YYYY-MM-DD
}
Endpoint ini tidak kena rate limit. Alasannya sederhana dan tertulis di rutenya sendiri: permukaannya sama dengan formulir publik tapi sifatnya baca-saja, tanpa tulis, jadi beban pengecekan berulang dari klien tidak dianggap penyalahgunaan.
Query Irisan Inklusif
Syarat dua rentang tanggal beririsan adalah dua perbandingan, bukan empat. Penyewaan lama dianggap benturan bila tanggal mulainya tidak lewat dari end yang diminta dan tanggal akhirnya tidak mendahului start yang diminta:
WHERE rental_start_date <= ?
AND rental_end_date >= ?
AND status <> 'ditolak'
Kedua perbandingan itu inklusif di kedua ujung, sehingga sewa 12 sampai 14 tetap terdeteksi oleh permintaan 10 sampai 15. Pengajuan yang ditolak keluar lewat filter status, dan baris tanpa tanggal sewa jatuh sendiri karena perbandingan NULL. Hasil diurutkan menaik per tanggal mulai agar daftarnya mudah dibaca.
Ada satu detail driver yang mudah terlewat. Ketika koneksi basis data memakai opsi parseTime, driver MySQL memindai kolom DATE sebagai objek waktu dan serialisasi JSON-nya ikut berubah bentuk. Klausa SELECT memakai DATE_FORMAT dengan pola YYYY-MM-DD supaya kolom itu ter-scan sebagai string; fungsi ini merender ulang nilai tanggal sesuai format string, dan mengembalikan NULL bila argumennya NULL [2]. Kontrak responsnya juga sengaja tipis: hanya pasangan tanggal mulai dan tanggal akhir, tanpa nama maupun identitas pemohon. Fasilitas yang tidak dikenal tidak dijawab 404; daftar kosong adalah jawaban baku untuk "tidak ada catatan", sehingga klien tidak perlu membedakan dua keadaan itu.
Peringatan yang Tidak Pernah Memblokir
Di sisi formulir, pengecekan baru jalan setelah pengguna berhenti mengubah pilihan selama 500 milidetik. Tiga lapis pengaman menjaga responsnya tetap benar. Debounce menahan permintaan saat rentang tanggal masih berubah. Penghitung generasi naik setiap kali syarat pengecekan berganti, dan respons yang tiba dengan nomor generasi lampau dibuang begitu saja. Pemakaian AbortController menutup sisanya: signal-nya diteruskan ke fetch sehingga permintaan yang sedang jalan bisa dibatalkan lewat fungsi cleanup useEffect [3].
Kegagalan diperlakukan sebagai ketiadaan peringatan, bukan sebagai halangan. Respons 400, 401, atau gangguan jaringan membuat kotak peringatan tidak muncul dan formulir tetap bisa dikirim. Kotak itu sendiri bertanda amber dengan atribut role="status" agar pembaca layar mengumumkan isinya tanpa mencuri fokus. Tombol kirim tidak pernah dikunci oleh keadaan ini.
Keputusan desainnya satu kalimat: pengecekan ini memberi tahu, manusia yang memutuskan. Hasilnya bisa diverifikasi langsung. Dua pengajuan beririsan kini memunculkan daftar periode sebelum tombol kirim disentuh; pengajuan tanpa benturan tidak pernah melihat kotak itu; dan saat API dimatikan, formulir tetap menyelesaikan tugasnya.
Sumber
[1] golang/go, src/time/format.go, diakses 13 Oktober 2026.
[2] MySQL 8.4 Reference Manual, Date and Time Functions (arsip Wayback), diakses 13 Oktober 2026.
[3] MDN Web Docs, AbortController, diakses 13 Oktober 2026.