From 8ece05a4b9f924bf9062256ec2d3b3de7ca742cd Mon Sep 17 00:00:00 2001 From: das31 <31259710+das31@users.noreply.github.com> Date: Mon, 27 Mar 2023 12:05:40 -0400 Subject: [PATCH 1/3] CEC-3943-display-ecu-vin-timeline (#303) * first push * fix test * Fix test * resolve comments * fix undefined * align items and change search field * fix dates * resolve comments * fix lint * fix test --------- Co-authored-by: jwu-fisker --- package-lock.json | 40 +++ package.json | 1 + .../App/__snapshots__/App.test.js.snap | 24 ++ .../Status/__snapshots__/index.test.jsx.snap | 24 ++ src/components/Cars/Status/index.jsx | 7 + .../Contexts/DTCTimelineContext.jsx | 43 ++++ .../DTCTimeline/DTCTimeline/index.jsx | 228 ++++++++++++++++++ src/components/DTCTimeline/DTCTimelineTab.jsx | 21 ++ src/services/DTCTimelineAPI.js | 32 +++ 9 files changed, 420 insertions(+) create mode 100644 src/components/Contexts/DTCTimelineContext.jsx create mode 100644 src/components/DTCTimeline/DTCTimeline/index.jsx create mode 100644 src/components/DTCTimeline/DTCTimelineTab.jsx create mode 100644 src/services/DTCTimelineAPI.js diff --git a/package-lock.json b/package-lock.json index 5dd6166..aa4c44c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@emotion/styled": "^11.8.1", "@material-ui/core": "^4.12.4", "@material-ui/icons": "^4.11.3", + "@material-ui/lab": "^4.0.0-alpha.61", "@material-ui/pickers": "^3.3.10", "@mui/material": "^5.10.14", "@superset-ui/embedded-sdk": "^0.1.0-alpha.8", @@ -4237,6 +4238,33 @@ } } }, + "node_modules/@material-ui/lab": { + "version": "4.0.0-alpha.61", + "resolved": "https://registry.npmjs.org/@material-ui/lab/-/lab-4.0.0-alpha.61.tgz", + "integrity": "sha512-rSzm+XKiNUjKegj8bzt5+pygZeckNLOr+IjykH8sYdVk7dE9y2ZuUSofiMV2bJk3qU+JHwexmw+q0RyNZB9ugg==", + "deprecated": "Material UI v4 doesn't receive active development since September 2021. See the guide https://mui.com/material-ui/migration/migration-v4/ to upgrade to v5.", + "dependencies": { + "@babel/runtime": "^7.4.4", + "@material-ui/utils": "^4.11.3", + "clsx": "^1.0.4", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "@material-ui/core": "^4.12.1", + "@types/react": "^16.8.6 || ^17.0.0", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@material-ui/pickers": { "version": "3.3.10", "resolved": "https://registry.npmjs.org/@material-ui/pickers/-/pickers-3.3.10.tgz", @@ -20962,6 +20990,18 @@ "@babel/runtime": "^7.4.4" } }, + "@material-ui/lab": { + "version": "4.0.0-alpha.61", + "resolved": "https://registry.npmjs.org/@material-ui/lab/-/lab-4.0.0-alpha.61.tgz", + "integrity": "sha512-rSzm+XKiNUjKegj8bzt5+pygZeckNLOr+IjykH8sYdVk7dE9y2ZuUSofiMV2bJk3qU+JHwexmw+q0RyNZB9ugg==", + "requires": { + "@babel/runtime": "^7.4.4", + "@material-ui/utils": "^4.11.3", + "clsx": "^1.0.4", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0" + } + }, "@material-ui/pickers": { "version": "3.3.10", "resolved": "https://registry.npmjs.org/@material-ui/pickers/-/pickers-3.3.10.tgz", diff --git a/package.json b/package.json index f90fa4c..04f2a47 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "@emotion/styled": "^11.8.1", "@material-ui/core": "^4.12.4", "@material-ui/icons": "^4.11.3", + "@material-ui/lab": "^4.0.0-alpha.61", "@material-ui/pickers": "^3.3.10", "@mui/material": "^5.10.14", "@superset-ui/embedded-sdk": "^0.1.0-alpha.8", diff --git a/src/components/App/__snapshots__/App.test.js.snap b/src/components/App/__snapshots__/App.test.js.snap index 2f67ceb..ebabefe 100644 --- a/src/components/App/__snapshots__/App.test.js.snap +++ b/src/components/App/__snapshots__/App.test.js.snap @@ -11229,6 +11229,24 @@ exports[`App Route /vehicle-status authenticated 1`] = ` class="MuiTouchRipple-root" /> + + diff --git a/src/components/Cars/Status/__snapshots__/index.test.jsx.snap b/src/components/Cars/Status/__snapshots__/index.test.jsx.snap index 2036b7d..9caaa1f 100644 --- a/src/components/Cars/Status/__snapshots__/index.test.jsx.snap +++ b/src/components/Cars/Status/__snapshots__/index.test.jsx.snap @@ -213,6 +213,24 @@ exports[`CarStatus Render 1`] = ` class="MuiTouchRipple-root" /> + + diff --git a/src/components/Cars/Status/index.jsx b/src/components/Cars/Status/index.jsx index 98f223a..1cde8dd 100644 --- a/src/components/Cars/Status/index.jsx +++ b/src/components/Cars/Status/index.jsx @@ -19,6 +19,7 @@ import ECUsTab from "./ECUsTab"; import FleetsTab from "./FleetsTab"; import RemoteCommandsTab from "./RemoteCommandsTab"; import TRexLogsTab from "./TRexLogsTab"; +import DTCTimeline from "../../DTCTimeline/DTCTimeline"; const tabHashes = ["details", "updates", "filters"]; @@ -72,7 +73,13 @@ const TabViews = [ label: "CAN Signal Export", component: SelfServeTab, rolesPerProvider: Permissions.FiskerRead, + }, + { + label: "DTC Timeline", + component: DTCTimeline, + rolesPerProvider: Permissions.FiskerMagnaRead, } + ]; const filterTabs = (data, groups, providers) => { diff --git a/src/components/Contexts/DTCTimelineContext.jsx b/src/components/Contexts/DTCTimelineContext.jsx new file mode 100644 index 0000000..9649182 --- /dev/null +++ b/src/components/Contexts/DTCTimelineContext.jsx @@ -0,0 +1,43 @@ +import React, { useContext, useState } from "react"; +import api from "../../services/DTCTimelineAPI"; + +const DTCTimelineContext = React.createContext(); + +export const DTCTimelineProvider = ({ children }) => { + const [busy, setBusy] = useState(false); + + const [dtcData, setDTCData] = useState([]); + const [total, setTotal] = useState(0) + + const getDTCData = async (vin, ecu, startDate, endDate, search,token) => { + try { + setBusy(true); + const result = await api.getDTCData(vin, ecu, startDate, endDate, search,token); + if (result.error) { + throw new Error(`Get DTC data error. ${result.message}`); + } + setDTCData(result.data ?? []); + if (result.total){ + setTotal(result.total) + } + return result; + } finally { + setBusy(false); + } + }; + + return ( + + {children} + + ); +}; + +export const useDTCTimelineContext = () => useContext(DTCTimelineContext); diff --git a/src/components/DTCTimeline/DTCTimeline/index.jsx b/src/components/DTCTimeline/DTCTimeline/index.jsx new file mode 100644 index 0000000..2af0b2b --- /dev/null +++ b/src/components/DTCTimeline/DTCTimeline/index.jsx @@ -0,0 +1,228 @@ +import DateFnsUtils from '@date-io/date-fns'; +import { Button, CircularProgress, Grid, TableFooter, TablePagination, TableCell, Table, TableRow, TableBody} from "@material-ui/core"; +import { KeyboardDatePicker, MuiPickersUtilsProvider } from '@material-ui/pickers'; +import React, { useEffect, useState } from "react"; +import { logger } from "../../../services/monitoring"; +import { DTCTimelineProvider, useDTCTimelineContext } from '../../Contexts/DTCTimelineContext'; +import { useStatusContext } from "../../Contexts/StatusContext"; +import { useUserContext } from "../../Contexts/UserContext"; +import { useLocalStorage } from "../../useLocalStorage"; +import clsx from "clsx"; +import TableHeaderSortable from "../../Table/HeaderSortable"; +import SearchField from '../../Controls/SearchField'; +import useStyles from "../../useStyles"; + +const MainForm = ({ vin }) => { + const classes = useStyles(); + + const PAGE_SIZE = "DTC_TABLE_PAGE_SIZE"; + const [pageSize, setPageSize] = useLocalStorage(PAGE_SIZE, 10); + const [pageIndex, setPageIndex] = useState(0); + const [orderBy, setOrderBy] = useState("epoch_usec"); + const [order, setOrder] = useState("desc"); + const { dtcData, getDTCData, total=0 } = useDTCTimelineContext(); + const [selectedStartDate, setSelectedStartDate] = useState(new Date(Date.now() - 24 * 60 * 60 * 1000)); + const [selectedEndDate, setSelectedEndDate] = useState(new Date()); + const [selectedECU, setSelectedECU] = useState(""); + const [loading, setLoading] = useState(false); + const { setMessage } = useStatusContext(); + + const tableColumns = [ + { + id: "id", + label: "Id", + }, + { + id: "vin", + label: "VIN", + }, + { + id: "ecu", + label: "ECU", + }, + { + id: "dtc", + label: "DTC", + }, + { + id: "epoch_usec", + label: "Date", + }, + ]; + + const handleSort = (_event, property) => { + if (property === orderBy) { + if (order === "asc") { + setOrder("desc"); + } else { + setOrder("asc"); + } + } else { + setOrderBy(property); + setOrder("desc"); + } + }; + + const handleChangePageIndex = (_event, newIndex) => { + setPageIndex(newIndex); + }; + + const handleChangePageSize = (event) => { + setPageSize(parseInt(event.target.value, 10)); + setPageIndex(0); + }; + + const { + token: { + idToken: { jwtToken: token }, + }, + } = useUserContext(); + + const fetchDTCData = async () => { + setLoading(true); + try { + let start_date = new Date(selectedStartDate); + start_date.setHours(0, 0, 0, 0); + start_date = start_date.toISOString(); + + let end_date = new Date(selectedEndDate); + end_date.setHours(23, 59, 59, 999); + end_date = end_date.toISOString(); + + const search = { + limit: pageSize, + offset: pageSize * pageIndex, + order: `${orderBy} ${order}`, + } + await getDTCData(vin, selectedECU, start_date, end_date, search, token); + // setDTCData(data); + } catch (e) { + setMessage(e.message); + logger.warn(e.stack); + } + setLoading(false); + }; + + function formatDate(microseconds) { + const date = new Date(microseconds / 1000); + return date.toLocaleString(); + } + + useEffect(() => { + fetchDTCData(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [vin, selectedECU, selectedStartDate, selectedEndDate, pageIndex, pageSize, order, orderBy]); + + return ( +
+ + +
+ { + setSelectedECU(searchValue); + }} + /> +
+
+ + + + + + + + + + + + + +
+
+ + + + {(dtcData || []).map((dtc, index) => ( + + + {dtc.id} + + {dtc.vin} + {dtc.ecu_name} + {dtc.dtc} + {formatDate(dtc.epoch_usec)} + + ))} + + + + + + +
+
+
+ ); + +}; + +const DTCTimeline = (props) => ( + + + +); + +export default DTCTimeline; \ No newline at end of file diff --git a/src/components/DTCTimeline/DTCTimelineTab.jsx b/src/components/DTCTimeline/DTCTimelineTab.jsx new file mode 100644 index 0000000..e8d7352 --- /dev/null +++ b/src/components/DTCTimeline/DTCTimelineTab.jsx @@ -0,0 +1,21 @@ +import { Typography } from "@material-ui/core"; +import clsx from "clsx"; +import React from "react"; +import { useParams } from "react-router"; + +import useStyles from "../useStyles"; +import DTCTimeline from "./DTCTimeline"; + +const SelfServeTab = () => { + const { vin } = useParams(); + const classes = useStyles(); + + return ( +
+ DTC Timeline + +
+ ); +}; + +export default SelfServeTab; \ No newline at end of file diff --git a/src/services/DTCTimelineAPI.js b/src/services/DTCTimelineAPI.js new file mode 100644 index 0000000..6468b2d --- /dev/null +++ b/src/services/DTCTimelineAPI.js @@ -0,0 +1,32 @@ +import { + addQueryParams, + errorHandler, + fetchRespHandler, + getAuthHeaderOptions, +} from "../utils/http"; + +const API_ENDPOINT = process.env.REACT_APP_OTA_SERVICE_URL; + +const DTCTimelineAPI = { + getDTCData: async (vin, ecu, startDate, endDate,search,token) => { + const queryParams = { + ecu, + start_time: startDate, + end_time: endDate, + ...search, + }; + const url = addQueryParams(`${API_ENDPOINT}/ecus/${vin}`, queryParams); + console.log(url) + return fetch(url, { + method: "GET", + headers: Object.assign( + { "Content-Type": "application/json" }, + getAuthHeaderOptions(token) + ), + }) + .then(fetchRespHandler) + .catch(errorHandler); + }, +}; + +export default DTCTimelineAPI; \ No newline at end of file From 7dd4a1c4c0713f6bc96c0ce383ea3ddd6404e674 Mon Sep 17 00:00:00 2001 From: John Wu <76966357+jwu-fisker@users.noreply.github.com> Date: Tue, 28 Mar 2023 09:52:11 -0700 Subject: [PATCH 2/3] CEC-3980 Remove ICC certificate type (#304) --- .../App/__snapshots__/App.test.js.snap | 55 +------------------ .../Certificates/Add/CreateForm.jsx | 2 +- src/utils/certificates.js | 10 +--- 3 files changed, 5 insertions(+), 62 deletions(-) diff --git a/src/components/App/__snapshots__/App.test.js.snap b/src/components/App/__snapshots__/App.test.js.snap index ebabefe..10cd7ed 100644 --- a/src/components/App/__snapshots__/App.test.js.snap +++ b/src/components/App/__snapshots__/App.test.js.snap @@ -7850,7 +7850,7 @@ exports[`App Route /tools/certificates/add authenticated 1`] = ` class="PrivateSwitchBase-input-0" name="cert-type" type="radio" - value="ICC" + value="Charging" />
- ICC + Charging -