FPSMS-frontend
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

CreateItem.tsx 8.6 KiB

před 11 měsíci
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 10 měsíci
před 11 měsíci
před 10 měsíci
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 7 měsíci
před 11 měsíci
před 11 měsíci
před 10 měsíci
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 10 měsíci
před 11 měsíci
před 11 měsíci
před 10 měsíci
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 11 měsíci
před 10 měsíci
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. "use client";
  2. import { useCallback, useEffect, useMemo, useState } from "react";
  3. import { useRouter, useSearchParams } from "next/navigation";
  4. import { useTranslation } from "react-i18next";
  5. import { CreateItemInputs, saveItem } from "@/app/api/settings/item/actions";
  6. import {
  7. FormProvider,
  8. SubmitErrorHandler,
  9. SubmitHandler,
  10. useForm,
  11. } from "react-hook-form";
  12. import { deleteDialog } from "../Swal/CustomAlerts";
  13. import {
  14. Box,
  15. Button,
  16. Grid,
  17. Stack,
  18. Tab,
  19. Tabs,
  20. TabsProps,
  21. Typography,
  22. } from "@mui/material";
  23. import { Check, Close, EditNote, ArrowBack } from "@mui/icons-material";
  24. import { TypeEnum } from "@/app/utils/typeEnum";
  25. import ProductDetails from "./ProductDetails";
  26. import { CreateItemResponse } from "@/app/api/utils";
  27. import QcDetails from "./QcDetails";
  28. import { ItemQc } from "@/app/api/settings/item";
  29. import { saveItemQcChecks } from "@/app/api/settings/qcCheck/actions";
  30. import { useGridApiRef } from "@mui/x-data-grid";
  31. import { QcCategoryCombo } from "@/app/api/settings/qcCategory";
  32. import { WarehouseResult } from "@/app/api/warehouse";
  33. import { softDeleteBagByItemId } from "@/app/api/bag/action";
  34. type Props = {
  35. isEditMode: boolean;
  36. // type: TypeEnum;
  37. defaultValues: Partial<CreateItemInputs> | undefined;
  38. qcChecks: ItemQc[];
  39. qcCategoryCombo: QcCategoryCombo[];
  40. warehouses: WarehouseResult[];
  41. };
  42. const CreateItem: React.FC<Props> = ({
  43. isEditMode,
  44. // type,
  45. defaultValues,
  46. qcChecks,
  47. qcCategoryCombo,
  48. warehouses,
  49. }) => {
  50. // console.log(type)
  51. const apiRef = useGridApiRef();
  52. const params = useSearchParams();
  53. console.log(params.get("id"));
  54. const [serverError, setServerError] = useState("");
  55. const [tabIndex, setTabIndex] = useState(0);
  56. const { t } = useTranslation("items");
  57. const router = useRouter();
  58. const title = "Product / Material";
  59. const [mode, redirPath] = useMemo(() => {
  60. // var typeId = TypeEnum.CONSUMABLE_ID
  61. let title = "";
  62. let mode = "";
  63. let redirPath = "";
  64. // if (type === TypeEnum.MATERIAL) {
  65. // typeId = TypeEnum.MATERIAL_ID
  66. // title = "Material";
  67. // redirPath = "/settings/material";
  68. // }
  69. // if (type === TypeEnum.PRODUCT) {
  70. // typeId = TypeEnum.PRODUCT_ID
  71. title = "Product";
  72. redirPath = "/settings/items";
  73. // }
  74. // if (type === TypeEnum.BYPRODUCT) {
  75. // typeId = TypeEnum.BYPRODUCT_ID
  76. // title = "By-Product";
  77. // redirPath = "/settings/byProduct";
  78. // }
  79. if (isEditMode) {
  80. mode = "Edit";
  81. } else {
  82. mode = "Create";
  83. }
  84. return [mode, redirPath];
  85. }, [isEditMode]);
  86. // console.log(typeId)
  87. const formProps = useForm<CreateItemInputs>({
  88. defaultValues: defaultValues ? defaultValues : {},
  89. });
  90. const errors = formProps.formState.errors;
  91. const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>(
  92. (_e, newValue) => {
  93. setTabIndex(newValue);
  94. },
  95. [],
  96. );
  97. const handleCancel = () => {
  98. router.replace(`/settings/product`);
  99. };
  100. const onSubmit = useCallback<SubmitHandler<CreateItemInputs & {}>>(
  101. async (data, event) => {
  102. const hasErrors = false;
  103. console.log(errors);
  104. // console.log(apiRef.current.getCellValue(2, "lowerLimit"))
  105. // apiRef.current.
  106. try {
  107. if (hasErrors) {
  108. setServerError(t("An error has occurred. Please try again later."));
  109. return false;
  110. }
  111. // Normalize LocationCode: convert empty string to null
  112. if (data.LocationCode && data.LocationCode.trim() !== "") {
  113. // Parse LocationCode and populate store_id, warehouse, area, slot
  114. const parts = data.LocationCode.split("-");
  115. if (parts.length >= 4) {
  116. data.store_id = parts[0] || undefined;
  117. data.warehouse = parts[1] || undefined;
  118. data.area = parts[2] || undefined;
  119. data.slot = parts[3] || undefined;
  120. }
  121. } else {
  122. // If LocationCode is null or empty, set LocationCode to null and clear related fields
  123. data.LocationCode = undefined;
  124. data.store_id = undefined;
  125. data.warehouse = undefined;
  126. data.area = undefined;
  127. data.slot = undefined;
  128. }
  129. console.log("data posted");
  130. console.log(data);
  131. const qcCheck =
  132. data.qcChecks.length > 0
  133. ? data.qcChecks
  134. .filter((q) => data.qcChecks_active.includes(q.id!))
  135. .map((qc) => {
  136. return {
  137. qcItemId: qc.id,
  138. instruction: qc.instruction,
  139. lowerLimit: qc.lowerLimit,
  140. upperLimit: qc.upperLimit,
  141. itemId: parseInt(params.get("id")!.toString()),
  142. };
  143. })
  144. : [];
  145. const test = data.qcChecks.filter((q) =>
  146. data.qcChecks_active.includes(q.id!),
  147. );
  148. // TODO:
  149. // 1. check field ( directly modify col def / check here )
  150. // 2. set error change tab index
  151. console.log(test);
  152. console.log(qcCheck);
  153. // return
  154. // do api
  155. const responseI = await saveItem(data);
  156. const responseQ = await saveItemQcChecks(qcCheck);
  157. if (responseI && responseQ) {
  158. if (!Boolean(responseI.id)) {
  159. formProps.setError(
  160. responseI.errorPosition! as keyof CreateItemInputs,
  161. {
  162. message: responseI.message!,
  163. type: "required",
  164. },
  165. );
  166. } else if (!Boolean(responseQ.id)) {
  167. } else if (Boolean(responseI.id) && Boolean(responseQ.id)) {
  168. // If special type is not "isBag", soft-delete the bag record if it exists
  169. if (data.isBag !== true && data.id) {
  170. try {
  171. const itemId = typeof data.id === "string" ? parseInt(data.id) : data.id;
  172. await softDeleteBagByItemId(itemId);
  173. } catch (bagError) {
  174. // Log error but don't block the save operation
  175. console.log("Error soft-deleting bag:", bagError);
  176. }
  177. }
  178. router.replace(redirPath);
  179. }
  180. }
  181. } catch (e) {
  182. // backend error
  183. setServerError(t("An error has occurred. Please try again later."));
  184. console.log(e);
  185. }
  186. },
  187. [apiRef, router, t],
  188. );
  189. // multiple tabs
  190. const onSubmitError = useCallback<SubmitErrorHandler<CreateItemInputs>>(
  191. (errors) => {},
  192. [],
  193. );
  194. return (
  195. <>
  196. <FormProvider {...formProps}>
  197. <Stack
  198. spacing={2}
  199. component="form"
  200. onSubmit={formProps.handleSubmit(onSubmit, onSubmitError)}
  201. >
  202. <Grid>
  203. <Stack direction="column" spacing={1} mb={2}>
  204. <Button
  205. variant="outlined"
  206. startIcon={<ArrowBack />}
  207. onClick={() => router.push("/settings/items")}
  208. sx={{ alignSelf: "flex-start", minWidth: "auto" }}
  209. >
  210. {t("Back")}
  211. </Button>
  212. <Typography variant="h4">
  213. {t(`${mode} ${title}`)}
  214. </Typography>
  215. </Stack>
  216. </Grid>
  217. <Tabs
  218. value={tabIndex}
  219. onChange={handleTabChange}
  220. variant="scrollable"
  221. >
  222. <Tab label={t("Product / Material Details")} iconPosition="end" />
  223. {/* <Tab label={t("Qc items")} iconPosition="end" /> */}
  224. </Tabs>
  225. {serverError && (
  226. <Typography variant="body2" color="error" alignSelf="flex-end">
  227. {serverError}
  228. </Typography>
  229. )}
  230. {tabIndex === 0 && (
  231. <ProductDetails
  232. isEditMode={isEditMode}
  233. qcCategoryCombo={qcCategoryCombo}
  234. warehouses={warehouses}
  235. defaultValues={defaultValues}
  236. />
  237. )}
  238. {tabIndex === 1 && <QcDetails apiRef={apiRef} />}
  239. {/* {type === TypeEnum.MATERIAL && <MaterialDetails />} */}
  240. {/* {type === TypeEnum.BYPRODUCT && <ByProductDetails />} */}
  241. {/*
  242. <Stack direction="row" justifyContent="flex-end" gap={1}>
  243. <Button
  244. name="submit"
  245. variant="contained"
  246. startIcon={<Check />}
  247. type="submit"
  248. >
  249. {isEditMode ? t("Save") : t("Confirm")}
  250. </Button>
  251. <Button
  252. variant="outlined"
  253. startIcon={<Close />}
  254. onClick={handleCancel}
  255. >
  256. {t("Cancel")}
  257. </Button>
  258. </Stack>
  259. */}
  260. </Stack>
  261. </FormProvider>
  262. </>
  263. );
  264. };
  265. export default CreateItem;