Merge branch 'main' into CEC-5443
This commit is contained in:
@@ -1,30 +1,72 @@
|
||||
import { useState, forwardRef, useImperativeHandle } from "react";
|
||||
import { useState, useEffect, forwardRef, useImperativeHandle } from "react";
|
||||
import {
|
||||
Checkbox,
|
||||
FormControl,
|
||||
FormControlLabel,
|
||||
FormGroup,
|
||||
FormLabel,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
TextField,
|
||||
} from '@material-ui/core';
|
||||
import SearchSelect from "../../SearchSelect/SearchSelect";
|
||||
import { useStatusContext } from "../../Contexts/StatusContext";
|
||||
import { useUserContext } from "../../Contexts/UserContext";
|
||||
import fleetsAPI from "../../../services/fleetsAPI";
|
||||
import useStyles from "../../useStyles";
|
||||
|
||||
const DEFAULT_LOG_LEVEL = "info";
|
||||
const DEFAULT_CANBUS_ENABLED = false;
|
||||
const DEFAULT_DATA_LOGGER_ENABLED = false;
|
||||
const DEFAULT_MAX_MEM_BUFFER_SIZE = 0;
|
||||
const DEFAULT_MAX_DISK_BUFFER_SIZE = 0;
|
||||
|
||||
export default forwardRef(({
|
||||
ids,
|
||||
idCSV,
|
||||
ids = [],
|
||||
idCSV = "",
|
||||
fleet,
|
||||
}, ref) => {
|
||||
const [fromFleet, setFromFleet] = useState(fleet);
|
||||
const [toFleet, setToFleet] = useState();
|
||||
const classnames = useStyles();
|
||||
const { setMessage } = useStatusContext();
|
||||
const { token: { idToken: { jwtToken: token } } } = useUserContext();
|
||||
|
||||
const [fromFleet, setFromFleet] = useState(fleet);
|
||||
const [toFleet, setToFleet] = useState();
|
||||
|
||||
|
||||
const [selectedLogLevel, setSelectedLogLevel] = useState(DEFAULT_LOG_LEVEL);
|
||||
const [canbusEnabled, setCANBusEnabled] = useState(DEFAULT_CANBUS_ENABLED);
|
||||
const [dataLoggerEnabled, setDataLoggerEnabled] = useState(DEFAULT_DATA_LOGGER_ENABLED);
|
||||
const [maxMemBufferSize, setMaxMemBufferSize] = useState(DEFAULT_MAX_MEM_BUFFER_SIZE);
|
||||
const [maxDiskBufferSize, setMaxDiskBufferSize] = useState(DEFAULT_MAX_DISK_BUFFER_SIZE);
|
||||
|
||||
const logLevel = [
|
||||
["trace", "Trace"],
|
||||
["debug", "Debug"],
|
||||
["info", "Info"],
|
||||
["warning", "Warning"],
|
||||
["error", "Error"],
|
||||
["critical", "Critical"],
|
||||
];
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
async submit() {
|
||||
const errorTracking = [false, false];
|
||||
|
||||
if (toFleet) {
|
||||
const payload = {
|
||||
vins: ids,
|
||||
log_level: selectedLogLevel,
|
||||
canbus: {
|
||||
enabled: canbusEnabled,
|
||||
data_logger_enabled: canbusEnabled ? dataLoggerEnabled : false,
|
||||
max_mem_buffer_size: parseInt(maxMemBufferSize),
|
||||
max_disk_buffer_size: parseInt(maxDiskBufferSize),
|
||||
}
|
||||
};
|
||||
|
||||
await fleetsAPI
|
||||
.addFleetVehicles(toFleet, ids, token)
|
||||
.addFleetVehicles(toFleet, payload, token)
|
||||
.then((response) => {
|
||||
if (response.error) {
|
||||
errorTracking[0] = true;
|
||||
@@ -85,6 +127,58 @@ export default forwardRef(({
|
||||
.catch(() => []);
|
||||
}
|
||||
|
||||
const onLogLevelChange = (event) => {
|
||||
setSelectedLogLevel(event.target.value);
|
||||
}
|
||||
|
||||
const onCANBusChange = (event) => {
|
||||
setCANBusEnabled(event.target.checked);
|
||||
}
|
||||
|
||||
const onDataLoggerChange = (event) => {
|
||||
setDataLoggerEnabled(event.target.checked);
|
||||
}
|
||||
|
||||
const onMaxMemBufferSizeChange = (event) => {
|
||||
setMaxMemBufferSize(event.target.value);
|
||||
}
|
||||
|
||||
const onMaxDiskBufferSizeChange = (event) => {
|
||||
setMaxDiskBufferSize(event.target.value);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!toFleet) {
|
||||
setSelectedLogLevel(DEFAULT_LOG_LEVEL);
|
||||
setCANBusEnabled(DEFAULT_CANBUS_ENABLED);
|
||||
setDataLoggerEnabled(DEFAULT_DATA_LOGGER_ENABLED);
|
||||
setMaxMemBufferSize(DEFAULT_MAX_MEM_BUFFER_SIZE);
|
||||
setMaxDiskBufferSize(DEFAULT_MAX_DISK_BUFFER_SIZE);
|
||||
}
|
||||
}, [
|
||||
toFleet, setSelectedLogLevel, setCANBusEnabled,
|
||||
setDataLoggerEnabled, setMaxMemBufferSize, setMaxDiskBufferSize
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!canbusEnabled) {
|
||||
setDataLoggerEnabled(DEFAULT_DATA_LOGGER_ENABLED);
|
||||
setMaxMemBufferSize(DEFAULT_MAX_MEM_BUFFER_SIZE);
|
||||
setMaxDiskBufferSize(DEFAULT_MAX_DISK_BUFFER_SIZE);
|
||||
}
|
||||
}, [
|
||||
canbusEnabled, setDataLoggerEnabled, setMaxMemBufferSize,
|
||||
setMaxDiskBufferSize,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!dataLoggerEnabled) {
|
||||
setMaxDiskBufferSize(DEFAULT_MAX_DISK_BUFFER_SIZE);
|
||||
}
|
||||
}, [
|
||||
dataLoggerEnabled, setMaxDiskBufferSize,
|
||||
]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
@@ -94,7 +188,8 @@ export default forwardRef(({
|
||||
VINs will be removed from the "From Fleet" and added to the "To Fleet". If you would
|
||||
like to only add or only remove the other field can be left blank.
|
||||
</p>
|
||||
<FormControl variant="filled" fullWidth={true}>
|
||||
<FormGroup row className={classnames.formGrid}>
|
||||
<FormControl variant="filled" className={classnames.formGridItem}>
|
||||
<SearchSelect
|
||||
label="Remove From Fleet"
|
||||
value={fromFleet}
|
||||
@@ -103,7 +198,7 @@ export default forwardRef(({
|
||||
research={true}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl variant="filled" fullWidth={true}>
|
||||
<FormControl variant="filled" className={classnames.formGridItem}>
|
||||
<SearchSelect
|
||||
label="Add To Fleet"
|
||||
value={toFleet}
|
||||
@@ -112,6 +207,68 @@ export default forwardRef(({
|
||||
research={true}
|
||||
/>
|
||||
</FormControl>
|
||||
</FormGroup>
|
||||
<FormGroup className={classnames.marginX}>
|
||||
<FormControl>
|
||||
<FormLabel id="demo-row-radio-buttons-group-label">Log Level</FormLabel>
|
||||
<RadioGroup
|
||||
aria-labelledby="demo-row-radio-buttons-group-label"
|
||||
value={selectedLogLevel}
|
||||
onChange={onLogLevelChange}
|
||||
row
|
||||
>
|
||||
{logLevel.map(([value, label]) => (
|
||||
<FormControlLabel
|
||||
key={value}
|
||||
value={value}
|
||||
control={<Radio size="small" />}
|
||||
label={label}
|
||||
disabled={!toFleet}
|
||||
/>
|
||||
))}
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
</FormGroup>
|
||||
<FormGroup row className={classnames.marginX}>
|
||||
<FormControlLabel control={
|
||||
<Checkbox
|
||||
checked={canbusEnabled}
|
||||
onChange={onCANBusChange}
|
||||
disabled={!toFleet}
|
||||
/>
|
||||
} label="CAN Bus Enabled" />
|
||||
<FormControlLabel control={
|
||||
<Checkbox
|
||||
checked={dataLoggerEnabled}
|
||||
onChange={onDataLoggerChange}
|
||||
disabled={!canbusEnabled}
|
||||
/>
|
||||
} label="Data Logger Enabled" />
|
||||
</FormGroup>
|
||||
<FormGroup row className={classnames.formGrid}>
|
||||
<TextField
|
||||
className={classnames.formGridItem}
|
||||
id="max_mem_buffer_size"
|
||||
label='Max Memory Buffer Size'
|
||||
value={maxMemBufferSize}
|
||||
onChange={onMaxMemBufferSizeChange}
|
||||
variant="outlined"
|
||||
inputProps={{ maxLength: "12" }}
|
||||
type="number"
|
||||
disabled={!canbusEnabled || !toFleet}
|
||||
/>
|
||||
<TextField
|
||||
className={classnames.formGridItem}
|
||||
id="max_disk_buffer_size"
|
||||
label='Max Disk Buffer Size'
|
||||
value={maxDiskBufferSize}
|
||||
onChange={onMaxDiskBufferSizeChange}
|
||||
variant="outlined"
|
||||
inputProps={{ maxLength: "12" }}
|
||||
type="number"
|
||||
disabled={!dataLoggerEnabled || !toFleet}
|
||||
/>
|
||||
</FormGroup>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
@@ -2,7 +2,7 @@ jest.mock("../../Contexts/UserContext");
|
||||
jest.mock("../../Contexts/StatusContext");
|
||||
jest.mock("../../../services/fleetsAPI");
|
||||
|
||||
import React, { useState } from "react";
|
||||
import React from "react";
|
||||
import {
|
||||
render,
|
||||
act,
|
||||
@@ -13,28 +13,53 @@ import { TEST_AUTH_OBJECT_FISKER } from "../../../utils/testing";
|
||||
import UpdateFleetVehicles from "./UpdateFleetVehicles";
|
||||
import fleetsAPI from "../../../services/fleetsAPI";
|
||||
|
||||
jest.mock('react', () => ({
|
||||
...jest.requireActual('react'),
|
||||
useState: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('@material-ui/core/FormControl', () => {
|
||||
const React = require('react');
|
||||
return () => <div data-testid="mock-form-control" />;
|
||||
});
|
||||
|
||||
jest.mock('@material-ui/core/FormControlLabel', () => {
|
||||
const React = require('react');
|
||||
return () => <div data-testid="mock-form-control-label" />;
|
||||
});
|
||||
|
||||
jest.mock('@material-ui/core/FormGroup', () => {
|
||||
const React = require('react');
|
||||
return () => <div data-testid="mock-form-group" />;
|
||||
});
|
||||
|
||||
jest.mock('@material-ui/core/FormLabel', () => {
|
||||
const React = require('react');
|
||||
return () => <div data-testid="mock-form-label" />;
|
||||
});
|
||||
|
||||
jest.mock('@material-ui/core/Checkbox', () => {
|
||||
const React = require('react');
|
||||
return () => <div data-testid="mock-checkbox" />;
|
||||
});
|
||||
|
||||
jest.mock('@material-ui/core/Radio', () => {
|
||||
const React = require('react');
|
||||
return () => <div data-testid="mock-radio" />;
|
||||
});
|
||||
|
||||
jest.mock('@material-ui/core/RadioGroup', () => {
|
||||
const React = require('react');
|
||||
return () => <div data-testid="mock-radio-group" />;
|
||||
});
|
||||
|
||||
jest.mock('@material-ui/core/TextField', () => {
|
||||
const React = require('react');
|
||||
return () => <div data-testid="mock-text-field" />;
|
||||
});
|
||||
|
||||
describe("BulkActions/UpdateFleetVehicles", () => {
|
||||
beforeAll(() => {
|
||||
setToken(TEST_AUTH_OBJECT_FISKER);
|
||||
});
|
||||
|
||||
it("makes request to update the config of multiple vehicles", async () => {
|
||||
useState
|
||||
.mockReturnValueOnce(["Default-Test", jest.fn()])
|
||||
.mockReturnValueOnce([["Default-Test"], jest.fn()])
|
||||
.mockReturnValueOnce(["Default-Test", jest.fn()])
|
||||
.mockReturnValueOnce([["Default-Test"], jest.fn()]);
|
||||
const add = jest.spyOn(fleetsAPI, "addFleetVehicles");
|
||||
it("makes request add vehicle to one fleet and remove from another", async () => {
|
||||
// const add = jest.spyOn(fleetsAPI, "addFleetVehicles");
|
||||
const remove = jest.spyOn(fleetsAPI, "deleteFleetVehicles");
|
||||
const ref = React.createRef();
|
||||
|
||||
@@ -43,15 +68,16 @@ describe("BulkActions/UpdateFleetVehicles", () => {
|
||||
<UserProvider>
|
||||
<UpdateFleetVehicles
|
||||
ref={ref}
|
||||
ids={["TESTVIN1234567890"]}
|
||||
ids={["TESTVIN123456789a", "TESTVIN123456789b", "TESTVIN123456789c"]}
|
||||
idCSV=""
|
||||
fleet="US-WEST"
|
||||
/>
|
||||
</UserProvider>
|
||||
</StatusProvider>
|
||||
);
|
||||
|
||||
await act(async () => ref.current.submit());
|
||||
expect(add).toHaveBeenCalled();
|
||||
// expect(add).toHaveBeenCalled();
|
||||
expect(remove).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -162,6 +162,10 @@ export const FleetProvider = ({ children }) => {
|
||||
};
|
||||
|
||||
const watchFleetVehicles = new Polling(async ({ token }) => {
|
||||
if (carUpdateIdsRef.current.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await updatesApi.getCarUpdateProgress(
|
||||
carUpdateIdsRef.current.join(","),
|
||||
token
|
||||
@@ -217,7 +221,7 @@ export const FleetProvider = ({ children }) => {
|
||||
return vehicle;
|
||||
}));
|
||||
return Promise.resolve();
|
||||
}, 5000);
|
||||
}, 2500);
|
||||
|
||||
const addFleetVehicles = async (name, vehicles, token) => {
|
||||
try {
|
||||
@@ -233,6 +237,7 @@ export const FleetProvider = ({ children }) => {
|
||||
if (result.error) {
|
||||
throw new Error(`Add fleet vehicle error. ${result.message}`);
|
||||
}
|
||||
console.log(result)
|
||||
return result;
|
||||
} finally {
|
||||
setBusy(false);
|
||||
|
||||
@@ -418,7 +418,7 @@ describe("FleetContext", () => {
|
||||
/>
|
||||
<button
|
||||
data-testid="addFleetVehicles"
|
||||
onClick={() => add("US-TEST", { vins: ["TESTVIN1234567890"] })}
|
||||
onClick={() => add("US-CENTRAL", { vins: ["TESTVIN1234567890"] })}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -340,6 +340,18 @@ const useStyles = makeStyles((theme) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
},
|
||||
formGrid: {
|
||||
display: "flex",
|
||||
gap: "16px",
|
||||
width: "100%",
|
||||
},
|
||||
formGridItem: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
marginX: {
|
||||
marginTop: theme.spacing(2),
|
||||
marginBottom: theme.spacing(2),
|
||||
},
|
||||
}));
|
||||
|
||||
export default useStyles;
|
||||
|
||||
@@ -82,20 +82,36 @@ const fleetsAPI = {
|
||||
return { data: vehicles };
|
||||
},
|
||||
addFleetVehicles: async (_name, payload) => {
|
||||
payload.vins && vehicles.push(...payload.vins.map((vin => ({
|
||||
const index = fleets.findIndex((fleet) => fleet.name === _name);
|
||||
|
||||
if (!payload.vins || index === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
vehicles.push(...payload.vins.map((vin => ({
|
||||
vin,
|
||||
connected: false,
|
||||
connectedHMI: false,
|
||||
trex_version: "",
|
||||
}))));
|
||||
fleets[index].vehicles.push(...payload.vins);
|
||||
|
||||
return payload;
|
||||
},
|
||||
deleteFleetVehicles: async (_name, vins) => {
|
||||
const index = fleets.findIndex((fleet) => fleet.name === _name);
|
||||
|
||||
if (!vins || index === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < vins; i++) {
|
||||
const vin = vins[i];
|
||||
const index = vehicles.findIndex(element => element.vin === vin);
|
||||
if (index >= 0) vehicles.splice(index, 1);
|
||||
fleets[index].vehicles = fleets[index].vehicles.filter((vin) => !vins.includes(vin))
|
||||
}
|
||||
|
||||
return { message: "Deleted" };
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user