FPSMS-frontend
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 

396 Zeilen
13 KiB

  1. "use client";
  2. import {
  3. Autocomplete,
  4. Box,
  5. Button,
  6. Card,
  7. CardContent,
  8. FormControl,
  9. FormControlLabel,
  10. FormLabel,
  11. Grid,
  12. InputLabel,
  13. MenuItem,
  14. Radio,
  15. RadioGroup,
  16. Select,
  17. Stack,
  18. TextField,
  19. Typography,
  20. } from "@mui/material";
  21. import { Check, EditNote } from "@mui/icons-material";
  22. import { Controller, useFormContext } from "react-hook-form";
  23. import { useTranslation } from "react-i18next";
  24. import InputDataGrid from "../InputDataGrid";
  25. import { SyntheticEvent, useCallback, useEffect, useMemo, useState } from "react";
  26. import { GridColDef, GridRowModel } from "@mui/x-data-grid";
  27. import { InputDataGridProps, TableRow } from "../InputDataGrid/InputDataGrid";
  28. import { TypeEnum } from "@/app/utils/typeEnum";
  29. import { CreateItemInputs } from "@/app/api/settings/item/actions";
  30. import { ItemQc } from "@/app/api/settings/item";
  31. import { QcCategoryCombo, QcItemInfo } from "@/app/api/settings/qcCategory";
  32. import { fetchQcItemsByCategoryId } from "@/app/api/settings/qcCategory/client";
  33. import { WarehouseResult } from "@/app/api/warehouse";
  34. import QcItemsList from "./QcItemsList";
  35. type Props = {
  36. // isEditMode: boolean;
  37. // type: TypeEnum;
  38. isEditMode: boolean;
  39. // type: TypeEnum;
  40. defaultValues?: Partial<CreateItemInputs> | undefined;
  41. qcChecks?: ItemQc[];
  42. qcCategoryCombo: QcCategoryCombo[];
  43. warehouses: WarehouseResult[];
  44. };
  45. const ProductDetails: React.FC<Props> = ({ isEditMode, qcCategoryCombo, warehouses, defaultValues: initialDefaultValues }) => {
  46. const [qcItems, setQcItems] = useState<QcItemInfo[]>([]);
  47. const [qcItemsLoading, setQcItemsLoading] = useState(false);
  48. const {
  49. t,
  50. i18n: { language },
  51. } = useTranslation("items");
  52. const {
  53. register,
  54. formState: { errors, touchedFields },
  55. watch,
  56. control,
  57. setValue,
  58. getValues,
  59. setError,
  60. clearErrors,
  61. } = useFormContext<CreateItemInputs>();
  62. // const typeColumns = useMemo<GridColDef[]>(
  63. // () => [
  64. // {
  65. // field: "type",
  66. // headerName: "type",
  67. // flex: 1,
  68. // editable: true,
  69. // },
  70. // ],
  71. // []
  72. // );
  73. // const weightUnitColumns = useMemo<GridColDef[]>(
  74. // () => [
  75. // {
  76. // field: "weightUnit",
  77. // headerName: "Weight Unit",
  78. // flex: 1,
  79. // editable: true,
  80. // },
  81. // {
  82. // field: "conversion",
  83. // headerName: "conversion", // show base unit
  84. // flex: 1,
  85. // type: "number",
  86. // editable: true,
  87. // },
  88. // ],
  89. // []
  90. // );
  91. // const uomColumns = useMemo<GridColDef[]>(
  92. // () => [
  93. // {
  94. // field: "uom",
  95. // headerName: "uom",
  96. // flex: 1,
  97. // editable: true,
  98. // },
  99. // ],
  100. // []
  101. // );
  102. // const validationTest = useCallback(
  103. // (
  104. // newRow: GridRowModel<TableRow<Partial<CreateItemInputs>, EntryError>>
  105. // ): EntryError => {
  106. // const error: EntryError = {};
  107. // console.log(newRow);
  108. // return Object.keys(error).length > 0 ? error : undefined;
  109. // },
  110. // []
  111. // );
  112. const handleAutoCompleteChange = useCallback((event: SyntheticEvent<Element, Event>, value: QcCategoryCombo, onChange: (...event: any[]) => void) => {
  113. onChange(value.id)
  114. }, [])
  115. // Ensure LocationCode is set from defaultValues when component mounts
  116. useEffect(() => {
  117. if (initialDefaultValues?.LocationCode && !getValues("LocationCode")) {
  118. setValue("LocationCode", initialDefaultValues.LocationCode);
  119. }
  120. }, [initialDefaultValues, setValue, getValues]);
  121. // Watch qcCategoryId and fetch QC items when it changes
  122. const qcCategoryId = watch("qcCategoryId");
  123. useEffect(() => {
  124. const fetchItems = async () => {
  125. if (qcCategoryId) {
  126. setQcItemsLoading(true);
  127. try {
  128. const items = await fetchQcItemsByCategoryId(qcCategoryId);
  129. setQcItems(items);
  130. } catch (error) {
  131. console.error("Failed to fetch QC items:", error);
  132. setQcItems([]);
  133. } finally {
  134. setQcItemsLoading(false);
  135. }
  136. } else {
  137. setQcItems([]);
  138. }
  139. };
  140. fetchItems();
  141. }, [qcCategoryId]);
  142. return (
  143. <Card sx={{ display: "block" }}>
  144. <CardContent component={Stack} spacing={4}>
  145. <Box>
  146. <Typography variant="overline" display="block" marginBlockEnd={1}>
  147. {t("Product Details")}
  148. </Typography>
  149. <Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
  150. <Grid item xs={6}>
  151. <TextField
  152. label={t("Name")}
  153. fullWidth
  154. disabled
  155. {...register("name", {
  156. required: "name required!",
  157. })}
  158. error={Boolean(errors.name)}
  159. helperText={errors.name?.message}
  160. />
  161. </Grid>
  162. <Grid item xs={6}>
  163. <TextField
  164. label={t("Code")}
  165. fullWidth
  166. disabled
  167. {...register("code", {
  168. required: "code required!",
  169. })}
  170. error={Boolean(errors.code)}
  171. helperText={errors.code?.message}
  172. />
  173. </Grid>
  174. <Grid item xs={6}>
  175. <Controller
  176. control={control}
  177. name="type"
  178. rules={{
  179. required: "type required!",
  180. }}
  181. render={({ field }) => (
  182. <FormControl fullWidth error={Boolean(errors.type)}>
  183. <InputLabel>{t("Type")}</InputLabel>
  184. <Select
  185. value={field.value || ""}
  186. label={t("Type")}
  187. onChange={field.onChange}
  188. onBlur={field.onBlur}
  189. >
  190. <MenuItem value="fg">FG</MenuItem>
  191. <MenuItem value="wip">WIP</MenuItem>
  192. <MenuItem value="mat">MAT</MenuItem>
  193. <MenuItem value="cmb">CMB</MenuItem>
  194. <MenuItem value="nm">NM</MenuItem>
  195. </Select>
  196. {errors.type && (
  197. <Typography variant="caption" color="error" sx={{ mt: 0.5, ml: 1.5 }}>
  198. {errors.type.message}
  199. </Typography>
  200. )}
  201. </FormControl>
  202. )}
  203. />
  204. </Grid>
  205. <Grid item xs={6}>
  206. <TextField
  207. label={t("description")}
  208. fullWidth
  209. disabled
  210. {...register("description")}
  211. />
  212. </Grid>
  213. <Grid item xs={6}>
  214. <Controller
  215. control={control}
  216. name="qcCategoryId"
  217. render={({ field }) => (
  218. <Autocomplete
  219. disableClearable
  220. options={qcCategoryCombo}
  221. defaultValue={qcCategoryCombo.find(qc => qc.id === field.value)}
  222. onChange={(event, value) => {
  223. handleAutoCompleteChange(event, value, field.onChange)
  224. }}
  225. onBlur={field.onBlur}
  226. renderInput={(params) => (
  227. <TextField
  228. {...params}
  229. variant="outlined"
  230. label={t("Qc Category")}
  231. />
  232. )}
  233. />
  234. )}
  235. />
  236. </Grid>
  237. <Grid item xs={6}>
  238. <Controller
  239. control={control}
  240. name="qcType"
  241. render={({ field }) => (
  242. <FormControl fullWidth>
  243. <InputLabel>{t("QC Type")}</InputLabel>
  244. <Select
  245. value={field.value || ""}
  246. label={t("QC Type")}
  247. onChange={field.onChange}
  248. onBlur={field.onBlur}
  249. >
  250. <MenuItem value="IPQC">{t("IPQC")}</MenuItem>
  251. <MenuItem value="EPQC">{t("EPQC")}</MenuItem>
  252. </Select>
  253. </FormControl>
  254. )}
  255. />
  256. </Grid>
  257. <Grid item xs={6}>
  258. <Controller
  259. control={control}
  260. name="LocationCode"
  261. render={({ field }) => (
  262. <Autocomplete
  263. freeSolo
  264. options={warehouses.map((w) => ({
  265. label: `${w.code}`,
  266. code: w.code,
  267. }))}
  268. getOptionLabel={(option) =>
  269. typeof option === "string"
  270. ? option
  271. : option.label ?? option.code ?? ""
  272. }
  273. value={
  274. warehouses
  275. .map((w) => ({
  276. label: `${w.code}`,
  277. code: w.code,
  278. }))
  279. .find((opt) => opt.code === field.value) ||
  280. (field.value
  281. ? { label: field.value as string, code: field.value as string }
  282. : null)
  283. }
  284. onChange={(_e, value) => {
  285. if (typeof value === "string") {
  286. field.onChange(value.trim() === "" ? undefined : value);
  287. } else {
  288. field.onChange(value?.code ? (value.code.trim() === "" ? undefined : value.code) : undefined);
  289. }
  290. }}
  291. onInputChange={(_e, value) => {
  292. // keep manual input synced - convert empty string to undefined
  293. field.onChange(value.trim() === "" ? undefined : value);
  294. }}
  295. renderInput={(params) => (
  296. <TextField
  297. {...params}
  298. label={t("DefaultLocationCode")}
  299. fullWidth
  300. error={Boolean(errors.LocationCode)}
  301. helperText={errors.LocationCode?.message}
  302. />
  303. )}
  304. />
  305. )}
  306. />
  307. </Grid>
  308. <Grid item xs={12}>
  309. <FormControl component="fieldset">
  310. <FormLabel component="legend">{t("Special Type")}</FormLabel>
  311. <RadioGroup
  312. row
  313. value={
  314. watch("isEgg") === true ? "isEgg" :
  315. watch("isFee") === true ? "isFee" :
  316. watch("isBag") === true ? "isBag" :
  317. "none"
  318. }
  319. onChange={(e) => {
  320. const value = e.target.value;
  321. setValue("isEgg", value === "isEgg", { shouldValidate: true });
  322. setValue("isFee", value === "isFee", { shouldValidate: true });
  323. setValue("isBag", value === "isBag", { shouldValidate: true });
  324. }}
  325. >
  326. <FormControlLabel value="none" control={<Radio />} label={t("None")} />
  327. <FormControlLabel value="isEgg" control={<Radio />} label={t("isEgg")} />
  328. <FormControlLabel value="isFee" control={<Radio />} label={t("isFee")} />
  329. <FormControlLabel value="isBag" control={<Radio />} label={t("isBag")} />
  330. </RadioGroup>
  331. </FormControl>
  332. </Grid>
  333. <Grid item xs={12}>
  334. <QcItemsList
  335. qcItems={qcItems}
  336. loading={qcItemsLoading}
  337. categorySelected={!!qcCategoryId}
  338. />
  339. </Grid>
  340. <Grid item xs={12}>
  341. <Stack
  342. direction="row"
  343. justifyContent="flex-end"
  344. spacing={2}
  345. sx={{ mt: 2 }}
  346. >
  347. <Button
  348. name="submit"
  349. variant="contained"
  350. startIcon={<Check />}
  351. type="submit"
  352. // disabled={submitDisabled}
  353. >
  354. {isEditMode ? t("Save") : t("Confirm")}
  355. </Button>
  356. </Stack>
  357. </Grid>
  358. {/* <Grid item xs={6}>
  359. <InputDataGrid<CreateItemInputs, EntryError>
  360. _formKey={"type"}
  361. columns={typeColumns}
  362. validateRow={validationTest}
  363. />
  364. </Grid>
  365. <Grid item xs={6}>
  366. <InputDataGrid<CreateItemInputs, EntryError>
  367. _formKey={"uom"}
  368. columns={uomColumns}
  369. validateRow={validationTest}
  370. />
  371. </Grid>
  372. <Grid item xs={12}>
  373. <InputDataGrid<CreateItemInputs, EntryError>
  374. _formKey={"weightUnit"}
  375. columns={weightUnitColumns}
  376. validateRow={validationTest}
  377. />
  378. </Grid>*/}
  379. </Grid>
  380. </Box>
  381. </CardContent>
  382. </Card>
  383. );
  384. };
  385. export default ProductDetails;