|
- "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<Props> = ({ 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<string>("2/F");
- const [selectedTruck, setSelectedTruck] = useState<string>("");
- const [selectedDefaultTruck, setSelectedDefaultTruck] = useState<string>("");
- 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<StoreLaneSummary | null>(null);
- const [summary4F, setSummary4F] = useState<StoreLaneSummary | null>(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<string>("today");
- const defaultTruckCount = summary4F?.defaultTruckCount ?? 0;
- const [beforeTodayTruckXCount, setBeforeTodayTruckXCount] = useState(0);
-
- const [selectedDate, setSelectedDate] = useState<string>("today");
- const [releaseType, setReleaseType] = useState<string>("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<string, number> = {};
- 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: `
- <div style="text-align: left; padding: 10px 0;">
- <p><strong>${t("Store")}:</strong> ${storeId}</p>
- <p><strong>${t("Lane Code")}:</strong> ${truckLanceCode}</p>
- ${loadingSequence != null ? `<p><strong>${t("Loading Sequence")}:</strong> ${loadingSequence}</p>` : ``}
- <p><strong>${t("Departure Time")}:</strong> ${truckDepartureTime}</p>
- <p><strong>${t("Required Date")}:</strong> ${dateDisplay}</p>
- <p><strong>${t("Available Orders")}:</strong> ${unassigned}/${total}</p>
- </div>
- `,
- 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<string, LaneSlot4F[]>();
- 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 (
- <Box sx={{ mb: 2 }}>
- {/* Date Selector Dropdown and Legend */}
- <Stack direction="row" spacing={2} sx={{ mb: 2, alignItems: 'flex-start' }}>
- <Box sx={{ maxWidth: 300 }}>
- <FormControl fullWidth size="small">
- <InputLabel id="date-select-label">{t("Select Date")}</InputLabel>
-
- <Select
- labelId="date-select-label"
- id="date-select"
- value={selectedDate}
- label={t("Select Date")}
-
- onChange={(e) => { {
- setSelectedDate(e.target.value);
- loadSummaries();
- }}}
- >
-
- <MenuItem value="today">
- {t("Today")} ({getDateLabel(0)})
- </MenuItem>
- <MenuItem value="tomorrow">
- {t("Tomorrow")} ({getDateLabel(1)})
- </MenuItem>
- <MenuItem value="dayAfterTomorrow">
- {t("Day After Tomorrow")} ({getDateLabel(2)})
- </MenuItem>
- </Select>
- </FormControl>
- </Box>
- <Box sx={{minWidth: 140, maxWidth: 300 }}>
- <FormControl fullWidth size="small">
- <InputLabel id="release-type-select-label">{t("Release Type")}</InputLabel>
-
- <Select
- labelId="release-type-select-label"
- id="release-type-select"
- value={releaseType}
- label={t("Release Type")}
-
- onChange={(e) => { {
- setReleaseType(e.target.value);
- loadSummaries();
- }}}
- >
-
- <MenuItem value="batch">
- {t("Batch")}
- </MenuItem>
- <MenuItem value="single">
- {t("Single")}
- </MenuItem>
-
- </Select>
- </FormControl>
- </Box>
- <Box sx={{ minWidth: 120, maxWidth: 200 }}>
- <FormControl fullWidth size="small">
- <InputLabel id="ticket-floor-select-label">{t("Floor ticket")}</InputLabel>
- <Select
- labelId="ticket-floor-select-label"
- id="ticket-floor-select"
- value={ticketFloor}
- label={t("Floor ticket")}
- onChange={(e) => setTicketFloor(e.target.value as "2/F" | "4/F")}
- >
- <MenuItem value="2/F">{t("2F ticket")}</MenuItem>
- <MenuItem value="4/F">{t("4F ticket")}</MenuItem>
- </Select>
- </FormControl>
- </Box>
- <Box
- sx={{
- p: 1,
- backgroundColor: '#fafafa',
- borderRadius: 1,
- border: '1px solid #e0e0e0',
- flex: 1,
- maxWidth: 400
- }}
- >
- <Typography variant="body2" sx={{ display: 'block', color: 'text.secondary', fontWeight: 600 }}>
- {t("EDT - Lane Code (Unassigned/Total)")}
- </Typography>
- </Box>
- </Stack>
-
- {/* Grid containing both floors */}
- <Grid container spacing={2}>
- {/* 2/F 楼层面板 */}
- {ticketFloor === "2/F" && (
- <Grid item xs={12}>
- <Stack direction="row" spacing={2} alignItems="flex-start">
- {/* Floor Label */}
- <Typography
- variant="h6"
- sx={{
- fontWeight: 600,
- minWidth: 60,
- pt: 1
- }}
- >
- 2/F
- </Typography>
-
- {/* Content Box */}
- <Box
- sx={{
- border: '1px solid #e0e0e0',
- borderRadius: 1,
- p: 1,
- backgroundColor: '#fafafa',
- flex: 1
- }}
- >
- {isLoadingSummary ? (
- <Typography variant="caption"> {t("Loading...")}</Typography>
- ) : !summary2F?.rows || summary2F.rows.length === 0 ? (
- <Typography
- variant="body2"
- color="text.secondary"
- sx={{
- fontWeight: 600,
- fontSize: '1rem',
- textAlign: 'center',
- py: 1
- }}
- >
- {t("No entries available")}
- </Typography>
- ) : (
- <Grid container spacing={1}>
- {summary2F.rows.map((row) => (
- <Grid item xs={12} key={row.truckDepartureTime}>
- <Stack
- direction={{ xs: "column", sm: "row" }}
- spacing={1}
- alignItems={{ xs: "stretch", sm: "center" }}
- sx={{
- border: "1px solid #e0e0e0",
- borderRadius: 0.5,
- p: 1,
- backgroundColor: "#fff",
- }}
- >
- <Typography
- variant="body2"
- sx={{
- fontWeight: 600,
- fontSize: "1rem",
- minWidth: { sm: 60 },
- whiteSpace: "nowrap",
- pt: { xs: 0, sm: 0.5 },
- }}
- >
- {row.truckDepartureTime}
- </Typography>
-
- <Stack
- direction="row"
- flexWrap="wrap"
- sx={{ flex: 1, gap: 1 }}
- >
- {row.lanes.map((lane) => (
- <Button
- key={`${row.truckDepartureTime}-${lane.truckLanceCode}`}
- variant="outlined"
- size="medium"
- disabled={lane.unassigned === 0 || isAssigning}
- onClick={() =>
- handleLaneButtonClick(
- "2/F",
- row.truckDepartureTime,
- lane.truckLanceCode,
- null,
- selectedDate,
- lane.unassigned,
- lane.total
- )
- }
- sx={{
- fontSize: "1.1rem",
- py: 1,
- px: 1.5,
- borderWidth: 1,
- borderColor: "#ccc",
- fontWeight: 500,
- "&:hover": {
- borderColor: "#999",
- backgroundColor: "#f5f5f5",
- },
- }}
- >
- {`${lane.truckLanceCode} (${lane.unassigned}/${lane.total})`}
- </Button>
- ))}
- </Stack>
- </Stack>
- </Grid>
- ))}
- </Grid>
- )}
- </Box>
- </Stack>
- </Grid>
- )}
- {ticketFloor === "4/F" && (
- <Grid item xs={12}>
- <Stack direction="row" spacing={2} alignItems="flex-start">
- {/* Floor Label */}
- <Typography
- variant="h6"
- sx={{
- fontWeight: 600,
- minWidth: 60,
- pt: 1
- }}
- >
- 4/F
- </Typography>
-
- {/* Content Box */}
- <Box
- sx={{
- border: '1px solid #e0e0e0',
- borderRadius: 1,
- p: 1,
- backgroundColor: '#fafafa',
- flex: 1
- }}
- >
- {isLoadingSummary ? (
- <Typography variant="caption">{t("Loading...")}</Typography>
- ) : truckGroups4F.length === 0 ? (
- <Typography
- variant="body2"
- color="text.secondary"
- sx={{
- fontWeight: 600,
- fontSize: '1rem',
- textAlign: 'center',
- py: 1
- }}
- >
- {t("No entries available")}
- </Typography>
- ) : (
- <Grid container spacing={1}>
- {truckGroups4F.map(({ truckLanceCode, slots }) => (
- <Grid item xs={12} key={truckLanceCode}>
- <Stack
- direction={{ xs: "column", sm: "row" }}
- spacing={1}
- alignItems={{ xs: "stretch", sm: "center" }}
- sx={{
- border: "1px solid #e0e0e0",
- borderRadius: 0.5,
- p: 1,
- backgroundColor: "#fff",
- }}
- >
- <Typography
- variant="body2"
- sx={{
- fontWeight: 700,
- fontSize: "1rem",
- minWidth: { sm: 160 },
- pt: { xs: 0, sm: 0.5 },
- }}
- >
- {truckLanceCode}
- </Typography>
- <Stack
- direction="row"
- flexWrap="wrap"
- sx={{ flex: 1, gap: 1 }}
- >
- {slots.map((slot) => (
- <Button
- key={`${truckLanceCode}-${slot.sequenceIndex}-${slot.lane.truckLanceCode}-${slot.truckDepartureTime}`}
- variant="outlined"
- size="medium"
- disabled={slot.lane.unassigned === 0 || isAssigning}
- onClick={() =>
- handleLaneButtonClick(
- "4/F",
- slot.truckDepartureTime,
- slot.lane.truckLanceCode,
- slot.lane.loadingSequence ?? null,
- selectedDate,
- slot.lane.unassigned,
- slot.lane.total
- )
- }
- sx={{
- fontSize: "1rem",
- py: 0.75,
- px: 1.25,
- borderWidth: 1,
- borderColor: "#ccc",
- fontWeight: 500,
- "&:hover": {
- borderColor: "#999",
- backgroundColor: "#f5f5f5",
- },
- }}
- >
- {`${t("Loading sequence n", { n: slot.lane.loadingSequence ?? slot.sequenceIndex })} (${slot.lane.unassigned}/${slot.lane.total})`}
- </Button>
- ))}
- </Stack>
- </Stack>
- </Grid>
- ))}
- </Grid>
- )}
- </Box>
- </Stack>
- </Grid>
- )}
- {/* 4/F Today default lane*/}
- <Grid item xs={12}>
-
- <Stack direction="row" spacing={2} alignItems="flex-start">
- <Typography sx={{ fontWeight: 600, minWidth: 60, pt: 1 }}>{t("Truck X")}</Typography>
- <Box
- sx={{
- border: '1px solid #e0e0e0',
- borderRadius: 1,
- p: 1,
- backgroundColor: '#fafafa',
- flex: 1
- }}
- >
- {defaultTruckCount === 0 ? (
- <Typography >{t("No entries available")}</Typography>
- ) : (
- <Button
- variant="outlined"
- size="medium"
- onClick={() => {
- // Truck X 綁 4/F,如果你要放在 4/F 區塊
- setSelectedStore("");
- // 真正的 Truck lane code:車線-X
- setSelectedTruck("車線-X");
- // 告訴 modal 這是 default truck 模式
- setIsDefaultTruck(true);
- // 打開 modal
- setModalOpen(true);
- setDefaultDateScope("today");
- }}
- >
- {`${t("Truck X")} (${defaultTruckCount})`}
- </Button>
- )}
- </Box>
- </Stack>
- </Grid>
- {/* 2/F 未完成已放單 - 與上方相同 UI */}
- <Grid item xs={12}>
- <Box
- sx={{
- py: 2,
- mt: 1,
- mb: 0.5,
- borderTop: "1px solid #e0e0e0",
- }}
- >
- <Typography
- variant="subtitle1"
- sx={{ fontWeight: 600, mb: 0.5 }}
- >
- {t("Not yet finished released do pick orders")}
- </Typography>
- <Typography variant="body2" color="text.secondary">
- {t("Released orders not yet completed - click lane to select and assign")}
- </Typography>
- </Box>
-
- </Grid>
- {ticketFloor === "2/F" && (
- <Grid item xs={12}>
- <Stack direction="row" spacing={2} alignItems="flex-start">
- <Typography variant="h6" sx={{ fontWeight: 600, minWidth: 60, pt: 1 }}>
- 2/F
- </Typography>
- <Box
- sx={{
- border: "1px solid #e0e0e0",
- borderRadius: 1,
- p: 1,
- backgroundColor: "#fafafa",
- flex: 1,
- }}
- >
- {truckCounts2F.length === 0 ? (
- <Typography
- variant="body2"
- color="text.secondary"
- sx={{
- fontWeight: 600,
- fontSize: "1rem",
- textAlign: "center",
- py: 1,
- }}
- >
- {t("No entries available")}
- </Typography>
- ) : (
- <Grid container spacing={1}>
- {truckCounts2F.map(({ truck, count }) => (
- <Grid item xs={6} sm={4} md={3} key={`2F-${truck}`} sx={{ display: "flex" }}>
-
-
- <Button
- variant="outlined"
- size="medium"
- onClick={() => {
- setIsDefaultTruck(false);
- setSelectedStore("2/F");
- setSelectedTruck(truck);
- setModalOpen(true);
- }}
- sx={{
- flex: 1,
- fontSize: "1.1rem",
- py: 1,
- px: 1.5,
- borderWidth: 1,
- borderColor: "#ccc",
- fontWeight: 500,
- "&:hover": {
- borderColor: "#999",
- backgroundColor: "#f5f5f5",
- },
- }}
- >
- {`${truck} (${count})`}
- </Button>
-
- </Grid>
- ))}
- </Grid>
- )}
- </Box>
- </Stack>
- </Grid>
- )}
- {ticketFloor === "4/F" && (
- <Grid item xs={12}>
- <Stack direction="row" spacing={2} alignItems="flex-start">
- <Typography variant="h6" sx={{ fontWeight: 600, minWidth: 60, pt: 1 }}>
- 4/F
- </Typography>
- <Box
- sx={{
- border: "1px solid #e0e0e0",
- borderRadius: 1,
- p: 1,
- backgroundColor: "#fafafa",
- flex: 1,
- }}
- >
- {truckCounts4F.length === 0 ? (
- <Typography
- variant="body2"
- color="text.secondary"
- sx={{
- fontWeight: 600,
- fontSize: "1rem",
- textAlign: "center",
- py: 1,
- }}
- >
- {t("No entries available")}
- </Typography>
- ) : (
- <Grid container spacing={1}>
- {truckCounts4F.map(({ truck, count }) => (
- <Grid item xs={6} sm={4} md={3} key={`4F-${truck}`} sx={{ display: "flex" }}>
-
-
- <Button
- variant="outlined"
- size="medium"
- onClick={() => {
- setIsDefaultTruck(false);
- setSelectedStore("4/F");
- setSelectedTruck(truck);
- setModalOpen(true);
- }}
- sx={{
- flex: 1,
- fontSize: "1.1rem",
- py: 1,
- px: 1.5,
- borderWidth: 1,
- borderColor: "#ccc",
- fontWeight: 500,
- "&:hover": {
- borderColor: "#999",
- backgroundColor: "#f5f5f5",
- },
- }}
- >
- {`${truck} (${count})`}
- </Button>
-
- </Grid>
- ))}
- </Grid>
- )}
- </Box>
- </Stack>
- </Grid>
- )}
- <Grid item xs={12}>
-
-
- <Stack direction="row" spacing={2}>
- <Typography sx={{ fontWeight: 600, minWidth: 60, pt: 1 }}>{t("Truck X")} </Typography>
- <Box
- sx={{
- border: '1px solid #e0e0e0',
- borderRadius: 1,
- p: 1,
- backgroundColor: '#fafafa',
- flex: 1
- }}
- >
- {beforeTodayTruckXCount === 0 ? (
- <Typography>{t("No entries available")}</Typography>
- ) : (
- <Button
- variant="outlined"
- size="medium"
- onClick={() => {
- setSelectedStore("4/F"); // 或用專門標示 Truck X
- setSelectedTruck("車線-X");
- setIsDefaultTruck(true);
- setDefaultDateScope("before"); // 類似上一輪說的 dateScope
- setModalOpen(true);
- }}
- >
- {`${t("Truck X")} (${beforeTodayTruckXCount})`}
- </Button>
- )}
- </Box>
- </Stack>
- </Grid>
- <ReleasedDoPickOrderSelectModal
- open={modalOpen}
- storeId={selectedStore}
- truck={selectedTruck}
- isDefaultTruck={isDefaultTruck}
- defaultDateScope={defaultDateScope}
- onClose={() => 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<string, number> = {};
- 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?.();
- }}
- />
- </Grid>
-
-
-
-
-
- </Box>
- );
- };
-
- export default FinishedGoodFloorLanePanel;
|