diff --git a/src/app/api/jo/actions.ts b/src/app/api/jo/actions.ts index e03883f..477bf4c 100644 --- a/src/app/api/jo/actions.ts +++ b/src/app/api/jo/actions.ts @@ -905,7 +905,7 @@ export const fetchAllJoborderProductProcessInfo = cache(async (type?: string | n ); }); -/** FP-MTMS Version Checklist | Functions Ref. No. 40 | v1.0.4 | 2026-08-05 */ +/** FP-MTMS Version Checklist | Functions Ref. No. 40 | v1.0.5 | 2026-08-06 */ export const fetchJoborderProductProcessesPage = cache(async (params: { /** Job order / process date(YYYY-MM-DD) */ date?: string | null; diff --git a/src/components/AppBar/Profile.tsx b/src/components/AppBar/Profile.tsx index 7146f81..f5d7fdd 100644 --- a/src/components/AppBar/Profile.tsx +++ b/src/components/AppBar/Profile.tsx @@ -45,7 +45,7 @@ const Profile: React.FC = ({ avatarImageSrc, profileName }) => { disablePadding: false, sx: { py: 0, minWidth: 180 }, }} - PaperProps={{ variant: "outlined" }} + PaperProps={{ variant: "outlined", elevation: 0 }} > {profileName} diff --git a/src/components/DoWorkbench/DoWorkbenchTabs.tsx b/src/components/DoWorkbench/DoWorkbenchTabs.tsx index 5c85e0c..69015ee 100644 --- a/src/components/DoWorkbench/DoWorkbenchTabs.tsx +++ b/src/components/DoWorkbench/DoWorkbenchTabs.tsx @@ -1,14 +1,12 @@ "use client"; import { - Autocomplete, Badge, Box, Button, CircularProgress, Tab, Tabs, - TextField, Tooltip, Typography, } from "@mui/material"; @@ -20,6 +18,7 @@ import GoodPickExecutionWorkbenchRecord from "./GoodPickExecutionWorkbenchRecord import { useTranslation } from "react-i18next"; import WorkbenchTicketReleaseTableTab from "./WorkbenchTicketReleaseTable"; import { Stack } from "@mui/system"; +import PrinterSelect from "@/components/common/PrinterSelect"; import Swal from "sweetalert2"; import { printDNWorkbench } from "@/app/api/do/actions"; import { @@ -251,41 +250,21 @@ const DoWorkbenchTabsInner: React.FC = ({ defaultTabIndex = 0, printerCom mb: 1, }} > - - {t("A4 Printer")}: - - option.name || option.label || option.code || `Printer ${option.id}`} + setA4Printer(newValue)} - sx={{ minWidth: 200 }} - size="small" - renderInput={(params) => ( - - )} + onChange={setA4Printer} + placeholder={t("A4 Printer")} + minWidth={200} /> - - {t("Label Printer")}: - - option.name || option.label || option.code || `Printer ${option.id}`} + setLabelPrinter(newValue)} - sx={{ minWidth: 200 }} - size="small" - renderInput={(params) => ( - - )} + onChange={setLabelPrinter} + placeholder={t("Label Printer")} + minWidth={200} /> - setPrimaryTab(v)} - variant="scrollable" - scrollButtons="auto" - sx={{ mb: 1, borderBottom: 1, borderColor: "divider" }} + - - - - - - - setPrimaryTab(v)} + variant="scrollable" + scrollButtons="auto" + sx={{ + flex: 1, + minWidth: 0, + borderBottom: 1, + borderColor: "divider", + "& .MuiTabs-flexContainer": { + columnGap: 0.5, + }, + "& .MuiTab-root": { + minWidth: "auto", + px: 1, + }, + }} + > + + + + + + + + + - + {( ["needs_action", "pending", "processing"] as PrimaryTab[] @@ -846,6 +939,22 @@ const JobOrderOpsTable: React.FC = ({ : t("view stockin")} )} + {showPrintLabelForRow(row) && ( + + )} {showManageActionsForRow(row) && ( )} + + {listTab === "putawayed" && + process.stockInLineId != null && ( + + )} = ({ printerCombo }) => { const { t } = useTranslation(["common", "productionProcess"]); @@ -49,10 +49,6 @@ const ProductionProcessPage: React.FC = ({ printerCo const [linkQcOpen, setLinkQcOpen] = useState(false); const [linkQcSilId, setLinkQcSilId] = useState(null); - const [selectedPrinter, setSelectedPrinter] = useState( - printerCombo && printerCombo.length > 0 ? printerCombo[0] : null - ); - useEffect(() => { if (typeof window !== 'undefined') { try { @@ -123,9 +119,6 @@ const ProductionProcessPage: React.FC = ({ printerCo router.replace(q ? `${pathname}?${q}` : pathname, { scroll: false }); }, [pathname, router, searchParams]); - const listTab = String(productionListState.pickBucket || "needs_action"); - const showPrinterBar = tabIndex === 0 && listTab === "pending_qc"; - if (selectedMatchingStock) { return ( = ({ printerCo return ( <> - {showPrinterBar && ( - - - - {t("Select Printer")}: - - - option.name || option.label || option.code || `Printer ${option.id}` - } - value={selectedPrinter || undefined} - onChange={(_, newValue) => setSelectedPrinter(newValue)} - sx={{ minWidth: 200 }} - size="small" - renderInput={(params) => ( - - )} - /> - - - )} - @@ -210,7 +155,7 @@ const ProductionProcessPage: React.FC = ({ printerCo {tabIndex === 0 && ( { @@ -231,7 +176,7 @@ const ProductionProcessPage: React.FC = ({ printerCo {tabIndex === 1 && ( { if (jobOrderId != null) setSelectedProcessId(jobOrderId); }} diff --git a/src/components/Qc/QcStockInModal.tsx b/src/components/Qc/QcStockInModal.tsx index 9568f10..9f093c2 100644 --- a/src/components/Qc/QcStockInModal.tsx +++ b/src/components/Qc/QcStockInModal.tsx @@ -1,7 +1,6 @@ "use client"; import { QcItemWithChecks, QcData } from "@/app/api/qc"; import { - Autocomplete, Box, Button, Divider, @@ -29,6 +28,7 @@ import dayjs from "dayjs"; import { fetchPoQrcode } from "@/app/api/pdf/actions"; import { downloadFile } from "@/app/utils/commonUtil"; import { PrinterCombo } from "@/app/api/settings/printer"; +import PrinterSelect from "@/components/common/PrinterSelect"; import { EscalationResult } from "@/app/api/escalation"; import { SessionWithTokens } from "@/config/authConfig"; import { GridRowModesModel } from "@mui/x-data-grid"; @@ -72,6 +72,7 @@ interface Props extends CommonProps { // itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] } & { escResult?: EscalationResult[] }; } +/** FP-MTMS Version Checklist | Functions Ref. No. 56 | v1.0.0 | 2026-08-06 */ const QcStockInModal: React.FC = ({ open, onClose, @@ -840,27 +841,17 @@ const printQrcode = useCallback( {tabIndex == 1 && ( - - - option.name || option.label || option.code || `Printer ${option.id}` - } - value={selectedPrinter} - onChange={(_, newValue) => { - if (newValue) setSelectedPrinter(newValue); - }} - renderInput={(params) => ( - - )} - /> + + { + if (p) setSelectedPrinter(p); + }} + placeholder={t("Label Printer")} + minWidth={300} + /> t.trim().toLowerCase(), + ); + const filtered = list.filter((p) => + types.includes(String(p.type ?? "").trim().toLowerCase()), + ); + if (filtered.length > 0) return filtered; + return fallbackToAll ? list : filtered; +} + +export type PrinterSelectProps = { + /** Outer label text (required), e.g. 「列印機」「標籤打印機」 */ + label: React.ReactNode; + printers: PrinterCombo[]; + value: PrinterCombo | null | undefined; + onChange: (printer: PrinterCombo | null) => void; + /** Filter by printer.type, e.g. "Label" | "A4" */ + typeFilter?: string | string[]; + /** If type filter yields none, show all printers (default false) */ + fallbackToAll?: boolean; + /** Shown inside Select when nothing selected */ + placeholder?: string; + minWidth?: number; + fullWidth?: boolean; + disabled?: boolean; + size?: "small" | "medium"; + /** Append ":" after label when label is a string (default true) */ + appendColon?: boolean; +}; + +/** + * FP-MTMS Version Checklist | Functions Ref. No. 54 | v1.0.0 | 2026-08-06 + * Shared printer picker: outer label + Select (selected value vertically centered, no floating label). + */ +const PrinterSelect: React.FC = ({ + label, + printers, + value, + onChange, + typeFilter, + fallbackToAll = false, + placeholder, + minWidth = 220, + fullWidth = false, + disabled = false, + size = "small", + appendColon = true, +}) => { + const options = useMemo( + () => filterPrintersByType(printers, typeFilter, fallbackToAll), + [printers, typeFilter, fallbackToAll], + ); + + const labelNode = + typeof label === "string" && appendColon && !label.trim().endsWith(":") + ? `${label}:` + : label; + + const height = size === "small" ? 40 : 48; + + return ( + + + {labelNode} + + + + + ); +}; + +export default PrinterSelect; diff --git a/src/i18n/en/productionProcess.json b/src/i18n/en/productionProcess.json index 3a2c388..6c7cec7 100644 --- a/src/i18n/en/productionProcess.json +++ b/src/i18n/en/productionProcess.json @@ -138,6 +138,13 @@ "Prep Time (Minutes)": "Prep Time (Minutes)", "Previous page": "Previous page", "Printer": "Printer", + "Label Printer": "Label Printer", + "A4 Printer": "A4 Printer", + "Print Label": "Print Label", + "Printing": "Printing", + "Print job sent successfully": "Print job sent successfully", + "Please select a printer": "Please select a printer", + "Please select a label printer": "Please select a label printer", "Process": "Process", "Process & Equipment": "Process & Equipment", "Process Description": "Process Description", diff --git a/src/i18n/en/purchaseOrder.json b/src/i18n/en/purchaseOrder.json index 1137ef1..132c88f 100644 --- a/src/i18n/en/purchaseOrder.json +++ b/src/i18n/en/purchaseOrder.json @@ -151,6 +151,8 @@ "Found": "Found", "escalation processing": "Escalation Processing", "Printer": "Printer", + "Label Printer": "Label Printer", + "A4 Printer": "A4 Printer", "Printing": "Printing", "rejectQty": "Reject Qty", "QC decision is required": "QC decision is required", diff --git a/src/i18n/zh/productionProcess.json b/src/i18n/zh/productionProcess.json index 9c40fde..be9f2ae 100644 --- a/src/i18n/zh/productionProcess.json +++ b/src/i18n/zh/productionProcess.json @@ -138,6 +138,13 @@ "Prep Time (Minutes)": "準備時間(分鐘)", "Previous page": "上一頁", "Printer": "列印機", + "Label Printer": "標籤打印機", + "A4 Printer": "A4 打印機", + "Print Label": "列印標籤", + "Printing": "列印中", + "Print job sent successfully": "列印作業已成功送出", + "Please select a printer": "請選擇打印機", + "Please select a label printer": "請選擇標籤打印機", "Process": "工序", "Process & Equipment": "製程與設備", "Process Description": "工序說明", diff --git a/src/i18n/zh/purchaseOrder.json b/src/i18n/zh/purchaseOrder.json index c023d8b..d81863d 100644 --- a/src/i18n/zh/purchaseOrder.json +++ b/src/i18n/zh/purchaseOrder.json @@ -151,6 +151,8 @@ "Found": "已找到", "escalation processing": "處理上報記錄", "Printer": "列印機", + "Label Printer": "標籤打印機", + "A4 Printer": "A4 打印機", "Printing": "列印中", "rejectQty": "拒絕數量", "QC decision is required": "請決定品檢結果",