diff --git a/src/components/App/__snapshots__/App.test.js.snap b/src/components/App/__snapshots__/App.test.js.snap
index c691a1a..e4a3185 100644
--- a/src/components/App/__snapshots__/App.test.js.snap
+++ b/src/components/App/__snapshots__/App.test.js.snap
@@ -6819,6 +6819,7 @@ exports[`App Route /packages authenticated 1`] = `
+
diff --git a/src/components/Manifest/List/index.jsx b/src/components/Manifest/List/index.jsx
index ad52162..da6dd49 100644
--- a/src/components/Manifest/List/index.jsx
+++ b/src/components/Manifest/List/index.jsx
@@ -21,7 +21,6 @@ import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import EditIcon from "@material-ui/icons/Edit";
-import { useArchiveManifests } from "../../../hooks";
import { logger } from "../../../services/monitoring";
import { LocalDateTimeString } from "../../../utils/dates";
import { TYPE_MANIFEST_AFTERSALES, TYPE_MANIFEST_CONFIG, TYPE_MANIFEST_SOFTWARE } from "../../../utils/manifest_types";
@@ -40,6 +39,8 @@ import DeleteConfirmation from "../../DeleteConfirmation";
import TableHeaderSortable from "../../Table/HeaderSortable";
import { useLocalStorage } from "../../useLocalStorage";
import useStyles from "../../useStyles";
+import { useUpdateManifest } from "../../../hooks";
+import GeneralConfirmation from "../../GeneralConfirmation";
const tableColumns = [
{
@@ -114,10 +115,10 @@ const MainForm = () => {
const [order, setOrder] = useState("asc");
const [search, setSearch] = useLocalStorage("DEPLOYMENT_SEARCH", "");
const [active, setActive] = useLocalStorage("DEPLOYMENT_TAB_TOGGLE", "software");
- const [selected, setSelected] = useState([]);
const [showDeleteModal, setShowDeleteModal] = useState(false);
- const [deleteRowName, setDeleteRowName] = useState("");
+ const [showArchiveModal, setShowArchiveModal] = useState(false);
+ const [archiveLabel, setArchiveLabel] = useState("Archive");
const { getManifests, manifests, totalManifests } =
useManifestsContext();
@@ -129,7 +130,13 @@ const MainForm = () => {
groups,
providers,
} = useUserContext();
- const { archive } = useArchiveManifests(token);
+ const {
+ remove,
+ archive,
+ updateManifestIds,
+ setUpdateManifestIds,
+ setMakeActive,
+ } = useUpdateManifest(token);
const sortHandler = (event, property) => {
if (property === orderBy) {
@@ -227,7 +234,11 @@ const MainForm = () => {
}
})();
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [pageIndex, pageSize, token, orderBy, order, search, active]);
+ }, [pageIndex, pageSize, token, orderBy, order, search, active, updateManifestIds]);
+
+ useEffect(() => {
+ setUpdateManifestIds([]);
+ }, [active, setUpdateManifestIds]);
const handleChangePageIndex = (_event, newIndex) => {
setPageIndex(newIndex);
@@ -245,20 +256,28 @@ const MainForm = () => {
const handleActiveChange = (event, newAlignment) => {
if (newAlignment !== null) {
- setActive(newAlignment)
+ setActive(newAlignment);
+ setMakeActive(newAlignment === 'archived');
+ setArchiveLabel(() => {
+ if (newAlignment === "archived") {
+ return "Activate";
+ }
+
+ return "Archive";
+ });
}
}
const handleSelectAll = () => {
- setSelected((selected) => selected.length ? [] : manifests);
+ setUpdateManifestIds((selected) => selected.length ? [] : manifests.map((manifest) => manifest.id));
};
const handleSelect = (event, manifest) => {
- setSelected((selected) => {
- if (event.target.checked && selected.find(({ id }) => id === manifest.id)) {
+ setUpdateManifestIds((selected) => {
+ if (event.target.checked && selected.find((id) => id === manifest.id)) {
return selected;
} else if (event.target.checked) {
- return [...selected, manifest];
+ return [...selected, manifest.id];
}
return selected.filter(({ id }) => id !== manifest.id);
});
@@ -269,11 +288,12 @@ const MainForm = () => {
setShowDeleteModal(true);
};
- const onDelete = async () => {
+ const onArchive = async () => {
try {
- await archive(selected.map((manifest) => manifest.id))
- .then(({ summary }) => {
- setMessage(summary);
+ await archive()
+ .then(({ message }) => {
+ setUpdateManifestIds([]);
+ setMessage(message);
});
} catch (e) {
setMessage(e.message);
@@ -281,12 +301,18 @@ const MainForm = () => {
}
};
- useEffect(() => {
- setDeleteRowName(() => selected
- .map((manifest) => `${manifest.name} ${manifest.version}`)
- .join(", ")
- );
- }, [selected]);
+ const onDelete = async () => {
+ try {
+ await remove()
+ .then(({ summary }) => {
+ setUpdateManifestIds([]);
+ setMessage(summary);
+ });
+ } catch (e) {
+ setMessage(e.message);
+ logger.warn(e.stack);
+ }
+ };
const Actions = (row) => {
let actions = [];
@@ -372,9 +398,9 @@ const MainForm = () => {
setShowDeleteModal(true),
- disabled: !selected.length,
+ name: archiveLabel,
+ trigger: () => setShowArchiveModal(true),
+ disabled: !updateManifestIds.length || active === "all",
}
]}
/>
@@ -389,13 +415,13 @@ const MainForm = () => {
onSortRequest={sortHandler}
multiSelect
onSelectAll={handleSelectAll}
- selectCount={selected ? selected.length : 0}
+ selectCount={updateManifestIds ? updateManifestIds.length : 0}
rowCount={manifests ? manifests.length : 0}
/>
{manifests.map((row) => {
- const isSelected = selected
- ? !!selected.find(({ id }) => id === row.id)
+ const isSelected = updateManifestIds
+ ? !!updateManifestIds.find((id) => id === row.id)
: false;
return (
@@ -457,11 +483,18 @@ const MainForm = () => {
setShowDeleteModal(false)}
deleteFunction={() => onDelete()}
/>
+ setShowArchiveModal(false)}
+ actionFunction={() => onArchive()}
+ />
);
};
diff --git a/src/components/Manifest/List/index.test.jsx b/src/components/Manifest/List/index.test.jsx
new file mode 100644
index 0000000..e7f2ab4
--- /dev/null
+++ b/src/components/Manifest/List/index.test.jsx
@@ -0,0 +1,35 @@
+jest.mock("../../Contexts/ManifestsContext");
+jest.mock("../../Contexts/UserContext");
+
+import React from "react";
+import { render, screen, fireEvent } from "@testing-library/react";
+import { BrowserRouter } from "react-router-dom";
+
+import { UserProvider, setToken } from "../../Contexts/UserContext";
+import { StatusProvider } from "../../Contexts/StatusContext";
+import ManifestList from ".";
+import { TEST_AUTH_OBJECT_FISKER } from "../../../utils/testing";
+
+const Page = (
+
+
+
+
+
+
+
+);
+
+describe("Manifest List Component", () => {
+ beforeAll(() => {
+ setToken(TEST_AUTH_OBJECT_FISKER);
+ });
+
+ it("adjusts the active state on switch to archived tab", async () => {
+ render(Page);
+
+ const archiveActionEl = screen.getByText("Archive");
+ fireEvent.click(screen.getByText("Archived"));
+ expect(archiveActionEl.innerHTML).toBe("Activate");
+ });
+});
diff --git a/src/hooks/index.js b/src/hooks/index.js
index 7b34538..5ee20bb 100644
--- a/src/hooks/index.js
+++ b/src/hooks/index.js
@@ -1 +1 @@
-export { useArchiveManifests } from "./useArchiveManifests";
\ No newline at end of file
+export { useUpdateManifest } from "./useUpdateManifest";
\ No newline at end of file
diff --git a/src/hooks/useArchiveManifests.js b/src/hooks/useArchiveManifests.js
deleted file mode 100644
index dc6a2d2..0000000
--- a/src/hooks/useArchiveManifests.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import manifestsAPI from "../services/manifestsAPI";
-import TaskRunner from "../utils/taskRunner";
-
-export const useArchiveManifests = (token) => {
-
- const archive = async (ids) => {
- return new Promise((resolve) => {
- const taskRunner = new TaskRunner(5, ids.length);
- let errorCount = 0;
-
- const task = (id) => {
- return async () => manifestsAPI.deleteManifest(id, token);
- }
-
- ids.forEach((id) => taskRunner.push(task(id))
- .then((response) => {
- if (response.error) {
- errorCount += 1;
- }
- })
- );
- taskRunner.onComplete().then((responses) => resolve({
- summary: `${ids.length - errorCount} out of ${ids.length} manifests were deleted.`,
- responses,
- }));
- });
- }
-
- return {
- archive,
- };
-};
diff --git a/src/hooks/useUpdateManifest.js b/src/hooks/useUpdateManifest.js
new file mode 100644
index 0000000..39b0b9b
--- /dev/null
+++ b/src/hooks/useUpdateManifest.js
@@ -0,0 +1,47 @@
+import { useState } from "react";
+import manifestsAPI from "../services/manifestsAPI";
+import TaskRunner from "../utils/taskRunner";
+
+export const useUpdateManifest = (token) => {
+ const [updateManifestIds, setUpdateManifestIds] = useState([]);
+ const [makeActive, setMakeActive] = useState(false);
+
+ const remove = async () => {
+ return new Promise((resolve) => {
+ const taskRunner = new TaskRunner(5, updateManifestIds.length);
+ let errorCount = 0;
+
+ const task = (id) => {
+ return async () => manifestsAPI.deleteManifest(id, token);
+ }
+
+ updateManifestIds.forEach((id) => taskRunner.push(task(id))
+ .then((response) => {
+ if (response.error) {
+ errorCount += 1;
+ }
+ })
+ );
+ taskRunner.onComplete().then((responses) => resolve({
+ summary: `${updateManifestIds.length - errorCount} out of ${updateManifestIds.length} manifests were deleted.`,
+ responses,
+ }));
+ });
+ }
+
+ const archive = async () => {
+ return manifestsAPI.archiveManifest({
+ ids: updateManifestIds,
+ active: makeActive,
+ }, token);
+ }
+
+ return {
+ updateManifestIds,
+ setUpdateManifestIds,
+ makeActive,
+ setMakeActive,
+ archive,
+ remove,
+ };
+};
diff --git a/src/services/__mocks__/manifestsAPI.js b/src/services/__mocks__/manifestsAPI.js
index 6404bc5..302e6d4 100644
--- a/src/services/__mocks__/manifestsAPI.js
+++ b/src/services/__mocks__/manifestsAPI.js
@@ -5,6 +5,10 @@ const manifestsAPI = {
return data;
},
+ archiveManifest: async (data, token) => {
+ return { message: "Archived 1 update manifests" };
+ },
+
deleteManifest: async (manifest_id, token) => {
return { message: "OK" };
},
diff --git a/src/services/manifestsAPI.js b/src/services/manifestsAPI.js
index 8fbe7a8..25782f7 100644
--- a/src/services/manifestsAPI.js
+++ b/src/services/manifestsAPI.js
@@ -1,10 +1,22 @@
import {
- addQueryParams, errorHandler, fetchRespHandler, getAuthHeaderOptions
+ addQueryParams, errorHandler, fetchRespHandler, getAuthHeaderOptions
} from "../utils/http";
const API_ENDPOINT = process.env.REACT_APP_OTA_SERVICE_URL;
const manifestsAPI = {
+ archiveManifest: async (data, token) =>
+ fetch(`${API_ENDPOINT}/vehicles/archive`, {
+ method: "PUT",
+ headers: Object.assign(
+ { "Content-Type": "application/json" },
+ getAuthHeaderOptions(token)
+ ),
+ body: JSON.stringify(data)
+ })
+ .then(fetchRespHandler)
+ .catch(errorHandler),
+
deleteManifest: async (manifest_id, token) =>
fetch(`${API_ENDPOINT}/manifest?id=${manifest_id}`, {
method: "DELETE",
@@ -79,8 +91,8 @@ const manifestsAPI = {
.then(fetchRespHandler)
.catch(errorHandler),
- migrateManifest: async (manifest_id, token) =>
- fetch(`${API_ENDPOINT}/manifestmigrate/${manifest_id}`,{
+ migrateManifest: async (manifest_id, token) =>
+ fetch(`${API_ENDPOINT}/manifestmigrate/${manifest_id}`, {
method: "POST",
headers: Object.assign(
{ "Content-Type": "application/json" },