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ů.

ProductDetails.tsx 13 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 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 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 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 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 11 měsíci
před 11 měsíci
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  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;