FPSMS-frontend
Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

CreateItem.tsx 8.6 KiB

há 11 meses
há 11 meses
há 11 meses
há 11 meses
há 11 meses
há 11 meses
há 11 meses
há 11 meses
há 11 meses
há 11 meses
há 11 meses
há 7 meses
há 11 meses
há 11 meses
há 11 meses
há 11 meses
há 11 meses
há 11 meses
há 11 meses
há 11 meses
há 11 meses
há 11 meses
há 11 meses
há 11 meses
há 11 meses
há 11 meses
há 11 meses
há 11 meses
há 11 meses
há 11 meses
há 11 meses
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;