|
- "use client";
- import { PickOrderResult } from "@/app/api/pickOrder";
- import { useCallback, useEffect, useMemo, useState } from "react";
- import { useTranslation } from "react-i18next";
- import SearchBox, { Criterion } from "../SearchBox";
- import {
- flatten,
- intersectionWith,
- isEmpty,
- sortBy,
- uniqBy,
- upperCase,
- upperFirst,
- } from "lodash";
- import {
- arrayToDayjs,
- } from "@/app/utils/formatUtil";
- import { Button, Grid, Stack, Tab, Tabs, TabsProps, Typography, Box } from "@mui/material";
- import PickOrders from "./FinishedGood";
- import ConsolidatedPickOrders from "./ConsolidatedPickOrders";
- import PickExecution from "./GoodPickExecution";
- import CreatePickOrderModal from "./CreatePickOrderModal";
- import NewCreateItem from "./newcreatitem";
- import AssignAndRelease from "./AssignAndRelease";
- import AssignTo from "./assignTo";
- import { fetchAllItemsInClient, ItemCombo } from "@/app/api/settings/item/actions";
- import { fetchPickOrderClient, autoAssignAndReleasePickOrder, autoAssignAndReleasePickOrderByStore, fetchReleasedDoPickOrders } from "@/app/api/pickOrder/actions";
- import Jobcreatitem from "./Jobcreatitem";
- import { useSession } from "next-auth/react";
- import { SessionWithTokens } from "@/config/authConfig";
- import PickExecutionDetail from "./GoodPickExecutiondetail";
- import GoodPickExecutionRecord from "./GoodPickExecutionRecord";
- import Swal from "sweetalert2";
- import { printDN, printDNLabels } from "@/app/api/do/actions";
- import { FGPickOrderResponse, fetchStoreLaneSummary, assignByLane,type StoreLaneSummary } from "@/app/api/pickOrder/actions";
- import FGPickOrderCard from "./FGPickOrderCard";
- import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
- import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
- import { DatePicker } from '@mui/x-date-pickers/DatePicker';
- import dayjs, { Dayjs } from 'dayjs';
- import FGPickOrderTicketReleaseTable from "./FGPickOrderTicketReleaseTable";
-
- interface Props {
- pickOrders: PickOrderResult[];
- }
-
- type SearchQuery = Partial<
- Omit<PickOrderResult, "id" | "consoCode" | "completeDate">
- >;
-
- type SearchParamNames = keyof SearchQuery;
-
- const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => {
- const { t } = useTranslation("pickOrder");
- const { data: session } = useSession() as { data: SessionWithTokens | null };
- const currentUserId = session?.id ? parseInt(session.id) : undefined;
-
- const [isOpenCreateModal, setIsOpenCreateModal] = useState(false)
- const [items, setItems] = useState<ItemCombo[]>([])
- const [printButtonsEnabled, setPrintButtonsEnabled] = useState(false);
- const [filteredPickOrders, setFilteredPickOrders] = useState(pickOrders);
- const [filterArgs, setFilterArgs] = useState<Record<string, any>>({});
- const [searchQuery, setSearchQuery] = useState<Record<string, any>>({});
- const [tabIndex, setTabIndex] = useState(0);
- const [totalCount, setTotalCount] = useState<number>();
- const [isAssigning, setIsAssigning] = useState(false);
- // const [summary2F, setSummary2F] = useState<StoreLaneSummary | null>(null);
- // const [summary4F, setSummary4F] = useState<StoreLaneSummary | null>(null);
- const [isLoadingSummary, setIsLoadingSummary] = useState(false);
- const [hideCompletedUntilNext, setHideCompletedUntilNext] = useState<boolean>(
- typeof window !== 'undefined' && localStorage.getItem('hideCompletedUntilNext') === 'true'
- );
-
- const [fgPickOrdersData, setFgPickOrdersData] = useState<FGPickOrderResponse[]>([]);
- const [releasedOrderCount, setReleasedOrderCount] = useState<number>(0);
-
- const fetchReleasedOrderCount = useCallback(async () => {
- try {
- const releasedOrders = await fetchReleasedDoPickOrders();
- const validCount = releasedOrders.length;
- setReleasedOrderCount(validCount);
- } catch (error) {
- console.error("Error fetching released order count:", error);
- setReleasedOrderCount(0);
- }
- }, []);
- /*
- const loadSummaries = useCallback(async () => {
- setIsLoadingSummary(true);
- try {
- const [s2, s4] = await Promise.all([
- fetchStoreLaneSummary("2/F"),
- fetchStoreLaneSummary("4/F")
- ]);
- setSummary2F(s2);
- setSummary4F(s4);
- } catch (error) {
- console.error("Error loading summaries:", error);
- } finally {
- setIsLoadingSummary(false);
- }
- }, []);
-
- useEffect(() => {
- loadSummaries();
- // 每30秒刷新一次
-
- }, [loadSummaries]);
- */
- const handleDraft = useCallback(async () =>{
- try{
- if (fgPickOrdersData.length === 0) {
- console.error("No FG Pick order data available");
- Swal.fire({
- title: "",
- text: t("Please take one pick order before printing the draft."),
- icon: "info"
- })
- return;
- }
-
- const currentFgOrder = fgPickOrdersData[0];
-
- const printRequest = {
- printerId: 1,
- printQty: 1,
- isDraft: true,
- numOfCarton: 0,
- doPickOrderId: currentFgOrder.doPickOrderId
- };
-
- console.log("Printing draft with request: ", printRequest);
-
- const response = await printDN(printRequest);
-
- console.log("Print Draft response: ", response);
-
- if(response.success){
- Swal.fire({
- position: "bottom-end",
- icon: "success",
- text: t("Printed Successfully."),
- showConfirmButton: false,
- timer: 1500
- });
- } else {
- console.error("Print failed: ", response.message);
- }
- } catch(error){
- console.error("error: ", error)
- }
- },[t, fgPickOrdersData]);
-
- const handleAllDraft = useCallback(async () =>{
- try {
- const releasedOrders = await fetchReleasedDoPickOrders();
- console.log('fgPickOrdersData length:' + releasedOrders.length)
-
- if(releasedOrders.length === 0) {
- console.log("No released do_pick_order records found");
- Swal.fire({
- title: "",
- text: t("No released pick order records found."),
- icon: "info"
- })
- return;
- }
- console.log("Found released orders:", releasedOrders);
-
- const confirmResult = await Swal.fire({
- title: t("Batch Print"),
- text: t("Confirm print: (") + releasedOrders.length.toString() + t("piece(s))"),
- icon: "question",
- showCancelButton: true,
- confirmButtonText: t("Confirm"),
- cancelButtonText: t("Cancel"),
- confirmButtonColor: "#8dba00",
- cancelButtonColor: "#F04438"
- });
-
- if (!confirmResult.isConfirmed) {
- return;
- }
-
- Swal.fire({
- title: t("Printing..."),
- text: t("Please wait..."),
- allowOutsideClick: false,
- allowEscapeKey: false,
- didOpen: () => {
- Swal.showLoading();
- }
- });
-
- for (const order of releasedOrders) {
- const doPickOrderId = order.id
-
- console.log(`Processing order - DoPickOrder ID: ${doPickOrderId}, Ticket No: ${order.ticketNo}`);
-
- const printRequest = {
- printerId: 1,
- printQty: 1,
- isDraft: true,
- numOfCarton: 0,
- doPickOrderId: doPickOrderId
- };
-
- console.log("Printing draft with request:", printRequest)
-
- const response = await printDN(printRequest);
- if(!response.success) {
- console.error(`Print failed for order ${order.ticketNo}:`, response.message);
- }
- }
-
- Swal.fire({
- position: "bottom-end",
- icon: "success",
- text: t("Printed Successfully."),
- showConfirmButton: false,
- timer: 1500
- });
- } catch(error){
- console.error("Error in handleAllDraft:",error);
- }
-
- },[t, fgPickOrdersData]);
-
-
-
- useEffect(() => {
- fetchReleasedOrderCount();
- }, [fetchReleasedOrderCount]);
-
- useEffect(() => {
- const onAssigned = () => {
- localStorage.removeItem('hideCompletedUntilNext');
- setHideCompletedUntilNext(false);
- // loadSummaries();
- };
- window.addEventListener('pickOrderAssigned', onAssigned);
- return () => window.removeEventListener('pickOrderAssigned', onAssigned);
- }, []);
- // ... existing code ...
-
- useEffect(() => {
- const handleCompletionStatusChange = (event: CustomEvent) => {
- const { allLotsCompleted, tabIndex: eventTabIndex } = event.detail;
-
- // ✅ 修复:根据标签页和事件来源决定是否更新打印按钮状态
- if (eventTabIndex === undefined || eventTabIndex === tabIndex) {
- setPrintButtonsEnabled(allLotsCompleted);
- console.log(`Print buttons enabled for tab ${tabIndex}:`, allLotsCompleted);
- }
- };
-
- window.addEventListener('pickOrderCompletionStatus', handleCompletionStatusChange as EventListener);
-
- return () => {
- window.removeEventListener('pickOrderCompletionStatus', handleCompletionStatusChange as EventListener);
- };
- }, [tabIndex]); // ✅ 添加 tabIndex 依赖
-
- // ✅ 新增:处理标签页切换时的打印按钮状态重置
- useEffect(() => {
- // 当切换到标签页 2 (GoodPickExecutionRecord) 时,重置打印按钮状态
- if (tabIndex === 2) {
- setPrintButtonsEnabled(false);
- console.log("Reset print buttons for Pick Execution Record tab");
- }
- }, [tabIndex]);
- /*
- // ... existing code ...
- const handleAssignByLane = useCallback(async (
- storeId: string,
- truckDepartureTime: string,
- truckLanceCode: string
- ) => {
- if (!currentUserId) {
- console.error("Missing user id in session");
- return;
- }
-
- setIsAssigning(true);
- try {
- const res = await assignByLane(currentUserId, storeId, truckLanceCode, truckDepartureTime);
-
- if (res.code === "SUCCESS") {
- console.log("✅ Successfully assigned pick order from lane", truckLanceCode);
- window.dispatchEvent(new CustomEvent('pickOrderAssigned'));
- loadSummaries(); // 刷新按钮状态
- } else if (res.code === "USER_BUSY") {
- Swal.fire({
- icon: "warning",
- title: t("Warning"),
- text: t("You already have a pick order in progess. Please complete it first before taking next pick order."),
- confirmButtonText: t("Confirm"),
- confirmButtonColor: "#8dba00"
- });
- window.dispatchEvent(new CustomEvent('pickOrderAssigned'));
- } else if (res.code === "NO_ORDERS") {
- Swal.fire({
- icon: "info",
- title: t("Info"),
- text: t("No available pick order(s) for this lane."),
- confirmButtonText: t("Confirm"),
- confirmButtonColor: "#8dba00"
- });
- } else {
- console.log("ℹ️ Assignment result:", res.message);
- }
- } catch (error) {
- console.error("❌ Error assigning by lane:", error);
- Swal.fire({
- icon: "error",
- title: t("Error"),
- text: t("Error occurred during assignment."),
- confirmButtonText: t("Confirm"),
- confirmButtonColor: "#8dba00"
- });
- } finally {
- setIsAssigning(false);
- }
- }, [currentUserId, t, loadSummaries]);
- // ✅ Manual assignment handler - uses the action function
- */
- const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>(
- (_e, newValue) => {
- setTabIndex(newValue);
- },
- [],
- );
-
- const openCreateModal = useCallback(async () => {
- console.log("testing")
- const res = await fetchAllItemsInClient()
- console.log(res)
- setItems(res)
- setIsOpenCreateModal(true)
- }, [])
-
- const closeCreateModal = useCallback(() => {
- setIsOpenCreateModal(false)
- }, [])
-
- useEffect(() => {
-
- if (tabIndex === 3) {
- const loadItems = async () => {
- try {
- const itemsData = await fetchAllItemsInClient();
- console.log("PickOrderSearch loaded items:", itemsData.length);
- setItems(itemsData);
- } catch (error) {
- console.error("Error loading items in PickOrderSearch:", error);
- }
- };
-
- // 如果还没有数据,则加载
- if (items.length === 0) {
- loadItems();
- }
- }
- }, [tabIndex, items.length]);
- useEffect(() => {
- const handleCompletionStatusChange = (event: CustomEvent) => {
- const { allLotsCompleted } = event.detail;
- setPrintButtonsEnabled(allLotsCompleted);
- console.log("Print buttons enabled:", allLotsCompleted);
- };
-
- window.addEventListener('pickOrderCompletionStatus', handleCompletionStatusChange as EventListener);
-
- return () => {
- window.removeEventListener('pickOrderCompletionStatus', handleCompletionStatusChange as EventListener);
- };
- }, []);
-
- const searchCriteria: Criterion<SearchParamNames>[] = useMemo(
- () => {
- const baseCriteria: Criterion<SearchParamNames>[] = [
- {
- label: tabIndex === 3 ? t("Item Code") : t("Code"),
- paramName: "code",
- type: "text"
- },
- {
- label: t("Type"),
- paramName: "type",
- type: "autocomplete",
- options: tabIndex === 3
- ?
- [
- { value: "Consumable", label: t("Consumable") },
- { value: "Material", label: t("Material") },
- { value: "Product", label: t("Product") }
- ]
- :
- sortBy(
- uniqBy(
- pickOrders.map((po) => ({
- value: po.type,
- label: t(upperCase(po.type)),
- })),
- "value",
- ),
- "label",
- ),
- },
- ];
-
- // Add Job Order search for Create Item tab (tabIndex === 3)
- if (tabIndex === 3) {
- baseCriteria.splice(1, 0, {
- label: t("Job Order"),
- paramName: "jobOrderCode" as any, // Type assertion for now
- type: "text",
- });
-
- baseCriteria.splice(2, 0, {
- label: t("Target Date"),
- paramName: "targetDate",
- type: "date",
- });
- } else {
- baseCriteria.splice(1, 0, {
- label: t("Target Date From"),
- label2: t("Target Date To"),
- paramName: "targetDate",
- type: "dateRange",
- });
- }
-
- // Add Items/Item Name criteria
- baseCriteria.push({
- label: tabIndex === 3 ? t("Item Name") : t("Items"),
- paramName: "items",
- type: tabIndex === 3 ? "text" : "autocomplete",
- options: tabIndex === 3
- ? []
- :
- uniqBy(
- flatten(
- sortBy(
- pickOrders.map((po) =>
- po.items
- ? po.items.map((item) => ({
- value: item.name,
- label: item.name,
- }))
- : [],
- ),
- "label",
- ),
- ),
- "value",
- ),
- });
-
- // Add Status criteria for non-Create Item tabs
- if (tabIndex !== 3) {
- baseCriteria.push({
- label: t("Status"),
- paramName: "status",
- type: "autocomplete",
- options: sortBy(
- uniqBy(
- pickOrders.map((po) => ({
- value: po.status,
- label: t(upperFirst(po.status)),
- })),
- "value",
- ),
- "label",
- ),
- });
- }
-
- return baseCriteria;
- },
- [pickOrders, t, tabIndex, items],
- );
-
- const fetchNewPagePickOrder = useCallback(
- async (
- pagingController: Record<string, number>,
- filterArgs: Record<string, number>,
- ) => {
- const params = {
- ...pagingController,
- ...filterArgs,
- };
- const res = await fetchPickOrderClient(params);
- if (res) {
- console.log(res);
- setFilteredPickOrders(res.records);
- setTotalCount(res.total);
- }
- },
- [],
- );
-
- const onReset = useCallback(() => {
- setFilteredPickOrders(pickOrders);
- }, [pickOrders]);
-
- useEffect(() => {
- if (!isOpenCreateModal) {
- setTabIndex(1)
- setTimeout(async () => {
- setTabIndex(0)
- }, 200)
- }
- }, [isOpenCreateModal])
-
- // 添加处理提料单创建成功的函数
- const handlePickOrderCreated = useCallback(() => {
- // 切换到 Assign & Release 标签页 (tabIndex = 1)
- setTabIndex(2);
- }, []);
-
- return (
- <Box sx={{
- // Full viewport height
- overflow: 'auto' // Single scrollbar for the whole page
- }}>
-
-
- {/* Header section */}
- <Box sx={{
- p: 1,
- borderBottom: '1px solid #e0e0e0',
- minHeight: 'auto' // 确保最小高度自适应
- }}>
- <Grid container alignItems="center" spacing={1}>
- <Grid item xs={8}>
- <Typography
- variant="h5"
- sx={{
- lineHeight: 1.4, // 调整行高
- m: 0,
- fontWeight: 500
- }}
- >
- {t("Finished Good Order")}
- </Typography>
- </Grid>
-
- <Grid item xs={4}>
- <Box sx={{
- display: 'flex',
- justifyContent: 'flex-end',
- alignItems: 'center',
- height: '100%'
- }}>
-
- <Stack
- direction="row"
- spacing={0.5}
- sx={{
- alignItems: 'center',
- height: '100%'
- }}
- >
- <Button
- variant="contained"
- sx={{
- py: 0.5, // 增加垂直padding
- px: 1.25, // 增加水平padding
- height: '40px', // 增加按钮高度
- fontSize: '0.75rem',
- lineHeight: 1.2, // 添加行高控制
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- '&.Mui-disabled': {
- height: '40px'
- }
- }}
- onClick={handleAllDraft}
- >
- {t("Print All Draft")} ({releasedOrderCount})
- </Button>
- <Button
- variant="contained"
- sx={{
- py: 0.5,
- px: 1.25,
- height: '40px',
- fontSize: '0.75rem',
- lineHeight: 1.2,
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- '&.Mui-disabled': {
- height: '40px'
- }
- }}
- title={!printButtonsEnabled ? t("All lots must be completed before printing") : ""}
- onClick={handleDraft}
- >
- {t("Print Draft")}
- </Button>
- </Stack>
- </Box>
- </Grid>
- </Grid>
- </Box>
-
- {/* Tabs section - ✅ Move the click handler here */}
- <Box sx={{
- borderBottom: '1px solid #e0e0e0'
- }}>
- <Tabs value={tabIndex} onChange={handleTabChange} variant="scrollable">
- <Tab label={t("Pick Order Detail")} iconPosition="end" />
- <Tab label={t("Finished Good Detail")} iconPosition="end" />
- <Tab label={t("Finished Good Record")} iconPosition="end" />
- <Tab label={t("Ticket Release Table")} iconPosition="end" />
- </Tabs>
-
- </Box>
-
- {/* Content section - NO overflow: 'auto' here */}
- <Box sx={{
- p: 2
- }}>
- {tabIndex === 0 && <PickExecution filterArgs={filterArgs} onFgPickOrdersChange={setFgPickOrdersData}/>}
- {tabIndex === 1 && <PickExecutionDetail filterArgs={filterArgs} />}
- {tabIndex === 2 && <GoodPickExecutionRecord filterArgs={filterArgs} />}
- {tabIndex === 3 && <FGPickOrderTicketReleaseTable/>}
- </Box>
- </Box>
- );
- };
-
- export default PickOrderSearch;
|