FPSMS-frontend
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

JoCreateFormModal.tsx 12 KiB

il y a 2 mois
il y a 2 mois
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. import { BomCombo } from "@/app/api/bom";
  2. import { JoDetail } from "@/app/api/jo";
  3. import { SaveJo, manualCreateJo } from "@/app/api/jo/actions";
  4. import { OUTPUT_DATE_FORMAT, OUTPUT_TIME_FORMAT, dateStringToDayjs, dayjsToDateString, dayjsToInputDateString, dayjsToInputDateTimeString } from "@/app/utils/formatUtil";
  5. import { Check } from "@mui/icons-material";
  6. import { Autocomplete, Box, Button, Card, Grid, Modal, Stack, TextField, Typography } from "@mui/material";
  7. import { DatePicker, DateTimePicker, LocalizationProvider } from "@mui/x-date-pickers";
  8. import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
  9. import dayjs, { Dayjs } from "dayjs";
  10. import { isFinite } from "lodash";
  11. import React, { SetStateAction, SyntheticEvent, useCallback, useEffect } from "react";
  12. import { Controller, FormProvider, SubmitErrorHandler, SubmitHandler, useForm, useFormContext } from "react-hook-form";
  13. import { useTranslation } from "react-i18next";
  14. import { msg } from "../Swal/CustomAlerts";
  15. interface Props {
  16. open: boolean;
  17. bomCombo: BomCombo[];
  18. onClose: () => void;
  19. onSearch: () => void;
  20. }
  21. const JoCreateFormModal: React.FC<Props> = ({
  22. open,
  23. bomCombo,
  24. onClose,
  25. onSearch,
  26. }) => {
  27. const { t } = useTranslation("jo");
  28. const formProps = useForm<SaveJo>({
  29. mode: "onChange",
  30. });
  31. const { reset, trigger, watch, control, register, formState: { errors } } = formProps
  32. const onModalClose = useCallback(() => {
  33. reset()
  34. onClose()
  35. }, [])
  36. const handleAutoCompleteChange = useCallback((event: SyntheticEvent<Element, Event>, value: BomCombo, onChange: (...event: any[]) => void) => {
  37. onChange(value.id)
  38. }, [])
  39. const handleDateTimePickerChange = useCallback((value: Dayjs | null, onChange: (...event: any[]) => void) => {
  40. if (value != null) {
  41. const updatedValue = dayjsToInputDateTimeString(value)
  42. onChange(updatedValue)
  43. } else {
  44. onChange(value)
  45. }
  46. }, [])
  47. const onSubmit = useCallback<SubmitHandler<SaveJo>>(async (data) => {
  48. data.type = "manual"
  49. const response = await manualCreateJo(data)
  50. if (response) {
  51. onSearch();
  52. msg(t("update success"));
  53. onModalClose();
  54. }
  55. }, [])
  56. const onSubmitError = useCallback<SubmitErrorHandler<SaveJo>>((error) => {
  57. console.log(error)
  58. }, [])
  59. const planStart = watch("planStart")
  60. const planEnd = watch("planEnd")
  61. useEffect(() => {
  62. trigger(['planStart', 'planEnd']);
  63. }, [trigger, planStart, planEnd])
  64. return (
  65. <Modal
  66. open={open}
  67. onClose={onModalClose}
  68. >
  69. <Card
  70. style={{
  71. flex: 10,
  72. marginBottom: "20px",
  73. width: "90%",
  74. // height: "80%",
  75. position: "fixed",
  76. top: "50%",
  77. left: "50%",
  78. transform: "translate(-50%, -50%)",
  79. }}
  80. >
  81. <Box
  82. sx={{
  83. display: "flex",
  84. "flex-direction": "column",
  85. padding: "20px",
  86. height: "100%", //'30rem',
  87. width: "100%",
  88. "& .actions": {
  89. color: "text.secondary",
  90. },
  91. "& .header": {
  92. // border: 1,
  93. // 'border-width': '1px',
  94. // 'border-color': 'grey',
  95. },
  96. "& .textPrimary": {
  97. color: "text.primary",
  98. },
  99. }}
  100. >
  101. <FormProvider {...formProps}>
  102. <Stack
  103. // spacing={2}
  104. component="form"
  105. onSubmit={formProps.handleSubmit(onSubmit, onSubmitError)}
  106. >
  107. <LocalizationProvider
  108. dateAdapter={AdapterDayjs}
  109. // TODO: Should maybe use a custom adapterLocale here to support YYYY-MM-DD
  110. adapterLocale="zh-hk"
  111. >
  112. <Grid container spacing={2}>
  113. <Grid item xs={12} sm={12} md={12}>
  114. <Typography variant="h6">{t("Create Job Order")}</Typography>
  115. </Grid>
  116. <Grid item xs={12} sm={12} md={6}>
  117. <Controller
  118. control={control}
  119. name="bomId"
  120. rules={{
  121. required: "Bom required!",
  122. validate: (value) => isFinite(value)
  123. }}
  124. render={({ field, fieldState: { error } }) => (
  125. <Autocomplete
  126. disableClearable
  127. options={bomCombo}
  128. onChange={(event, value) => {
  129. handleAutoCompleteChange(event, value, field.onChange)
  130. }}
  131. onBlur={field.onBlur}
  132. renderInput={(params) => (
  133. <TextField
  134. {...params}
  135. error={Boolean(error)}
  136. variant="outlined"
  137. label={t("Bom")}
  138. />
  139. )}
  140. />
  141. )}
  142. />
  143. </Grid>
  144. <Grid item xs={12} sm={12} md={6}>
  145. <TextField
  146. {...register("reqQty", {
  147. required: "Req. Qty. required!",
  148. validate: (value) => value > 0
  149. })}
  150. label={t("Req. Qty")}
  151. fullWidth
  152. error={Boolean(errors.reqQty)}
  153. variant="outlined"
  154. type="number"
  155. />
  156. </Grid>
  157. {/* <Grid item xs={12} sm={12} md={6}>
  158. <Controller
  159. control={control}
  160. name="planStart"
  161. rules={{
  162. required: "Plan start required!",
  163. validate: {
  164. isValid: (value) => dateStringToDayjs(value).isValid(),
  165. isBeforePlanEnd: (value) => {
  166. const planStartDayjs = dateStringToDayjs(value)
  167. const planEndDayjs = dateStringToDayjs(planEnd)
  168. return planStartDayjs.isBefore(planEndDayjs) || planStartDayjs.isSame(planEndDayjs)
  169. }
  170. }
  171. }}
  172. render={({ field, fieldState: { error } }) => (
  173. <DateTimePicker
  174. label={t("Plan Start")}
  175. format={`${OUTPUT_DATE_FORMAT} ${OUTPUT_TIME_FORMAT}`}
  176. onChange={(newValue: Dayjs | null) => {
  177. handleDateTimePickerChange(newValue, field.onChange)
  178. }}
  179. slotProps={{ textField: { fullWidth: true, error: Boolean(error) } }}
  180. />
  181. )}
  182. />
  183. </Grid>
  184. <Grid item xs={12} sm={12} md={6}>
  185. <Controller
  186. control={control}
  187. name="planEnd"
  188. rules={{
  189. required: "Plan end required!",
  190. validate: {
  191. isValid: (value) => dateStringToDayjs(value).isValid(),
  192. isBeforePlanEnd: (value) => {
  193. const planStartDayjs = dateStringToDayjs(planStart)
  194. const planEndDayjs = dateStringToDayjs(value)
  195. return planEndDayjs.isAfter(planStartDayjs) || planEndDayjs.isSame(planStartDayjs)
  196. }
  197. }
  198. }}
  199. render={({ field, fieldState: { error } }) => (
  200. <DateTimePicker
  201. label={t("Plan End")}
  202. format={`${OUTPUT_DATE_FORMAT} ${OUTPUT_TIME_FORMAT}`}
  203. onChange={(newValue: Dayjs | null) => {
  204. handleDateTimePickerChange(newValue, field.onChange)
  205. }}
  206. slotProps={{ textField: { fullWidth: true } }}
  207. />
  208. )}
  209. />
  210. </Grid> */}
  211. </Grid>
  212. <Stack
  213. direction="row"
  214. justifyContent="flex-end"
  215. spacing={2}
  216. sx={{ mt: 2 }}
  217. >
  218. <Button
  219. name="submit"
  220. variant="contained"
  221. startIcon={<Check />}
  222. type="submit"
  223. >
  224. {t("Create")}
  225. </Button>
  226. </Stack>
  227. </LocalizationProvider>
  228. </Stack>
  229. </FormProvider>
  230. </Box>
  231. </Card>
  232. </Modal>
  233. )
  234. }
  235. export default JoCreateFormModal;