diff --git a/src/components/CANFilter/Table/index.jsx b/src/components/CANFilter/Table/index.jsx index 547dadc..587ba26 100644 --- a/src/components/CANFilter/Table/index.jsx +++ b/src/components/CANFilter/Table/index.jsx @@ -121,7 +121,7 @@ const MainForm = ({ vin }) => { } if (hasRole([Roles.DELETE], groups)) { actions.push({ - tip: `Delete ""${row.can_id}""`, + tip: `Delete "${row.can_id}"`, id: row.can_id, icon: }) diff --git a/src/components/Cars/Status/CANFiltersTab.jsx b/src/components/Cars/Status/CANFiltersTab.jsx index e92e028..81aa82e 100644 --- a/src/components/Cars/Status/CANFiltersTab.jsx +++ b/src/components/Cars/Status/CANFiltersTab.jsx @@ -7,15 +7,15 @@ import CANFiltersTable from "../../CANFilter/Table"; import useStyles from "../../useStyles"; const CANFiltersTab = () => { - const { vin } = useParams(); - const classes = useStyles(); + const { vin } = useParams(); + const classes = useStyles(); - return ( -
- CAN Filters - -
- ); + return ( +
+ CAN Filters + +
+ ); }; export default CANFiltersTab; diff --git a/src/components/Contexts/FleetContext.jsx b/src/components/Contexts/FleetContext.jsx index b36deb4..e2cea30 100644 --- a/src/components/Contexts/FleetContext.jsx +++ b/src/components/Contexts/FleetContext.jsx @@ -5,9 +5,13 @@ const FleetContext = React.createContext(); export const FleetProvider = ({ children }) => { const [busy, setBusy] = useState(false); + const [fleets, setFleets] = useState([]); const [totalFleets, setTotalFleets] = useState(0); + const [fleetVehicles, setFleetVehicles] = useState([]); + const [totalFleetVehicles, setTotalFleetVehicles] = useState(0); + const addFleet = async (fleet, token) => { try { setBusy(true); @@ -31,7 +35,6 @@ export const FleetProvider = ({ children }) => { setFleets([]) throw new Error(`Get fleets error. ${result.message}`); } - setFleets(result.data) if (result.total) { setTotalFleets(result.total); @@ -78,16 +81,80 @@ export const FleetProvider = ({ children }) => { } } + const getFleetVehicles = async (name, search, token) => { + try { + setBusy(true); + + const result = await api.getFleetVehicles(name, search, token); + if (result.error) { + setFleetVehicles([]) + throw new Error(`Get fleet vehicles error. ${result.message}`); + } + + setFleetVehicles(result.data) + if (result.total) { + setTotalFleetVehicles(result.total); + } + return result; + } finally { + setBusy(false); + } + } + + const addFleetVehicle = async (name, vehicle, token) => { + try { + setBusy(true); + + validateFleetName(name); + validateVIN(vehicle.vin); + + const result = await api.addFleetVehicle(name, vehicle, token); + if (result.error) { + throw new Error(`Add fleet vehicle error. ${result.message}`); + } + return result; + } finally { + setBusy(false); + } + } + + const deleteFleetVehicle = async (name, vehicle, token) => { + try { + setBusy(true); + + validateFleetName(name); + validateVIN(vehicle.vin); + + const result = await api.deleteFleetVehicle(name, vehicle, token); + if (result.error) { + throw new Error(`Delete fleet vehicle error. ${result.message}`); + } + + const index = fleetVehicles.findIndex(element => element === vehicle.vin); + if (index >= 0) fleetVehicles.splice(index, 1); + return result; + } finally { + setBusy(false); + } + } + return ( {children} @@ -104,9 +171,15 @@ const validateFleet = (fleet) => { } const validateFleetName = (name) => { - if (name == null || !/^[A-Za-z-]+$/.test(name)) { + if (name == null || !/^[0-9A-Za-z-]+$/.test(name)) { throw new Error("Invalid name"); } } +const validateVIN = (vin) => { + if (vin == null || vin.length !== 17) { + throw new Error("Invalid VIN"); + } +} + export const useFleetContext = () => useContext(FleetContext); diff --git a/src/components/Contexts/FleetContext.test.jsx b/src/components/Contexts/FleetContext.test.jsx index 9a032d8..6276545 100644 --- a/src/components/Contexts/FleetContext.test.jsx +++ b/src/components/Contexts/FleetContext.test.jsx @@ -15,6 +15,11 @@ const checkFleetResults = (error, busy, fleets) => { expect(screen.getByTestId("fleets").innerHTML).toEqual(fleets); }; +const checkFleetVehicleResults = (error, busy, vehicles) => { + checkBaseResults(error, busy); + expect(screen.getByTestId("fleet-vehicles").innerHTML).toEqual(vehicles); +} + const checkBaseResults = (error, busy) => { expect(screen.getByTestId("error").innerHTML).toEqual(error); expect(screen.getByTestId("busy").innerHTML).toEqual(busy); @@ -271,10 +276,208 @@ describe("FleetContext", () => { checkBaseResults("", "false"); }); }); + + describe("getFleetVehicles", () => { + beforeEach(() => { + const TestComp = () => { + const { busy, error, fleetVehicles, getFleetVehicles } = useFleetContext(); + + return ( + <> +
{error}
+
{busy.toString()}
+
{JSON.stringify(fleetVehicles)}
+ + + + + + f + + +`; diff --git a/src/components/Fleets/Status/Vehicles/Add/index.jsx b/src/components/Fleets/Status/Vehicles/Add/index.jsx new file mode 100644 index 0000000..debf7b6 --- /dev/null +++ b/src/components/Fleets/Status/Vehicles/Add/index.jsx @@ -0,0 +1,103 @@ +import React, { useEffect, useRef, useState } from "react"; +import { Redirect, useParams } from "react-router"; +import { Button, TextField } from "@material-ui/core"; + +import useStyles from "../../../../useStyles"; +import { + useFleetContext, + FleetProvider +} from "../../../../Contexts/FleetContext"; +import { useStatusContext } from "../../../../Contexts/StatusContext"; +import { useUserContext } from "../../../../Contexts/UserContext"; +import { logger } from "../../../../../services/monitoring"; + +const MainForm = () => { + const { name } = useParams(); + const { setMessage, setTitle, setSitePath } = useStatusContext(); + const { addFleetVehicle, busy } = useFleetContext(); + const { + token: { + idToken: { jwtToken: token }, + }, + } = useUserContext(); + const classes = useStyles(); + const vinEl = useRef(null); + const [redirect, setRedirect] = useState(null); + + useEffect(() => { + const title = "Add Vehicle"; + setTitle(title); + setSitePath([ + { + label: "Fleets", + link: "/fleets", + }, + { + label: `${name}`, + link: `/fleet/${name}` + }, + { + label: title + } + ]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const onSubmit = async (event) => { + try { + event.preventDefault(); + + const formData = { vin: vinEl.current.value }; + const result = await addFleetVehicle(name, formData, token); + + setMessage(`Added ${result.vin}`); + setRedirect(`/fleet/${name}#vehicles`); + } catch (e) { + setMessage(e.message); + logger.warn(e.stack); + } + }; + + if (redirect && redirect.length > 0) { + return ; + } + + return ( +
+
+ + + +
+ ); +}; + +const FleetAddVehicleForm = () => ( + + + +); + +export default FleetAddVehicleForm; diff --git a/src/components/Fleets/Status/Vehicles/Add/index.test.jsx b/src/components/Fleets/Status/Vehicles/Add/index.test.jsx new file mode 100644 index 0000000..31afb5b --- /dev/null +++ b/src/components/Fleets/Status/Vehicles/Add/index.test.jsx @@ -0,0 +1,36 @@ +jest.mock("../../../../Contexts/CANFiltersContext"); +jest.mock("../../../../Contexts/StatusContext"); +jest.mock("../../../../Contexts/UserContext"); + +import { render, waitFor } from "@testing-library/react"; +import { BrowserRouter } from "react-router-dom"; + +import { CANFiltersProvider } from "../../../../Contexts/CANFiltersContext"; +import { StatusProvider } from "../../../../Contexts/StatusContext"; +import { UserProvider, setToken } from "../../../../Contexts/UserContext"; +import { TEST_AUTH_OBJECT } from "../../../../../utils/testing"; +import MainForm from "./index" + +const renderCANFiltersAdd = async () => { + const { container } = render( + + + + + + + + f + + ); + await waitFor(() => { }); + return container; +}; + +describe("FleetVehicleAdd", () => { + it("Render", async () => { + setToken(TEST_AUTH_OBJECT); + const container = await renderCANFiltersAdd(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/src/components/Fleets/Status/Vehicles/Table/__snapshots__/index.test.jsx.snap b/src/components/Fleets/Status/Vehicles/Table/__snapshots__/index.test.jsx.snap new file mode 100644 index 0000000..71732df --- /dev/null +++ b/src/components/Fleets/Status/Vehicles/Table/__snapshots__/index.test.jsx.snap @@ -0,0 +1,326 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FleetVehiclesTable Render 1`] = ` +
+
+
+
+
+
+
+ +
+
+ +
+ +
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + VIN + + + + Actions +
+ + USWESTVIN12345678 + + + No actions +
+ + USWESTVIN12345679 + + + No actions +
+ + USWESTVIN12345670 + + + No actions +
+
+
+
+
+
+
+`; diff --git a/src/components/Fleets/Status/Vehicles/Table/index.jsx b/src/components/Fleets/Status/Vehicles/Table/index.jsx new file mode 100644 index 0000000..0fdfc23 --- /dev/null +++ b/src/components/Fleets/Status/Vehicles/Table/index.jsx @@ -0,0 +1,194 @@ +import React, { useEffect, useState } from "react"; +import { Link } from 'react-router-dom'; +import { + Grid, + Table, + TableBody, + TableCell, + TableFooter, + TablePagination, + TableRow, + Tooltip, +} from "@material-ui/core"; +import AddCircleIcon from "@material-ui/icons/AddCircle"; +import DeleteIcon from "@material-ui/icons/Delete"; +import clsx from "clsx"; + +import TableHeaderSortable from "../../../../Table/HeaderSortable"; +import { useUserContext } from "../../../../Contexts/UserContext" +import { useStatusContext } from "../../../../Contexts/StatusContext"; +import { FleetProvider, useFleetContext } from "../../../../Contexts/FleetContext" +import useStyles from "../../../../useStyles"; +import SearchField from "../../../../Controls/SearchField"; +import { logger } from "../../../../../services/monitoring"; +import { Roles, hasRole } from "../../../../../utils/roles"; + +const tableColumns = [ + { + id: "vin", + label: "VIN" + }, + { + id: "", + label: "Actions" + } +]; + +const MainForm = ({ name }) => { + const [pageSize, setPageSize] = useState(10); + const [pageIndex, setPageIndex] = useState(0); + const [orderBy, setOrderBy] = useState("id"); + const [order, setOrder] = useState("desc"); + const classes = useStyles(); + const { setMessage } = useStatusContext(); + const { fleetVehicles, totalFleetVehicles, getFleetVehicles, deleteFleetVehicle } = useFleetContext(); + const { token: { idToken: { jwtToken: token } }, groups } = useUserContext(); + + useEffect(() => { + (async () => { + try { + if (!token) return; + await getFleetVehicles( + name, + { + 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 + }, [token, pageIndex, pageSize, orderBy, order]); + + 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); + } + }; + + const onDelete = async (vin) => { + try { + await deleteFleetVehicle(name, { vin: vin }, token); + setMessage(`Deleted ${vin}`) + } catch (e) { + setMessage(e.message); + logger.warn(e.stack); + } + }; + + const Actions = (vin) => { + let actions = []; + if (hasRole([Roles.DELETE], groups)) { + actions.push({ + tip: `Delete "${vin}"`, + id: vin, + icon: + }) + } + if (actions.length === 0) return ["No actions"]; + + return actions.map((action) => { + if (action.link != null) { + return ( + + + {action.icon} + + + ); + } else { + return ( + + onDelete(action.id)}> + {action.icon} + + + ); + } + }); + }; + + return ( +
+ + + + + + + + + + + + + + {fleetVehicles.map(vin => ( + + + {vin} + + {Actions(vin)} + + ))} + + + + + + +
+
+ ); +}; + +const FleetVehiclesTable = (props) => ( + + + +); + +export default FleetVehiclesTable; diff --git a/src/components/Fleets/Status/Vehicles/Table/index.test.jsx b/src/components/Fleets/Status/Vehicles/Table/index.test.jsx new file mode 100644 index 0000000..6559abd --- /dev/null +++ b/src/components/Fleets/Status/Vehicles/Table/index.test.jsx @@ -0,0 +1,39 @@ +jest.mock("../../../../Contexts/FleetContext"); +jest.mock("../../../../Contexts/StatusContext"); +jest.mock("../../../../Contexts/UserContext"); +jest.mock('@material-ui/core/utils/unstable_useId', () => + jest.fn().mockReturnValue('mui-test-id'), +); + +import { render, waitFor } from "@testing-library/react"; +import { BrowserRouter } from "react-router-dom"; + +import { FleetProvider } from "../../../../Contexts/FleetContext"; +import { StatusProvider } from "../../../../Contexts/StatusContext"; +import { UserProvider, setToken } from "../../../../Contexts/UserContext"; +import { TEST_AUTH_OBJECT } from "../../../../../utils/testing"; +import MainForm from "./index" + +const renderFleetTable = async () => { + const { container } = render( + + + + + + + + + + ); + await waitFor(() => { }); + return container; +}; + +describe("FleetVehiclesTable", () => { + it("Render", async () => { + setToken(TEST_AUTH_OBJECT); + const container = await renderFleetTable(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/src/components/Fleets/Status/VehiclesTab.jsx b/src/components/Fleets/Status/VehiclesTab.jsx new file mode 100644 index 0000000..ca4bf28 --- /dev/null +++ b/src/components/Fleets/Status/VehiclesTab.jsx @@ -0,0 +1,21 @@ +import React from "react"; +import { useParams } from "react-router"; +import clsx from "clsx"; +import { Typography } from "@material-ui/core"; + +import FleetVehiclesTable from "./Vehicles/Table"; +import useStyles from "../../useStyles"; + +const FleetVehiclesTab = () => { + const { name } = useParams(); + const classes = useStyles(); + + return ( +
+ Vehicles + +
+ ); +}; + +export default FleetVehiclesTab; diff --git a/src/components/Fleets/Status/VehiclesTab.test.jsx b/src/components/Fleets/Status/VehiclesTab.test.jsx new file mode 100644 index 0000000..5284c39 --- /dev/null +++ b/src/components/Fleets/Status/VehiclesTab.test.jsx @@ -0,0 +1,31 @@ +jest.mock("../../Contexts/FleetContext"); +jest.mock("../../Contexts/StatusContext"); +jest.mock("../../Contexts/UserContext"); +jest.mock('@material-ui/core/utils/unstable_useId', () => + jest.fn().mockReturnValue('mui-test-id'), +); + +import { render, waitFor } from "@testing-library/react"; +import { BrowserRouter } from "react-router-dom"; + +import { setToken } from "../../Contexts/UserContext"; +import { TEST_AUTH_OBJECT } from "../../../utils/testing"; +import VehiclesTab from "./VehiclesTab" + +const renderVehiclesTab = async () => { + const { container } = render( + + + + ); + await waitFor(() => { }); + return container; +}; + +describe("VehiclesTab", () => { + it("Render", async () => { + setToken(TEST_AUTH_OBJECT); + const container = await renderVehiclesTab(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/src/components/Fleets/Status/__snapshots__/VehiclesTab.test.jsx.snap b/src/components/Fleets/Status/__snapshots__/VehiclesTab.test.jsx.snap new file mode 100644 index 0000000..ecd8f29 --- /dev/null +++ b/src/components/Fleets/Status/__snapshots__/VehiclesTab.test.jsx.snap @@ -0,0 +1,323 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`VehiclesTab Render 1`] = ` +
+
+
+ Vehicles +
+
+
+
+ +
+
+ +
+ +
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + VIN + + + + Actions +
+ + USWESTVIN12345678 + + + No actions +
+ + USWESTVIN12345679 + + + No actions +
+ + USWESTVIN12345670 + + + No actions +
+
+
+
+
+`; diff --git a/src/components/Fleets/Status/__snapshots__/index.test.jsx.snap b/src/components/Fleets/Status/__snapshots__/index.test.jsx.snap new file mode 100644 index 0000000..d5c1309 --- /dev/null +++ b/src/components/Fleets/Status/__snapshots__/index.test.jsx.snap @@ -0,0 +1,414 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FleetStatus Render 1`] = ` +
+
+
+
+
+
+
+
+
+ + +
+ +
+
+
+
+
+
+
+ Vehicles +
+
+
+
+ +
+
+ +
+ +
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + VIN + + + + Actions +
+ + USWESTVIN12345678 + + + No actions +
+ + USWESTVIN12345679 + + + No actions +
+ + USWESTVIN12345670 + + + No actions +
+
+
+
+
+
+ +
+
+
+
+`; diff --git a/src/components/Fleets/Status/index.jsx b/src/components/Fleets/Status/index.jsx new file mode 100644 index 0000000..7d70f3c --- /dev/null +++ b/src/components/Fleets/Status/index.jsx @@ -0,0 +1,76 @@ +import React, { useEffect } from "react"; +import { useParams } from "react-router"; +import { useLocation } from "react-router-dom"; +import clsx from "clsx"; +import { Box, Tab, Tabs } from "@material-ui/core"; + +import FleetVehiclesTab from "./VehiclesTab"; +import TabPanel from "../../Controls/TabPanel"; +import { useStatusContext } from "../../Contexts/StatusContext"; +import useStyles from "../../useStyles"; + +const tabHashes = [ + "updates", + "filters" +] + +const FleetStatus = () => { + const { name } = useParams(); + const classes = useStyles(); + const { setTitle, setSitePath } = useStatusContext(); + const { hash } = useLocation(); + const [tabIndex, setTabIndex] = React.useState(0); + + useEffect(() => { + const key = hash.replace("#", "") + const index = tabHashes.findIndex(element => element === key); + if (index >= 0) setTabIndex(index); + }, [hash]); + + useEffect(() => { + const title = `Fleet ${name} Details`; + setTitle(title); + setSitePath([ + { + label: "Fleets", + link: "/fleets", + }, + { + label: title, + }, + ]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [name]); + + const handleTabChange = (event, newIndex) => { + setTabIndex(newIndex); + }; + + return ( +
+ + + + + + + + + + + + + {/* */} + +
+ ); +}; + +function tabProps(index) { + return { + id: `tab-${index}`, + "aria-controls": `tabpanel-${index}` + }; +} + +export default FleetStatus; diff --git a/src/components/Fleets/Status/index.test.jsx b/src/components/Fleets/Status/index.test.jsx new file mode 100644 index 0000000..4c5f52f --- /dev/null +++ b/src/components/Fleets/Status/index.test.jsx @@ -0,0 +1,39 @@ +jest.mock("../../Contexts/FleetContext"); +jest.mock("../../Contexts/StatusContext"); +jest.mock("../../Contexts/UserContext"); +jest.mock('@material-ui/core/utils/unstable_useId', () => + jest.fn().mockReturnValue('mui-test-id'), +); + +import { render, waitFor } from "@testing-library/react"; +import { BrowserRouter } from "react-router-dom"; + +import { FleetProvider } from "../../Contexts/FleetContext"; +import { StatusProvider } from "../../Contexts/StatusContext"; +import { UserProvider, setToken } from "../../Contexts/UserContext"; +import { TEST_AUTH_OBJECT } from "../../../utils/testing"; +import FleetStatus from "./index" + +const renderCarStatus = async () => { + const { container } = render( + + + + + + + + + + ); + await waitFor(() => { }); + return container; +}; + +describe("FleetStatus", () => { + it("Render", async () => { + setToken(TEST_AUTH_OBJECT); + const container = await renderCarStatus(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/src/components/Fleets/Table/__snapshots__/index.test.jsx.snap b/src/components/Fleets/Table/__snapshots__/index.test.jsx.snap index e7a7815..425c94c 100644 --- a/src/components/Fleets/Table/__snapshots__/index.test.jsx.snap +++ b/src/components/Fleets/Table/__snapshots__/index.test.jsx.snap @@ -240,7 +240,7 @@ exports[`FleetTable Render 1`] = ` class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignCenter" > US-WEST @@ -258,7 +258,7 @@ exports[`FleetTable Render 1`] = ` - 0 + 3 US-CENTRAL @@ -313,7 +313,7 @@ exports[`FleetTable Render 1`] = ` - 0 + 3 US-EAST @@ -368,7 +368,7 @@ exports[`FleetTable Render 1`] = ` - 0 + 3 { const [orderBy, setOrderBy] = useState("id"); const [order, setOrder] = useState("desc"); const classes = useStyles(); - const { setMessage, setTitle } = useStatusContext(); + const { setMessage, setSitePath, setTitle } = useStatusContext(); const { fleets, totalFleets, getFleets, deleteFleet } = useFleetContext(); const { token: { idToken: { jwtToken: token } }, groups } = useUserContext(); useEffect(() => { setTitle("Fleets"); + setSitePath([]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { (async () => { try { if (!token) return; @@ -131,7 +136,7 @@ const MainForm = ({ vin }) => { } if (hasRole([Roles.DELETE], groups)) { actions.push({ - tip: `Delete ""${row.name}""`, + tip: `Delete "${row.name}"`, id: row.name, icon: }) @@ -184,7 +189,7 @@ const MainForm = ({ vin }) => { {fleets.map((row) => ( - {row.name} + {row.name} {row.canbus.enabled ? "true" : "false"} {row.log_level} diff --git a/src/components/Routes/SiteRoutes.jsx b/src/components/Routes/SiteRoutes.jsx index 0880085..4aabca3 100644 --- a/src/components/Routes/SiteRoutes.jsx +++ b/src/components/Routes/SiteRoutes.jsx @@ -14,8 +14,10 @@ const CarStatus = React.lazy(() => import("../Cars/Status")); const CarUpdateStatus = React.lazy(() => import("../Cars/UpdateStatus")); const Datascope = React.lazy(() => import("../Datascope/Home")); const FleetsList = React.lazy(() => import("../Fleets/Table")); +const FleetStatus = React.lazy(() => import("../Fleets/Status")); const FleetAddForm = React.lazy(() => import("../Fleets/Add")); const FleetUpdateForm = React.lazy(() => import("../Fleets/Update")) +const FleetAddVehicleForm = React.lazy(() => import("../Fleets/Status/Vehicles/Add")) const Home = React.lazy(() => import("../Home")); const Manifests = React.lazy(() => import("../Manifest/List")); const ManifestDeploy = React.lazy(() => import("../Manifest/Deploy")); @@ -78,6 +80,22 @@ const SiteRoutes = () => { groups={groups} roles={[Roles.READ, Roles.CREATE]} /> + } + type={TYPES.PROTECTED} + token={token} + groups={groups} + roles={[Roles.READ, Roles.CREATE]} + /> + } + type={TYPES.PROTECTED} + token={token} + groups={groups} + roles={[Roles.READ, Roles.CREATE]} + /> } diff --git a/src/services/__mocks__/fleetsAPI.js b/src/services/__mocks__/fleetsAPI.js index 9f4376b..2ca7cf0 100644 --- a/src/services/__mocks__/fleetsAPI.js +++ b/src/services/__mocks__/fleetsAPI.js @@ -1,27 +1,57 @@ -const data = [ - { name: "US-WEST", log_level: "info", canbus: { enabled: true } }, - { name: "US-CENTRAL", log_level: "warn", canbus: { enabled: false } }, - { name: "US-EAST", log_level: "error", canbus: { enabled: true } }, +const fleets = [ + { + name: "US-WEST", + log_level: "info", + canbus: { enabled: true }, + vehicles: ["USWESTVIN12345678", "USWESTVIN12345679", "USWESTVIN12345670"] + }, + { + name: "US-CENTRAL", + log_level: "warn", + canbus: { enabled: false }, + vehicles: ["USCENTVIN12345678", "USCENTVIN12345679", "USCENTVIN12345670"] + }, + { + name: "US-EAST", + log_level: "error", + canbus: { enabled: true }, + vehicles: ["USEASTVIN12345678", "USEASTVIN12345679", "USEASTVIN12345670"] + }, ]; +const vehicles = ["USWESTVIN12345678", "USWESTVIN12345679", "USWESTVIN12345670"]; + const fleetsAPI = { addFleet: async (fleet, token) => { - data.push(fleet); + fleets.push(fleet); return fleet; }, getFleets: async (search, token) => { - return { data }; + return {data: fleets}; }, updateFleet: async (name, fleet, token) => { - const index = data.findIndex(element => element.name === name); - if (index >= 0) data[index] = fleet; + const index = fleets.findIndex(element => element.name === name); + if (index >= 0) fleets[index] = fleet; return fleet; }, deleteFleet: async (name, token) => { - const index = data.findIndex(element => element.name === name); - if (index >= 0) data.splice(index, 1); + const index = fleets.findIndex(element => element.name === name); + if (index >= 0) fleets.splice(index, 1); return name; }, + + getFleetVehicles: async (name, search, token) => { + return {data: vehicles}; + }, + addFleetVehicle: async (name, vehicle, token) => { + vehicles.push(vehicle.vin); + return vehicle; + }, + deleteFleetVehicle: async (name, vehicle, token) => { + const index = vehicles.findIndex(element => element === vehicle.vin); + if (index >= 0) vehicles.splice(index, 1); + return vehicle; + } }; export default fleetsAPI; diff --git a/src/services/fleetsAPI.js b/src/services/fleetsAPI.js index 36e58c6..60100fa 100644 --- a/src/services/fleetsAPI.js +++ b/src/services/fleetsAPI.js @@ -44,6 +44,34 @@ const fleetsAPI = { getAuthHeaderOptions(token) ) }).then(fetchRespHandler), + + getFleetVehicles: async (name, search, token) => + fetch(addQueryParams(`${API_ENDPOINT}/fleet/${name}/vehicles`, search), { + method: "GET", + headers: Object.assign( + { "Content-Type": "application/json" }, + getAuthHeaderOptions(token) + ) + }).then(fetchRespHandler), + + addFleetVehicle: async (name, vehicle, token) => + fetch(`${API_ENDPOINT}/fleet/${name}/vehicle`, { + method: "POST", + headers: Object.assign( + { "Content-Type": "application/json" }, + getAuthHeaderOptions(token) + ), + body: JSON.stringify(vehicle) + }).then(fetchRespHandler), + + deleteFleetVehicle: async (name, vehicle, token) => + fetch(`${API_ENDPOINT}/fleet/${name}/vehicle/${vehicle.vin}`, { + method: "DELETE", + headers: Object.assign( + { "Content-Type": "application/json" }, + getAuthHeaderOptions(token) + ) + }).then(fetchRespHandler), }; export default fleetsAPI;