diff --git a/src/App.js b/src/App.js index 5939aef..44dc14c 100644 --- a/src/App.js +++ b/src/App.js @@ -89,7 +89,7 @@ class App extends React.Component { id: "showSpeed", name: "Speed", docLink: "https://github.com/googlemaps/fleet-debugger/blob/main/docs/Speed.md", - columns: ["lastlocation.speed"], + columns: [], solutionTypes: ["ODRD", "LMFS"], }, { @@ -131,7 +131,7 @@ class App extends React.Component { id: "showHighVelocityJumps", name: "Jumps (unrealistic velocity)", docLink: "https://github.com/googlemaps/fleet-debugger/blob/main/docs/VelocityJumps.md", - columns: ["lastlocation.speed"], + columns: [], solutionTypes: ["ODRD", "LMFS"], }, { @@ -145,7 +145,7 @@ class App extends React.Component { id: "showClientServerTimeDeltas", name: "Client/Server Time Deltas", docLink: "https://github.com/googlemaps/fleet-debugger/blob/main/README.md", - columns: ["response.laslLocation.rawlocationlime", "response.laslLocation.serverlime"], + columns: [], solutionTypes: ["ODRD", "LMFS"], }, { diff --git a/src/LogTable.js b/src/LogTable.js index b695a25..b04a165 100644 --- a/src/LogTable.js +++ b/src/LogTable.js @@ -162,7 +162,7 @@ function LogTable(props) { { Header: "DayTime", accessor: "formattedDate", - Cell: ({ cell: { value } }) => value.substring(8, 10) + "T" + value.substring(11, 23), + Cell: ({ cell: { value } }) => value.substring(5, 10) + " " + value.substring(11, 19), width: columnRegularWidth, className: "logtable-cell", solutionTypes: ["ODRD", "LMFS"], diff --git a/src/TrafficPolyline.js b/src/TrafficPolyline.js index c5958ee..ca334da 100644 --- a/src/TrafficPolyline.js +++ b/src/TrafficPolyline.js @@ -5,9 +5,13 @@ import { log } from "./Utils"; export const TRAFFIC_COLORS = { STYLE_NO_DATA: "#808080", // Gray + NO_DATA: "#808080", STYLE_NORMAL: "#4285F4", // Google Maps Blue + NORMAL: "#4285F4", STYLE_SLOWER_TRAFFIC: "#FFA500", // Orange + SLOWER_TRAFFIC: "#FFA500", STYLE_TRAFFIC_JAM: "#FF0000", // Red + TRAFFIC_JAM: "#FF0000", }; export class TrafficPolyline { diff --git a/src/localStorage.js b/src/localStorage.js index 0e1f7a4..b03ebaa 100644 --- a/src/localStorage.js +++ b/src/localStorage.js @@ -140,14 +140,45 @@ export function parseJsonContent(content) { }, {}); }; + const processJsonObject = (obj) => { + if (obj === null || typeof obj !== "object") return obj; + if (Array.isArray(obj)) return obj.map(processJsonObject); + + return Object.keys(obj).reduce((result, key) => { + const newKey = key.replace(/_/g, ""); + let value = obj[key]; + + // Check if this is a value object with only a 'value' property and flatten + if ( + value !== null && + typeof value === "object" && + !Array.isArray(value) && + Object.keys(value).length === 1 && + "value" in value + ) { + value = value.value; + } else if (typeof value === "object" && value !== null) { + // Recursively process nested objects + value = processJsonObject(value); + } + + result[newKey] = value; + return result; + }, {}); + }; + try { const parsed = JSON.parse(content); - return sortObjectKeys(parsed); + const processedData = processJsonObject(parsed); + log("Processed JSON data: removed underscores and flattened value objects"); + return sortObjectKeys(processedData); } catch (error) { log("Initial JSON parsing failed, attempting to wrap in array"); try { const parsed = JSON.parse(`[${content}]`); - return sortObjectKeys(parsed); + const processedData = processJsonObject(parsed); + log("Processed JSON data in array format"); + return sortObjectKeys(processedData); } catch (innerError) { console.error("JSON parsing error:", innerError); throw new Error(`Invalid JSON content: ${innerError.message}`); diff --git a/src/localStorage.test.js b/src/localStorage.test.js index 482db80..60370e2 100644 --- a/src/localStorage.test.js +++ b/src/localStorage.test.js @@ -35,6 +35,95 @@ test("parseJsonContent throws error for invalid JSON", () => { expect(() => parseJsonContent(invalidJson)).toThrow("Invalid JSON content"); }); +test("parseJsonContent removes underscores from keys", () => { + const snakeCaseJson = JSON.stringify({ + snake_case_key: "value", + normal_key: "value2", + }); + + const result = parseJsonContent(snakeCaseJson); + + expect(result).toHaveProperty("snakecasekey", "value"); + expect(result).toHaveProperty("normalkey", "value2"); + expect(result).not.toHaveProperty("snake_case_key"); + expect(result).not.toHaveProperty("normal_key"); +}); + +test("parseJsonContent removes underscores from deeply nested object keys", () => { + const nestedJson = JSON.stringify({ + top_level: { + nested_key: { + deeply_nested_key: "value", + }, + }, + }); + + const result = parseJsonContent(nestedJson); + + expect(result).toHaveProperty("toplevel.nestedkey.deeplynestedkey", "value"); + expect(result).not.toHaveProperty("top_level"); +}); + +// New tests for value object flattening +test("parseJsonContent flattens objects with a single 'value' property", () => { + const valueObjectJson = JSON.stringify({ + normalKey: "normal", + valueObject: { value: "flattened" }, + }); + + const result = parseJsonContent(valueObjectJson); + + expect(result.normalKey).toBe("normal"); + expect(result.valueObject).toBe("flattened"); + expect(typeof result.valueObject).toBe("string"); +}); + +test("parseJsonContent flattens nested objects with a single 'value' property", () => { + const nestedValueObjectJson = JSON.stringify({ + level1: { + level2: { + normalObj: { key: "value" }, + valueObj: { value: "flattened" }, + }, + }, + }); + + const result = parseJsonContent(nestedValueObjectJson); + + expect(result.level1.level2.normalObj.key).toBe("value"); + expect(result.level1.level2.valueObj).toBe("flattened"); + expect(typeof result.level1.level2.valueObj).toBe("string"); +}); + +test("parseJsonContent handles both underscore removal and value flattening together", () => { + const complexJson = JSON.stringify({ + snake_case: { + nested_value_obj: { value: 123 }, + other_key: { some_nested: { value: "test" } }, + }, + }); + + const result = parseJsonContent(complexJson); + + expect(result.snakecase.nestedvalueobj).toBe(123); + expect(result.snakecase.otherkey.somenested).toBe("test"); +}); + +test("parseJsonContent properly handles arrays containing value objects", () => { + const arrayWithValueObjects = JSON.stringify({ + items: [ + { name: "item1", property: { value: 100 } }, + { name: "item2", property: { value: 200 } }, + ], + }); + + const result = parseJsonContent(arrayWithValueObjects); + + expect(result.items[0].name).toBe("item1"); + expect(result.items[0].property).toBe(100); + expect(result.items[1].property).toBe(200); +}); + test("removeEmptyObjects removes empty nested objects", () => { const input = { a: {},