Jam buka tutup dinamis, tapi booking nya jadi gabisa
This commit is contained in:
parent
d59f1fb75e
commit
b8f70e7f6f
|
@ -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
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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
|
Loading…
Reference in New Issue