Compare commits
	
		
			2 Commits
		
	
	
		
			3ad22e0cf5
			...
			ccd7b19ff5
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| ccd7b19ff5 | |||
| c492711d83 | 
| @ -22,11 +22,11 @@ class ReportsController extends Controller | ||||
| { | ||||
| 	public function view_report_vehicle_trips(Request $req) | ||||
| 	{ | ||||
| 		$q = "select id, nopol1 from t_vehicles WHERE dlt is null order by nopol1"; | ||||
| 		$listNopol = DB::select($q); | ||||
|  | ||||
| 		$data = [ | ||||
| 			// 'lanes' => ConfRates::getLanesActive(), | ||||
| 			// 'provs' => Region::listProv(), | ||||
| 			// 'vendors' => Users::getUsersActiveByRole(Users::ROLE_VENDOR), | ||||
| 			// 'truck_types' => ConfTruckTypes::listTruckTypes(ConfTruckTypes::IS_ACTIVE), | ||||
| 			'listNopol' => $listNopol, | ||||
| 		]; | ||||
|  | ||||
| 		return view('menu_v1.reports.vehicle_trips', $data); | ||||
| @ -49,10 +49,9 @@ class ReportsController extends Controller | ||||
| 			return new Response($apiResp, $apiResp["meta"]["code"]); | ||||
| 		} | ||||
|  | ||||
|  | ||||
|  | ||||
| 		$from_date = $req->input('from_date'); | ||||
| 		$to_date = $req->input('to_date'); | ||||
| 		$vid = $req->input('vid'); | ||||
| 		// $from_date = 1756054800; | ||||
| 		// $to_date = 1756745940; | ||||
|  | ||||
| @ -81,19 +80,25 @@ class ReportsController extends Controller | ||||
| 						SUM(trip_start) OVER (PARTITION BY vhc_id ORDER BY crt_d) AS trip_id | ||||
| 					FROM trips | ||||
| 				) | ||||
| 				SELECT | ||||
| 				select | ||||
| 					v.id, | ||||
| 					coalesce(max(a.trip_id), 0) numOfTrip, | ||||
| 					SUM(pre_milleage) AS total_milleage, | ||||
| 					v.name, v.nopol1  | ||||
| 					-- 	coalesce(max(a.trip_id), 0) numOfTrip, | ||||
| 					-- 	SUM(pre_milleage) AS total_milleage, | ||||
| 					v.name, v.nopol1, | ||||
| 					vhc_id, trip_id, | ||||
| 					sum(pre_milleage) milleage, min(a.crt_d ) start, max(a.crt_d ) finish, | ||||
| 					(select fulladdress from t_gps_tracks_address where master_id = min(a.id) limit 1) startLoc, | ||||
| 					(select fulladdress from t_gps_tracks_address where master_id = max(a.id) limit 1) finishLoc | ||||
| 				FROM   | ||||
| 					t_vehicles v | ||||
| 					left join numbered a on a.vhc_id = v.id | ||||
| 				WHERE v.dlt is null | ||||
| 				GROUP BY v.id | ||||
| 				ORDER BY v.id; | ||||
| 			"; | ||||
| 			$d = [$from_date, $to_date]; | ||||
| 				WHERE  | ||||
| 					v.dlt is null and trip_id != 0  | ||||
| 					and if(? , v.id = ? , 1=1) | ||||
| 				GROUP BY v.id, a.trip_id  | ||||
| 				ORDER BY v.id | ||||
| 				"; | ||||
| 			$d = [$from_date, $to_date, $vid, $vid]; | ||||
|  | ||||
| 			$list = DB::select($q, $d); | ||||
|  | ||||
| @ -177,5 +182,72 @@ class ReportsController extends Controller | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	public function view_report_abnormalities(Request $req) | ||||
| 	{ | ||||
| 		$q = "select id, nopol1 from t_vehicles WHERE dlt is null order by nopol1"; | ||||
| 		$listNopol = DB::select($q); | ||||
|  | ||||
| 		$data = [ | ||||
| 			'listNopol' => $listNopol, | ||||
| 		]; | ||||
|  | ||||
| 		return view('menu_v1.reports.abnormalities', $data); | ||||
| 	} | ||||
| 	public function api_report_abnormalities_list(Request $req) | ||||
| 	{ | ||||
| 		// Validate input | ||||
| 		$rules = [ | ||||
| 			// 'from_date' => 'required|date', | ||||
| 			// 'to_date' => 'required|date|after_or_equal:from_date', | ||||
| 			'type' => 'nullable|in:report,list', // enum "report", "list". nullable default "list" | ||||
| 		]; | ||||
|  | ||||
| 		$isValidInput = Validator::make($req->all(), $rules); | ||||
| 		if (!$isValidInput->passes()) { | ||||
| 			$apiResp = Responses::bad_input($isValidInput->messages()->first()); | ||||
| 			return new Response($apiResp, $apiResp["meta"]["code"]); | ||||
| 		} | ||||
|  | ||||
| 		$from_date = $req->input('from_date'); | ||||
| 		$to_date = $req->input('to_date'); | ||||
| 		$vid = $req->input('vid'); | ||||
|  | ||||
| 		try { | ||||
| 			$q = " | ||||
| 				select | ||||
| 					tv.name, tv.nopol1,  | ||||
| 					t.crt_d, t.speed, tgta.fulladdress, | ||||
| 					tvd.speed_limit | ||||
| 				from | ||||
| 					t_gps_tracks t | ||||
| 					left join t_vehicles tv on tv.id = t.vhc_id  | ||||
| 					left join t_vehicles_detail tvd on tvd.vid = tv.id | ||||
| 					left join t_gps_tracks_address tgta on tgta.master_id = t.id | ||||
| 				WHERE  | ||||
| 					t.action = 'location' | ||||
| 					and t.speed != 0  | ||||
| 					AND t.crt_d BETWEEN ? AND ? | ||||
| 					and if(? , tv.id = ? , 1=1) | ||||
| 					-- and t.speed > tvd.speed_limit  | ||||
| 				having t.speed > tvd.speed_limit | ||||
| 				ORDER BY t.crt_d | ||||
| 			"; | ||||
| 			$d = [$from_date, $to_date, $vid, $vid]; | ||||
|  | ||||
| 			$list = DB::select($q, $d); | ||||
|  | ||||
| 			// // RETURN 1 - LIST | ||||
| 			// if($req->type != 'report'){ | ||||
| 				$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"]); | ||||
| 			// return Responses::json(Responses::SERVER_ERROR, 'An error occurred while generating the report.', (object)[]); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -272,6 +272,7 @@ | ||||
|                         <p class="text-bold mb-0">Engine Status</p> | ||||
|                         <p id="infoVehicles-engineStatus" class="text-muted mb-0">Idling</p> | ||||
|                     </li> | ||||
| 					 | ||||
|                     {{-- <li class="list-group-item p-1 px-2"> | ||||
|                         <p class="text-bold mb-0">Durasi Berhenti (<span id="infoVehicles-idlingUnit">min</span>)</p> | ||||
|                         <p id="infoVehicles-idlingDuration" class="text-muted mb-0">2946</p> | ||||
| @ -408,10 +409,11 @@ | ||||
|                             <label class="text-muted">From</label> | ||||
|                             <input class="form-control" id="historyStartDate"> | ||||
|                         </div> | ||||
|                         <div class="mb-3"> | ||||
|                         <div class="mb-2"> | ||||
|                             <label class="text-muted">To</label> | ||||
|                             <input class="form-control" id="historyEndDate"> | ||||
|                         </div> | ||||
| 						<button class="btn btn-primary btn-sm w-100 mb-3" id="historySearch">Search</button> | ||||
|                     </div> | ||||
|                     <div id="infoMove-plots" class="infoMove-plots"> | ||||
|                         {{-- <a href="#" class="plotMove-item"> | ||||
| @ -513,6 +515,13 @@ | ||||
|                     <span class="badge p-1 ps-3 pe-3 bg-danger me-2">Stopped</span> | ||||
|                     <span class="badge p-1 ps-3 pe-3 bg-warning me-2">Idling</span> | ||||
|                     <span class="badge p-1 ps-3 pe-3 bg-success">Moving</span> | ||||
| 				</div> | ||||
| 				<div class="col text-end" id="tripInfo"> | ||||
| 					<span class="badge p-1 ps-3 pe-3 bg-info me-2" style="background-color:#C0392B !important;">Trip 1</span> | ||||
| 					<span class="badge p-1 ps-3 pe-3 bg-info me-2" style="background-color:#2980B9 !important;">Trip 2</span> | ||||
| 					<span class="badge p-1 ps-3 pe-3 bg-info me-2" style="background-color:#27AE60 !important;">Trip 3</span> | ||||
| 					<span class="badge p-1 ps-3 pe-3 bg-info me-2" style="background-color:#D35400 !important;">Trip 4</span> | ||||
| 					<span class="badge p-1 ps-3 pe-3 bg-info me-2" style="background-color:#F39C12 !important;">Trip 5</span> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
| @ -941,7 +950,7 @@ | ||||
| 					waypoints: latLngs.reverse(), // reverse to make first point as start | ||||
| 					// router: L.Routing.osrmv1(), | ||||
| 					router: L.Routing.osrmv1({ | ||||
| 						serviceUrl: "https://brilianapps.britimorleste.tl/osrm-backend/route/v1", | ||||
| 						serviceUrl: "https://brilianapps.britimorleste.tl:5001/route/v1", | ||||
| 					}), | ||||
| 					show: false, | ||||
| 					itinerary: null, // 👈 completely removes the panel | ||||
| @ -950,7 +959,7 @@ | ||||
| 					routeWhileDragging: false, // optional: don’t reroute while dragging | ||||
| 					createMarker: () => null, | ||||
| 					lineOptions:{ | ||||
| 						styles: [{ color: locs[0]?.options?.color, weight: 5, opacity: 0.7 }], | ||||
| 						styles: [{ color: locs[0]?.options?.color, weight: 3, opacity: 0.7 }], | ||||
| 					}, | ||||
| 				}).addTo(Leaflet.map) | ||||
|  | ||||
| @ -1505,7 +1514,14 @@ | ||||
|                 }); | ||||
|             }, | ||||
|             eventFilterHistoryDate: function() { | ||||
|                 $('#historyStartDate, #historyEndDate').on('change', function(e) { | ||||
|                 $('#historySearch').on('click', function(e) { | ||||
| 					e.preventDefault(); | ||||
| 					// console.log("change history date", this.id, this.value); | ||||
| 					const value = this.value; | ||||
| 					const id = this.id; | ||||
| 					if(State[id] == moment(value, "DD-MM-YYYY HH:mm").unix()) | ||||
| 						return false; | ||||
|  | ||||
| 					const date0 = moment($('#historyStartDate').val(), "DD-MM-YYYY HH:mm") | ||||
| 					const date1 = moment($('#historyEndDate').val(), "DD-MM-YYYY HH:mm") | ||||
|                     const { | ||||
| @ -2637,95 +2653,95 @@ | ||||
|                 Trucks.routeStartEndGroupTrip(truckRoutes); | ||||
|             }, | ||||
|             // routeStartEnd: function(truckRoutes) { | ||||
|             //     let key_length = truckRoutes.length; | ||||
|             //     let polyTruckRoutes = truckRoutes.map((obj, key) => { | ||||
| 					 | ||||
|             //         obj.key_index = key + 1 // key_length - key; | ||||
|             //         // lists per detail info movement | ||||
|             //         Menu.showToListMovement(obj); | ||||
|             //         return { | ||||
|             //             lat: obj.latitude, | ||||
|             //             lng: obj.longitude, | ||||
|             //             options: { | ||||
|             //                 // polyline | ||||
|             //                 smoothFactor: 1.0, | ||||
|             //                 noClip: true, | ||||
|             //                 bubblingMouseEvents: false, | ||||
|             //                 // circle | ||||
|             //                 radius: 5, | ||||
| 			// 				// markerOpacity: 0 | ||||
|             //             }, | ||||
|             //             // circle | ||||
|             //             // label: `<b>${obj.key_index}</b><br>${obj.nopol1} ${obj.nopol2} ${obj.nopol3}<br>${moment.unix(obj?.lst_loc_crt).format('DD MMM YYYY HH:mm')}<br>Speed: ${(typeof obj.speed != 'undefined') ? obj.speed : '0'}<br>${Number(obj.latitude).toFixed(5)} - ${Number(obj.longitude).toFixed(6)}<br>${decodeURIComponent(obj?.fulladdress || 'address')}`, | ||||
|             //             // label: `<b>${obj.nopol1} ${obj.nopol2} ${obj.nopol3}</b><br>${moment.unix(obj?.lst_loc_crt).format('DD MMM YYYY HH:mm')}<br>${decodeURIComponent(obj?.fulladdress || 'address')}`, | ||||
|             //             label: `<b>${obj.nopol1} ${obj.nopol2} ${obj.nopol3}</b><br>${moment.unix(obj?.lst_loc_crt_d).format('DD MMM YYYY HH:mm:ss')}<br>${decodeURIComponent(obj?.fulladdress || 'address')}`, | ||||
|             //         } | ||||
|             //     }); | ||||
| 				//     let key_length = truckRoutes.length; | ||||
| 				//     let polyTruckRoutes = truckRoutes.map((obj, key) => { | ||||
| 						 | ||||
| 				//         obj.key_index = key + 1 // key_length - key; | ||||
| 				//         // lists per detail info movement | ||||
| 				//         Menu.showToListMovement(obj); | ||||
| 				//         return { | ||||
| 				//             lat: obj.latitude, | ||||
| 				//             lng: obj.longitude, | ||||
| 				//             options: { | ||||
| 				//                 // polyline | ||||
| 				//                 smoothFactor: 1.0, | ||||
| 				//                 noClip: true, | ||||
| 				//                 bubblingMouseEvents: false, | ||||
| 				//                 // circle | ||||
| 				//                 radius: 5, | ||||
| 				// 				// markerOpacity: 0 | ||||
| 				//             }, | ||||
| 				//             // circle | ||||
| 				//             // label: `<b>${obj.key_index}</b><br>${obj.nopol1} ${obj.nopol2} ${obj.nopol3}<br>${moment.unix(obj?.lst_loc_crt).format('DD MMM YYYY HH:mm')}<br>Speed: ${(typeof obj.speed != 'undefined') ? obj.speed : '0'}<br>${Number(obj.latitude).toFixed(5)} - ${Number(obj.longitude).toFixed(6)}<br>${decodeURIComponent(obj?.fulladdress || 'address')}`, | ||||
| 				//             // label: `<b>${obj.nopol1} ${obj.nopol2} ${obj.nopol3}</b><br>${moment.unix(obj?.lst_loc_crt).format('DD MMM YYYY HH:mm')}<br>${decodeURIComponent(obj?.fulladdress || 'address')}`, | ||||
| 				//             label: `<b>${obj.nopol1} ${obj.nopol2} ${obj.nopol3}</b><br>${moment.unix(obj?.lst_loc_crt_d).format('DD MMM YYYY HH:mm:ss')}<br>${decodeURIComponent(obj?.fulladdress || 'address')}`, | ||||
| 				//         } | ||||
| 				//     }); | ||||
|  | ||||
|             //     let polyline = Leaflet.addPolylines(polyTruckRoutes); | ||||
|             //     Leaflet.map.fitBounds(polyline.getBounds()); | ||||
|             //     // let ctrWaypoint = Leaflet.addWaypoints(polyTruckRoutes.slice(0, 100)) | ||||
| 				//     let polyline = Leaflet.addPolylines(polyTruckRoutes); | ||||
| 				//     Leaflet.map.fitBounds(polyline.getBounds()); | ||||
| 				//     // let ctrWaypoint = Leaflet.addWaypoints(polyTruckRoutes.slice(0, 100)) | ||||
|  | ||||
|             //     Leaflet.addCircles(polyTruckRoutes, function(circle, i) { | ||||
|             //         window.addEventListener('eventRemoveRouteStartEnd', function handler(e) { | ||||
|             //             circle.remove(); | ||||
|             //         }); | ||||
|             //         circle.on('click', function() { | ||||
|             //             PgBar.syncToPlotTravelHistory(i); | ||||
|             //         }) | ||||
|             //     }); | ||||
| 				//     Leaflet.addCircles(polyTruckRoutes, function(circle, i) { | ||||
| 				//         window.addEventListener('eventRemoveRouteStartEnd', function handler(e) { | ||||
| 				//             circle.remove(); | ||||
| 				//         }); | ||||
| 				//         circle.on('click', function() { | ||||
| 				//             PgBar.syncToPlotTravelHistory(i); | ||||
| 				//         }) | ||||
| 				//     }); | ||||
|  | ||||
|             //     let start = truckRoutes.at(-1); | ||||
|             //     let finish = truckRoutes.at(0); | ||||
|             //     // ${(start?.city_text || start?.state_tex || 'address't)} - ${(start?.postcode || 'postcode')} | ||||
|             //     // ${(finish?.city_text || finish?.state_text || 'address')} - ${(finish?.postcode || 'postcode')} | ||||
|             //     let startMarker = Leaflet.addMarkers({ | ||||
|             //         lat: start.latitude, | ||||
|             //         lng: start.longitude, | ||||
|             //         label: `<b>Start Poin</b><br>${start.nopol1} ${start.nopol2} ${start.nopol3}<br>${moment.unix(start?.lst_loc_crt).format('DD MMM YYYY HH:mm')}<br>Speed: ${(typeof start.lst_speed != 'undefined') ? start.lst_speed : '0'}<br>${Number(start.latitude).toFixed(5)},${Number(start.longitude).toFixed(6)}<br>${decodeURIComponent(start.fulladdress || 'address')}`, | ||||
|             //         // label: `<b>Start Poin</b><br>${start.nopol1} ${start.nopol2} ${start.nopol3}<br>${moment.unix(start?.lst_loc_crt_d).utcOffset(9 * 60).format('DD MMM YYYY HH:mm:ss')}<br>Speed: ${(typeof start.lst_speed != 'undefined') ? start.lst_speed : '0'}<br>${Number(start.latitude).toFixed(5)},${Number(start.longitude).toFixed(6)}<br>${decodeURIComponent(start.fulladdress || 'address')}`, | ||||
|             //         options: { | ||||
|             //             icon: Icon.titikAwal(), | ||||
|             //             // rotationAngle: 290 | ||||
|             //         } | ||||
|             //     }); | ||||
|             //     let finishMarker = Leaflet.addMarkers({ | ||||
|             //         lat: finish.latitude, | ||||
|             //         lng: finish.longitude, | ||||
|             //         label: `<b>End Poin</b><br>${finish.nopol1} ${finish.nopol2} ${finish.nopol3}<br>${moment.unix(finish?.lst_loc_crt).format('DD MMM YYYY HH:mm')}<br>Speed: ${(typeof finish.lst_speed != 'undefined') ? finish.lst_speed : '0'}<br>${Number(finish.latitude).toFixed(5)},${Number(finish.longitude).toFixed(6)}<br>${decodeURIComponent(finish.fulladdress || 'address')}`, | ||||
|             //         //label: `<b>End Poin</b><br>${finish.nopol1} ${finish.nopol2} ${finish.nopol3}<br>${moment.unix(finish?.lst_loc_crt_d).utcOffset(9 * 60).format('DD MMM YYYY HH:mm:ss')}<br>Speed: ${(typeof finish.lst_speed != 'undefined') ? finish.lst_speed : '0'}<br>${Number(finish.latitude).toFixed(5)},${Number(finish.longitude).toFixed(6)}<br>${decodeURIComponent(finish.fulladdress || 'address')}`, | ||||
|             //         options: { | ||||
|             //             icon: Icon.titikAkhir() | ||||
|             //         } | ||||
|             //     }); | ||||
| 				//     let start = truckRoutes.at(-1); | ||||
| 				//     let finish = truckRoutes.at(0); | ||||
| 				//     // ${(start?.city_text || start?.state_tex || 'address't)} - ${(start?.postcode || 'postcode')} | ||||
| 				//     // ${(finish?.city_text || finish?.state_text || 'address')} - ${(finish?.postcode || 'postcode')} | ||||
| 				//     let startMarker = Leaflet.addMarkers({ | ||||
| 				//         lat: start.latitude, | ||||
| 				//         lng: start.longitude, | ||||
| 				//         label: `<b>Start Poin</b><br>${start.nopol1} ${start.nopol2} ${start.nopol3}<br>${moment.unix(start?.lst_loc_crt).format('DD MMM YYYY HH:mm')}<br>Speed: ${(typeof start.lst_speed != 'undefined') ? start.lst_speed : '0'}<br>${Number(start.latitude).toFixed(5)},${Number(start.longitude).toFixed(6)}<br>${decodeURIComponent(start.fulladdress || 'address')}`, | ||||
| 				//         // label: `<b>Start Poin</b><br>${start.nopol1} ${start.nopol2} ${start.nopol3}<br>${moment.unix(start?.lst_loc_crt_d).utcOffset(9 * 60).format('DD MMM YYYY HH:mm:ss')}<br>Speed: ${(typeof start.lst_speed != 'undefined') ? start.lst_speed : '0'}<br>${Number(start.latitude).toFixed(5)},${Number(start.longitude).toFixed(6)}<br>${decodeURIComponent(start.fulladdress || 'address')}`, | ||||
| 				//         options: { | ||||
| 				//             icon: Icon.titikAwal(), | ||||
| 				//             // rotationAngle: 290 | ||||
| 				//         } | ||||
| 				//     }); | ||||
| 				//     let finishMarker = Leaflet.addMarkers({ | ||||
| 				//         lat: finish.latitude, | ||||
| 				//         lng: finish.longitude, | ||||
| 				//         label: `<b>End Poin</b><br>${finish.nopol1} ${finish.nopol2} ${finish.nopol3}<br>${moment.unix(finish?.lst_loc_crt).format('DD MMM YYYY HH:mm')}<br>Speed: ${(typeof finish.lst_speed != 'undefined') ? finish.lst_speed : '0'}<br>${Number(finish.latitude).toFixed(5)},${Number(finish.longitude).toFixed(6)}<br>${decodeURIComponent(finish.fulladdress || 'address')}`, | ||||
| 				//         //label: `<b>End Poin</b><br>${finish.nopol1} ${finish.nopol2} ${finish.nopol3}<br>${moment.unix(finish?.lst_loc_crt_d).utcOffset(9 * 60).format('DD MMM YYYY HH:mm:ss')}<br>Speed: ${(typeof finish.lst_speed != 'undefined') ? finish.lst_speed : '0'}<br>${Number(finish.latitude).toFixed(5)},${Number(finish.longitude).toFixed(6)}<br>${decodeURIComponent(finish.fulladdress || 'address')}`, | ||||
| 				//         options: { | ||||
| 				//             icon: Icon.titikAkhir() | ||||
| 				//         } | ||||
| 				//     }); | ||||
|  | ||||
|             //     // remove marker, circle, event listener and all about this marker | ||||
|             //     State.eventRemoveRouteStartEnd = new CustomEvent('eventRemoveRouteStartEnd', { | ||||
|             //         startMarker, | ||||
|             //         finishMarker, | ||||
|             //         polyline, | ||||
|             //         polyline, | ||||
|             //     }); | ||||
|             //     window.addEventListener('eventRemoveRouteStartEnd', function handler(e) { | ||||
|             //         startMarker.removeEventListener('click'); | ||||
|             //         startMarker.removeEventListener('moveend'); | ||||
|             //         startMarker.remove(); | ||||
|             //         finishMarker.removeEventListener('click'); | ||||
|             //         finishMarker.removeEventListener('moveend'); | ||||
|             //         finishMarker.remove(); | ||||
|             //         polyline.remove(); | ||||
|             //         e.currentTarget.removeEventListener(e.type, | ||||
|             //             handler); // window.removeEventListener('remove', this.handler, true); | ||||
|             //         State.eventRemoveRouteStartEnd = null; | ||||
|             //         State.inShowLastMove = null; | ||||
| 				//     // remove marker, circle, event listener and all about this marker | ||||
| 				//     State.eventRemoveRouteStartEnd = new CustomEvent('eventRemoveRouteStartEnd', { | ||||
| 				//         startMarker, | ||||
| 				//         finishMarker, | ||||
| 				//         polyline, | ||||
| 				//         polyline, | ||||
| 				//     }); | ||||
| 				//     window.addEventListener('eventRemoveRouteStartEnd', function handler(e) { | ||||
| 				//         startMarker.removeEventListener('click'); | ||||
| 				//         startMarker.removeEventListener('moveend'); | ||||
| 				//         startMarker.remove(); | ||||
| 				//         finishMarker.removeEventListener('click'); | ||||
| 				//         finishMarker.removeEventListener('moveend'); | ||||
| 				//         finishMarker.remove(); | ||||
| 				//         polyline.remove(); | ||||
| 				//         e.currentTarget.removeEventListener(e.type, | ||||
| 				//             handler); // window.removeEventListener('remove', this.handler, true); | ||||
| 				//         State.eventRemoveRouteStartEnd = null; | ||||
| 				//         State.inShowLastMove = null; | ||||
|  | ||||
|             //         PgBar.tglMenuPlayback(false); | ||||
|             //         PgBar.reset(); | ||||
|             //     }); | ||||
| 				//         PgBar.tglMenuPlayback(false); | ||||
| 				//         PgBar.reset(); | ||||
| 				//     }); | ||||
|  | ||||
|             //     PgBar.setMinMax(0, truckRoutes.length - 1); | ||||
| 				//     PgBar.setMinMax(0, truckRoutes.length - 1); | ||||
|             // }, | ||||
|             routeStartEndGroupTrip: function(truckRoutes) { | ||||
|             routeStartEndGroupTrip:  function(truckRoutes) { | ||||
| 				const colors = [ | ||||
| 					"#2980B9",  // Dark Blue | ||||
| 					"#C0392B", // Dark Red | ||||
| @ -2780,29 +2796,61 @@ | ||||
| 				}) | ||||
| 				// console.log("truckRoutes update", polyTruckRoutes); | ||||
|  | ||||
| 				polyTruckRoutes.forEach((poly, idx) => { | ||||
| 				polyTruckRoutes.forEach(async (poly, idx) => { | ||||
| 					// console.log("poly", poly);					 | ||||
| 					let polyline = Leaflet.addRoutes(poly) | ||||
| 					// let polyline = Leaflet.addPolylines(poly) | ||||
| 					// if(idx == 0) | ||||
| 						// Leaflet.map.fitBounds(polyline.getBounds()); | ||||
|  | ||||
| 					// let circlesStartStop = [] | ||||
| 					// Leaflet.addCircles(poly, function(circle, i) { | ||||
| 					// 	if(i == 0 || i == (polyTruckRoutes.length - 1)) | ||||
| 					// 		circlesStartStop.push(circle) | ||||
|  | ||||
| 					// let circlesStartStop = [...poly.filter((p, i) => i == 0 || i == (poly.length - 1))] | ||||
| 					let circlesStartStop = [] | ||||
| 					// console.log("circlesStartStop", circlesStartStop); | ||||
| 					 | ||||
| 					Leaflet.addCircles(poly, function(circle, i) { | ||||
| 						if(i == 0 || i == (polyTruckRoutes.length - 1)) | ||||
| 							circlesStartStop.push(circle) | ||||
| 					// 	window.addEventListener('eventRemoveRouteStartEnd', function handler(e) { | ||||
| 					// 		circle.remove(); | ||||
| 					// 	}); | ||||
| 					// 	circle.on('click', function() { | ||||
| 					// 		PgBar.syncToPlotTravelHistory(i); | ||||
| 					// 	}) | ||||
| 					// }); | ||||
| 					function addCirclesAsync(poly) { | ||||
| 						return new Promise(resolve => { | ||||
| 							let circles = []; | ||||
|  | ||||
| 						window.addEventListener('eventRemoveRouteStartEnd', function handler(e) { | ||||
| 							circle.remove(); | ||||
| 							Leaflet.addCircles(poly, function(circle, i) { | ||||
| 								if (i === 0 || i === (poly.length - 1)) { | ||||
| 									circles.push(circle); | ||||
| 								} | ||||
|  | ||||
| 								circle.on('click', function() { | ||||
| 									PgBar.syncToPlotTravelHistory(i); | ||||
| 								}); | ||||
|  | ||||
| 								// ✅ When last point processed, resolve | ||||
| 								if (i === poly.length - 1) { | ||||
| 									resolve(circles); | ||||
| 								} | ||||
| 							}); | ||||
| 						}); | ||||
| 						circle.on('click', function() { | ||||
| 							PgBar.syncToPlotTravelHistory(i); | ||||
| 						}) | ||||
| 					}); | ||||
| 					} | ||||
|  | ||||
| 					let circlesStartStop = []; | ||||
| 					for (let [idx, poly] of polyTruckRoutes.entries()) { | ||||
| 						let polyline = Leaflet.addRoutes(poly); | ||||
|  | ||||
| 						// wait for all circles | ||||
| 						circlesStartStop = await addCirclesAsync(poly); | ||||
|  | ||||
| 						// keep event + removal logic here | ||||
| 						State.eventRemoveRouteStartEnd = new CustomEvent('eventRemoveRouteStartEnd', { | ||||
| 							detail: { polyline, circlesStartStop } | ||||
| 						}); | ||||
|  | ||||
| 						window.addEventListener('eventRemoveRouteStartEnd', function(e) { | ||||
| 							polyline.remove(); | ||||
| 							circlesStartStop.forEach(c => c.remove()); | ||||
| 						}); | ||||
| 					} | ||||
| 					 | ||||
| 					// // remove marker, circle, event listener and all about this marker | ||||
| 					State.eventRemoveRouteStartEnd = new CustomEvent('eventRemoveRouteStartEnd', { | ||||
| @ -2813,28 +2861,12 @@ | ||||
| 					}); | ||||
|  | ||||
| 					window.addEventListener('eventRemoveRouteStartEnd', function (e) { | ||||
| 					// console.log("circlesStartStop", circlesStartStop); | ||||
| 						polyline.remove(); | ||||
| 						circlesStartStop.forEach(c => c.remove()) | ||||
| 					}); | ||||
| 					// State.eventRemoveRouteStartEnd = new CustomEvent('eventRemoveRouteStartEnd', { | ||||
| 					// 	polyline, | ||||
| 					// 	circlesStartStop, | ||||
| 					// }); | ||||
| 					// console.log("polyline", polyline); | ||||
| 					// console.log("circlesStartStop", circlesStartStop); | ||||
| 					// console.log("State.eventRemoveRouteStartEnd", State.eventRemoveRouteStartEnd); | ||||
| 					 | ||||
| 				}) | ||||
|  | ||||
|                 window.addEventListener('eventRemoveRouteStartEnd', function handler(e) { | ||||
|                     // startMarker.removeEventListener('click'); | ||||
|                     // startMarker.removeEventListener('moveend'); | ||||
|                     // startMarker.remove(); | ||||
|                     // finishMarker.removeEventListener('click'); | ||||
|                     // finishMarker.removeEventListener('moveend'); | ||||
|                     // finishMarker.remove(); | ||||
|                     // polyline.remove(); | ||||
|                     e.currentTarget.removeEventListener(e.type, | ||||
|                         handler); // window.removeEventListener('remove', this.handler, true); | ||||
|                     State.eventRemoveRouteStartEnd = null; | ||||
|  | ||||
							
								
								
									
										241
									
								
								resources/views/menu_v1/reports/abnormalities.blade.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								resources/views/menu_v1/reports/abnormalities.blade.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,241 @@ | ||||
| @php | ||||
|     $user_role = Auth::user()->role; | ||||
| @endphp | ||||
|  | ||||
| @extends('app.app') | ||||
|  | ||||
| @section('title') | ||||
|     Vehicles | ||||
| @endsection | ||||
|  | ||||
| @section('customcss') | ||||
| @endsection | ||||
|  | ||||
| @section('content') | ||||
|     <div class="container-fluid"> | ||||
|         <div class="content"> | ||||
|             <div class="card"> | ||||
|                 <div class="card-header"> | ||||
|                     <div class="row d-flex align-items-center"> | ||||
|                         <div class="col-3"> | ||||
|                             <p class="card-title text-bold mb-0">Abnormality Report</p> | ||||
|                         </div> | ||||
|                         @if ($user_role == \App\Models\Users::ROLE_VENDOR || $user_role == \App\Models\Users::ROLE_ADMIN) | ||||
|                             @can('vehicle.create') | ||||
|                                 <!-- <div class="col text-end"> | ||||
|                                     <button id="btnMdlNewVhc" class="btn btn-sm btn-danger">Add New Vehicle</button> | ||||
|                                 </div> --> | ||||
|                             @endcan | ||||
|  | ||||
|                             {{-- <div class="col-auto text-end ps-0"> | ||||
|                                 <button class="btn btn-sm btn-danger">Upload</button> | ||||
|                             </div> --}} | ||||
|                         @endif | ||||
|                         <div class="col-auto text-end ps-0"> | ||||
|                             <!-- <button class="btn btn-sm btn-danger">Download</button> --> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|                 <div class="card-body"> | ||||
| 					<!-- filter --> | ||||
| 					<div class="row"> | ||||
| 						<div class="col-2"> | ||||
| 							<div class="form-group"> | ||||
| 								<label class="text-muted">From</label> | ||||
| 								<!-- default today --> | ||||
| 								<!-- <input type="date" class="form-control" id="tgl0" value="{{ date('Y-m-d') }}"> --> | ||||
| 								<input class="form-control" id="tgl0" value="{{ date('d-m-Y 00:00') }}"> | ||||
| 							</div> | ||||
| 						</div> | ||||
| 						<div class="col-2"> | ||||
| 							<div class="form-group"> | ||||
| 								<label class="text-muted">To</label> | ||||
| 								<input class="form-control" id="tgl1" value="{{ date('d-m-Y 23:59') }}"> | ||||
| 							</div> | ||||
| 						</div> | ||||
| 						<div class="col-2"> | ||||
| 							<div class="form-group"> | ||||
| 								<label class="text-muted">License Plate</label> | ||||
| 								<select name="license_plate" class="form-control" id="filterNopol"> | ||||
| 									<option value="">-- All --</option> | ||||
| 									@foreach ($listNopol as $nopol) | ||||
| 										<option value="{{ $nopol->id }}">{{ $nopol->nopol1 }}</option> | ||||
| 									@endforeach | ||||
| 								</select> | ||||
| 							</div> | ||||
| 						</div> | ||||
| 						<div class="col-1 d-flex align-items-end"> | ||||
| 							<button class="btn btn-sm btn-primary" id="submitFilter">Submit</button> | ||||
| 						</div> | ||||
| 						<div class="col-5 d-flex align-items-end"> | ||||
| 							<button class="btn btn-sm btn-danger ms-auto" id="btnDownloadReport">Download Report</button> | ||||
| 						</div> | ||||
| 					</div> | ||||
| 					</div> | ||||
|                     <div class="table-responsive p-3"> | ||||
|                         <table id="tVehicleTrips" class="table table-hover dataTable w-100"> | ||||
|                             <thead> | ||||
|                                 <tr class=""> | ||||
|                                     <!-- <th class="">Vehicle ID</th> --> | ||||
|                                     <!-- <th class="text-center">Action</th> --> | ||||
|                                     <th class="">Vehicle Name</th> | ||||
|                                     <th class="">License Plate Number</th> | ||||
|                                     <th class="">Time</th> | ||||
|                                     <th class="">Speed (kph)</th> | ||||
|                                     <th class="">Location</th> | ||||
|                                 </tr> | ||||
|                             </thead> | ||||
|                         </table> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
| 	<!-- modal --> | ||||
| @endsection | ||||
|  | ||||
| @section('customjs') | ||||
|     <script src="{{ asset('assets/js/load-image.all.min.js') }}"></script> | ||||
| 	<!-- DataTables Buttons + JSZip (for Excel export) --> | ||||
| 	<script src="https://cdn.datatables.net/buttons/2.4.2/js/dataTables.buttons.min.js"></script> | ||||
| 	<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script> | ||||
| 	<script src="https://cdn.datatables.net/buttons/2.4.2/js/buttons.html5.min.js"></script> | ||||
| 	<script> | ||||
|         'use strict'; | ||||
|         const State = { | ||||
|             file_jimp_worker: "{{ asset('assets/js/worker/jimp.js') }}", | ||||
|             storage_lara: "{{ asset('storage') }}/", | ||||
|             dvc_type: { | ||||
|                 built_in: "{{ \App\Models\Devices::TYPE_BUILT_IN }}", | ||||
|                 portable: "{{ \App\Models\Devices::TYPE_PORTABLE }}", | ||||
|             }, | ||||
|         }; | ||||
|  | ||||
|         const Wrapper = { | ||||
|             activate: function() { | ||||
|                 Wrapper.init(); | ||||
|                 Wrapper.event(); | ||||
|                 DTable.activate(); | ||||
|  | ||||
| 			}, | ||||
| 			init: ()=>{ | ||||
| 				$('#tgl0, #tgl1').datetimepicker({ | ||||
| 					format:'d-m-Y H:i', | ||||
| 					defaultTime:'00:00', | ||||
| 					closeOnDateSelect: true, | ||||
| 					// mask:true | ||||
| 				}); | ||||
|  | ||||
| 			}, | ||||
|             event: function() { | ||||
|                 $('#submitFilter').on('click', function() { | ||||
| 					DTable.reload(); | ||||
| 				}); | ||||
| 				// Trigger export on external button click | ||||
| 				$('#btnDownloadReport').on('click', function() { | ||||
| 					DTable.table.button('.buttons-excel').trigger(); | ||||
| 				}); | ||||
|             }, | ||||
|         }; | ||||
|  | ||||
|         const DTable = { | ||||
| 			table: null, | ||||
|     		lastAjax: null, // keep track of the last ajax request | ||||
|             activate: function() { | ||||
|                 DTable.reload(); | ||||
|             }, | ||||
|             reload: function() { | ||||
| 				// Abort the last request if it's still running | ||||
| 				if (DTable.lastAjax) { | ||||
| 					DTable.lastAjax.abort(); | ||||
| 				} | ||||
| 				if (DTable.table) { | ||||
| 					// If table already exists → reload | ||||
| 					DTable.table.ajax.reload(); | ||||
| 					return; | ||||
| 				} | ||||
|  | ||||
| 				DTable.table = $('#tVehicleTrips').DataTable({ | ||||
| 					searching: false,   // 🔹 remove search box | ||||
| 					ordering: false,    // 🔹 disable sorting for all columns | ||||
| 					processing: true, | ||||
| 					serverSide: false, | ||||
| 					bLengthChange: true, | ||||
| 					deferRender: true, | ||||
| 					ajax: function(data, callback, settings) { | ||||
| 						// Abort previous | ||||
| 						if (DTable.lastAjax) { | ||||
| 							DTable.lastAjax.abort(); | ||||
| 						} | ||||
|  | ||||
| 						// Fire new request | ||||
| 						DTable.lastAjax = $.ajax({ | ||||
| 							url: `{{ route('api_report_abnormalities_list') }}? | ||||
| 								cptid=${AppState.current_company} | ||||
| 								&from_date=${moment($('#tgl0').val(), "DD-MM-YYYY HH:mm").unix()} | ||||
| 								&to_date=${moment($('#tgl1').val(), "DD-MM-YYYY HH:mm").unix()} | ||||
| 								&vid=${$('#filterNopol').val() || ''}`, | ||||
| 							type: 'GET', | ||||
| 							success: function(json) { | ||||
| 								callback(json); | ||||
| 							}, | ||||
| 							error: function(xhr, status, error) { | ||||
| 								if (status !== 'abort') { | ||||
| 									console.error("AJAX error:", error); | ||||
| 								} | ||||
| 							}, | ||||
| 							complete: function() { | ||||
| 								DTable.lastAjax = null; | ||||
| 							} | ||||
| 						}); | ||||
| 					}, | ||||
|                     deferRender: true, | ||||
| 					columns: [ | ||||
| 						// { data: 'id', visible: false }, // vhc_id | ||||
| 						{ | ||||
| 							data: 'name', | ||||
| 							className: 'text-start text-nowrap', | ||||
| 						}, | ||||
| 						{ | ||||
| 							data: 'nopol1', | ||||
| 							className: 'text-start', | ||||
| 						}, | ||||
| 						{  | ||||
| 							data: "crt_d", | ||||
| 							className: 'text-nowrap', | ||||
| 							render: (data, type, row, meta) => { | ||||
| 								return moment.unix(data).format('DD MMM YYYY HH:mm:ss'); | ||||
| 							} | ||||
| 						}, | ||||
| 						{ data: "speed", className: 'text-end'}, | ||||
| 						{  | ||||
| 							data: "fulladdress", | ||||
| 							render: (data, type, row, meta) => { | ||||
| 								return Helper.shortenText(decodeURIComponent(data || ''), 255) | ||||
| 							}, | ||||
| 						}, | ||||
| 					], | ||||
| 					paging: false, | ||||
| 					buttons: [ | ||||
| 						{ | ||||
| 							extend: 'excelHtml5', | ||||
| 							title: `Abnormality Report - ${moment(safeVal('#tgl0')).format('DD MMM YYYY')} to ${moment(safeVal('#tgl1')).format('DD MMM YYYY')}`, | ||||
| 							className: 'd-none', // hide default button | ||||
| 							exportOptions: { | ||||
| 								columns: ':visible:not(:first-child)' // 🔹 exclude first column | ||||
| 							} | ||||
| 						} | ||||
| 					] | ||||
| 				}); | ||||
|             }, | ||||
|         }; | ||||
|  | ||||
|         function safeVal(selector) { | ||||
|             const val = $(selector).val(); | ||||
|             return (val === undefined || val === null || val === '') ? null : val; | ||||
|         } | ||||
|  | ||||
|         Wrapper.activate(); | ||||
|     </script> | ||||
| @endsection | ||||
| @ -43,30 +43,49 @@ | ||||
| 							<div class="form-group"> | ||||
| 								<label class="text-muted">From</label> | ||||
| 								<!-- default today --> | ||||
| 								<input type="date" class="form-control" id="tgl0" value="{{ date('Y-m-d') }}"> | ||||
| 								<!-- <input type="date" class="form-control" id="tgl0" value="{{ date('Y-m-d') }}"> --> | ||||
| 								<input class="form-control" id="tgl0" value="{{ date('d-m-Y 00:00') }}"> | ||||
| 							</div> | ||||
| 						</div> | ||||
| 						<div class="col-2"> | ||||
| 							<div class="form-group"> | ||||
| 								<label class="text-muted">To</label> | ||||
| 								<input type="date" class="form-control" id="tgl1" value="{{ date('Y-m-d') }}"> | ||||
| 								<input class="form-control" id="tgl1" value="{{ date('d-m-Y 23:59') }}"> | ||||
| 							</div> | ||||
| 						</div> | ||||
| 						<div class="col-8 d-flex align-items-end"> | ||||
| 						<div class="col-2"> | ||||
| 							<div class="form-group"> | ||||
| 								<label class="text-muted">License Plate</label> | ||||
| 								<select name="license_plate" class="form-control" id="filterNopol"> | ||||
| 									<option value="">-- All --</option> | ||||
| 									@foreach ($listNopol as $nopol) | ||||
| 										<option value="{{ $nopol->id }}">{{ $nopol->nopol1 }}</option> | ||||
| 									@endforeach | ||||
| 								</select> | ||||
| 							</div> | ||||
| 						</div> | ||||
| 						<div class="col-1 d-flex align-items-end"> | ||||
| 							<button class="btn btn-sm btn-primary" id="submitFilter">Submit</button> | ||||
| 						</div> | ||||
| 						<div class="col-5 d-flex align-items-end"> | ||||
| 							<button class="btn btn-sm btn-danger ms-auto" id="btnDownloadReport">Download Report</button> | ||||
| 						</div> | ||||
| 					</div> | ||||
| 					</div> | ||||
|                     <div class="table-responsive p-3"> | ||||
|                         <table id="tVehicleTrips" class="table table-hover dataTable "> | ||||
|                         <table id="tVehicleTrips" class="table table-hover dataTable w-100"> | ||||
|                             <thead> | ||||
|                                 <tr class=""> | ||||
|                                     <!-- <th class="">#</th> --> | ||||
|                                     <th class="">Vehicle ID</th> | ||||
|                                     <!-- <th class="text-center">Action</th> --> | ||||
|                                     <th class="">Vehicle Name</th> | ||||
|                                     <th class="">License Plate Number</th> | ||||
|                                     <th class="">Number of Trip</th> | ||||
|                                     <th class="">Total Milage (km)</th> | ||||
|                                     <th class="">Total Mileage (km)</th> | ||||
|                                     <th class="">Trip #</th> | ||||
|                                     <th class="">Start</th> | ||||
|                                     <th class="">Finish</th> | ||||
|                                     <th class="">Mileage (km)</th> | ||||
|                                 </tr> | ||||
|                             </thead> | ||||
|                             <!-- <tbody> | ||||
| @ -166,12 +185,22 @@ | ||||
|  | ||||
|         const Wrapper = { | ||||
|             activate: function() { | ||||
|                 Wrapper.init(); | ||||
|                 Wrapper.event(); | ||||
|                 DTable.activate(); | ||||
|  | ||||
| 			}, | ||||
| 			init: ()=>{ | ||||
| 				$('#tgl0, #tgl1').datetimepicker({ | ||||
| 					format:'d-m-Y H:i', | ||||
| 					defaultTime:'00:00', | ||||
| 					closeOnDateSelect: true, | ||||
| 					// mask:true | ||||
| 				}); | ||||
|  | ||||
| 			}, | ||||
|             event: function() { | ||||
|                 $('#tgl0, #tgl1').on('change', function() { | ||||
|                 $('#submitFilter').on('click', function() { | ||||
| 					DTable.reload(); | ||||
| 				}); | ||||
| 				// Trigger export on external button click | ||||
| @ -192,8 +221,19 @@ | ||||
| 				if (DTable.lastAjax) { | ||||
| 					DTable.lastAjax.abort(); | ||||
| 				} | ||||
| 				if (DTable.table) { | ||||
| 					// If table already exists → reload | ||||
| 					DTable.table.ajax.reload(); | ||||
| 					return; | ||||
| 				} | ||||
|  | ||||
| 				let mileageSum = {}; | ||||
| 				let tripCount = {}; | ||||
|  | ||||
|  | ||||
| 				DTable.table = $('#tVehicleTrips').DataTable({ | ||||
| 					searching: false,   // 🔹 remove search box | ||||
| 					ordering: false,    // 🔹 disable sorting for all columns | ||||
| 					processing: true, | ||||
| 					serverSide: false, | ||||
| 					bLengthChange: true, | ||||
| @ -209,10 +249,22 @@ | ||||
| 						DTable.lastAjax = $.ajax({ | ||||
| 							url: `{{ route('api_report_vehicle_trips_list') }}? | ||||
| 								cptid=${AppState.current_company} | ||||
| 								&from_date=${moment(safeVal('#tgl0')).startOf('day').unix()} | ||||
| 								&to_date=${moment(safeVal('#tgl1')).endOf('day').unix()}`, | ||||
| 								&from_date=${moment($('#tgl0').val(), "DD-MM-YYYY HH:mm").unix()} | ||||
| 								&to_date=${moment($('#tgl1').val(), "DD-MM-YYYY HH:mm").unix()} | ||||
| 								&vid=${$('#filterNopol').val() || ''}`, | ||||
| 							type: 'GET', | ||||
| 							success: function(json) { | ||||
| 								// Precompute totals by vhc_id | ||||
|  | ||||
| 								json.data.forEach(row => { | ||||
| 									const id = row.id; // vhc_id | ||||
| 									if (!mileageSum[id]) { | ||||
| 										mileageSum[id] = 0; | ||||
| 										tripCount[id] = 0; | ||||
| 									} | ||||
| 									mileageSum[id] += parseFloat(row.milleage || 0); | ||||
| 									tripCount[id] += 1; | ||||
| 								}); | ||||
| 								callback(json); | ||||
| 							}, | ||||
| 							error: function(xhr, status, error) { | ||||
| @ -227,6 +279,7 @@ | ||||
| 					}, | ||||
|                     deferRender: true, | ||||
| 					columns: [ | ||||
| 						{ data: 'id', visible: false }, // vhc_id | ||||
| 						{ | ||||
| 							data: 'name', | ||||
| 							className: 'text-start text-nowrap', | ||||
| @ -241,29 +294,88 @@ | ||||
| 							orderable: true, | ||||
| 							searchable: true, | ||||
| 						}, | ||||
| 						{ | ||||
| 							data: 'numOfTrip', | ||||
| 							className: 'text-end', | ||||
| 							visible: true, | ||||
| 							orderable: true, | ||||
| 							searchable: true, | ||||
|  						{ data: "id", className: 'text-end'}, | ||||
| 						{ data: "id", className: 'text-end'}, | ||||
| 						{ data: "trip_id", className: 'text-end'}, | ||||
| 						{  | ||||
| 							data: 'start',  | ||||
| 							render: (data, type, row, meta) => { | ||||
| 								// let addr = row | ||||
| 								return ` | ||||
| 									${moment.unix(data).format('DD MMM YYYY HH:mm:ss')}<br> | ||||
| 									${Helper.shortenText(decodeURIComponent(row.startLoc || 'address'), 255) || "-"} | ||||
| 								`; | ||||
| 							}  | ||||
| 						}, | ||||
| 						{ | ||||
| 							data: 'total_milleage', | ||||
| 							className: 'text-end', | ||||
| 							visible: true, | ||||
| 							orderable: true, | ||||
| 							searchable: false, | ||||
| 							render: function(data, type, row, meta) { | ||||
| 						{  | ||||
| 							data: 'finish',  | ||||
| 							render: (data, type, row, meta) => { | ||||
| 								// let addr = row | ||||
| 								return ` | ||||
| 									${moment.unix(data).format('DD MMM YYYY HH:mm:ss')}<br> | ||||
| 									${Helper.shortenText(decodeURIComponent(row.finishLoc || 'address'), 255) || "-"} | ||||
| 								`; | ||||
| 							}  | ||||
| 						}, | ||||
| 						{  | ||||
| 							data: 'milleage',  | ||||
| 							className: 'text-end', render: function(data, type, row, meta) { | ||||
| 								return (data === null) ? '0' : parseFloat(data).toFixed(2); | ||||
| 							} | ||||
| 						}, | ||||
| 					], | ||||
| 					paging: false, | ||||
| 					drawCallback: function (settings) { | ||||
| 						const api = this.api(); | ||||
| 						const rows = api.rows({ page: 'current' }).nodes(); | ||||
| 						let last = null; | ||||
|  | ||||
| 						api.column(0, { page: 'current' }) // vhc_id col | ||||
| 							.data() | ||||
| 							.each(function (group, i) { | ||||
| 								if (last !== group) { | ||||
| 									// count how many rows this vhc_id spans | ||||
| 									const rowspanCount = api | ||||
| 										.column(0, { page: 'current' }) | ||||
| 										.data() | ||||
| 										.filter(function (d) { | ||||
| 											return d === group; | ||||
| 										}).length; | ||||
|  | ||||
| 									// merge vhc_id | ||||
| 									$(rows).eq(i).find('td:eq(0)').attr('rowspan', rowspanCount); | ||||
| 									$(rows).eq(i).find('td:eq(1)').attr('rowspan', rowspanCount); | ||||
| 									$(rows).eq(i).find('td:eq(2)').attr('rowspan', rowspanCount); | ||||
| 									$(rows).eq(i).find('td:eq(3)').attr('rowspan', rowspanCount); | ||||
|  | ||||
| 									// fill trip count | ||||
| 									$(rows).eq(i).find('td:eq(2)') | ||||
| 										.attr('rowspan', rowspanCount) | ||||
| 										.text(tripCount[group]); | ||||
|  | ||||
| 									// fill mileage sum | ||||
| 									$(rows).eq(i).find('td:eq(3)') | ||||
| 										.attr('rowspan', rowspanCount) | ||||
| 										.text(mileageSum[group].toFixed(2)); | ||||
|  | ||||
| 									last = group; | ||||
| 								} else { | ||||
| 									// remove duplicate cells | ||||
| 									$(rows).eq(i).find('td:eq(0)').remove(); | ||||
| 									$(rows).eq(i).find('td:eq(0)').remove(); | ||||
| 									$(rows).eq(i).find('td:eq(0)').remove(); | ||||
| 									$(rows).eq(i).find('td:eq(0)').remove(); | ||||
| 								} | ||||
| 							}); | ||||
| 						}, | ||||
| 					buttons: [ | ||||
| 						{ | ||||
| 							extend: 'excelHtml5', | ||||
| 							title: `Vehicle Trip Report - ${moment(safeVal('#tgl0')).format('DD MMM YYYY')} to ${moment(safeVal('#tgl1')).format('DD MMM YYYY')}`, | ||||
| 							className: 'd-none' // hide default button | ||||
| 							className: 'd-none', // hide default button | ||||
| 							exportOptions: { | ||||
| 								columns: ':visible:not(:first-child)' // 🔹 exclude first column | ||||
| 							} | ||||
| 						} | ||||
| 					] | ||||
| 				}); | ||||
|  | ||||
| @ -51,6 +51,9 @@ | ||||
| 							<li> | ||||
| 								<a class="dropdown-item {{ Request::segment(2) == 'vehicle-trips' ? 'active' : '' }}" href="{{ route('view_report_vehicle_trips') }}" title="">Vehicle Trips</a> | ||||
| 							</li> | ||||
| 							<li> | ||||
| 								<a class="dropdown-item {{ Request::segment(2) == 'abnormalities' ? 'active' : '' }}" href="{{ route('view_report_abnormalities') }}" title="">Abnormalities</a> | ||||
| 							</li> | ||||
|                         </ul> | ||||
|                     </li> | ||||
|                 @endif | ||||
|  | ||||
| @ -156,6 +156,8 @@ Route::middleware(["auth", "auth.user"])->group(function () { | ||||
| 	// reports | ||||
|     Route::get("/reports/vehicle-trips", "ReportsController@view_report_vehicle_trips")->name("view_report_vehicle_trips"); | ||||
|     Route::get("/reports/vehicle-trips-list", "ReportsController@api_report_vehicle_trips_list")->name("api_report_vehicle_trips_list"); | ||||
|     Route::get("/reports/abnormalities", "ReportsController@view_report_abnormalities")->name("view_report_abnormalities"); | ||||
|     Route::get("/reports/abnormalities-list", "ReportsController@api_report_abnormalities_list")->name("api_report_abnormalities_list"); | ||||
|  | ||||
|  | ||||
|     Route::get("/user/vendor/transactions", "MenuController@view_user_vendor_transaction")->name( | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	