Mengatur ketersediaan

Atur jam operasional mingguan, blokir waktu, dan hari libur agar slot booking Termilo selalu akurat. Termilo menghitung slot dari aturan ketersediaan dikurangi libur, blokir, dan booking yang sudah ada.

Lihat .md
Semua waktu disimpan sebagai UTC epoch milidetik. Jam buka/tutup per hari disimpan sebagai menit sejak tengah malam (startMinute/endMinute), dan weekday memakai 0–6 dengan 0 = Minggu.

Tiga lapis ketersediaan#

Ketersediaan di Termilo dibangun dari tiga tabel terpisah. Mesin booking menggabungkannya saat menghitung slot: aturan menentukan kapan Anda bisa menerima booking, sedangkan blokir dan libur memotong kembali waktu itu.

availability_rules
Jam operasional

Jendela mingguan berulang (buka/tutup per hari). Bisa per bisnis, per staf, atau per resource.

blocked_times
Blokir waktu

Waktu libur sekali jalan dengan rentang epoch-ms — cuti, rapat, atau maintenance.

holidays
Hari libur

Penutupan seluruh bisnis pada rentang tanggal, mis. libur nasional.

Owner per lapis
Setiap aturan dan blokir punya ownerTypetenant, staff, atau resource. Aturan tingkat bisnis (tenant) berlaku untuk semua orang; aturan tingkat staf/resource mempersempit ketersediaan orang atau ruangan tertentu.

Atur lewat dashboard#

Jalur tercepat ada di dashboard. Langkah-langkahnya cocok dengan layar Jam operasional.

  1. Buka Operasional → Jam operasional.
  2. Atur jam buka/tutup tiap hari (Sen–Min, 06:00–22:00, kelipatan 30 menit). Gunakan “Salin ke semua hari” untuk menyamakan jadwal.
  3. Aktifkan “Tutup otomatis saat libur nasional” bila perlu.
  4. Tambahkan “Pengecualian & hari libur” (Tanggal + Keterangan) untuk blokir dan libur satu kali.
Setara dengan API
Menyimpan di dashboard menulis baris yang sama seperti POST /api/availability-rules, POST /api/blocked-times, dan POST /api/holidays. Detail tiap endpoint ada di bawah.

Jam operasional (availability rules)#

Aturan ketersediaan adalah jendela mingguan berulang. Satu baris = satu rentang buka pada satu hari. Untuk jadwal Senin–Jumat 09:00–17:00, buat lima baris (weekday 1 sampai 5) dengan startMinute: 540 dan endMinute: 1020. Server memvalidasi startMinute < endMinute.

Endpoint: GET|POST /api/availability-rules dan PATCH /api/availability-rules/:ruleId — sesi dashboard + peran tulis + header CSRF X-Termilo-CSRF.

PARAMETERTIPEWAJIBKETERANGAN
ownerType"tenant" | "staff" | "resource"wajibPemilik jadwal: bisnis, staf tertentu, atau resource tertentu.
ownerIdstringwajibID pemilik. Untuk tenant, ID tenant aktif.
weekdayinteger (0–6)wajibHari dalam minggu. 0 = Minggu, 6 = Sabtu.
startMinuteintegerwajibMenit buka sejak tengah malam. 06:00 = 360.
endMinuteintegerwajibMenit tutup. Harus lebih besar dari startMinute.
timezonestring (IANA)wajibZona waktu jadwal, mis. Asia/Jakarta.
effectiveFromAtMsinteger | nullopsionalEpoch ms saat aturan mulai berlaku. Null = selalu.
effectiveUntilAtMsinteger | nullopsionalEpoch ms saat aturan berhenti. Null = tanpa batas.
status"active" | "disabled"opsionalBawaan active. Set disabled untuk menonaktifkan tanpa menghapus.
# Senin–Jumat, 09:00–17:00 (540–1020 menit) untuk seluruh bisnis curl -X POST https://termilo.id/api/availability-rules \ -H "X-Termilo-CSRF: $CSRF" \ -H "Content-Type: application/json" \ --cookie "__Host-termilo_session=$SESSION" \ -d '{ "ownerType": "tenant", "ownerId": "$TENANT_ID", "weekday": 1, "startMinute": 540, "endMinute": 1020, "timezone": "Asia/Jakarta" }'

Blokir waktu (blocked times)#

Blokir waktu adalah libur sekali jalan dengan rentang epoch-ms eksplisit — di luar jadwal mingguan. Gunakan untuk cuti, rapat internal, atau perawatan ruangan. Set status: "cancelled" untuk membatalkan blokir tanpa menghapus baris.

