FPSMS-frontend
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

CreateItem.tsx 8.6 KiB

11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
7 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
11 miesięcy temu
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;