validate([ 'status' => ['nullable', 'in:active,inactive'], 'area_id' => ['nullable', 'integer'], 'q' => ['nullable', 'string', 'max:100'], 'per_page' => ['nullable', 'integer', 'min:1', 'max:500'], ]); $perPage = (int) $request->input('per_page', 25); $routes = Route::query() ->with(['area', 'defaultDumpsite', 'tenant']) ->withCount('stops') ->when($request->filled('status'), fn ($q) => $q->where('status', $request->string('status'))) ->when($request->filled('area_id'), fn ($q) => $q->where('area_id', $request->integer('area_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)); }) ->orderBy('name') ->paginate($perPage); return $this->ok( RouteResource::collection($routes), null, [ 'page' => $routes->currentPage(), 'per_page' => $routes->perPage(), 'total' => $routes->total(), 'last_page' => $routes->lastPage(), ], ); } public function store(StoreRouteRequest $request): JsonResponse { $data = $request->validated(); $route = DB::transaction(function () use ($data, $request) { $route = Route::create([ 'name' => $data['name'], 'code' => $data['code'], 'area_id' => $data['area_id'] ?? null, 'default_dumpsite_id' => $data['default_dumpsite_id'] ?? null, 'default_team_id' => $data['default_team_id'] ?? null, 'status' => $data['status'] ?? Route::STATUS_ACTIVE, 'notes' => $data['notes'] ?? null, 'created_by_admin_id' => $request->user()->id, ]); $this->syncStops($route, $data['stops']); return $route; }); $this->calc->recompute($route); return $this->created( new RouteResource($route->fresh()->load(['area', 'defaultDumpsite', 'stops.dropOffPoint'])->loadCount('stops')), 'Route created', ); } public function show(Route $route): JsonResponse { $route->load(['area', 'defaultDumpsite', 'stops.dropOffPoint.barangay']) ->loadCount('stops'); return $this->ok(new RouteResource($route)); } public function update(UpdateRouteRequest $request, Route $route): JsonResponse { $data = $request->validated(); $stopsPayload = $data['stops'] ?? null; unset($data['stops']); DB::transaction(function () use ($route, $data, $stopsPayload) { $route->update($data); if ($stopsPayload !== null) { $this->syncStops($route, $stopsPayload); } }); $this->calc->recompute($route); return $this->ok( new RouteResource($route->fresh()->load(['area', 'defaultDumpsite', 'stops.dropOffPoint'])->loadCount('stops')), 'Route updated', ); } public function destroy(Route $route): JsonResponse { $route->delete(); return $this->ok(null, 'Route deleted'); } public function clone(Request $request, Route $route): JsonResponse { $request->validate([ 'name' => ['nullable', 'string', 'max:191'], 'code' => ['nullable', 'string', 'max:32'], ]); $newCode = $request->string('code')->isEmpty() ? $route->code.'-COPY-'.strtoupper(Str::random(4)) : (string) $request->string('code'); $newName = $request->string('name')->isEmpty() ? $route->name.' (copy)' : (string) $request->string('name'); $clone = DB::transaction(function () use ($route, $request, $newCode, $newName) { $clone = Route::create([ 'name' => $newName, 'code' => $newCode, 'area_id' => $route->area_id, 'default_dumpsite_id' => $route->default_dumpsite_id, 'default_team_id' => $route->default_team_id, 'status' => Route::STATUS_INACTIVE, 'notes' => $route->notes, 'created_by_admin_id' => $request->user()->id, ]); $payload = $route->stops->map(fn ($s) => [ 'drop_off_point_id' => $s->drop_off_point_id, 'estimated_duration_minutes' => $s->estimated_duration_at_stop_minutes, ])->all(); $this->syncStops($clone, $payload); return $clone; }); $this->calc->recompute($clone); return $this->created( new RouteResource($clone->fresh()->load(['area', 'defaultDumpsite', 'stops.dropOffPoint'])->loadCount('stops')), 'Route cloned', ); } /** * Replace the route's stops with the given ordered list. Sequence is * derived from array position (1-based) so admin can drag-reorder * client-side and just submit the new order. */ private function syncStops(Route $route, array $stops): void { $route->stops()->delete(); foreach (array_values($stops) as $i => $stop) { RouteStop::create([ 'route_id' => $route->id, 'drop_off_point_id' => $stop['drop_off_point_id'], 'sequence' => $i + 1, 'estimated_duration_at_stop_minutes' => $stop['estimated_duration_minutes'] ?? 10, ]); } } }