diff --git a/app/Http/Controllers/TripsLabelingController.php b/app/Http/Controllers/TripsLabelingController.php new file mode 100644 index 0000000..c30a40a --- /dev/null +++ b/app/Http/Controllers/TripsLabelingController.php @@ -0,0 +1,241 @@ +where('id', $id) + ->first(); + + if (!$data) { + $apiResp = Responses::error("Sites labeling not found"); + return new Response($apiResp, $apiResp["meta"]["code"]); + } + + $apiResp = [ + "meta" => [ + "code" => 200, + "type" => "success", + "message" => "success", + ], + "data" => $data, + ]; + + return new Response($apiResp, $apiResp["meta"]["code"]); + + } catch (\Exception $e) { + $apiResp = Responses::error($e->getMessage()); + return new Response($apiResp, $apiResp["meta"]["code"]); + } + } + /** + * View + */ + + public function view_trips_label(Request $req) + { + $data = []; + + $log = [ + "module" => "Trips Label", + "action" => "View", + "desc" => "Open Trips Label", + ]; + UserLogs::insert(Auth::user()->id, $log); + return view("menu_v1.trips_label", $data); + } + + /** + * API + */ + public function api_list_trips_label(Request $req) + { + try { + $datas = DB::table('t_sites_label') + ->where('isdeleted', 0) + ->get(); + + foreach ($datas as $key => $row) { + $datas[$key]->DT_RowIndex = $key + 1; + $datas[$key]->action = "-"; + } + + $apiResp = Responses::success("success list sites labeling"); + $apiResp["count"] = count($datas); + $apiResp["data"] = $datas; + return new Response($apiResp, $apiResp["meta"]["code"]); + } catch (\Exception $e) { + $apiResp = Responses::error($e->getMessage()); + return new Response($apiResp, $apiResp["meta"]["code"]); + } + } + + /** + * Show the form for creating a new resource. + * + * @return Response + */ + public function api_add_trips_label(Request $req) + { + try { + $now = time(); + + $input = [ + "address" => $req->address, + "lat" => $req->lat, + "lng" => $req->lng, + "radius" => $req->radius + ]; + $rulesInput = [ + "address" => "required|string", + "lat" => "required|numeric|between:-90,90", + "lng" => "required|numeric|between:-180,180", + "radius" => "required|integer|min:0", + ]; + + // validasi input + $isValidInput = Validator::make($input, $rulesInput); + if (!$isValidInput->passes()) { + $apiResp = Responses::bad_input($isValidInput->messages()->first()); + return new Response($apiResp, $apiResp["meta"]["code"]); + } + + DB::beginTransaction(); + + $insert = [ + "address" => $req->address, + "lat" => $req->lat, + "lng" => $req->lng, + "radius" => $req->radius, + // "created_at" => date("Y-m-d H:i:s", $now), + // "udt" => date("Y-m-d H:i:s", $now), + // "created_by" => Auth::user()->id, + // "uby" => Auth::user()->id + ]; + DB::table('t_sites_label')->insert($insert); + $apiResp = Responses::created("success add new trips label"); + + DB::commit(); + + + $log = [ + "module" => "Trips Label", + "action" => "Create", + "desc" => "Add new trips label: " . $req->address, + ]; + UserLogs::insert(Auth::user()->id, $log); + return new Response($apiResp, $apiResp["meta"]["code"]); + } catch (\Exception $e) { + DB::rollBack(); + $apiResp = Responses::error($e->getMessage()); + return new Response($apiResp, $apiResp["meta"]["code"]); + } + } + + /** + * Update the specified resource in storage. + * + * @param Request $req + * @param int $id + * @return Response + */ + public function api_edit_trips_label(Request $req, $id) + { + try { + $now = time(); + + $input = [ + "address" => $req->address, + "lat" => $req->lat, + "lng" => $req->lng, + "radius" => $req->radius + ]; + $rulesInput = [ + "address" => "required|string", + "lat" => "required|numeric|between:-90,90", + "lng" => "required|numeric|between:-180,180", + "radius" => "required|integer|min:0", + ]; + + // validasi input + $isValidInput = Validator::make($input, $rulesInput); + if (!$isValidInput->passes()) { + $apiResp = Responses::bad_input($isValidInput->messages()->first()); + return new Response($apiResp, $apiResp["meta"]["code"]); + } + + DB::beginTransaction(); + + $update = [ + "address" => $req->address, + "lat" => $req->lat, + "lng" => $req->lng, + "radius" => $req->radius, + ]; + DB::table('t_sites_label')->where('id', $id)->update($update); + + $apiResp = Responses::created("success update trips label"); + + DB::commit(); + + $log = [ + "module" => "Trips Label", + "action" => "Update", + "desc" => "Update trips label: " . $req->address, + ]; + UserLogs::insert(Auth::user()->id, $log); + return new Response($apiResp, $apiResp["meta"]["code"]); + } catch (\Exception $e) { + DB::rollBack(); + $apiResp = Responses::error($e->getMessage()); + return new Response($apiResp, $apiResp["meta"]["code"]); + } + } + + /** + * Remove the specified resource from storage. + * + * @param int $id + * @return Response + */ + public function api_del_trips_label($id) + { + try { + DB::beginTransaction(); + + DB::table('t_sites_label')->where('id', $id)->update([ + 'isdeleted' => 1, + // 'ddt' => date("Y-m-d H:i:s", time()), + // 'dby' => Auth::user()->id + ]); + + DB::commit(); + + $apiResp = Responses::success("success delete trips label"); + + $log = [ + "module" => "Trips Label", + "action" => "Delete", + "desc" => "Delete trips label: " . $id, + ]; + UserLogs::insert(Auth::user()->id, $log); + return new Response($apiResp, $apiResp["meta"]["code"]); + } catch (\Exception $e) { + DB::rollBack(); + $apiResp = Responses::error($e->getMessage()); + return new Response($apiResp, $apiResp["meta"]["code"]); + } + } +} diff --git a/app/Http/Controllers/VehicleMaintenanceController.php b/app/Http/Controllers/VehicleMaintenanceController.php index 479be71..2592dc4 100644 --- a/app/Http/Controllers/VehicleMaintenanceController.php +++ b/app/Http/Controllers/VehicleMaintenanceController.php @@ -20,7 +20,7 @@ class VehicleMaintenanceController extends Controller ->first(); if (!$data) { - $apiResp = Responses::error("Truck type not found", 404); + $apiResp = Responses::error("Vehicle Maintenance type not found"); return new Response($apiResp, $apiResp["meta"]["code"]); } diff --git a/resources/views/menu_v1/reports/vehicle_trips.blade.php b/resources/views/menu_v1/reports/vehicle_trips.blade.php index 3011ca2..ef7e2cc 100644 --- a/resources/views/menu_v1/reports/vehicle_trips.blade.php +++ b/resources/views/menu_v1/reports/vehicle_trips.blade.php @@ -129,74 +129,6 @@ Detail - @@ -207,18 +139,6 @@ @endsection @@ -228,9 +148,6 @@ - @@ -254,15 +171,12 @@ activate: function() { Wrapper.init(); Wrapper.event(); - // DTable.table = $('#tVehicleTrips').DataTable() - // DTable.activate(); }, init: () => { $('#tgl0, #tgl1').datetimepicker({ format: 'd-m-Y H:i', defaultTime: '00:00', closeOnDateSelect: true, - // mask:true }); }, @@ -283,7 +197,6 @@ const tgl1 = $(this).data('tgl1'); const nopol1 = $(this).data('nopol1'); - // $("#mdlDetailTrip").load("{{ route('view_report_trip_detail') }}"); $('#mdlDetailTrip').empty().load("{{ route('view_report_trip_detail') }}", `vid=${vid}&tgl0=${tgl0}&tgl1=${tgl1}&nopol1=${nopol1}`, () => { $('#mdlDetailTrip').modal('show') @@ -293,35 +206,38 @@ $('#filterNopol').select2({}); }, }; - // Data referensi site (lat/lng dalam desimal, bukan koma) - const SITE_LOCATIONS = [{ - name: 'Bitterlaun', - district: 'Dili', - subdistrict: 'Cristo Rei', - village: 'Hera', - lat: -8.548328, - lng: 125.623808 - }, - { - name: 'Bebonuk', - district: 'Dili', - subdistrict: 'Dom Aleixo', - village: 'Comoro', - lat: -8.547527, - lng: 125.548280 - }, - { - name: 'Balide', - district: 'Dili', - subdistrict: 'Nain Feto', - village: 'Santa Cruz', - lat: -8.564396, - lng: 125.582151 - }, - ]; - // Radius toleransi "berdekatan" dalam meter — sesuaikan sesuai kebutuhan - const SITE_PROXIMITY_RADIUS_METERS = 500; + // ================================ + // SITE LABELING (dari API, bukan hardcode) + // ================================ + + // Data referensi site (lat/lng dalam desimal) — diisi via loadSiteLocations() + let SITE_LOCATIONS = []; + + // Ambil data site labeling dari API + function loadSiteLocations() { + return $.ajax({ + url: "{{ route('api_list_trips_label') }}", + type: 'GET', + dataType: 'json', + }).then(function(json) { + const list = (json && json.data) ? json.data : []; + // hanya ambil yang belum dihapus (isdeleted = 0), lalu normalisasi field yang dipakai + SITE_LOCATIONS = list + .filter(site => Number(site.isdeleted) === 0) + .map(site => ({ + id: site.id, + address: site.address, + lat: parseFloat(site.lat), + lng: parseFloat(site.lng), + // radius dari API sudah dalam meter + radius: parseFloat(site.radius), + })); + }).catch(function(err) { + console.error('Failed to load site locations:', err); + SITE_LOCATIONS = []; + }); + } // Helper untuk parse string "lat, long" menjadi { lat, lng } function parseLatLong(latLongStr) { @@ -361,6 +277,7 @@ } // Cari site terdekat dari koordinat yang diberikan; null jika tidak ada yang cukup dekat + // menggunakan radius masing-masing site (bukan radius global) function findNearestSite(lat, lng) { if (lat === undefined || lng === undefined || lat === null || lng === null || lat === '' || lng === '') { return null; @@ -369,7 +286,7 @@ let minDist = Infinity; SITE_LOCATIONS.forEach(site => { const dist = getDistanceMeters(parseFloat(lat), parseFloat(lng), site.lat, site.lng); - if (dist <= SITE_PROXIMITY_RADIUS_METERS && dist < minDist) { + if (dist <= site.radius && dist < minDist) { minDist = dist; nearest = site; } @@ -378,10 +295,15 @@ } // Format label sesuai output yang diinginkan + // address dari API sudah lengkap (mis. "Bitterlaun, Hera, Cristo Rei, Dili") function formatSiteLabel(site) { - return `Site ${site.name}, ${site.village}, ${site.subdistrict}, ${site.district}, Timor-Leste`; + return `Site ${site.address}, Timor-Leste`; } + // ================================ + // END SITE LABELING + // ================================ + const DTable = { table: null, lastAjax: null, // keep track of the last ajax request @@ -704,7 +626,6 @@ // Convert canvas to ImageState. let imageData = ctx.getImageData(0, 0, imgWidth, imgHeight) - // console.log(imageState.data.byteLength + ' bytes.') // Convert canvas to Blob canvas.toBlob((blob) => { @@ -712,13 +633,6 @@ newReader.addEventListener('loadend', () => { // Convert canvas to ArrayBuffer let arrayBuffer = newReader.result - // console.log(arrayBuffer.byteLength + ' bytes.') - - // Convert ArrayBuffer to Blob - // let blob = new Blob([arrayBuffer], {type: mimeType}) - - // Dispay Blob content in an Image. - // console.log(URL.createObjectURL(blob)) // Generate as file let newFile = new File([arrayBuffer], fileName, { @@ -736,6 +650,10 @@ }); } - Wrapper.activate(); + // Pastikan data site sudah ter-load SEBELUM tabel trip dirender, + // supaya findNearestSite() punya data yang benar saat kolom start/finish di-render. + loadSiteLocations().always(function() { + Wrapper.activate(); + }); @endsection diff --git a/resources/views/menu_v1/trips_label.blade.php b/resources/views/menu_v1/trips_label.blade.php new file mode 100755 index 0000000..c04595d --- /dev/null +++ b/resources/views/menu_v1/trips_label.blade.php @@ -0,0 +1,656 @@ +@extends('app.app') + +@section('title') + Trips Label +@endsection + +@section('customcss') + +@endsection + +@section('content') +
+
+
+
+
+
+ {{--

Vehicle Types (0)

--}} +

Trips Label

+
+ @can('config_truck_type.create') +
+ +
+ @endcan + +
+ {{-- --}} + {{-- --}} +
+
+
+
+
+ + + + + + + + + + + + + + +
#ActionAddressLatitudeLongitudeRadius (m)
+
+
+
+
+
+ + + + + + +@endsection + +@section('customjs') + + +@endsection diff --git a/routes/web.php b/routes/web.php index c5e0238..f025227 100755 --- a/routes/web.php +++ b/routes/web.php @@ -145,6 +145,10 @@ Route::middleware(["auth", "auth.user"])->group(function () { ->name("view_vehicle_maintenance_type") ->middleware("permission:vehicle_maintenance_type.view"); + Route::get("/trips_label", "TripsLabelingController@view_trips_label") + ->name("view_trips_label") + ->middleware("permission:trips_label.view"); + Route::get("/config/devices", "DevicesController@view_devices") ->name("view_config_devices") ->middleware("permission:config_master_device.view"); @@ -636,6 +640,24 @@ Route::middleware(["auth", "auth.user"])->group(function () { "api_del_vehicle_maintenance_type" ); + Route::get("/api/trips_label/{id}", "TripsLabelingController@api_show_trips_label")->name( + "api_show_trips_label" + ); + + Route::get("/api/trips_label", "TripsLabelingController@api_list_trips_label")->name( + "api_list_trips_label" + ); + + Route::post("/api/trips_label", "TripsLabelingController@api_add_trips_label")->name( + "api_add_trips_label" + ); + Route::put("/api/trips_label/{id}", "TripsLabelingController@api_edit_trips_label")->name( + "api_edit_trips_label" + ); + Route::delete("/api/trips_label/{id}", "TripsLabelingController@api_del_trips_label")->name( + "api_del_trips_label" + ); + Route::get("/api/devices", "DevicesController@api_list_devices")->name("api_list_devices"); Route::get("/api/devices/{id}", "DevicesController@api_show_device")->name("api_show_device"); Route::post("/api/devices", "DevicesController@api_add_device")->name("api_add_device");