diff --git a/src/components/App/__snapshots__/App.test.js.snap b/src/components/App/__snapshots__/App.test.js.snap index 637ad6b..c42b644 100644 --- a/src/components/App/__snapshots__/App.test.js.snap +++ b/src/components/App/__snapshots__/App.test.js.snap @@ -6918,6 +6918,24 @@ exports[`App Route /vehicle-status authenticated 1`] = ` class="MuiTouchRipple-root" /> + + diff --git a/src/components/CANFilter/Table/index.jsx b/src/components/CANFilter/Table/index.jsx index 61b2a7a..55d985d 100644 --- a/src/components/CANFilter/Table/index.jsx +++ b/src/components/CANFilter/Table/index.jsx @@ -53,6 +53,7 @@ const PAGE_SIZE = "CAN_FILTER_TABLE_PAGE_SIZE"; const MainForm = ({ vin }) => { const classes = useStyles(); + const [search, onSearch] = useState(""); const [pageSize, setPageSize] = useLocalStorage(PAGE_SIZE, 10); const [pageIndex, setPageIndex] = useState(0); const [orderBy, setOrderBy] = useState("id"); @@ -68,6 +69,7 @@ const MainForm = ({ vin }) => { await getFilters( vin, { + search, limit: pageSize, offset: pageSize * pageIndex, order: `${orderBy} ${order}`, @@ -80,7 +82,7 @@ const MainForm = ({ vin }) => { } })(); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [vin, token, pageIndex, pageSize, orderBy, order]); + }, [vin, token, pageIndex, pageSize, orderBy, order, search]); const handleChangePageIndex = (event, newIndex) => { setPageIndex(newIndex); @@ -166,7 +168,7 @@ const MainForm = ({ vin }) => { - + diff --git a/src/components/Cars/Status/FleetsTab.jsx b/src/components/Cars/Status/FleetsTab.jsx new file mode 100644 index 0000000..f981fd0 --- /dev/null +++ b/src/components/Cars/Status/FleetsTab.jsx @@ -0,0 +1,140 @@ +import useStyles from "../../useStyles"; +import clsx from "clsx"; +import {Grid, Table, TableBody, TableCell, TableFooter, TablePagination, TableRow, Typography} from "@material-ui/core"; +import React, {useEffect, useState} from "react"; +import {useVehicleContext, VehicleProvider} from "../../Contexts/VehicleContext"; +import SearchField from "../../Controls/SearchField"; +import {useLocalStorage} from "../../useLocalStorage"; +import {useStatusContext} from "../../Contexts/StatusContext"; +import {useUserContext} from "../../Contexts/UserContext"; +import {logger} from "../../../services/monitoring"; +import TableHeaderSortable from "../../Table/HeaderSortable"; +import {Link} from "react-router-dom"; + +const PAGE_SIZE = "VEHICLE_FLEETS_TABLE_PAGE_SIZE"; + +const tableColumns = [ + { + id: "fleet", + label: "CAN ID" + }, +]; + +const MainForm = (props) => { + const classes = useStyles(); + const {vin} = props; + + const [search, onSearch] = useState(""); + const [pageSize, setPageSize] = useLocalStorage(PAGE_SIZE, 10); + const [pageIndex, setPageIndex] = useState(0); + const [orderBy, setOrderBy] = useState("id"); + const [order, setOrder] = useState("desc"); + const {setMessage} = useStatusContext(); + const {token: {idToken: {jwtToken: token}}} = useUserContext(); + const {getFleets, fleets, totalFleets} = useVehicleContext(); + + useEffect(() => { + (async () => { + try { + if (!vin || !token) return; + await getFleets( + vin, + { + search, + limit: pageSize, + offset: pageSize * pageIndex, + order: `${orderBy} ${order}`, + }, + token + ); + } catch (e) { + setMessage(e.message); + logger.warn(e.stack); + } + })(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [vin, token, pageIndex, pageSize, orderBy, order, search]); + + const handleChangePageIndex = (event, newIndex) => { + setPageIndex(newIndex); + }; + + const handleChangePageSize = (event) => { + setPageSize(parseInt(event.target.value, 10)); + setPageIndex(0); + }; + + const handleSort = (event, property) => { + try { + if (property === orderBy) { + if (order === "asc") { + setOrder("desc"); + } else { + setOrder("asc"); + } + } else { + setOrderBy(property); + setOrder("asc"); + } + } catch (e) { + logger.warn(e.stack); + } + }; + + return ( +
+ Fleets + + + + + +
+ + + {fleets.map((row) => ( + + + {row} + + + ))} + + + + + + +
+ + ) +} + +const FleetsTab = (props) => { + return ( + + + + ) +} + + +export default FleetsTab; \ No newline at end of file diff --git a/src/components/Cars/Status/FleetsTab.test.jsx b/src/components/Cars/Status/FleetsTab.test.jsx new file mode 100644 index 0000000..58fad88 --- /dev/null +++ b/src/components/Cars/Status/FleetsTab.test.jsx @@ -0,0 +1,47 @@ +jest.mock("../../Contexts/VehicleContext"); +jest.mock("../../Contexts/StatusContext"); +jest.mock("../../Contexts/UserContext"); +jest.mock("@material-ui/core/utils/unstable_useId", () => + jest.fn().mockReturnValue("mui-test-id") +); + +import React from "react"; +import {render, waitFor} from "@testing-library/react"; +import { BrowserRouter } from "react-router-dom"; + +import {StatusProvider} from "../../Contexts/StatusContext"; +import {VehicleProvider} from "../../Contexts/VehicleContext"; +import {setToken, UserProvider} from "../../Contexts/UserContext"; +import {TEST_AUTH_OBJECT} from "../../../utils/testing"; +import addSnapshotSerializer from "../../../utils/snapshot"; +import FleetsTab from "./FleetsTab"; + +const renderFleetsTab = async () => { + const {container} = render( + + + + + + + + + + ); + await waitFor(() => { + /* render */ + }); + return container; +}; + +describe("FleetsTab", () => { + beforeAll(() => { + addSnapshotSerializer(expect); + }); + + it("Render", async () => { + setToken(TEST_AUTH_OBJECT); + const container = await renderFleetsTab(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/src/components/Cars/Status/__snapshots__/FleetsTab.test.jsx.snap b/src/components/Cars/Status/__snapshots__/FleetsTab.test.jsx.snap new file mode 100644 index 0000000..4353af6 --- /dev/null +++ b/src/components/Cars/Status/__snapshots__/FleetsTab.test.jsx.snap @@ -0,0 +1,280 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FleetsTab Render 1`] = ` +
+
+
+
+
+
+
+ Fleets +
+
+
+
+ +
+ +
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + CAN ID + + +
+ + fleet1 + +
+ + fleet2 + +
+
+
+
+
+
+
+`; diff --git a/src/components/Cars/Status/__snapshots__/index.test.jsx.snap b/src/components/Cars/Status/__snapshots__/index.test.jsx.snap index 02f5063..87bafee 100644 --- a/src/components/Cars/Status/__snapshots__/index.test.jsx.snap +++ b/src/components/Cars/Status/__snapshots__/index.test.jsx.snap @@ -137,6 +137,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 0943eb7..bed8b79 100644 --- a/src/components/Cars/Status/index.jsx +++ b/src/components/Cars/Status/index.jsx @@ -13,6 +13,7 @@ import { useStatusContext } from "../../Contexts/StatusContext"; import useStyles from "../../useStyles"; import CANSignalsTab from "./CANSignalsTab"; import RemoteCommandsTab from "./RemoteCommandsTab"; +import FleetsTab from "./FleetsTab"; const tabHashes = ["details", "updates", "filters"]; @@ -66,6 +67,8 @@ const CarStatus = () => { + + @@ -92,6 +95,10 @@ const CarStatus = () => { + + + + ); }; diff --git a/src/components/Contexts/CarUpdatesContext.jsx b/src/components/Contexts/CarUpdatesContext.jsx index a16bed2..070e49d 100644 --- a/src/components/Contexts/CarUpdatesContext.jsx +++ b/src/components/Contexts/CarUpdatesContext.jsx @@ -36,6 +36,21 @@ export const CarUpdatesProvider = ({ children }) => { const [delayCount, setDelayCount] = useState(0); let progressTimer = 0; + const cancelUpdate = async (id, token) => { + let result; + + try { + setBusy(true); + result = await api.cancelCarUpdate(id, token); + if (result.error) + throw new Error(`Cancel car update error. ${result.message}`); + } finally { + setBusy(false); + } + + return result; + }; + const deployCarUpdates = async (data, token) => { let result; @@ -228,6 +243,7 @@ export const CarUpdatesProvider = ({ children }) => { busy, carUpdates, totalCarUpdates, + cancelUpdate, deployCarUpdates, deployFleetUpdates, getCarUpdates, diff --git a/src/components/Contexts/VehicleContext.jsx b/src/components/Contexts/VehicleContext.jsx index 657dbec..3896f9d 100644 --- a/src/components/Contexts/VehicleContext.jsx +++ b/src/components/Contexts/VehicleContext.jsx @@ -31,6 +31,8 @@ export const VehicleProvider = ({ children }) => { const [vehicle, setVehicle] = useState({}); const [vehicles, setVehicles] = useState([]); const [totalVehicles, setTotalVehicles] = useState(0); + const [fleets, setFleets] = useState([]); + const [totalFleets, setTotalFleets] = useState(0); const [models, setModels] = useState([]); const [years, setYears] = useState([]); @@ -220,6 +222,25 @@ export const VehicleProvider = ({ children }) => { } }; + const getFleets = async (vin, search, token) => { + try { + setBusy(true); + validateVIN(vin); + + const result = await api.getFleets(vin, search, token); + if (result.error) { + setFleets([]); + throw new Error(`Get Fleets of vehicle`) + } + setFleets(result.data ?? []); + if (result.total) { + setTotalFleets(result.total); + } + } finally { + setBusy(false) + } + } + return ( { vehicle, vehicles, years, + fleets, + totalFleets, addVehicle, deleteVehicle, getConnections, @@ -242,6 +265,7 @@ export const VehicleProvider = ({ children }) => { getVehicles, sendCommand, updateVehicle, + getFleets, }} > {children} diff --git a/src/components/Contexts/VehicleContext.test.jsx b/src/components/Contexts/VehicleContext.test.jsx index cda9d11..6478414 100644 --- a/src/components/Contexts/VehicleContext.test.jsx +++ b/src/components/Contexts/VehicleContext.test.jsx @@ -20,12 +20,57 @@ const checkVehiclesResult = (error, busy, vehicles) => { expect(screen.getByTestId("vehicles").innerHTML).toEqual(vehicles); }; +const checkFleetsResult = (error, busy, fleets) => { + checkBaseResults(error, busy); + expect(screen.getByTestId("fleets").innerHTML).toEqual(fleets); +} + const checkBaseResults = (error, busy) => { expect(screen.getByTestId("error").innerHTML).toEqual(error); expect(screen.getByTestId("busy").innerHTML).toEqual(busy); }; describe("VehicleContext", () => { + describe("getFleets", () => { + beforeEach(() => { + const TestComp = () => { + const { busy, error, fleets, getFleets } = useVehicleContext(); + + return ( + <> +
{error}
+
{busy.toString()}
+
{JSON.stringify(fleets)}
+