FPSMS-frontend
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

277 lines
8.6 KiB

  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;