From 2a7f41183456c62ebef1adcb4f8074e042ed0044 Mon Sep 17 00:00:00 2001 From: Theis Gaedigk Date: Wed, 3 Jun 2026 23:47:01 +0200 Subject: [PATCH] added product details quick view page --- backend/routes/app/products.route.js | 4 +- frontend/src/pages/ProductQuickView.tsx | 312 +++++++++++++++++++++++- 2 files changed, 312 insertions(+), 4 deletions(-) diff --git a/backend/routes/app/products.route.js b/backend/routes/app/products.route.js index 6c267ec..fbe2eb1 100644 --- a/backend/routes/app/products.route.js +++ b/backend/routes/app/products.route.js @@ -74,7 +74,7 @@ router.get("/all-products", authenticate, async (req, res) => { } }); -router.get("/view", authenticate, async (req, res) => { +router.get("/view", async (req, res) => { const uuid = req.query.uuid; const result = await productDetails(uuid); @@ -123,7 +123,7 @@ router.put("/mutate/set-amount", authenticate, async (req, res) => { } }); -router.post("/mutate/update-item", authenticate, async (req, res) => { +router.post("/mutate/update-item", async (req, res) => { const itemUUID = req.query.item; const newValues = req.body; diff --git a/frontend/src/pages/ProductQuickView.tsx b/frontend/src/pages/ProductQuickView.tsx index e724910..49b553f 100644 --- a/frontend/src/pages/ProductQuickView.tsx +++ b/frontend/src/pages/ProductQuickView.tsx @@ -1,9 +1,317 @@ +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { getStorages } from "../utils/api/storages.ts"; +import { + CircularProgress, + Typography, + Select, + Option, + Input, + Button, + Chip, + Divider, + Box, + Alert, +} from "@mui/joy"; +import { useTranslation } from "react-i18next"; +import { useEffect, useState } from "react"; +import { useForm } from "@tanstack/react-form"; +import { mutateProduct, getProductDetails } from "../utils/api/products.ts"; +import { toInputDate } from "../utils/uxFncs"; +import type { ProductFormValues } from "../misc/interfaces"; +import type { productDetailsInterface } from "../misc/interfaces"; +import Cookies from "js-cookie"; + export const ProductQuickView = () => { - const product = window.location.search.split("uuid=")[1]; + const uuid = window.location.search.split("uuid=")[1]; + const { t } = useTranslation(); + const queryClient = useQueryClient(); + const [success, setSuccess] = useState(false); + + const { + data: productDetails, + isLoading: productDetailsLoading, + isSuccess, + } = useQuery({ + queryKey: ["product", uuid], + queryFn: () => getProductDetails(uuid), + }); + + const { data: storages } = useQuery({ + queryKey: ["storages"], + queryFn: () => getStorages(), + }); + + const form = useForm({ + defaultValues: { + amount: 0, + bottling_date: "", + description: "", + expiry_date: "", + name: "", + price: "", + storage_location_uuid: "", + }, + onSubmit: async ({ value }) => { + if (!productDetails?.uuid) { + return; + } + + mutate({ values: value, uuid: productDetails.uuid }); + }, + }); + + const { mutate, isPending } = useMutation({ + mutationFn: ({ + values, + uuid, + }: { + values: ProductFormValues; + uuid: string; + }) => mutateProduct(values, uuid), + onSuccess: (_data, variables) => { + setSuccess(true); + queryClient.invalidateQueries({ queryKey: ["product", variables.uuid] }); + }, + }); + + useEffect(() => { + if (!productDetails) { + return; + } + + form.setFieldValue("amount", productDetails.amount ?? 0); + form.setFieldValue( + "bottling_date", + toInputDate(productDetails.bottling_date), + ); + form.setFieldValue("description", productDetails.description ?? ""); + form.setFieldValue("expiry_date", toInputDate(productDetails.expiry_date)); + form.setFieldValue("name", productDetails.name ?? ""); + form.setFieldValue("price", productDetails.price ?? ""); + form.setFieldValue( + "storage_location_uuid", + productDetails.storage_location_uuid ?? "", + ); + }, [form, productDetails]); return ( <> -

View

+
+
+
+ + {t("product-details")} + +
+ + {t("details")} + +
+ {productDetailsLoading && } +
+ {isSuccess && ( + +
{ + e.preventDefault(); + form.handleSubmit(); + }} + > +
+
+
+ + {t("product-name")} + + + {(field) => ( + field.handleChange(e.target.value)} + onBlur={field.handleBlur} + size="lg" + variant="outlined" + className="rounded-2xl bg-white/90 shadow-[0_10px_24px_rgba(15,23,42,0.08)]" + /> + )} + +
+
+ + {t("description")} + + + {(field) => ( + field.handleChange(e.target.value)} + onBlur={field.handleBlur} + size="lg" + variant="outlined" + className="rounded-2xl bg-white/90 shadow-[0_10px_24px_rgba(15,23,42,0.08)]" + /> + )} + +
+
+
+ + {t("expiry-date")} + + + {(field) => ( + field.handleChange(e.target.value)} + onBlur={field.handleBlur} + size="lg" + variant="outlined" + className="rounded-2xl bg-white/90" + /> + )} + +
+
+ + {t("bottling-date")} + + + {(field) => ( + field.handleChange(e.target.value)} + onBlur={field.handleBlur} + size="lg" + variant="outlined" + className="rounded-2xl bg-white/90" + /> + )} + +
+
+
+
+
+ + {t("inventory")} + + +
+
+ + {t("amount")} + + + {(field) => ( + { + const nextValue = Number(e.target.value); + field.handleChange( + Number.isNaN(nextValue) ? 0 : nextValue, + ); + }} + onBlur={field.handleBlur} + className="rounded-2xl bg-white/80" + /> + )} + +
+
+ + {t("price")} + + + {(field) => ( + field.handleChange(e.target.value)} + onBlur={field.handleBlur} + size="lg" + variant="outlined" + className="rounded-2xl bg-white/90" + /> + )} + + + {Cookies.get("currency")} + +
+
+ + {t("storage-place")} + + + {(field) => ( + + )} + +
+
+
+
+
+
+ + {t("product-details")} + + +
+ {success && ( + +
+ + {t("success")} + +
+
+ )} +
+
+ )} ); };