"use client"; import { fetchPoWithStockInLines, PoResult, PurchaseOrderLine, StockInLine, } from "@/app/api/po"; import { Box, Button, ButtonProps, Collapse, Grid, IconButton, Paper, Stack, Tab, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Tabs, TabsProps, TextField, Typography, Checkbox, FormControlLabel, } from "@mui/material"; import { useTranslation } from "react-i18next"; // import InputDataGrid, { TableRow } from "../InputDataGrid/InputDataGrid"; import { GridColDef, GridRowId, GridRowModel, useGridApiRef, } from "@mui/x-data-grid"; import { checkPolAndCompletePo, fetchPoInClient, fetchStockInLineInfo, PurchaseQcResult, startPo, } from "@/app/api/po/actions"; import { useCallback, useContext, useEffect, useMemo, useState, } from "react"; import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; import PoInputGrid from "./PoInputGrid"; import { QcItemWithChecks } from "@/app/api/qc"; import { useRouter, useSearchParams, usePathname } from "next/navigation"; import { WarehouseResult } from "@/app/api/warehouse"; import { calculateWeight, returnWeightUnit } from "@/app/utils/formatUtil"; import { CameraContext } from "../Cameras/CameraProvider"; import PoQcStockInModal from "./PoQcStockInModal"; import QrModal from "./QrModal"; import { PlayArrow } from "@mui/icons-material"; import DoneIcon from "@mui/icons-material/Done"; import { getCustomWidth } from "@/app/utils/commonUtil"; import PoInfoCard from "./PoInfoCard"; import { decimalFormatter, integerFormatter } from "@/app/utils/formatUtil"; import { fetchPoListClient } from "@/app/api/po/actions"; import { List, ListItem, ListItemButton, ListItemText, Divider } from "@mui/material"; import { createStockInLine } from "@/app/api/dashboard/actions"; //import { useRouter } from "next/navigation"; type Props = { po: PoResult; qc: QcItemWithChecks[]; warehouse: WarehouseResult[]; }; type EntryError = | { [field in keyof StockInLine]?: string; } | undefined; // type PolRow = TableRow, EntryError>; const PoSearchList: React.FC<{ poList: PoResult[]; selectedPoId: number; onSelect: (po: PoResult) => void; }> = ({ poList, selectedPoId, onSelect }) => { const { t } = useTranslation("purchaseOrder"); const [searchTerm, setSearchTerm] = useState(''); const filteredPoList = useMemo(() => { if (searchTerm.trim() === '') { return poList; } return poList.filter(poItem => poItem.code.toLowerCase().includes(searchTerm.toLowerCase()) || poItem.supplier?.toLowerCase().includes(searchTerm.toLowerCase()) || t(`${poItem.status.toLowerCase()}`).toLowerCase().includes(searchTerm.toLowerCase()) ); }, [poList, searchTerm, t]); return ( {t("Purchase Orders")} setSearchTerm(e.target.value)} sx={{ mb: 2 }} InputProps={{ startAdornment: ( ), }} /> {filteredPoList.map((poItem, index) => (
onSelect(poItem)} sx={{ width: '100%', "&.Mui-selected": { backgroundColor: "primary.light", "&:hover": { backgroundColor: "primary.light", }, }, }} > {poItem.code} } secondary={ {t(`${poItem.status.toLowerCase()}`)} } /> {index < filteredPoList.length - 1 && }
))}
{searchTerm && ( {t("Found")} {filteredPoList.length} {t("of")} {poList.length} {t("items")} )}
); }; const PoDetail: React.FC = ({ po, qc, warehouse }) => { const cameras = useContext(CameraContext); console.log(cameras); const { t } = useTranslation("purchaseOrder"); const apiRef = useGridApiRef(); const [purchaseOrder, setPurchaseOrder] = useState({ ...po }); const [rows, setRows] = useState( purchaseOrder.pol || [], ); const pathname = usePathname() const searchParams = useSearchParams(); const [row, setRow] = useState({}); const [stockInLine, setStockInLine] = useState([]); const [processedQty, setProcessedQty] = useState(0); const router = useRouter(); const [poList, setPoList] = useState([]); const [selectedPoId, setSelectedPoId] = useState(po.id); const currentPoId = searchParams.get('id'); const selectedIdsParam = searchParams.get('selectedIds'); const fetchPoList = useCallback(async () => { try { if (selectedIdsParam) { const selectedIds = selectedIdsParam.split(',').map(id => parseInt(id)); const promises = selectedIds.map(id => fetchPoInClient(id)); const results = await Promise.all(promises); setPoList(results.filter(Boolean)); } else { const result = await fetchPoListClient({ limit: 20, offset: 0 }); if (result && result.records) { setPoList(result.records); } } } catch (error) { console.error("Failed to fetch PO list:", error); } }, [selectedIdsParam]); const handlePoSelect = useCallback( (selectedPo: PoResult) => { setSelectedPoId(selectedPo.id); const newSelectedIds = selectedIdsParam || selectedPo.id.toString(); router.push(`/po/edit?id=${selectedPo.id}&start=true&selectedIds=${newSelectedIds}`, { scroll: false }); }, [router, selectedIdsParam] ); const fetchPoDetail = useCallback(async (poId: string) => { try { const result = await fetchPoInClient(parseInt(poId)); if (result) { setPurchaseOrder(result); setRows(result.pol || []); if (result.pol && result.pol.length > 0) { setRow(result.pol[0]); setStockInLine(result.pol[0].stockInLine); setProcessedQty(result.pol[0].processed); } } } catch (error) { console.error("Failed to fetch PO detail:", error); } }, []); useEffect(() => { if (currentPoId && currentPoId !== selectedPoId.toString()) { setSelectedPoId(parseInt(currentPoId)); fetchPoDetail(currentPoId); } }, [currentPoId, selectedPoId, fetchPoDetail]); useEffect(() => { fetchPoList(); }, [fetchPoList]); useEffect(() => { if (currentPoId) { setSelectedPoId(parseInt(currentPoId)); } }, [currentPoId]); const removeParam = (paramToRemove: string) => { const newParams = new URLSearchParams(searchParams.toString()); newParams.delete(paramToRemove); window.history.replaceState({}, '', `${window.location.pathname}?${newParams}`); }; const handleCompletePo = useCallback(async () => { const checkRes = await checkPolAndCompletePo(purchaseOrder.id); console.log(checkRes); const newPo = await fetchPoInClient(purchaseOrder.id); setPurchaseOrder(newPo); }, [purchaseOrder.id]); const handleStartPo = useCallback(async () => { const startRes = await startPo(purchaseOrder.id); console.log(startRes); const newPo = await fetchPoInClient(purchaseOrder.id); setPurchaseOrder(newPo); }, [purchaseOrder.id]); useEffect(() => { setRows(purchaseOrder.pol || []); }, [purchaseOrder]); // useEffect(() => { // setStockInLine([]) // }, []); function Row(props: { row: PurchaseOrderLine }) { const { row } = props; // const [firstReceiveQty, setFirstReceiveQty] = useState() const [secondReceiveQty, setSecondReceiveQty] = useState() // const [open, setOpen] = useState(false); const [processedQty, setProcessedQty] = useState(row.processed); const [currStatus, setCurrStatus] = useState(row.status); // const [stockInLine, setStockInLine] = useState(row.stockInLine); const totalWeight = useMemo( () => calculateWeight(row.qty, row.uom), [row.qty, row.uom], ); const weightUnit = useMemo( () => returnWeightUnit(row.uom), [row.uom], ); useEffect(() => { const polId = searchParams.get("polId") != null ? parseInt(searchParams.get("polId")!) : null if (polId) { setStockInLine(rows.find((r) => r.id == polId)!.stockInLine) } }, []); useEffect(() => { if (processedQty === row.qty) { setCurrStatus("completed".toUpperCase()); } else if (processedQty > 0) { setCurrStatus("receiving".toUpperCase()); } else { setCurrStatus("pending".toUpperCase()); } }, [processedQty, row.qty]); const changeStockInLines = useCallback( (id: number) => { console.log(id) //rows = purchaseOrderLine console.log(rows) const target = rows.find((r) => r.id === id) const stockInLine = target!.stockInLine console.log(stockInLine) setStockInLine(stockInLine) setRow(target!) // console.log(pathname) // router.replace(`/po/edit?id=${item.poId}&polId=${item.polId}&stockInLineId=${item.stockInLineId}`); }, [rows] ); const handleStart = useCallback( () => { setTimeout(async () => { // post stock in line const oldId = row.id; const postData = { itemId: row.itemId, itemNo: row.itemNo, itemName: row.itemName, purchaseOrderId: row.purchaseOrderId, purchaseOrderLineId: row.id, acceptedQty: secondReceiveQty || 0, // acceptedQty: row.acceptedQty, }; if (secondReceiveQty === 0) return const res = await createStockInLine(postData); console.log(res); }, 200); }, [], ); const handleChange = (e: React.ChangeEvent) => { const raw = e.target.value; // Allow empty input if (raw.trim() === '') { setSecondReceiveQty(undefined); return; } // Keep digits only const cleaned = raw.replace(/[^\d]/g, ''); if (cleaned === '') { // If the user typed only non-digits, keep previous value return; } // Parse and clamp to non-negative integer const next = Math.max(0, Math.floor(Number(cleaned))); setSecondReceiveQty(next); }; return ( <> *": { borderBottom: "unset" }, color: "black" }} onClick={() => changeStockInLines(row.id)} > {/* setOpen(!open)} > {open ? : } */} {row.itemNo} {row.itemName} {integerFormatter.format(row.qty)} {integerFormatter.format(processedQty)} {row.uom?.code} {/* {decimalFormatter.format(totalWeight)} {weightUnit} */} {/* {weightUnit} */} {decimalFormatter.format(row.price)} {/* {row.expiryDate} */} {t(`${currStatus.toLowerCase()}`)} {integerFormatter.format(row.receivedQty)} {/* */} {/* */} {/* */} {/* */} {/* */} {/*
*/} {/*
*/} {/*
*/} {/*
*/} ); } const [tabIndex, setTabIndex] = useState(0); const handleTabChange = useCallback>( (_e, newValue) => { setTabIndex(newValue); }, [], ); const [isOpenScanner, setOpenScanner] = useState(false); // const testing = useCallback(() => { // // setOpenScanner(true); // const newParams = new URLSearchParams(searchParams.toString()); // console.log(pathname) // }, [pathname, router, searchParams]); const onOpenScanner = useCallback(() => { setOpenScanner(true); }, []); const onCloseScanner = useCallback(() => { setOpenScanner(false); }, []); const [itemInfo, setItemInfo] = useState< StockInLine & { warehouseId?: number } >(); const [putAwayOpen, setPutAwayOpen] = useState(false); // const [scannedInfo, setScannedInfo] = useState({} as QrCodeInfo); const closePutAwayModal = useCallback(() => { setPutAwayOpen(false); setItemInfo(undefined); }, []); const openPutAwayModal = useCallback(() => { setPutAwayOpen(true); }, []); const buttonData = useMemo(() => { switch (purchaseOrder.status.toLowerCase()) { case "pending": return { buttonName: "start", title: t("Do you want to start?"), confirmButtonText: t("Start"), successTitle: t("Start Success"), errorTitle: t("Start Fail"), buttonText: t("Start PO"), buttonIcon: , buttonColor: "success", disabled: false, onClick: handleStartPo, }; case "receiving": return { buttonName: "complete", title: t("Do you want to complete?"), confirmButtonText: t("Complete"), successTitle: t("Complete Success"), errorTitle: t("Complete Fail"), buttonText: t("Complete PO"), buttonIcon: , buttonColor: "info", disabled: false, onClick: handleCompletePo, }; default: return { buttonName: "complete", title: t("Do you want to complete?"), confirmButtonText: t("Complete"), successTitle: t("Complete Success"), errorTitle: t("Complete Fail"), buttonText: t("Complete PO"), buttonIcon: , buttonColor: "info", disabled: true, }; // break; } }, [purchaseOrder.status, t, handleStartPo, handleCompletePo]); const FIRST_IN_FIELD = "firstInQty" const SECOND_IN_FIELD = "secondInQty" const renderFieldCondition = useCallback((field: "firstInQty" | "secondInQty"): boolean => { switch (field) { case FIRST_IN_FIELD: return true; case SECOND_IN_FIELD: return true; default: return false; // Default case } }, []); useEffect(() => { const params = searchParams.get("polId") if (params) { const polId = parseInt(params) } }, [searchParams]) return ( <> {/* Area1: title */} {purchaseOrder.code} -{" "} {t(`${purchaseOrder.status.toLowerCase()}`)} {/* area2: dn info */} {/* left side select po */} {/* right side po info */} {true ? ( ) : undefined} {/* Area4: Main Table */} {t("itemNo")} {t("itemName")} {t("qty")} {t("processed")} {t("uom")} {/* {t("total weight")} */} {`${t("price")} (HKD)`} {t("status")} {renderFieldCondition(FIRST_IN_FIELD) ? {t("receivedQty")} : undefined} {renderFieldCondition(SECOND_IN_FIELD) ? {t("dnQty")}(以訂單單位計算) : undefined} {rows.map((row) => ( ))}
{/* area5: selected item info */} {row.itemNo && row.itemName ? `已選擇: ${row.itemNo}-${row.itemName}` : `未選擇貨品`}
{/* tab 2 */} {/* */}
{itemInfo !== undefined && ( <> )} ); }; export default PoDetail;