validate([ 'status' => ['nullable', 'in:active,maintenance,closed'], 'barangay_id' => ['nullable', 'integer'], 'q' => ['nullable', 'string', 'max:100'], 'per_page' => ['nullable', 'integer', 'min:1', 'max:500'], ]); $perPage = (int) $request->input('per_page', 25); $points = DropOffPoint::query() ->with('barangay') ->when($request->filled('status'), fn ($q) => $q->where('status', $request->string('status'))) ->when($request->filled('barangay_id'), fn ($q) => $q->where('barangay_id', $request->integer('barangay_id'))) ->when($request->filled('q'), function ($q) use ($request) { $term = '%'.$request->string('q').'%'; $q->where(fn ($qq) => $qq->where('name', 'like', $term) ->orWhere('code', 'like', $term) ->orWhere('address_line', 'like', $term)); }) ->orderBy('name') ->paginate($perPage); return $this->ok( DropOffPointResource::collection($points), null, [ 'page' => $points->currentPage(), 'per_page' => $points->perPage(), 'total' => $points->total(), 'last_page' => $points->lastPage(), ], ); } public function store(StoreDropOffPointRequest $request): JsonResponse { $data = $request->validated(); $point = $this->makePoint($data); $dop = DropOffPoint::create([ 'name' => $data['name'], 'code' => $data['code'], 'barangay_id' => $data['barangay_id'] ?? null, 'coordinates' => $point, 'address_line' => $data['address_line'], 'capacity_kg' => $data['capacity_kg'] ?? null, 'operating_hours' => $data['operating_hours'] ?? null, 'accepted_waste_types' => $data['accepted_waste_types'] ?? null, 'status' => $data['status'] ?? DropOffPoint::STATUS_ACTIVE, 'photo_path' => $data['photo_path'] ?? null, 'contact_person' => $data['contact_person'] ?? null, 'contact_phone' => $data['contact_phone'] ?? null, 'geofence' => !empty($data['geofence_wkt']) ? Polygon::fromJson($data['geofence_wkt'], 4326) : null, ]); return $this->created(new DropOffPointResource($dop->load('barangay')), 'Drop-off point created'); } public function show(DropOffPoint $dropOffPoint): JsonResponse { $dropOffPoint->load('barangay.cityMunicipality'); $dropOffPoint->loadMissing(['capacityLogs' => fn ($q) => $q->latest('recorded_at')->limit(20)]); return $this->ok([ 'drop_off_point' => new DropOffPointResource($dropOffPoint), 'recent_capacity_logs' => DropOffCapacityLogResource::collection($dropOffPoint->capacityLogs), ]); } public function update(UpdateDropOffPointRequest $request, DropOffPoint $dropOffPoint): JsonResponse { $data = $request->validated(); $oldStatus = $dropOffPoint->status; $newStatus = $data['status'] ?? $oldStatus; if (isset($data['lat'], $data['lng'])) { $data['coordinates'] = $this->makePoint($data); } unset($data['lat'], $data['lng']); if (array_key_exists('geofence_wkt', $data)) { $data['geofence'] = !empty($data['geofence_wkt']) ? Polygon::fromJson($data['geofence_wkt'], 4326) : null; unset($data['geofence_wkt']); } DB::transaction(function () use ($dropOffPoint, $data, $oldStatus, $newStatus) { $dropOffPoint->update($data); if ($newStatus !== DropOffPoint::STATUS_ACTIVE && $oldStatus === DropOffPoint::STATUS_ACTIVE) { $households = $dropOffPoint->households; foreach ($households as $household) { $nearest = $this->dopFinder->nearest( $household->coordinates->latitude, $household->coordinates->longitude ); $household->update(['assigned_drop_off_point_id' => $nearest?->id]); } } }); return $this->ok( new DropOffPointResource($dropOffPoint->fresh()->load('barangay')), 'Drop-off point updated', ); } public function destroy(DropOffPoint $dropOffPoint): JsonResponse { $dropOffPoint->delete(); return $this->ok(null, 'Drop-off point deleted'); } public function logCapacity(StoreCapacityLogRequest $request, DropOffPoint $dropOffPoint): JsonResponse { $data = $request->validated(); $log = DropOffCapacityLog::create([ 'drop_off_point_id' => $dropOffPoint->id, 'fill_percent' => $data['fill_percent'], 'recorded_at' => $data['recorded_at'] ?? now(), 'recorded_by_user_id' => $request->user()->id, 'notes' => $data['notes'] ?? null, ]); return $this->created( new DropOffCapacityLogResource($log->load('recordedBy')), 'Capacity reading recorded', ); } private function makePoint(array $data): Point { return new Point((float) $data['lat'], (float) $data['lng'], 4326); } }