From 9ae96fda49adadb6e321e9a0c9ac47a8f37b623b Mon Sep 17 00:00:00 2001 From: Paulo Andre Azevedo Quirino Date: Thu, 24 Oct 2024 15:49:53 +0100 Subject: [PATCH 01/10] FP-2857: Fleetboard - Logs refactor, merge fix for FP-2810 --- CHANGELOG.md | 2 + src/Components/Logs/Logs.js | 148 +++++++++--------- .../sub-components/SearchInput.js | 2 +- 3 files changed, 76 insertions(+), 76 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2c58b3e..b032568e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # TBD +- [FP-2857](https://movai.atlassian.net/browse/FP-2857): Fleetboard - Logs refactor, merge fix for FP-2810 +- [FP-2780](https://movai.atlassian.net/browse/FP-2780): Fleetboard - Logs - clear search not working - [FP-2917](https://movai.atlassian.net/browse/FP-2917): Configure husky, lint-staged and prettier for lib-react # 1.3.7 diff --git a/src/Components/Logs/Logs.js b/src/Components/Logs/Logs.js index f0357f7c..d90bf9e7 100644 --- a/src/Components/Logs/Logs.js +++ b/src/Components/Logs/Logs.js @@ -19,18 +19,69 @@ import { useStyles } from "./styles"; import { logsSub } from "./sub"; import "./Logs.css"; +/** + * Tranform log from the format received from the API to the format + * required to be rendered + * @returns {array} Transformed log + */ +function transformLog(log, _index, _data, ts_multiplier = 1000) { + const timestamp = ts_multiplier * log.time; + const date = new Date(timestamp); + return { + ...log, + timestamp, + time: date.toLocaleTimeString(), + date: date.toLocaleDateString(), + key: log.message + timestamp, + }; +} + +/** + * Remove duplicates from logs for the second overlaping the + * current and the last request + * @returns {array} Concatenated logs without duplicates + */ +export function logsDedupe(oldLogs, data) { + if (!data.length) return oldLogs; + + // date of the oldest log received in the current request + const oldDate = data[data.length - 1].timestamp; + // map to store the old logs of the overlaped second + let map = {}; + + // iter over old logs with last timestamp of the new logs + // and put in a map + for (let i = 0; i < oldLogs.length && oldLogs[i].timestamp === oldDate; i++) + map[oldLogs[i].message] = oldLogs[i]; + + // array to store logs from overlap second which had not + // been sent before + let newSecOverlap = []; + let z; + + // iter over new logs (oldest to latest) with last timestamp, + // check if present in last map + // - if not, push + for (z = data.length - 1; z >= 0 && data[z].timestamp === oldDate; z--) + if (!map[data[z].message]) newSecOverlap.push(data[z]); + + // cut new logs up to z, concat with the deduped ones + // and the old logs up to i + return data.slice(0, z + 1).concat(newSecOverlap.reverse(), oldLogs); +} + // TODO this should be exported. Fleetboard uses it function blobDownload(file, fileName, charset = "text/plain;charset=utf-8") { const blob = new Blob([file], { type: charset }); const url = URL.createObjectURL(blob); - const a = document.createElement("a"); - document.body.appendChild(a); + const a = globalThis.document.createElement("a"); + globalThis.document.body.appendChild(a); a.style = "display: none"; a.href = url; a.download = fileName; a.click(); URL.revokeObjectURL(url); - document.body.removeChild(a); + globalThis.document.body.removeChild(a); } function noSelection(obj) { @@ -46,16 +97,6 @@ function getRobots(robotsData) { .reduce((a, robot) => ({ ...a, [robot]: true }), {}); } -async function hashString(string) { - const msgUint8 = new TextEncoder().encode(string); - const hashBuffer = await crypto.subtle.digest("SHA-256", msgUint8); - const hashArray = Array.from(new Uint8Array(hashBuffer)); - const hashHex = hashArray - .map((b) => b.toString(16).padStart(2, "0")) - .join(""); - return hashHex; -} - function matchTags(tags, item) { for (const tag in tags) if (item[tag] !== undefined) continue; @@ -142,54 +183,20 @@ const Logs = (props) => { limit: MAX_FETCH_LOGS, date: { from: logsDataGlobal.length - ? logsDataGlobal[logsDataGlobal.length - 1].timestamp + ? logsDataGlobal[0].timestamp : selectedFromDate, to: selectedToDate, }, - }) - .then((response) => { - const data = response?.data || []; - return Promise.all( - [Promise.resolve(data)].concat( - data.map((item) => hashString(item.message)), - ), - ); - }) - .then(([data, ...hashes]) => { - const oldLogs = logsDataGlobal || []; - let j = data.length - 1; - - for (let i = 0; j > -1 && i < oldLogs.length; i++, j--) { - const timestamp = data[j].time * 1000; - const date = new Date(timestamp); - - if ( - date === oldLogs[i].timestamp && - hashes[j] + timestamp * 1000 === oldLogs[i].key - ) - break; - - if (date < oldLogs[i].timestamp) break; - } + }).then((response) => { + const data = response?.data || []; + const oldLogs = logsDataGlobal || []; + const newLogs = (logsDataGlobal = logsDedupe( + oldLogs, + data.map(transformLog), + ).slice(0, MAX_FETCH_LOGS)); - const newLogs = (logsDataGlobal = data - .slice(0, j) - .map((log, index) => { - const timestamp = log.time * 1000; - const date = new Date(timestamp); - return { - ...log, - timestamp: date, - time: date.toLocaleTimeString(), - date: date.toLocaleDateString(), - key: hashes[index] + timestamp * 1000, - }; - }) - .concat(oldLogs) - .slice(0, MAX_FETCH_LOGS)); - - setLogsData(newLogs); - }); + setLogsData(newLogs); + }); }, [ selectedFromDate, selectedToDate, @@ -208,22 +215,13 @@ const Logs = (props) => { const onMessage = useCallback( (msg) => { const item = JSON.parse(msg?.data ?? {}); - const date = new Date(item.time / 1000000); - hashString(item.message).then((hash) => { - setLogsData( - (prevState) => - (logsDataGlobal = [ - { - ...item, - timestamp: date, - time: date.toLocaleTimeString(), - date: date.toLocaleDateString(), - key: hash + item.time, - }, - ...prevState, - ].slice(0, MAX_FETCH_LOGS)), - ); - }); + setLogsData( + (prevState) => + (logsDataGlobal = [ + transformLog(item, 0, [item], 0.000001), + ...prevState, + ].slice(0, MAX_FETCH_LOGS)), + ); }, [setLogsData], ); @@ -250,8 +248,8 @@ const Logs = (props) => { const handleExport = useCallback(() => { const sep = "\t"; const contents = filteredLogs.map((log) => { - const [date, time] = getDateTime(log.time); - return [date, time, log.robot, log.message].join(sep); + const { date, time, robot, message } = log; + return [date, time, robot, message].join(sep); }); // from https://www.epochconverter.com/programming/ const dateString = !filteredLogs.length diff --git a/src/Components/Logs/LogsFilterBar/sub-components/SearchInput.js b/src/Components/Logs/LogsFilterBar/sub-components/SearchInput.js index 5f5432d6..2c5f7a58 100644 --- a/src/Components/Logs/LogsFilterBar/sub-components/SearchInput.js +++ b/src/Components/Logs/LogsFilterBar/sub-components/SearchInput.js @@ -29,7 +29,7 @@ const SearchInput = () => { onChangeText("")} + onClick={() => onChangeText({ target: { value: "" } })} > From 454c3e68fa5aa81c8a13d0d303c5c66282bb061d Mon Sep 17 00:00:00 2001 From: Paulo Andre Azevedo Quirino Date: Mon, 28 Oct 2024 14:47:47 +0000 Subject: [PATCH 02/10] Get remaining differences from #341 --- src/Components/Logs/Logs.js | 3 +- src/Components/Logs/Logs.test.js | 51 +++++++++++++++++++++++++++ src/Components/Modal/RobotLogModal.js | 5 ++- 3 files changed, 54 insertions(+), 5 deletions(-) create mode 100644 src/Components/Logs/Logs.test.js diff --git a/src/Components/Logs/Logs.js b/src/Components/Logs/Logs.js index d90bf9e7..882d4feb 100644 --- a/src/Components/Logs/Logs.js +++ b/src/Components/Logs/Logs.js @@ -12,7 +12,6 @@ import LogsFilterBar from "./LogsFilterBar/LogsFilterBar"; import LogsTable from "./LogsTable/LogsTable"; import { ROBOT_LOG_TYPE } from "./utils/Constants"; import { COLUMNS_LABEL } from "./utils/Constants"; -import { getDateTime } from "./utils/Utils"; import useUpdateEffect from "./hooks/useUpdateEffect"; import _isEqual from "lodash/isEqual"; import { useStyles } from "./styles"; @@ -254,7 +253,7 @@ const Logs = (props) => { // from https://www.epochconverter.com/programming/ const dateString = !filteredLogs.length ? new Date().toISOString() - : new Date(filteredLogs[0].time * 1e3).toISOString(); + : new Date(filteredLogs[0].time * 0.001).toISOString(); const columnLabels = Object.keys(columns) .filter((key) => columns[key]) .map((key) => COLUMNS_LABEL[key]); diff --git a/src/Components/Logs/Logs.test.js b/src/Components/Logs/Logs.test.js new file mode 100644 index 00000000..1257f467 --- /dev/null +++ b/src/Components/Logs/Logs.test.js @@ -0,0 +1,51 @@ +import { logsDedupe } from "./Logs.js"; + +describe("Dedupes logs correctly", () => { + // this first test is based on an algorithm + // that does not keep relative order. If that's + // something we're interested in. We have to change it + it("Dedupes without keeping relative order", () => { + const a = [ + { timestamp: 1, message: "e" }, + { timestamp: 1, message: "c" }, + { timestamp: 0, message: "b" }, + { timestamp: 0, message: "a" }, + ]; + const b = [ + { timestamp: 2, message: "f" }, + { timestamp: 1, message: "e" }, + { timestamp: 1, message: "d" }, + { timestamp: 1, message: "c" }, + ]; + expect(logsDedupe(a, b)).toEqual([ + { timestamp: 2, message: "f" }, + { timestamp: 1, message: "d" }, + { timestamp: 1, message: "e" }, + { timestamp: 1, message: "c" }, + { timestamp: 0, message: "b" }, + { timestamp: 0, message: "a" }, + ]); + }); + + it("returns deduped logs 1", () => { + const a = [ + { timestamp: 1, message: "c" }, + { timestamp: 0, message: "b" }, + { timestamp: 0, message: "a" }, + ]; + const b = [ + { timestamp: 2, message: "f" }, + { timestamp: 1, message: "e" }, + { timestamp: 1, message: "d" }, + { timestamp: 1, message: "c" }, + ]; + expect(logsDedupe(a, b)).toEqual([ + { timestamp: 2, message: "f" }, + { timestamp: 1, message: "e" }, + { timestamp: 1, message: "d" }, + { timestamp: 1, message: "c" }, + { timestamp: 0, message: "b" }, + { timestamp: 0, message: "a" }, + ]); + }); +}); diff --git a/src/Components/Modal/RobotLogModal.js b/src/Components/Modal/RobotLogModal.js index c71314ee..4371eaf8 100644 --- a/src/Components/Modal/RobotLogModal.js +++ b/src/Components/Modal/RobotLogModal.js @@ -13,7 +13,7 @@ import ChatIcon from "@material-ui/icons/Chat"; import { MasterDB } from "@mov-ai/mov-fe-lib-core"; import AbstractModal from "./AbstractModal"; -const styles = (theme) => ({ +const styles = (_theme) => ({ breakWord: { wordBreak: "break-all", }, @@ -29,11 +29,10 @@ class RobotLogModal extends Component { open = (alert) => { const { data } = this.state; // Format time - const time = new Date(alert.time * 1000); const alertButton = alert.button ? JSON.parse(alert.button.replace(/'/g, '"')) : null; - data.time = `${time.toLocaleTimeString("pt")}`; + data.time = alert.time; data.action = alert.action; data.message = alert.message; data.robot = alert.robot; From bcfb12bb2d97114615afc01a0632efc6a8956e43 Mon Sep 17 00:00:00 2001 From: Paulo Andre Azevedo Quirino Date: Mon, 28 Oct 2024 14:54:10 +0000 Subject: [PATCH 03/10] Fix css jest problem --- jest.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index 47bb9e21..07842225 100644 --- a/jest.config.js +++ b/jest.config.js @@ -6,7 +6,7 @@ module.exports = { moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], testPathIgnorePatterns: ["/node_modules/", "/dist/"], moduleNameMapper: { - "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|css|oga)$": "/src/__mocks__/fileMock.js", "@fontsource/.+": "/src/__mocks__/fileMock.js", }, From 90d3aa56ccc22fdca9babb8a8538f5096ff17ae6 Mon Sep 17 00:00:00 2001 From: Edward Angeles Date: Tue, 12 Nov 2024 11:36:00 +0000 Subject: [PATCH 04/10] Remove redundant onClick handler from ProfileMenu --- src/Components/ProfileMenu/ProfileMenu.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Components/ProfileMenu/ProfileMenu.tsx b/src/Components/ProfileMenu/ProfileMenu.tsx index 0937a3f8..ed3c8ce9 100644 --- a/src/Components/ProfileMenu/ProfileMenu.tsx +++ b/src/Components/ProfileMenu/ProfileMenu.tsx @@ -181,10 +181,7 @@ const ProfileMenu = (props: ProfileMenuProps) => { )} {customEl} {handleToggleTheme && ( - ev.preventDefault()} - > + {darkThemeLabel} Date: Mon, 9 Dec 2024 11:23:15 +0000 Subject: [PATCH 05/10] Update package and CHANGELOG --- CHANGELOG.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b032568e..5a1bc367 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# TBD +# v1.3.8 - [FP-2857](https://movai.atlassian.net/browse/FP-2857): Fleetboard - Logs refactor, merge fix for FP-2810 - [FP-2780](https://movai.atlassian.net/browse/FP-2780): Fleetboard - Logs - clear search not working diff --git a/package.json b/package.json index 0121af06..5e19868c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mov-ai/mov-fe-lib-react", - "version": "1.3.7-1", + "version": "1.3.8-0", "description": "The Mov.AI's frontend library for React.", "keywords": [ "frontend", From 0a5689ea95735bfc86f3350e221d7de1dab0d548 Mon Sep 17 00:00:00 2001 From: OttoMation-Movai Date: Mon, 9 Dec 2024 14:08:56 +0000 Subject: [PATCH 06/10] [skip actions] Automatic Bump of build version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5e19868c..6e95f5bc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mov-ai/mov-fe-lib-react", - "version": "1.3.8-0", + "version": "1.3.8-1", "description": "The Mov.AI's frontend library for React.", "keywords": [ "frontend", From e0c54449768188ce189123d2e54bf2ea9f03d459 Mon Sep 17 00:00:00 2001 From: Manuel Nogueira Date: Mon, 16 Dec 2024 19:59:51 +0000 Subject: [PATCH 07/10] Release lib-react 1.3.9 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a1bc367..37131d1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# v1.3.9 + +- [FP-2959](https://movai.atlassian.net/browse/FP-2959): Removed onClick handler that was causing issues + # v1.3.8 - [FP-2857](https://movai.atlassian.net/browse/FP-2857): Fleetboard - Logs refactor, merge fix for FP-2810 diff --git a/package.json b/package.json index 5e19868c..221753c5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mov-ai/mov-fe-lib-react", - "version": "1.3.8-0", + "version": "1.3.9-0", "description": "The Mov.AI's frontend library for React.", "keywords": [ "frontend", From c816cad8d2b9bfa417722c73ac9962bd34104378 Mon Sep 17 00:00:00 2001 From: Manuel Nogueira Date: Mon, 16 Dec 2024 20:06:38 +0000 Subject: [PATCH 08/10] Used a ticket that made more sense for the issue --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37131d1f..157289c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # v1.3.9 -- [FP-2959](https://movai.atlassian.net/browse/FP-2959): Removed onClick handler that was causing issues +- [FP-3093](https://movai.atlassian.net/browse/FP-3093): Removed onClick handler that was causing issues # v1.3.8 From 2f5d8dc8b85932ff4a872dee6fdb55d527cd4261 Mon Sep 17 00:00:00 2001 From: Manuel Nogueira Date: Wed, 18 Dec 2024 11:43:10 +0000 Subject: [PATCH 09/10] Add the correct version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6e95f5bc..4d136cd6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mov-ai/mov-fe-lib-react", - "version": "1.3.8-1", + "version": "1.3.9", "description": "The Mov.AI's frontend library for React.", "keywords": [ "frontend", From 8d32dc5f613e8f3c72163aa6b17190a431fcf5e3 Mon Sep 17 00:00:00 2001 From: OttoMation-Movai Date: Wed, 18 Dec 2024 12:00:49 +0000 Subject: [PATCH 10/10] [skip actions] Automatic Bump of build version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4d136cd6..bb37c5a5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mov-ai/mov-fe-lib-react", - "version": "1.3.9", + "version": "1.3.10-0", "description": "The Mov.AI's frontend library for React.", "keywords": [ "frontend",