184 lines
6.3 KiB
PHP
184 lines
6.3 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api\V1\Admin;
|
|
|
|
use App\Http\Controllers\Api\V1\ApiController;
|
|
use App\Http\Requests\Route\StoreRouteRequest;
|
|
use App\Http\Requests\Route\UpdateRouteRequest;
|
|
use App\Http\Resources\RouteResource;
|
|
use App\Models\Route;
|
|
use App\Models\RouteStop;
|
|
use App\Services\Route\RouteCalculator;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Str;
|
|
|
|
class AdminRouteController extends ApiController
|
|
{
|
|
public function __construct(private readonly RouteCalculator $calc) {}
|
|
|
|
public function index(Request $request): JsonResponse
|
|
{
|
|
$request->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'])
|
|
->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,
|
|
]);
|
|
}
|
|
}
|
|
}
|