Jam buka tutup dinamis, tapi booking nya jadi gabisa

This commit is contained in:
Stephen Gesityan 2025-06-04 14:25:42 +07:00
parent d59f1fb75e
commit b8f70e7f6f
2 changed files with 106 additions and 145 deletions

View File

@ -18,12 +18,17 @@ public function venue($venueName) {
} }
// Ambil tabel-tabel terkait dengan venue // Ambil tabel-tabel terkait dengan venue
$tables = $venue->tables; $venue->load('tables'); // Eager loading untuk optimasi
// Mengirim data venue dan tabel ke view // Parsing jam operasional dari format H:i:s menjadi integer
$openHour = (int) date('H', strtotime($venue->open_time));
$closeHour = (int) date('H', strtotime($venue->close_time));
// Mengirim data venue dengan jam operasional ke view
return view('pages.venue', [ return view('pages.venue', [
'venue' => $venue, 'venue' => $venue,
'tables' => $tables 'openHour' => $openHour,
'closeHour' => $closeHour
]); ]);
} }
} }

View File

@ -21,7 +21,12 @@ class="fixed inset-0 bg-black bg-opacity-50 z-40 flex items-center justify-cente
class="w-full h-full object-cover rounded-lg mb-4 mt-8" /> class="w-full h-full object-cover rounded-lg mb-4 mt-8" />
<h1 class="text-xl text-gray-800 font-semibold">{{ $venue['name'] }}</h1> <h1 class="text-xl text-gray-800 font-semibold">{{ $venue['name'] }}</h1>
<p class="text-sm text-gray-500">{{ $venue['location'] }}</p> <p class="text-sm text-gray-500">{{ $venue['location'] ?? 'Lokasi tidak tersedia' }}</p>
<p class="text-sm text-gray-600 mt-1">
<i class="fa-regular fa-clock"></i>
Jam Operasional: {{ date('H:i', strtotime($venue['open_time'])) }} -
{{ date('H:i', strtotime($venue['close_time'])) }}
</p>
</div> </div>
<a href="https://www.google.com/maps/search/?api=1&query={{ urlencode($venue['address']) }}" target="_blank" <a href="https://www.google.com/maps/search/?api=1&query={{ urlencode($venue['address']) }}" target="_blank"
class="flex items-center bg-[url('/public/images/map.jpg')] bg-cover bg-center p-4"> class="flex items-center bg-[url('/public/images/map.jpg')] bg-cover bg-center p-4">
@ -100,7 +105,7 @@ class="bg-gray-200 text-gray-700 text-sm px-3 py-1 rounded-md hover:bg-gray-300"
</div> </div>
</div> </div>
@foreach ($venue['tables'] as $table) @foreach ($venue['tables'] as $table)
<div x-data="booking(@json(auth()->check()), '{{ $table['id'] }}')" <div x-data="booking(@json(auth()->check()), '{{ $table['id'] }}', {{ $openHour }}, {{ $closeHour }})"
class="border rounded-lg shadow-md p-4 mb-4"> class="border rounded-lg shadow-md p-4 mb-4">
<div class="flex items-center justify-between cursor-pointer" <div class="flex items-center justify-between cursor-pointer"
@click="open = !open; if(open) checkBookedSchedules()"> @click="open = !open; if(open) checkBookedSchedules()">
@ -123,7 +128,7 @@ class="border rounded-lg shadow-md p-4 mb-4">
<h4 class="font-semibold mb-2">Pilih Jam Booking:</h4> <h4 class="font-semibold mb-2">Pilih Jam Booking:</h4>
<select class="w-full border p-2 rounded-lg" x-model="selectedTime"> <select class="w-full border p-2 rounded-lg" x-model="selectedTime">
<option value="">-- Pilih Jam --</option> <option value="">-- Pilih Jam --</option>
<template x-for="hour in getHoursInRange(9, 24)" :key="hour"> <template x-for="hour in getAvailableHours()" :key="hour">
<option :value="hour + ':00'" :disabled="isTimeBooked(hour + ':00')" <option :value="hour + ':00'" :disabled="isTimeBooked(hour + ':00')"
x-text="hour + ':00' + (isTimeBooked(hour + ':00') ? ' (Booked)' : '')"> x-text="hour + ':00' + (isTimeBooked(hour + ':00') ? ' (Booked)' : '')">
</option> </option>
@ -535,19 +540,22 @@ function formatPrice(price) {
} }
})); }));
// Regular booking component (existing) // Regular booking component (updated with dynamic hours)
Alpine.data('booking', (isLoggedIn, tableId) => ({ Alpine.data('booking', (isLoggedIn, tableId, openHour, closeHour) => ({
isLoggedIn, isLoggedIn,
tableId, tableId,
openHour, // Dynamic open hour from venue
closeHour, // Dynamic close hour from venue
open: false, open: false,
selectedTime: '', selectedTime: '',
selectedDuration: '', selectedDuration: '',
isLoading: false, isLoading: false,
bookedSchedules: [], bookedSchedules: [],
getHoursInRange(startHour, endHour) { // Updated method to use dynamic hours from venue
getAvailableHours() {
let hours = []; let hours = [];
for (let i = startHour; i <= endHour; i++) { for (let i = this.openHour; i <= this.closeHour; i++) {
hours.push(i.toString().padStart(2, '0')); hours.push(i.toString().padStart(2, '0'));
} }
return hours; return hours;
@ -594,34 +602,17 @@ function formatPrice(price) {
// Uncomment this for production to prevent booking past times // Uncomment this for production to prevent booking past times
if (selectedDateTime <= now) { if (selectedDateTime <= now) {
showToast('Jam yang dipilih sudah lewat. Silakan pilih jam yang masih tersedia.', 'warning'); showToast('Tidak bisa booking untuk waktu yang sudah berlalu', 'warning');
return; return;
} }
this.isLoading = true; this.isLoading = true;
window.dispatchEvent(new CustomEvent('show-loading')); window.dispatchEvent(new CustomEvent('show-loading'));
// Hitung end time
const bookingStart = new Date();
bookingStart.setHours(selectedHour, selectedMinute, 0, 0);
const bookingEnd = new Date(bookingStart);
bookingEnd.setHours(bookingEnd.getHours() + parseInt(selectedDuration));
const endTimeFormatted = ('0' + bookingEnd.getHours()).slice(-2) + ':' + ('0' + bookingEnd.getMinutes()).slice(-2);
// Gunakan tanggal Jakarta yang konsisten // Gunakan tanggal Jakarta yang konsisten
const today = getJakartaDateString(); const bookingDate = getJakartaDateString();
const start_time = `${today} ${selectedTime}`; fetch('/booking/initiate', {
const end_time = `${today} ${endTimeFormatted}`;
console.log('Booking data:', { start_time, end_time, today });
// Track that we're creating a new booking
window.creatingNewBooking = true;
// Kirim ke backend untuk membuat payment intent
fetch('/booking/payment-intent', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@ -629,53 +620,21 @@ function formatPrice(price) {
}, },
body: JSON.stringify({ body: JSON.stringify({
table_id: tableId, table_id: tableId,
start_time: start_time, start_time: selectedTime,
end_time: end_time, duration: selectedDuration,
booking_date: bookingDate
}), }),
}) })
.then(res => { .then(res => res.json())
if (!res.ok) {
return res.json().then(err => {
throw new Error(err.message || 'Gagal membuat booking');
});
}
return res.json();
})
.then(data => { .then(data => {
window.dispatchEvent(new CustomEvent('hide-loading')); window.dispatchEvent(new CustomEvent('hide-loading'));
// Cek apakah ini response dari admin direct booking if (data.success) {
if (data.booking_id && data.message === 'Booking created successfully') { console.log("Opening payment with snap token:", data.snap_token);
// Admin booking berhasil tanpa payment // Open Snap payment
showToast('Booking berhasil dibuat!', 'success');
this.isLoading = false;
// Reset form
this.selectedTime = '';
this.selectedDuration = '';
this.open = false;
// Refresh halaman atau komponen yang diperlukan
this.checkBookedSchedules();
// Refresh pending bookings jika ada
document.dispatchEvent(refreshPendingBookingsEvent);
return; // Exit early untuk admin booking
}
// Flow normal untuk customer (membutuhkan payment)
if (!data.snap_token) {
throw new Error('Snap token tidak ditemukan');
}
// Track bahwa kita sedang membuat booking baru
window.creatingNewBooking = true;
// Buka Snap Midtrans untuk customer
window.snap.pay(data.snap_token, { window.snap.pay(data.snap_token, {
onSuccess: (result) => { onSuccess: (result) => {
this.createBookingAfterPayment(data.order_id, result); this.createBooking(data.order_id, result);
}, },
onPending: (result) => { onPending: (result) => {
showToast('Pembayaran pending, silahkan selesaikan pembayaran', 'warning'); showToast('Pembayaran pending, silahkan selesaikan pembayaran', 'warning');
@ -684,31 +643,30 @@ function formatPrice(price) {
onError: (result) => { onError: (result) => {
showToast('Pembayaran gagal', 'error'); showToast('Pembayaran gagal', 'error');
this.isLoading = false; this.isLoading = false;
window.creatingNewBooking = false;
}, },
onClose: () => { onClose: () => {
showToast('Anda menutup popup tanpa menyelesaikan pembayaran', 'warning'); showToast('Anda menutup popup tanpa menyelesaikan pembayaran', 'warning');
this.isLoading = false; this.isLoading = false;
window.justClosedPayment = true; window.justClosedPayment = true;
if (window.creatingNewBooking) { // Dispatch event to refresh pending bookings
window.creatingNewBooking = false;
document.dispatchEvent(refreshPendingBookingsEvent); document.dispatchEvent(refreshPendingBookingsEvent);
} }
}
}); });
} else {
showToast(data.message, 'error');
this.isLoading = false;
}
}) })
.catch(err => { .catch(err => {
window.dispatchEvent(new CustomEvent('hide-loading')); window.dispatchEvent(new CustomEvent('hide-loading'));
console.error('Booking error:', err); console.error('Booking initiation error:', err);
showToast('Gagal membuat booking: ' + err.message, 'error'); showToast('Gagal melakukan booking', 'error');
this.isLoading = false; this.isLoading = false;
window.creatingNewBooking = false;
}); });
}, },
// Fungsi untuk menyimpan booking setelah pembayaran berhasil createBooking(orderId, paymentResult) {
createBookingAfterPayment(orderId, paymentResult) {
window.dispatchEvent(new CustomEvent('show-loading')); window.dispatchEvent(new CustomEvent('show-loading'));
fetch('/booking', { fetch('/booking', {
@ -735,14 +693,17 @@ function formatPrice(price) {
.then(data => { .then(data => {
window.dispatchEvent(new CustomEvent('hide-loading')); window.dispatchEvent(new CustomEvent('hide-loading'));
showToast('Pembayaran dan booking berhasil!', 'success'); showToast('Pembayaran dan booking berhasil!', 'success');
this.isLoading = false;
// Reset the flag // Reset form
window.creatingNewBooking = false; this.selectedTime = '';
this.selectedDuration = '';
this.open = false;
// Refresh component data // Refresh booked schedules
document.dispatchEvent(new CustomEvent('booking-completed')); this.checkBookedSchedules();
// Redirect to booking history page // Redirect to booking history
setTimeout(() => { setTimeout(() => {
window.location.href = '/booking/history'; window.location.href = '/booking/history';
}, 2000); }, 2000);
@ -752,19 +713,14 @@ function formatPrice(price) {
console.error('Booking error:', err); console.error('Booking error:', err);
showToast('Pembayaran berhasil tetapi gagal menyimpan booking: ' + err.message, 'error'); showToast('Pembayaran berhasil tetapi gagal menyimpan booking: ' + err.message, 'error');
this.isLoading = false; this.isLoading = false;
window.creatingNewBooking = false;
}); });
},
// Method to refresh booked schedules without reloading the page
async refreshBookedSchedules() {
await this.checkBookedSchedules();
} }
})); }));
}); });
// Initialize clock update // Initialize clock
updateClock();
setInterval(updateClock, 1000); setInterval(updateClock, 1000);
updateClock(); // Initial call
</script> </script>
@endsection @endsection