Endpoint: GET|POST /api/blocked-times dan PATCH /api/blocked-times/:blockId — sesi dashboard + CSRF.

PARAMETERTIPEWAJIBKETERANGAN
ownerType"tenant" | "staff" | "resource"wajibSiapa yang tidak tersedia selama rentang ini.
ownerIdstringwajibID pemilik yang diblokir.
startsAtMsinteger (epoch ms)wajibAwal blokir, UTC epoch milidetik.
endsAtMsinteger (epoch ms)wajibAkhir blokir, UTC epoch milidetik.
reasonstringopsionalCatatan internal, mis. cuti atau maintenance.
status"active" | "cancelled"opsionalBawaan active. Set cancelled untuk membatalkan blokir.

Hari libur (holidays)#

Hari libur menutup seluruh bisnis pada rentang tanggal — tidak terikat pada satu owner. Cocok untuk libur nasional atau penutupan tahunan.

Endpoint: GET|POST /api/holidays dan PATCH /api/holidays/:holidayId — sesi dashboard + CSRF.

PARAMETERTIPEWAJIBKETERANGAN
namestringwajibNama hari libur, mis. Idulfitri.
startsAtMsinteger (epoch ms)wajibAwal penutupan, UTC epoch milidetik.
endsAtMsinteger (epoch ms)wajibAkhir penutupan, UTC epoch milidetik.
timezonestring (IANA)wajibZona waktu rentang libur.
status"active" | "disabled"opsionalBawaan active. Set disabled untuk menonaktifkan libur.
Impor libur nasional Indonesia
Pratinjau / segeraImpor otomatis kalender libur nasional Indonesia adalah automasi yang direncanakan, belum aktif di backend. Untuk sekarang, masukkan hari libur secara manual lewat dashboard atau POST /api/holidays.

Cara slot dihitung#

Slot bukan tabel — slot dihitung. Mesin ketersediaan mengambil aturan mingguan lalu mengurangi hari libur, blokir waktu, dan booking yang sudah ada, lalu memotongnya ke durasi layanan. Hasilnya adalah daftar slot kandidat untuk halaman publik.

Bacaan publik tidak perlu auth: GET /book/:tenantSlug/availability. Jendela pencarian dibatasi maksimal 31 hari.

PARAMETERTIPEWAJIBKETERANGAN
serviceSlugstringwajibSlug layanan yang dicari ketersediaannya.
fromMsinteger (epoch ms)wajibAwal jendela pencarian, UTC epoch milidetik.
toMsinteger (epoch ms)wajibAkhir jendela. Jendela maksimal 31 hari.
# Jendela pencarian dibatasi maksimal 31 hari. curl "https://termilo.id/book/akupunktur-sehat/availability\ ?serviceSlug=konsultasi-30&fromMs=1750723200000&toMs=1750896000000"
200 OKapplication/json
{
  "service": { "serviceId": "...", "slug": "konsultasi-30", "durationMin": 30 },
  "finalAuthority": "booking_create_recheck_under_lock",
  "slots": [
    {
      "startsAtMs": 1750723200000,
      "endsAtMs": 1750725000000,
      "staffId": null,
      "resourceId": null,
      "capacityRemaining": 1
    }
  ]
}

Slot adalah pratinjau, bukan otoritas akhir

Respons membawa penanda literal "finalAuthority": "booking_create_recheck_under_lock". Artinya: tampilkan slot ini untuk dipilih, tapi ketersediaan sesungguhnya baru ditetapkan saat POST /book/:tenantSlug/bookings mengecek ulang slot di bawah lock Durable Object. Bila slot sudah terisi, create mengembalikan BOOKING_SLOT_UNAVAILABLE (409). Jangan perlakukan respons availability sebagai jaminan.

Buffer antar booking#

Buffer adalah jeda di sekitar setiap booking, dan itu milik layanan — bukan aturan ketersediaan. Setiap layanan punya bufferBeforeMin dan bufferAfterMin (bawaan 0, diatur lewat /api/services). Mesin ikut memperhitungkan buffer ini saat membentuk slot, sehingga ada jeda persiapan atau bersih- bersih antar sesi.

Buffer vs jam operasional
Perlebar atau persempit ketersediaan secara umum lewat aturan jam operasional; atur jeda per sesi lewat buffer layanan. Keduanya digabungkan saat slot dihitung. Lihat Membuat layanan untuk mengatur buffer.