implemented bulk deletion of cases
This commit is contained in:
@@ -55,11 +55,13 @@ const casesShowRoute = require("./routes/cases/show")(sequelize);
|
|||||||
const casesNewRoute = require("./routes/cases/new")(sequelize);
|
const casesNewRoute = require("./routes/cases/new")(sequelize);
|
||||||
const casesEditRoute = require("./routes/cases/edit")(sequelize);
|
const casesEditRoute = require("./routes/cases/edit")(sequelize);
|
||||||
const casesDeleteRoute = require("./routes/cases/delete")(sequelize);
|
const casesDeleteRoute = require("./routes/cases/delete")(sequelize);
|
||||||
|
const casesBulkDeleteRoute = require("./routes/cases/bulkDelete")(sequelize);
|
||||||
app.use("/cases", casesIndexRoute);
|
app.use("/cases", casesIndexRoute);
|
||||||
app.use("/cases", casesShowRoute);
|
app.use("/cases", casesShowRoute);
|
||||||
app.use("/cases", casesNewRoute);
|
app.use("/cases", casesNewRoute);
|
||||||
app.use("/cases", casesEditRoute);
|
app.use("/cases", casesEditRoute);
|
||||||
app.use("/cases", casesDeleteRoute);
|
app.use("/cases", casesDeleteRoute);
|
||||||
|
app.use("/cases", casesBulkDeleteRoute);
|
||||||
|
|
||||||
// "/steps"
|
// "/steps"
|
||||||
const stepsNewRoute = require("./routes/steps/new")(sequelize);
|
const stepsNewRoute = require("./routes/steps/new")(sequelize);
|
||||||
|
|||||||
25
backend/routes/cases/bulkDelete.js
Normal file
25
backend/routes/cases/bulkDelete.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
const express = require("express");
|
||||||
|
const router = express.Router();
|
||||||
|
const defineCase = require("../../models/cases");
|
||||||
|
const { DataTypes } = require("sequelize");
|
||||||
|
|
||||||
|
module.exports = function (sequelize) {
|
||||||
|
const Case = defineCase(sequelize, DataTypes);
|
||||||
|
|
||||||
|
router.post("/bulkdelete", async (req, res) => {
|
||||||
|
const { caseIds } = req.body;
|
||||||
|
if (!caseIds || !Array.isArray(caseIds)) {
|
||||||
|
return res.status(400).send("Invalid caseIds array");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Case.destroy({ where: { id: caseIds } });
|
||||||
|
res.status(204).send();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
res.status(500).send("Internal Server Error");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return router;
|
||||||
|
};
|
||||||
@@ -89,6 +89,28 @@ async function fetchDeleteCase(caseId: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fetchDeleteCases(deleteCases: string[]) {
|
||||||
|
const fetchOptions = {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ caseIds: deleteCases }),
|
||||||
|
};
|
||||||
|
|
||||||
|
const url = `${apiServer}/cases/bulkdelete`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(url, fetchOptions);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error deleting cases:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default function Page({
|
export default function Page({
|
||||||
params,
|
params,
|
||||||
}: {
|
}: {
|
||||||
@@ -123,6 +145,12 @@ export default function Page({
|
|||||||
setCases(data);
|
setCases(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleDeleteCases = async (deleteCases: string[]) => {
|
||||||
|
await fetchDeleteCases(deleteCases);
|
||||||
|
const data = await fetchCases(url);
|
||||||
|
setCases(data);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TestCaseTable
|
<TestCaseTable
|
||||||
@@ -130,6 +158,7 @@ export default function Page({
|
|||||||
cases={cases}
|
cases={cases}
|
||||||
onCreateCase={() => handleCreateCase(params.folderId)}
|
onCreateCase={() => handleCreateCase(params.folderId)}
|
||||||
onDeleteCase={handleDeleteCase}
|
onDeleteCase={handleDeleteCase}
|
||||||
|
onDeleteCases={handleDeleteCases}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import {
|
|||||||
SortDescriptor,
|
SortDescriptor,
|
||||||
Link,
|
Link,
|
||||||
} from "@nextui-org/react";
|
} from "@nextui-org/react";
|
||||||
import { Plus, MoreVertical } from "lucide-react";
|
import { Plus, MoreVertical, Trash } from "lucide-react";
|
||||||
|
|
||||||
const headerColumns = [
|
const headerColumns = [
|
||||||
{ name: "ID", uid: "id", sortable: true },
|
{ name: "ID", uid: "id", sortable: true },
|
||||||
@@ -48,9 +48,16 @@ type Props = {
|
|||||||
cases: Case[];
|
cases: Case[];
|
||||||
onCreateCase: () => void;
|
onCreateCase: () => void;
|
||||||
onDeleteCase: (caseId: number) => void;
|
onDeleteCase: (caseId: number) => void;
|
||||||
|
onDeleteCases: (selectedCases: string[]) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function TestCaseTable({ projectId, cases, onCreateCase, onDeleteCase }: Props) {
|
export default function TestCaseTable({
|
||||||
|
projectId,
|
||||||
|
cases,
|
||||||
|
onCreateCase,
|
||||||
|
onDeleteCase,
|
||||||
|
onDeleteCases,
|
||||||
|
}: Props) {
|
||||||
const [selectedKeys, setSelectedKeys] = useState<Selection>(new Set([]));
|
const [selectedKeys, setSelectedKeys] = useState<Selection>(new Set([]));
|
||||||
const [sortDescriptor, setSortDescriptor] = useState<SortDescriptor>({
|
const [sortDescriptor, setSortDescriptor] = useState<SortDescriptor>({
|
||||||
column: "id",
|
column: "id",
|
||||||
@@ -102,7 +109,10 @@ export default function TestCaseTable({ projectId, cases, onCreateCase, onDelete
|
|||||||
</Button>
|
</Button>
|
||||||
</DropdownTrigger>
|
</DropdownTrigger>
|
||||||
<DropdownMenu aria-label="test case actions">
|
<DropdownMenu aria-label="test case actions">
|
||||||
<DropdownItem className="text-danger" onClick={() => onDeleteCase(testCase.id)}>
|
<DropdownItem
|
||||||
|
className="text-danger"
|
||||||
|
onClick={() => onDeleteCase(testCase.id)}
|
||||||
|
>
|
||||||
Delete test case
|
Delete test case
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
@@ -132,12 +142,27 @@ export default function TestCaseTable({ projectId, cases, onCreateCase, onDelete
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onDeleteCasesClick = async() => {
|
||||||
|
setSelectedKeys(new Set([]));
|
||||||
|
onDeleteCases([...selectedKeys])
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="border-b-1 dark:border-neutral-700 w-full p-3 flex items-center justify-between">
|
<div className="border-b-1 dark:border-neutral-700 w-full p-3 flex items-center justify-between">
|
||||||
<h3 className="font-bold">
|
<h3 className="font-bold">Cases</h3>
|
||||||
Cases
|
<div>
|
||||||
</h3>
|
{selectedKeys.size > 0 && (
|
||||||
|
<Button
|
||||||
|
startContent={<Trash size={16} />}
|
||||||
|
size="sm"
|
||||||
|
color="danger"
|
||||||
|
className="me-2"
|
||||||
|
onClick={onDeleteCasesClick}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
<Button
|
<Button
|
||||||
startContent={<Plus size={16} />}
|
startContent={<Plus size={16} />}
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -147,6 +172,7 @@ export default function TestCaseTable({ projectId, cases, onCreateCase, onDelete
|
|||||||
New Test Case
|
New Test Case
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Table
|
<Table
|
||||||
isCompact
|
isCompact
|
||||||
|
|||||||
Reference in New Issue
Block a user