diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 9756fcf..b2be036 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -62,7 +62,7 @@ jobs: uses: rtCamp/action-slack-notify@v2 env: MSG_MINIMAL: true - SLACK_MESSAGE: "Deploying to ${{ env.ENVIRONMENT }}... :partydeploy:" + SLACK_MESSAGE: "Deploying to ${{ env.ENVIRONMENT }}... :partydeploy:" - name: Deploy id: deploy env: @@ -74,7 +74,7 @@ jobs: --set image.name=$PROJECT \ --set image.tag=$TAG-$ENVIRONMENT \ --wait -i -f k8s/values-$ENVIRONMENT.yaml $PROJECT k8s/ - + - name: Notify if success if: ${{ success() }} uses: rtCamp/action-slack-notify@v2 @@ -87,4 +87,4 @@ jobs: uses: rtCamp/action-slack-notify@v2 env: SLACK_COLOR: ${{ job.status }} - SLACK_MESSAGE: "Something failed! :this-is-fine:" \ No newline at end of file + SLACK_MESSAGE: "Something failed! :this-is-fine:" diff --git a/src/components/App/__snapshots__/App.test.js.snap b/src/components/App/__snapshots__/App.test.js.snap index c7b6919..d890adc 100644 --- a/src/components/App/__snapshots__/App.test.js.snap +++ b/src/components/App/__snapshots__/App.test.js.snap @@ -155,7 +155,7 @@ exports[`App Route / authenticated 1`] = ` viewBox="0 0 24 24" > @@ -173,6 +173,42 @@ exports[`App Route / authenticated 1`] = ` /> +
  • + +
    + +
    +
    + + Fleets + +
    + +
    +
  • @@ -621,6 +657,42 @@ exports[`App Route /datascope authenticated 1`] = ` />
  • +
  • + +
    + +
    +
    + + Fleets + +
    + +
    +
  • @@ -1087,6 +1159,42 @@ exports[`App Route /datascope/battery authenticated 1`] = ` />
  • +
  • + +
    + +
    +
    + + Fleets + +
    + +
    +
  • @@ -1672,6 +1780,42 @@ exports[`App Route /home authenticated 1`] = ` />
  • +
  • + +
    + +
    +
    + + Fleets + +
    + +
    +
  • @@ -2156,6 +2300,42 @@ exports[`App Route /package-create authenticated 1`] = ` />
  • +
  • + +
    + +
    +
    + + Fleets + +
    + +
    +
  • @@ -3595,6 +3775,42 @@ exports[`App Route /package-deploy authenticated 1`] = ` />
  • +
  • + +
    + +
    +
    + + Fleets + +
    + +
    +
  • @@ -4417,6 +4633,42 @@ exports[`App Route /package-status authenticated 1`] = ` />
  • +
  • + +
    + +
    +
    + + Fleets + +
    + +
    +
  • @@ -4975,6 +5227,42 @@ exports[`App Route /packages authenticated 1`] = ` />
  • +
  • + +
    + +
    +
    + + Fleets + +
    + +
    +
  • @@ -5734,6 +6022,42 @@ exports[`App Route /page-not-found authenticated 1`] = ` />
  • +
  • + +
    + +
    +
    + + Fleets + +
    + +
    +
  • @@ -6105,6 +6429,42 @@ exports[`App Route /vehicle-add authenticated 1`] = ` />
  • +
  • + +
    + +
    +
    + + Fleets + +
    + +
    +
  • @@ -6691,6 +7051,42 @@ exports[`App Route /vehicle-status authenticated 1`] = ` />
  • +
  • + +
    + +
    +
    + + Fleets + +
    + +
    +
  • @@ -7863,6 +8259,42 @@ exports[`App Route /vehicles authenticated 1`] = ` />
  • +
  • + +
    + +
    +
    + + Fleets + +
    + +
    +
  • { +const renderCANFiltersAdd = async () => { const { container } = render( @@ -27,10 +27,10 @@ const renderCANFiltersCreate = async () => { return container; }; -describe("CANFiltersCreate", () => { +describe("CANFiltersAdd", () => { it("Render", async () => { setToken(TEST_AUTH_OBJECT); - const container = await renderCANFiltersCreate(); + const container = await renderCANFiltersAdd(); expect(container).toMatchSnapshot(); }); }); diff --git a/src/components/CANFilter/Table/__snapshots__/index.test.jsx.snap b/src/components/CANFilter/Table/__snapshots__/index.test.jsx.snap index 3b0f4d2..e7ff406 100644 --- a/src/components/CANFilter/Table/__snapshots__/index.test.jsx.snap +++ b/src/components/CANFilter/Table/__snapshots__/index.test.jsx.snap @@ -25,7 +25,7 @@ exports[`CANFiltersTable Render 1`] = ` >
    - + diff --git a/src/components/Cars/Add/index.jsx b/src/components/Cars/Add/index.jsx index 3e3ef55..05c086b 100644 --- a/src/components/Cars/Add/index.jsx +++ b/src/components/Cars/Add/index.jsx @@ -1,4 +1,6 @@ -import React, { useEffect, useRef } from "react"; +import React, { useEffect, useRef, useState } from "react"; +import { Redirect } from "react-router"; +import { Button, TextField } from "@material-ui/core"; import useStyles from "../../useStyles"; import { @@ -7,7 +9,6 @@ import { } from "../../Contexts/VehicleContext"; import { useStatusContext } from "../../Contexts/StatusContext"; import { useUserContext } from "../../Contexts/UserContext"; -import { Button, TextField } from "@material-ui/core"; import { logger } from "../../../services/monitoring"; const MainForm = () => { @@ -23,6 +24,7 @@ const MainForm = () => { const modelEl = useRef(null); const yearEl = useRef(null); const trimEl = useRef(null); + const [redirect, setRedirect] = useState(null); useEffect(() => { setTitle("Add Vehicle"); @@ -37,6 +39,7 @@ const MainForm = () => { ]); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + const onSubmit = async (event) => { try { event.preventDefault(); @@ -51,13 +54,17 @@ const MainForm = () => { const result = await addVehicle(formData, token); setMessage(`Added ${result.vin}`); - vinEl.current.value = ""; + setRedirect(`/vehicles`); } catch (e) { setMessage(e.message); logger.warn(e.stack); } }; + if (redirect && redirect.length > 0) { + return ; + } + return (
    diff --git a/src/components/Cars/Status/__snapshots__/CANFiltersTab.test.jsx.snap b/src/components/Cars/Status/__snapshots__/CANFiltersTab.test.jsx.snap index 8b543b0..ab0d032 100644 --- a/src/components/Cars/Status/__snapshots__/CANFiltersTab.test.jsx.snap +++ b/src/components/Cars/Status/__snapshots__/CANFiltersTab.test.jsx.snap @@ -24,7 +24,7 @@ exports[`CANFiltersTab Render 1`] = ` >
    {message}
    +
    {busy.toString()}
    + + +
    +
    +
    + + + +`; diff --git a/src/components/Fleets/Add/index.jsx b/src/components/Fleets/Add/index.jsx new file mode 100644 index 0000000..516b742 --- /dev/null +++ b/src/components/Fleets/Add/index.jsx @@ -0,0 +1,132 @@ +import React, { useEffect, useRef, useState } from "react"; +import { Redirect } 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 { setMessage, setTitle, setSitePath } = useStatusContext(); + const { addFleet, busy } = useFleetContext(); + const { + token: { + idToken: { jwtToken: token }, + }, + } = useUserContext(); + const classes = useStyles(); + const nameEl = useRef(null); + const canbusEnabledEl = useRef(null); + const logLevelEl = useRef(null); + const [redirect, setRedirect] = useState(null); + + useEffect(() => { + setTitle("Add Fleet"); + setSitePath([ + { + label: "Fleets", + link: "/fleets", + }, + { + label: "Add Fleet", + }, + ]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const onSubmit = async (event) => { + try { + event.preventDefault(); + + const formData = { + name: nameEl.current.value, + canbus: { enabled: canbusEnabledEl.current.value === "true" }, + log_level: logLevelEl.current.value, + }; + + const result = await addFleet(formData, token); + + setMessage(`Added ${result.name}`); + setRedirect(`/fleets`); + } catch (e) { + setMessage(e.message); + logger.warn(e.stack); + } + }; + + if (redirect && redirect.length > 0) { + return ; + } + + return ( +
    +
    + + + + + +
    + ); +}; + +const FleetAddForm = () => ( + + + +); + +export default FleetAddForm; diff --git a/src/components/Fleets/Add/index.test.jsx b/src/components/Fleets/Add/index.test.jsx new file mode 100644 index 0000000..e5c719a --- /dev/null +++ b/src/components/Fleets/Add/index.test.jsx @@ -0,0 +1,36 @@ +jest.mock("../../Contexts/FleetContext"); +jest.mock("../../Contexts/StatusContext"); +jest.mock("../../Contexts/UserContext"); + +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 renderFleetAdd = async () => { + const { container } = render( + + + + + + + + + + ); + await waitFor(() => { }); + return container; +}; + +describe("FleetAddForm", () => { + it("Render", async () => { + setToken(TEST_AUTH_OBJECT); + const container = await renderFleetAdd(); + 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 new file mode 100644 index 0000000..e7a7815 --- /dev/null +++ b/src/components/Fleets/Table/__snapshots__/index.test.jsx.snap @@ -0,0 +1,532 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FleetTable Render 1`] = ` +
    + +`; diff --git a/src/components/Fleets/Table/index.jsx b/src/components/Fleets/Table/index.jsx new file mode 100644 index 0000000..713cf82 --- /dev/null +++ b/src/components/Fleets/Table/index.jsx @@ -0,0 +1,225 @@ +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 EditIcon from '@material-ui/icons/Edit'; +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: "name", + label: "Name" + }, + { + id: "canbus_enabled", + label: "CANBus Enabled" + }, + { + id: "log_level", + label: "Log Level" + }, + { + id: "num_vehicles", + label: "Vehicles" + }, + { + id: "num_filters", + label: "Filters" + }, + { + id: "", + label: "Actions" + } +]; + +const MainForm = ({ vin }) => { + 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, setTitle } = useStatusContext(); + const { fleets, totalFleets, getFleets, deleteFleet } = useFleetContext(); + const { token: { idToken: { jwtToken: token } }, groups } = useUserContext(); + + useEffect(() => { + setTitle("Fleets"); + (async () => { + try { + if (!token) return; + await getFleets( + { + 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 (name) => { + try { + await deleteFleet(name, token); + setMessage(`Deleted ${name}`) + } catch (e) { + setMessage(e.message); + logger.warn(e.stack); + } + }; + + const Actions = (row) => { + let actions = []; + if (hasRole([Roles.CREATE], groups)) { + actions.push({ + tip: `Update "${row.name}"`, + link: `/fleet-update?name=${row.name}&canbus_enabled=${row.canbus.enabled.toString()}&log_level=${row.log_level}`, + icon: + }); + } + if (hasRole([Roles.DELETE], groups)) { + actions.push({ + tip: `Delete ""${row.name}""`, + id: row.name, + 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 ( +
    + + + + + + + + + + + + + + + {fleets.map((row) => ( + + + {row.name} + + {row.canbus.enabled ? "true" : "false"} + {row.log_level} + {!row.vehicles ? 0 : row.vehicles.length} + {!row.canbus.filters ? 0 : row.canbus.filters.length} + {Actions(row)} + + ))} + + + + + + +
    +
    + ); +}; + +const FleetsTable = (props) => ( + + + +); + +export default FleetsTable; diff --git a/src/components/Fleets/Table/index.test.jsx b/src/components/Fleets/Table/index.test.jsx new file mode 100644 index 0000000..ab0b599 --- /dev/null +++ b/src/components/Fleets/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("FleetTable", () => { + it("Render", async () => { + setToken(TEST_AUTH_OBJECT); + const container = await renderFleetTable(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/src/components/Fleets/Update/__snapshots__/index.test.jsx.snap b/src/components/Fleets/Update/__snapshots__/index.test.jsx.snap new file mode 100644 index 0000000..1a1234c --- /dev/null +++ b/src/components/Fleets/Update/__snapshots__/index.test.jsx.snap @@ -0,0 +1,185 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FleetUpdate Render 1`] = ` +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + +
    +
    +
    + +
    + + +
    +
    +
    + +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +`; diff --git a/src/components/Fleets/Update/index.jsx b/src/components/Fleets/Update/index.jsx new file mode 100644 index 0000000..079149d --- /dev/null +++ b/src/components/Fleets/Update/index.jsx @@ -0,0 +1,136 @@ +import React, { useEffect, useRef, useState } from "react"; +import { Redirect } from "react-router"; +import { useLocation } from "react-router-dom"; +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 queries = new URLSearchParams(useLocation().search); + const name = queries.get("name") ?? "" + const canbusEnabled = queries.get("canbus_enabled") ?? "" + const logLevel = queries.get("log_level") ?? "" + + const { updateFleet, busy } = useFleetContext(); + const { token: { idToken: { jwtToken: token } } } = useUserContext(); + const { setMessage, setTitle, setSitePath } = useStatusContext(); + const [redirect, setRedirect] = useState(null); + const classes = useStyles(); + + const canbusEnabledEl = useRef(null); + const logLevelEl = useRef(null); + + useEffect(() => { + setTitle("Update Fleet"); + setSitePath([ + { + label: `Fleet ${name}`, + link: "/fleets", + }, + { + label: "Update Fleet", + }, + ]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const onSubmit = async (event) => { + try { + event.preventDefault(); + const formData = { + name: name, + canbus: { enabled: canbusEnabledEl.current.value === "true" }, + log_level: logLevelEl.current.value + }; + console.log(formData); + const result = await updateFleet(name, formData, token); + if (!result || result.error) return; + + setMessage(`Updated ${result.name}`); + setRedirect("/fleets"); + } catch (e) { + setMessage(e.message); + logger.warn(e.stack); + } + }; + + if (redirect && redirect.length > 0) { + return ; + } + + return ( +
    +
    + + + + + +
    + ); +}; + +const FleetUpdateForm = (props) => ( + + + +); + +export default FleetUpdateForm; \ No newline at end of file diff --git a/src/components/Fleets/Update/index.test.jsx b/src/components/Fleets/Update/index.test.jsx new file mode 100644 index 0000000..7dccd65 --- /dev/null +++ b/src/components/Fleets/Update/index.test.jsx @@ -0,0 +1,36 @@ +jest.mock("../../Contexts/FleetContext"); +jest.mock("../../Contexts/StatusContext"); +jest.mock("../../Contexts/UserContext"); + +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 renderFleetUpdate = async () => { + const { container } = render( + + + + + + + + + + ); + await waitFor(() => { }); + return container; +}; + +describe("FleetUpdate", () => { + it("Render", async () => { + setToken(TEST_AUTH_OBJECT); + const container = await renderFleetUpdate(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/src/components/Layouts/SideMenu.jsx b/src/components/Layouts/SideMenu.jsx index d77ded1..6cdd99c 100644 --- a/src/components/Layouts/SideMenu.jsx +++ b/src/components/Layouts/SideMenu.jsx @@ -1,6 +1,7 @@ import React from "react"; import { List } from "@material-ui/core"; import HomeIcon from "@material-ui/icons/Home"; +import DirectionsCarIcon from '@material-ui/icons/DirectionsCar'; import CommuteIcon from "@material-ui/icons/Commute"; import CloudDownloadIcon from "@material-ui/icons/CloudDownload"; import AssessmentIcon from "@material-ui/icons/Assessment"; @@ -22,19 +23,25 @@ const menuData = [ label: "Deployments", to: "/packages", icon: , - roles: [Roles.CREATE, Roles.READ], + roles: [Roles.READ, Roles.CREATE], }, { label: "Vehicles", to: "/vehicles", + icon: , + roles: [Roles.READ, Roles.CREATE], + }, + { + label: "Fleets", + to: "/fleets", icon: , - roles: [Roles.CREATE], + roles: [Roles.READ, Roles.CREATE], }, { label: "Datascope", to: "/datascope", icon: , - roles: [Roles.CREATE, Roles.READ], + roles: [Roles.READ, Roles.CREATE], submenus: [ { label: "Battery", diff --git a/src/components/Layouts/__snapshots__/SideMenu.test.jsx.snap b/src/components/Layouts/__snapshots__/SideMenu.test.jsx.snap index 9feb0ad..9b9c33c 100644 --- a/src/components/Layouts/__snapshots__/SideMenu.test.jsx.snap +++ b/src/components/Layouts/__snapshots__/SideMenu.test.jsx.snap @@ -98,7 +98,7 @@ exports[`SideMenu Authenticated 1`] = ` viewBox="0 0 24 24" >
    @@ -116,6 +116,42 @@ exports[`SideMenu Authenticated 1`] = ` />
  • +
  • + +
    + +
    +
    + + Fleets + +
    + +
    +
  • import("../Datascope/Battery")); -const CANFilterCreate = React.lazy(() => import("../CANFilter/Create")) +const CANFilterCreate = React.lazy(() => import("../CANFilter/Add")) const CANFilterUpdate = React.lazy(() => import("../CANFilter/Update")) const CarsList = React.lazy(() => import("../Cars/List")); 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 FleetAddForm = React.lazy(() => import("../Fleets/Add")); +const FleetUpdateForm = React.lazy(() => import("../Fleets/Update")) const Home = React.lazy(() => import("../Home")); const Manifests = React.lazy(() => import("../Manifest/List")); const ManifestDeploy = React.lazy(() => import("../Manifest/Deploy")); @@ -52,7 +55,7 @@ const SiteRoutes = () => { roles={[Roles.READ, Roles.CREATE]} /> } type={TYPES.PROTECTED} token={token} @@ -67,6 +70,30 @@ const SiteRoutes = () => { groups={groups} roles={[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]} + /> + } + type={TYPES.PROTECTED} + token={token} + groups={groups} + roles={[Roles.READ, Roles.CREATE]} + /> } @@ -111,7 +138,7 @@ const SiteRoutes = () => { type={TYPES.PROTECTED} token={token} groups={groups} - roles={[Roles.CREATE]} + roles={[Roles.READ, Roles.CREATE]} /> { - data.push(filter); - return filter; - }, - getFilters: async (vin, search, token) => { - return { data }; - }, - updateFilter: async (vin, canID, filter, token) => { - const index = data.findIndex(element => element.can_id === canID); - if (index >= 0) data[index] = filter; - }, - deleteFilter: async (vin, canID, token) => { - const index = data.findIndex(element => element.can_id === canID); - if (index >= 0) data.splice(index, 1); - }, + addFilter: async (vin, filter, token) => { + data.push(filter); + return filter; + }, + getFilters: async (vin, search, token) => { + return { data }; + }, + updateFilter: async (vin, canID, filter, token) => { + const index = data.findIndex(element => element.can_id === canID); + if (index >= 0) data[index] = filter; + return filter; + }, + deleteFilter: async (vin, canID, token) => { + const index = data.findIndex(element => element.can_id === canID); + if (index >= 0) data.splice(index, 1); + return canID; + }, }; export default canFiltersAPI; diff --git a/src/services/__mocks__/fleetsAPI.js b/src/services/__mocks__/fleetsAPI.js new file mode 100644 index 0000000..9f4376b --- /dev/null +++ b/src/services/__mocks__/fleetsAPI.js @@ -0,0 +1,27 @@ +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 fleetsAPI = { + addFleet: async (fleet, token) => { + data.push(fleet); + return fleet; + }, + getFleets: async (search, token) => { + return { data }; + }, + updateFleet: async (name, fleet, token) => { + const index = data.findIndex(element => element.name === name); + if (index >= 0) data[index] = fleet; + return fleet; + }, + deleteFleet: async (name, token) => { + const index = data.findIndex(element => element.name === name); + if (index >= 0) data.splice(index, 1); + return name; + }, +}; + +export default fleetsAPI; diff --git a/src/services/fleetsAPI.js b/src/services/fleetsAPI.js new file mode 100644 index 0000000..36e58c6 --- /dev/null +++ b/src/services/fleetsAPI.js @@ -0,0 +1,49 @@ +import { + getAuthHeaderOptions, + fetchRespHandler, + addQueryParams, +} from "../utils/http"; + +const API_ENDPOINT = process.env.REACT_APP_UPLOAD_SERVICE_URL; + +const fleetsAPI = { + addFleet: async (fleet, token) => + fetch(`${API_ENDPOINT}/fleet`, { + method: "POST", + headers: Object.assign( + { "Content-Type": "application/json" }, + getAuthHeaderOptions(token) + ), + body: JSON.stringify(fleet) + }).then(fetchRespHandler), + + getFleets: async (search, token) => + fetch(addQueryParams(`${API_ENDPOINT}/fleets`, search), { + method: "GET", + headers: Object.assign( + { "Content-Type": "application/json" }, + getAuthHeaderOptions(token) + ), + }).then(fetchRespHandler), + + updateFleet: async (name, fleet, token) => + fetch(`${API_ENDPOINT}/fleet/${name}`, { + method: "PUT", + headers: Object.assign( + { "Content-Type": "application/json" }, + getAuthHeaderOptions(token) + ), + body: JSON.stringify(fleet) + }).then(fetchRespHandler), + + deleteFleet: async (name, token) => + fetch(`${API_ENDPOINT}/fleet/${name}`, { + method: "DELETE", + headers: Object.assign( + { "Content-Type": "application/json" }, + getAuthHeaderOptions(token) + ) + }).then(fetchRespHandler), +}; + +export default fleetsAPI;