From c3abd60868a1980e9d7123b9ad902aae0012ec5d Mon Sep 17 00:00:00 2001 From: Pringgosutono Date: Tue, 16 Dec 2025 07:59:50 +0700 Subject: [PATCH] feat: Implement GPS reports functionality including vehicle trips, trip details, and abnormalities. --- app/Http/Controllers/ReportsController.php | 142 ++++++- .../reports/view_trip_detail.blade.php | 374 ++++++++++++++++++ routes/api.php | 3 + 3 files changed, 502 insertions(+), 17 deletions(-) create mode 100644 resources/views/menu_v1/reports/view_trip_detail.blade.php diff --git a/app/Http/Controllers/ReportsController.php b/app/Http/Controllers/ReportsController.php index ba319ce..61fcf72 100644 --- a/app/Http/Controllers/ReportsController.php +++ b/app/Http/Controllers/ReportsController.php @@ -6,19 +6,15 @@ use App\Http\Controllers\Controller; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Storage; use Validator; use Auth; use App\Responses; use App\Helper; -use Maatwebsite\Excel\Facades\Excel; -use Maatwebsite\Excel\Concerns\FromArray; -use Maatwebsite\Excel\Concerns\WithHeadings; -use Maatwebsite\Excel\Concerns\WithStyles; -use Maatwebsite\Excel\Concerns\WithCustomStartCell; -use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use Carbon\Carbon; use App\Models\UserLogs; +use Illuminate\Support\Facades\Crypt; +use Illuminate\Contracts\Encryption\DecryptException; +use Illuminate\Support\Facades\Log; class ReportsController extends Controller { @@ -160,9 +156,9 @@ class ReportsController extends Controller // // RETURN 1 - LIST // if($req->type != 'report'){ - $apiResp = Responses::success("success list vehicles report"); - $apiResp["data"] = $list; - return new Response($apiResp, $apiResp["meta"]["code"]); + $apiResp = Responses::success("success list vehicles report"); + $apiResp["data"] = $list; + return new Response($apiResp, $apiResp["meta"]["code"]); // } // // RETURN 2 - REPORT @@ -232,8 +228,8 @@ class ReportsController extends Controller // return Excel::download($export, 'trip_report.xlsx'); // } } catch (\Exception $e) { - $apiResp = Responses::error($e->getMessage()); - return new Response($apiResp, $apiResp["meta"]["code"]); + $apiResp = Responses::error($e->getMessage()); + return new Response($apiResp, $apiResp["meta"]["code"]); // return Responses::json(Responses::SERVER_ERROR, 'An error occurred while generating the report.', (object)[]); } } @@ -356,16 +352,128 @@ class ReportsController extends Controller // // RETURN 1 - LIST // if($req->type != 'report'){ - $apiResp = Responses::success("success list abnormalities report"); - $apiResp["data"] = $list; - return new Response($apiResp, $apiResp["meta"]["code"]); + $apiResp = Responses::success("success list abnormalities report"); + $apiResp["data"] = $list; + return new Response($apiResp, $apiResp["meta"]["code"]); // } } catch (\Exception $e) { - $apiResp = Responses::error($e->getMessage()); - return new Response($apiResp, $apiResp["meta"]["code"]); + $apiResp = Responses::error($e->getMessage()); + return new Response($apiResp, $apiResp["meta"]["code"]); // return Responses::json(Responses::SERVER_ERROR, 'An error occurred while generating the report.', (object)[]); } } + public function api_view_trip_detail(Request $req, $token) + { + // token = base64_encode(tgl0 + '|' + tgl1 + '|' + nopol1 + '|' + now_unix()) + // $token = "1759686805|1759693045|B.10-517|1765845676"; + $token = base64_decode($token); + $token = explode('|', $token); + $tgl0 = (int) $token[0] ?? null; + $tgl1 = (int) $token[1] ?? null; + $nopol1 = $token[2] ?? null; + $now = (int) $token[3] ?? null; + $isMoreThanOneHour = time() - $now > 60 * 60; + + if ($tgl0 == null || $tgl1 == null || $nopol1 == null || $now == null || $isMoreThanOneHour) { + $apiResp = Responses::bad_request("Invalid token"); + return new Response($apiResp, $apiResp["meta"]["code"]); + } + + // get vid by nopol1 + $vid = DB::select("SELECT id FROM t_vehicles WHERE nopol1 = ?", [$nopol1]); + if (count($vid) == 0) { + $apiResp = Responses::bad_request("Vehicle not found"); + return new Response($apiResp, $apiResp["meta"]["code"]); + } + $vid = $vid[0]->id; + + $d = [$vid, $tgl0, $tgl1]; + $list = DB::select("SELECT + t.crt_d, t.latitude, t.longitude, t.speed, + tgta.fulladdress, + t.pre_milleage, t.vhc_milleage, fuel_count + FROM + t_gps_tracks t + left join t_gps_tracks_address tgta on tgta.master_id = t.id + WHERE + t.vhc_id = ? + and t.latitude IS NOT NULL + AND t.longitude IS NOT NULL + AND t.action = 'location' + AND t.crt_d BETWEEN ? AND ? + ORDER BY t.crt_d asc + ", $d); + if (count($list) == 0) { + $apiResp = Responses::not_found("Track not found"); + return new Response($apiResp, $apiResp["meta"]["code"]); + } + + $start = [ + 'time' => $list[0]->crt_d, + 'fulladdress' => urldecode($list[0]->fulladdress), + 'mileage' => $list[0]->vhc_milleage, + ]; + $finish = [ + 'time' => $list[count($list) - 1]->crt_d, + 'fulladdress' => urldecode($list[count($list) - 1]->fulladdress), + 'mileage' => $list[count($list) - 1]->vhc_milleage, + ]; + + $t0 = Carbon::createFromTimestamp($list[0]->crt_d); + $t1 = Carbon::createFromTimestamp($list[count($list) - 1]->crt_d); + $diff = $t1->diff($t0); + $hours = $diff->h + ($diff->days * 24); // include days converted to hours + $minutes = $diff->i; + $duration = "{$hours} hour" . ($hours > 1 ? 's' : '') . " {$minutes} minute" . ($minutes > 1 ? 's' : ''); + + $distance = $list[count($list) - 1]->vhc_milleage - $list[0]->vhc_milleage; + $fuel_consumed = $list[count($list) - 1]->fuel_count - $list[0]->fuel_count; + + $data = [ + 'nopol1' => $nopol1, + 'vid' => $vid, + 'tgl0' => $tgl0, + 'tgl1' => $tgl1, + 'list' => $list, + 'start' => $start, + 'finish' => $finish, + 'duration' => $duration, + 'distance' => $distance, + 'fuel_consumed' => $fuel_consumed, + ]; + // dd($list); + return view('menu_v1.reports.view_trip_detail', $data); + } + + public function decryptText(string $ciphertext = "FqujI/06YPCpCSP0Xlt6bA==") + { + $secret = "Mg3Xt1169cRNJWX6HG12DVkgmAXINVuq"; + $iv_str = "Mg3Xt1169cRNJWX6"; + + // Derive key and IV exactly as the web tool does + $md5 = md5($secret, true); // 16-byte raw binary MD5 + $key = $md5 . $md5; // 32 bytes → AES-256 key + $iv = substr($md5, 0, 16); // IV = first 16 bytes of MD5 + + $data = base64_decode($ciphertext, true); + if ($data === false) { + return 'Invalid Base64'; + } + + $plaintext = openssl_decrypt( + $data, + 'aes-256-cbc', + $key, + 0, + $iv + ); + + if ($plaintext === false) { + return 'Decryption failed: ' . openssl_error_string(); + } + + return $plaintext; // → "1764833739" + } } diff --git a/resources/views/menu_v1/reports/view_trip_detail.blade.php b/resources/views/menu_v1/reports/view_trip_detail.blade.php new file mode 100644 index 0000000..2d85baa --- /dev/null +++ b/resources/views/menu_v1/reports/view_trip_detail.blade.php @@ -0,0 +1,374 @@ + + + + + Movana Fleet Management | @yield('title', 'App') + + + + + + + + + + + + + + + @yield('customcss') + + +{{-- + + --}} + + +
+
+
+
+

{{$nopol1}}

+
+
+
+
+

Start

+

{{ $start['time'] }}

+

Vehicle Mileage: {{number_format($start['mileage'], 2)}} km

+

{{$start['fulladdress']}}

+
+
+

Finish

+

{{ $finish['time'] }}

+

Vehicle Mileage: {{number_format($finish['mileage'], 2)}} km

+

{{$finish['fulladdress']}}

+
+
+

Distance

+

{{number_format($distance, 2)}} km

+
+
+

Duration

+

{{$duration}}

+
+
+

Fuel consumption

+

{{$fuel_consumed / 10}} L

+
+
+
+
+
+ + @foreach ($list as $item) + +
  • +
    +
    +

    Time: {{ $item->crt_d }} +

    +

    Vehicle Mileage: + {{number_format($item->vhc_milleage, 2)}} km +

    +

    Current speed: {{$item->speed}} km/h

    +
    +
    +

    {{number_format($item->latitude, 6)}}, + {{number_format($item->longitude, 6)}} +

    +

    {{urldecode($item->fulladdress)}}

    +
    +
    +
  • + @endforeach +
    +
    + +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/routes/api.php b/routes/api.php index dfac777..23d0680 100755 --- a/routes/api.php +++ b/routes/api.php @@ -36,4 +36,7 @@ Route::post("/v1/inject/add_conf_rate_v1", "InjectController@add_conf_rate_v1"); Route::post("/v1/storage/save_photos", "StorageController@save_photos")->name("api_storage_save_photos"); Route::post("auth/login", [AuthController::class, "login"]); + +Route::get("/view/vehicle-trip-detail/{token}", "ReportsController@api_view_trip_detail")->name("api_view_trip_detail"); + // });