| @@ -29,6 +29,7 @@ export interface SearchJoResultRequest extends Pageable { | |||||
| planStart?: string; | planStart?: string; | ||||
| planStartTo?: string; | planStartTo?: string; | ||||
| jobTypeName?: string; | jobTypeName?: string; | ||||
| joSearchStatus?: string; | |||||
| } | } | ||||
| export interface productProcessLineQtyRequest { | export interface productProcessLineQtyRequest { | ||||
| @@ -672,6 +673,16 @@ export const deleteJobOrder=cache(async (jobOrderId: number) => { | |||||
| } | } | ||||
| ); | ); | ||||
| }); | }); | ||||
| export const setJobOrderHidden = cache(async (jobOrderId: number, hidden: boolean) => { | |||||
| const response = await serverFetchJson<any>(`${BASE_API_URL}/jo/set-hidden`, { | |||||
| method: "POST", | |||||
| headers: { "Content-Type": "application/json" }, | |||||
| body: JSON.stringify({ id: jobOrderId, hidden }), | |||||
| }); | |||||
| revalidateTag("jos"); | |||||
| return response; | |||||
| }); | |||||
| export const fetchAllJobTypes = cache(async () => { | export const fetchAllJobTypes = cache(async () => { | ||||
| return serverFetchJson<JobTypeResponse[]>( | return serverFetchJson<JobTypeResponse[]>( | ||||
| `${BASE_API_URL}/jo/jobTypes`, | `${BASE_API_URL}/jo/jobTypes`, | ||||
| @@ -1,5 +1,5 @@ | |||||
| "use client" | "use client" | ||||
| import { SearchJoResultRequest, fetchJos, releaseJo, updateJo, updateProductProcessPriority, updateJoReqQty } from "@/app/api/jo/actions"; | |||||
| import { SearchJoResultRequest, fetchJos, releaseJo, setJobOrderHidden, updateJo, updateProductProcessPriority, updateJoReqQty } from "@/app/api/jo/actions"; | |||||
| import React, { useCallback, useEffect, useMemo, useState } from "react"; | import React, { useCallback, useEffect, useMemo, useState } from "react"; | ||||
| import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||
| import { Criterion } from "../SearchBox"; | import { Criterion } from "../SearchBox"; | ||||
| @@ -39,8 +39,7 @@ interface Props { | |||||
| jobTypes: JobTypeResponse[]; | jobTypes: JobTypeResponse[]; | ||||
| } | } | ||||
| type SearchQuery = Partial<Omit<JobOrder, "id">>; | |||||
| type SearchParamNames = keyof SearchQuery; | |||||
| type SearchParamNames = "code" | "itemName" | "planStart" | "planStartTo" | "jobTypeName" | "joSearchStatus"; | |||||
| const JoSearch: React.FC<Props> = ({ defaultInputs, bomCombo, printerCombo, jobTypes }) => { | const JoSearch: React.FC<Props> = ({ defaultInputs, bomCombo, printerCombo, jobTypes }) => { | ||||
| const { t } = useTranslation("jo"); | const { t } = useTranslation("jo"); | ||||
| @@ -58,6 +57,9 @@ const JoSearch: React.FC<Props> = ({ defaultInputs, bomCombo, printerCombo, jobT | |||||
| const [checkboxIds, setCheckboxIds] = useState<(string | number)[]>([]); | const [checkboxIds, setCheckboxIds] = useState<(string | number)[]>([]); | ||||
| const [releasingJoIds, setReleasingJoIds] = useState<Set<number>>(new Set()); | const [releasingJoIds, setReleasingJoIds] = useState<Set<number>>(new Set()); | ||||
| const [isBatchReleasing, setIsBatchReleasing] = useState(false); | const [isBatchReleasing, setIsBatchReleasing] = useState(false); | ||||
| const [cancelConfirmJoId, setCancelConfirmJoId] = useState<number | null>(null); | |||||
| const [cancelSubmitting, setCancelSubmitting] = useState(false); | |||||
| const [cancelingJoIds, setCancelingJoIds] = useState<Set<number>>(new Set()); | |||||
| // 合并后的统一编辑 Dialog 状态 | // 合并后的统一编辑 Dialog 状态 | ||||
| const [openEditDialog, setOpenEditDialog] = useState(false); | const [openEditDialog, setOpenEditDialog] = useState(false); | ||||
| @@ -160,6 +162,19 @@ const JoSearch: React.FC<Props> = ({ defaultInputs, bomCombo, printerCombo, jobT | |||||
| type: "select", | type: "select", | ||||
| options: jobTypes.map(jt => jt.name) | options: jobTypes.map(jt => jt.name) | ||||
| }, | }, | ||||
| { | |||||
| label: t("Status"), | |||||
| paramName: "joSearchStatus", | |||||
| type: "select-labelled", | |||||
| options: [ | |||||
| { label: t("Pending"), value: "pending" }, | |||||
| { label: t("Packaging"), value: "packaging" }, | |||||
| { label: t("Processing"), value: "processing" }, | |||||
| { label: t("Storing"), value: "storing" }, | |||||
| { label: t("Put Awayed"), value: "putAwayed" }, | |||||
| { label: t("cancel"), value: "cancel" }, | |||||
| ], | |||||
| }, | |||||
| ], [t, jobTypes]) | ], [t, jobTypes]) | ||||
| const fetchBomForJo = useCallback(async (jo: JobOrder): Promise<BomCombo | null> => { | const fetchBomForJo = useCallback(async (jo: JobOrder): Promise<BomCombo | null> => { | ||||
| @@ -288,6 +303,29 @@ const JoSearch: React.FC<Props> = ({ defaultInputs, bomCombo, printerCombo, jobT | |||||
| }); | }); | ||||
| } | } | ||||
| }, [inputs, pagingController, t, newPageFetch]); | }, [inputs, pagingController, t, newPageFetch]); | ||||
| const handleConfirmCancelJobOrder = useCallback(async () => { | |||||
| if (cancelConfirmJoId == null) return; | |||||
| const id = cancelConfirmJoId; | |||||
| setCancelSubmitting(true); | |||||
| setCancelingJoIds((prev) => new Set(prev).add(id)); | |||||
| try { | |||||
| await setJobOrderHidden(id, true); | |||||
| msg(t("update success")); | |||||
| setCancelConfirmJoId(null); | |||||
| await newPageFetch(pagingController, inputs); | |||||
| } catch (error) { | |||||
| console.error("Error cancelling job order:", error); | |||||
| msg(t("update failed")); | |||||
| } finally { | |||||
| setCancelSubmitting(false); | |||||
| setCancelingJoIds((prev) => { | |||||
| const next = new Set(prev); | |||||
| next.delete(id); | |||||
| return next; | |||||
| }); | |||||
| } | |||||
| }, [cancelConfirmJoId, newPageFetch, pagingController, inputs, t]); | |||||
| const selectedPlanningJoIds = useMemo(() => { | const selectedPlanningJoIds = useMemo(() => { | ||||
| const selectedIds = new Set(checkboxIds.map((id) => Number(id))); | const selectedIds = new Set(checkboxIds.map((id) => Number(id))); | ||||
| @@ -444,6 +482,8 @@ const JoSearch: React.FC<Props> = ({ defaultInputs, bomCombo, printerCombo, jobT | |||||
| renderCell: (row) => { | renderCell: (row) => { | ||||
| const isPlanning = isPlanningJo(row); | const isPlanning = isPlanningJo(row); | ||||
| const isReleasing = releasingJoIds.has(row.id) || isBatchReleasing; | const isReleasing = releasingJoIds.has(row.id) || isBatchReleasing; | ||||
| const isCancelingRow = cancelingJoIds.has(row.id); | |||||
| const isPutAwayed = row.stockInLineStatus?.toLowerCase() === "completed"; | |||||
| return ( | return ( | ||||
| <Stack direction="row" spacing={1} alignItems="center"> | <Stack direction="row" spacing={1} alignItems="center"> | ||||
| <Button | <Button | ||||
| @@ -458,6 +498,7 @@ const JoSearch: React.FC<Props> = ({ defaultInputs, bomCombo, printerCombo, jobT | |||||
| > | > | ||||
| {t("View")} | {t("View")} | ||||
| </Button> | </Button> | ||||
| {isPlanning ? ( | |||||
| <Button | <Button | ||||
| type="button" | type="button" | ||||
| variant="contained" | variant="contained" | ||||
| @@ -472,11 +513,27 @@ const JoSearch: React.FC<Props> = ({ defaultInputs, bomCombo, printerCombo, jobT | |||||
| > | > | ||||
| {t("Release")} | {t("Release")} | ||||
| </Button> | </Button> | ||||
| ) : ( | |||||
| <Button | |||||
| type="button" | |||||
| variant="contained" | |||||
| color="warning" | |||||
| disabled={isPutAwayed || isCancelingRow || isBatchReleasing || cancelSubmitting} | |||||
| sx={{ minWidth: 120 }} | |||||
| onClick={(e) => { | |||||
| e.stopPropagation(); | |||||
| setCancelConfirmJoId(row.id); | |||||
| }} | |||||
| startIcon={isCancelingRow ? <CircularProgress size={16} color="inherit" /> : undefined} | |||||
| > | |||||
| {t("Cancel Job Order")} | |||||
| </Button> | |||||
| )} | |||||
| </Stack> | </Stack> | ||||
| ) | ) | ||||
| } | } | ||||
| }, | }, | ||||
| ], [t, inventoryData, detailedJos, handleOpenEditDialog, handleReleaseJo, isPlanningJo, releasingJoIds, isBatchReleasing] | |||||
| ], [t, inventoryData, detailedJos, handleOpenEditDialog, handleReleaseJo, isPlanningJo, releasingJoIds, isBatchReleasing, cancelingJoIds, cancelSubmitting] | |||||
| ) | ) | ||||
| const handleUpdateReqQty = useCallback(async (jobOrderId: number, newReqQty: number) => { | const handleUpdateReqQty = useCallback(async (jobOrderId: number, newReqQty: number) => { | ||||
| @@ -622,7 +679,8 @@ const JoSearch: React.FC<Props> = ({ defaultInputs, bomCombo, printerCombo, jobT | |||||
| ...query, | ...query, | ||||
| planStart: query.planStart ? `${query.planStart}T00:00` : query.planStart, | planStart: query.planStart ? `${query.planStart}T00:00` : query.planStart, | ||||
| planStartTo: query.planStartTo ? `${query.planStartTo}T23:59:59` : query.planStartTo, | planStartTo: query.planStartTo ? `${query.planStartTo}T23:59:59` : query.planStartTo, | ||||
| jobTypeName: query.jobTypeName && query.jobTypeName !== "All" ? query.jobTypeName : "" | |||||
| jobTypeName: query.jobTypeName && query.jobTypeName !== "All" ? query.jobTypeName : "", | |||||
| joSearchStatus: query.joSearchStatus && query.joSearchStatus !== "All" ? query.joSearchStatus : "all", | |||||
| }; | }; | ||||
| setInputs({ | setInputs({ | ||||
| @@ -630,7 +688,8 @@ const JoSearch: React.FC<Props> = ({ defaultInputs, bomCombo, printerCombo, jobT | |||||
| itemName: transformedQuery.itemName, | itemName: transformedQuery.itemName, | ||||
| planStart: transformedQuery.planStart, | planStart: transformedQuery.planStart, | ||||
| planStartTo: transformedQuery.planStartTo, | planStartTo: transformedQuery.planStartTo, | ||||
| jobTypeName: transformedQuery.jobTypeName | |||||
| jobTypeName: transformedQuery.jobTypeName, | |||||
| joSearchStatus: transformedQuery.joSearchStatus | |||||
| }); | }); | ||||
| setPagingController(defaultPagingController); | setPagingController(defaultPagingController); | ||||
| @@ -839,6 +898,24 @@ const JoSearch: React.FC<Props> = ({ defaultInputs, bomCombo, printerCombo, jobT | |||||
| </Button> | </Button> | ||||
| </DialogActions> | </DialogActions> | ||||
| </Dialog> | </Dialog> | ||||
| <Dialog | |||||
| open={cancelConfirmJoId !== null} | |||||
| onClose={() => !cancelSubmitting && setCancelConfirmJoId(null)} | |||||
| maxWidth="xs" | |||||
| fullWidth | |||||
| > | |||||
| <DialogTitle>{t("Confirm cancel job order")}</DialogTitle> | |||||
| <DialogContent> | |||||
| <Typography variant="body2">{t("Cancel job order confirm message")}</Typography> | |||||
| </DialogContent> | |||||
| <DialogActions> | |||||
| <Button onClick={() => setCancelConfirmJoId(null)} disabled={cancelSubmitting}>{t("Cancel")}</Button> | |||||
| <Button variant="contained" color="warning" onClick={() => void handleConfirmCancelJobOrder()} disabled={cancelSubmitting}> | |||||
| {cancelSubmitting ? <CircularProgress size={20} /> : t("Cancel Job Order")} | |||||
| </Button> | |||||
| </DialogActions> | |||||
| </Dialog> | |||||
| </> | </> | ||||
| } | } | ||||
| @@ -18,6 +18,7 @@ const JoSearchWrapper: React.FC & SubComponents = async () => { | |||||
| itemName: "", | itemName: "", | ||||
| planStart: `${todayStr}T00:00`, | planStart: `${todayStr}T00:00`, | ||||
| planStartTo: `${todayStr}T23:59:59`, | planStartTo: `${todayStr}T23:59:59`, | ||||
| joSearchStatus: "all", | |||||
| } | } | ||||
| @@ -22,7 +22,14 @@ const OverallTimeRemainingCard: React.FC<OverallTimeRemainingCardProps> = ({ | |||||
| console.log("🕐 OverallTimeRemainingCard - processData?.startTime type:", typeof processData?.startTime); | console.log("🕐 OverallTimeRemainingCard - processData?.startTime type:", typeof processData?.startTime); | ||||
| console.log("🕐 OverallTimeRemainingCard - processData?.startTime isArray:", Array.isArray(processData?.startTime)); | console.log("🕐 OverallTimeRemainingCard - processData?.startTime isArray:", Array.isArray(processData?.startTime)); | ||||
| if (!processData?.startTime) { | |||||
| const jobOrderStatus = String((processData as any)?.jobOrderStatus ?? "").trim().toLowerCase(); | |||||
| const shouldStopCount = | |||||
| jobOrderStatus === "storing" || | |||||
| jobOrderStatus === "completed" || | |||||
| jobOrderStatus === "pendingqc" || | |||||
| jobOrderStatus === "pending_qc"; | |||||
| if (shouldStopCount || !processData?.startTime) { | |||||
| console.log("❌ OverallTimeRemainingCard - No startTime found"); | console.log("❌ OverallTimeRemainingCard - No startTime found"); | ||||
| setOverallRemainingTime(null); | setOverallRemainingTime(null); | ||||
| setIsOverTime(false); | setIsOverTime(false); | ||||
| @@ -176,7 +183,7 @@ const OverallTimeRemainingCard: React.FC<OverallTimeRemainingCardProps> = ({ | |||||
| update(); | update(); | ||||
| const timer = setInterval(update, 1000); | const timer = setInterval(update, 1000); | ||||
| return () => clearInterval(timer); | return () => clearInterval(timer); | ||||
| }, [processData?.startTime, processData?.productProcessLines]); | |||||
| }, [processData?.startTime, processData?.productProcessLines, (processData as any)?.jobOrderStatus]); | |||||
| if (!processData?.startTime || overallRemainingTime === null) { | if (!processData?.startTime || overallRemainingTime === null) { | ||||
| return null; | return null; | ||||
| @@ -23,7 +23,7 @@ import { | |||||
| } from "@mui/material"; | } from "@mui/material"; | ||||
| import ArrowBackIcon from '@mui/icons-material/ArrowBack'; | import ArrowBackIcon from '@mui/icons-material/ArrowBack'; | ||||
| import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||
| import { fetchProductProcessesByJobOrderId ,deleteJobOrder, updateProductProcessPriority, updateJoPlanStart,updateJoReqQty,newProductProcessLine,JobOrderLineInfo} from "@/app/api/jo/actions"; | |||||
| import { fetchProductProcessesByJobOrderId ,deleteJobOrder, setJobOrderHidden, updateProductProcessPriority, updateJoPlanStart,updateJoReqQty,newProductProcessLine,JobOrderLineInfo} from "@/app/api/jo/actions"; | |||||
| import ProductionProcessDetail from "./ProductionProcessDetail"; | import ProductionProcessDetail from "./ProductionProcessDetail"; | ||||
| import { BomCombo } from "@/app/api/bom"; | import { BomCombo } from "@/app/api/bom"; | ||||
| import { fetchBomCombo } from "@/app/api/bom/index"; | import { fetchBomCombo } from "@/app/api/bom/index"; | ||||
| @@ -265,15 +265,42 @@ const stockCounts = useMemo(() => { | |||||
| insufficient: total - sufficient, | insufficient: total - sufficient, | ||||
| }; | }; | ||||
| }, [jobOrderLines, inventoryData]); | }, [jobOrderLines, inventoryData]); | ||||
| const status = processData?.status?.toLowerCase?.() ?? ""; | |||||
| const handleDeleteJobOrder = useCallback(async ( jobOrderId: number) => { | |||||
| const response = await deleteJobOrder(jobOrderId) | |||||
| if (response) { | |||||
| //setProcessData(response.entity); | |||||
| //await fetchData(); | |||||
| const jobOrderPlanning = useMemo( | |||||
| () => (processData?.jobOrderStatus ?? "").toLowerCase() === "planning", | |||||
| [processData?.jobOrderStatus] | |||||
| ); | |||||
| const isPutAwayed = useMemo( | |||||
| () => (processData?.jobOrderStatus ?? "").toLowerCase() === "completed", | |||||
| [processData?.jobOrderStatus] | |||||
| ); | |||||
| const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false); | |||||
| const [cancelConfirmOpen, setCancelConfirmOpen] = useState(false); | |||||
| const [deleteLoading, setDeleteLoading] = useState(false); | |||||
| const [cancelLoading, setCancelLoading] = useState(false); | |||||
| const handleConfirmDeleteJobOrder = useCallback(async () => { | |||||
| setDeleteLoading(true); | |||||
| try { | |||||
| const response = await deleteJobOrder(jobOrderId); | |||||
| if (response) { | |||||
| setDeleteConfirmOpen(false); | |||||
| onBack(); | |||||
| } | |||||
| } finally { | |||||
| setDeleteLoading(false); | |||||
| } | |||||
| }, [jobOrderId, onBack]); | |||||
| const handleConfirmCancelJobOrder = useCallback(async () => { | |||||
| setCancelLoading(true); | |||||
| try { | |||||
| await setJobOrderHidden(jobOrderId, true); | |||||
| setCancelConfirmOpen(false); | |||||
| onBack(); | onBack(); | ||||
| } finally { | |||||
| setCancelLoading(false); | |||||
| } | } | ||||
| }, [jobOrderId]); | |||||
| }, [jobOrderId, onBack]); | |||||
| const handleRelease = useCallback(async ( jobOrderId: number) => { | const handleRelease = useCallback(async ( jobOrderId: number) => { | ||||
| // TODO: 替换为实际的 release 调用 | // TODO: 替换为实际的 release 调用 | ||||
| console.log("Release clicked for jobOrderId:", jobOrderId); | console.log("Release clicked for jobOrderId:", jobOrderId); | ||||
| @@ -675,15 +702,24 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { | |||||
| <Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}> | <Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}> | ||||
| {t("Lines with insufficient stock: ")}<strong style={{ color: "red" }}>{stockCounts.insufficient}</strong> | {t("Lines with insufficient stock: ")}<strong style={{ color: "red" }}>{stockCounts.insufficient}</strong> | ||||
| </Typography> | </Typography> | ||||
| {fromJosave && ( | |||||
| {fromJosave && jobOrderPlanning && ( | |||||
| <Button | <Button | ||||
| variant="contained" | |||||
| color="error" | |||||
| onClick={() => handleDeleteJobOrder(jobOrderId)} | |||||
| disabled={processData?.jobOrderStatus !== "planning"} | |||||
| > | |||||
| {t("Delete Job Order")} | |||||
| </Button> | |||||
| variant="contained" | |||||
| color="error" | |||||
| onClick={() => setDeleteConfirmOpen(true)} | |||||
| > | |||||
| {t("Delete Job Order")} | |||||
| </Button> | |||||
| )} | |||||
| {fromJosave && !jobOrderPlanning && ( | |||||
| <Button | |||||
| variant="contained" | |||||
| color="warning" | |||||
| onClick={() => setCancelConfirmOpen(true)} | |||||
| disabled={isPutAwayed} | |||||
| > | |||||
| {t("Cancel Job Order")} | |||||
| </Button> | |||||
| )} | )} | ||||
| {fromJosave && ( | {fromJosave && ( | ||||
| <Button | <Button | ||||
| @@ -781,7 +817,7 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { | |||||
| setTabIndex(0); | setTabIndex(0); | ||||
| }} | }} | ||||
| fromJosave={fromJosave} | |||||
| fromJosave={Boolean(fromJosave && !isPutAwayed)} | |||||
| /> | /> | ||||
| )} | )} | ||||
| {tabIndex === 3 && <ProductionProcessesLineRemarkTableContent />} | {tabIndex === 3 && <ProductionProcessesLineRemarkTableContent />} | ||||
| @@ -928,6 +964,32 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { | |||||
| </DialogActions> | </DialogActions> | ||||
| </Dialog> | </Dialog> | ||||
| <Dialog open={deleteConfirmOpen} onClose={() => !deleteLoading && setDeleteConfirmOpen(false)} maxWidth="xs" fullWidth> | |||||
| <DialogTitle>{t("Confirm delete job order")}</DialogTitle> | |||||
| <DialogContent> | |||||
| <Typography variant="body2">{t("Delete job order confirm message")}</Typography> | |||||
| </DialogContent> | |||||
| <DialogActions> | |||||
| <Button onClick={() => setDeleteConfirmOpen(false)} disabled={deleteLoading}>{t("Cancel")}</Button> | |||||
| <Button variant="contained" color="error" onClick={() => void handleConfirmDeleteJobOrder()} disabled={deleteLoading}> | |||||
| {deleteLoading ? <CircularProgress size={20} /> : t("Delete Job Order")} | |||||
| </Button> | |||||
| </DialogActions> | |||||
| </Dialog> | |||||
| <Dialog open={cancelConfirmOpen} onClose={() => !cancelLoading && setCancelConfirmOpen(false)} maxWidth="xs" fullWidth> | |||||
| <DialogTitle>{t("Confirm cancel job order")}</DialogTitle> | |||||
| <DialogContent> | |||||
| <Typography variant="body2">{t("Cancel job order confirm message")}</Typography> | |||||
| </DialogContent> | |||||
| <DialogActions> | |||||
| <Button onClick={() => setCancelConfirmOpen(false)} disabled={cancelLoading}>{t("Cancel")}</Button> | |||||
| <Button variant="contained" color="warning" onClick={() => void handleConfirmCancelJobOrder()} disabled={cancelLoading}> | |||||
| {cancelLoading ? <CircularProgress size={20} /> : t("Cancel Job Order")} | |||||
| </Button> | |||||
| </DialogActions> | |||||
| </Dialog> | |||||
| </Box> | </Box> | ||||
| </Box> | </Box> | ||||
| @@ -217,16 +217,11 @@ const ProductProcessList: React.FC<ProductProcessListProps> = ({ | |||||
| (inputs: Record<SearchParam | `${SearchParam}To`, string>) => { | (inputs: Record<SearchParam | `${SearchParam}To`, string>) => { | ||||
| const selectedProcessType = (inputs.processType || "all") as ProcessFilter; | const selectedProcessType = (inputs.processType || "all") as ProcessFilter; | ||||
| const fallback = defaultPlanStartRange(); | const fallback = defaultPlanStartRange(); | ||||
| let from = (inputs.date || "").trim() || fallback.from; | |||||
| let to = (inputs.dateTo || "").trim() || fallback.to; | |||||
| if (dayjs(from).isAfter(dayjs(to), "day")) { | |||||
| [from, to] = [to, from]; | |||||
| } | |||||
| const selectedDate = (inputs.date || "").trim() || fallback.from; | |||||
| onListPersistedStateChange((prev) => ({ | onListPersistedStateChange((prev) => ({ | ||||
| ...prev, | ...prev, | ||||
| filter: selectedProcessType, | filter: selectedProcessType, | ||||
| planStartFrom: from, | |||||
| planStartTo: to, | |||||
| date: selectedDate, | |||||
| itemCode: inputs.itemCode?.trim() ? inputs.itemCode.trim() : null, | itemCode: inputs.itemCode?.trim() ? inputs.itemCode.trim() : null, | ||||
| jobOrderCode: inputs.jobOrderCode?.trim() ? inputs.jobOrderCode.trim() : null, | jobOrderCode: inputs.jobOrderCode?.trim() ? inputs.jobOrderCode.trim() : null, | ||||
| selectedItemCodes: [], | selectedItemCodes: [], | ||||
| @@ -241,8 +236,7 @@ const ProductProcessList: React.FC<ProductProcessListProps> = ({ | |||||
| onListPersistedStateChange((prev) => ({ | onListPersistedStateChange((prev) => ({ | ||||
| ...prev, | ...prev, | ||||
| filter: "all", | filter: "all", | ||||
| planStartFrom: r.from, | |||||
| planStartTo: r.to, | |||||
| date: r.from, | |||||
| itemCode: null, | itemCode: null, | ||||
| jobOrderCode: null, | jobOrderCode: null, | ||||
| selectedItemCodes: [], | selectedItemCodes: [], | ||||
| @@ -368,16 +362,11 @@ const ProductProcessList: React.FC<ProductProcessListProps> = ({ | |||||
| const r = defaultPlanStartRange(); | const r = defaultPlanStartRange(); | ||||
| return [ | return [ | ||||
| { | { | ||||
| type: "dateRange", | |||||
| label: t("Plan start (from)"), | |||||
| label2: t("Plan start (to)"), | |||||
| type: "date", | |||||
| label: t("Search date"), | |||||
| paramName: "date", | paramName: "date", | ||||
| defaultValue: r.from, | |||||
| defaultValueTo: r.to, | |||||
| preFilledValue: { | |||||
| from: appliedSearch.date, | |||||
| to: appliedSearch.date, | |||||
| }, | |||||
| defaultValue: appliedSearch.date, | |||||
| preFilledValue: appliedSearch.date, | |||||
| }, | }, | ||||
| { | { | ||||
| type: "text", | type: "text", | ||||
| @@ -471,6 +460,7 @@ const ProductProcessList: React.FC<ProductProcessListProps> = ({ | |||||
| {paged.map((process) => { | {paged.map((process) => { | ||||
| const status = String(process.status || ""); | const status = String(process.status || ""); | ||||
| const statusLower = status.toLowerCase(); | const statusLower = status.toLowerCase(); | ||||
| const displayStatus = statusLower === "in_progress" ? "processing" : status; | |||||
| const statusColor = | const statusColor = | ||||
| statusLower === "completed" | statusLower === "completed" | ||||
| ? "success" | ? "success" | ||||
| @@ -529,7 +519,7 @@ const ProductProcessList: React.FC<ProductProcessListProps> = ({ | |||||
| </Typography> | </Typography> | ||||
| </Box> | </Box> | ||||
| <Chip size="small" label={t(status)} color={statusColor as any} /> | |||||
| <Chip size="small" label={t(displayStatus)} color={statusColor as any} /> | |||||
| </Stack> | </Stack> | ||||
| <Typography variant="body2" color="text.secondary"> | <Typography variant="body2" color="text.secondary"> | ||||
| {t("Lot No")}: {process.lotNo ?? "-"} | {t("Lot No")}: {process.lotNo ?? "-"} | ||||
| @@ -24,6 +24,11 @@ | |||||
| "Confirm to Pass this Process?": "確認要通過此工序嗎?", | "Confirm to Pass this Process?": "確認要通過此工序嗎?", | ||||
| "Equipment Name": "設備", | "Equipment Name": "設備", | ||||
| "Confirm to update this Job Order?": "確認要完成此工單嗎?", | "Confirm to update this Job Order?": "確認要完成此工單嗎?", | ||||
| "Cancel Job Order": "取消工單", | |||||
| "Confirm delete job order": "確認刪除工單", | |||||
| "Delete job order confirm message": "確定要刪除此工單嗎?此操作無法復原。", | |||||
| "Confirm cancel job order": "確認取消工單", | |||||
| "Cancel job order confirm message": "確定要取消此工單嗎?工單將從列表中隱藏。", | |||||
| "all": "全部", | "all": "全部", | ||||
| "Bom Uom": "BOM 單位", | "Bom Uom": "BOM 單位", | ||||
| "Searched Item": "已搜索物料", | "Searched Item": "已搜索物料", | ||||
| @@ -25,6 +25,7 @@ | |||||
| "UoM": "銷售單位", | "UoM": "銷售單位", | ||||
| "Select Another Bag Lot":"選擇另一個包裝袋", | "Select Another Bag Lot":"選擇另一個包裝袋", | ||||
| "No": "沒有", | "No": "沒有", | ||||
| "Packaging":"提料中", | |||||
| "Overall Time Remaining": "總剩餘時間", | "Overall Time Remaining": "總剩餘時間", | ||||
| "User not found with staffNo:": "用戶不存在", | "User not found with staffNo:": "用戶不存在", | ||||
| "Time Remaining": "剩餘時間", | "Time Remaining": "剩餘時間", | ||||
| @@ -41,9 +42,16 @@ | |||||
| "Lot No.": "批號", | "Lot No.": "批號", | ||||
| "Pass": "通過", | "Pass": "通過", | ||||
| "Delete Job Order": "刪除工單", | "Delete Job Order": "刪除工單", | ||||
| "Cancel Job Order": "取消工單", | |||||
| "Confirm delete job order": "確認刪除工單", | |||||
| "Delete job order confirm message": "確定要刪除此工單嗎?此操作無法復原。", | |||||
| "Confirm cancel job order": "確認取消工單", | |||||
| "Cancel job order confirm message": "確定要取消此工單嗎?工單將從列表中隱藏。", | |||||
| "Bom": "半成品/成品編號", | "Bom": "半成品/成品編號", | ||||
| "Release": "放單", | "Release": "放單", | ||||
| "Pending": "待掃碼", | "Pending": "待掃碼", | ||||
| "Put Awayed": "已上架", | |||||
| "cancel": "已取消", | |||||
| "Pending for pick": "待提料", | "Pending for pick": "待提料", | ||||
| "Planning": "計劃中", | "Planning": "計劃中", | ||||
| "Processing": "已開始工序", | "Processing": "已開始工序", | ||||