"use client"; import { Box, Button, Grid, Stack, Typography, Select, MenuItem, FormControl, InputLabel ,Tooltip} from "@mui/material"; import { useCallback, useEffect, useMemo, useState, useRef } from "react"; import { useTranslation } from "react-i18next"; import { useSession } from "next-auth/react"; import { SessionWithTokens } from "@/config/authConfig"; import { fetchStoreLaneSummary,fetchReleasedDoPickOrdersForSelection,fetchReleasedDoPickOrderCountByStore, assignByLane, type StoreLaneSummary, type LaneRow, type LaneBtn } from "@/app/api/pickOrder/actions"; import Swal from "sweetalert2"; import dayjs from "dayjs"; import ReleasedDoPickOrderSelectModal from "./ReleasedDoPickOrderSelectModal"; interface Props { onPickOrderAssigned?: () => void; onSwitchToDetailTab?: () => void; } type LaneSlot4F = { truckDepartureTime: string; lane: LaneBtn }; type TruckGroup4F = { truckLanceCode: string; slots: (LaneSlot4F & { sequenceIndex: number })[] }; const FinishedGoodFloorLanePanel: React.FC = ({ onPickOrderAssigned, onSwitchToDetailTab }) => { 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 [selectedDefaultTruck, setSelectedDefaultTruck] = 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); // 其他 state 旁邊加一組: const hasLoggedRef = useRef(false); const fullReadyLoggedRef = useRef(false); const pendingRef = useRef(0); const [defaultDateScope, setDefaultDateScope] = useState<"today" | "before">("today"); const [isLoadingSummary, setIsLoadingSummary] = useState(false); const [isAssigning, setIsAssigning] = useState(false); const [isDefaultTruck, setIsDefaultTruck] = useState(false); //const [selectedDate, setSelectedDate] = useState("today"); const defaultTruckCount = summary4F?.defaultTruckCount ?? 0; const [beforeTodayTruckXCount, setBeforeTodayTruckXCount] = useState(0); const [selectedDate, setSelectedDate] = useState("today"); const [releaseType, setReleaseType] = useState("batch"); const [ticketFloor, setTicketFloor] = useState<"2/F" | "4/F">("2/F"); 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 { // Convert selectedDate to the format needed 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([ fetchStoreLaneSummary("2/F", dateParam, releaseType), fetchStoreLaneSummary("4/F", dateParam, releaseType) ]); setSummary2F(s2); setSummary4F(s4); } catch (error) { console.error("Error loading summaries:", error); } finally { setIsLoadingSummary(false); // ⭐ 新增:this async 结束,pending--,尝试结束 full ready 计时 pendingRef.current -= 1; tryEndFullTimer(); if (!hasLoggedRef.current) { hasLoggedRef.current = true; if (typeof window !== "undefined") { const key = "__FG_FLOOR_PANEL_TIMER_STARTED__" as const; if ((window as any)[key]) { console.timeEnd("[FG] FloorLanePanel initial load"); delete (window as any)[key]; } else { console.log("Timer '[FG] FloorLanePanel initial load' already ended or never started, skip."); } } } } }, [selectedDate, releaseType]); // 初始化 useEffect(() => { loadSummaries(); }, [loadSummaries]); useEffect(() => { const loadCounts = async () => { pendingRef.current += 1; startFullTimer(); try { const [list2F, list4F] = await Promise.all([ fetchReleasedDoPickOrdersForSelection(undefined, "2/F"), fetchReleasedDoPickOrdersForSelection(undefined, "4/F"), ]); const groupByTruck = (list: { truckLanceCode?: string | null }[]) => { const map: Record = {}; list.forEach((item) => { const t = item.truckLanceCode || "-"; map[t] = (map[t] || 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 { // ⭐ 新增:结束时 pending--,尝试结束 full ready 计时 pendingRef.current -= 1; tryEndFullTimer(); } }; loadCounts(); }, [loadSummaries]); useEffect(() => { const loadBeforeTodayTruckX = async () => { pendingRef.current += 1; startFullTimer(); try { const list = await fetchReleasedDoPickOrdersForSelection( undefined, // shopName undefined, // storeId: Truck X 的 store_id 是 null "車線-X" // 只看 Truck X ); setBeforeTodayTruckXCount(list.length); } catch (e) { console.error("Error loading beforeTodayTruckX:", e); setBeforeTodayTruckXCount(0); }finally { // ⭐ 新增:结束时 pending--,尝试结束 full ready 计时 pendingRef.current -= 1; tryEndFullTimer(); } }; loadBeforeTodayTruckX(); }, []); const handleAssignByLane = useCallback(async ( storeId: string, truckDepartureTime: string, truckLanceCode: string, loadingSequence: number | null | undefined, requiredDate: string ) => { if (!currentUserId) { console.error("Missing user id in session"); 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 assignByLane(currentUserId, storeId, truckLanceCode, truckDepartureTime, loadingSequence ?? null, dateParam); if (res.code === "SUCCESS") { console.log(" Successfully assigned pick order from lane", truckLanceCode); window.dispatchEvent(new CustomEvent('pickOrderAssigned')); loadSummaries(); // 刷新按钮状态 onPickOrderAssigned?.(); onSwitchToDetailTab?.(); } 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, selectedDate, onPickOrderAssigned, onSwitchToDetailTab, loadSummaries]); const handleLaneButtonClick = useCallback(async ( storeId: string, truckDepartureTime: string, truckLanceCode: string, loadingSequence: number | null | undefined, requiredDate: string, unassigned: number, total: number ) => { // Format the date for display let dateDisplay: string; 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'); } else { dateDisplay = requiredDate; } // Show confirmation dialog 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", reverseButtons: true }); // Only proceed if user confirmed if (result.isConfirmed) { await handleAssignByLane(storeId, truckDepartureTime, truckLanceCode, loadingSequence, requiredDate); } }, [handleAssignByLane, t]); const getDateLabel = (offset: number) => { return dayjs().add(offset, 'day').format('YYYY-MM-DD'); }; // Flatten rows to create one box per lane const flattenRows = (rows: any[]) => { const flattened: any[] = []; rows.forEach(row => { row.lanes.forEach((lane: any) => { flattened.push({ truckDepartureTime: row.truckDepartureTime, lane: lane }); }); }); return flattened; }; /** 4/F:依車線匯總,同車多筆依 API 出現順序為裝載序(出發時間相同時仍可分序)。 */ 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 code = lane.truckLanceCode; const list = map.get(code); const slot: LaneSlot4F = { truckDepartureTime: row.truckDepartureTime, lane }; if (list) list.push(slot); else map.set(code, [slot]); } } return Array.from(map.entries()) .sort(([a], [b]) => a.localeCompare(b)) .map(([truckLanceCode, slots]) => ({ truckLanceCode, slots: slots .slice() .sort((a, b) => (a.lane.loadingSequence ?? 999) - (b.lane.loadingSequence ?? 999)) .map((s: LaneSlot4F, i: number) => ({ ...s, sequenceIndex: i + 1 })), })); }, [summary4F?.rows]); return ( {/* Date Selector Dropdown and Legend */} {t("Select Date")} {t("Release Type")} {t("Floor ticket")} {t("EDT - Lane Code (Unassigned/Total)")} {/* Grid containing both floors */} {/* 2/F 楼层面板 */} {ticketFloor === "2/F" && ( {/* Floor Label */} 2/F {/* Content Box */} {isLoadingSummary ? ( {t("Loading...")} ) : !summary2F?.rows || summary2F.rows.length === 0 ? ( {t("No entries available")} ) : ( {summary2F.rows.map((row) => ( {row.truckDepartureTime} {row.lanes.map((lane) => ( ))} ))} )} )} {ticketFloor === "4/F" && ( {/* Floor Label */} 4/F {/* Content Box */} {isLoadingSummary ? ( {t("Loading...")} ) : truckGroups4F.length === 0 ? ( {t("No entries available")} ) : ( {truckGroups4F.map(({ truckLanceCode, slots }) => ( {truckLanceCode} {slots.map((slot) => ( ))} ))} )} )} {/* 4/F Today default lane*/} {t("Truck X")} {defaultTruckCount === 0 ? ( {t("No entries available")} ) : ( )} {/* 2/F 未完成已放單 - 與上方相同 UI */} {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 ? ( {t("No entries available")} ) : ( {truckCounts2F.map(({ truck, count }) => ( ))} )} )} {ticketFloor === "4/F" && ( 4/F {truckCounts4F.length === 0 ? ( {t("No entries available")} ) : ( {truckCounts4F.map(({ truck, count }) => ( ))} )} )} {t("Truck X")} {beforeTodayTruckXCount === 0 ? ( {t("No entries available")} ) : ( )} setModalOpen(false)} onAssigned={() => { loadSummaries(); const loadCounts = async () => { try { const [list2F, list4F] = await Promise.all([ fetchReleasedDoPickOrdersForSelection(undefined, "2/F"), fetchReleasedDoPickOrdersForSelection(undefined, "4/F"), ]); const groupByTruck = (list: { truckLanceCode?: string | null }[]) => { const map: Record = {}; list.forEach((item) => { const t = item.truckLanceCode || "-"; map[t] = (map[t] || 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) { setTruckCounts2F([]); setTruckCounts4F([]); } }; loadCounts(); onPickOrderAssigned?.(); onSwitchToDetailTab?.(); }} /> ); }; export default FinishedGoodFloorLanePanel;