Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 81 additions & 9 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@ import LogTable from "./LogTable";
import ToggleBar from "./ToggleBar";
import TripLogs from "./TripLogs";
import CloudLogging from "./CloudLogging";
import { uploadFile, getUploadedData, deleteUploadedData, uploadCloudLogs, saveDatasetAsJson } from "./localStorage";
import {
uploadFile,
getUploadedData,
deleteUploadedData,
uploadCloudLogs,
saveDatasetAsJson,
saveToIndexedDB,
} from "./localStorage";
import _ from "lodash";
import { getQueryStringValue, setQueryStringValue } from "./queryString";
import "./global.css";
Expand Down Expand Up @@ -519,18 +526,80 @@ class App extends React.Component {

const handleSaveClick = async (e) => {
e.stopPropagation();
log(`Save initiated for dataset ${index}`);
log(`Export initiated for dataset ${index}`);

// Close menu
this.setState({ activeMenuIndex: null });

try {
await saveDatasetAsJson(index);
toast.success(`Dataset ${index + 1} saved successfully`);
log(`Dataset ${index} saved successfully`);
toast.success(`Dataset ${index + 1} exported successfully`);
log(`Dataset ${index} exported successfully`);
} catch (error) {
console.error("Error saving dataset:", error);
toast.error(`Error saving dataset: ${error.message}`);
console.error("Error exporting dataset:", error);
toast.error(`Error exporting dataset: ${error.message}`);
}
};

const handlePruneClick = async (e) => {
e.stopPropagation();
log(`Prune initiated for dataset ${index}`);

// Close menu
this.setState({ activeMenuIndex: null });

try {
const { minTime, maxTime } = this.state.timeRange;
const data = await getUploadedData(index);

if (!data || !data.rawLogs || !Array.isArray(data.rawLogs)) {
throw new Error("No valid data to prune");
}

// Calculate how many logs will be removed
const originalLength = data.rawLogs.length;
const minDate = new Date(minTime);
const maxDate = new Date(maxTime);
const remainingLogs = data.rawLogs.filter((log) => {
const logTime = new Date(log.timestamp || log.insertTime);
return logTime >= minDate && logTime <= maxDate;
});

const removeCount = originalLength - remainingLogs.length;

if (
!confirm(
`This will remove ${removeCount} logs outside the selected time range.\nAre you sure you want to continue?`
)
) {
log("Prune operation cancelled by user");
return;
}

log(`Pruning dataset ${index}: removing ${removeCount} logs outside time range`);

data.rawLogs = remainingLogs;

// Save the pruned dataset back to storage
await saveToIndexedDB(data, index);

// Update the current dataset if this is the active one
if (this.state.activeDatasetIndex === index) {
const tripLogs = new TripLogs(data.rawLogs, data.solutionType);
this.props.logData.tripLogs = tripLogs;
this.props.logData.solutionType = data.solutionType;

// Force update of components
this.forceUpdate();

// Select first row after pruning
this.selectFirstRow();
}

toast.success(`Dataset pruned: removed ${removeCount} logs outside the selected time range.`);
} catch (error) {
console.error("Error pruning dataset:", error);
toast.error(`Error pruning dataset: ${error.message}`);
}
};

Expand Down Expand Up @@ -598,13 +667,16 @@ class App extends React.Component {
>
{isUploaded ? `Dataset ${index + 1}` : `Select Dataset ${index + 1}`}

{isUploaded && (
{isUploaded && isActive && (
<span className="dataset-button-actions" onClick={toggleMenu}>
{isMenuOpen && (
<div className="dataset-button-menu">
<div className="dataset-button-menu-item save" onClick={handleSaveClick}>
Save
<div className="dataset-button-menu-item export" onClick={handleSaveClick}>
Export
</div>
<div className="dataset-button-menu-item prune" onClick={handlePruneClick}>
Prune
</div>
<div className="dataset-button-menu-item delete" onClick={handleDeleteClick}>
Delete
Expand Down
12 changes: 11 additions & 1 deletion src/Trip.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,17 @@ class Trip {
* Colors were chosen for visibility
*/
export function getColor(tripIdx) {
const colors = ["#2d7dd2", "#97cc04", "#eeb902", "#f45d01", "#474647", "00aa00"];
const colors = [
"#007bff", // Blue
"#97cc04", // Lime Green
"#d63384", // Magenta
"#198754", // Green
"#f45d01", // Orange
"#6A0DAD", // Purple
"#fdc500", // Gold
"#0dcaf0", // Cyan
"#8B4513", // Brown
];
return colors[tripIdx % colors.length];
}

Expand Down
13 changes: 13 additions & 0 deletions src/TripLogs.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ function processRawLogs(rawLogs, solutionType) {
heading: 0,
routeSegment: null,
routeSegmentTraffic: null,
currentTrips: [],
};

for (let idx = 0; idx < sortedLogs.length; idx++) {
Expand All @@ -151,6 +152,7 @@ function processRawLogs(rawLogs, solutionType) {
newLog.request = apiCall.request;
newLog.response = apiCall.response;
newLog.error = apiCall.error;
const hasApiError = !!newLog.error || (origLog.jsonpayload && origLog.jsonpayload.errorresponse);

adjustFieldFormats(solutionType, newLog);

Expand All @@ -171,6 +173,17 @@ function processRawLogs(rawLogs, solutionType) {
newLog.lastlocation.heading = lastKnownState.heading;
}

// Keep the same current trips if we had an API error
if (hasApiError && lastKnownState.currentTrips.length > 0) {
log(`Preserving current trips due to API error for log at ${newLog.timestamp}`);
if (!newLog.response) {
newLog.response = {};
}
newLog.response.currenttrips = [...lastKnownState.currentTrips];
} else if (_.get(newLog, "response.currenttrips")) {
lastKnownState.currentTrips = [...newLog.response.currenttrips];
}

// If Navigation SDK is NO_GUIDANCE, reset the lastKnownState planned route and traffic
if (typeof newLog.navStatus === "string" && newLog.navStatus.endsWith("NO_GUIDANCE")) {
lastKnownState.routeSegment = null;
Expand Down
17 changes: 15 additions & 2 deletions src/TripObjects.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,13 @@ export class TripObjects {

addTripVisuals(trip, minDate, maxDate) {
const tripId = trip.tripName;
const isNonTripSegment = tripId.startsWith("non-trip-segment-");

log(`Processing trip visuals for ${tripId}`, {
isNonTripSegment,
coordsCount: trip.pathCoords.length,
firstUpdate: trip.firstUpdate,
lastUpdate: trip.lastUpdate,
pickupPoint: trip.getPickupPoint(),
actualPickupPoint: trip.getActualPickupPoint(),
dropoffPoint: trip.getDropoffPoint(),
Expand All @@ -126,11 +132,13 @@ export class TripObjects {
// Add path polyline
const tripCoords = trip.getPathCoords(minDate, maxDate);
if (tripCoords.length > 0) {
const strokeColor = isNonTripSegment ? "#474647" : getColor(trip.tripIdx);

const path = new google.maps.Polyline({
path: tripCoords,
geodesic: true,
strokeColor: getColor(trip.tripIdx),
strokeOpacity: 0.5,
strokeColor: strokeColor,
strokeOpacity: 0.3,
strokeWeight: 6,
clickable: false,
map: this.map,
Expand All @@ -148,6 +156,11 @@ export class TripObjects {
this.paths.set(tripId, path);
}

// Skip creating markers for non-trip segments
if (isNonTripSegment) {
return;
}

const markers = [];

// Get points
Expand Down
2 changes: 1 addition & 1 deletion src/localStorage.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ export function ensureCorrectFormat(data) {
};
}

async function saveToIndexedDB(data, index) {
export async function saveToIndexedDB(data, index) {
const db = await openDB();
const transaction = db.transaction(STORE_NAME, "readwrite");
const store = transaction.objectStore(STORE_NAME);
Expand Down
Loading