diff --git a/src/components/App/__snapshots__/App.test.js.snap b/src/components/App/__snapshots__/App.test.js.snap index b23802c..2b98119 100644 --- a/src/components/App/__snapshots__/App.test.js.snap +++ b/src/components/App/__snapshots__/App.test.js.snap @@ -4380,7 +4380,7 @@ exports[`App Route /package-deploy authenticated 1`] = ` data-testid="mocked-vehicleprovider" >
{ } }; - const addFleetVehicle = async (name, vehicle, token) => { + const addFleetVehicles = async (name, vehicles, token) => { try { setBusy(true); validateFleetName(name); - validateVIN(vehicle.vin); - const result = await api.addFleetVehicle(name, vehicle, token); + for (const vin of vehicles.vins) { + validateVIN(vin); + } + + const result = await api.addFleetVehicles(name, vehicles, token); if (result.error) { throw new Error(`Add fleet vehicle error. ${result.message}`); } @@ -250,7 +253,7 @@ export const FleetProvider = ({ children }) => { fleetVehicles, totalFleetVehicles, getFleetVehicles, - addFleetVehicle, + addFleetVehicles, deleteFleetVehicle, fleetCANFilters, diff --git a/src/components/Contexts/FleetContext.test.jsx b/src/components/Contexts/FleetContext.test.jsx index f595e2a..93b88a1 100644 --- a/src/components/Contexts/FleetContext.test.jsx +++ b/src/components/Contexts/FleetContext.test.jsx @@ -391,14 +391,14 @@ describe("FleetContext", () => { }); }); - describe("addFleetVehicle", () => { + describe("addFleetVehicles", () => { beforeEach(async () => { const TestComp = () => { - const { busy, addFleetVehicle } = useFleetContext(); + const { busy, addFleetVehicles } = useFleetContext(); const { message, setMessage } = useStatusContext(); const add = async (name, vehicle) => { try { - await addFleetVehicle(name, vehicle); + await addFleetVehicles(name, vehicle); } catch (e) { setMessage(e.message); } @@ -417,8 +417,8 @@ describe("FleetContext", () => { onClick={() => add({})} />
- -
-
- -
- - - - - - -
- -
- -
-
- - + + + +
-
- -
- - -
+
+ + + + + + + + + + + + + + +
+ + + + + + + + + + VIN + + sorted ascending + + + + + + Model + + + + + Year + + + + + Trim + + + + + Created + + + + + Updated + + +
- + +
+
+
+
+
+
+ +
+ + + + + + +
+ +
+ +
+ +
+ + +
+
+ +
+
+ +
+ + +
+
+
+
+
+
+
+
+ + + diff --git a/src/components/Fleets/Status/Vehicles/Add/index.jsx b/src/components/Fleets/Status/Vehicles/Add/index.jsx index 483b416..6b775df 100644 --- a/src/components/Fleets/Status/Vehicles/Add/index.jsx +++ b/src/components/Fleets/Status/Vehicles/Add/index.jsx @@ -1,6 +1,7 @@ -import React, { useEffect, useRef, useState } from "react"; +import React, { useEffect, useState } from "react"; import { Redirect, useParams } from "react-router"; import { + Box, Button, Checkbox, FormControlLabel, @@ -8,7 +9,11 @@ import { FormLabel, Radio, RadioGroup, - TextField + TextField, + Accordion, + AccordionSummary, + AccordionDetails, + Typography, } from "@material-ui/core"; import useStyles from "../../../../useStyles"; @@ -19,11 +24,14 @@ import { import { useStatusContext } from "../../../../Contexts/StatusContext"; import { useUserContext } from "../../../../Contexts/UserContext"; import { logger } from "../../../../../services/monitoring"; +import { VehicleProvider } from "../../../../Contexts/VehicleContext"; +import CarSelectionTable from "../../../../Controls/CarSelectionTable"; +import SearchField from "../../../../Controls/SearchField"; const MainForm = () => { const { name } = useParams(); const { setMessage, setTitle, setSitePath } = useStatusContext(); - const { addFleetVehicle, busy } = useFleetContext(); + const { addFleetVehicles, busy } = useFleetContext(); const { token: { idToken: { jwtToken: token }, @@ -32,7 +40,8 @@ const MainForm = () => { const classes = useStyles(); const [redirect, setRedirect] = useState(null); - const vinEl = useRef(null); + const [vins, setVins] = useState([]); + const [search, setSearch] = useState(""); const [selectedLogLevel, setSelectedLogLevel] = useState("info"); const [canbusEnabled, setCANBusEnabled] = useState(true); const [dataLoggerEnabled, setDataLoggerEnabled] = useState(false); @@ -58,6 +67,30 @@ const MainForm = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + const handleSearch = (query) => { + setVins([]); + setSearch(query); + }; + + const handleSelectAll = (cars) => { + setVins(cars); + }; + + const handleSelect = (event, key) => { + try { + let newVins; + if (event.target.checked) { + newVins = [...vins]; + newVins.push(key); + } else { + newVins = vins.filter((vin) => vin !== key); + } + setVins(newVins); + } catch (e) { + logger.warn(e.stack); + } + }; + const onLogLevelChange = (event) => { setSelectedLogLevel(event.target.value); } @@ -79,11 +112,11 @@ const MainForm = () => { } const onSubmit = async (event) => { - try { - event.preventDefault(); + event.preventDefault(); + try { const formData = { - vin: vinEl.current.value, + vins, log_level: selectedLogLevel, canbus: { enabled: canbusEnabled, @@ -93,9 +126,9 @@ const MainForm = () => { } }; - const result = await addFleetVehicle(name, formData, token); + const result = await addFleetVehicles(name, formData, token); - setMessage(`Added ${result.vin}`); + setMessage(`Added ${result.vins.join(", ")}`); setRedirect(`/fleet/${name}#vehicles`); } catch (e) { setMessage(e.message); @@ -110,94 +143,106 @@ const MainForm = () => { return (
- - Log Level - - } label="Trace" /> - } label="Debug" /> - } label="Info" /> - } label="Warning" /> - } label="Error" /> - } label="Critical" /> - - CAN Bus - - - } label="CAN Bus Enabled" /> - + + - - } label="Data Logger Enabled" /> - - - + + + + + + Additional Options + + + + Log Level + + } label="Trace" /> + } label="Debug" /> + } label="Info" /> + } label="Warning" /> + } label="Error" /> + } label="Critical" /> + + CAN Bus + + + } label="CAN Bus Enabled" /> + + + } label="Data Logger Enabled" /> + + + + + + + +
); diff --git a/src/components/useStyles.jsx b/src/components/useStyles.jsx index 5e7769e..d3daf28 100644 --- a/src/components/useStyles.jsx +++ b/src/components/useStyles.jsx @@ -295,6 +295,14 @@ const useStyles = makeStyles((theme) => ({ whiteSpace: "normal", wordWrap: "break-word", }, + fleetVehicleAddSubmit: { + position: "sticky", + bottom: 0, + right: 0, + width: "100%", + padding: "16px 0", + backgroundColor: "#fafafa", + } })); export default useStyles; diff --git a/src/services/__mocks__/fleetsAPI.js b/src/services/__mocks__/fleetsAPI.js index c51b490..87fb172 100644 --- a/src/services/__mocks__/fleetsAPI.js +++ b/src/services/__mocks__/fleetsAPI.js @@ -62,8 +62,8 @@ const fleetsAPI = { getFleetVehicles: async () => { return { data: vehicles }; }, - addFleetVehicle: async (_name, vehicle) => { - vehicles.push(vehicle.vin); + addFleetVehicles: async (_name, vehicle) => { + vehicles.push(...vehicle.vins); return vehicle; }, deleteFleetVehicle: async (_name, vehicle) => { diff --git a/src/services/fleetsAPI.js b/src/services/fleetsAPI.js index 542cc5e..2576334 100644 --- a/src/services/fleetsAPI.js +++ b/src/services/fleetsAPI.js @@ -73,14 +73,14 @@ const fleetsAPI = { .then(fetchRespHandler) .catch(errorHandler), - addFleetVehicle: async (name, vehicle, token) => + addFleetVehicles: async (name, vehicles, token) => fetch(`${API_ENDPOINT}/fleet/${name}/vehicle`, { method: "POST", headers: Object.assign( { "Content-Type": "application/json" }, getAuthHeaderOptions(token) ), - body: JSON.stringify(vehicle), + body: JSON.stringify(vehicles), }) .then(fetchRespHandler) .catch(errorHandler),