import { BomCombo } from "@/app/api/bom"; import { JoDetail } from "@/app/api/jo"; import { SaveJo, manualCreateJo } from "@/app/api/jo/actions"; import { OUTPUT_DATE_FORMAT, OUTPUT_TIME_FORMAT, dateStringToDayjs, dayjsToDateString, dayjsToDateTimeString } from "@/app/utils/formatUtil"; import { Check } from "@mui/icons-material"; import { Autocomplete, Box, Button, Card, CircularProgress, Grid, Modal, Stack, TextField, Typography ,FormControl, InputLabel, Select, MenuItem,InputAdornment} from "@mui/material"; import { DatePicker, DateTimePicker, LocalizationProvider } from "@mui/x-date-pickers"; import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; import dayjs, { Dayjs } from "dayjs"; import { isFinite } from "lodash"; import React, { SetStateAction, SyntheticEvent, useCallback, useEffect, useMemo, useState} from "react"; import { Controller, FormProvider, SubmitErrorHandler, SubmitHandler, useForm, useFormContext } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { msg } from "../Swal/CustomAlerts"; import { JobTypeResponse } from "@/app/api/jo/actions"; interface Props { open: boolean; bomCombo: BomCombo[]; jobTypes: JobTypeResponse[]; onClose: () => void; onSearch: () => void; } const JoCreateFormModal: React.FC = ({ open, bomCombo, jobTypes, onClose, onSearch, }) => { const { t } = useTranslation("jo"); const [multiplier, setMultiplier] = useState(1); const [isSubmitting, setIsSubmitting] = useState(false); const formProps = useForm({ mode: "onChange", defaultValues: { productionPriority: 50 } }); const { reset, trigger, watch, control, register, formState: { errors }, setValue } = formProps // 监听 bomId 变化 const selectedBomId = watch("bomId"); /* const handleAutoCompleteChange = useCallback( (event: SyntheticEvent, value: BomCombo, onChange: (...event: any[]) => void) => { console.log("BOM changed to:", value); onChange(value.id); // 重置倍数为 1 setMultiplier(1); // 1) 根据 BOM 设置数量(倍数 * outputQty) if (value.outputQty != null) { const calculatedQty = 1 * Number(value.outputQty); formProps.setValue("reqQty", calculatedQty, { shouldValidate: true, shouldDirty: true }); } // 2) 选 BOM 时,把日期默认设为"今天" const today = dayjs(); const todayStr = dayjsToDateString(today, "input"); formProps.setValue("planStart", todayStr, { shouldValidate: true, shouldDirty: true }); }, [formProps] ); */ // 添加 useEffect 来监听倍数变化,自动计算 reqQty useEffect(() => { const selectedBom = bomCombo.find(bom => bom.id === selectedBomId); if (selectedBom && selectedBom.outputQty != null) { const calculatedQty = multiplier * Number(selectedBom.outputQty); formProps.setValue("reqQty", calculatedQty, { shouldValidate: true, shouldDirty: true }); } }, [multiplier, selectedBomId, bomCombo, formProps]); const onModalClose = useCallback(() => { if (isSubmitting) return; reset() onClose() setMultiplier(1); }, [reset, onClose, isSubmitting]) const duplicateLabels = useMemo(() => { const count = new Map(); bomCombo.forEach((b) => count.set(b.label, (count.get(b.label) ?? 0) + 1)); return new Set(Array.from(count.entries()).filter(([, c]) => c > 1).map(([l]) => l)); }, [bomCombo]); const handleAutoCompleteChange = useCallback( (event: SyntheticEvent, value: BomCombo, onChange: (...event: any[]) => void) => { console.log("BOM changed to:", value); onChange(value.id); // 1) 根据 BOM 设置数量 if (value.outputQty != null) { formProps.setValue("reqQty", Number(value.outputQty), { shouldValidate: true, shouldDirty: true }); } // 2) 选 BOM 时,把日期默认设为“今天” const today = dayjs(); const todayStr = dayjsToDateString(today, "input"); // 你已经有的工具函数 formProps.setValue("planStart", todayStr, { shouldValidate: true, shouldDirty: true }); }, [formProps] ); // 当 BOM 改变时,自动选择匹配的 Job Type useEffect(() => { if (!selectedBomId) { return; } const selectedBom = bomCombo.find(bom => bom.id === selectedBomId); if (!selectedBom) { return; } const description = selectedBom.description; console.log("Auto-select effect - BOM description:", description); if (!description) { console.log("Auto-select effect - No description found, skipping auto-select"); return; } const descriptionUpper = description.toUpperCase(); console.log("Auto-selecting Job Type for BOM description:", descriptionUpper); // 查找匹配的 Job Type const matchingJobType = jobTypes.find(jt => { const jobTypeName = jt.name.toUpperCase(); const matches = jobTypeName === descriptionUpper; console.log(`Checking JobType ${jt.name} (${jobTypeName}) against ${descriptionUpper}: ${matches}`); return matches; }); if (matchingJobType) { console.log("Found matching Job Type, setting jobTypeId to:", matchingJobType.id); setValue("jobTypeId", matchingJobType.id, { shouldValidate: true, shouldDirty: true }); } else { console.log("No matching Job Type found for description:", descriptionUpper); } }, [selectedBomId, bomCombo, jobTypes, setValue]); const handleDateTimePickerChange = useCallback((value: Dayjs | null, onChange: (...event: any[]) => void) => { if (value != null) { const updatedValue = dayjsToDateTimeString(value) onChange(updatedValue) } else { onChange(value) } }, []) const onSubmit = useCallback>(async (data) => { if (isSubmitting) return; setIsSubmitting(true); try { data.type = "manual" if (data.planStart) { const dateDayjs = dateStringToDayjs(data.planStart) data.planStart = dayjsToDateTimeString(dateDayjs.startOf('day')) } data.jobTypeId = Number(data.jobTypeId); // 如果 productionPriority 为空或无效,使用默认值 50 data.productionPriority = data.productionPriority != null && !isNaN(data.productionPriority) ? Number(data.productionPriority) : 50; const response = await manualCreateJo(data) if (response) { onSearch(); msg(t("update success")); onModalClose(); } } catch (e) { console.error(e); msg(t("update failed")); } finally { setIsSubmitting(false); } }, [onSearch, onModalClose, t, isSubmitting]) const onSubmitError = useCallback>((error) => { console.log(error) }, []) const planStart = watch("planStart") const planEnd = watch("planEnd") useEffect(() => { trigger(['planStart', 'planEnd']); }, [trigger, planStart, planEnd]) return ( {t("Create Job Order")} isFinite(value) }} render={({ field, fieldState: { error } }) => ( { if (!option) return ""; if (duplicateLabels.has(option.label)) { const d = (option.description || "").trim().toUpperCase(); const suffix = d === "WIP" ? t("WIP") : d === "FG" ? t("FG") : option.description ? t(option.description) : ""; return suffix ? `${option.label} (${suffix})` : option.label; } return option.label; }} onChange={(event, value) => { handleAutoCompleteChange(event, value, field.onChange); }} onBlur={field.onBlur} renderInput={(params) => ( )} /> )} /> value > 0 }} render={({ field, fieldState: { error } }) => { const selectedBom = bomCombo.find(bom => bom.id === formProps.watch("bomId")); const uom = selectedBom?.outputQtyUom || ""; const outputQty = selectedBom?.outputQty ?? 0; const calculatedValue = multiplier * outputQty; return ( {uom} ) : null }} sx={{ flex: 1 }} /> × { const val = e.target.value === "" ? 1 : Math.max(1, Math.floor(Number(e.target.value))); setMultiplier(val); }} inputProps={{ min: 1, step: 1 }} sx={{ flex: 1 }} /> = {uom} ) : null }} sx={{ flex: 1 }} /> ); }} /> { //console.log("Job Type Select render - filteredJobTypes:", filteredJobTypes); //console.log("Current field.value:", field.value); return ( {t("Job Type")} ); }} /> { if (value === undefined || value === null || isNaN(value)) { return t("Production Priority required!") as string; } return true; } }} render={({ field, fieldState: { error } }) => ( { const inputValue = e.target.value; // 允许空字符串(用户正在删除) if (inputValue === "") { field.onChange(""); return; } // 转换为数字并验证范围 const numValue = Number(inputValue); if (!isNaN(numValue) && numValue >= 1 && numValue <= 100) { field.onChange(numValue); } }} /> )} /> dateStringToDayjs(value).isValid(), // isBeforePlanEnd: (value) => { // const planStartDayjs = dateStringToDayjs(value) // const planEndDayjs = dateStringToDayjs(planEnd) // return planStartDayjs.isBefore(planEndDayjs) || planStartDayjs.isSame(planEndDayjs) // } } }} render={({ field, fieldState: { error } }) => ( // { handleDateTimePickerChange(newValue, field.onChange) }} slotProps={{ textField: { fullWidth: true, error: Boolean(error) } }} /> )} /> {/* dateStringToDayjs(value).isValid(), isBeforePlanEnd: (value) => { const planStartDayjs = dateStringToDayjs(planStart) const planEndDayjs = dateStringToDayjs(value) return planEndDayjs.isAfter(planStartDayjs) || planEndDayjs.isSame(planStartDayjs) } } }} render={({ field, fieldState: { error } }) => ( { handleDateTimePickerChange(newValue, field.onChange) }} slotProps={{ textField: { fullWidth: true } }} /> )} /> */} ) } export default JoCreateFormModal;