withStatus()->get(); $parkingLots = $rawLots->map(fn($lot) => [ 'id' => $lot->id, 'name' => $lot->name, 'address' => $lot->address, 'total' => $lot->total_capacity, 'avail' => max(0, $lot->total_capacity - ($lot->active_bookings_count + $lot->active_registries_count)), 'occupied'=> $lot->active_bookings_count + $lot->active_registries_count, 'price' => (float) $lot->price_per_hour, 'hours' => $lot->working_hours, 'lat' => (float) $lot->latitude, 'lng' => (float) $lot->longitude, 'image' => $lot->image ? Storage::url($lot->image) : null, ])->values(); $selectedLotId = $request->get('lot_id'); $selectedLot = null; $activeCars = collect(); $reservations = collect(); if ($selectedLotId) { $selectedLot = ParkingLot::active()->findOrFail($selectedLotId); // Walk-in cars currently inside (source=walk_in, status=active) $activeCars = Booking::where('parking_lot_id', $selectedLotId) ->where('source', 'walk_in') ->where('status', 'active') ->latest('start_time') ->get(); // Pre-reservations not yet activated (source=reservation, status=active) $reservations = Booking::where('parking_lot_id', $selectedLotId) ->where('source', 'reservation') ->where('status', 'active') ->orderBy('start_time') ->get(); } return view('operator.dashboard', compact( 'parkingLots', 'selectedLot', 'activeCars', 'reservations', 'selectedLotId' )); } // ── Walk-in check-in ──────────────────────────────────────────────────────── public function checkIn(Request $request): JsonResponse { $data = $request->validate([ 'parking_lot_id' => 'required|exists:parking_lots,id', 'vehicle_plate' => 'required|string|max:50', 'customer_name' => 'nullable|string|max:255', 'phone' => 'nullable|string|max:20', 'duration_hours' => 'required|numeric|min:0.25|max:72', ]); $lot = ParkingLot::findOrFail($data['parking_lot_id']); $activeCount = $lot->bookings()->where('status', 'active')->count(); if ($activeCount >= $lot->total_capacity) { return response()->json(['success' => false, 'message' => 'الموقف ممتلئ حالياً'], 422); } $booking = Booking::create([ 'parking_lot_id' => $lot->id, 'vehicle_plate' => $data['vehicle_plate'], 'customer_name' => $data['customer_name'] ?? null, 'phone' => $data['phone'] ?? null, 'source' => 'walk_in', 'start_time' => now(), 'end_time' => now()->addHours($data['duration_hours']), 'status' => 'active', ]); return response()->json([ 'success' => true, 'message' => 'تم تسجيل دخول السيارة بنجاح', 'data' => ['id' => $booking->id, 'plate' => $booking->vehicle_plate], ]); } // ── Activate a reservation (open parking for reserved car) ───────────────── public function activateReservation(Booking $booking): JsonResponse { if ($booking->source !== 'reservation' || $booking->status !== 'active') { return response()->json(['success' => false, 'message' => 'الحجز غير صالح للتفعيل'], 400); } $booking->update([ 'source' => 'walk_in', // now treated as an active walk-in 'start_time' => now(), 'end_time' => now()->addHours( max(1, now()->diffInHours($booking->end_time, false)) ), ]); return response()->json(['success' => true, 'message' => 'تم تفعيل الحجز وفتح بوابة الدخول']); } // ── Checkout: calculate fee and return receipt data ───────────────────────── public function checkoutPreview(Booking $booking): JsonResponse { if ($booking->status !== 'active') { return response()->json(['success' => false, 'message' => 'الحجز غير نشط'], 400); } $lot = $booking->parkingLot; $start = $booking->start_time; $end = now(); $duration = $start->diffInMinutes($end); $calc = $lot->calculateFee($start, $end); return response()->json([ 'success' => true, 'data' => [ 'booking_id' => $booking->id, 'plate' => $booking->vehicle_plate, 'customer_name' => $booking->customer_name, 'lot_name' => $lot->name, 'entry_time' => $start->format('Y/m/d H:i'), 'exit_time' => $end->format('Y/m/d H:i'), 'duration_min' => $duration, 'duration_label'=> floor($duration / 60) . 'س ' . ($duration % 60) . 'د', 'fee_details' => $calc['details'], 'total_fee' => $calc['total'], ], ]); } // ── Process payment and complete booking ───────────────────────────────────── public function processPayment(Request $request, Booking $booking): JsonResponse { $data = $request->validate([ 'payment_method' => 'required|in:cash,upload', 'payment_proof' => 'nullable|file|mimes:jpg,jpeg,png,pdf|max:4096', ]); if ($booking->status !== 'active') { return response()->json(['success' => false, 'message' => 'الحجز غير نشط'], 400); } $lot = $booking->parkingLot; $start = $booking->start_time; $end = now(); $calc = $lot->calculateFee($start, $end); $proofPath = null; if ($request->hasFile('payment_proof')) { $proofPath = $request->file('payment_proof') ->store('payment_proofs', 'public'); } $booking->update([ 'status' => 'completed', 'end_time' => $end, 'total_fee' => $calc['total'], 'payment_method' => $data['payment_method'], 'payment_proof' => $proofPath, 'paid_at' => now(), ]); return response()->json([ 'success' => true, 'message' => 'تم تسجيل الخروج وإتمام الدفع بنجاح', 'data' => [ 'total_fee' => $calc['total'], 'payment_method' => $data['payment_method'], ], ]); } // ── Legacy checkout (kept for backward compatibility) ─────────────────────── public function checkOut(Request $request, Booking $booking): JsonResponse { return $this->processPayment($request->merge(['payment_method' => 'cash']), $booking); } }