routes + route_stops tables. Admin CRUD with stops submitted as an ordered array (drag-reorder is client-side; sequence is array-index). Cloning produces an inactive copy with -COPY-XXXX suffix. RouteCalculator recomputes total_distance_km via ST_Distance_Sphere across stop coordinates + dumpsite, and estimated_duration_minutes from dwell time + travel time at config-driven avg speed (default 25 km/h). default_team_id stays nullable bigint until Module 9 adds the FK. 139 feature tests passing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
195 lines
11 KiB
PHP
195 lines
11 KiB
PHP
<?php
|
|
|
|
use App\Http\Controllers\Api\V1\Admin\AdminDropOffPointController;
|
|
use App\Http\Controllers\Api\V1\Admin\AdminDumpsiteController;
|
|
use App\Http\Controllers\Api\V1\Admin\AdminHouseholdController;
|
|
use App\Http\Controllers\Api\V1\Admin\AdminQrBatchController;
|
|
use App\Http\Controllers\Api\V1\Admin\AdminQrCodeController;
|
|
use App\Http\Controllers\Api\V1\Admin\AdminRouteController;
|
|
use App\Http\Controllers\Api\V1\Admin\AdminUserController;
|
|
use App\Http\Controllers\Api\V1\Auth\ForgotPasswordController;
|
|
use App\Http\Controllers\Api\V1\Auth\LoginController;
|
|
use App\Http\Controllers\Api\V1\Auth\LogoutController;
|
|
use App\Http\Controllers\Api\V1\Auth\MeController;
|
|
use App\Http\Controllers\Api\V1\Auth\RefreshTokenController;
|
|
use App\Http\Controllers\Api\V1\Auth\RegisterController;
|
|
use App\Http\Controllers\Api\V1\Auth\ResendOtpController;
|
|
use App\Http\Controllers\Api\V1\Auth\ResetPasswordController;
|
|
use App\Http\Controllers\Api\V1\Auth\SelfProfileController;
|
|
use App\Http\Controllers\Api\V1\Auth\UpdateSelfController;
|
|
use App\Http\Controllers\Api\V1\Auth\VerifyOtpController;
|
|
use App\Http\Controllers\Api\V1\DropOff\DropOffPointController;
|
|
use App\Http\Controllers\Api\V1\Geo\BarangayController;
|
|
use App\Http\Controllers\Api\V1\Geo\CityMunicipalityController;
|
|
use App\Http\Controllers\Api\V1\Geo\ProvinceController;
|
|
use App\Http\Controllers\Api\V1\Geo\RegionController;
|
|
use App\Http\Controllers\Api\V1\Geo\ResolveLocationController;
|
|
use App\Http\Controllers\Api\V1\Geo\ServiceAreaController;
|
|
use App\Http\Controllers\Api\V1\HealthController;
|
|
use App\Http\Controllers\Api\V1\Household\HouseholdController;
|
|
use App\Http\Controllers\Api\V1\Qr\MyQrCodeController;
|
|
use Illuminate\Support\Facades\Route;
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| API Routes (v1)
|
|
|--------------------------------------------------------------------------
|
|
| All routes here are prefixed with /api/v1 (configured in bootstrap/app.php).
|
|
*/
|
|
|
|
Route::get('/health', HealthController::class)->name('api.v1.health');
|
|
|
|
Route::prefix('auth')->name('api.v1.auth.')->group(function () {
|
|
Route::middleware('throttle:10,1')->group(function () {
|
|
Route::post('/register', RegisterController::class)->name('register');
|
|
Route::post('/login', LoginController::class)->name('login');
|
|
Route::post('/forgot-password', ForgotPasswordController::class)->name('forgot-password');
|
|
Route::post('/reset-password', ResetPasswordController::class)->name('reset-password');
|
|
Route::post('/otp/verify', VerifyOtpController::class)->name('otp.verify');
|
|
Route::post('/otp/resend', ResendOtpController::class)->name('otp.resend');
|
|
});
|
|
|
|
Route::middleware('auth:sanctum')->group(function () {
|
|
Route::post('/logout', LogoutController::class)->name('logout');
|
|
Route::post('/refresh', RefreshTokenController::class)->name('refresh');
|
|
Route::get('/me', MeController::class)->name('me');
|
|
});
|
|
});
|
|
|
|
Route::middleware('auth:sanctum')->prefix('me')->name('api.v1.me.')->group(function () {
|
|
Route::patch('/', UpdateSelfController::class)->name('update');
|
|
Route::get('/profile', [SelfProfileController::class, 'show'])->name('profile.show');
|
|
Route::patch('/profile', [SelfProfileController::class, 'update'])->name('profile.update');
|
|
});
|
|
|
|
Route::prefix('admin/users')
|
|
->name('api.v1.admin.users.')
|
|
->middleware(['auth:sanctum', 'role:admin'])
|
|
->group(function () {
|
|
Route::get('/', [AdminUserController::class, 'index'])->name('index');
|
|
Route::get('/{user:uuid}', [AdminUserController::class, 'show'])->name('show');
|
|
Route::patch('/{user:uuid}', [AdminUserController::class, 'update'])->name('update');
|
|
Route::delete('/{user:uuid}', [AdminUserController::class, 'destroy'])->name('destroy');
|
|
Route::post('/{user:uuid}/suspend', [AdminUserController::class, 'suspend'])->name('suspend');
|
|
Route::post('/{user:uuid}/activate', [AdminUserController::class, 'activate'])->name('activate');
|
|
Route::post('/{user:uuid}/approve-profile', [AdminUserController::class, 'approveProfile'])->name('approve-profile');
|
|
Route::post('/{user:uuid}/reject-profile', [AdminUserController::class, 'rejectProfile'])->name('reject-profile');
|
|
});
|
|
|
|
Route::prefix('households')
|
|
->name('api.v1.households.')
|
|
->middleware(['auth:sanctum'])
|
|
->group(function () {
|
|
Route::post('/', [HouseholdController::class, 'store'])->name('store');
|
|
Route::get('/{household}', [HouseholdController::class, 'show'])->name('show');
|
|
Route::patch('/{household}', [HouseholdController::class, 'update'])->name('update');
|
|
Route::post('/{household}/proof', [HouseholdController::class, 'uploadProof'])->name('proof.upload');
|
|
Route::post('/{household}/members', [HouseholdController::class, 'addMember'])->name('members.add');
|
|
Route::delete('/{household}/members/{member}', [HouseholdController::class, 'removeMember'])->name('members.remove');
|
|
});
|
|
|
|
Route::prefix('admin/households')
|
|
->name('api.v1.admin.households.')
|
|
->middleware(['auth:sanctum', 'role:admin'])
|
|
->group(function () {
|
|
Route::get('/', [AdminHouseholdController::class, 'index'])->name('index');
|
|
Route::get('/{household}', [AdminHouseholdController::class, 'show'])->name('show');
|
|
Route::post('/{household}/approve', [AdminHouseholdController::class, 'approve'])->name('approve');
|
|
Route::post('/{household}/reject', [AdminHouseholdController::class, 'reject'])->name('reject');
|
|
});
|
|
|
|
Route::prefix('drop-off-points')->name('api.v1.drop-off-points.')->group(function () {
|
|
Route::get('/nearby', [DropOffPointController::class, 'nearby'])->name('nearby');
|
|
Route::get('/{drop_off_point}', [DropOffPointController::class, 'show'])->name('show');
|
|
});
|
|
|
|
Route::prefix('admin/drop-off-points')
|
|
->name('api.v1.admin.drop-off-points.')
|
|
->middleware(['auth:sanctum', 'role:admin'])
|
|
->group(function () {
|
|
Route::get('/', [AdminDropOffPointController::class, 'index'])->name('index');
|
|
Route::post('/', [AdminDropOffPointController::class, 'store'])->name('store');
|
|
Route::get('/{drop_off_point}', [AdminDropOffPointController::class, 'show'])->name('show');
|
|
Route::patch('/{drop_off_point}', [AdminDropOffPointController::class, 'update'])->name('update');
|
|
Route::delete('/{drop_off_point}', [AdminDropOffPointController::class, 'destroy'])->name('destroy');
|
|
Route::post('/{drop_off_point}/capacity', [AdminDropOffPointController::class, 'logCapacity'])->name('capacity.store');
|
|
});
|
|
|
|
Route::prefix('admin/dumpsites')
|
|
->name('api.v1.admin.dumpsites.')
|
|
->middleware(['auth:sanctum', 'role:admin'])
|
|
->group(function () {
|
|
Route::get('/', [AdminDumpsiteController::class, 'index'])->name('index');
|
|
Route::post('/', [AdminDumpsiteController::class, 'store'])->name('store');
|
|
Route::get('/{dumpsite}', [AdminDumpsiteController::class, 'show'])->name('show');
|
|
Route::patch('/{dumpsite}', [AdminDumpsiteController::class, 'update'])->name('update');
|
|
Route::delete('/{dumpsite}', [AdminDumpsiteController::class, 'destroy'])->name('destroy');
|
|
});
|
|
|
|
Route::prefix('admin/qr-batches')
|
|
->name('api.v1.admin.qr-batches.')
|
|
->middleware(['auth:sanctum', 'role:admin'])
|
|
->group(function () {
|
|
Route::get('/', [AdminQrBatchController::class, 'index'])->name('index');
|
|
Route::post('/', [AdminQrBatchController::class, 'store'])->name('store');
|
|
Route::get('/{qr_code_batch}', [AdminQrBatchController::class, 'show'])->name('show');
|
|
Route::post('/{qr_code_batch}/mark-printed', [AdminQrBatchController::class, 'markPrinted'])->name('mark-printed');
|
|
Route::get('/{qr_code_batch}/print.pdf', [AdminQrBatchController::class, 'printPdf'])->name('print-pdf');
|
|
});
|
|
|
|
Route::prefix('admin/qr-codes')
|
|
->name('api.v1.admin.qr-codes.')
|
|
->middleware(['auth:sanctum', 'role:admin'])
|
|
->group(function () {
|
|
Route::get('/{serial}', [AdminQrCodeController::class, 'show'])->name('show');
|
|
Route::post('/{qr_code}/void', [AdminQrCodeController::class, 'void'])->name('void');
|
|
});
|
|
|
|
Route::middleware('auth:sanctum')->prefix('me/qr-codes')->name('api.v1.me.qr-codes.')->group(function () {
|
|
Route::get('/', [MyQrCodeController::class, 'index'])->name('index');
|
|
Route::get('/balance', [MyQrCodeController::class, 'balance'])->name('balance');
|
|
Route::post('/{qr_code}/activate', [MyQrCodeController::class, 'activate'])->name('activate');
|
|
});
|
|
|
|
Route::prefix('admin/routes')
|
|
->name('api.v1.admin.routes.')
|
|
->middleware(['auth:sanctum', 'role:admin'])
|
|
->group(function () {
|
|
Route::get('/', [AdminRouteController::class, 'index'])->name('index');
|
|
Route::post('/', [AdminRouteController::class, 'store'])->name('store');
|
|
Route::get('/{route}', [AdminRouteController::class, 'show'])->name('show');
|
|
Route::patch('/{route}', [AdminRouteController::class, 'update'])->name('update');
|
|
Route::delete('/{route}', [AdminRouteController::class, 'destroy'])->name('destroy');
|
|
Route::post('/{route}/clone', [AdminRouteController::class, 'clone'])->name('clone');
|
|
});
|
|
|
|
Route::middleware('auth:sanctum')->prefix('households')->name('api.v1.households.')->group(function () {
|
|
Route::post('/{household}/reassign-drop-off', [HouseholdController::class, 'reassignDropOff'])
|
|
->name('reassign-drop-off');
|
|
});
|
|
|
|
Route::middleware('auth:sanctum')->group(function () {
|
|
Route::get('/me', MeController::class)->name('api.v1.me');
|
|
});
|
|
|
|
Route::prefix('geo')->name('api.v1.geo.')->group(function () {
|
|
Route::get('/regions', RegionController::class)->name('regions.index');
|
|
Route::get('/provinces', ProvinceController::class)->name('provinces.index');
|
|
Route::get('/cities', CityMunicipalityController::class)->name('cities.index');
|
|
Route::get('/barangays', BarangayController::class)->name('barangays.index');
|
|
Route::post('/resolve', ResolveLocationController::class)->name('resolve');
|
|
});
|
|
|
|
Route::prefix('service-areas')
|
|
->name('api.v1.service-areas.')
|
|
->middleware(['auth:sanctum', 'role:admin'])
|
|
->group(function () {
|
|
Route::get('/', [ServiceAreaController::class, 'index'])->name('index');
|
|
Route::post('/', [ServiceAreaController::class, 'store'])->name('store');
|
|
Route::get('/{service_area}', [ServiceAreaController::class, 'show'])->name('show');
|
|
Route::patch('/{service_area}', [ServiceAreaController::class, 'update'])->name('update');
|
|
Route::delete('/{service_area}', [ServiceAreaController::class, 'destroy'])->name('destroy');
|
|
Route::post('/{service_area}/barangays', [ServiceAreaController::class, 'attachBarangays'])->name('barangays.attach');
|
|
Route::delete('/{service_area}/barangays/{barangay}', [ServiceAreaController::class, 'detachBarangay'])->name('barangays.detach');
|
|
});
|