"use client"; import { Box, Button, Grid, Stack, Typography, Select, MenuItem, FormControl, InputLabel } from "@mui/material"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { useSession } from "next-auth/react"; import { SessionWithTokens } from "@/config/authConfig"; import type { StoreLaneSummary, LaneRow, LaneBtn } from "@/app/api/pickOrder/actions"; import { assignByDeliveryOrderPickOrderId, assignWorkbenchByLane, fetchWorkbenchReleasedDoPickOrdersForSelection, fetchWorkbenchReleasedDoPickOrdersForSelectionToday, fetchWorkbenchStoreLaneSummary, } from "@/app/api/doworkbench/actions"; import Swal from "sweetalert2"; import dayjs from "dayjs"; import ReleasedDoPickOrderSelectModal from "@/components/FinishedGoodSearch/ReleasedDoPickOrderSelectModal"; interface Props { onPickOrderAssigned?: () => void; onSwitchToDetailTab?: () => void; initialReleaseType?: string; } type LaneSlot4F = { truckDepartureTime: string; lane: LaneBtn }; type TruckGroup4F = { truckLanceCode: string; slots: (LaneSlot4F & { sequenceIndex: number })[] }; const WorkbenchFloorLanePanel: React.FC = ({ onPickOrderAssigned, onSwitchToDetailTab, initialReleaseType = "batch" }) => { const { t } = useTranslation("pickOrder"); const { data: session } = useSession() as { data: SessionWithTokens | null }; const currentUserId = session?.id ? parseInt(session.id) : undefined; const [selectedStore, setSelectedStore] = useState("2/F"); const [selectedTruck, setSelectedTruck] = useState(""); const [modalOpen, setModalOpen] = useState(false); const [truckCounts2F, setTruckCounts2F] = useState<{ truck: string; count: number }[]>([]); const [truckCounts4F, setTruckCounts4F] = useState<{ truck: string; count: number }[]>([]); const [summary2F, setSummary2F] = useState(null); const [summary4F, setSummary4F] = useState(null); const [defaultDateScope, setDefaultDateScope] = useState<"today" | "before">("today"); const [isLoadingSummary, setIsLoadingSummary] = useState(false); const [isAssigning, setIsAssigning] = useState(false); const [isDefaultTruck, setIsDefaultTruck] = useState(false); const [beforeTodayTruckXCount, setBeforeTodayTruckXCount] = useState(0); const [selectedDate, setSelectedDate] = useState("today"); const [releaseType, setReleaseType] = useState(initialReleaseType); const [ticketFloor, setTicketFloor] = useState<"2/F" | "4/F">("2/F"); const defaultTruckCount = summary4F?.defaultTruckCount ?? 0; const hasLoggedRef = useRef(false); const fullReadyLoggedRef = useRef(false); const pendingRef = useRef(0); const workbenchReleasedListBridge = useMemo( () => ({ loadBeforeToday: fetchWorkbenchReleasedDoPickOrdersForSelection, loadToday: fetchWorkbenchReleasedDoPickOrdersForSelectionToday, assignByListItemId: assignByDeliveryOrderPickOrderId, }), [], ); const startFullTimer = () => { if (typeof window === "undefined") return; const key = "__FG_FLOOR_FULL_TIMER_STARTED__" as const; if (!(window as any)[key]) { (window as any)[key] = true; console.time("[FG] FloorLanePanel full ready"); } }; const tryEndFullTimer = () => { if (typeof window === "undefined") return; const key = "__FG_FLOOR_FULL_TIMER_STARTED__" as const; if ((window as any)[key] && !fullReadyLoggedRef.current && pendingRef.current === 0) { fullReadyLoggedRef.current = true; console.timeEnd("[FG] FloorLanePanel full ready"); delete (window as any)[key]; } }; const loadSummaries = useCallback(async () => { setIsLoadingSummary(true); pendingRef.current += 1; startFullTimer(); try { let dateParam: string | undefined; if (selectedDate === "today") dateParam = dayjs().format("YYYY-MM-DD"); else if (selectedDate === "tomorrow") dateParam = dayjs().add(1, "day").format("YYYY-MM-DD"); else if (selectedDate === "dayAfterTomorrow") dateParam = dayjs().add(2, "day").format("YYYY-MM-DD"); const [s2, s4] = await Promise.all([ fetchWorkbenchStoreLaneSummary("2/F", dateParam, releaseType), fetchWorkbenchStoreLaneSummary("4/F", dateParam, releaseType), ]); setSummary2F(s2); setSummary4F(s4); } catch (error) { console.error("Error loading summaries:", error); } finally { setIsLoadingSummary(false); pendingRef.current -= 1; tryEndFullTimer(); if (!hasLoggedRef.current) { hasLoggedRef.current = true; } } }, [selectedDate, releaseType]); useEffect(() => { void loadSummaries(); }, [loadSummaries]); useEffect(() => { const loadCounts = async () => { pendingRef.current += 1; startFullTimer(); try { const [list2F, list4F] = await Promise.all([ fetchWorkbenchReleasedDoPickOrdersForSelection(undefined, "2/F"), fetchWorkbenchReleasedDoPickOrdersForSelection(undefined, "4/F"), ]); const groupByTruck = (list: { truckLanceCode?: string | null }[]) => { const map: Record = {}; list.forEach((item) => { const lane = item.truckLanceCode || "-"; map[lane] = (map[lane] || 0) + 1; }); return Object.entries(map) .map(([truck, count]) => ({ truck, count })) .sort((a, b) => a.truck.localeCompare(b.truck)); }; setTruckCounts2F(groupByTruck(list2F)); setTruckCounts4F(groupByTruck(list4F)); } catch (e) { console.error("Error loading counts:", e); setTruckCounts2F([]); setTruckCounts4F([]); } finally { pendingRef.current -= 1; tryEndFullTimer(); } }; void loadCounts(); }, [loadSummaries]); useEffect(() => { const loadBeforeTodayTruckX = async () => { pendingRef.current += 1; startFullTimer(); try { const list = await fetchWorkbenchReleasedDoPickOrdersForSelection(undefined, undefined, "車線-X"); setBeforeTodayTruckXCount(list.length); } catch { setBeforeTodayTruckXCount(0); } finally { pendingRef.current -= 1; tryEndFullTimer(); } }; void loadBeforeTodayTruckX(); }, []); const handleAssignByLane = useCallback( async (storeId: string, truckDepartureTime: string, truckLanceCode: string, requiredDate: string) => { if (!currentUserId) return; let dateParam: string | undefined; if (requiredDate === "today") dateParam = dayjs().format("YYYY-MM-DD"); else if (requiredDate === "tomorrow") dateParam = dayjs().add(1, "day").format("YYYY-MM-DD"); else if (requiredDate === "dayAfterTomorrow") dateParam = dayjs().add(2, "day").format("YYYY-MM-DD"); setIsAssigning(true); try { const res = await assignWorkbenchByLane({ userId: currentUserId, storeId, truckLanceCode, truckDepartureTime, requiredDate: dateParam, }); console.log("assignByLane result:", res); if (res.code === "SUCCESS") { window.dispatchEvent(new CustomEvent("pickOrderAssigned")); void loadSummaries(); onPickOrderAssigned?.(); onSwitchToDetailTab?.(); } } catch { await Swal.fire({ icon: "error", title: t("Error"), text: t("Error occurred during assignment."), confirmButtonText: t("Confirm"), confirmButtonColor: "#8dba00" }); } finally { setIsAssigning(false); } }, [currentUserId, loadSummaries, onPickOrderAssigned, onSwitchToDetailTab, t], ); const handleLaneButtonClick = useCallback( async ( storeId: string, truckDepartureTime: string, truckLanceCode: string, loadingSequence: number | null | undefined, requiredDate: string, unassigned: number, total: number, ) => { let dateDisplay = requiredDate; if (requiredDate === "today") dateDisplay = dayjs().format("YYYY-MM-DD"); else if (requiredDate === "tomorrow") dateDisplay = dayjs().add(1, "day").format("YYYY-MM-DD"); else if (requiredDate === "dayAfterTomorrow") dateDisplay = dayjs().add(2, "day").format("YYYY-MM-DD"); const result = await Swal.fire({ title: t("Confirm Assignment"), html: `

${t("Store")}: ${storeId}

${t("Lane Code")}: ${truckLanceCode}

${loadingSequence != null ? `

${t("Loading Sequence")}: ${loadingSequence}

` : ""}

${t("Departure Time")}: ${truckDepartureTime}

${t("Required Date")}: ${dateDisplay}

${t("Available Orders")}: ${unassigned}/${total}

`, icon: "question", showCancelButton: true, confirmButtonText: t("Confirm"), cancelButtonText: t("Cancel"), confirmButtonColor: "#8dba00", cancelButtonColor: "#F04438", }); if (result.isConfirmed) { await handleAssignByLane(storeId, truckDepartureTime, truckLanceCode, requiredDate); } }, [handleAssignByLane, t], ); const getDateLabel = (offset: number) => dayjs().add(offset, "day").format("YYYY-MM-DD"); const truckGroups4F = useMemo((): TruckGroup4F[] => { const rows = summary4F?.rows as LaneRow[] | undefined; if (!rows?.length) return []; const map = new Map(); for (const row of rows) { for (const lane of row.lanes) { const list = map.get(lane.truckLanceCode); const slot: LaneSlot4F = { truckDepartureTime: row.truckDepartureTime, lane }; if (list) list.push(slot); else map.set(lane.truckLanceCode, [slot]); } } return Array.from(map.entries()).map(([truckLanceCode, slots]) => ({ truckLanceCode, slots: slots .slice() .sort((a, b) => (a.lane.loadingSequence ?? 999) - (b.lane.loadingSequence ?? 999)) .map((s, i) => ({ ...s, sequenceIndex: i + 1 })), })); }, [summary4F?.rows]); const renderNoEntry = () => ( {t("No entries available")} ); return ( {t("Select Date")} {t("Release Type")} {t("Floor ticket")} {ticketFloor === "2/F" && ( 2/F {isLoadingSummary ? {t("Loading...")} : !summary2F?.rows?.length ? renderNoEntry() : ( {summary2F.rows.map((row) => ( {row.truckDepartureTime} {row.lanes.map((lane) => ( ))} ))} )} )} {ticketFloor === "4/F" && ( 4/F {isLoadingSummary ? {t("Loading...")} : !truckGroups4F.length ? renderNoEntry() : ( {truckGroups4F.map(({ truckLanceCode, slots }) => ( {truckLanceCode} {slots.map((slot) => { const handlerName = (slot.lane.handlerName ?? "").trim(); return ( ); })} ))} )} )} {t("Not yet finished released do pick orders")} {t("Released orders not yet completed - click lane to select and assign")} {ticketFloor === "2/F" && ( 2/F {truckCounts2F.length === 0 ? renderNoEntry() : ( {truckCounts2F.map(({ truck, count }) => ( ))} )} )} {ticketFloor === "4/F" && ( 4/F {truckCounts4F.length === 0 ? renderNoEntry() : ( {truckCounts4F.map(({ truck, count }) => ( ))} )} )} {t("Truck X")} {beforeTodayTruckXCount === 0 && defaultTruckCount === 0 ? renderNoEntry() : ( {defaultTruckCount > 0 && ( )} {beforeTodayTruckXCount > 0 && ( )} )} setModalOpen(false)} onAssigned={() => { void loadSummaries(); onPickOrderAssigned?.(); onSwitchToDetailTab?.(); }} /> ); }; export default WorkbenchFloorLanePanel;