diff --git a/src/components/Cars/List/useQuery.js b/src/components/Cars/List/useQuery.js index e3d73dd..48d353b 100644 --- a/src/components/Cars/List/useQuery.js +++ b/src/components/Cars/List/useQuery.js @@ -6,11 +6,16 @@ const TYPE_VIN = "vin"; /** * Match VIN with RegEx * @param {string} vin - potential VIN - * @returns {boolean} + * @returns {[boolean, string]} */ -function isVIN(vin) { +function parseVin(vin = "") { + const prefix = new RegExp(`^${TYPE_VIN}:`); + if (vin.match(prefix)) { + return [true, vin.replace(prefix, "")]; + } + var re = new RegExp("^[A-HJ-NPR-Z0-9]{8}[0-9X][A-HJ-NPR-Z0-9]{2}[0-9]{6}$"); - return vin.match(re) + return [vin.match(re), vin]; } /** @@ -24,9 +29,10 @@ function isVIN(vin) { function parseQueryPart(part) { let type = "search"; - if (isVIN(part)) { + const [isVin, vin] = parseVin(part) + if (isVin) { type = TYPE_VIN; - part = `${part}`; + part = vin; } return [type, part]; @@ -55,11 +61,19 @@ export default function useQuery() { } }); + setQuery(() => parts.map(([type, part]) => { + if (type !== "search") { + return `${type}:${part}`; + } + + return part; + }).join(" ").replace(/\s{2,}/g, ' ').trim()); + setPayload({ search, vins: vins.join(","), }) - }, [query, setPayload]); + }, [query, setPayload, setQuery]); useEffect(() => { setLoading(false); diff --git a/src/components/Cars/List/useQuery.test.jsx b/src/components/Cars/List/useQuery.test.jsx index ae2e511..fbf2e1b 100644 --- a/src/components/Cars/List/useQuery.test.jsx +++ b/src/components/Cars/List/useQuery.test.jsx @@ -35,25 +35,80 @@ const template = (desc, data, expected) => { fireEvent.change(input, { target: { value: data } }); expect(search.innerHTML).toBe(expected[0]); expect(vins.innerHTML).toBe(expected[1]); - expect(query.innerHTML).toBe(data); + expect(query.innerHTML).toBe(expected[2]); }); } describe("useQuery", () => { [ - ["parses a search query", "test", ["test", ""]], - ["parses a vin query", "VCF1ZBU23PG001209", ["", "VCF1ZBU23PG001209"]], - ["parses a mixed search and vin query", "test VCF1ZBU23PG001209", ["test", "VCF1ZBU23PG001209"]], + [ + "parses a search query", + "test", + ["test", "", "test"] + ], + [ + "parses a vin query", + "VCF1ZBU23PG001209", + ["", "VCF1ZBU23PG001209", "vin:VCF1ZBU23PG001209"] + ], + [ + "parses a mixed search and vin query", + "test VCF1ZBU23PG001209", + ["test", "VCF1ZBU23PG001209", "test vin:VCF1ZBU23PG001209"] + ], - ["parses a comma separated search query", "ocean,pear,alaska,ronin", ["ocean pear alaska ronin", ""]], - ["parses a comma separated vin query", "VCF1EBE2008016235,VCF1EBE20PG001002,VCF1EBE20PG001162", ["", "VCF1EBE2008016235,VCF1EBE20PG001002,VCF1EBE20PG001162"]], - ["parses a comma separated mixed search and vin query", "test,VCF1EBE2008016235,VCF1EBE20PG001002,VCF1EBE20PG001162", ["test", "VCF1EBE2008016235,VCF1EBE20PG001002,VCF1EBE20PG001162"]], + [ + "parses a comma separated search query", + "ocean,pear,alaska,ronin", + ["ocean pear alaska ronin", "", "ocean pear alaska ronin"] + ], + [ + "parses a comma separated vin query", + "VCF1EBE2008016235,VCF1EBE20PG001002,VCF1EBE20PG001162", + ["", "VCF1EBE2008016235,VCF1EBE20PG001002,VCF1EBE20PG001162", "vin:VCF1EBE2008016235 vin:VCF1EBE20PG001002 vin:VCF1EBE20PG001162"] + ], + [ + "parses a comma separated mixed search and vin query", + "test,VCF1EBE2008016235,VCF1EBE20PG001002,VCF1EBE20PG001162", + ["test", "VCF1EBE2008016235,VCF1EBE20PG001002,VCF1EBE20PG001162", "test vin:VCF1EBE2008016235 vin:VCF1EBE20PG001002 vin:VCF1EBE20PG001162"] + ], - ["parses a space separated search query", "ocean pear alaska ronin", ["ocean pear alaska ronin", ""]], - ["parses a space separated vin query", "VCF1EBE2008016235 VCF1EBE20PG001002 VCF1EBE20PG001162", ["", "VCF1EBE2008016235,VCF1EBE20PG001002,VCF1EBE20PG001162"]], - ["parses a space separated mixed search and vin query", "test VCF1EBE2008016235 VCF1EBE20PG001002 VCF1EBE20PG001162", ["test", "VCF1EBE2008016235,VCF1EBE20PG001002,VCF1EBE20PG001162"]], + [ + "parses a space separated search query", + "ocean pear alaska ronin", + ["ocean pear alaska ronin", "", "ocean pear alaska ronin"] + ], + [ + "parses a space separated vin query", + "VCF1EBE2008016235 VCF1EBE20PG001002 VCF1EBE20PG001162", + ["", "VCF1EBE2008016235,VCF1EBE20PG001002,VCF1EBE20PG001162", "vin:VCF1EBE2008016235 vin:VCF1EBE20PG001002 vin:VCF1EBE20PG001162"] + ], + [ + "parses a space separated mixed search and vin query", + "test VCF1EBE2008016235 VCF1EBE20PG001002 VCF1EBE20PG001162", + ["test", "VCF1EBE2008016235,VCF1EBE20PG001002,VCF1EBE20PG001162", "test vin:VCF1EBE2008016235 vin:VCF1EBE20PG001002 vin:VCF1EBE20PG001162"] + ], - ["trims extraneous values from search", "ocean,, , ,,,,pear,,, ", ["ocean pear", ""]], - ["trims extraneous values from vins", "VCF1EBE2008016235,, , ,,,,VCF1EBE20PG001002,,, ", ["", "VCF1EBE2008016235,VCF1EBE20PG001002"]], + [ + "trims extraneous values from search", + "ocean,, , ,,,,pear,,, ", + ["ocean pear", "", "ocean pear"] + ], + [ + "trims extraneous values from vins", + "VCF1EBE2008016235,, , ,,,,VCF1EBE20PG001002,,, ", + ["", "VCF1EBE2008016235,VCF1EBE20PG001002", "vin:VCF1EBE2008016235 vin:VCF1EBE20PG001002"] + ], + + [ + "honors a declared vin", + "vin:1209", + ["", "1209", "vin:1209"] + ], + [ + "keeps order of query parts", + "test vin:1209 VCF1EBE2008016235,VCF1EBE20PG001002 test2", + ["test test2", "1209,VCF1EBE2008016235,VCF1EBE20PG001002", "test vin:1209 vin:VCF1EBE2008016235 vin:VCF1EBE20PG001002 test2"] + ], ].forEach((args) => template(...args)) }); diff --git a/src/components/Contexts/FleetContext.jsx b/src/components/Contexts/FleetContext.jsx index 0be1f7c..71fcace 100644 --- a/src/components/Contexts/FleetContext.jsx +++ b/src/components/Contexts/FleetContext.jsx @@ -116,19 +116,25 @@ export const FleetProvider = ({ children }) => { const result = await api.getFleetVehicles(name, search, token); if (result.error) { - setFleetVehicles([]) + setFleetVehicles([]); throw new Error(`Get fleet vehicles error. ${result.message}`); } - const vins = result.data.map(vehicle => vehicle.vin); + if (!result.data) { + setFleetVehicles([]); + throw new Error("Fleet is empty, consider adding some vehicles."); + } + + const vins = result.data?.map(vehicle => vehicle.vin); + const connectionsResult = await vehiclesAPI.getConnections({ "VINs": vins }, token) if (connectionsResult.error) { - setFleetVehicles([]) + setFleetVehicles([]); throw new Error(`Get vehicles connections error. ${result.message}`); } var cars = [] - result.data.forEach((vehicle) => { + result.data?.forEach((vehicle) => { cars.push({ vin: vehicle.vin, connected: connectionsResult[vehicle.vin] || false, diff --git a/src/components/Fleets/Status/Vehicles/Add/index.jsx b/src/components/Fleets/Status/Vehicles/Add/index.jsx index 6b775df..85d9898 100644 --- a/src/components/Fleets/Status/Vehicles/Add/index.jsx +++ b/src/components/Fleets/Status/Vehicles/Add/index.jsx @@ -27,6 +27,7 @@ import { logger } from "../../../../../services/monitoring"; import { VehicleProvider } from "../../../../Contexts/VehicleContext"; import CarSelectionTable from "../../../../Controls/CarSelectionTable"; import SearchField from "../../../../Controls/SearchField"; +import useQuery from "../../../../Cars/List/useQuery"; const MainForm = () => { const { name } = useParams(); @@ -41,7 +42,7 @@ const MainForm = () => { const [redirect, setRedirect] = useState(null); const [vins, setVins] = useState([]); - const [search, setSearch] = useState(""); + const { payload, query, setQuery } = useQuery(); const [selectedLogLevel, setSelectedLogLevel] = useState("info"); const [canbusEnabled, setCANBusEnabled] = useState(true); const [dataLoggerEnabled, setDataLoggerEnabled] = useState(false); @@ -69,7 +70,7 @@ const MainForm = () => { const handleSearch = (query) => { setVins([]); - setSearch(query); + setQuery(query); }; const handleSelectAll = (cars) => { @@ -143,20 +144,20 @@ const MainForm = () => { return (
- + - + Additional Options @@ -164,73 +165,73 @@ const MainForm = () => { Log Level - - } label="Trace" /> - } label="Debug" /> - } label="Info" /> - } label="Warning" /> - } label="Error" /> - } label="Critical" /> - - CAN Bus - - - } label="CAN Bus Enabled" /> - + } label="Trace" /> + } label="Debug" /> + } label="Info" /> + } label="Warning" /> + } label="Error" /> + } label="Critical" /> + + CAN Bus + + - - } label="Data Logger Enabled" /> - + } label="CAN Bus Enabled" /> + + } label="Data Logger Enabled" /> + + - +