diff --git a/backend/routes/app/database/storage.database.js b/backend/routes/app/database/storage.database.js index 40362c0..242d3a6 100644 --- a/backend/routes/app/database/storage.database.js +++ b/backend/routes/app/database/storage.database.js @@ -35,3 +35,16 @@ export const newStorage = async (name, description) => { return { code: "es002" }; } }; + +export const updateStorage = async (uuid, values) => { + const [result] = await pool.query( + "UPDATE storage_locations SET name = ?, description = ? WHERE uuid = UUID_TO_BIN(?);", + [values.name, values.description, uuid], + ); + + if (result.affectedRows > 0) { + return { code: "ss003" }; + } else { + return { code: "es003" }; + } +}; diff --git a/backend/routes/app/storage.route.js b/backend/routes/app/storage.route.js index a0af03c..02f9834 100644 --- a/backend/routes/app/storage.route.js +++ b/backend/routes/app/storage.route.js @@ -1,7 +1,11 @@ import express from "express"; import dotenv from "dotenv"; import { authenticate } from "../../services/tokenService.js"; -import { allStorages, newStorage } from "./database/storage.database.js"; +import { + allStorages, + newStorage, + updateStorage, +} from "./database/storage.database.js"; dotenv.config(); const router = express.Router(); @@ -51,4 +55,29 @@ router.post("/new-storage", authenticate, async (req, res) => { } }); +router.post("/update-storage", authenticate, async (req, res) => { + const storageUUID = req.query.storageUUID; + const values = req.body; + + const result = await updateStorage(storageUUID, values); + + if (result.code === "es003") { + res.status(500).json({ + success: false, + code: "es003", + data: null, + message: "unexpected server error", + }); + } + + if (result.code === "ss003") { + res.status(201).json({ + success: true, + code: "ss003", + data: null, + message: "", + }); + } +}); + export default router; diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index 30b7018..15e203e 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -2,6 +2,7 @@ import { useTranslation } from "react-i18next"; import { Button, Typography } from "@mui/joy"; import InventoryIcon from "@mui/icons-material/Inventory"; import AddBoxIcon from "@mui/icons-material/AddBox"; +import StorageIcon from "@mui/icons-material/Storage"; import AccountBoxIcon from "@mui/icons-material/AccountBox"; import { useNavigate, useMatchRoute } from "@tanstack/react-router"; @@ -50,6 +51,14 @@ export const Sidebar = () => { > {t("add")} + + + + ); +}; diff --git a/frontend/src/misc/interfaces.ts b/frontend/src/misc/interfaces.ts index 10f4a25..501d6cd 100644 --- a/frontend/src/misc/interfaces.ts +++ b/frontend/src/misc/interfaces.ts @@ -20,3 +20,11 @@ export type ProductFormValues = { price: string; storage_location_uuid: string; }; + +export interface Storage { + name: string; + description: string | null; + created_at: string; + updated_at: string; + uuid: string; +} \ No newline at end of file diff --git a/frontend/src/pages/Inventory.tsx b/frontend/src/pages/Inventory.tsx index 6930358..308385c 100644 --- a/frontend/src/pages/Inventory.tsx +++ b/frontend/src/pages/Inventory.tsx @@ -28,6 +28,7 @@ import ArrowDownwardIcon from "@mui/icons-material/ArrowDownward"; import { useQuery } from "@tanstack/react-query"; import { getProducts } from "../utils/uxFncs"; import { visuallyHidden } from "@mui/utils"; +import { formatDate } from "../utils/uxFncs"; type Order = "asc" | "desc"; @@ -47,17 +48,6 @@ type ProductRow = { refillDate: string; }; -const formatDate = (value?: string | null) => { - if (!value) { - return "-"; - } - const date = new Date(value); - if (Number.isNaN(date.getTime())) { - return "-"; - } - return date.toLocaleDateString("de-DE"); -}; - const descendingComparator = (a: T, b: T, orderBy: keyof T) => { const aValue = (a[orderBy] ?? "") as string | number; const bValue = (b[orderBy] ?? "") as string | number; diff --git a/frontend/src/pages/Storages.tsx b/frontend/src/pages/Storages.tsx new file mode 100644 index 0000000..1e80d94 --- /dev/null +++ b/frontend/src/pages/Storages.tsx @@ -0,0 +1,43 @@ +import { useQuery } from "@tanstack/react-query"; +import { getStorages } from "../utils/uxFncs"; +import { Sheet, Table, Button } from "@mui/joy"; +import { useTranslation } from "react-i18next"; +import type { Storage } from "../misc/interfaces"; +import { StorageRow } from "../components/StorageRow"; + +export const Storages = () => { + const { t } = useTranslation(); + + const { data: storages, isLoading } = useQuery({ + queryKey: ["storages"], + queryFn: () => getStorages(), + }); + + return ( + + + + + + + + + + + + + + {storages?.map((storage: Storage) => ( + + ))} + +
{t("name")}{t("description")}{t("created-at")}{t("updated-at")}
+
+ ); +}; diff --git a/frontend/src/routeTree.gen.ts b/frontend/src/routeTree.gen.ts index 3eb5c67..1e6db25 100644 --- a/frontend/src/routeTree.gen.ts +++ b/frontend/src/routeTree.gen.ts @@ -13,6 +13,7 @@ import { Route as LoginRouteImport } from './routes/login' import { Route as IndexRouteImport } from './routes/index' import { Route as AppHiddenLayoutRouteImport } from './routes/app/_hiddenLayout' import { Route as AppHiddenLayoutViewProductRouteImport } from './routes/app/_hiddenLayout/view-product' +import { Route as AppHiddenLayoutStoragesRouteImport } from './routes/app/_hiddenLayout/storages' import { Route as AppHiddenLayoutProfileRouteImport } from './routes/app/_hiddenLayout/profile' import { Route as AppHiddenLayoutInventoryRouteImport } from './routes/app/_hiddenLayout/inventory' import { Route as AppHiddenLayoutAddProductRouteImport } from './routes/app/_hiddenLayout/add-product' @@ -38,6 +39,11 @@ const AppHiddenLayoutViewProductRoute = path: '/view-product', getParentRoute: () => AppHiddenLayoutRoute, } as any) +const AppHiddenLayoutStoragesRoute = AppHiddenLayoutStoragesRouteImport.update({ + id: '/storages', + path: '/storages', + getParentRoute: () => AppHiddenLayoutRoute, +} as any) const AppHiddenLayoutProfileRoute = AppHiddenLayoutProfileRouteImport.update({ id: '/profile', path: '/profile', @@ -63,6 +69,7 @@ export interface FileRoutesByFullPath { '/app/add-product': typeof AppHiddenLayoutAddProductRoute '/app/inventory': typeof AppHiddenLayoutInventoryRoute '/app/profile': typeof AppHiddenLayoutProfileRoute + '/app/storages': typeof AppHiddenLayoutStoragesRoute '/app/view-product': typeof AppHiddenLayoutViewProductRoute } export interface FileRoutesByTo { @@ -72,6 +79,7 @@ export interface FileRoutesByTo { '/app/add-product': typeof AppHiddenLayoutAddProductRoute '/app/inventory': typeof AppHiddenLayoutInventoryRoute '/app/profile': typeof AppHiddenLayoutProfileRoute + '/app/storages': typeof AppHiddenLayoutStoragesRoute '/app/view-product': typeof AppHiddenLayoutViewProductRoute } export interface FileRoutesById { @@ -82,6 +90,7 @@ export interface FileRoutesById { '/app/_hiddenLayout/add-product': typeof AppHiddenLayoutAddProductRoute '/app/_hiddenLayout/inventory': typeof AppHiddenLayoutInventoryRoute '/app/_hiddenLayout/profile': typeof AppHiddenLayoutProfileRoute + '/app/_hiddenLayout/storages': typeof AppHiddenLayoutStoragesRoute '/app/_hiddenLayout/view-product': typeof AppHiddenLayoutViewProductRoute } export interface FileRouteTypes { @@ -93,6 +102,7 @@ export interface FileRouteTypes { | '/app/add-product' | '/app/inventory' | '/app/profile' + | '/app/storages' | '/app/view-product' fileRoutesByTo: FileRoutesByTo to: @@ -102,6 +112,7 @@ export interface FileRouteTypes { | '/app/add-product' | '/app/inventory' | '/app/profile' + | '/app/storages' | '/app/view-product' id: | '__root__' @@ -111,6 +122,7 @@ export interface FileRouteTypes { | '/app/_hiddenLayout/add-product' | '/app/_hiddenLayout/inventory' | '/app/_hiddenLayout/profile' + | '/app/_hiddenLayout/storages' | '/app/_hiddenLayout/view-product' fileRoutesById: FileRoutesById } @@ -150,6 +162,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AppHiddenLayoutViewProductRouteImport parentRoute: typeof AppHiddenLayoutRoute } + '/app/_hiddenLayout/storages': { + id: '/app/_hiddenLayout/storages' + path: '/storages' + fullPath: '/app/storages' + preLoaderRoute: typeof AppHiddenLayoutStoragesRouteImport + parentRoute: typeof AppHiddenLayoutRoute + } '/app/_hiddenLayout/profile': { id: '/app/_hiddenLayout/profile' path: '/profile' @@ -178,6 +197,7 @@ interface AppHiddenLayoutRouteChildren { AppHiddenLayoutAddProductRoute: typeof AppHiddenLayoutAddProductRoute AppHiddenLayoutInventoryRoute: typeof AppHiddenLayoutInventoryRoute AppHiddenLayoutProfileRoute: typeof AppHiddenLayoutProfileRoute + AppHiddenLayoutStoragesRoute: typeof AppHiddenLayoutStoragesRoute AppHiddenLayoutViewProductRoute: typeof AppHiddenLayoutViewProductRoute } @@ -185,6 +205,7 @@ const AppHiddenLayoutRouteChildren: AppHiddenLayoutRouteChildren = { AppHiddenLayoutAddProductRoute: AppHiddenLayoutAddProductRoute, AppHiddenLayoutInventoryRoute: AppHiddenLayoutInventoryRoute, AppHiddenLayoutProfileRoute: AppHiddenLayoutProfileRoute, + AppHiddenLayoutStoragesRoute: AppHiddenLayoutStoragesRoute, AppHiddenLayoutViewProductRoute: AppHiddenLayoutViewProductRoute, } diff --git a/frontend/src/routes/app/_hiddenLayout/storages.tsx b/frontend/src/routes/app/_hiddenLayout/storages.tsx new file mode 100644 index 0000000..aedf9de --- /dev/null +++ b/frontend/src/routes/app/_hiddenLayout/storages.tsx @@ -0,0 +1,10 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { Storages } from "../../../pages/Storages"; + +export const Route = createFileRoute("/app/_hiddenLayout/storages")({ + component: RouteComponent, +}); + +function RouteComponent() { + return ; +} diff --git a/frontend/src/utils/uxFncs.ts b/frontend/src/utils/uxFncs.ts index 7b9ee85..e40d86d 100644 --- a/frontend/src/utils/uxFncs.ts +++ b/frontend/src/utils/uxFncs.ts @@ -1,6 +1,6 @@ import { API_BASE } from "../config/api.config"; import Cookies from "js-cookie"; -import type { ProductFormValues } from "../misc/interfaces"; +import type { ProductFormValues, Storage } from "../misc/interfaces"; export const getProducts = async () => { const result = await fetch(`${API_BASE}/products/all-products`, { @@ -143,3 +143,42 @@ export const createProduct = async (values: ProductFormValues) => { return response.data; } }; + +export const updateStorage = async ( + uuid: string, + values: Pick, +) => { + const result = await fetch( + `${API_BASE}/storage/update-storage?storageUUID=${uuid}`, + { + 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 === "ep001") { + return { success: false, code: response.code }; + } + + if (response.code === "sp001") { + return response.data; + } +}; + +export const formatDate = (value?: string | null) => { + if (!value) { + return "-"; + } + const date = new Date(value); + if (Number.isNaN(date.getTime())) { + return "-"; + } + return date.toLocaleDateString("de-DE"); +};