| @@ -133,6 +133,7 @@ export interface PrintPickRecordRequest{ | |||
| pickOrderId: number; | |||
| printerId: number; | |||
| printQty: number; | |||
| floor?: "2F" | "3F" | "4F" | "ALL"; | |||
| } | |||
| export interface PrintPickRecordResponse{ | |||
| @@ -604,6 +605,9 @@ export interface StockOutLineDetailResponse { | |||
| location: string | null; | |||
| availableQty: number | null; | |||
| noLot: boolean; | |||
| /** Workbench API: matched suggest_pick_lot qty for this SOL lot line */ | |||
| //suggestedPickQty?: number | null; | |||
| //suggestedPickLotId?: number | null; | |||
| } | |||
| export interface LotDetailResponse { | |||
| @@ -712,6 +716,21 @@ export const fetchJobOrderLotsHierarchicalByPickOrderId = cache(async (pickOrder | |||
| }, | |||
| ); | |||
| }); | |||
| /** JO Workbench: in−out available (matches scan-pick); stockouts include suggestedPickQty / suggestedPickLotId when SPL matches SOL lot line */ | |||
| /* | |||
| export const fetchJobOrderLotsHierarchicalByPickOrderIdWorkbench = cache( | |||
| async (pickOrderId: number) => { | |||
| return serverFetchJson<JobOrderLotsHierarchicalResponse>( | |||
| `${BASE_API_URL}/jo/all-lots-hierarchical-by-pick-order-workbench/${pickOrderId}`, | |||
| { | |||
| method: "GET", | |||
| next: { tags: ["jo-hierarchical-workbench"] }, | |||
| }, | |||
| ); | |||
| }, | |||
| ); | |||
| */ | |||
| // 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 (type?: string | null, floor?: string | null) => { | |||
| @@ -1076,6 +1095,32 @@ export const fetchCompletedJobOrderPickOrdersrecords = async (completedDate?: st | |||
| cache: "no-store", | |||
| }); | |||
| }; | |||
| export const fetchJobOrderPickOrdersrecords = async ( | |||
| date?: string | null, | |||
| status?: string | null, | |||
| ) => { | |||
| const params = new URLSearchParams(); | |||
| if (date && String(date).trim() !== "") { | |||
| params.set("date", String(date).trim()); | |||
| } | |||
| if (status && String(status).trim() !== "" && String(status) !== "All") { | |||
| params.set("status", String(status).trim()); | |||
| } | |||
| const q = params.toString() ? `?${params.toString()}` : ""; | |||
| return serverFetchJson<any>(`${BASE_API_URL}/jo/job-order-pick-orders${q}`, { | |||
| method: "GET", | |||
| cache: "no-store", | |||
| }); | |||
| }; | |||
| export const fetchJobOrderPickOrderLotDetailsForPick = cache(async (pickOrderId: number) => { | |||
| return serverFetchJson<any[]>(`${BASE_API_URL}/jo/job-order-pick-order-lot-details/${pickOrderId}`, { | |||
| method: "GET", | |||
| headers: { "Content-Type": "application/json" } | |||
| }) | |||
| }) | |||
| export const fetchJoForPrintQrCode = cache(async (date: string) => { | |||
| return serverFetchJson<JobOrderListForPrintQrCodeResponse[]>( | |||
| `${BASE_API_URL}/jo/joForPrintQrCode/${date}`, | |||
| @@ -1274,6 +1319,9 @@ export async function PrintPickRecord(request: PrintPickRecordRequest){ | |||
| if (request.printQty !== null && request.printQty !== undefined) { | |||
| params.append('printQty', request.printQty.toString()); | |||
| } | |||
| if (request.floor) { | |||
| params.append('floor', request.floor); | |||
| } | |||
| //const response = await serverFetchWithNoContent(`${BASE_API_URL}/jo/print-PickRecord?${params.toString()}`,{ | |||
| const response = await serverFetchWithNoContent(`${BASE_API_URL}/jo/print-PickRecord?${params.toString()}`,{ | |||
| @@ -0,0 +1,255 @@ | |||
| "use client"; | |||
| import { useCallback, useEffect, useMemo, useState } from "react"; | |||
| import { | |||
| Alert, | |||
| Box, | |||
| CircularProgress, | |||
| Grid, | |||
| MenuItem, | |||
| Paper, | |||
| Stack, | |||
| Table, | |||
| TableBody, | |||
| TableCell, | |||
| TableContainer, | |||
| TableHead, | |||
| TableRow, | |||
| TextField, | |||
| Typography, | |||
| } from "@mui/material"; | |||
| import type { ApexOptions } from "apexcharts"; | |||
| import dayjs from "dayjs"; | |||
| import { | |||
| CompletedDoPickOrderResponse, | |||
| fetchCompletedDoPickOrdersAll, | |||
| } from "@/app/api/pickOrder/actions"; | |||
| import SafeApexCharts from "@/components/charts/SafeApexCharts"; | |||
| type FloorFilter = "all" | "2/F" | "4/F"; | |||
| type DailySummaryRow = { | |||
| date: string; | |||
| floor2F: number; | |||
| floor4F: number; | |||
| truckX: number; | |||
| total: number; | |||
| }; | |||
| const FinishedGoodCartonDashboardTab: React.FC = () => { | |||
| const [floor, setFloor] = useState<FloorFilter>("all"); | |||
| const [date, setDate] = useState<string>(dayjs().format("YYYY-MM-DD")); | |||
| const [loading, setLoading] = useState(false); | |||
| const [error, setError] = useState<string>(""); | |||
| const [records, setRecords] = useState<CompletedDoPickOrderResponse[]>([]); | |||
| const loadData = useCallback(async () => { | |||
| setLoading(true); | |||
| setError(""); | |||
| try { | |||
| const data = await fetchCompletedDoPickOrdersAll( | |||
| date ? { targetDate: date } : undefined, | |||
| ); | |||
| setRecords(data); | |||
| } catch (err) { | |||
| console.error("Failed to load finished good carton dashboard data", err); | |||
| setError("載入成品出倉出箱數量失敗,請稍後再試。"); | |||
| setRecords([]); | |||
| } finally { | |||
| setLoading(false); | |||
| } | |||
| }, [date]); | |||
| useEffect(() => { | |||
| loadData(); | |||
| }, [loadData]); | |||
| const rows = useMemo<DailySummaryRow[]>(() => { | |||
| const filtered = | |||
| floor === "all" ? records : records.filter((record) => record.storeId === floor); | |||
| const summary = new Map<string, DailySummaryRow>(); | |||
| filtered.forEach((record) => { | |||
| const day = dayjs(record.deliveryDate).isValid() | |||
| ? dayjs(record.deliveryDate).format("YYYY-MM-DD") | |||
| : "-"; | |||
| const cartonQty = Number(record.numberOfCartons ?? 0); | |||
| const current = summary.get(day) ?? { | |||
| date: day, | |||
| floor2F: 0, | |||
| floor4F: 0, | |||
| truckX: 0, | |||
| total: 0, | |||
| }; | |||
| if (record.storeId === "2/F") { | |||
| current.floor2F += cartonQty; | |||
| } | |||
| if (record.storeId === "4/F") { | |||
| current.floor4F += cartonQty; | |||
| } | |||
| if (String(record.truckLanceCode ?? "").trim() === "車線-X") { | |||
| current.truckX += cartonQty; | |||
| } | |||
| current.total += cartonQty; | |||
| summary.set(day, current); | |||
| }); | |||
| return Array.from(summary.values()).sort((a, b) => b.date.localeCompare(a.date)); | |||
| }, [records, floor]); | |||
| const chartOptions = useMemo<ApexOptions>( | |||
| () => ({ | |||
| chart: { | |||
| type: "bar", | |||
| toolbar: { show: false }, | |||
| }, | |||
| colors: ["#1976d2", "#9c27b0", "#ff9800", "#2e7d32"], | |||
| dataLabels: { enabled: false }, | |||
| stroke: { show: true, width: 1, colors: ["transparent"] }, | |||
| plotOptions: { | |||
| bar: { | |||
| horizontal: false, | |||
| borderRadius: 3, | |||
| columnWidth: "55%", | |||
| }, | |||
| }, | |||
| xaxis: { | |||
| categories: rows.map((row) => row.date), | |||
| title: { text: "日期" }, | |||
| }, | |||
| yaxis: { | |||
| title: { text: "箱數" }, | |||
| labels: { | |||
| formatter: (val) => Number(val || 0).toLocaleString("zh-HK"), | |||
| }, | |||
| }, | |||
| tooltip: { | |||
| y: { | |||
| formatter: (val) => `${Number(val || 0).toLocaleString("zh-HK")} 箱`, | |||
| }, | |||
| }, | |||
| legend: { | |||
| position: "top", | |||
| }, | |||
| noData: { | |||
| text: "沒有圖表資料", | |||
| }, | |||
| }), | |||
| [rows], | |||
| ); | |||
| const chartSeries = useMemo( | |||
| () => [ | |||
| { name: "2/F", data: rows.map((row) => row.floor2F) }, | |||
| { name: "4/F", data: rows.map((row) => row.floor4F) }, | |||
| { name: "車線-X", data: rows.map((row) => row.truckX) }, | |||
| { name: "總數", data: rows.map((row) => row.total) }, | |||
| ], | |||
| [rows], | |||
| ); | |||
| const summary = useMemo(() => { | |||
| return rows.reduce( | |||
| (acc, row) => { | |||
| acc.floor2F += row.floor2F; | |||
| acc.floor4F += row.floor4F; | |||
| acc.truckX += row.truckX; | |||
| acc.total += row.total; | |||
| return acc; | |||
| }, | |||
| { floor2F: 0, floor4F: 0, truckX: 0, total: 0 }, | |||
| ); | |||
| }, [rows]); | |||
| return ( | |||
| <Box sx={{ width: "100%" }}> | |||
| <Typography variant="h6" sx={{ mb: 2 }}> | |||
| 成品出倉出箱數量 | |||
| </Typography> | |||
| {error && ( | |||
| <Alert severity="error" sx={{ mb: 2 }}> | |||
| {error} | |||
| </Alert> | |||
| )} | |||
| {loading ? ( | |||
| <Box sx={{ py: 6, display: "flex", justifyContent: "center" }}> | |||
| <CircularProgress /> | |||
| </Box> | |||
| ) : ( | |||
| <Stack spacing={2}> | |||
| <Grid container spacing={1.5}> | |||
| <Grid item xs={12} md={6}> | |||
| <TextField | |||
| select | |||
| fullWidth | |||
| label="樓層" | |||
| value={floor} | |||
| onChange={(event) => setFloor(event.target.value as FloorFilter)} | |||
| > | |||
| <MenuItem value="all">全部</MenuItem> | |||
| <MenuItem value="2/F">2/F</MenuItem> | |||
| <MenuItem value="4/F">4/F</MenuItem> | |||
| </TextField> | |||
| </Grid> | |||
| <Grid item xs={12} md={6}> | |||
| <TextField | |||
| fullWidth | |||
| label="日期" | |||
| type="date" | |||
| value={date} | |||
| InputLabelProps={{ shrink: true }} | |||
| onChange={(event) => setDate(event.target.value)} | |||
| /> | |||
| </Grid> | |||
| </Grid> | |||
| <Grid container spacing={1.5} alignItems="stretch"> | |||
| <Grid item xs={12} md={4}> | |||
| <TableContainer component={Paper} sx={{ height: "100%" }}> | |||
| <Table size="small"> | |||
| <TableBody> | |||
| <TableRow> | |||
| <TableCell>2/F 出箱數</TableCell> | |||
| <TableCell align="right">{summary.floor2F.toLocaleString("zh-HK")}</TableCell> | |||
| </TableRow> | |||
| <TableRow> | |||
| <TableCell>4/F 出箱數</TableCell> | |||
| <TableCell align="right">{summary.floor4F.toLocaleString("zh-HK")}</TableCell> | |||
| </TableRow> | |||
| <TableRow> | |||
| <TableCell>車線-X 出箱數</TableCell> | |||
| <TableCell align="right">{summary.truckX.toLocaleString("zh-HK")}</TableCell> | |||
| </TableRow> | |||
| <TableRow> | |||
| <TableCell>總出箱數</TableCell> | |||
| <TableCell align="right">{summary.total.toLocaleString("zh-HK")}</TableCell> | |||
| </TableRow> | |||
| </TableBody> | |||
| </Table> | |||
| </TableContainer> | |||
| </Grid> | |||
| <Grid item xs={12} md={8}> | |||
| <Paper sx={{ p: 1.5, height: "100%" }}> | |||
| <SafeApexCharts | |||
| type="bar" | |||
| height={240} | |||
| options={chartOptions} | |||
| series={chartSeries} | |||
| chartRevision={`${floor}-${date}-${rows.length}`} | |||
| /> | |||
| </Paper> | |||
| </Grid> | |||
| </Grid> | |||
| </Stack> | |||
| )} | |||
| </Box> | |||
| ); | |||
| }; | |||
| export default FinishedGoodCartonDashboardTab; | |||
| @@ -42,6 +42,7 @@ import { PrinterCombo } from "@/app/api/settings/printer"; | |||
| import { Autocomplete } from "@mui/material"; | |||
| import FGPickOrderTicketReleaseTable from "./FGPickOrderTicketReleaseTable"; | |||
| import TruckRoutingSummaryTab, { TruckRoutingSummaryFilters } from "./TruckRoutingSummaryTab"; | |||
| import FinishedGoodCartonDashboardTab from "./FinishedGoodCartonDashboardTab"; | |||
| import { clientAuthFetch } from "@/app/utils/clientAuthFetch"; | |||
| import { NEXT_PUBLIC_API_URL } from "@/config/api"; | |||
| import { fetchTruckRoutingSummaryPrecheck } from "@/app/(main)/report/truckRoutingSummaryApi"; | |||
| @@ -378,7 +379,7 @@ const [selectedPrinterForDraft, setSelectedPrinterForDraft] = useState<PrinterCo | |||
| } | |||
| }, [truckRoutingFilters, selectedPrinterForAllDraft, t]); | |||
| const isTruckRoutingTab = tabIndex === 5; | |||
| const isTruckRoutingTab = tabIndex === 6; | |||
| const canPrintTruckRoutingSummary = Boolean( | |||
| truckRoutingFilters.storeId && | |||
| truckRoutingFilters.truckLanceCode && | |||
| @@ -393,7 +394,7 @@ const [selectedPrinterForDraft, setSelectedPrinterForDraft] = useState<PrinterCo | |||
| }, [fetchReleasedOrderCount]); | |||
| useEffect(() => { | |||
| if (tabIndex === 5) { | |||
| if (tabIndex === 6) { | |||
| logFeatureUsage(FEATURE_USAGE.TRUCK_ROUTING_SUMMARY, FEATURE_USAGE_ACTION.PAGE_VIEW); | |||
| } | |||
| }, [tabIndex]); | |||
| @@ -831,6 +832,7 @@ const handleAssignByLane = useCallback(async ( | |||
| <Tab label={t("Finished Good Record")} iconPosition="end" /> | |||
| <Tab label={t("Ticket Release Table")} iconPosition="end" /> | |||
| <Tab label={t("Finished Good Record (All)")} iconPosition="end" /> | |||
| <Tab label="成品出倉出箱數量" iconPosition="end" /> | |||
| <Tab label="送貨路線摘要" iconPosition="end" /> | |||
| </Tabs> | |||
| @@ -887,6 +889,9 @@ const handleAssignByLane = useCallback(async ( | |||
| /> | |||
| )} | |||
| {tabIndex === 5 && ( | |||
| <FinishedGoodCartonDashboardTab /> | |||
| )} | |||
| {tabIndex === 6 && ( | |||
| <TruckRoutingSummaryTab onFiltersChange={setTruckRoutingFilters} /> | |||
| )} | |||
| </Box> | |||
| @@ -32,8 +32,8 @@ import { useCallback, useEffect, useState, useRef, useMemo } from "react"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { useRouter } from "next/navigation"; | |||
| import { | |||
| fetchCompletedJobOrderPickOrdersrecords, | |||
| fetchCompletedJobOrderPickOrderLotDetailsForCompletedPick, | |||
| fetchJobOrderPickOrdersrecords, | |||
| fetchJobOrderPickOrderLotDetailsForPick, | |||
| PrintPickRecord | |||
| } from "@/app/api/jo/actions"; | |||
| import { fetchNameList, NameList } from "@/app/api/user/actions"; | |||
| @@ -148,8 +148,8 @@ const CompleteJobOrderRecord: React.FC<Props> = ({ | |||
| const errors = formProps.formState.errors; | |||
| // 修改:使用新的 Job Order API 获取已完成的 Job Order Pick Orders(仅完成pick的) | |||
| const fetchCompletedJobOrderPickOrdersData = useCallback( | |||
| async (forDate?: string) => { | |||
| const fetchJobOrderPickOrdersData = useCallback( | |||
| async (forDate?: string, forStatus?: string) => { | |||
| if (!currentUserId) return; | |||
| setCompletedJobOrderPickOrdersLoading(true); | |||
| @@ -160,21 +160,27 @@ const CompleteJobOrderRecord: React.FC<Props> = ({ | |||
| : searchQuery.completedDate | |||
| ? String(searchQuery.completedDate) | |||
| : dayjs().format("YYYY-MM-DD"); | |||
| const completedJobOrderPickOrders = await fetchCompletedJobOrderPickOrdersrecords( | |||
| dateParam.trim() ? dateParam.trim() : null, | |||
| const statusParam = | |||
| forStatus !== undefined | |||
| ? forStatus | |||
| : searchQuery.pickOrderStatus | |||
| ? String(searchQuery.pickOrderStatus) | |||
| : null; | |||
| const data = await fetchJobOrderPickOrdersrecords( | |||
| dateParam?.trim() ? dateParam.trim() : null, | |||
| statusParam?.trim() ? statusParam.trim() : null, | |||
| ); | |||
| const safeData = Array.isArray(completedJobOrderPickOrders) ? completedJobOrderPickOrders : []; | |||
| const safeData = Array.isArray(data) ? data : []; | |||
| setCompletedJobOrderPickOrders(safeData); | |||
| setFilteredJobOrderPickOrders(safeData); | |||
| } catch (error) { | |||
| console.error("❌ Error fetching completed Job Order pick orders:", error); | |||
| setCompletedJobOrderPickOrders([]); | |||
| setFilteredJobOrderPickOrders([]); | |||
| } finally { | |||
| setCompletedJobOrderPickOrdersLoading(false); | |||
| } | |||
| }, | |||
| [currentUserId, searchQuery.completedDate], | |||
| [currentUserId, searchQuery.completedDate, searchQuery.pickOrderStatus], | |||
| ); | |||
| // 新增:获取 lot 详情数据(使用新的API) | |||
| const fetchLotDetailsData = useCallback(async (pickOrderId: number) => { | |||
| @@ -182,7 +188,7 @@ const CompleteJobOrderRecord: React.FC<Props> = ({ | |||
| try { | |||
| console.log("🔍 Fetching lot details for completed pick order:", pickOrderId); | |||
| const lotDetails = await fetchCompletedJobOrderPickOrderLotDetailsForCompletedPick(pickOrderId); | |||
| const lotDetails = await fetchJobOrderPickOrderLotDetailsForPick(pickOrderId); | |||
| setDetailLotData(Array.isArray(lotDetails) ? lotDetails : []); | |||
| console.log(" Fetched lot details:", lotDetails); | |||
| @@ -198,9 +204,13 @@ const CompleteJobOrderRecord: React.FC<Props> = ({ | |||
| useEffect(() => { | |||
| if (!currentUserId) return; | |||
| const d = searchQuery?.completedDate; | |||
| const s = searchQuery?.pickOrderStatus; | |||
| const dateStr = d != null && String(d).trim() !== "" ? String(d).trim() : ""; | |||
| void fetchCompletedJobOrderPickOrdersData(dateStr || undefined); | |||
| }, [currentUserId, searchQuery?.completedDate, fetchCompletedJobOrderPickOrdersData]); | |||
| const statusStr = s != null && String(s).trim() !== "" ? String(s).trim() : ""; | |||
| void fetchJobOrderPickOrdersData(dateStr || undefined, statusStr || undefined); | |||
| }, [currentUserId, searchQuery?.completedDate, searchQuery?.pickOrderStatus, fetchJobOrderPickOrdersData]); | |||
| // 修改:搜索功能(只更新 query;实际过滤交给 useEffect + date filter 统一处理) | |||
| const handleSearch = useCallback((query: Record<string, any>) => { | |||
| @@ -316,6 +326,15 @@ const CompleteJobOrderRecord: React.FC<Props> = ({ | |||
| paramName: "jobOrderCode", | |||
| type: "text", | |||
| }, | |||
| { | |||
| label: t("Pick Order Status"), | |||
| paramName: "pickOrderStatus", | |||
| type: "select-labelled", | |||
| options: [ | |||
| { label: t("Released"), value: "RELEASED" }, | |||
| { label: t("Completed"), value: "COMPLETED" }, | |||
| ], // 依你后端实际枚举 | |||
| }, | |||
| { | |||
| label: t("Job Order Item Name"), | |||
| paramName: "jobOrderName", | |||
| @@ -359,7 +378,10 @@ const CompleteJobOrderRecord: React.FC<Props> = ({ | |||
| })); | |||
| }, []); | |||
| const handlePickRecord = useCallback(async (jobOrderPickOrder: CompletedJobOrderPickOrder) => { | |||
| const handlePickRecord = useCallback(async ( | |||
| jobOrderPickOrder: CompletedJobOrderPickOrder, | |||
| floor: "2F" | "3F" | "4F" | "ALL" | |||
| ) => { | |||
| try { | |||
| if (!jobOrderPickOrder) { | |||
| console.error("No selected job order pick order available"); | |||
| @@ -399,7 +421,8 @@ const CompleteJobOrderRecord: React.FC<Props> = ({ | |||
| const printRequest = { | |||
| pickOrderId: pickOrderId, | |||
| printerId: printerId, | |||
| printQty: printQty | |||
| printQty: printQty, | |||
| floor, | |||
| }; | |||
| console.log("Printing Pick Record with request: ", printRequest); | |||
| @@ -640,7 +663,9 @@ const CompleteJobOrderRecord: React.FC<Props> = ({ | |||
| </Box> | |||
| ) : ( | |||
| <Stack spacing={2}> | |||
| {paginatedData.map((jobOrderPickOrder) => ( | |||
| {paginatedData.map((jobOrderPickOrder) => { | |||
| const normalizedStatus = String(jobOrderPickOrder.pickOrderStatus ?? "").toLowerCase(); | |||
| return ( | |||
| <Card key={jobOrderPickOrder.id}> | |||
| <CardContent> | |||
| <Stack direction="row" justifyContent="space-between" alignItems="center"> | |||
| @@ -660,21 +685,18 @@ const CompleteJobOrderRecord: React.FC<Props> = ({ | |||
| </Box> | |||
| <Box> | |||
| <Chip | |||
| label={t(jobOrderPickOrder.pickOrderStatus) } | |||
| color={jobOrderPickOrder.pickOrderStatus === 'completed' ? 'success' : 'default'} | |||
| size="small" | |||
| sx={{ mb: 1 }} | |||
| /> | |||
| <Chip | |||
| label={t(jobOrderPickOrder.pickOrderStatus)} | |||
| color={normalizedStatus === "completed" ? "success" : "default"} | |||
| size="small" | |||
| sx={{ mb: 1 }} | |||
| /> | |||
| <Typography variant="body2" color="text.secondary"> | |||
| {jobOrderPickOrder.completedItems}/{jobOrderPickOrder.totalItems} {t("items completed")} | |||
| </Typography> | |||
| <Chip | |||
| label={jobOrderPickOrder.secondScanCompleted ? t("Second Scan Completed") : t("Second Scan Pending")} | |||
| color={jobOrderPickOrder.secondScanCompleted ? 'success' : 'warning'} | |||
| size="small" | |||
| sx={{ mt: 1 }} | |||
| /> | |||
| </Box> | |||
| </Stack> | |||
| </CardContent> | |||
| @@ -685,16 +707,39 @@ const CompleteJobOrderRecord: React.FC<Props> = ({ | |||
| > | |||
| {t("View Details")} | |||
| </Button> | |||
| <Button | |||
| variant="contained" | |||
| <Button | |||
| variant="contained" | |||
| color="primary" | |||
| onClick={() => handlePickRecord(jobOrderPickOrder, "ALL")} | |||
| > | |||
| 打印全部樓層板頭紙 | |||
| </Button> | |||
| <Button | |||
| variant="contained" | |||
| color="primary" | |||
| onClick={() => handlePickRecord(jobOrderPickOrder, "2F")} | |||
| > | |||
| {t("Print Pick Record")} 2F | |||
| </Button> | |||
| <Button | |||
| variant="contained" | |||
| color="primary" | |||
| onClick={() => handlePickRecord(jobOrderPickOrder, "3F")} | |||
| > | |||
| {t("Print Pick Record")} 3F | |||
| </Button> | |||
| <Button | |||
| variant="contained" | |||
| color="primary" | |||
| onClick={() => handlePickRecord(jobOrderPickOrder)} | |||
| onClick={() => handlePickRecord(jobOrderPickOrder, "4F")} | |||
| > | |||
| {t("Print Pick Record")} | |||
| {t("Print Pick Record")} 4F | |||
| </Button> | |||
| </CardActions> | |||
| </Card> | |||
| ))} | |||
| ); | |||
| })} | |||
| </Stack> | |||
| )} | |||
| @@ -167,6 +167,9 @@ | |||
| "View Details": "查看詳情", | |||
| "Skip": "跳過", | |||
| "Handler": "提料員", | |||
| "RELEASED": "已放單", | |||
| "Released": "已放單", | |||
| "COMPLETED": "已完成", | |||
| "Now": "現時", | |||
| "Last updated": "最後更新", | |||
| "Auto-refresh every 5 minutes": "每5分鐘自動刷新", | |||