CEC-4594: add bulk actions to fleet (#368)
* CEC-4594: add bulk actions to fleet * add reject case
This commit is contained in:
84
src/components/BulkActions/index.jsx
Normal file
84
src/components/BulkActions/index.jsx
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import TransformModal from "../TransformModal";
|
||||||
|
import DropDownButton from "../Controls/DropDownButton";
|
||||||
|
import { useUserContext } from "../Contexts/UserContext";
|
||||||
|
import { useStatusContext } from "../Contexts/StatusContext";
|
||||||
|
import useAddTags from "./useAddTags";
|
||||||
|
import useUpdateConfig from "./useUpdateConfig";
|
||||||
|
|
||||||
|
const transformArrayToCSV = (arr) => arr.join(", ");
|
||||||
|
|
||||||
|
export default function BulkActions({
|
||||||
|
vins = [],
|
||||||
|
}) {
|
||||||
|
const [vinCSV, setVinCSV] = useState(transformArrayToCSV(vins));
|
||||||
|
const [active, setActive] = useState(null);
|
||||||
|
const actions = [
|
||||||
|
{
|
||||||
|
name: "Update Configs",
|
||||||
|
disabled: vins.length === 0,
|
||||||
|
trigger: () => setActive("updateConfig"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Add Tags",
|
||||||
|
disabled: vins.length === 0,
|
||||||
|
trigger: () => setActive("addTags"),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const updateConfig = useUpdateConfig();
|
||||||
|
const addTags = useAddTags();
|
||||||
|
|
||||||
|
const { setMessage } = useStatusContext();
|
||||||
|
const {
|
||||||
|
token: {
|
||||||
|
idToken: { jwtToken: token },
|
||||||
|
},
|
||||||
|
} = useUserContext();
|
||||||
|
|
||||||
|
const handleUpdateConfig = () => {
|
||||||
|
updateConfig.submit(vins, token)
|
||||||
|
.then(() => {
|
||||||
|
setMessage(`${vins.length} vehicles updated.`);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
setMessage(error.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleAddTags = () => {
|
||||||
|
addTags.submit(vins, token)
|
||||||
|
.then(() => setMessage(`Added ${addTags.data.tags.value.length} tags to ${vins.length} vehicles.`))
|
||||||
|
.catch((error) => setMessage(error.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClose = () => setActive(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setVinCSV(transformArrayToCSV(vins));
|
||||||
|
}, [vins]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<DropDownButton actions={actions} payload={[vins]} />
|
||||||
|
<TransformModal
|
||||||
|
title="Update Config"
|
||||||
|
body={`You are updating the config for the following VINs: ${vinCSV}.`}
|
||||||
|
close={handleClose}
|
||||||
|
open={active === "updateConfig"}
|
||||||
|
data={updateConfig.data}
|
||||||
|
setData={updateConfig.setData}
|
||||||
|
submit={handleUpdateConfig}
|
||||||
|
/>
|
||||||
|
<TransformModal
|
||||||
|
title="Add Tags"
|
||||||
|
body={`You are adding tags for the following VINs: ${vinCSV}.`}
|
||||||
|
close={handleClose}
|
||||||
|
open={active === "addTags"}
|
||||||
|
data={addTags.data}
|
||||||
|
setData={addTags.setData}
|
||||||
|
submit={handleAddTags}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
22
src/components/BulkActions/useAddTags.js
Normal file
22
src/components/BulkActions/useAddTags.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import vehiclesAPI from "../../services/vehiclesAPI";
|
||||||
|
|
||||||
|
export default function useAddTags() {
|
||||||
|
const [tags, setTags] = useState({
|
||||||
|
tags: {
|
||||||
|
label: "Tags",
|
||||||
|
type: "list.string",
|
||||||
|
value: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const submit = async (vins, token) => {
|
||||||
|
return vehiclesAPI.addTags(vins, tags.tags.value, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: tags,
|
||||||
|
setData: setTags,
|
||||||
|
submit,
|
||||||
|
};
|
||||||
|
}
|
||||||
40
src/components/BulkActions/useUpdateConfig.js
Normal file
40
src/components/BulkActions/useUpdateConfig.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import TaskRunner from "../../utils/taskRunner";
|
||||||
|
import vehiclesAPI from "../../services/vehiclesAPI";
|
||||||
|
|
||||||
|
export default function useUpdateConfig() {
|
||||||
|
const [config, setConfig] = useState({
|
||||||
|
force: {
|
||||||
|
label: "Force Push",
|
||||||
|
type: "boolean",
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const submit = async (vins, token) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const taskRunner = new TaskRunner(5);
|
||||||
|
|
||||||
|
const task = (vin, isLast) => {
|
||||||
|
return async () => vehiclesAPI.updateConfig(vin, config.force.value, token)
|
||||||
|
.then((response) => {
|
||||||
|
if (isLast) {
|
||||||
|
if (response.error) {
|
||||||
|
reject(response);
|
||||||
|
}
|
||||||
|
resolve(response)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => reject(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
vins.forEach((vin, index) => taskRunner.push(task(vin, index === vins.length - 1)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: config,
|
||||||
|
setData: setConfig,
|
||||||
|
submit,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -40,6 +40,10 @@ const DropDownButton = ({ actions = [], payload = [] }) => {
|
|||||||
setOpen(false);
|
setOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!actions.length) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ButtonGroup
|
<ButtonGroup
|
||||||
|
|||||||
@@ -143,6 +143,48 @@ exports[`FleetDetailsTab Render 1`] = `
|
|||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="MuiGrid-root makeStyles-textCenterAlign-0 MuiGrid-item MuiGrid-grid-md-12"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
aria-label="choose action"
|
||||||
|
class="MuiButtonGroup-root MuiButtonGroup-contained css-zqcytf-MuiButtonGroup-root"
|
||||||
|
role="group"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="MuiButtonBase-root MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium MuiButtonGroup-grouped MuiButtonGroup-groupedHorizontal MuiButtonGroup-groupedContained MuiButtonGroup-groupedContainedHorizontal MuiButtonGroup-groupedContainedPrimary MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium MuiButtonGroup-grouped MuiButtonGroup-groupedHorizontal MuiButtonGroup-groupedContained MuiButtonGroup-groupedContainedHorizontal MuiButtonGroup-groupedContainedPrimary css-sghohy-MuiButtonBase-root-MuiButton-root"
|
||||||
|
tabindex="0"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
Update Configs
|
||||||
|
<span
|
||||||
|
class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
aria-haspopup="menu"
|
||||||
|
aria-label="select action"
|
||||||
|
class="MuiButtonBase-root MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeSmall MuiButton-containedSizeSmall MuiButtonGroup-grouped MuiButtonGroup-groupedHorizontal MuiButtonGroup-groupedContained MuiButtonGroup-groupedContainedHorizontal MuiButtonGroup-groupedContainedPrimary MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeSmall MuiButton-containedSizeSmall MuiButtonGroup-grouped MuiButtonGroup-groupedHorizontal MuiButtonGroup-groupedContained MuiButtonGroup-groupedContainedHorizontal MuiButtonGroup-groupedContainedPrimary css-11qr2p8-MuiButtonBase-root-MuiButton-root"
|
||||||
|
tabindex="0"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium css-i4bv87-MuiSvgIcon-root"
|
||||||
|
data-testid="ArrowDropDownIcon"
|
||||||
|
focusable="false"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="m7 10 5 5 5-5z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span
|
||||||
|
class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div />
|
<div />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { FleetProvider, useFleetContext } from "../../../Contexts/FleetContext"
|
|||||||
import useStyles from "../../../useStyles";
|
import useStyles from "../../../useStyles";
|
||||||
import { logger } from "../../../../services/monitoring";
|
import { logger } from "../../../../services/monitoring";
|
||||||
import DeleteConfirmation from "../../../DeleteConfirmation";
|
import DeleteConfirmation from "../../../DeleteConfirmation";
|
||||||
|
import BulkActions from "../../../BulkActions";
|
||||||
|
|
||||||
const MainForm = ({ name }) => {
|
const MainForm = ({ name }) => {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
@@ -94,6 +95,9 @@ const MainForm = ({ name }) => {
|
|||||||
</Link>
|
</Link>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid item md={12} className={classes.textCenterAlign}>
|
||||||
|
<BulkActions vins={fleet.vehicles} />
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<DeleteConfirmation message={name} open={showDeleteModal} close={() => setShowDeleteModal(false)} deleteFunction={onDelete} />
|
<DeleteConfirmation message={name} open={showDeleteModal} close={() => setShowDeleteModal(false)} deleteFunction={onDelete} />
|
||||||
</div >
|
</div >
|
||||||
|
|||||||
@@ -151,6 +151,48 @@ exports[`DetailsTab Render 1`] = `
|
|||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="MuiGrid-root makeStyles-textCenterAlign-0 MuiGrid-item MuiGrid-grid-md-12"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
aria-label="choose action"
|
||||||
|
class="MuiButtonGroup-root MuiButtonGroup-contained css-zqcytf-MuiButtonGroup-root"
|
||||||
|
role="group"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="MuiButtonBase-root MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium MuiButtonGroup-grouped MuiButtonGroup-groupedHorizontal MuiButtonGroup-groupedContained MuiButtonGroup-groupedContainedHorizontal MuiButtonGroup-groupedContainedPrimary MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium MuiButtonGroup-grouped MuiButtonGroup-groupedHorizontal MuiButtonGroup-groupedContained MuiButtonGroup-groupedContainedHorizontal MuiButtonGroup-groupedContainedPrimary css-sghohy-MuiButtonBase-root-MuiButton-root"
|
||||||
|
tabindex="0"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
Update Configs
|
||||||
|
<span
|
||||||
|
class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
aria-haspopup="menu"
|
||||||
|
aria-label="select action"
|
||||||
|
class="MuiButtonBase-root MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeSmall MuiButton-containedSizeSmall MuiButtonGroup-grouped MuiButtonGroup-groupedHorizontal MuiButtonGroup-groupedContained MuiButtonGroup-groupedContainedHorizontal MuiButtonGroup-groupedContainedPrimary MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeSmall MuiButton-containedSizeSmall MuiButtonGroup-grouped MuiButtonGroup-groupedHorizontal MuiButtonGroup-groupedContained MuiButtonGroup-groupedContainedHorizontal MuiButtonGroup-groupedContainedPrimary css-11qr2p8-MuiButtonBase-root-MuiButton-root"
|
||||||
|
tabindex="0"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium css-i4bv87-MuiSvgIcon-root"
|
||||||
|
data-testid="ArrowDropDownIcon"
|
||||||
|
focusable="false"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="m7 10 5 5 5-5z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span
|
||||||
|
class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div />
|
<div />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -239,6 +239,48 @@ exports[`FleetStatus Render 1`] = `
|
|||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="MuiGrid-root makeStyles-textCenterAlign-0 MuiGrid-item MuiGrid-grid-md-12"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
aria-label="choose action"
|
||||||
|
class="MuiButtonGroup-root MuiButtonGroup-contained css-zqcytf-MuiButtonGroup-root"
|
||||||
|
role="group"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="MuiButtonBase-root MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium MuiButtonGroup-grouped MuiButtonGroup-groupedHorizontal MuiButtonGroup-groupedContained MuiButtonGroup-groupedContainedHorizontal MuiButtonGroup-groupedContainedPrimary MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium MuiButtonGroup-grouped MuiButtonGroup-groupedHorizontal MuiButtonGroup-groupedContained MuiButtonGroup-groupedContainedHorizontal MuiButtonGroup-groupedContainedPrimary css-sghohy-MuiButtonBase-root-MuiButton-root"
|
||||||
|
tabindex="0"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
Update Configs
|
||||||
|
<span
|
||||||
|
class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
aria-haspopup="menu"
|
||||||
|
aria-label="select action"
|
||||||
|
class="MuiButtonBase-root MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeSmall MuiButton-containedSizeSmall MuiButtonGroup-grouped MuiButtonGroup-groupedHorizontal MuiButtonGroup-groupedContained MuiButtonGroup-groupedContainedHorizontal MuiButtonGroup-groupedContainedPrimary MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeSmall MuiButton-containedSizeSmall MuiButtonGroup-grouped MuiButtonGroup-groupedHorizontal MuiButtonGroup-groupedContained MuiButtonGroup-groupedContainedHorizontal MuiButtonGroup-groupedContainedPrimary css-11qr2p8-MuiButtonBase-root-MuiButton-root"
|
||||||
|
tabindex="0"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium css-i4bv87-MuiSvgIcon-root"
|
||||||
|
data-testid="ArrowDropDownIcon"
|
||||||
|
focusable="false"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="m7 10 5 5 5-5z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span
|
||||||
|
class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div />
|
<div />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user