"use client"; import React, { useCallback, useEffect, useState } from "react"; import { Box, Button, Card, Stack, Typography, Chip, CircularProgress, Grid, } from "@mui/material"; import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import { useTranslation } from "react-i18next"; import { fetchAllJoPickOrders, AllJoPickOrderResponse } from "@/app/api/jo/actions"; import JobPickExecution from "./newJobPickExecution"; import SearchBox, { Criterion } from "../SearchBox"; import dayjs from "dayjs"; import type { PrinterCombo } from "@/app/api/settings/printer"; interface Props { /** Reserved for tabs parity with Jodetail; not used in workbench list yet. */ onSwitchToRecordTab?: () => void; printerCombo: PrinterCombo[]; } const chipSx = { flexShrink: 0, height: 28, borderRadius: "14px", "& .MuiChip-label": { typography: "body2", px: 1.25, lineHeight: 1.2, }, } as const; /** Jo workbench: same list + detail flow as Jodetail `JoPickOrderList`, detail uses `JoWorkbench/newJobPickExecution`. */ const JoPickOrderList: React.FC = ({ printerCombo }) => { const { t } = useTranslation(["common", "jo"]); const today = dayjs().format("YYYY-MM-DD"); const [loading, setLoading] = useState(false); const [pickOrders, setPickOrders] = useState([]); const [selectedPickOrderId, setSelectedPickOrderId] = useState(undefined); const [selectedJobOrderId, setSelectedJobOrderId] = useState(undefined); const [searchQuery, setSearchQuery] = useState>({ planStart: today }); type FloorFilter = "ALL" | "2F" | "3F" | "4F" | "NO_LOT"; const searchCriteria: Criterion[] = [ { label: t("Job Order Code"), paramName: "jobOrderCode", type: "text" }, { label: t("Pick Order"), paramName: "pickOrderCode", type: "text" }, { label: t("Item Name"), paramName: "itemName", type: "text" }, { label: t("Job Order Type"), paramName: "bomDescription", type: "select-labelled", options: [ { label: t("FG"), value: "FG" }, { label: t("WIP"), value: "WIP" }, ], }, { label: t("Plan Start"), paramName: "planStart", type: "date" }, { label: t("BOM Type"), paramName: "bomType", type: "select-labelled", options: [ { label: t("Drink"), value: "drink" }, { label: t("Powder Mixture"), value: "Powder_Mixture" }, { label: t("Other"), value: "other" }, ], }, { label: t("Floor"), paramName: "floor", type: "select-labelled", options: [ { label: "2F", value: "2F" }, { label: "3F", value: "3F" }, { label: "4F", value: "4F" }, { label: t("No Lot"), value: "NO_LOT" }, ], }, ]; const selectedFloor: FloorFilter = (() => { const floor = String(searchQuery.floor || "ALL"); if (floor === "2F" || floor === "3F" || floor === "4F" || floor === "NO_LOT") { return floor; } return "ALL"; })(); const fetchPickOrders = useCallback(async (query?: Record) => { setLoading(true); try { const currentQuery = query ?? { planStart: today }; const bomTypeValue = String(currentQuery.bomType || "All"); const floorValue = String(currentQuery.floor || "ALL"); const isAllOption = (value: string) => { const normalized = value.trim().toLowerCase(); return normalized === "" || normalized === "all"; }; const typeParam = isAllOption(bomTypeValue) ? undefined : bomTypeValue; const floorParam = isAllOption(floorValue) ? undefined : floorValue; const jobOrderCode = String(currentQuery.jobOrderCode || "").trim(); const pickOrderCode = String(currentQuery.pickOrderCode || "").trim(); const itemName = String(currentQuery.itemName || "").trim(); const bomDescription = String(currentQuery.bomDescription || "").trim(); const planStart = String(currentQuery.planStart || "").trim(); const data = await fetchAllJoPickOrders(typeParam, floorParam, { jobOrderCode: jobOrderCode || undefined, pickOrderCode: pickOrderCode || undefined, itemName: itemName || undefined, bomDescription: isAllOption(bomDescription) ? undefined : bomDescription, planStart: planStart || undefined, }); setPickOrders(Array.isArray(data) ? data : []); } catch (e) { console.error(e); setPickOrders([]); } finally { setLoading(false); } }, [today]); const handleSearch = useCallback((query: Record) => { const nextQuery = { ...query }; setSearchQuery(nextQuery); void fetchPickOrders(nextQuery); }, [fetchPickOrders]); const handleSearchReset = useCallback(() => { const resetQuery = {}; setSearchQuery(resetQuery); void fetchPickOrders(resetQuery); }, [fetchPickOrders]); useEffect(() => { void fetchPickOrders({ planStart: today }); }, [fetchPickOrders, today]); const handleBackToList = useCallback(() => { setSelectedPickOrderId(undefined); setSelectedJobOrderId(undefined); void fetchPickOrders(searchQuery); }, [fetchPickOrders, searchQuery]); if (selectedPickOrderId !== undefined) { return ( ); } return ( {loading ? ( ) : ( {t("Total pick orders")}: {pickOrders.length} {pickOrders.map((pickOrder) => { const status = String(pickOrder.jobOrderStatus || ""); const statusLower = status.toLowerCase(); const statusColor = statusLower === "completed" ? "success" : statusLower === "pending" || statusLower === "processing" ? "primary" : "default"; const finishedCount = pickOrder.finishedPickOLineCount ?? 0; const bomDescription = pickOrder.bomDescription ? String(pickOrder.bomDescription).trim() : ""; const bomType = pickOrder.bomType ? String(pickOrder.bomType).trim() : ""; return ( {[pickOrder.itemCode, pickOrder.itemName] .filter(Boolean) .join(" ") || "-"} {bomDescription ? ( ) : null} {bomType ? ( ) : null} {t("Pick Order")}: {pickOrder.pickOrderCode || "-"} {t("Required Qty")}: {pickOrder.reqQty} ({pickOrder.uomName}) {selectedFloor === "ALL" ? ( <> {pickOrder.floorPickCounts?.map( ({ floor, finishedCount, totalCount }) => ( {floor}: {finishedCount}/{totalCount} ), )} {!!pickOrder.noLotPickCount && ( {t("No Lot")}:{" "} {pickOrder.noLotPickCount.finishedCount}/ {pickOrder.noLotPickCount.totalCount} )} ) : selectedFloor === "NO_LOT" ? ( !!pickOrder.noLotPickCount && ( {t("No Lot")}:{" "} {pickOrder.noLotPickCount.finishedCount}/ {pickOrder.noLotPickCount.totalCount} ) ) : ( pickOrder.floorPickCounts ?.filter((c) => c.floor === selectedFloor) .map(({ floor, finishedCount, totalCount }) => ( {floor}: {finishedCount}/{totalCount} )) )} {typeof pickOrder.suggestedFailCount === "number" && pickOrder.suggestedFailCount > 0 && ( {t("Suggested Fail")}: {pickOrder.suggestedFailCount} )} {statusLower !== "pending" && finishedCount > 0 && ( {t("Finished lines")}: {finishedCount} )} {pickOrder.jobOrderCode || "-"} {" ยท "} {t("Lot No")}: {pickOrder.lotNo || "-"} ); })} )} ); }; export default JoPickOrderList;