diff --git a/backend/routes/app/storage.route.js b/backend/routes/app/storage.route.js
index 02f9834..5cebd5c 100644
--- a/backend/routes/app/storage.route.js
+++ b/backend/routes/app/storage.route.js
@@ -34,6 +34,16 @@ router.get("/all-storages", authenticate, async (req, res) => {
router.post("/new-storage", authenticate, async (req, res) => {
const { name, description } = req.body;
+ if (!name || !description) {
+ res.status(400).json({
+ success: false,
+ code: "es000",
+ data: null,
+ message: "invalid request body",
+ });
+ return;
+ }
+
const result = await newStorage(name, description);
if (result.code === "es002") {
diff --git a/frontend/src/components/StorageRow.tsx b/frontend/src/components/StorageRow.tsx
index 3f478d7..f641f0e 100644
--- a/frontend/src/components/StorageRow.tsx
+++ b/frontend/src/components/StorageRow.tsx
@@ -66,11 +66,18 @@ export const StorageRow = ({ storage }: StorageRowProps) => {
{formatDate(storage.updated_at)} |
+
|
);
diff --git a/frontend/src/components/modals/AddStorageModal.tsx b/frontend/src/components/modals/AddStorageModal.tsx
new file mode 100644
index 0000000..eebf46d
--- /dev/null
+++ b/frontend/src/components/modals/AddStorageModal.tsx
@@ -0,0 +1,106 @@
+import {
+ Modal,
+ ModalDialog,
+ DialogTitle,
+ DialogContent,
+ Stack,
+ Input,
+ Button,
+ Alert,
+} from "@mui/joy";
+import { useForm } from "@tanstack/react-form";
+import { useMutation, useQueryClient } from "@tanstack/react-query";
+import { useTranslation } from "react-i18next";
+import type { NewStorage, AlertInterface } from "../../misc/interfaces";
+import { mutateNewStorage } from "../../utils/uxFncs";
+import { useState } from "react";
+
+interface AddStorageModalProps {
+ isOpen: boolean;
+ setOpen: (value: boolean) => void;
+}
+
+export const AddStorageModal = (props: AddStorageModalProps) => {
+ const { t } = useTranslation();
+ const queryClient = useQueryClient();
+ const [alert, setAlert] = useState({
+ isAlert: false,
+ type: "neutral",
+ header: "",
+ text: "",
+ });
+
+ const form = useForm({
+ defaultValues: {
+ name: "",
+ description: "",
+ },
+ onSubmit: async ({ value }) => {
+ mutate(value);
+ },
+ });
+
+ const { mutate } = useMutation({
+ mutationFn: (values: NewStorage) => mutateNewStorage(values),
+ onError: (error: { code?: string }) => {
+ setAlert({
+ isAlert: true,
+ type: "danger",
+ header: t("error"),
+ text: error.code ? t(`errors.${error.code}`) : t("unknown-error"),
+ });
+ },
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ["storages"] });
+ props.setOpen(false);
+ },
+ });
+
+ return (
+ <>
+ props.setOpen(false)}>
+
+ {t("new-storage-title")}
+ {t("new-storage-content")}
+
+ {alert.isAlert && (
+
+ {alert.header}
+
+ {alert.text}
+
+ )}
+
+
+ >
+ );
+};
diff --git a/frontend/src/misc/interfaces.ts b/frontend/src/misc/interfaces.ts
index 501d6cd..fc13ddb 100644
--- a/frontend/src/misc/interfaces.ts
+++ b/frontend/src/misc/interfaces.ts
@@ -27,4 +27,16 @@ export interface Storage {
created_at: string;
updated_at: string;
uuid: string;
-}
\ No newline at end of file
+}
+
+export interface NewStorage {
+ name: string;
+ description: string | null;
+}
+
+export interface AlertInterface {
+ isAlert: boolean;
+ type: "success" | "warning" | "danger" | "neutral" | "primary";
+ header: string;
+ text: string;
+}
diff --git a/frontend/src/pages/Storages.tsx b/frontend/src/pages/Storages.tsx
index 1e80d94..c5bf8e3 100644
--- a/frontend/src/pages/Storages.tsx
+++ b/frontend/src/pages/Storages.tsx
@@ -1,12 +1,15 @@
import { useQuery } from "@tanstack/react-query";
import { getStorages } from "../utils/uxFncs";
-import { Sheet, Table, Button } from "@mui/joy";
+import { Sheet, Table, Button, CircularProgress } from "@mui/joy";
import { useTranslation } from "react-i18next";
import type { Storage } from "../misc/interfaces";
import { StorageRow } from "../components/StorageRow";
+import { useState } from "react";
+import { AddStorageModal } from "../components/modals/AddStorageModal";
export const Storages = () => {
const { t } = useTranslation();
+ const [modal, setModal] = useState(false);
const { data: storages, isLoading } = useQuery({
queryKey: ["storages"],
@@ -15,29 +18,36 @@ export const Storages = () => {
return (
-
-
-
-
- | {t("name")} |
- {t("description")} |
- {t("created-at")} |
- {t("updated-at")} |
- |
-
-
-
- {storages?.map((storage: Storage) => (
-
- ))}
-
-
+ {isLoading ? (
+
+ ) : (
+ <>
+
+
+
+
+
+ | {t("name")} |
+ {t("description")} |
+ {t("created-at")} |
+ {t("updated-at")} |
+ |
+
+
+
+ {storages?.map((storage: Storage) => (
+
+ ))}
+
+
+ >
+ )}
);
};
diff --git a/frontend/src/utils/uxFncs.ts b/frontend/src/utils/uxFncs.ts
index e40d86d..118f37e 100644
--- a/frontend/src/utils/uxFncs.ts
+++ b/frontend/src/utils/uxFncs.ts
@@ -1,6 +1,10 @@
import { API_BASE } from "../config/api.config";
import Cookies from "js-cookie";
-import type { ProductFormValues, Storage } from "../misc/interfaces";
+import type {
+ NewStorage,
+ ProductFormValues,
+ Storage,
+} from "../misc/interfaces";
export const getProducts = async () => {
const result = await fetch(`${API_BASE}/products/all-products`, {
@@ -182,3 +186,25 @@ export const formatDate = (value?: string | null) => {
}
return date.toLocaleDateString("de-DE");
};
+
+export const mutateNewStorage = async (values: NewStorage) => {
+ const result = await fetch(`${API_BASE}/storage/new-storage`, {
+ method: "POST",
+ body: JSON.stringify(values),
+ headers: {
+ Authorization: `Bearer ${Cookies.get("token") || ""}`,
+ "Content-Type": "application/json",
+ Accept: "application/json",
+ },
+ });
+
+ const response = await result.json();
+
+ if (response.code === "es002") {
+ return { success: false, code: response.code };
+ }
+
+ if (response.code === "ss002") {
+ return response.data;
+ }
+};