|
- // 新建檔案
- "use client";
-
- import {
- Modal,
- Box,
- Typography,
- TextField,
- Table,
- TableBody,
- TableCell,
- TableContainer,
- TableHead,
- TableRow,
- Paper,
- CircularProgress,
- Button,
- } from "@mui/material";
- import { useCallback, useEffect, useState } from "react";
- import { useTranslation } from "react-i18next";
- import {
- fetchReleasedDoPickOrdersForSelection,
- assignByDoPickOrderId,
- type ReleasedDoPickOrderListItem,
- fetchReleasedDoPickOrdersForSelectionToday,
- type PostPickOrderResponse,
- } from "@/app/api/pickOrder/actions";
- import { useSession } from "next-auth/react";
- import { SessionWithTokens } from "@/config/authConfig";
- import Swal from "sweetalert2";
- import { requiredDeliveryDateToDayString } from "@/app/utils/formatUtil";
-
- /** DO workbench 使用 workbench released API;Finished Good 不傳即可 */
- export type ReleasedDoPickListBridge = {
- loadBeforeToday: (
- shopName?: string,
- storeId?: string,
- truck?: string,
- releaseType?: string,
- /** Optional `2F`/`4F` for Truck X supplier-floor split */
- floor?: string
- ) => Promise<ReleasedDoPickOrderListItem[]>;
- /** Optional 4th arg: workbench `requiredDeliveryDate` (YYYY-MM-DD) for default truck list; omit = calendar today. */
- loadToday: (
- shopName?: string,
- storeId?: string,
- truck?: string,
- requiredDeliveryDate?: string,
- releaseType?: string,
- /** Optional `2F`/`4F` for Truck X supplier-floor split */
- floor?: string
- ) => Promise<ReleasedDoPickOrderListItem[]>;
- assignByListItemId: (userId: number, id: number) => Promise<PostPickOrderResponse>;
- };
-
- interface Props {
- open: boolean;
- onClose: () => void;
- onAssigned: () => void;
- storeId: string;
- truck: string;
- isDefaultTruck: boolean;
- /** Truck X only: today → released-today; before → released (歷史未完工) */
- defaultDateScope?: "today" | "before";
- listBridge?: ReleasedDoPickListBridge;
- /** Workbench: `delivery_order_pick_order.requiredDeliveryDate` for Truck X (select day); used when [defaultDateScope] is today. */
- defaultTruckRequiredDeliveryDate?: string;
- /** Workbench: filter list to `releaseType` (e.g. `isExtra`). */
- releaseTypeFilter?: string;
- /** Prefill shop search when opening (e.g. Etra lane picker). */
- initialShopSearch?: string;
- /**
- * When set with a workbench listBridge, non–Truck-X list uses released-today with this
- * requiredDate instead of historical released (delivery date before calendar today).
- */
- filterRequiredDeliveryDate?: string;
- /**
- * Truck X only: `2F`/`4F` (or `2/F`/`4/F`) — filter by DO supplier preferred floor; storeId stays null.
- */
- truckXFloor?: string;
- }
-
- /** FP-MTMS Version Checklist | Functions Ref. No. 36 | v1.0.0 | 2026-07-27 */
- const ReleasedDoPickOrderSelectModal: React.FC<Props> = ({
- open,
- onClose,
- onAssigned,
- storeId,
- truck,
- isDefaultTruck,
- defaultDateScope: defaultDateScopeProp = "today",
- listBridge,
- defaultTruckRequiredDeliveryDate,
- releaseTypeFilter,
- initialShopSearch,
- filterRequiredDeliveryDate,
- truckXFloor,
- }) => {
- const { t } = useTranslation("pickOrder");
- const { data: session } = useSession() as { data: SessionWithTokens | null };
- const currentUserId = session?.id ? parseInt(session.id) : undefined;
- const [list, setList] = useState<ReleasedDoPickOrderListItem[]>([]);
- const [loading, setLoading] = useState(false);
- const [shopSearch, setShopSearch] = useState("");
- const [isAssigning, setIsAssigning] = useState(false);
-
- const loadList = useCallback(async () => {
- if (!open) return;
- setLoading(true);
- try {
- let data: ReleasedDoPickOrderListItem[];
- const loadReleased = listBridge?.loadBeforeToday ?? fetchReleasedDoPickOrdersForSelection;
- const loadTodayFn = listBridge?.loadToday ?? fetchReleasedDoPickOrdersForSelectionToday;
-
- const floorArg = truckXFloor?.trim() || undefined;
- const shopArg = shopSearch.trim() || undefined;
- if (isDefaultTruck) {
- if (defaultDateScopeProp === "today") {
- data = await loadTodayFn(
- shopArg,
- undefined,
- "車線-X",
- defaultTruckRequiredDeliveryDate?.trim() || undefined,
- releaseTypeFilter?.trim() || undefined,
- floorArg
- );
- } else {
- data = await loadReleased(
- shopArg,
- undefined,
- "車線-X",
- releaseTypeFilter?.trim() || undefined,
- floorArg
- );
- }
- } else if (filterRequiredDeliveryDate?.trim() && listBridge?.loadToday) {
- data = await listBridge.loadToday(
- shopSearch.trim() || undefined,
- storeId,
- truck?.trim() || undefined,
- filterRequiredDeliveryDate.trim(),
- releaseTypeFilter?.trim() || undefined
- );
- } else {
- data = await loadReleased(
- shopSearch.trim() || undefined,
- storeId,
- truck?.trim() || undefined,
- releaseTypeFilter?.trim() || undefined
- );
- }
-
- setList(data);
- } catch (e) {
- console.error(e);
- setList([]);
- } finally {
- setLoading(false);
- }
- }, [open, shopSearch, storeId, truck, isDefaultTruck, defaultDateScopeProp, listBridge, defaultTruckRequiredDeliveryDate, releaseTypeFilter, filterRequiredDeliveryDate, truckXFloor]);
-
- useEffect(() => {
- loadList();
- }, [loadList]);
-
- useEffect(() => {
- if (!open) return;
- setShopSearch(initialShopSearch?.trim() ?? "");
- }, [open, initialShopSearch]);
-
- const handleSelectRow = useCallback(
- async (item: ReleasedDoPickOrderListItem) => {
- if (!currentUserId) return;
- const confirmResult = await Swal.fire({
- title: t("Confirm Assignment"),
- html: `
- <div style="text-align: left;">
- <p><strong>${t("Date")}:</strong> ${requiredDeliveryDateToDayString(item.requiredDeliveryDate) || "-"}</p>
- <p><strong>${t("Shop")}:</strong> ${item.shopName ?? item.shopCode ?? "-"}</p>
- <p><strong>${t("Truck")}:</strong> ${item.truckLanceCode ?? "-"}</p>
- <p><strong>${t("Delivery Order")}:</strong> ${(item.deliveryOrderCodes ?? []).join(", ")}</p>
- </div>
- `,
- icon: "question",
- showCancelButton: true,
- confirmButtonText: t("Confirm"),
- cancelButtonText: t("Cancel"),
- confirmButtonColor: "#8dba00",
- cancelButtonColor: "#F04438",
- didOpen: () => {
- const container = document.querySelector('.swal2-container');
- const popup = Swal.getPopup();
- if (container) (container as HTMLElement).style.zIndex = '9999';
- if (popup) popup.style.zIndex = '9999';
- }
- });
-
- if (!confirmResult.isConfirmed) return;
-
- setIsAssigning(true);
- try {
- const assignFn = listBridge?.assignByListItemId ?? assignByDoPickOrderId;
- const res = await assignFn(currentUserId, item.id);
- if (res?.code === "SUCCESS") {
- Swal.fire({
- icon: "success",
- text: t("Assigned successfully"),
- timer: 1500,
- showConfirmButton: false,
- });
- onAssigned?.();
- onClose();
- } else {
- Swal.fire({
- icon: "error",
- text: res?.message ?? t("Assignment failed"),
- });
- }
- } catch (e) {
- console.error(e);
- Swal.fire({ icon: "error", text: t("Error occurred during assignment.") });
- } finally {
- setIsAssigning(false);
- }
- },
- [currentUserId, t, onAssigned, onClose, listBridge]
- );
-
- return (
- <Modal open={open} onClose={onClose}>
- <Box
- sx={{
- position: "absolute",
- top: "50%",
- left: "50%",
- transform: "translate(-50%, -50%)",
- width: { xs: "95%", sm: 900 },
- maxHeight: "80vh",
- bgcolor: "background.paper",
- borderRadius: 2,
- boxShadow: 24,
- p: 2,
- display: "flex",
- flexDirection: "column",
- overflow: "hidden",
- }}
- >
- <Typography variant="h6" sx={{ mb: 2 }}>
- {t("Not Yet Finished Released Do Pick Orders")}
- </Typography>
-
- <TextField
- label={t("Search by Shop")}
- size="small"
- value={shopSearch}
- onChange={(e) => setShopSearch(e.target.value)}
- sx={{ mb: 2 }}
- fullWidth
- />
-
- {loading ? (
- <Box sx={{ display: "flex", justifyContent: "center", p: 3 }}>
- <CircularProgress />
- </Box>
- ) : (
- <TableContainer sx={{ flex: 1, overflow: "auto" }}>
- <Table size="small" stickyHeader>
- <TableHead>
- <TableRow>
- <TableCell>{t("Date")}</TableCell>
- <TableCell>{t("Shop")}</TableCell>
- <TableCell>{t("Truck")}</TableCell>
- <TableCell>{t("Delivery Order Code")}</TableCell>
- <TableCell align="right">{t("Action")}</TableCell>
- </TableRow>
- </TableHead>
- <TableBody>
- {list.map((row) => (
- <TableRow key={row.id} hover>
- <TableCell>
- {requiredDeliveryDateToDayString(row.requiredDeliveryDate) || "-"}
- </TableCell>
- <TableCell>{row.shopName ?? row.shopCode ?? "-"}</TableCell>
- <TableCell>{row.truckLanceCode ?? "-"}</TableCell>
- <TableCell sx={{ whiteSpace: "pre-line" }}>
- {(row.deliveryOrderCodes ?? []).join("\n") || "-"}
- </TableCell>
- <TableCell align="right">
- <Button
- variant="outlined"
- size="small"
- disabled={isAssigning}
- onClick={() => handleSelectRow(row)}
- >
- {t("Select")}
- </Button>
- </TableCell>
- </TableRow>
- ))}
- </TableBody>
- </Table>
- </TableContainer>
- )}
-
- {!loading && list.length === 0 && (
- <Typography color="text.secondary" sx={{ py: 3, textAlign: "center" }}>
- {t("No entries available")}
- </Typography>
- )}
-
- <Box sx={{ mt: 2, display: "flex", justifyContent: "flex-end" }}>
- <Button onClick={onClose} variant="outlined">
- {t("Close")}
- </Button>
- </Box>
- </Box>
- </Modal>
- );
- };
-
- export default ReleasedDoPickOrderSelectModal;
|