From 41f8e395e88235f6977456aa0f18c5e8923a943e Mon Sep 17 00:00:00 2001 From: Louis King Date: Fri, 24 Jul 2026 23:49:16 +0100 Subject: [PATCH] fix(web): compact Routes stats dash and chart 'Now' label MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace arrow with dash in the matched/threshold/degraded stat (0/2→6 becomes 0/2-6) to save horizontal space and prevent the stats row from wrapping on mobile. Change the last history chart label from 'Last XXh' to 'Now' for clarity and brevity. Add 'now' key to en.json ('Now') and nl.json ('Nu'); remove the now-dead 'last_n_hours' key from both locale files. Add tests for the dash separator format and the 'Now' label. --- .../static/js/spa-react/pages/Routes.test.tsx | 46 +++++++++++++++++++ .../web/static/js/spa-react/pages/Routes.tsx | 4 +- src/meshcore_hub/web/static/locales/en.json | 4 +- src/meshcore_hub/web/static/locales/nl.json | 4 +- 4 files changed, 52 insertions(+), 6 deletions(-) diff --git a/src/meshcore_hub/web/static/js/spa-react/pages/Routes.test.tsx b/src/meshcore_hub/web/static/js/spa-react/pages/Routes.test.tsx index 0c171da..b97c844 100644 --- a/src/meshcore_hub/web/static/js/spa-react/pages/Routes.test.tsx +++ b/src/meshcore_hub/web/static/js/spa-react/pages/Routes.test.tsx @@ -74,6 +74,52 @@ describe("Routes", () => { ).not.toBeNull(); }); + it("renders matched/threshold/degraded stat with dash separator", async () => { + const data = { + items: [ + { + ...ROUTES.items[0], + route_result: { + quality: "clear", + state: "healthy", + matched_count: 3, + threshold: 5, + effective_clear: 2, + }, + }, + ], + }; + vi.spyOn(api, "apiGet").mockImplementation(async (path) => { + if (path === "/api/v1/routes") return data; + if (path.match(/\/api\/v1\/routes\/[^/]+$/)) return ROUTE_DETAIL; + if (path.includes("/history")) return ROUTE_HISTORY; + throw new Error(`Unexpected: ${path}`); + }); + renderWithProviders(); + await waitFor(() => { + expect(screen.getByText("3/5-2")).toBeInTheDocument(); + }); + }); + + it("shows Now label for the last history chart entry", async () => { + const historyWithData = { + data: [ + { date: "2026-07-20", matched: 0, total: 0 }, + { date: "2026-07-21", matched: 1, total: 1 }, + ], + }; + vi.spyOn(api, "apiGet").mockImplementation(async (path) => { + if (path === "/api/v1/routes") return ROUTES; + if (path.match(/\/api\/v1\/routes\/[^/]+$/)) return ROUTE_DETAIL; + if (path.includes("/history")) return historyWithData; + throw new Error(`Unexpected: ${path}`); + }); + renderWithProviders(); + await waitFor(() => { + expect(screen.getByText("common.now")).toBeInTheDocument(); + }); + }); + it("shows an error on fetch failure", async () => { vi.spyOn(api, "apiGet").mockRejectedValue(new Error("routes error")); renderWithProviders(); diff --git a/src/meshcore_hub/web/static/js/spa-react/pages/Routes.tsx b/src/meshcore_hub/web/static/js/spa-react/pages/Routes.tsx index e44187b..4a8fed4 100644 --- a/src/meshcore_hub/web/static/js/spa-react/pages/Routes.tsx +++ b/src/meshcore_hub/web/static/js/spa-react/pages/Routes.tsx @@ -310,7 +310,7 @@ function StatsRow({ route }: { route: RouteItem }) { {matched}/{threshold} - {"\u2192"} + {"-"} {degraded} @@ -473,7 +473,7 @@ function DetailContent({ {historyData.map((d, i) => ( {i === historyData.length - 1 - ? t("routes.last_n_hours", { n: route.window_hours }) + ? t("common.now") : new Date(`${d.date}T00:00:00`).toLocaleDateString( undefined, { day: "2-digit", month: "2-digit" }, diff --git a/src/meshcore_hub/web/static/locales/en.json b/src/meshcore_hub/web/static/locales/en.json index deb2d85..3a0aab6 100644 --- a/src/meshcore_hub/web/static/locales/en.json +++ b/src/meshcore_hub/web/static/locales/en.json @@ -117,7 +117,8 @@ "unnamed_node": "Unnamed Node", "validation_invalid_number": "Value must be a valid number", "validation_invalid_boolean": "Value must be true, false, yes, no, 1, or 0", - "sort_by": "Sort by" + "sort_by": "Sort by", + "now": "Now" }, "links": { "website": "Website", @@ -359,7 +360,6 @@ "quality_no_coverage": "No Coverage", "quality_unknown": "Unknown", "recent_packets": "Recent Packets", - "last_n_hours": "Last {{n}}h", "min_nodes_error": "At least 2 path nodes are required.", "other_routes": "Other routes", "filter_mine": "Show only my routes" diff --git a/src/meshcore_hub/web/static/locales/nl.json b/src/meshcore_hub/web/static/locales/nl.json index 014b915..a041a19 100644 --- a/src/meshcore_hub/web/static/locales/nl.json +++ b/src/meshcore_hub/web/static/locales/nl.json @@ -108,7 +108,8 @@ "filter_operator_label": "Operator", "filter_observer_label": "Waarnemer", "filter_observer_enable": "Klik om deze waarnemer te tonen", - "filter_observer_disable": "Klik om deze waarnemer te verbergen" + "filter_observer_disable": "Klik om deze waarnemer te verbergen", + "now": "Nu" }, "links": { "website": "Website", @@ -282,7 +283,6 @@ "quality_no_coverage": "Geen dekking", "quality_unknown": "Onbekend", "recent_packets": "Recente packets", - "last_n_hours": "Laatste {{n}}u", "min_nodes_error": "Minimaal 2 padknooppunten zijn vereist.", "other_routes": "Overige routes", "filter_mine": "Toon alleen mijn routes"