Merge pull request #529 from Fisker-Inc/CEC-6051
CEC-6051 - Select All for fleet bulk actions
This commit is contained in:
@@ -6,8 +6,6 @@ import vehiclesAPI from "../../../services/vehiclesAPI";
|
||||
export default forwardRef(({
|
||||
ids,
|
||||
idCSV,
|
||||
fleet,
|
||||
selectAll,
|
||||
}, ref) => {
|
||||
const { setMessage } = useStatusContext();
|
||||
const { token: { idToken: { jwtToken: token } } } = useUserContext();
|
||||
@@ -15,12 +13,10 @@ export default forwardRef(({
|
||||
useImperativeHandle(ref, () => ({
|
||||
async submit() {
|
||||
return vehiclesAPI
|
||||
.flashpackVersionBulkUpdate(selectAll, fleet, ids, token)
|
||||
.flashpackVersionBulkUpdate(ids, token)
|
||||
.then((data) => {
|
||||
if (data.error) {
|
||||
setMessage(`${data.error}: ${data.message}`);
|
||||
} else if (selectAll) {
|
||||
setMessage(`Updating flashpack numbers for all VINs in fleet: ${fleet}`);
|
||||
} else if (ids.length === 1) {
|
||||
setMessage(`Updating flashpack number for ${ids[0]}`);
|
||||
} else {
|
||||
@@ -30,15 +26,6 @@ export default forwardRef(({
|
||||
},
|
||||
}));
|
||||
|
||||
if (selectAll) {
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
You are updating flashpack numbers for all VINs in the following fleet: {fleet}.
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
< div >
|
||||
<p>
|
||||
@@ -46,5 +33,4 @@ export default forwardRef(({
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
@@ -20,8 +20,6 @@ const UpdateFlashpackNumbers = lazy(() => import("./actions/UpdateFlashpackNumbe
|
||||
export default function BulkActions({
|
||||
ids = [],
|
||||
actions = [],
|
||||
fleet = undefined,
|
||||
selectAll = false,
|
||||
callback = (active, ids, context) => { }, // context is raised from the action itself
|
||||
}) {
|
||||
const [open, setOpen] = useState(false);
|
||||
@@ -92,8 +90,6 @@ export default function BulkActions({
|
||||
const payload = {
|
||||
ids,
|
||||
idCSV: (ids && ids.length > 0) ? truncateCSV(ids, 10) : "N/A",
|
||||
fleet,
|
||||
selectAll,
|
||||
ref: activeRef
|
||||
};
|
||||
|
||||
@@ -114,7 +110,7 @@ export default function BulkActions({
|
||||
setEmbedded(action?.embedded);
|
||||
}, [active, filteredActions]);
|
||||
|
||||
if (!selectAll && (!ids || ids.length === 0)) return <></>;
|
||||
if (!ids || ids.length === 0) return <></>;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -163,6 +163,23 @@ export const FleetProvider = ({ children }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const getFleetVINs = async (name, token) => {
|
||||
try {
|
||||
setBusy(true);
|
||||
|
||||
const result = await api.getFleetVehicles(name, null, token);
|
||||
if (result.error) {
|
||||
throw new Error(`Get fleet vehicles error. ${result.message}`);
|
||||
}
|
||||
|
||||
const vins = result.data?.map(vehicle => vehicle.vin);
|
||||
|
||||
return vins;
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
};
|
||||
|
||||
const watchFleetVehicles = new Polling(async ({ token }) => {
|
||||
if (carUpdateIdsRef.current.length === 0) {
|
||||
return;
|
||||
@@ -396,6 +413,7 @@ export const FleetProvider = ({ children }) => {
|
||||
totalFleetVehicles,
|
||||
watchFleetVehicles,
|
||||
getFleetVehicles,
|
||||
getFleetVINs,
|
||||
addFleetVehicles,
|
||||
deleteFleetVehicles,
|
||||
removeFleetVehiclesLocal,
|
||||
|
||||
@@ -18,7 +18,7 @@ const DropDownButton = ({ actions = [], payload = [], onClick = () => { } }) =>
|
||||
|
||||
const handleClick = () => {
|
||||
onClick()
|
||||
actions[selectedIndex].trigger(...payload);
|
||||
actions[selectedIndex] ? actions[selectedIndex].trigger(...payload) : actions[0].trigger(...payload)
|
||||
};
|
||||
|
||||
const handleMenuItemClick = (event, index) => {
|
||||
@@ -55,9 +55,9 @@ const DropDownButton = ({ actions = [], payload = [], onClick = () => { } }) =>
|
||||
>
|
||||
<Button
|
||||
onClick={handleClick}
|
||||
disabled={actions[selectedIndex].disabled}
|
||||
disabled={actions[selectedIndex] ? actions[selectedIndex].disabled : actions[0].disabled}
|
||||
>
|
||||
{actions[selectedIndex].name}
|
||||
{actions[selectedIndex] ? actions[selectedIndex].name : actions[0].name}
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
|
||||
@@ -38,47 +38,6 @@ exports[`FleetVehiclesTable Render 1`] = `
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="MuiGrid-root makeStyles-textJustifyAlign-0 MuiGrid-item MuiGrid-grid-md-2"
|
||||
>
|
||||
<label
|
||||
class="MuiFormControlLabel-root"
|
||||
>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
class="MuiButtonBase-root MuiIconButton-root PrivateSwitchBase-root-0 MuiCheckbox-root MuiCheckbox-colorSecondary MuiIconButton-colorSecondary"
|
||||
>
|
||||
<span
|
||||
class="MuiIconButton-label"
|
||||
>
|
||||
<input
|
||||
class="PrivateSwitchBase-input-0"
|
||||
data-indeterminate="false"
|
||||
type="checkbox"
|
||||
value=""
|
||||
/>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="MuiSvgIcon-root"
|
||||
focusable="false"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M19 5v14H5V5h14m0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="MuiTouchRipple-root"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="MuiTypography-root MuiFormControlLabel-label MuiTypography-body1"
|
||||
>
|
||||
Select All Vehicles
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="MuiGrid-root MuiGrid-item MuiGrid-grid-md-4"
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {
|
||||
Checkbox,
|
||||
FormControlLabel,
|
||||
Grid,
|
||||
LinearProgress,
|
||||
Table,
|
||||
@@ -71,7 +70,7 @@ const MainForm = ({ name }) => {
|
||||
const [order, setOrder] = useState("desc");
|
||||
const [search, setSearch] = useState("");
|
||||
const [selected, setSelected] = useState([]);
|
||||
const [selectAllSelected, setSelectAllSelected] = useState(false);
|
||||
const [fleetVINs, setFleetVINs] = useState([]);
|
||||
const componentMounted = useRef(true);
|
||||
const classes = useStyles();
|
||||
const { setMessage } = useStatusContext();
|
||||
@@ -80,6 +79,7 @@ const MainForm = ({ name }) => {
|
||||
totalFleetVehicles,
|
||||
watchFleetVehicles,
|
||||
getFleetVehicles,
|
||||
getFleetVINs,
|
||||
removeFleetVehiclesLocal,
|
||||
} = useFleetContext();
|
||||
const {
|
||||
@@ -106,6 +106,12 @@ const MainForm = ({ name }) => {
|
||||
},
|
||||
token
|
||||
);
|
||||
await getFleetVINs(
|
||||
name,
|
||||
token
|
||||
).then((vins) => {
|
||||
setFleetVINs(vins);
|
||||
});
|
||||
if (componentMounted.current) {
|
||||
watchFleetVehicles.start({ token });
|
||||
}
|
||||
@@ -148,19 +154,16 @@ const MainForm = ({ name }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectAll = () => {
|
||||
setSelected(fleetVINs);
|
||||
};
|
||||
|
||||
const handleSelect = (vin, setState) => {
|
||||
setSelected(selected => setState
|
||||
? [...selected, vin]
|
||||
: selected.filter(select => select !== vin));
|
||||
};
|
||||
|
||||
const handleSelectAll = (event) => {
|
||||
const allSelected = !event.target.checked;
|
||||
setSelected(() => allSelected
|
||||
? []
|
||||
: fleetVehicles.map((vehicle) => vehicle.vin));
|
||||
}
|
||||
|
||||
const bulkActionCallback = (action, _, context) => {
|
||||
if (action === "updateFleetVehicles") {
|
||||
const vinsToRemove = context.fromFleet === name ? context.fromVehicles : [];
|
||||
@@ -176,24 +179,10 @@ const MainForm = ({ name }) => {
|
||||
<AddCircleIcon fontSize="large" />
|
||||
</Link>
|
||||
</Grid>
|
||||
<Grid item md={2} className={classes.textJustifyAlign}>
|
||||
<FormControlLabel control={
|
||||
<Checkbox
|
||||
checked={selectAllSelected}
|
||||
onChange={() => setSelectAllSelected(selectAllSelected => !selectAllSelected)}
|
||||
/>
|
||||
} label="Select All Vehicles" />
|
||||
</Grid>
|
||||
<Grid item md={4}>
|
||||
<BulkActions
|
||||
actions={
|
||||
selectAllSelected ?
|
||||
["updateFlashpackNumbers"] :
|
||||
["updateFleetVehicles", "addTags", "sms", "updateConfig", "remoteCommand", "diagnostic", "updateFlashpackNumbers"]
|
||||
}
|
||||
actions={["updateFleetVehicles", "addTags", "sms", "updateConfig", "remoteCommand", "diagnostic", "updateFlashpackNumbers"]}
|
||||
ids={selected}
|
||||
fleet={name}
|
||||
selectAll={selectAllSelected}
|
||||
callback={bulkActionCallback}
|
||||
/>
|
||||
</Grid>
|
||||
@@ -210,8 +199,9 @@ const MainForm = ({ name }) => {
|
||||
onSortRequest={handleSort}
|
||||
multiSelect={true}
|
||||
onSelectAll={handleSelectAll}
|
||||
selectCount={selected.length}
|
||||
selectCount={selected ? selected.length : 0}
|
||||
rowCount={fleetVehicles.length}
|
||||
selectAllForAllPages={true}
|
||||
/>
|
||||
<TableBody>
|
||||
{fleetVehicles && fleetVehicles.map((car) => {
|
||||
|
||||
@@ -37,47 +37,6 @@ exports[`VehiclesTab Render 1`] = `
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="MuiGrid-root makeStyles-textJustifyAlign-0 MuiGrid-item MuiGrid-grid-md-2"
|
||||
>
|
||||
<label
|
||||
class="MuiFormControlLabel-root"
|
||||
>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
class="MuiButtonBase-root MuiIconButton-root PrivateSwitchBase-root-0 MuiCheckbox-root MuiCheckbox-colorSecondary MuiIconButton-colorSecondary"
|
||||
>
|
||||
<span
|
||||
class="MuiIconButton-label"
|
||||
>
|
||||
<input
|
||||
class="PrivateSwitchBase-input-0"
|
||||
data-indeterminate="false"
|
||||
type="checkbox"
|
||||
value=""
|
||||
/>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="MuiSvgIcon-root"
|
||||
focusable="false"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M19 5v14H5V5h14m0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="MuiTouchRipple-root"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="MuiTypography-root MuiFormControlLabel-label MuiTypography-body1"
|
||||
>
|
||||
Select All Vehicles
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="MuiGrid-root MuiGrid-item MuiGrid-grid-md-4"
|
||||
/>
|
||||
|
||||
@@ -6,9 +6,10 @@ import {
|
||||
TableSortLabel
|
||||
} from "@material-ui/core";
|
||||
import PropTypes from "prop-types";
|
||||
import React from "react";
|
||||
import React, { useState } from "react";
|
||||
|
||||
const HeaderSortable = (props) => {
|
||||
const [selectAll, setSelectAll] = useState(false);
|
||||
const {
|
||||
classes,
|
||||
order,
|
||||
@@ -19,6 +20,7 @@ const HeaderSortable = (props) => {
|
||||
onSelectAll,
|
||||
selectCount,
|
||||
rowCount,
|
||||
selectAllForAllPages,
|
||||
} = props;
|
||||
|
||||
const sortHandler = (property) => (event) => {
|
||||
@@ -27,6 +29,7 @@ const HeaderSortable = (props) => {
|
||||
};
|
||||
|
||||
const selectAllHandler = (event) => {
|
||||
setSelectAll(selectAll => !selectAll);
|
||||
if (!onSelectAll) return;
|
||||
onSelectAll(event);
|
||||
};
|
||||
@@ -71,8 +74,8 @@ const HeaderSortable = (props) => {
|
||||
{multiSelect && (
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
indeterminate={selectCount > 0 && selectCount < rowCount}
|
||||
checked={rowCount > 0 && selectCount === rowCount}
|
||||
indeterminate={!selectAllForAllPages && selectCount > 0 && selectCount < rowCount}
|
||||
checked={selectAllForAllPages ? selectAll : rowCount > 0 && selectCount === rowCount}
|
||||
onChange={selectAllHandler}
|
||||
inputProps={{ "aria-label": "select all items" }}
|
||||
/>
|
||||
|
||||
@@ -341,16 +341,8 @@ const vehiclesAPI = {
|
||||
.catch(errorHandler)
|
||||
},
|
||||
|
||||
flashpackVersionBulkUpdate: async (selectAll, fleet, vins, token) => {
|
||||
var url = `${API_ENDPOINT}/flashpack_version_bulk_update`
|
||||
const queryParams = {
|
||||
fleet,
|
||||
};
|
||||
if (selectAll) {
|
||||
url = addQueryParams(url, queryParams);
|
||||
}
|
||||
|
||||
return fetch(url, {
|
||||
flashpackVersionBulkUpdate: async (vins, token) => {
|
||||
return fetch(`${API_ENDPOINT}/flashpack_version_bulk_update`, {
|
||||
method: "PUT",
|
||||
headers: Object.assign(
|
||||
{ "Content-Type": "application/json" },
|
||||
|
||||
Reference in New Issue
Block a user