"use client"; import { Box, Button, Checkbox, Paper, Stack, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField, Typography, TablePagination, Modal, } from "@mui/material"; import { useCallback, useMemo, useState, useEffect } from "react"; import { useTranslation } from "react-i18next"; import QrCodeIcon from '@mui/icons-material/QrCode'; import { GetPickOrderLineInfo } from "@/app/api/pickOrder/actions"; import { useQrCodeScannerContext } from '../QrCodeScannerProvider/QrCodeScannerProvider'; interface LotPickData { id: number; lotId: number; lotNo: string; expiryDate: string; location: string; stockUnit: string; availableQty: number; requiredQty: number; actualPickQty: number; lotStatus: string; lotAvailability: 'available' | 'insufficient_stock' | 'expired' | 'status_unavailable'; stockOutLineId?: number; stockOutLineStatus?: string; stockOutLineQty?: number; } interface PickQtyData { [lineId: number]: { [lotId: number]: number; }; } interface LotTableProps { lotData: LotPickData[]; selectedRowId: number | null; selectedRow: (GetPickOrderLineInfo & { pickOrderCode: string }) | null; pickQtyData: PickQtyData; selectedLotRowId: string | null; selectedLotId: number | null; onLotSelection: (uniqueLotId: string, lotId: number) => void; onPickQtyChange: (lineId: number, lotId: number, value: number) => void; onSubmitPickQty: (lineId: number, lotId: number) => void; onCreateStockOutLine: (inventoryLotLineId: number) => void; onQcCheck: (line: GetPickOrderLineInfo, pickOrderCode: string) => void; onLotSelectForInput: (lot: LotPickData) => void; showInputBody: boolean; setShowInputBody: (show: boolean) => void; selectedLotForInput: LotPickData | null; generateInputBody: () => any; } // ✅ QR Code Modal Component const QrCodeModal: React.FC<{ open: boolean; onClose: () => void; lot: LotPickData | null; onQrCodeSubmit: (lotNo: string) => void; }> = ({ open, onClose, lot, onQrCodeSubmit }) => { const { t } = useTranslation("pickOrder"); const { values: qrValues, isScanning, startScan, stopScan, resetScan } = useQrCodeScannerContext(); const [manualInput, setManualInput] = useState(''); // ✅ Add state to track manual input submission const [manualInputSubmitted, setManualInputSubmitted] = useState(false); const [manualInputError, setManualInputError] = useState(false); // ✅ Process scanned QR codes useEffect(() => { if (qrValues.length > 0 && lot) { const latestQr = qrValues[qrValues.length - 1]; const qrContent = latestQr.replace(/[{}]/g, ''); if (qrContent === lot.lotNo) { onQrCodeSubmit(lot.lotNo); onClose(); resetScan(); } else { // ✅ Set error state for helper text setManualInputError(true); setManualInputSubmitted(true); } } }, [qrValues, lot, onQrCodeSubmit, onClose, resetScan]); // ✅ Clear states when modal opens or lot changes useEffect(() => { if (open) { setManualInput(''); setManualInputSubmitted(false); setManualInputError(false); } }, [open]); useEffect(() => { if (lot) { setManualInput(''); setManualInputSubmitted(false); setManualInputError(false); } }, [lot]); const handleManualSubmit = () => { if (manualInput.trim() === lot?.lotNo) { // ✅ Success - no error helper text needed onQrCodeSubmit(lot.lotNo); onClose(); setManualInput(''); } else { // ✅ Show error helper text after submit setManualInputError(true); setManualInputSubmitted(true); // Don't clear input - let user see what they typed } }; return ( {t("QR Code Scan for Lot")}: {lot?.lotNo} {/* QR Scanner Status */} Scanner Status: {isScanning ? 'Scanning...' : 'Ready'} {/* Manual Input with Submit-Triggered Helper Text */} Manual Input: setManualInput(e.target.value)} sx={{ mb: 1 }} // ✅ Only show error after submit button is clicked error={manualInputSubmitted && manualInputError} helperText={ // ✅ Show helper text only after submit with error manualInputSubmitted && manualInputError ? `The input is not the same as the expected lot number. Expected: ${lot?.lotNo}` : '' } /> {/* ✅ Show QR Scan Status */} {qrValues.length > 0 && ( QR Scan Result: {qrValues[qrValues.length - 1]} {manualInputError && ( ❌ Mismatch! Expected: {lot?.lotNo} )} )} ); }; const LotTable: React.FC = ({ lotData, selectedRowId, selectedRow, pickQtyData, selectedLotRowId, selectedLotId, onLotSelection, onPickQtyChange, onSubmitPickQty, onCreateStockOutLine, onQcCheck, onLotSelectForInput, showInputBody, setShowInputBody, selectedLotForInput, generateInputBody, }) => { const { t } = useTranslation("pickOrder"); // ✅ Add QR scanner context const { values: qrValues, isScanning, startScan, stopScan, resetScan } = useQrCodeScannerContext(); // ✅ Add state for QR input modal const [qrModalOpen, setQrModalOpen] = useState(false); const [selectedLotForQr, setSelectedLotForQr] = useState(null); const [manualQrInput, setManualQrInput] = useState(''); // 分页控制器 const [lotTablePagingController, setLotTablePagingController] = useState({ pageNum: 0, pageSize: 10, }); // ✅ 添加状态消息生成函数 const getStatusMessage = useCallback((lot: LotPickData) => { if (!lot.stockOutLineId) { return "Please finish QR code scan, QC check and pick order."; } switch (lot.stockOutLineStatus?.toLowerCase()) { case 'pending': return "Please finish QC check and pick order."; case 'completed': return "Please submit the pick order."; case 'unavailable': return "This order is insufficient, please pick another lot."; default: return "Please finish QR code scan, QC check and pick order."; } }, []); const prepareLotTableData = useMemo(() => { return lotData.map((lot) => ({ ...lot, id: lot.lotId, })); }, [lotData]); // 分页数据 const paginatedLotTableData = useMemo(() => { const startIndex = lotTablePagingController.pageNum * lotTablePagingController.pageSize; const endIndex = startIndex + lotTablePagingController.pageSize; return prepareLotTableData.slice(startIndex, endIndex); }, [prepareLotTableData, lotTablePagingController]); // 分页处理函数 const handleLotTablePageChange = useCallback((event: unknown, newPage: number) => { setLotTablePagingController(prev => ({ ...prev, pageNum: newPage, })); }, []); const handleLotTablePageSizeChange = useCallback((event: React.ChangeEvent) => { const newPageSize = parseInt(event.target.value, 10); setLotTablePagingController({ pageNum: 0, pageSize: newPageSize, }); }, []); // ✅ Handle QR code submission const handleQrCodeSubmit = useCallback((lotNo: string) => { if (selectedLotForQr && selectedLotForQr.lotNo === lotNo) { console.log(`✅ QR Code verified for lot: ${lotNo}`); // ✅ Create stock out line onCreateStockOutLine(selectedLotForQr.lotId); // ✅ Show success message console.log("Stock out line created successfully!"); // ✅ Close modal setQrModalOpen(false); setSelectedLotForQr(null); } }, [selectedLotForQr, onCreateStockOutLine]); return ( <> {t("Selected")} {t("Lot#")} {t("Lot Expiry Date")} {t("Lot Location")} {t("Available Lot")} {t("Lot Required Pick Qty")} {t("Stock Unit")} {t("QR Code Scan")} {t("QC Check")} {t("Lot Actual Pick Qty")} {t("Submit")} {paginatedLotTableData.length === 0 ? ( {t("No data available")} ) : ( paginatedLotTableData.map((lot, index) => ( onLotSelection(`row_${index}`, lot.lotId)} // ✅ Allow selection of available AND insufficient_stock lots disabled={lot.lotAvailability === 'expired' || lot.lotAvailability === 'status_unavailable'} value={`row_${index}`} name="lot-selection" /> {lot.lotNo} {lot.lotAvailability !== 'available' && ( ({lot.lotAvailability === 'expired' ? 'Expired' : lot.lotAvailability === 'insufficient_stock' ? 'Insufficient' : 'Unavailable'}) )} {lot.expiryDate} {lot.location} {lot.availableQty.toLocaleString()} {lot.requiredQty.toLocaleString()} {lot.stockUnit} {/* QR Code Scan Button */} {/* QC Check Button */} {/* Lot Actual Pick Qty */} { if (selectedRowId) { onPickQtyChange( selectedRowId, lot.lotId, // This should be unique (ill.id) parseInt(e.target.value) || 0 ); } }} inputProps={{ min: 0, max: lot.availableQty }} // ✅ Allow input for available AND insufficient_stock lots disabled={lot.lotAvailability === 'expired' || lot.lotAvailability === 'status_unavailable'} sx={{ width: '80px' }} /> {/* Submit Button */} )) )}
{/* ✅ Status Messages Display */} {paginatedLotTableData.length > 0 && ( {paginatedLotTableData.map((lot, index) => ( Lot {lot.lotNo}: {getStatusMessage(lot)} ))} )} `${from}-${to} of ${count !== -1 ? count : `more than ${to}`}` } /> {/* ✅ QR Code Modal */} { setQrModalOpen(false); setSelectedLotForQr(null); stopScan(); resetScan(); }} lot={selectedLotForQr} onQrCodeSubmit={handleQrCodeSubmit} /> ); }; export default LotTable;