diff --git a/backend/database.scheme.sql b/backend/database.scheme.sql index ecc6552..05ed839 100644 --- a/backend/database.scheme.sql +++ b/backend/database.scheme.sql @@ -35,6 +35,7 @@ CREATE TABLE IF NOT EXISTS products ( picture VARCHAR(500) DEFAULT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + deleted BOOLEAN DEFAULT FALSE NOT NULL, FOREIGN KEY (storage_location) REFERENCES storage_locations(uuid) ON DELETE CASCADE ); diff --git a/backend/routes/app/database/products.database.js b/backend/routes/app/database/products.database.js index 515f354..f6cd75d 100644 --- a/backend/routes/app/database/products.database.js +++ b/backend/routes/app/database/products.database.js @@ -90,6 +90,7 @@ export const allProducts = async () => { p.updated_at FROM products p JOIN storage_locations s ON p.storage_location = s.uuid + WHERE p.deleted = 0 `); if (result.length > 0) { @@ -137,3 +138,16 @@ export const updateItem = async (itemUUID, newValues) => { return { code: "ep005" }; // error } }; + +export const deleteProduct = async (uuid) => { + const [result] = await pool.query( + `UPDATE products SET deleted = 1 WHERE uuid = UUID_TO_BIN(?);`, + [uuid], + ); + + if (result.affectedRows > 0) { + return { code: "sp006" }; // success + } else { + return { code: "ep006" }; // error + } +}; diff --git a/backend/routes/app/products.route.js b/backend/routes/app/products.route.js index d6d7410..6c267ec 100644 --- a/backend/routes/app/products.route.js +++ b/backend/routes/app/products.route.js @@ -3,6 +3,7 @@ import dotenv from "dotenv"; import { authenticate } from "../../services/tokenService.js"; import { allProducts, + deleteProduct, newProduct, productDetails, setAmount, @@ -147,4 +148,33 @@ router.post("/mutate/update-item", authenticate, async (req, res) => { } }); +router.post("/delete-selection", authenticate, async (req, res) => { + let isError = false; + const uuidArray = req.body; + + for (const uuid of uuidArray) { + const response = await deleteProduct(uuid); + if (response.code === "ep006" || !response) { + isError = true; + break; + } + } + + if (isError === false) { + res.status(202).json({ + success: true, + code: "sp006", + data: null, + message: "", + }); + } else { + res.status(500).json({ + success: false, + code: "ep006", + data: null, + message: "", + }); + } +}); + export default router; diff --git a/frontend/src/pages/Inventory.tsx b/frontend/src/pages/Inventory.tsx index 2ee72aa..151d0c5 100644 --- a/frontend/src/pages/Inventory.tsx +++ b/frontend/src/pages/Inventory.tsx @@ -25,8 +25,8 @@ import FilterListIcon from "@mui/icons-material/FilterList"; import KeyboardArrowLeftIcon from "@mui/icons-material/KeyboardArrowLeft"; import KeyboardArrowRightIcon from "@mui/icons-material/KeyboardArrowRight"; import ArrowDownwardIcon from "@mui/icons-material/ArrowDownward"; -import { useQuery } from "@tanstack/react-query"; -import { getProducts } from "../utils/uxFncs"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { getProducts, deleteSelectedProducts } from "../utils/uxFncs"; import { visuallyHidden } from "@mui/utils"; import { formatDate } from "../utils/uxFncs"; import Cookies from "js-cookie"; @@ -182,8 +182,22 @@ const EnhancedTableHead = (props: EnhancedTableHeadProps) => { ); }; -const EnhancedTableToolbar = ({ numSelected }: { numSelected: number }) => { +const EnhancedTableToolbar = ({ + numSelected, + selected, +}: { + numSelected: number; + selected: readonly string[]; +}) => { const { t } = useTranslation(); + const queryClient = useQueryClient(); + + const { mutate } = useMutation({ + mutationFn: (values: string[]) => deleteSelectedProducts(values), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["products"] }); + }, + }); return ( { > {numSelected > 0 ? ( - {numSelected} ausgewahlt + {numSelected} {t("selected")} ) : ( - + {t("inventory")} )} {numSelected > 0 ? ( - + mutate([...selected])} + size="sm" + color="danger" + variant="solid" + > @@ -346,7 +370,10 @@ export const InventoryPage = () => { variant="outlined" className="mt-6 overflow-hidden rounded-2xl border border-slate-200 bg-white/80 shadow-sm" > - + { return { success: true, data: response.data, code: response.code }; } }; + +export const deleteSelectedProducts = async (uuids: string[]) => { + const result = await fetch(`${API_BASE}/products/delete-selection`, { + method: "POST", + body: JSON.stringify(uuids), + headers: { + Authorization: `Bearer ${Cookies.get("token") || ""}`, + "Content-Type": "application/json", + Accept: "application/json", + }, + }); + + const response = await result.json(); + + console.log(response); +};