diff --git a/src/app/api/jo/actions.ts b/src/app/api/jo/actions.ts index 8e4c757..f3f10b2 100644 --- a/src/app/api/jo/actions.ts +++ b/src/app/api/jo/actions.ts @@ -692,16 +692,19 @@ export const fetchJobOrderLotsHierarchicalByPickOrderId = cache(async (pickOrder }, ); }); -export const fetchAllJoPickOrders = cache(async (isDrink?: boolean | null, floor?: string | null) => { +// NOTE: Do NOT wrap in `cache()` because the list needs to reflect just-completed lines +// immediately when navigating back from JobPickExecution. +export const fetchAllJoPickOrders = async (isDrink?: boolean | null, floor?: string | null) => { const params = new URLSearchParams(); if (isDrink !== undefined && isDrink !== null) params.set("isDrink", String(isDrink)); if (floor) params.set("floor", floor); const query = params.toString() ? `?${params.toString()}` : ""; return serverFetchJson( `${BASE_API_URL}/jo/AllJoPickOrder${query}`, - { method: "GET" } + // Force re-fetch. This page reflects real-time pick completion state. + { method: "GET", cache: "no-store" } ); -}); +}; export const fetchProductProcessLineDetail = cache(async (lineId: number) => { return serverFetchJson( `${BASE_API_URL}/product-process/Demo/ProcessLine/detail/${lineId}`, diff --git a/src/components/Jodetail/JoPickOrderList.tsx b/src/components/Jodetail/JoPickOrderList.tsx index 1460f79..2e6f192 100644 --- a/src/components/Jodetail/JoPickOrderList.tsx +++ b/src/components/Jodetail/JoPickOrderList.tsx @@ -27,7 +27,7 @@ const JoPickOrderList: React.FC = ({ onSwitchToRecordTab }) =>{ const [selectedJobOrderId, setSelectedJobOrderId] = useState(undefined); type PickOrderFilter = "all" | "drink" | "other"; const [filter, setFilter] = useState("all"); - type FloorFilter = "ALL" | "2F" | "3F" | "4F"; + type FloorFilter = "ALL" | "2F" | "3F" | "4F" | "NO_LOT"; const [floorFilter, setFloorFilter] = useState("ALL"); const fetchPickOrders = useCallback(async () => { setLoading(true); @@ -60,10 +60,7 @@ const JoPickOrderList: React.FC = ({ onSwitchToRecordTab }) =>{ + {t("Total pick orders")}: {pickOrders.length} @@ -163,8 +167,8 @@ const JoPickOrderList: React.FC = ({ onSwitchToRecordTab }) =>{ = ({ onSwitchToRecordTab }) =>{ {t("Required Qty")}: {pickOrder.reqQty} ({pickOrder.uomName}) - {pickOrder.floorPickCounts?.map(({ floor, finishedCount, totalCount }) => ( - - {floor}: {finishedCount}/{totalCount} - - ))} - {!!pickOrder.noLotPickCount && ( - - {t("No Lot")}: {pickOrder.noLotPickCount.finishedCount}/{pickOrder.noLotPickCount.totalCount} - + {floorFilter === "ALL" ? ( + <> + {pickOrder.floorPickCounts?.map(({ floor, finishedCount, totalCount }) => ( + + {floor}: {finishedCount}/{totalCount} + + ))} + {!!pickOrder.noLotPickCount && ( + + {t("No Lot")}: {pickOrder.noLotPickCount.finishedCount}/{pickOrder.noLotPickCount.totalCount} + + )} + + ) : floorFilter === "NO_LOT" ? ( + !!pickOrder.noLotPickCount && ( + + {t("No Lot")}: {pickOrder.noLotPickCount.finishedCount}/{pickOrder.noLotPickCount.totalCount} + + ) + ) : ( + pickOrder.floorPickCounts + ?.filter((c) => c.floor === floorFilter) + .map(({ floor, finishedCount, totalCount }) => ( + + {floor}: {finishedCount}/{totalCount} + + )) )} {typeof pickOrder.suggestedFailCount === "number" && pickOrder.suggestedFailCount > 0 && ( diff --git a/src/components/ProductionProcess/JobProcessStatus.tsx b/src/components/ProductionProcess/JobProcessStatus.tsx index cd74669..f93a9f0 100644 --- a/src/components/ProductionProcess/JobProcessStatus.tsx +++ b/src/components/ProductionProcess/JobProcessStatus.tsx @@ -19,9 +19,11 @@ import { } from '@mui/material'; import { useTranslation } from 'react-i18next'; import dayjs from 'dayjs'; +import type { Dayjs } from 'dayjs'; import { fetchJobProcessStatus, JobProcessStatusResponse } from '@/app/api/jo/actions'; import { arrayToDayjs } from '@/app/utils/formatUtil'; -import { FormControl, Select, MenuItem } from "@mui/material"; +import { DatePicker, LocalizationProvider } from "@mui/x-date-pickers"; +import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; const REFRESH_INTERVAL = 10 * 60 * 1000; // 10 minutes const JobProcessStatus: React.FC = () => { @@ -30,8 +32,8 @@ const JobProcessStatus: React.FC = () => { const [loading, setLoading] = useState(true); const refreshCountRef = useRef(0); const [currentTime, setCurrentTime] = useState(dayjs()); - const [selectedDate, setSelectedDate] = useState(dayjs().format("YYYY-MM-DD")); -const [lastDataRefreshTime, setLastDataRefreshTime] = useState(null); + const [selectedDate, setSelectedDate] = useState(dayjs()); + const [lastDataRefreshTime, setLastDataRefreshTime] = useState(null); // Update current time every second for countdown useEffect(() => { @@ -44,7 +46,7 @@ const [lastDataRefreshTime, setLastDataRefreshTime] = useState { setLoading(true); try { - const result = await fetchJobProcessStatus(selectedDate); + const result = await fetchJobProcessStatus(selectedDate.format("YYYY-MM-DD")); setData(result); refreshCountRef.current += 1; setLastDataRefreshTime(dayjs()); @@ -181,16 +183,19 @@ const [lastDataRefreshTime, setLastDataRefreshTime] = useState - - - + onChange={(newValue) => { + if (newValue) setSelectedDate(newValue); + }} + format="YYYY-MM-DD" + slotProps={{ + textField: { size: "small", sx: { minWidth: 160 } }, + }} + /> + diff --git a/src/components/ProductionProcess/OperatorKpiDashboard.tsx b/src/components/ProductionProcess/OperatorKpiDashboard.tsx index d851e8a..02f59c8 100644 --- a/src/components/ProductionProcess/OperatorKpiDashboard.tsx +++ b/src/components/ProductionProcess/OperatorKpiDashboard.tsx @@ -14,13 +14,13 @@ import { TableRow, Paper, Typography, - FormControl, - Select, - MenuItem, Stack } from "@mui/material"; import { useTranslation } from "react-i18next"; import dayjs from "dayjs"; +import type { Dayjs } from "dayjs"; +import { DatePicker, LocalizationProvider } from "@mui/x-date-pickers"; +import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; import { fetchOperatorKpi, OperatorKpiResponse, OperatorKpiProcessInfo } from "@/app/api/jo/actions"; import { arrayToDayjs } from "@/app/utils/formatUtil"; @@ -30,10 +30,10 @@ const OperatorKpiDashboard: React.FC = () => { const { t } = useTranslation(["common", "jo"]); const [data, setData] = useState([]); const [loading, setLoading] = useState(true); - const [selectedDate, setSelectedDate] = useState(dayjs().format("YYYY-MM-DD")); + const [selectedDate, setSelectedDate] = useState(dayjs()); const refreshCountRef = useRef(0); const [now, setNow] = useState(dayjs()); - const [lastDataRefreshTime, setLastDataRefreshTime] = useState(null); + const [lastDataRefreshTime, setLastDataRefreshTime] = useState(null); const formatTime = (timeData: any): string => { if (!timeData) return "-"; @@ -69,7 +69,7 @@ const OperatorKpiDashboard: React.FC = () => { const loadData = useCallback(async () => { setLoading(true); try { - const result = await fetchOperatorKpi(selectedDate); + const result = await fetchOperatorKpi(selectedDate.format("YYYY-MM-DD")); setData(result); setLastDataRefreshTime(dayjs()); refreshCountRef.current += 1; @@ -165,16 +165,19 @@ const OperatorKpiDashboard: React.FC = () => { {/* Filters */} - - - + onChange={(newValue) => { + if (newValue) setSelectedDate(newValue); + }} + format="YYYY-MM-DD" + slotProps={{ + textField: { size: "small", sx: { minWidth: 160 } }, + }} + /> +