diff --git a/app/Http/Controllers/Admin/AdminOperatorController.php b/app/Http/Controllers/Admin/AdminOperatorController.php new file mode 100644 index 0000000..fb0a2b0 --- /dev/null +++ b/app/Http/Controllers/Admin/AdminOperatorController.php @@ -0,0 +1,92 @@ +with('assignedLot') + ->orderBy('name') + ->get(); + + $lots = ParkingLot::orderBy('name')->get(); + + return view('admin.operators.index', compact('operators', 'lots')); + } + + public function store(Request $request) + { + $data = $request->validate([ + 'name' => 'required|string|max:255', + 'email' => 'required|email|unique:users,email', + 'phone' => 'nullable|string|max:20', + 'password' => ['required', Password::min(8)], + 'parking_lot_id' => 'nullable|exists:parking_lots,id', + ], [ + 'email.unique' => 'هذا البريد الإلكتروني مستخدم بالفعل.', + ]); + + User::create([ + 'name' => $data['name'], + 'email' => $data['email'], + 'phone' => $data['phone'] ?? null, + 'password' => Hash::make($data['password']), + 'role' => 'operator', + 'parking_lot_id' => $data['parking_lot_id'] ?? null, + ]); + + return response()->json(['success' => true, 'message' => 'تم إنشاء حساب المشغّل بنجاح.']); + } + + public function update(Request $request, User $operator) + { + if ($operator->role !== 'operator') { + return response()->json(['success' => false, 'message' => 'المستخدم ليس مشغّلاً.'], 403); + } + + $data = $request->validate([ + 'name' => 'required|string|max:255', + 'email' => 'required|email|unique:users,email,' . $operator->id, + 'phone' => 'nullable|string|max:20', + 'password' => ['nullable', Password::min(8)], + 'parking_lot_id' => 'nullable|exists:parking_lots,id', + ], [ + 'email.unique' => 'هذا البريد الإلكتروني مستخدم بالفعل.', + ]); + + $updates = [ + 'name' => $data['name'], + 'email' => $data['email'], + 'phone' => $data['phone'] ?? null, + 'parking_lot_id' => $data['parking_lot_id'] ?? null, + ]; + + if (!empty($data['password'])) { + $updates['password'] = Hash::make($data['password']); + } + + $operator->update($updates); + + return response()->json(['success' => true, 'message' => 'تم تحديث بيانات المشغّل.']); + } + + public function destroy(User $operator) + { + if ($operator->role !== 'operator') { + return response()->json(['success' => false, 'message' => 'المستخدم ليس مشغّلاً.'], 403); + } + + $operator->delete(); + + return response()->json(['success' => true, 'message' => 'تم حذف المشغّل.']); + } +} diff --git a/app/Http/Controllers/Operator/OperatorController.php b/app/Http/Controllers/Operator/OperatorController.php index 71f6b49..2dabdaa 100644 --- a/app/Http/Controllers/Operator/OperatorController.php +++ b/app/Http/Controllers/Operator/OperatorController.php @@ -7,6 +7,7 @@ use App\Models\Booking; use App\Models\ParkingLot; use Illuminate\Http\Request; use Illuminate\Http\JsonResponse; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Storage; use Carbon\Carbon; @@ -16,23 +17,39 @@ class OperatorController extends Controller public function dashboard(Request $request): \Illuminate\View\View { + $user = Auth::user(); + $assignedLotId = $user->parking_lot_id; // null = no restriction (e.g. admin) + $rawLots = ParkingLot::active()->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, + '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, + 'locked' => $assignedLotId !== null && $lot->id !== $assignedLotId, ])->values(); - $selectedLotId = $request->get('lot_id'); + // If operator has an assigned lot, force that lot + $selectedLotId = $request->get('lot_id'); + if ($assignedLotId !== null) { + // Reject any attempt to view a different lot + if ($selectedLotId && (int) $selectedLotId !== $assignedLotId) { + return redirect()->route('operator.dashboard', ['lot_id' => $assignedLotId]); + } + // Auto-select assigned lot if nothing selected + if (!$selectedLotId) { + $selectedLotId = $assignedLotId; + } + } + $selectedLot = null; $activeCars = collect(); $reservations = collect(); @@ -56,7 +73,7 @@ class OperatorController extends Controller } return view('operator.dashboard', compact( - 'parkingLots', 'selectedLot', 'activeCars', 'reservations', 'selectedLotId' + 'parkingLots', 'selectedLot', 'activeCars', 'reservations', 'selectedLotId', 'assignedLotId' )); } diff --git a/app/Models/User.php b/app/Models/User.php index 977decd..b05eed8 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -24,6 +24,7 @@ class User extends Authenticatable 'phone', 'password', 'role', + 'parking_lot_id', ]; protected $casts = [ @@ -54,4 +55,9 @@ class User extends Authenticatable 'password' => 'hashed', ]; } + + public function assignedLot(): \Illuminate\Database\Eloquent\Relations\BelongsTo + { + return $this->belongsTo(ParkingLot::class, 'parking_lot_id'); + } } diff --git a/database/migrations/2026_04_16_140529_add_parking_lot_id_to_users_table.php b/database/migrations/2026_04_16_140529_add_parking_lot_id_to_users_table.php new file mode 100644 index 0000000..acfeea0 --- /dev/null +++ b/database/migrations/2026_04_16_140529_add_parking_lot_id_to_users_table.php @@ -0,0 +1,27 @@ +foreignId('parking_lot_id') + ->nullable() + ->after('role') + ->constrained('parking_lots') + ->nullOnDelete(); + }); + } + + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropForeignIdFor(\App\Models\ParkingLot::class); + $table->dropColumn('parking_lot_id'); + }); + } +}; diff --git a/resources/views/admin/operators/index.blade.php b/resources/views/admin/operators/index.blade.php new file mode 100644 index 0000000..358b6fa --- /dev/null +++ b/resources/views/admin/operators/index.blade.php @@ -0,0 +1,392 @@ +@extends('layouts.admin') +@section('title', 'إدارة المشغّلين — دمشق باركينغ') +@section('page-title', 'إدارة المشغّلين') + +@section('styles') + +@endsection + +@section('content') + +
إنشاء حسابات المشغّلين وتخصيص مواقف لهم
+