| @@ -36,7 +36,7 @@ const Page: React.FC<Props> = async ({ searchParams }) => { | |||||
| </p> | </p> | ||||
| <I18nProvider namespaces={["do", "common"]}> | <I18nProvider namespaces={["do", "common"]}> | ||||
| <Suspense fallback={<DoDetail.Loading />}> | <Suspense fallback={<DoDetail.Loading />}> | ||||
| <DoDetail id={parseInt(id)} /> | |||||
| <DoDetail id={parseInt(id)} workbenchRelease /> | |||||
| </Suspense> | </Suspense> | ||||
| </I18nProvider> | </I18nProvider> | ||||
| </> | </> | ||||
| @@ -231,19 +231,23 @@ export async function fetchWorkbenchReleasedDoPickOrdersForSelection( | |||||
| return response ?? []; | return response ?? []; | ||||
| } | } | ||||
| /** When `requiredDeliveryDate` is set (YYYY-MM-DD), filters `delivery_order_pick_order.requiredDeliveryDate`; otherwise calendar today. */ | |||||
| export async function fetchWorkbenchReleasedDoPickOrdersForSelectionToday( | export async function fetchWorkbenchReleasedDoPickOrdersForSelectionToday( | ||||
| shopName?: string, | shopName?: string, | ||||
| storeId?: string, | storeId?: string, | ||||
| truck?: string | |||||
| truck?: string, | |||||
| requiredDeliveryDate?: string | |||||
| ): Promise<ReleasedDoPickOrderListItem[]> { | ): Promise<ReleasedDoPickOrderListItem[]> { | ||||
| const params = new URLSearchParams(); | const params = new URLSearchParams(); | ||||
| if (shopName?.trim()) params.append("shopName", shopName.trim()); | if (shopName?.trim()) params.append("shopName", shopName.trim()); | ||||
| if (storeId?.trim()) params.append("storeId", storeId.trim()); | if (storeId?.trim()) params.append("storeId", storeId.trim()); | ||||
| if (truck?.trim()) params.append("truck", truck.trim()); | if (truck?.trim()) params.append("truck", truck.trim()); | ||||
| if (requiredDeliveryDate?.trim()) params.append("requiredDate", requiredDeliveryDate.trim()); | |||||
| const query = params.toString(); | const query = params.toString(); | ||||
| const url = `${BASE_API_URL}/doPickOrder/workbench/released-today${query ? `?${query}` : ""}`; | const url = `${BASE_API_URL}/doPickOrder/workbench/released-today${query ? `?${query}` : ""}`; | ||||
| const response = await serverFetchJson<ReleasedDoPickOrderListItem[]>(url, { method: "GET" }); | const response = await serverFetchJson<ReleasedDoPickOrderListItem[]>(url, { method: "GET" }); | ||||
| return response ?? []; | |||||
| if (response == null) return []; | |||||
| return Array.isArray(response) ? response : []; | |||||
| } | } | ||||
| /** Same body as `/doPickOrder/assign-by-lane` but resolves `delivery_order_pick_order`. */ | /** Same body as `/doPickOrder/assign-by-lane` but resolves `delivery_order_pick_order`. */ | ||||
| @@ -356,7 +360,21 @@ export async function fetchWorkbenchAvailableLotsByItem(itemId: number) { | |||||
| }, | }, | ||||
| ); | ); | ||||
| } | } | ||||
| /** Single DO; JSON body is one number (same as legacy `batch-release/async-single`). */ | |||||
| export async function startWorkbenchBatchReleaseAsyncSingleV2(data: { | |||||
| doId: number; | |||||
| userId: number; | |||||
| }): Promise<WorkbenchMessageResponse> { | |||||
| const { doId, userId } = data; | |||||
| return serverFetchJson<WorkbenchMessageResponse>( | |||||
| `${BASE_API_URL}/doPickOrder/workbench/batch-release/async-single-v2?userId=${userId}`, | |||||
| { | |||||
| method: "POST", | |||||
| body: JSON.stringify(doId), | |||||
| headers: { "Content-Type": "application/json" }, | |||||
| } | |||||
| ); | |||||
| } | |||||
| export async function printWorkbenchLotLabel(params: { | export async function printWorkbenchLotLabel(params: { | ||||
| inventoryLotLineId: number; | inventoryLotLineId: number; | ||||
| printerId: number; | printerId: number; | ||||
| @@ -1677,7 +1677,8 @@ export const fetchReleasedDoPickOrdersForSelection = async ( | |||||
| export const fetchReleasedDoPickOrdersForSelectionToday = async ( | export const fetchReleasedDoPickOrdersForSelectionToday = async ( | ||||
| shopName?: string, | shopName?: string, | ||||
| storeId?: string, | storeId?: string, | ||||
| truck?: string | |||||
| truck?: string, | |||||
| _requiredDeliveryDate?: string | |||||
| ): Promise<ReleasedDoPickOrderListItem[]> => { | ): Promise<ReleasedDoPickOrderListItem[]> => { | ||||
| const params = new URLSearchParams(); | const params = new URLSearchParams(); | ||||
| if (shopName?.trim()) params.append("shopName", shopName.trim()); | if (shopName?.trim()) params.append("shopName", shopName.trim()); | ||||
| @@ -68,6 +68,18 @@ export const arrayToDayjs = (arr: ConfigType | (number | undefined)[], showTime: | |||||
| return dayjs(tempArr as ConfigType); | return dayjs(tempArr as ConfigType); | ||||
| }; | }; | ||||
| /** | |||||
| * Backend `LocalDate` is often JSON `[year, month, day]` with month 1–12. | |||||
| * Do not pass that array to `dayjs(arr)` — it uses JS month index 0–11 and shifts the calendar month. | |||||
| */ | |||||
| export function requiredDeliveryDateToDayString(value: unknown): string { | |||||
| if (value == null) return ""; | |||||
| if (Array.isArray(value) && value.length >= 3 && value.every((x) => typeof x === "number")) { | |||||
| return arrayToDayjs(value as number[]).format(OUTPUT_DATE_FORMAT); | |||||
| } | |||||
| return dayjs(value as string | number | Date).format(OUTPUT_DATE_FORMAT); | |||||
| } | |||||
| export const arrayToDateString = (arr: ConfigType | (number | undefined)[], format: "input"|"output" = "output") => { | export const arrayToDateString = (arr: ConfigType | (number | undefined)[], format: "input"|"output" = "output") => { | ||||
| if (format == "output") { | if (format == "output") { | ||||
| return arrayToDayjs(arr).format(OUTPUT_DATE_FORMAT); | return arrayToDayjs(arr).format(OUTPUT_DATE_FORMAT); | ||||
| @@ -9,7 +9,12 @@ import { useCallback, useState } from "react"; | |||||
| import { Button, Stack, Typography, Box, Alert } from "@mui/material"; | import { Button, Stack, Typography, Box, Alert } from "@mui/material"; | ||||
| import ArrowBackIcon from '@mui/icons-material/ArrowBack'; | import ArrowBackIcon from '@mui/icons-material/ArrowBack'; | ||||
| import StartIcon from "@mui/icons-material/Start"; | import StartIcon from "@mui/icons-material/Start"; | ||||
| import { releaseDo,startBatchReleaseAsyncSingle, assignPickOrderByStore, releaseAssignedPickOrderByStore } from "@/app/api/do/actions"; | |||||
| import { releaseDo, startBatchReleaseAsyncSingle, assignPickOrderByStore, releaseAssignedPickOrderByStore } from "@/app/api/do/actions"; | |||||
| import { | |||||
| getWorkbenchBatchReleaseProgress, | |||||
| startWorkbenchBatchReleaseAsyncSingleV2, | |||||
| } from "@/app/api/doworkbench/actions"; | |||||
| import Swal from "sweetalert2"; | |||||
| import DoInfoCard from "./DoInfoCard"; | import DoInfoCard from "./DoInfoCard"; | ||||
| import DoLineTable from "./DoLineTable"; | import DoLineTable from "./DoLineTable"; | ||||
| import { useSession } from "next-auth/react"; | import { useSession } from "next-auth/react"; | ||||
| @@ -63,14 +68,20 @@ console.log("🔍 DoSearch - currentUserId:", currentUserId); | |||||
| //userId: currentUserId // Pass user ID from session | //userId: currentUserId // Pass user ID from session | ||||
| }) | }) | ||||
| */ | */ | ||||
| const response = await startBatchReleaseAsyncSingle({ | |||||
| const response = await startWorkbenchBatchReleaseAsyncSingleV2({ | |||||
| doId: id, | doId: id, | ||||
| userId: currentUserId ?? 0 | userId: currentUserId ?? 0 | ||||
| }) | }) | ||||
| if (response) { | |||||
| formProps.setValue("status", response.entity.status) | |||||
| setSuccessMessage(t("DO released successfully! Pick orders created.")) | |||||
| } | |||||
| if (response?.code === "STARTED") { | |||||
| setSuccessMessage(t("DO released successfully! Pick orders created.")); | |||||
| router.refresh(); | |||||
| } else if (response) { | |||||
| setServerError( | |||||
| response.message ?? | |||||
| response.code ?? | |||||
| t("An error has occurred. Please try again later."), | |||||
| ); | |||||
| } | |||||
| } | } | ||||
| } catch (e) { | } catch (e) { | ||||
| setServerError(t("An error has occurred. Please try again later.")); | setServerError(t("An error has occurred. Please try again later.")); | ||||
| @@ -9,16 +9,19 @@ interface SubComponents { | |||||
| type DoDetailProps = { | type DoDetailProps = { | ||||
| id?: number; | id?: number; | ||||
| /** When true (e.g. `/doworkbench/edit`), Release uses workbench `delivery_order_pick_order` async V2 + job progress. */ | |||||
| workbenchRelease?: boolean; | |||||
| } | } | ||||
| type Props = DoDetailProps | type Props = DoDetailProps | ||||
| const DoDetailWrapper: React.FC<Props> & SubComponents = async ({ | const DoDetailWrapper: React.FC<Props> & SubComponents = async ({ | ||||
| id, | id, | ||||
| workbenchRelease = false, | |||||
| }) => { | }) => { | ||||
| const doDetail = id ? await fetchDoDetail(id) : undefined | const doDetail = id ? await fetchDoDetail(id) : undefined | ||||
| return <DoDetail id={id} defaultValues={doDetail}/> | |||||
| return <DoDetail id={id} defaultValues={doDetail} workbenchRelease={workbenchRelease} /> | |||||
| } | } | ||||
| DoDetailWrapper.Loading = GeneralLoading; | DoDetailWrapper.Loading = GeneralLoading; | ||||
| @@ -47,6 +47,13 @@ const WorkbenchFloorLanePanel: React.FC<Props> = ({ onPickOrderAssigned, onSwitc | |||||
| const [ticketFloor, setTicketFloor] = useState<"2/F" | "4/F">("2/F"); | const [ticketFloor, setTicketFloor] = useState<"2/F" | "4/F">("2/F"); | ||||
| const defaultTruckCount = summary4F?.defaultTruckCount ?? 0; | const defaultTruckCount = summary4F?.defaultTruckCount ?? 0; | ||||
| const selectedDeliveryDateYmd = useMemo(() => { | |||||
| if (selectedDate === "today") return dayjs().format("YYYY-MM-DD"); | |||||
| if (selectedDate === "tomorrow") return dayjs().add(1, "day").format("YYYY-MM-DD"); | |||||
| if (selectedDate === "dayAfterTomorrow") return dayjs().add(2, "day").format("YYYY-MM-DD"); | |||||
| return dayjs().format("YYYY-MM-DD"); | |||||
| }, [selectedDate]); | |||||
| const hasLoggedRef = useRef(false); | const hasLoggedRef = useRef(false); | ||||
| const fullReadyLoggedRef = useRef(false); | const fullReadyLoggedRef = useRef(false); | ||||
| const pendingRef = useRef(0); | const pendingRef = useRef(0); | ||||
| @@ -54,7 +61,13 @@ const WorkbenchFloorLanePanel: React.FC<Props> = ({ onPickOrderAssigned, onSwitc | |||||
| const workbenchReleasedListBridge = useMemo( | const workbenchReleasedListBridge = useMemo( | ||||
| () => ({ | () => ({ | ||||
| loadBeforeToday: fetchWorkbenchReleasedDoPickOrdersForSelection, | loadBeforeToday: fetchWorkbenchReleasedDoPickOrdersForSelection, | ||||
| loadToday: fetchWorkbenchReleasedDoPickOrdersForSelectionToday, | |||||
| loadToday: ( | |||||
| shopName?: string, | |||||
| storeId?: string, | |||||
| truck?: string, | |||||
| requiredDeliveryDate?: string | |||||
| ) => | |||||
| fetchWorkbenchReleasedDoPickOrdersForSelectionToday(shopName, storeId, truck, requiredDeliveryDate), | |||||
| assignByListItemId: assignByDeliveryOrderPickOrderId, | assignByListItemId: assignByDeliveryOrderPickOrderId, | ||||
| }), | }), | ||||
| [], | [], | ||||
| @@ -358,6 +371,38 @@ const WorkbenchFloorLanePanel: React.FC<Props> = ({ onPickOrderAssigned, onSwitc | |||||
| </Grid> | </Grid> | ||||
| )} | )} | ||||
| <Grid item xs={12}> | |||||
| <Stack direction="row" spacing={2} alignItems="flex-start"> | |||||
| <Stack sx={{ minWidth: 60, pt: 1 }} spacing={0.25}> | |||||
| <Typography sx={{ fontWeight: 600 }}>{t("Truck X")}</Typography> | |||||
| {/* | |||||
| <Typography variant="caption" color="text.secondary" sx={{ lineHeight: 1.2 }}> | |||||
| {t("Required Date")}: {selectedDeliveryDateYmd} | |||||
| </Typography> | |||||
| */} | |||||
| </Stack> | |||||
| <Box sx={{ border: "1px solid #e0e0e0", borderRadius: 1, p: 1, backgroundColor: "#fafafa", flex: 1 }}> | |||||
| {defaultTruckCount === 0 ? ( | |||||
| renderNoEntry() | |||||
| ) : ( | |||||
| <Button | |||||
| variant="outlined" | |||||
| onClick={() => { | |||||
| setSelectedStore(""); | |||||
| setSelectedTruck("車線-X"); | |||||
| setIsDefaultTruck(true); | |||||
| setDefaultDateScope("today"); | |||||
| setModalOpen(true); | |||||
| }} | |||||
| > | |||||
| {/*{`${selectedDeliveryDateYmd} (${defaultTruckCount})`}*/} | |||||
| {t("車線-X")}({defaultTruckCount}) | |||||
| </Button> | |||||
| )} | |||||
| </Box> | |||||
| </Stack> | |||||
| </Grid> | |||||
| <Grid item xs={12}> | <Grid item xs={12}> | ||||
| <Box sx={{ py: 2, mt: 1, mb: 0.5, borderTop: "1px solid #e0e0e0" }}> | <Box sx={{ py: 2, mt: 1, mb: 0.5, borderTop: "1px solid #e0e0e0" }}> | ||||
| <Typography variant="subtitle1" sx={{ fontWeight: 600, mb: 0.5 }}> | <Typography variant="subtitle1" sx={{ fontWeight: 600, mb: 0.5 }}> | ||||
| @@ -431,39 +476,28 @@ const WorkbenchFloorLanePanel: React.FC<Props> = ({ onPickOrderAssigned, onSwitc | |||||
| <Grid item xs={12}> | <Grid item xs={12}> | ||||
| <Stack direction="row" spacing={2} alignItems="flex-start"> | <Stack direction="row" spacing={2} alignItems="flex-start"> | ||||
| <Typography sx={{ fontWeight: 600, minWidth: 60, pt: 1 }}>{t("Truck X")}</Typography> | |||||
| <Stack sx={{ minWidth: 60, pt: 1 }} spacing={0.25}> | |||||
| <Typography sx={{ fontWeight: 600 }}>{t("Truck X")}</Typography> | |||||
| <Typography variant="caption" color="text.secondary" sx={{ lineHeight: 1.2 }}> | |||||
| {t("Before today")} | |||||
| </Typography> | |||||
| </Stack> | |||||
| <Box sx={{ border: "1px solid #e0e0e0", borderRadius: 1, p: 1, backgroundColor: "#fafafa", flex: 1 }}> | <Box sx={{ border: "1px solid #e0e0e0", borderRadius: 1, p: 1, backgroundColor: "#fafafa", flex: 1 }}> | ||||
| {beforeTodayTruckXCount === 0 && defaultTruckCount === 0 ? renderNoEntry() : ( | |||||
| <Stack direction="row" spacing={1}> | |||||
| {defaultTruckCount > 0 && ( | |||||
| <Button | |||||
| variant="outlined" | |||||
| onClick={() => { | |||||
| setSelectedStore(""); | |||||
| setSelectedTruck("車線-X"); | |||||
| setIsDefaultTruck(true); | |||||
| setDefaultDateScope("today"); | |||||
| setModalOpen(true); | |||||
| }} | |||||
| > | |||||
| {`${t("Today")} (${defaultTruckCount})`} | |||||
| </Button> | |||||
| )} | |||||
| {beforeTodayTruckXCount > 0 && ( | |||||
| <Button | |||||
| variant="outlined" | |||||
| onClick={() => { | |||||
| setSelectedStore("4/F"); | |||||
| setSelectedTruck("車線-X"); | |||||
| setIsDefaultTruck(true); | |||||
| setDefaultDateScope("before"); | |||||
| setModalOpen(true); | |||||
| }} | |||||
| > | |||||
| {`${t("車線-X")} (${beforeTodayTruckXCount})`} | |||||
| </Button> | |||||
| )} | |||||
| </Stack> | |||||
| {beforeTodayTruckXCount === 0 ? ( | |||||
| renderNoEntry() | |||||
| ) : ( | |||||
| <Button | |||||
| variant="outlined" | |||||
| onClick={() => { | |||||
| setSelectedStore("4/F"); | |||||
| setSelectedTruck("車線-X"); | |||||
| setIsDefaultTruck(true); | |||||
| setDefaultDateScope("before"); | |||||
| setModalOpen(true); | |||||
| }} | |||||
| > | |||||
| {`${t("車線-X")} (${beforeTodayTruckXCount})`} | |||||
| </Button> | |||||
| )} | )} | ||||
| </Box> | </Box> | ||||
| </Stack> | </Stack> | ||||
| @@ -475,6 +509,7 @@ const WorkbenchFloorLanePanel: React.FC<Props> = ({ onPickOrderAssigned, onSwitc | |||||
| truck={selectedTruck} | truck={selectedTruck} | ||||
| isDefaultTruck={isDefaultTruck} | isDefaultTruck={isDefaultTruck} | ||||
| defaultDateScope={defaultDateScope} | defaultDateScope={defaultDateScope} | ||||
| defaultTruckRequiredDeliveryDate={selectedDeliveryDateYmd} | |||||
| listBridge={workbenchReleasedListBridge} | listBridge={workbenchReleasedListBridge} | ||||
| onClose={() => setModalOpen(false)} | onClose={() => setModalOpen(false)} | ||||
| onAssigned={() => { | onAssigned={() => { | ||||
| @@ -30,7 +30,7 @@ import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; | |||||
| import { DatePicker } from "@mui/x-date-pickers/DatePicker"; | import { DatePicker } from "@mui/x-date-pickers/DatePicker"; | ||||
| import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"; | import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"; | ||||
| import dayjs, { Dayjs } from "dayjs"; | import dayjs, { Dayjs } from "dayjs"; | ||||
| import { arrayToDayjs } from "@/app/utils/formatUtil"; | |||||
| import { arrayToDayjs, requiredDeliveryDateToDayString } from "@/app/utils/formatUtil"; | |||||
| import { | import { | ||||
| fetchWorkbenchTicketReleaseTable, | fetchWorkbenchTicketReleaseTable, | ||||
| forceCompleteWorkbenchTicket, | forceCompleteWorkbenchTicket, | ||||
| @@ -41,14 +41,6 @@ import Swal from "sweetalert2"; | |||||
| import { AUTH } from "@/authorities"; | import { AUTH } from "@/authorities"; | ||||
| import { SessionWithTokens } from "@/config/authConfig"; | import { SessionWithTokens } from "@/config/authConfig"; | ||||
| function requiredDeliveryDateToDayString(value: unknown): string { | |||||
| if (value == null) return ""; | |||||
| if (Array.isArray(value) && value.length >= 3 && value.every((x) => typeof x === "number")) { | |||||
| return arrayToDayjs(value as number[]).format("YYYY-MM-DD"); | |||||
| } | |||||
| return dayjs(value as string | number | Date).format("YYYY-MM-DD"); | |||||
| } | |||||
| function formatTicketDateTime(value: unknown): string { | function formatTicketDateTime(value: unknown): string { | ||||
| if (!value) return "-"; | if (!value) return "-"; | ||||
| if (Array.isArray(value)) { | if (Array.isArray(value)) { | ||||
| @@ -35,7 +35,7 @@ import { | |||||
| import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||
| import { useSession } from "next-auth/react"; | import { useSession } from "next-auth/react"; | ||||
| import dayjs, { Dayjs } from "dayjs"; | import dayjs, { Dayjs } from "dayjs"; | ||||
| import { arrayToDayjs } from "@/app/utils/formatUtil"; | |||||
| import { arrayToDayjs, requiredDeliveryDateToDayString } from "@/app/utils/formatUtil"; | |||||
| import { fetchTicketReleaseTable, getTicketReleaseTable } from "@/app/api/do/actions"; | import { fetchTicketReleaseTable, getTicketReleaseTable } from "@/app/api/do/actions"; | ||||
| import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"; | import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"; | ||||
| import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; | import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; | ||||
| @@ -69,18 +69,6 @@ function shouldLogTicketFilterDebug(): boolean { | |||||
| return (window as unknown as { __FG_TICKET_FILTER_DEBUG__?: boolean }).__FG_TICKET_FILTER_DEBUG__ === true; | return (window as unknown as { __FG_TICKET_FILTER_DEBUG__?: boolean }).__FG_TICKET_FILTER_DEBUG__ === true; | ||||
| } | } | ||||
| /** | |||||
| * 後端 LocalDate 常序列化為 [year, month, day](month 為 1–12)。 | |||||
| * 不可使用 dayjs(array):會被當成 [年, 月索引 0–11, 日],導致月份錯一個月、篩選與畫面日期錯誤。 | |||||
| */ | |||||
| function requiredDeliveryDateToDayString(value: unknown): string { | |||||
| if (value == null) return ""; | |||||
| if (Array.isArray(value) && value.length >= 3 && value.every((x) => typeof x === "number")) { | |||||
| return arrayToDayjs(value as number[]).format("YYYY-MM-DD"); | |||||
| } | |||||
| return dayjs(value as string | number | Date).format("YYYY-MM-DD"); | |||||
| } | |||||
| const FGPickOrderTicketReleaseTable: React.FC = () => { | const FGPickOrderTicketReleaseTable: React.FC = () => { | ||||
| const { t } = useTranslation("ticketReleaseTable"); | const { t } = useTranslation("ticketReleaseTable"); | ||||
| const { data: session } = useSession() as { data: SessionWithTokens | null }; | const { data: session } = useSession() as { data: SessionWithTokens | null }; | ||||
| @@ -28,7 +28,7 @@ import { | |||||
| import { useSession } from "next-auth/react"; | import { useSession } from "next-auth/react"; | ||||
| import { SessionWithTokens } from "@/config/authConfig"; | import { SessionWithTokens } from "@/config/authConfig"; | ||||
| import Swal from "sweetalert2"; | import Swal from "sweetalert2"; | ||||
| import dayjs from "dayjs"; | |||||
| import { requiredDeliveryDateToDayString } from "@/app/utils/formatUtil"; | |||||
| /** DO workbench 使用 workbench released API;Finished Good 不傳即可 */ | /** DO workbench 使用 workbench released API;Finished Good 不傳即可 */ | ||||
| export type ReleasedDoPickListBridge = { | export type ReleasedDoPickListBridge = { | ||||
| @@ -37,10 +37,12 @@ export type ReleasedDoPickListBridge = { | |||||
| storeId?: string, | storeId?: string, | ||||
| truck?: string | truck?: string | ||||
| ) => Promise<ReleasedDoPickOrderListItem[]>; | ) => Promise<ReleasedDoPickOrderListItem[]>; | ||||
| /** Optional 4th arg: workbench `requiredDeliveryDate` (YYYY-MM-DD) for default truck list; omit = calendar today. */ | |||||
| loadToday: ( | loadToday: ( | ||||
| shopName?: string, | shopName?: string, | ||||
| storeId?: string, | storeId?: string, | ||||
| truck?: string | |||||
| truck?: string, | |||||
| requiredDeliveryDate?: string | |||||
| ) => Promise<ReleasedDoPickOrderListItem[]>; | ) => Promise<ReleasedDoPickOrderListItem[]>; | ||||
| assignByListItemId: (userId: number, id: number) => Promise<PostPickOrderResponse>; | assignByListItemId: (userId: number, id: number) => Promise<PostPickOrderResponse>; | ||||
| }; | }; | ||||
| @@ -55,6 +57,8 @@ interface Props { | |||||
| /** Truck X only: today → released-today; before → released (歷史未完工) */ | /** Truck X only: today → released-today; before → released (歷史未完工) */ | ||||
| defaultDateScope?: "today" | "before"; | defaultDateScope?: "today" | "before"; | ||||
| listBridge?: ReleasedDoPickListBridge; | listBridge?: ReleasedDoPickListBridge; | ||||
| /** Workbench: `delivery_order_pick_order.requiredDeliveryDate` for Truck X (select day); used when [defaultDateScope] is today. */ | |||||
| defaultTruckRequiredDeliveryDate?: string; | |||||
| } | } | ||||
| const ReleasedDoPickOrderSelectModal: React.FC<Props> = ({ | const ReleasedDoPickOrderSelectModal: React.FC<Props> = ({ | ||||
| @@ -66,6 +70,7 @@ const ReleasedDoPickOrderSelectModal: React.FC<Props> = ({ | |||||
| isDefaultTruck, | isDefaultTruck, | ||||
| defaultDateScope: defaultDateScopeProp = "today", | defaultDateScope: defaultDateScopeProp = "today", | ||||
| listBridge, | listBridge, | ||||
| defaultTruckRequiredDeliveryDate, | |||||
| }) => { | }) => { | ||||
| const { t } = useTranslation("pickOrder"); | const { t } = useTranslation("pickOrder"); | ||||
| const { data: session } = useSession() as { data: SessionWithTokens | null }; | const { data: session } = useSession() as { data: SessionWithTokens | null }; | ||||
| @@ -85,7 +90,12 @@ const ReleasedDoPickOrderSelectModal: React.FC<Props> = ({ | |||||
| if (isDefaultTruck) { | if (isDefaultTruck) { | ||||
| if (defaultDateScopeProp === "today") { | if (defaultDateScopeProp === "today") { | ||||
| data = await loadTodayFn(undefined, undefined, "車線-X"); | |||||
| data = await loadTodayFn( | |||||
| undefined, | |||||
| undefined, | |||||
| "車線-X", | |||||
| defaultTruckRequiredDeliveryDate?.trim() || undefined | |||||
| ); | |||||
| } else { | } else { | ||||
| data = await loadReleased(undefined, undefined, "車線-X"); | data = await loadReleased(undefined, undefined, "車線-X"); | ||||
| } | } | ||||
| @@ -104,7 +114,7 @@ const ReleasedDoPickOrderSelectModal: React.FC<Props> = ({ | |||||
| } finally { | } finally { | ||||
| setLoading(false); | setLoading(false); | ||||
| } | } | ||||
| }, [open, shopSearch, storeId, truck, isDefaultTruck, defaultDateScopeProp, listBridge]); | |||||
| }, [open, shopSearch, storeId, truck, isDefaultTruck, defaultDateScopeProp, listBridge, defaultTruckRequiredDeliveryDate]); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| loadList(); | loadList(); | ||||
| @@ -117,7 +127,7 @@ const ReleasedDoPickOrderSelectModal: React.FC<Props> = ({ | |||||
| title: t("Confirm Assignment"), | title: t("Confirm Assignment"), | ||||
| html: ` | html: ` | ||||
| <div style="text-align: left;"> | <div style="text-align: left;"> | ||||
| <p><strong>${t("Date")}:</strong> ${item.requiredDeliveryDate ?? "-"}</p> | |||||
| <p><strong>${t("Date")}:</strong> ${requiredDeliveryDateToDayString(item.requiredDeliveryDate) || "-"}</p> | |||||
| <p><strong>${t("Shop")}:</strong> ${item.shopName ?? item.shopCode ?? "-"}</p> | <p><strong>${t("Shop")}:</strong> ${item.shopName ?? item.shopCode ?? "-"}</p> | ||||
| <p><strong>${t("Truck")}:</strong> ${item.truckLanceCode ?? "-"}</p> | <p><strong>${t("Truck")}:</strong> ${item.truckLanceCode ?? "-"}</p> | ||||
| <p><strong>${t("Delivery Order")}:</strong> ${(item.deliveryOrderCodes ?? []).join(", ")}</p> | <p><strong>${t("Delivery Order")}:</strong> ${(item.deliveryOrderCodes ?? []).join(", ")}</p> | ||||
| @@ -220,9 +230,7 @@ const ReleasedDoPickOrderSelectModal: React.FC<Props> = ({ | |||||
| {list.map((row) => ( | {list.map((row) => ( | ||||
| <TableRow key={row.id} hover> | <TableRow key={row.id} hover> | ||||
| <TableCell> | <TableCell> | ||||
| {row.requiredDeliveryDate | |||||
| ? dayjs(row.requiredDeliveryDate).format("YYYY-MM-DD") | |||||
| : "-"} | |||||
| {requiredDeliveryDateToDayString(row.requiredDeliveryDate) || "-"} | |||||
| </TableCell> | </TableCell> | ||||
| <TableCell>{row.shopName ?? row.shopCode ?? "-"}</TableCell> | <TableCell>{row.shopName ?? row.shopCode ?? "-"}</TableCell> | ||||
| <TableCell>{row.truckLanceCode ?? "-"}</TableCell> | <TableCell>{row.truckLanceCode ?? "-"}</TableCell> | ||||