diff --git a/resources/views/menu_v1/reports/vehicle_trips.blade.php b/resources/views/menu_v1/reports/vehicle_trips.blade.php
index a32b5aa..abccf6c 100644
--- a/resources/views/menu_v1/reports/vehicle_trips.blade.php
+++ b/resources/views/menu_v1/reports/vehicle_trips.blade.php
@@ -11,6 +11,12 @@
@section('customcss')
+
@endsection
@section('content')
@@ -346,33 +352,59 @@
});
}
- // Isi row.startLoc / row.finishLoc yang masih kosong (null/undefined/'')
- // menggunakan hasil reverse geocode dari koordinat start_lat_long / finish_lat_long.
- // Nilai disimpan dalam bentuk encodeURIComponent supaya konsisten dengan
- // field startLoc/finishLoc bawaan API yang memang sudah URL-encoded,
- // karena proses render memanggil decodeURIComponent(row.startLoc).
- async function fillMissingLocations(rows) {
+ // Bikin id unik untuk elemen placeholder lokasi per baris+kolom,
+ // dipakai untuk update teks langsung via DOM setelah geocode selesai di background.
+ function geoElementId(row, kind) {
+ // kind: 'start' | 'finish'
+ return `geo-${kind}-${row.id}-${row.trip_id}`;
+ }
+
+ // Jalankan reverse geocode di BACKGROUND (tidak menunggu / tidak blocking render tabel).
+ // Tabel sudah tampil duluan dengan placeholder "Load for location...", lalu begitu
+ // Nominatim balas untuk satu titik, teks placeholder-nya langsung di-update di tempat
+ // (tanpa redraw seluruh tabel), row demi row sesuai antrian reverseGeocode.
+ function scheduleBackgroundGeocode(rows) {
if (!Array.isArray(rows)) return;
- for (const row of rows) {
- if (!row.startLoc) {
- const {
- lat,
- lng
- } = parseLatLong(row.start_lat_long);
- const address = await reverseGeocode(lat, lng);
- row.startLoc = address ? encodeURIComponent(address) : null;
+ rows.forEach(row => {
+ const {
+ lat: startLat,
+ lng: startLng
+ } = parseLatLong(row.start_lat_long);
+ const startNearestSite = findNearestSite(startLat, startLng);
+
+ // hanya perlu geocode kalau bukan site terdaftar & startLoc memang kosong
+ if (!startNearestSite && !row.startLoc) {
+ geocodeAndUpdateCell(geoElementId(row, 'start'), startLat, startLng);
}
- if (!row.finishLoc) {
- const {
- lat,
- lng
- } = parseLatLong(row.finish_lat_long);
- const address = await reverseGeocode(lat, lng);
- row.finishLoc = address ? encodeURIComponent(address) : null;
+ const {
+ lat: finishLat,
+ lng: finishLng
+ } = parseLatLong(row.finish_lat_long);
+ const finishNearestSite = findNearestSite(finishLat, finishLng);
+
+ if (!finishNearestSite && !row.finishLoc) {
+ geocodeAndUpdateCell(geoElementId(row, 'finish'), finishLat, finishLng);
}
- }
+ });
+ }
+
+ // Panggil reverseGeocode (sudah antri otomatis max 1 request/detik),
+ // begitu hasilnya datang, cari elemen placeholder di DOM lewat id-nya
+ // dan replace teksnya. Kalau elemen sudah tidak ada (misal tabel sudah
+ // di-reload/filter berubah), update di-skip secara aman.
+ function geocodeAndUpdateCell(elId, lat, lng) {
+ reverseGeocode(lat, lng).then(address => {
+ const $el = $('#' + elId);
+ if (!$el.length) return; // tabel sudah berganti, abaikan hasil basi ini
+
+ if (address) {
+ $el.text(Helper.shortenText(address, 255)).removeClass('geo-pending');
+ } else {
+ $el.text('-').removeClass('geo-pending');
+ }
+ });
}
// ================================
@@ -421,17 +453,22 @@
&poolcode=${$('#poolcode').val() || ''}
`,
type: 'GET',
- success: async function(json) {
- // Isi startLoc/finishLoc yang kosong via Nominatim
- // SEBELUM tabel dirender, karena render() DataTables bersifat sinkron
- // dan tidak bisa menunggu fetch() di dalamnya.
- try {
- await fillMissingLocations(json && json.data);
- } catch (e) {
- console.error('Gagal melengkapi lokasi via reverse geocode:', e);
- }
-
+ success: function(json) {
+ // Tampilkan tabel DULUAN dengan semua data apa adanya
+ // (baris yang startLoc/finishLoc kosong akan muncul
+ // sebagai placeholder "Load for location...").
callback(json);
+
+ // Reset antrian geocode supaya request dari load sebelumnya
+ // (misal filter lama) tidak numpuk / membuang kuota Nominatim.
+ geocodeQueue = Promise.resolve();
+
+ // Baru SETELAH itu, isi lokasi yang kosong di background.
+ // setTimeout 0 supaya DOM tabel sudah pasti ter-render dulu
+ // sebelum kita cari elemen placeholder-nya.
+ setTimeout(() => {
+ scheduleBackgroundGeocode(json && json.data);
+ }, 0);
},
error: function(xhr, status, error) {
if (status !== 'abort') {
@@ -508,12 +545,23 @@
} = parseLatLong(row.start_lat_long);
const nearestSite = findNearestSite(startLat, startLng);
- // Prioritas: site terdaftar > startLoc (dari API atau hasil reverse geocode) > '-'
- const locationLabel = nearestSite ?
- formatSiteLabel(nearestSite) :
- (row.startLoc ?
- Helper.shortenText(decodeURIComponent(row.startLoc), 255) :
- '-');
+ let locationLabel;
+ if (nearestSite) {
+ // Prioritas 1: site terdaftar
+ locationLabel = formatSiteLabel(nearestSite);
+ } else if (row.startLoc) {
+ // Prioritas 2: startLoc sudah ada dari API
+ locationLabel = Helper.shortenText(decodeURIComponent(row.startLoc), 255);
+ } else if (type === 'export') {
+ // Export bersifat sinkron, tidak bisa menunggu hasil geocode background
+ locationLabel = '-';
+ } else {
+ // Prioritas 3: kosong -> tampilkan placeholder dulu,
+ // nanti di-update otomatis oleh scheduleBackgroundGeocode()
+ // begitu hasil dari Nominatim datang.
+ locationLabel =
+ `Load for location...`;
+ }
if (type === 'export') return locationLabel;
@@ -529,11 +577,17 @@
} = parseLatLong(row.finish_lat_long);
const nearestSite = findNearestSite(finishLat, finishLng);
- const locationLabel = nearestSite ?
- formatSiteLabel(nearestSite) :
- (row.finishLoc ?
- Helper.shortenText(decodeURIComponent(row.finishLoc), 255) :
- '-');
+ let locationLabel;
+ if (nearestSite) {
+ locationLabel = formatSiteLabel(nearestSite);
+ } else if (row.finishLoc) {
+ locationLabel = Helper.shortenText(decodeURIComponent(row.finishLoc), 255);
+ } else if (type === 'export') {
+ locationLabel = '-';
+ } else {
+ locationLabel =
+ `Load for location...`;
+ }
if (type === 'export') return locationLabel;