improved product details page
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
export interface productDetailsInterface {
|
||||
amount: number;
|
||||
bottling_date: string;
|
||||
description: string;
|
||||
expiry_date: string;
|
||||
name: string;
|
||||
picture: string | null;
|
||||
price: string;
|
||||
storage_location_name: string;
|
||||
storage_location_uuid: string;
|
||||
uuid: string;
|
||||
}
|
||||
|
||||
export type ProductFormValues = {
|
||||
amount: number;
|
||||
bottling_date: string;
|
||||
description: string;
|
||||
expiry_date: string;
|
||||
name: string;
|
||||
price: string;
|
||||
storage_location_uuid: string;
|
||||
};
|
||||
@@ -1,22 +1,20 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { getProductDetails } from "../utils/uxFncs";
|
||||
import { CircularProgress, Typography } from "@mui/joy";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { getProductDetails, getStorages } from "../utils/uxFncs";
|
||||
import {
|
||||
CircularProgress,
|
||||
Typography,
|
||||
Select,
|
||||
Option,
|
||||
Input,
|
||||
Button,
|
||||
} from "@mui/joy";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useEffect } from "react";
|
||||
import { formatDate } from "../utils/uxFncs";
|
||||
|
||||
interface productDetailsInterface {
|
||||
amount: number;
|
||||
bottling_date: string;
|
||||
description: string;
|
||||
expiry_date: string;
|
||||
name: string;
|
||||
picture: string | null;
|
||||
price: string;
|
||||
storage_location_name: string;
|
||||
storage_location_uuid: string;
|
||||
uuid: string;
|
||||
}
|
||||
import { useForm } from "@tanstack/react-form";
|
||||
import { mutateProduct } from "../utils/uxFncs";
|
||||
import { toInputDate } from "../utils/uxFncs";
|
||||
import type { ProductFormValues } from "../misc/interfaces";
|
||||
import type { productDetailsInterface } from "../misc/interfaces";
|
||||
|
||||
interface ViewProductProps {
|
||||
uuid: string;
|
||||
@@ -25,6 +23,7 @@ interface ViewProductProps {
|
||||
export const ViewProduct = (props: ViewProductProps) => {
|
||||
const uuid = props.uuid;
|
||||
const { t } = useTranslation();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const {
|
||||
data: productDetails,
|
||||
@@ -35,9 +34,62 @@ export const ViewProduct = (props: ViewProductProps) => {
|
||||
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) => {
|
||||
queryClient.invalidateQueries({ queryKey: ["product", variables.uuid] });
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
console.log(productDetails);
|
||||
}, [isSuccess]);
|
||||
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 (
|
||||
<>
|
||||
@@ -45,11 +97,100 @@ export const ViewProduct = (props: ViewProductProps) => {
|
||||
{productDetailsLoading && <CircularProgress size="sm" />}
|
||||
{isSuccess && (
|
||||
<>
|
||||
<Typography level="h3">{productDetails.name}</Typography>
|
||||
<Typography level="body-lg">{productDetails.description}</Typography>
|
||||
<Typography level="body-lg">
|
||||
{t("expiry-date") + " " + formatDate(productDetails.expiry_date)}
|
||||
</Typography>
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
form.handleSubmit();
|
||||
}}
|
||||
>
|
||||
<form.Field name="name">
|
||||
{(field) => (
|
||||
<Input
|
||||
type="text"
|
||||
value={field.state.value}
|
||||
onChange={(e) => field.handleChange(e.target.value)}
|
||||
onBlur={field.handleBlur}
|
||||
/>
|
||||
)}
|
||||
</form.Field>
|
||||
<form.Field name="description">
|
||||
{(field) => (
|
||||
<Input
|
||||
type="text"
|
||||
value={field.state.value}
|
||||
onChange={(e) => field.handleChange(e.target.value)}
|
||||
onBlur={field.handleBlur}
|
||||
/>
|
||||
)}
|
||||
</form.Field>
|
||||
{t("expiry-date")}
|
||||
<form.Field name="expiry_date">
|
||||
{(field) => (
|
||||
<Input
|
||||
type="date"
|
||||
value={field.state.value}
|
||||
onChange={(e) => field.handleChange(e.target.value)}
|
||||
onBlur={field.handleBlur}
|
||||
/>
|
||||
)}
|
||||
</form.Field>
|
||||
{t("bottling-date")}
|
||||
<form.Field name="bottling_date">
|
||||
{(field) => (
|
||||
<Input
|
||||
type="date"
|
||||
value={field.state.value}
|
||||
onChange={(e) => field.handleChange(e.target.value)}
|
||||
onBlur={field.handleBlur}
|
||||
/>
|
||||
)}
|
||||
</form.Field>
|
||||
<form.Field name="amount">
|
||||
{(field) => (
|
||||
<Input
|
||||
type="number"
|
||||
color="neutral"
|
||||
id="amountInput"
|
||||
placeholder={t("amount")}
|
||||
value={field.state.value}
|
||||
variant="soft"
|
||||
onChange={(e) => {
|
||||
const nextValue = Number(e.target.value);
|
||||
field.handleChange(Number.isNaN(nextValue) ? 0 : nextValue);
|
||||
}}
|
||||
onBlur={field.handleBlur}
|
||||
/>
|
||||
)}
|
||||
</form.Field>
|
||||
<form.Field name="price">
|
||||
{(field) => (
|
||||
<Input
|
||||
type="text"
|
||||
value={field.state.value}
|
||||
onChange={(e) => field.handleChange(e.target.value)}
|
||||
onBlur={field.handleBlur}
|
||||
/>
|
||||
)}
|
||||
</form.Field>
|
||||
{t("currency")}
|
||||
<form.Field name="storage_location_uuid">
|
||||
{(field) => (
|
||||
<Select
|
||||
value={field.state.value}
|
||||
onChange={(_event, value) => field.handleChange(value ?? "")}
|
||||
>
|
||||
{storages?.map((storage: { uuid: string; name: string }) => (
|
||||
<Option key={storage.uuid} value={storage.uuid}>
|
||||
{storage.name}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
)}
|
||||
</form.Field>
|
||||
<Button type="submit" loading={isPending}>
|
||||
{t("save")}
|
||||
</Button>
|
||||
</form>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { API_BASE } from "../config/api.config";
|
||||
import Cookies from "js-cookie";
|
||||
import type { ProductFormValues } from "../misc/interfaces";
|
||||
|
||||
export const getProducts = async () => {
|
||||
const result = await fetch(`${API_BASE}/products/all-products`, {
|
||||
@@ -40,10 +41,73 @@ export const getProductDetails = async (uuid: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const formatDate = (isoString: string) => {
|
||||
const date = new Date(isoString);
|
||||
const day = String(date.getDate()).padStart(2, "0");
|
||||
const month = String(date.getMonth() + 1).padStart(2, "0");
|
||||
const year = date.getFullYear();
|
||||
return `${day}.${month}.${year}`;
|
||||
export const toInputDate = (value?: string) => {
|
||||
if (!value) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
const date = new Date(value);
|
||||
if (Number.isNaN(date.getTime())) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return date.toISOString().slice(0, 10);
|
||||
};
|
||||
|
||||
export const mutateProduct = async (
|
||||
values: ProductFormValues,
|
||||
itemUUID: string,
|
||||
) => {
|
||||
const payload = {
|
||||
...values,
|
||||
expiry_date: values.expiry_date || null,
|
||||
bottling_date: values.bottling_date || null,
|
||||
};
|
||||
|
||||
const result = await fetch(
|
||||
`${API_BASE}/products/mutate/update-item?item=${itemUUID}`,
|
||||
{
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload),
|
||||
headers: {
|
||||
Authorization: `Bearer ${Cookies.get("token") || ""}`,
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const response = await result.json();
|
||||
|
||||
if (response.code === "ep004") {
|
||||
return { success: false, code: response.code };
|
||||
}
|
||||
|
||||
if (response.code === "sp004") {
|
||||
return response.data;
|
||||
}
|
||||
};
|
||||
|
||||
export const getStorages = async () => {
|
||||
const result = await fetch(`${API_BASE}/storage/all-storages`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${Cookies.get("token") || ""}`,
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const response = await result.json();
|
||||
|
||||
if (response.code === "es001") {
|
||||
return { success: false, code: response.code };
|
||||
}
|
||||
|
||||
if (response.code === "ss001") {
|
||||
return response.data;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user