orderBy('date', 'desc') ->orderBy('start_time', 'desc') ->get(); $users = User::where('role', 'user')->get(); // Get all tables regardless of status $tables = Meja::orderBy('nomor_meja')->get(); return view('admin.reservations', compact('reservations', 'users', 'tables')); } public function store(Request $request) { $request->validate([ 'user_id' => 'required|exists:users,id', 'meja_id' => 'required|exists:meja,id', 'name' => 'required|string|max:255', 'phone' => 'required|string|max:20', 'date' => 'required|date|after_or_equal:today', 'start_time' => 'required|date_format:H:i', 'end_time' => 'required|date_format:H:i|after:start_time', 'notes' => 'nullable|string', 'total_harga' => 'required|numeric|min:0' ]); // Check if the time slot is available if (!Reservasi::isTimeSlotAvailable( $request->meja_id, $request->date, $request->start_time, $request->end_time )) { return back()->withErrors(['time' => 'Waktu yang dipilih sudah dipesan']); } try { // Create reservation $reservation = Reservasi::create([ 'user_id' => $request->user_id, 'meja_id' => $request->meja_id, 'name' => $request->name, 'phone' => $request->phone, 'date' => $request->date, 'start_time' => $request->start_time, 'end_time' => $request->end_time, 'notes' => $request->notes, 'status' => 'pending', 'total_harga' => $request->total_harga ]); // Update table status if reservation is for today if ($request->date == now()->format('Y-m-d')) { Meja::where('id', $request->meja_id) ->update(['status' => 'dipesan']); } return redirect()->back()->with('success', 'Reservasi berhasil ditambahkan'); } catch (\Exception $e) { Log::error('Error creating reservation:', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); return back()->withErrors(['error' => 'Terjadi kesalahan saat membuat reservasi. Silakan coba lagi.'])->withInput(); } } public function update(Request $request, Reservasi $reservation) { $request->validate([ 'status' => 'required|in:pending,confirmed,completed,cancelled', 'meja_id' => 'required|exists:meja,id' ]); // Update both status and table $reservation->update([ 'status' => $request->status, 'meja_id' => $request->meja_id ]); // Update table status based on reservation status if ($reservation->date == now()->format('Y-m-d')) { if ($request->status === 'confirmed') { $reservation->meja()->update(['status' => 'dipesan']); } elseif (in_array($request->status, ['completed', 'cancelled'])) { $reservation->meja()->update(['status' => 'tersedia']); } } if ($request->ajax()) { return response()->json([ 'success' => true, 'message' => 'Reservasi berhasil diperbarui' ]); } return redirect()->back()->with('success', 'Reservasi berhasil diperbarui'); } public function destroy(Reservasi $reservation) { try { // Set table status back to available if reservation was for today if ($reservation->date == now()->format('Y-m-d') && $reservation->meja) { $reservation->meja()->update(['status' => 'tersedia']); } $reservation->delete(); return redirect()->back()->with('success', 'Reservasi berhasil dihapus'); } catch (\Exception $e) { Log::error('Error deleting reservation:', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); return back()->withErrors(['error' => 'Terjadi kesalahan saat menghapus reservasi.']); } } public function getAvailableTimeSlots(Request $request) { try { Log::info('Admin getAvailableTimeSlots called with request:', [ 'all_params' => $request->all(), 'date' => $request->date, 'meja_id' => $request->meja_id ]); $request->validate([ 'meja_id' => 'required|exists:meja,id', 'date' => 'required|date|after_or_equal:today', ]); Log::info('Validation passed, getting reservations'); // Get existing reservations for this table and date $reservations = Reservasi::where('meja_id', $request->meja_id) ->whereDate('date', $request->date) ->whereNotIn('status', ['cancelled']) ->get(); Log::info('Found existing reservations', [ 'count' => $reservations->count(), 'reservations' => $reservations->toArray() ]); $timeSlots = []; $startHour = 10; // 10:00 $endHour = 22; // 22:00 $interval = 30; // 30 minutes interval Log::info('Starting to generate time slots'); // Create a helper function to check if a time slot is reserved $isTimeSlotReserved = function($checkTime) use ($reservations) { $checkDateTime = Carbon::parse($checkTime); foreach ($reservations as $reservation) { $reservationStart = Carbon::parse($reservation->start_time); $reservationEnd = Carbon::parse($reservation->end_time); if ($checkDateTime->between($reservationStart, $reservationEnd, true)) { return true; } } return false; }; // Generate all possible time slots for ($hour = $startHour; $hour < $endHour; $hour++) { for ($minute = 0; $minute < 60; $minute += $interval) { $currentTime = sprintf('%02d:%02d', $hour, $minute); // Check if this time slot is already reserved $isReserved = $isTimeSlotReserved($currentTime); if (!$isReserved) { $endTimeOptions = []; // Generate possible end times (30 minutes to 4 hours after start time) for ($duration = 30; $duration <= 240; $duration += 30) { $potentialEndTime = Carbon::parse($currentTime)->addMinutes($duration); // Don't add end times past closing time if ($potentialEndTime->format('H:i') > '22:00') { continue; } // Check if any time between start and potential end time is reserved $hasConflict = false; $checkTime = Carbon::parse($currentTime); while ($checkTime < $potentialEndTime) { if ($isTimeSlotReserved($checkTime->format('H:i'))) { $hasConflict = true; break; } $checkTime->addMinutes(30); } if (!$hasConflict) { $endTimeOptions[] = $potentialEndTime->format('H:i'); } } // Only add start time if it has at least one valid end time option if (count($endTimeOptions) > 0) { $timeSlots[] = [ 'start_time' => $currentTime, 'end_time_options' => $endTimeOptions, 'is_available' => true ]; } } else { // Add reserved time slot $timeSlots[] = [ 'start_time' => $currentTime, 'end_time_options' => [], 'is_available' => false ]; } } } // If date is today, remove past time slots if ($request->date == Carbon::today()->format('Y-m-d')) { $currentTime = Carbon::now(); Log::info('Filtering past time slots for today', [ 'current_time' => $currentTime->format('H:i'), 'before_count' => count($timeSlots) ]); $timeSlots = array_filter($timeSlots, function($slot) use ($currentTime) { return Carbon::parse($slot['start_time'])->gt($currentTime); }); Log::info('After filtering past slots', [ 'after_count' => count($timeSlots) ]); } $timeSlots = array_values($timeSlots); Log::info('Generated available time slots', [ 'count' => count($timeSlots), 'first_few_slots' => array_slice($timeSlots, 0, 3) ]); return response()->json([ 'success' => true, 'data' => $timeSlots ]); } catch (\Exception $e) { Log::error('Error in admin getAvailableTimeSlots', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); return response()->json([ 'success' => false, 'message' => 'Terjadi kesalahan saat memuat jadwal: ' . $e->getMessage() ], 500); } } }