|
- "use client";
- import React, { useCallback, useEffect, useState, useRef } from "react";
- import {
- Box,
- Button,
- Paper,
- Stack,
- Typography,
- TextField,
- Table,
- TableBody,
- TableCell,
- TableContainer,
- TableHead,
- TableRow,
- Chip,
- Card,
- CardContent,
- CircularProgress,
- Dialog,
- DialogTitle,
- DialogContent,
- DialogActions,
- } from "@mui/material";
- import QrCodeIcon from '@mui/icons-material/QrCode';
- import { useTranslation } from "react-i18next";
- import { Operator, Machine } from "@/app/api/jo";
- import { useQrCodeScannerContext } from '../QrCodeScannerProvider/QrCodeScannerProvider';
- import { useSession } from "next-auth/react";
- import { SessionWithTokens } from "@/config/authConfig";
- import PlayArrowIcon from "@mui/icons-material/PlayArrow";
- import CheckCircleIcon from "@mui/icons-material/CheckCircle";
- import dayjs from "dayjs";
- import { OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
- import {
- // updateProductProcessLineQrscan,
- newUpdateProductProcessLineQrscan,
- fetchProductProcessLineDetail,
- JobOrderProcessLineDetailResponse,
- ProductProcessLineInfoResponse,
- startProductProcessLine,
- fetchProductProcessesByJobOrderId
- } from "@/app/api/jo/actions";
- import { fetchNameList, NameList } from "@/app/api/user/actions";
- import ProductionProcessStepExecution from "./ProductionProcessStepExecution";
- import ProductionOutputFormPage from "./ProductionOutputFormPage";
- import ProcessSummaryHeader from "./ProcessSummaryHeader";
- interface ProductProcessDetailProps {
- jobOrderId: number;
- onBack: () => void;
- fromJosave?: boolean;
- }
-
- const ProductionProcessDetail: React.FC<ProductProcessDetailProps> = ({
- jobOrderId,
- onBack,
- fromJosave,
- }) => {
- const { t } = useTranslation("common");
- const { data: session } = useSession() as { data: SessionWithTokens | null };
- const currentUserId = session?.id ? parseInt(session.id) : undefined;
- const { values: qrValues, startScan, stopScan, resetScan } = useQrCodeScannerContext();
- const [showOutputPage, setShowOutputPage] = useState(false);
- // 基本信息
- const [processData, setProcessData] = useState<any>(null);
- const [lines, setLines] = useState<ProductProcessLineInfoResponse[]>([]);
- const [loading, setLoading] = useState(false);
-
- // 选中的 line 和执行状态
- const [selectedLineId, setSelectedLineId] = useState<number | null>(null);
- const [isExecutingLine, setIsExecutingLine] = useState(false);
- const [isAutoSubmitting, setIsAutoSubmitting] = useState(false);
- // 扫描器状态
- const [isManualScanning, setIsManualScanning] = useState(false);
- const [processedQrCodes, setProcessedQrCodes] = useState<Set<string>>(new Set());
- const [scannedOperatorId, setScannedOperatorId] = useState<number | null>(null);
- const [scannedEquipmentId, setScannedEquipmentId] = useState<number | null>(null);
- // const [scannedEquipmentTypeSubTypeEquipmentNo, setScannedEquipmentTypeSubTypeEquipmentNo] = useState<string | null>(null);
- const [scannedStaffNo, setScannedStaffNo] = useState<string | null>(null);
- // const [scannedEquipmentDetailId, setScannedEquipmentDetailId] = useState<number | null>(null);
- const [scannedEquipmentCode, setScannedEquipmentCode] = useState<string | null>(null);
- const [scanningLineId, setScanningLineId] = useState<number | null>(null);
- const [lineDetailForScan, setLineDetailForScan] = useState<JobOrderProcessLineDetailResponse | null>(null);
- const [showScanDialog, setShowScanDialog] = useState(false);
- const autoSubmitTimerRef = useRef<NodeJS.Timeout | null>(null);
-
- // 产出表单
- const [outputData, setOutputData] = useState({
- byproductName: "",
- byproductQty: "",
- byproductUom: "",
- scrapQty: "",
- scrapUom: "",
- defectQty: "",
- defectUom: "",
- outputFromProcessQty: "",
- outputFromProcessUom: "",
- });
-
- // 处理 QR 码扫描
- // 处理 QR 码扫描
- const handleBackFromStep = async () => {
- await fetchProcessDetail(); // 重新拉取最新的 process/lines
- setIsExecutingLine(false);
- setSelectedLineId(null);
- setShowOutputPage(false);
- };
-
- // 获取 process 和 lines 数据
- const fetchProcessDetail = useCallback(async () => {
- setLoading(true);
- try {
- console.log(`🔍 Loading process detail for JobOrderId: ${jobOrderId}`);
-
- // 使用 fetchProductProcessesByJobOrderId 获取基础数据
- const processesWithLines = await fetchProductProcessesByJobOrderId(jobOrderId);
-
- if (!processesWithLines || processesWithLines.length === 0) {
- throw new Error("No processes found for this job order");
- }
-
- // 如果有多个 process,取第一个(或者可以根据需要选择)
- const currentProcess = processesWithLines[0];
-
- setProcessData(currentProcess);
-
- // 使用 productProcessLines 字段(API 返回的字段名)
- const lines = (currentProcess as any).productProcessLines || [];
- setLines(lines);
-
- console.log(" Process data loaded:", currentProcess);
- console.log(" Lines loaded:", lines);
- } catch (error) {
- console.error("❌ Error loading process detail:", error);
- //alert(`无法加载 Job Order ID ${jobOrderId} 的生产流程。该记录可能不存在。`);
- onBack();
- } finally {
- setLoading(false);
- }
- }, [jobOrderId, onBack]);
-
- useEffect(() => {
- fetchProcessDetail();
- }, [fetchProcessDetail]);
-
- // 开始执行某个 line
-
-
- // 提交产出数据
- /*
- const processQrCode = useCallback((qrValue: string, lineId: number) => {
- // 操作员格式:{2fitestu1} - 键盘模拟输入(测试用)
- if (qrValue.match(/\{2fitestu(\d+)\}/)) {
- const match = qrValue.match(/\{2fitestu(\d+)\}/);
- const userId = parseInt(match![1]);
-
- fetchNameList().then((users: NameList[]) => {
- const user = users.find((u: NameList) => u.id === userId);
- if (user) {
- setScannedOperatorId(user.id);
- }
- });
- return;
- }
-
- // 设备格式:{2fiteste1} - 键盘模拟输入(测试用)
- if (qrValue.match(/\{2fiteste(\d+)\}/)) {
- const match = qrValue.match(/\{2fiteste(\d+)\}/);
- const equipmentId = parseInt(match![1]);
- setScannedEquipmentId(equipmentId);
- return;
- }
-
-
- // 正常 QR 扫描器扫描:格式为 "operatorId: 1" 或 "equipmentId: 1"
- const trimmedValue = qrValue.trim();
-
- // 检查 operatorId 格式
- const operatorMatch = trimmedValue.match(/^operatorId:\s*(\d+)$/i);
- if (operatorMatch) {
- const operatorId = parseInt(operatorMatch[1]);
- fetchNameList().then((users: NameList[]) => {
- const user = users.find((u: NameList) => u.id === operatorId);
- if (user) {
- setScannedOperatorId(user.id);
- } else {
- console.warn(`User with ID ${operatorId} not found`);
- }
- });
- return;
- }
-
- // 检查 equipmentId 格式
- const equipmentMatch = trimmedValue.match(/^equipmentId:\s*(\d+)$/i);
- if (equipmentMatch) {
- const equipmentId = parseInt(equipmentMatch[1]);
- setScannedEquipmentId(equipmentId);
- return;
- }
-
- // 其他格式处理(JSON、普通文本等)
- try {
- const qrData = JSON.parse(qrValue);
- // TODO: 处理 JSON 格式的 QR 码
- } catch {
- // 普通文本格式
- // TODO: 处理普通文本格式
- }
- }, []);
- */
- // 提交产出数据
- const processQrCode = useCallback((qrValue: string, lineId: number) => {
- // 设备快捷格式:{2fiteste数字} - 自动生成 equipmentTypeSubTypeEquipmentNo
- // 格式:{2fiteste数字} = line.equipment_name + "-数字號"
- // 例如:{2fiteste1} = "包裝機類-真空八爪魚機-1號"
- if (qrValue.match(/\{2fiteste(\d+)\}/)) {
- const match = qrValue.match(/\{2fiteste(\d+)\}/);
- const equipmentNo = parseInt(match![1]);
-
- // 根据 lineId 找到对应的 line
- const currentLine = lines.find(l => l.id === lineId);
- if (currentLine && currentLine.equipment_name) {
- const equipmentTypeSubTypeEquipmentNo = `${currentLine.equipment_name}-${equipmentNo}號`;
- setScannedEquipmentCode(equipmentTypeSubTypeEquipmentNo);
- console.log(`Generated equipmentTypeSubTypeEquipmentNo: ${equipmentTypeSubTypeEquipmentNo}`);
- } else {
- // 如果找不到 line,尝试从 API 获取 line detail
- console.warn(`Line with ID ${lineId} not found in current lines, fetching from API...`);
- fetchProductProcessLineDetail(lineId)
- .then((lineDetail) => {
- // 从 lineDetail 中获取 equipment_name
- const equipmentName = (lineDetail as any).equipment || (lineDetail as any).equipmentType || "";
- if (equipmentName) {
- const equipmentTypeSubTypeEquipmentNo = `${equipmentName}-${equipmentNo}號`;
- setScannedEquipmentCode(equipmentTypeSubTypeEquipmentNo);
- console.log(`Generated equipmentTypeSubTypeEquipmentNo from API: ${equipmentTypeSubTypeEquipmentNo}`);
- } else {
- console.warn(`Equipment name not found in line detail for lineId: ${lineId}`);
- }
- })
- .catch((err) => {
- console.error(`Failed to fetch line detail for lineId ${lineId}:`, err);
- });
- }
- return;
- }
-
- // 员工编号格式:{2fitestu任何内容} - 直接作为 staffNo
- // 例如:{2fitestu123} = staffNo: "123"
- // 例如:{2fitestustaff001} = staffNo: "staff001"
- if (qrValue.match(/\{2fitestu(.+)\}/)) {
- const match = qrValue.match(/\{2fitestu(.+)\}/);
- const staffNo = match![1];
- setScannedStaffNo(staffNo);
- return;
- }
-
- // 正常 QR 扫描器扫描格式
- const trimmedValue = qrValue.trim();
-
- // 检查 staffNo 格式:"staffNo: STAFF001" 或 "staffNo:STAFF001"
- const staffNoMatch = trimmedValue.match(/^staffNo:\s*(.+)$/i);
- if (staffNoMatch) {
- const staffNo = staffNoMatch[1].trim();
- setScannedStaffNo(staffNo);
- return;
- }
-
- // 检查 equipmentCode 格式
- const equipmentCodeMatch = trimmedValue.match(/^(?:equipmentTypeSubTypeEquipmentNo|EquipmentType-SubType-EquipmentNo|equipmentCode):\s*(.+)$/i);
- if (equipmentCodeMatch) {
- const equipmentCode = equipmentCodeMatch[1].trim();
- setScannedEquipmentCode(equipmentCode);
- return;
- }
-
- // 其他格式处理(JSON、普通文本等)
- try {
- const qrData = JSON.parse(qrValue);
- if (qrData.staffNo) {
- setScannedStaffNo(String(qrData.staffNo));
- }
- if (qrData.equipmentTypeSubTypeEquipmentNo || qrData.equipmentCode) {
- setScannedEquipmentCode(
- String(qrData.equipmentTypeSubTypeEquipmentNo ?? qrData.equipmentCode)
- );
- }
- } catch {
- // 普通文本格式 - 尝试判断是 staffNo 还是 equipmentCode
- if (trimmedValue.length > 0) {
- if (trimmedValue.toUpperCase().startsWith("STAFF") || /^\d+$/.test(trimmedValue)) {
- // 可能是员工编号
- setScannedStaffNo(trimmedValue);
- } else if (trimmedValue.includes("-")) {
- // 可能包含 "-" 的是设备代码(如 "包裝機類-真空八爪魚機-1號")
- setScannedEquipmentCode(trimmedValue);
- }
- }
- }
- }, [lines]);
- // 处理 QR 码扫描效果
- useEffect(() => {
- if (isManualScanning && qrValues.length > 0 && scanningLineId) {
- const latestQr = qrValues[qrValues.length - 1];
-
- if (processedQrCodes.has(latestQr)) {
- return;
- }
-
- setProcessedQrCodes(prev => new Set(prev).add(latestQr));
- processQrCode(latestQr, scanningLineId);
- }
- }, [qrValues, isManualScanning, scanningLineId, processedQrCodes, processQrCode]);
-
-
- const submitScanAndStart = useCallback(async (lineId: number) => {
- console.log("submitScanAndStart called with:", {
- lineId,
- scannedStaffNo,
- // scannedEquipmentTypeSubTypeEquipmentNo,
- scannedEquipmentCode,
- });
-
- if (!scannedStaffNo) {
- console.log("No staffNo, cannot submit");
- setIsAutoSubmitting(false);
- return false;
- }
-
- try {
- const lineDetail = lineDetailForScan || await fetchProductProcessLineDetail(lineId);
-
- // ✅ 统一使用一个最终的 equipmentCode(优先用 scannedEquipmentCode,其次用 scannedEquipmentTypeSubTypeEquipmentNo)
- const effectiveEquipmentCode =
- scannedEquipmentCode ?? null;
-
-
- console.log("Submitting scan data with equipmentCode:", {
- productProcessLineId: lineId,
- staffNo: scannedStaffNo,
- equipmentCode: effectiveEquipmentCode,
- });
-
- const response = await newUpdateProductProcessLineQrscan({
- productProcessLineId: lineId,
- equipmentCode: effectiveEquipmentCode ?? "",
- staffNo: scannedStaffNo,
- });
-
- console.log("Scan submit response:", response);
-
- if (response && response.type === "error") {
- console.error("Scan validation failed:", response.message);
- alert(t(response.message) || t("Validation failed. Please check your input."));
- setIsAutoSubmitting(false);
- if (autoSubmitTimerRef.current) {
- clearTimeout(autoSubmitTimerRef.current);
- autoSubmitTimerRef.current = null;
- }
- return false;
- }
-
- console.log("Validation passed, starting line...");
- handleStopScan();
- setShowScanDialog(false);
- setIsAutoSubmitting(false);
-
- await handleStartLine(lineId);
- setSelectedLineId(lineId);
- setIsExecutingLine(true);
- await fetchProcessDetail();
-
- return true;
- } catch (error) {
- console.error("Error submitting scan:", error);
- alert("Failed to submit scan data. Please try again.");
- setIsAutoSubmitting(false);
- return false;
- }
- }, [
- scannedStaffNo,
- scannedEquipmentCode,
- lineDetailForScan,
- t,
- fetchProcessDetail,
- ]);
- const handleSubmitScanAndStart = useCallback(async (lineId: number) => {
- console.log("handleSubmitScanAndStart called with lineId:", lineId);
-
- if (!scannedStaffNo) {
- //alert(t("Please scan operator code first"));
- return;
- }
-
- // 如果正在自动提交,等待一下
- if (isAutoSubmitting) {
- console.log("Already auto-submitting, skipping manual submit");
- return;
- }
-
- await submitScanAndStart(lineId);
- }, [scannedOperatorId, isAutoSubmitting, submitScanAndStart, t]);
-
-
- // 开始扫描
- const handleStartScan = useCallback((lineId: number) => {
- if (autoSubmitTimerRef.current) {
- clearTimeout(autoSubmitTimerRef.current);
- autoSubmitTimerRef.current = null;
- }
- setScanningLineId(lineId);
- setIsManualScanning(true);
- setProcessedQrCodes(new Set());
- setScannedOperatorId(null);
- setScannedEquipmentId(null);
- setScannedStaffNo(null); // ✅ Add this
- setScannedEquipmentCode(null);
- setIsAutoSubmitting(false); // 添加:重置自动提交状态
- setLineDetailForScan(null);
- // 获取 line detail 以获取 bomProcessEquipmentId
- fetchProductProcessLineDetail(lineId)
- .then(setLineDetailForScan)
- .catch(err => {
- console.error("Failed to load line detail", err);
- // 不阻止扫描继续,line detail 不是必需的
- });
- startScan();
- }, [startScan]);
-
- // 停止扫描
- const handleStopScan = useCallback(() => {
- console.log("🛑 Stopping scan");
-
- // 清除定时器
- if (autoSubmitTimerRef.current) {
- clearTimeout(autoSubmitTimerRef.current);
- autoSubmitTimerRef.current = null;
- }
-
- setIsManualScanning(false);
- setIsAutoSubmitting(false);
- setScannedStaffNo(null); // ✅ Add this
- setScannedEquipmentCode(null);
- stopScan();
- resetScan();
- }, [stopScan, resetScan]);
-
- // 开始执行某个 line(原有逻辑,现在在验证通过后调用)
- const handleStartLine = async (lineId: number) => {
- try {
- await startProductProcessLine(lineId);
- } catch (error) {
- console.error("Error starting line:", error);
- //alert("Failed to start line. Please try again.");
- }
- };
- // 提交扫描结果并验证
- /*
- useEffect(() => {
- console.log("Auto-submit check:", {
- scanningLineId,
- scannedStaffNo,
- scannedEquipmentCode,
- isAutoSubmitting,
- isManualScanning,
- });
-
- // ✅ Update condition to check for either equipmentTypeSubTypeEquipmentNo OR equipmentDetailId
- if (
- scanningLineId &&
- scannedStaffNo !== null &&
- (scannedEquipmentCode !== null) &&
- !isAutoSubmitting &&
- isManualScanning
- ) {
- console.log("Auto-submitting triggered!");
- setIsAutoSubmitting(true);
-
- // 清除之前的定时器(如果有)
- if (autoSubmitTimerRef.current) {
- clearTimeout(autoSubmitTimerRef.current);
- }
-
- // 延迟一点时间,让用户看到两个都扫描完成了
- autoSubmitTimerRef.current = setTimeout(() => {
- console.log("Executing auto-submit...");
- submitScanAndStart(scanningLineId);
- autoSubmitTimerRef.current = null;
- }, 500);
- }
-
- // 清理函数:只在组件卸载或条件不再满足时清除定时器
- return () => {
- // 注意:这里不立即清除定时器,因为我们需要它执行
- // 只在组件卸载时清除
- };
- }, [scanningLineId, scannedStaffNo, scannedEquipmentCode, isAutoSubmitting, isManualScanning, submitScanAndStart]);
- */
- useEffect(() => {
- return () => {
- if (autoSubmitTimerRef.current) {
- clearTimeout(autoSubmitTimerRef.current);
- }
- };
- }, []);
-
- const handleStartLineWithScan = async (lineId: number) => {
- console.log("🚀 Starting line with scan for lineId:", lineId);
-
- // 确保状态完全重置
- setIsAutoSubmitting(false);
- setScannedOperatorId(null);
- setScannedEquipmentId(null);
- setProcessedQrCodes(new Set());
-
- setScannedStaffNo(null);
- setScannedEquipmentCode(null);
- setProcessedQrCodes(new Set());
- // 清除之前的定时器
- if (autoSubmitTimerRef.current) {
- clearTimeout(autoSubmitTimerRef.current);
- autoSubmitTimerRef.current = null;
- }
-
- setScanningLineId(lineId);
- setShowScanDialog(true);
- handleStartScan(lineId);
- };
- const selectedLine = lines.find(l => l.id === selectedLineId);
-
- if (loading) {
- return (
- <Box sx={{ display: 'flex', justifyContent: 'center', p: 3 }}>
- <CircularProgress/>
- </Box>
- );
- }
-
- return (
- <Box>
- {/*
- <Box sx={{ mb: 2 }}>
- <Button variant="outlined" onClick={onBack}>
- {t("Back to List")}
- </Button>
- </Box>
-
-
- <Paper sx={{ p: 3, mb: 3 }}>
- <Typography variant="h6" gutterBottom fontWeight="bold">
- {t("Production Process Information")}
- </Typography>
- <Stack spacing={2} direction="row" useFlexGap flexWrap="wrap">
- <Typography variant="subtitle1">
- <strong>{t("Job Order Code")}:</strong> {processData?.jobOrderCode}
- </Typography>
- <Typography variant="subtitle1">
- <strong>{t("Is Dark")}:</strong> {processData?.isDark}
- </Typography>
- <Typography variant="subtitle1">
- <strong>{t("Is Dense")}:</strong> {processData?.isDense}
- </Typography>
- <Typography variant="subtitle1">
- <strong>{t("Is Float")}:</strong> {processData?.isFloat}
- </Typography>
- <Typography variant="subtitle1">
- <strong>{t("Output Qty")}:</strong> {processData?.outputQty+" "+"("+processData?.outputQtyUom +")"}
- </Typography>
- <Box>
- <strong>{t("Status")}:</strong>{" "}
- <Chip
- label={
- processData?.status === 'completed' ? t("Completed") : processData?.status === 'IN_PROGRESS' ? t("In Progress") : processData?.status === 'pending' ? t("Pending") : t("Unknown")
- }
- color={processData?.status === 'completed' ? 'success' : processData?.status === 'IN_PROGRESS' ? 'success' : processData?.status === 'pending' ? 'primary' : 'error'}
- size="small"
- />
- </Box>
- <Typography variant="subtitle1">
- <strong>{t("Date")}:</strong> {dayjs(processData?.date).format(OUTPUT_DATE_FORMAT)}
- </Typography>
- <Typography variant="subtitle1">
- <strong>{t("Total Steps")}:</strong> {lines.length}
- </Typography>
- </Stack>
- </Paper>
- */}
- {/* ========== 第二部分:Process Lines ========== */}
- <Paper sx={{ p: 3 }}>
- <Typography variant="h6" gutterBottom fontWeight="bold">
- {t("Production Process Steps")}
- </Typography>
- <ProcessSummaryHeader processData={processData} />
- {!isExecutingLine ? (
- /* ========== 步骤列表视图 ========== */
- <TableContainer>
- <Table>
- <TableHead>
- <TableRow>
- <TableCell>{t("Seq")}</TableCell>
- <TableCell>{t("Step Name")}</TableCell>
- <TableCell>{t("Description")}</TableCell>
- <TableCell>{t("EquipmentType-EquipmentName-Code")}</TableCell>
- <TableCell>{t("Operator")}</TableCell>
- {/*}
- <TableCell>{t("Processing Time (mins)")}</TableCell>
- <TableCell>{t("Setup Time (mins)")}</TableCell>
- <TableCell>{t("Changeover Time (mins)")}</TableCell>
- */}
-
- <TableCell>
- <Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.5 }}>
- <Typography variant="subtitle2" sx={{ fontWeight: 600 }}>
- {t("Time Information(mins)")}
- </Typography>
- {/*
- <Typography variant="caption" sx={{ color: 'text.secondary' }}>
- {t("Processing Time")}-
- </Typography>
- <Typography variant="caption" sx={{ color: 'text.secondary' }}>
- {t("Setup Time")} - {t("Changeover Time")}
- </Typography>
- */}
- </Box>
- </TableCell>
- <TableCell align="center">{t("Status")}</TableCell>
-
- {!fromJosave&&(<TableCell align="center">{t("Action")}</TableCell>)}
- </TableRow>
- </TableHead>
- <TableBody>
- {lines.map((line) => {
- const status = (line as any).status || '';
- const statusLower = status.toLowerCase();
- const equipmentName = line.equipment_name || "-";
-
- const isCompleted = statusLower === 'completed';
- const isInProgress = statusLower === 'inprogress' || statusLower === 'in progress';
- const isPaused = statusLower === 'paused';
- const isPending = statusLower === 'pending' || status === '';
-
- return (
- <TableRow key={line.id}>
- <TableCell>{line.seqNo}</TableCell>
- <TableCell>
- <Typography fontWeight={500}>{line.name}</Typography>
- </TableCell>
- <TableCell><Typography fontWeight={500}>{line.description || "-"}</Typography></TableCell>
- <TableCell><Typography fontWeight={500}>{line.equipmentDetailCode||equipmentName}</Typography></TableCell>
- <TableCell><Typography fontWeight={500}>{line.operatorName}</Typography></TableCell>
- {/*
- <TableCell><Typography fontWeight={500}>{line.durationInMinutes} </Typography></TableCell>
- <TableCell><Typography fontWeight={500}>{line.prepTimeInMinutes} </Typography></TableCell>
- <TableCell><Typography fontWeight={500}>{line.postProdTimeInMinutes} </Typography></TableCell>
- */}
- <TableCell>
- <Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.5 }}>
- <Typography variant="body2" >
- {t("Processing Time")}: {line.durationInMinutes}{t("mins")}
- </Typography>
- <Typography variant="body2" >
- {t("Setup Time")}: {line.prepTimeInMinutes} {t("mins")}
- </Typography>
- <Typography variant="body2" >
-
- {t("Changeover Time")}: {line.postProdTimeInMinutes} {t("mins")}
- </Typography>
- </Box>
- </TableCell>
- <TableCell align="center">
- {isCompleted ? (
- <Chip label={t("Completed")} color="success" size="small"
- onClick={async () => {
- setSelectedLineId(line.id);
- setShowOutputPage(false); // 不显示输出页面
- setIsExecutingLine(true);
- await fetchProcessDetail();
- }}
- />
- ) : isInProgress ? (
- <Chip label={t("In Progress")} color="primary" size="small"
- onClick={async () => {
- setSelectedLineId(line.id);
- setShowOutputPage(false); // 不显示输出页面
- setIsExecutingLine(true);
- await fetchProcessDetail();
- }} />
- ) : isPending ? (
- <Chip label={t("Pending")} color="default" size="small" />
- ) : isPaused ? (
- <Chip label={t("Paused")} color="warning" size="small" />
- ) : (
- <Chip label={t("Unknown")} color="error" size="small" />
- )}
- </TableCell>
- {!fromJosave&&(
- <TableCell align="center">
- {statusLower === 'pending' ? (
- <Button
- variant="contained"
- size="small"
- startIcon={<PlayArrowIcon />}
- onClick={() => handleStartLineWithScan(line.id)}
- >
- {t("Start")}
- </Button>
- ) : statusLower === 'in_progress' || statusLower === 'in progress' || statusLower === 'paused' ? (
- <Button
- variant="contained"
- size="small"
- startIcon={<CheckCircleIcon />}
- onClick={async () => {
- setSelectedLineId(line.id);
- setIsExecutingLine(true);
- await fetchProcessDetail();
- }}
- >
- {t("View")}
- </Button>
- ) : (
- <Button
- variant="outlined"
- size="small"
- onClick={async() => {
- setSelectedLineId(line.id);
- setIsExecutingLine(true);
- await fetchProcessDetail();
- }}
- >
- {t("View")}
- </Button>
- )}
- </TableCell>
- )}
- </TableRow>
- );
- })}
- </TableBody>
- </Table>
- </TableContainer>
- ) : (
- /* ========== 步骤执行视图 ========== */
- <ProductionProcessStepExecution
- lineId={selectedLineId}
- onBack={handleBackFromStep}
- //onClose={() => {
- // setIsExecutingLine(false)
- // setSelectedLineId(null)
- //}}
- //onOutputSubmitted={async () => {
- // await fetchProcessDetail()
- //}}
- />
- )}
- </Paper>
-
- {/* QR 扫描对话框 */}
- <Dialog
- open={showScanDialog}
- onClose={() => {
- handleStopScan();
- setShowScanDialog(false);
- }}
- maxWidth="sm"
- fullWidth
- >
- <DialogTitle>{t("Scan Operator & Equipment")}</DialogTitle>
- <DialogContent>
- <Stack spacing={2} sx={{ mt: 2 }}>
- <Box>
- <Typography variant="body2" color="text.secondary">
- {scannedStaffNo
- ? `${t("Staff No")}: ${scannedStaffNo}`
- : t("Please scan staff no")
- }
- </Typography>
- </Box>
-
- <Box>
- <Typography variant="body2" color="text.secondary">
- {/* ✅ Show both options */}
- {scannedEquipmentCode
- ? `${t("Equipment Code")}: ${scannedEquipmentCode}`
- : t("Please scan equipment code")
- }
- </Typography>
- </Box>
-
- <Button
- variant={isManualScanning ? "outlined" : "contained"}
- startIcon={<QrCodeIcon />}
- onClick={isManualScanning ? handleStopScan : () => scanningLineId && handleStartScan(scanningLineId)}
- color={isManualScanning ? "secondary" : "primary"}
- fullWidth
- >
- {isManualScanning ? t("Stop QR Scan") : t("Start QR Scan")}
- </Button>
- </Stack>
- </DialogContent>
- <DialogActions>
- <Button onClick={() => {
- handleStopScan();
- setShowScanDialog(false);
- }}>
- {t("Cancel")}
- </Button>
- <Button
- variant="contained"
- onClick={() => scanningLineId && handleSubmitScanAndStart(scanningLineId)}
- disabled={!scannedStaffNo }
- >
- {t("Submit & Start")}
- </Button>
- </DialogActions>
- </Dialog>
- </Box>
- );
- };
-
- export default ProductionProcessDetail;
|