"use client"; import { Box, Button, Chip, CircularProgress, Dialog, DialogTitle, DialogContent, DialogActions, Paper, Stack, Table, TableBody, TableCell, TableContainer, TableHead, TablePagination, TableRow, TextField, Typography, } from "@mui/material"; import { useCallback, useEffect, useState, useMemo } from "react"; import { useTranslation } from "react-i18next"; import SearchBox, { Criterion } from "../SearchBox"; import SearchResults, { Column } from "../SearchResults"; import { defaultPagingController } from "../SearchResults/SearchResults"; import { fetchAllPickExecutionIssues, PickExecutionIssue, updatePickExecutionIssueStatus, FetchPickExecutionIssuesParams, } from "@/app/api/pickOrder/actions"; import dayjs from "dayjs"; import { OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; type SearchQuery = { issueNo: string; pickOrderCode: string; itemCode: string; lotNo: string; type: string; status: string; }; type SearchParamNames = keyof SearchQuery; const PickExecutionIssuesTab: React.FC = () => { const { t } = useTranslation(["stockTake", "pickOrder"]); // States const [issues, setIssues] = useState([]); const [loading, setLoading] = useState(false); const [pagingController, setPagingController] = useState(defaultPagingController); const [totalCount, setTotalCount] = useState(0); // Search inputs const defaultInputs = useMemo(() => ({ issueNo: "", pickOrderCode: "", itemCode: "", lotNo: "", type: "", status: "", }), []); const [inputs, setInputs] = useState>(defaultInputs); // Handle dialog const [selectedIssue, setSelectedIssue] = useState(null); const [handleDialogOpen, setHandleDialogOpen] = useState(false); const [handleRemark, setHandleRemark] = useState(""); // Search criteria const searchCriteria: Criterion[] = useMemo( () => [ { label: t("Issue No"), paramName: "issueNo", type: "text" }, { label: t("Pick Order Code"), paramName: "pickOrderCode", type: "text" }, { label: t("Item Code"), paramName: "itemCode", type: "text" }, { label: t("Lot No"), paramName: "lotNo", type: "text" }, { label: t("Type"), paramName: "type", type: "select-labelled", options: [ { value: "jo", label: t("jo") }, { value: "do", label: t("do") }, { value: "material", label: t("material") }, ], }, { label: t("Status"), paramName: "status", type: "select-labelled", options: [ { value: "pending", label: t("pending") }, { value: "resolved", label: t("resolved") }, ], }, ], [t], ); // Fetch issues const refetchIssuesData = useCallback(async ( query: Record, actionType: "reset" | "search" | "paging" | "init", pagingController: typeof defaultPagingController, ) => { console.log("%c Action Type.", "color:red", actionType); setLoading(true); try { // Build API parameters with proper type casting let apiParams: FetchPickExecutionIssuesParams | undefined = undefined; if (query.type && query.type !== "All") { // Type assertion to ensure the string is one of the valid types const validType = query.type as "jo" | "do" | "material"; apiParams = { type: validType }; } // Call API with type parameter if specified const result = await fetchAllPickExecutionIssues(apiParams); // Apply client-side filtering for other parameters let filteredResult = result; if (query.issueNo) { filteredResult = filteredResult.filter(issue => issue.issueNo.toLowerCase().includes(query.issueNo.toLowerCase()) ); } if (query.pickOrderCode) { filteredResult = filteredResult.filter(issue => issue.pickOrderCode.toLowerCase().includes(query.pickOrderCode.toLowerCase()) ); } if (query.itemCode) { filteredResult = filteredResult.filter(issue => issue.itemCode.toLowerCase().includes(query.itemCode.toLowerCase()) ); } if (query.lotNo) { filteredResult = filteredResult.filter(issue => issue.lotNo && issue.lotNo.toLowerCase().includes(query.lotNo.toLowerCase()) ); } if (query.status && query.status !== "All") { filteredResult = filteredResult.filter(issue => { if (query.status === "pending") { return issue.handleStatus.toLowerCase() === "pending"; } if (query.status === "resolved") { return issue.handleStatus.toLowerCase() !== "pending"; } return true; }); } setIssues(filteredResult); setTotalCount(filteredResult.length); } catch (error) { console.error("Error fetching pick execution issues:", error); setIssues([]); setTotalCount(0); } finally { setLoading(false); } }, []); useEffect(() => { refetchIssuesData(defaultInputs, "init", defaultPagingController); }, [refetchIssuesData, defaultInputs]); useEffect(() => { refetchIssuesData(inputs, "paging", pagingController); }, [pagingController, refetchIssuesData, inputs]); // Handlers const onReset = useCallback(() => { refetchIssuesData(defaultInputs, "reset", defaultPagingController); setInputs(() => defaultInputs); setPagingController(() => defaultPagingController); }, [refetchIssuesData, defaultInputs]); const onSearch = useCallback((query: Record) => { refetchIssuesData(query, "search", defaultPagingController); setInputs(() => query); setPagingController(() => defaultPagingController); }, [refetchIssuesData]); const handleViewDetail = (issue: PickExecutionIssue) => { setSelectedIssue(issue); setHandleDialogOpen(true); setHandleRemark(""); }; const handleCloseDialog = () => { setHandleDialogOpen(false); setSelectedIssue(null); setHandleRemark(""); }; const handleMarkAsResolved = async () => { if (!selectedIssue) return; try { await updatePickExecutionIssueStatus({ issueId: selectedIssue.id, handleStatus: "resolved", handleDate: new Date().toISOString(), handleRemark: handleRemark, }); // Refresh data await refetchIssuesData(inputs, "search", pagingController); handleCloseDialog(); } catch (error) { console.error("Error updating issue status:", error); } }; const getIssueCategoryColor = (category: string) => { switch (category.toLowerCase()) { case "lot_issue": return "error"; case "quality_issue": return "warning"; case "quantity_mismatch": return "info"; default: return "default"; } }; // Table columns const columns: Column[] = useMemo( () => [ { name: "issueNo", label: t("Issue No") }, { name: "pickOrderCode", label: t("Pick Order") }, { name: "itemCode", label: t("Item"), renderCell: (params) => ( {params.itemCode} {params.itemDescription} ), }, { name: "lotNo", label: t("Lot No"), renderCell: (params) => params.lotNo || "-" }, { name: "requiredQty", label: t("Required Qty"), align: "right", type: "integer" }, { name: "actualPickQty", label: t("Actual Qty"), align: "right", type: "integer", renderCell: (params) => params.actualPickQty || 0 }, { name: "missQty", label: t("Miss Qty"), align: "right", type: "integer", renderCell: (params) => params.missQty || 0 }, { name: "issueCategory", label: t("Category"), renderCell: (params) => ( ), }, { name: "handleStatus", label: t("Status"), renderCell: (params) => ( ), }, { name: "pickExecutionDate", label: t("Date"), renderCell: (params) => dayjs(params.pickExecutionDate).format(OUTPUT_DATE_FORMAT), }, { name: "id", label: t("Action"), renderCell: (params) => ( ), }, ], [t, handleViewDetail], ); return ( items={issues} columns={columns} pagingController={pagingController} setPagingController={setPagingController} totalCount={totalCount} /> {/* Handle Issue Dialog */} {t("Issue Detail")} {selectedIssue && ( {t("Issue No")} {selectedIssue.issueNo} {t("Pick Order")} {selectedIssue.pickOrderCode} {t("Item")} {selectedIssue.itemCode} - {selectedIssue.itemDescription} {selectedIssue.lotNo && ( {t("Lot No")} {selectedIssue.lotNo} )} {t("Location")} {selectedIssue.storeLocation || "-"} {t("Required Qty")} {selectedIssue.requiredQty} {t("Actual Qty")} {selectedIssue.actualPickQty || 0} {t("Miss Qty")} {selectedIssue.missQty || 0} {selectedIssue.issueRemark && ( {t("Issue Remark")} {selectedIssue.issueRemark} )} {t("Picker")} {selectedIssue.pickerName || "-"} {t("Status")} {selectedIssue.handleStatus.toLowerCase() === "pending" && ( setHandleRemark(e.target.value)} fullWidth /> )} )} {selectedIssue && selectedIssue.handleStatus.toLowerCase() === "pending" && ( )} ); }; export default PickExecutionIssuesTab;