Bladeren bron

update truck X

reset-do-picking-order
CANCERYS\kw093 2 weken geleden
bovenliggende
commit
42cb203514
8 gewijzigde bestanden met toevoegingen van 323 en 71 verwijderingen
  1. +30
    -7
      src/app/api/pickOrder/actions.ts
  2. +149
    -3
      src/components/FinishedGoodSearch/FinishedGoodFloorLanePanel.tsx
  3. +32
    -1
      src/components/FinishedGoodSearch/FinishedGoodSearch.tsx
  4. +33
    -9
      src/components/FinishedGoodSearch/GoodPickExecution.tsx
  5. +34
    -8
      src/components/FinishedGoodSearch/ReleasedDoPickOrderSelectModal.tsx
  6. +1
    -1
      src/components/ProductionProcess/JobProcessStatus.tsx
  7. +42
    -42
      src/components/QrCodeScannerProvider/QrCodeScannerProvider.tsx
  8. +2
    -0
      src/i18n/zh/pickOrder.json

+ 30
- 7
src/app/api/pickOrder/actions.ts Bestand weergeven

@@ -443,6 +443,7 @@ export interface UpdatePickExecutionIssueRequest {
export interface StoreLaneSummary {
storeId: string;
rows: LaneRow[];
defaultTruckCount: number | null;
}

export interface LaneRow {
@@ -606,16 +607,22 @@ export const updatePickExecutionIssueStatus = async (
};
export async function fetchStoreLaneSummary(storeId: string, requiredDate?: string, releaseType?: string): Promise<StoreLaneSummary> {
const dateToUse = requiredDate || dayjs().format('YYYY-MM-DD');

const url = `${BASE_API_URL}/doPickOrder/summary-by-store?storeId=${encodeURIComponent(storeId)}&requiredDate=${encodeURIComponent(dateToUse)}&releaseType=${encodeURIComponent(releaseType || 'all')}`;
const response = await serverFetchJson<StoreLaneSummary>(
url,
{
const label = `[API] fetchStoreLaneSummary ${storeId}`;
console.time(label);
try {
const response = await serverFetchJson<StoreLaneSummary>(url, {
method: "GET",
cache: "no-store",
next: { revalidate: 0 }
}
);
return response;
next: { revalidate: 0 },
});
console.timeEnd(label);
return response;
} catch (error) {
console.error(`[API] Error in fetchStoreLaneSummary ${storeId}:`, error);
throw error;
}
}

// 按车道分配订单
@@ -1414,6 +1421,22 @@ export const fetchReleasedDoPickOrdersForSelection = async (
});
return response ?? [];
};
export const fetchReleasedDoPickOrdersForSelectionToday = async (
shopName?: string,
storeId?: string,
truck?: string
): Promise<ReleasedDoPickOrderListItem[]> => {
const params = new URLSearchParams();
if (shopName?.trim()) params.append("shopName", shopName.trim());
if (storeId?.trim()) params.append("storeId", storeId.trim());
if (truck?.trim()) params.append("truck", truck.trim());
const query = params.toString();
const url = `${BASE_API_URL}/doPickOrder/released-today${query ? `?${query}` : ""}`;
const response = await serverFetchJson<ReleasedDoPickOrderListItem[]>(url, {
method: "GET",
});
return response ?? [];
};
export const fetchReleasedDoPickOrderCountByStore = async (
storeId: string
): Promise<number> => {


+ 149
- 3
src/components/FinishedGoodSearch/FinishedGoodFloorLanePanel.tsx Bestand weergeven

@@ -1,7 +1,7 @@
"use client";

import { Box, Button, Grid, Stack, Typography, Select, MenuItem, FormControl, InputLabel ,Tooltip} from "@mui/material";
import { useCallback, useEffect, useState } from "react";
import { useCallback, useEffect, useState, useRef } from "react";
import { useTranslation } from "react-i18next";
import { useSession } from "next-auth/react";
import { SessionWithTokens } from "@/config/authConfig";
@@ -21,18 +21,48 @@ const FinishedGoodFloorLanePanel: React.FC<Props> = ({ onPickOrderAssigned, onSw
const currentUserId = session?.id ? parseInt(session.id) : undefined;
const [selectedStore, setSelectedStore] = useState<string>("2/F");
const [selectedTruck, setSelectedTruck] = useState<string>("");
const [selectedDefaultTruck, setSelectedDefaultTruck] = useState<string>("");
const [modalOpen, setModalOpen] = useState(false);
const [truckCounts2F, setTruckCounts2F] = useState<{ truck: string; count: number }[]>([]);
const [truckCounts4F, setTruckCounts4F] = useState<{ truck: string; count: number }[]>([]);
const [summary2F, setSummary2F] = useState<StoreLaneSummary | null>(null);
const [summary4F, setSummary4F] = useState<StoreLaneSummary | null>(null);
// 其他 state 旁邊加一組:
const hasLoggedRef = useRef(false);
const fullReadyLoggedRef = useRef(false);
const pendingRef = useRef(0);
const [defaultDateScope, setDefaultDateScope] = useState<"today" | "before">("today");
const [isLoadingSummary, setIsLoadingSummary] = useState(false);
const [isAssigning, setIsAssigning] = useState(false);
const [isDefaultTruck, setIsDefaultTruck] = useState(false);
//const [selectedDate, setSelectedDate] = useState<string>("today");
const defaultTruckCount = summary4F?.defaultTruckCount ?? 0;
const [beforeTodayTruckXCount, setBeforeTodayTruckXCount] = useState(0);

const [selectedDate, setSelectedDate] = useState<string>("today");
const [releaseType, setReleaseType] = useState<string>("batch");
const startFullTimer = () => {
if (typeof window === "undefined") return;
const key = "__FG_FLOOR_FULL_TIMER_STARTED__" as const;
if (!(window as any)[key]) {
(window as any)[key] = true;
console.time("[FG] FloorLanePanel full ready");
}
};
const tryEndFullTimer = () => {
if (typeof window === "undefined") return;
const key = "__FG_FLOOR_FULL_TIMER_STARTED__" as const;
if ((window as any)[key] && !fullReadyLoggedRef.current && pendingRef.current === 0) {
fullReadyLoggedRef.current = true;
console.timeEnd("[FG] FloorLanePanel full ready");
delete (window as any)[key];
}
};
const loadSummaries = useCallback(async () => {
setIsLoadingSummary(true);
pendingRef.current += 1;
startFullTimer();
try {
// Convert selectedDate to the format needed
let dateParam: string | undefined;
@@ -54,15 +84,33 @@ const FinishedGoodFloorLanePanel: React.FC<Props> = ({ onPickOrderAssigned, onSw
console.error("Error loading summaries:", error);
} finally {
setIsLoadingSummary(false);
// ⭐ 新增:this async 结束,pending--,尝试结束 full ready 计时
pendingRef.current -= 1;
tryEndFullTimer();
if (!hasLoggedRef.current) {
hasLoggedRef.current = true;
if (typeof window !== "undefined") {
const key = "__FG_FLOOR_PANEL_TIMER_STARTED__" as const;
if ((window as any)[key]) {
console.timeEnd("[FG] FloorLanePanel initial load");
delete (window as any)[key];
} else {
console.log("Timer '[FG] FloorLanePanel initial load' already ended or never started, skip.");
}
}
}
}
}, [selectedDate, releaseType]);

// 初始化
useEffect(() => {
loadSummaries();
}, [loadSummaries]);
useEffect(() => {
const loadCounts = async () => {
pendingRef.current += 1;
startFullTimer();
try {
const [list2F, list4F] = await Promise.all([
fetchReleasedDoPickOrdersForSelection(undefined, "2/F"),
@@ -84,10 +132,36 @@ const FinishedGoodFloorLanePanel: React.FC<Props> = ({ onPickOrderAssigned, onSw
console.error("Error loading counts:", e);
setTruckCounts2F([]);
setTruckCounts4F([]);
}finally {
// ⭐ 新增:结束时 pending--,尝试结束 full ready 计时
pendingRef.current -= 1;
tryEndFullTimer();
}
};
loadCounts();
}, [loadSummaries]);
useEffect(() => {
const loadBeforeTodayTruckX = async () => {
pendingRef.current += 1;
startFullTimer();
try {
const list = await fetchReleasedDoPickOrdersForSelection(
undefined, // shopName
undefined, // storeId: Truck X 的 store_id 是 null
"車線-X" // 只看 Truck X
);
setBeforeTodayTruckXCount(list.length);
} catch (e) {
console.error("Error loading beforeTodayTruckX:", e);
setBeforeTodayTruckXCount(0);
}finally {
// ⭐ 新增:结束时 pending--,尝试结束 full ready 计时
pendingRef.current -= 1;
tryEndFullTimer();
}
};
loadBeforeTodayTruckX();
}, []);
const handleAssignByLane = useCallback(async (
storeId: string,
truckDepartureTime: string,
@@ -299,7 +373,7 @@ const getDateLabel = (offset: number) => {


{/* Grid containing both floors */}
<Grid container spacing={2}>
<Grid container spacing={2}>
{/* 2/F 楼层面板 */}
<Grid item xs={12}>
<Stack direction="row" spacing={2} alignItems="flex-start">
@@ -499,6 +573,45 @@ const getDateLabel = (offset: number) => {
</Box>
</Stack>
</Grid>

{/* 4/F Today default lane*/}
<Grid item xs={12}>
<Stack direction="row" spacing={2} alignItems="flex-start">
<Typography sx={{ fontWeight: 600, minWidth: 60, pt: 1 }}>{t("Truck X")}</Typography>
<Box
sx={{
border: '1px solid #e0e0e0',
borderRadius: 1,
p: 1,
backgroundColor: '#fafafa',
flex: 1
}}
>
{defaultTruckCount === 0 ? (
<Typography >{t("No entries available")}</Typography>
) : (
<Button
variant="outlined"
size="medium"
onClick={() => {
// Truck X 綁 4/F,如果你要放在 4/F 區塊
setSelectedStore("");
// 真正的 Truck lane code:車線-X
setSelectedTruck("車線-X");
// 告訴 modal 這是 default truck 模式
setIsDefaultTruck(true);
// 打開 modal
setModalOpen(true);
setDefaultDateScope("today");
}}
>
{`${t("Truck X")} (${defaultTruckCount})`}
</Button>
)}
</Box>
</Stack>
</Grid>
{/* 2/F 未完成已放單 - 與上方相同 UI */}
<Grid item xs={12}>
<Box
@@ -652,10 +765,43 @@ const getDateLabel = (offset: number) => {
</Box>
</Stack>
</Grid>
<Grid item xs={12}>
<Stack direction="row" spacing={2}>
<Typography sx={{ fontWeight: 600, minWidth: 60, pt: 1 }}>{t("Truck X")} </Typography>
<Box
sx={{
border: '1px solid #e0e0e0',
borderRadius: 1,
p: 1,
backgroundColor: '#fafafa',
flex: 1
}}
>
{beforeTodayTruckXCount === 0 ? (
<Typography>{t("No entries available")}</Typography>
) : (
<Button
variant="outlined"
size="medium"
onClick={() => {
setSelectedStore("4/F"); // 或用專門標示 Truck X
setSelectedTruck("車線-X");
setIsDefaultTruck(true);
setDefaultDateScope("before"); // 類似上一輪說的 dateScope
setModalOpen(true);
}}
>
{`${t("Truck X")} (${beforeTodayTruckXCount})`}
</Button>
)}
</Box>
</Stack>
</Grid>
<ReleasedDoPickOrderSelectModal
open={modalOpen}
storeId={selectedStore}
truck={selectedTruck}
isDefaultTruck={isDefaultTruck}
onClose={() => setModalOpen(false)}
onAssigned={() => {
loadSummaries();


+ 32
- 1
src/components/FinishedGoodSearch/FinishedGoodSearch.tsx Bestand weergeven

@@ -82,8 +82,27 @@ const [selectedPrinterForDraft, setSelectedPrinterForDraft] = useState<PrinterCo
);

const [fgPickOrdersData, setFgPickOrdersData] = useState<FGPickOrderResponse[]>([]);
useEffect(() => {
if (typeof window === "undefined") return;
const key = "__FG_PAGE_READY_TIMER_STARTED__" as const;
if ((window as any)[key]) {
console.log("Timer '[FinishedGoodSearch] page ready' already started, skip.");
return;
}
(window as any)[key] = true;
console.time("[FinishedGoodSearch] page ready");
}, []);
const [releasedOrderCount, setReleasedOrderCount] = useState<number>(0);

useEffect(() => {
console.time("[FinishedGoodSearch] initial render");
return () => {
console.timeEnd("[FinishedGoodSearch] initial render");
};
}, []);
const fetchReleasedOrderCount = useCallback(async () => {
try {
const releasedOrders = await fetchReleasedDoPickOrders();
@@ -702,6 +721,18 @@ const handleAssignByLane = useCallback(async (
filterArgs={filterArgs}
onFgPickOrdersChange={setFgPickOrdersData}
onSwitchToDetailTab={handleSwitchToDetailTab}
onFirstLoadDone={() => {
if (typeof window === "undefined") return;
const key = "__FG_PAGE_READY_TIMER_STARTED__" as const;
// 只在计时器真的存在时才调用 timeEnd,避免 "does not exist"
if ((window as any)[key]) {
console.timeEnd("[FinishedGoodSearch] page ready");
delete (window as any)[key];
} else {
console.log("Timer '[FinishedGoodSearch] page ready' was already ended, skip.");
}
}}
/>
)}
{tabIndex === 1 && (


+ 33
- 9
src/components/FinishedGoodSearch/GoodPickExecution.tsx Bestand weergeven

@@ -59,6 +59,7 @@ interface Props {
filterArgs: Record<string, any>;
onFgPickOrdersChange?: (fgPickOrders: FGPickOrderResponse[]) => void;
onSwitchToDetailTab?: () => void;
onFirstLoadDone?: () => void;
}

// QR Code Modal Component (from LotTable)
@@ -72,7 +73,7 @@ const QrCodeModal: React.FC<{
const { t } = useTranslation("pickOrder");
const { values: qrValues, isScanning, startScan, stopScan, resetScan } = useQrCodeScannerContext();
const [manualInput, setManualInput] = useState<string>('');
const floorPanelTimerStartedRef = useRef(false);
const [manualInputSubmitted, setManualInputSubmitted] = useState<boolean>(false);
const [manualInputError, setManualInputError] = useState<boolean>(false);
const [isProcessingQr, setIsProcessingQr] = useState<boolean>(false);
@@ -315,7 +316,7 @@ const QrCodeModal: React.FC<{
);
};

const PickExecution: React.FC<Props> = ({ filterArgs, onFgPickOrdersChange, onSwitchToDetailTab }) => {
const PickExecution: React.FC<Props> = ({ filterArgs, onFgPickOrdersChange, onSwitchToDetailTab, onFirstLoadDone }) => {
const { t } = useTranslation("pickOrder");
const router = useRouter();
const { data: session } = useSession() as { data: SessionWithTokens | null };
@@ -353,7 +354,7 @@ const [pickOrderSwitching, setPickOrderSwitching] = useState(false);
// Add QR modal states
const [qrModalOpen, setQrModalOpen] = useState(false);
const [selectedLotForQr, setSelectedLotForQr] = useState<any | null>(null);
const floorPanelTimerStartedRef = useRef(false);
// Add GoodPickExecutionForm states
const [pickExecutionFormOpen, setPickExecutionFormOpen] = useState(false);
const [selectedLotForExecutionForm, setSelectedLotForExecutionForm] = useState<any | null>(null);
@@ -523,12 +524,18 @@ const fetchFgPickOrdersData = useCallback(async () => {
if (session && currentUserId && !initializationRef.current) {
console.log(" Session loaded, initializing pick order...");
initializationRef.current = true;
// Only fetch existing data, no auto-assignment
fetchAllCombinedLotData();
(async () => {
try {
await fetchAllCombinedLotData(); // ✅ 等待数据加载完成
} finally {
if (onFirstLoadDone) {
onFirstLoadDone(); // ✅ 这时候再结束 [FinishedGoodSearch] page ready
}
}
})();
}
}, [session, currentUserId, fetchAllCombinedLotData]);

}, [session, currentUserId, fetchAllCombinedLotData, onFirstLoadDone]);
// Add event listener for manual assignment
useEffect(() => {
const handlePickOrderAssigned = () => {
@@ -823,7 +830,24 @@ const fetchFgPickOrdersData = useCallback(async () => {
}
}, [qrValues, combinedLotData, handleQrCodeSubmit]);


useEffect(() => {
if (typeof window === "undefined") return;
const key = "__FG_FLOOR_PANEL_TIMER_STARTED__" as const;
// 只有当「没有 FG 订单」时才会显示 FinishedGoodFloorLanePanel
if (fgPickOrders.length === 0 && !fgPickOrdersLoading) {
if (!(window as any)[key]) {
(window as any)[key] = true;
console.time("[FG] FloorLanePanel initial load");
}
}
// 如果之后拿到 FG 订单,你可以选择在这里清掉标记(可选)
if (fgPickOrders.length > 0 && (window as any)[key]) {
// delete (window as any)[key];
}
}, [fgPickOrders.length, fgPickOrdersLoading]);
const handlePickQtyChange = useCallback((lotKey: string, value: number | string) => {
if (value === '' || value === null || value === undefined) {
setPickQtyData(prev => ({


+ 34
- 8
src/components/FinishedGoodSearch/ReleasedDoPickOrderSelectModal.tsx Bestand weergeven

@@ -22,18 +22,21 @@ import {
fetchReleasedDoPickOrdersForSelection,
assignByDoPickOrderId,
type ReleasedDoPickOrderListItem,
fetchReleasedDoPickOrdersForSelectionToday,
} from "@/app/api/pickOrder/actions";
import { useSession } from "next-auth/react";
import { SessionWithTokens } from "@/config/authConfig";
import Swal from "sweetalert2";
import dayjs from "dayjs";
type DateScope = "today" | "before";
interface Props {
open: boolean;
onClose: () => void;
onAssigned: () => void;
storeId: string;
truck: string;
isDefaultTruck: boolean;
defaultDateScope?: "today" | "before";
}

const ReleasedDoPickOrderSelectModal: React.FC<Props> = ({
@@ -42,11 +45,12 @@ const ReleasedDoPickOrderSelectModal: React.FC<Props> = ({
onAssigned,
storeId,
truck,
isDefaultTruck,
}) => {
const { t } = useTranslation("pickOrder");
const { data: session } = useSession() as { data: SessionWithTokens | null };
const currentUserId = session?.id ? parseInt(session.id) : undefined;
const [defaultDateScope, setDefaultDateScope] = useState<"today" | "before">("today");
const [list, setList] = useState<ReleasedDoPickOrderListItem[]>([]);
const [loading, setLoading] = useState(false);
const [shopSearch, setShopSearch] = useState("");
@@ -56,11 +60,33 @@ const ReleasedDoPickOrderSelectModal: React.FC<Props> = ({
if (!open) return;
setLoading(true);
try {
const data = await fetchReleasedDoPickOrdersForSelection(
shopSearch.trim() || undefined,
storeId,
truck?.trim() || undefined // 傳入 truck
);
let data: ReleasedDoPickOrderListItem[]; // ⭐ 先宣告
if (isDefaultTruck) {
if (defaultDateScope === "today") {
// Truck X 今天
data = await fetchReleasedDoPickOrdersForSelectionToday(
undefined, // shopName
undefined, // storeId
"車線-X" // truck
);
} else {
// Truck X 以前(/released,server 內是 < today)
data = await fetchReleasedDoPickOrdersForSelection(
undefined,
undefined,
"車線-X"
);
}
} else {
// 一般車道
data = await fetchReleasedDoPickOrdersForSelection(
shopSearch.trim() || undefined,
storeId,
truck?.trim() || undefined
);
}
setList(data);
} catch (e) {
console.error(e);
@@ -68,7 +94,7 @@ const ReleasedDoPickOrderSelectModal: React.FC<Props> = ({
} finally {
setLoading(false);
}
}, [open, shopSearch, storeId, truck]);
}, [open, shopSearch, storeId, truck, isDefaultTruck, defaultDateScope]);

useEffect(() => {
loadList();


+ 1
- 1
src/components/ProductionProcess/JobProcessStatus.tsx Bestand weergeven

@@ -213,7 +213,7 @@ const [lastDataRefreshTime, setLastDataRefreshTime] = useState<dayjs.Dayjs | nul
sx={{
border: '3px solid #135fed',
overflowX: 'auto',
maxHeight: 440,
maxHeight: 540,
overflow: 'auto'
}}
>


+ 42
- 42
src/components/QrCodeScannerProvider/QrCodeScannerProvider.tsx Bestand weergeven

@@ -57,7 +57,7 @@ const QrCodeScannerProvider: React.FC<QrCodeScannerProviderProps> = ({
setScanResult(undefined);
resetScannerInput();

console.log("%c Scanner Reset", "color:cyan");
//console.log("%c Scanner Reset", "color:cyan");
if (error.length > 0) {
console.log("%c Error:", "color:red", error);
@@ -68,25 +68,25 @@ const QrCodeScannerProvider: React.FC<QrCodeScannerProviderProps> = ({

const startQrCodeScanner = useCallback(() => {
const startTime = performance.now();
console.log(`⏱️ [SCANNER START] Called at: ${new Date().toISOString()}`);
//console.log(`⏱️ [SCANNER START] Called at: ${new Date().toISOString()}`);
resetQrCodeScanner();
const resetTime = performance.now() - startTime;
console.log(`⏱️ [SCANNER START] Reset time: ${resetTime.toFixed(2)}ms`);
//console.log(`⏱️ [SCANNER START] Reset time: ${resetTime.toFixed(2)}ms`);
setIsScanning(() => true);
const setScanningTime = performance.now() - startTime;
console.log(`⏱️ [SCANNER START] setScanning time: ${setScanningTime.toFixed(2)}ms`);
//console.log(`⏱️ [SCANNER START] setScanning time: ${setScanningTime.toFixed(2)}ms`);
const totalTime = performance.now() - startTime;
console.log(`%c Scanning started `, "color:cyan");
console.log(`⏱️ [SCANNER START] Total start time: ${totalTime.toFixed(2)}ms`);
console.log(`⏰ [SCANNER START] Scanner started at: ${new Date().toISOString()}`);
//console.log(`%c Scanning started `, "color:cyan");
//console.log(`⏱️ [SCANNER START] Total start time: ${totalTime.toFixed(2)}ms`);
//console.log(`⏰ [SCANNER START] Scanner started at: ${new Date().toISOString()}`);
}, [resetQrCodeScanner]);

const endQrCodeScanner = useCallback(() => {
setIsScanning(() => false);
console.log("%c Scanning stopped ", "color:cyan");
//console.log("%c Scanning stopped ", "color:cyan");
}, []);

// Find by rough match, return 0 if not found
@@ -127,12 +127,12 @@ const QrCodeScannerProvider: React.FC<QrCodeScannerProviderProps> = ({

useEffect(() => {
const effectStartTime = performance.now();
console.log(`⏱️ [KEYBOARD LISTENER EFFECT] Triggered at: ${new Date().toISOString()}`);
console.log(`⏱️ [KEYBOARD LISTENER EFFECT] isScanning: ${isScanning}`);
//console.log(`⏱️ [KEYBOARD LISTENER EFFECT] Triggered at: ${new Date().toISOString()}`);
//console.log(`⏱️ [KEYBOARD LISTENER EFFECT] isScanning: ${isScanning}`);
if (isScanning) {
const listenerRegisterStartTime = performance.now();
console.log(`⏱️ [KEYBOARD LISTENER] Registering keyboard listener at: ${new Date().toISOString()}`);
//console.log(`⏱️ [KEYBOARD LISTENER] Registering keyboard listener at: ${new Date().toISOString()}`);
// Reset refs when starting scan
keysRef.current = [];
@@ -147,9 +147,9 @@ const QrCodeScannerProvider: React.FC<QrCodeScannerProviderProps> = ({
// ✅ OPTIMIZED: Use refs to accumulate keys immediately (no state update delay)
if (event.key.length === 1) {
if (isFirstKeyRef.current) {
console.log(`⏱️ [KEYBOARD] First key press detected: "${event.key}"`);
console.log(`⏰ [KEYBOARD] First key press at: ${keyPressTimestamp}`);
console.log(`⏱️ [KEYBOARD] Time since listener registered: ${(keyPressTime - listenerRegisterStartTime).toFixed(2)}ms`);
//console.log(`⏱️ [KEYBOARD] First key press detected: "${event.key}"`);
//console.log(`⏰ [KEYBOARD] First key press at: ${keyPressTimestamp}`);
//console.log(`⏱️ [KEYBOARD] Time since listener registered: ${(keyPressTime - listenerRegisterStartTime).toFixed(2)}ms`);
isFirstKeyRef.current = false;
}
keysRef.current.push(event.key);
@@ -157,20 +157,20 @@ const QrCodeScannerProvider: React.FC<QrCodeScannerProviderProps> = ({
if (event.key === "{") {
const braceTime = performance.now();
console.log(`⏱️ [KEYBOARD] Left brace "{" detected at: ${new Date().toISOString()}`);
console.log(`⏱️ [KEYBOARD] Time since listener registered: ${(braceTime - listenerRegisterStartTime).toFixed(2)}ms`);
//console.log(`⏱️ [KEYBOARD] Left brace "{" detected at: ${new Date().toISOString()}`);
//console.log(`⏱️ [KEYBOARD] Time since listener registered: ${(braceTime - listenerRegisterStartTime).toFixed(2)}ms`);
leftBraceCountRef.current += 1;
} else if (event.key === "}") {
const braceTime = performance.now();
console.log(`⏱️ [KEYBOARD] Right brace "}" detected at: ${new Date().toISOString()}`);
console.log(`⏱️ [KEYBOARD] Time since listener registered: ${(braceTime - listenerRegisterStartTime).toFixed(2)}ms`);
//console.log(`⏱️ [KEYBOARD] Right brace "}" detected at: ${new Date().toISOString()}`);
//console.log(`⏱️ [KEYBOARD] Time since listener registered: ${(braceTime - listenerRegisterStartTime).toFixed(2)}ms`);
rightBraceCountRef.current += 1;
// ✅ OPTIMIZED: Check for complete QR immediately and update state only once
if (leftBraceCountRef.current === rightBraceCountRef.current && leftBraceCountRef.current > 0) {
const completeTime = performance.now();
console.log(`⏱️ [KEYBOARD] Complete QR detected immediately! Time: ${completeTime.toFixed(2)}ms`);
console.log(`⏰ [KEYBOARD] Complete QR at: ${new Date().toISOString()}`);
//console.log(`⏱️ [KEYBOARD] Complete QR detected immediately! Time: ${completeTime.toFixed(2)}ms`);
//console.log(`⏰ [KEYBOARD] Complete QR at: ${new Date().toISOString()}`);
const qrValue = keysRef.current.join("").substring(
keysRef.current.indexOf("{"),
@@ -223,26 +223,26 @@ const QrCodeScannerProvider: React.FC<QrCodeScannerProviderProps> = ({
document.addEventListener("keydown", handleKeyDown);
const listenerRegisterTime = performance.now() - listenerRegisterStartTime;
console.log(`⏱️ [KEYBOARD LISTENER] Listener registered in: ${listenerRegisterTime.toFixed(2)}ms`);
console.log(`⏰ [KEYBOARD LISTENER] Listener ready at: ${new Date().toISOString()}`);
//console.log(`⏱️ [KEYBOARD LISTENER] Listener registered in: ${listenerRegisterTime.toFixed(2)}ms`);
//console.log(`⏰ [KEYBOARD LISTENER] Listener ready at: ${new Date().toISOString()}`);
return () => {
console.log(`⏱️ [KEYBOARD LISTENER] Removing keyboard listener at: ${new Date().toISOString()}`);
// console.log(`⏱️ [KEYBOARD LISTENER] Removing keyboard listener at: ${new Date().toISOString()}`);
document.removeEventListener("keydown", handleKeyDown);
};
} else {
console.log(`⏱️ [KEYBOARD LISTENER EFFECT] Scanner not active, skipping listener registration`);
//console.log(`⏱️ [KEYBOARD LISTENER EFFECT] Scanner not active, skipping listener registration`);
}
const effectTime = performance.now() - effectStartTime;
console.log(`⏱️ [KEYBOARD LISTENER EFFECT] Total effect time: ${effectTime.toFixed(2)}ms`);
//console.log(`⏱️ [KEYBOARD LISTENER EFFECT] Total effect time: ${effectTime.toFixed(2)}ms`);
}, [isScanning]);

// ✅ OPTIMIZED: Simplify the QR scanner effect - it's now mainly for initial detection
useEffect(() => {
const effectStartTime = performance.now();
console.log(`⏱️ [QR SCANNER EFFECT] Triggered at: ${new Date().toISOString()}`);
console.log(`⏱️ [QR SCANNER EFFECT] Keys count: ${keys.length}, leftBrace: ${leftCurlyBraceCount}, rightBrace: ${rightCurlyBraceCount}`);
//console.log(`⏱️ [QR SCANNER EFFECT] Triggered at: ${new Date().toISOString()}`);
//console.log(`⏱️ [QR SCANNER EFFECT] Keys count: ${keys.length}, leftBrace: ${leftCurlyBraceCount}, rightBrace: ${rightCurlyBraceCount}`);
if (rightCurlyBraceCount > leftCurlyBraceCount || leftCurlyBraceCount > 1) { // Prevent multiple scan
setScanState("retry");
@@ -254,9 +254,9 @@ useEffect(() => {
{
const scanDetectedTime = performance.now();
setScanState("scanning");
console.log(`%c Scan detected, waiting for inputs...`, "color:cyan");
console.log(`⏱️ [QR SCANNER] Scan detected time: ${scanDetectedTime.toFixed(2)}ms`);
console.log(`⏰ [QR SCANNER] Scan detected at: ${new Date().toISOString()}`);
// console.log(`%c Scan detected, waiting for inputs...`, "color:cyan");
//console.log(`⏱️ [QR SCANNER] Scan detected time: ${scanDetectedTime.toFixed(2)}ms`);
//console.log(`⏰ [QR SCANNER] Scan detected at: ${new Date().toISOString()}`);
}
// Note: Complete QR detection is now handled directly in handleKeyDown
// This effect is mainly for UI feedback and error handling
@@ -266,13 +266,13 @@ useEffect(() => {
useEffect(() => {
if (qrCodeScannerValues.length > 0) {
const processStartTime = performance.now();
console.log(`⏱️ [QR SCANNER PROCESS] Processing qrCodeScannerValues at: ${new Date().toISOString()}`);
console.log(`⏱️ [QR SCANNER PROCESS] Values count: ${qrCodeScannerValues.length}`);
// console.log(`⏱️ [QR SCANNER PROCESS] Processing qrCodeScannerValues at: ${new Date().toISOString()}`);
//console.log(`⏱️ [QR SCANNER PROCESS] Values count: ${qrCodeScannerValues.length}`);
const scannedValues = qrCodeScannerValues[0];
console.log(`%c Scanned Result: `, "color:cyan", scannedValues);
console.log(`⏱️ [QR SCANNER PROCESS] Scanned value: ${scannedValues}`);
console.log(`⏰ [QR SCANNER PROCESS] Processing at: ${new Date().toISOString()}`);
//console.log(`%c Scanned Result: `, "color:cyan", scannedValues);
//console.log(`⏱️ [QR SCANNER PROCESS] Scanned value: ${scannedValues}`);
//console.log(`⏰ [QR SCANNER PROCESS] Processing at: ${new Date().toISOString()}`);
if (scannedValues.substring(0, 8) == "{2fitest") { // DEBUGGING
// 先检查是否是 {2fiteste...} 或 {2fitestu...} 格式
@@ -310,7 +310,7 @@ useEffect(() => {
}
setScanResult(debugValue);
const processTime = performance.now() - processStartTime;
console.log(`⏱️ [QR SCANNER PROCESS] Non-numeric processing time: ${processTime.toFixed(2)}ms`);
// console.log(`⏱️ [QR SCANNER PROCESS] Non-numeric processing time: ${processTime.toFixed(2)}ms`);
return;
}
}
@@ -319,8 +319,8 @@ useEffect(() => {
const parseStartTime = performance.now();
const data: QrCodeInfo = JSON.parse(scannedValues);
const parseTime = performance.now() - parseStartTime;
console.log(`%c Parsed scan data`, "color:green", data);
console.log(`⏱️ [QR SCANNER PROCESS] JSON parse time: ${parseTime.toFixed(2)}ms`);
// console.log(`%c Parsed scan data`, "color:green", data);
//console.log(`⏱️ [QR SCANNER PROCESS] JSON parse time: ${parseTime.toFixed(2)}ms`);
const content = scannedValues.substring(1, scannedValues.length - 1);
data.value = content;
@@ -328,14 +328,14 @@ useEffect(() => {
const setResultStartTime = performance.now();
setScanResult(data);
const setResultTime = performance.now() - setResultStartTime;
console.log(`⏱️ [QR SCANNER PROCESS] setScanResult time: ${setResultTime.toFixed(2)}ms`);
console.log(`⏰ [QR SCANNER PROCESS] setScanResult at: ${new Date().toISOString()}`);
// console.log(`⏱️ [QR SCANNER PROCESS] setScanResult time: ${setResultTime.toFixed(2)}ms`);
//console.log(`⏰ [QR SCANNER PROCESS] setScanResult at: ${new Date().toISOString()}`);
const processTime = performance.now() - processStartTime;
console.log(`⏱️ [QR SCANNER PROCESS] Total processing time: ${processTime.toFixed(2)}ms`);
// console.log(`⏱️ [QR SCANNER PROCESS] Total processing time: ${processTime.toFixed(2)}ms`);
} catch (error) { // Rough match for other scanner input -- Pending Review
console.log(`⏱️ [QR SCANNER PROCESS] JSON parse failed, trying rough match`);
//console.log(`⏱️ [QR SCANNER PROCESS] JSON parse failed, trying rough match`);
const silId = findIdByRoughMatch(scannedValues, "StockInLine").number ?? 0;
if (silId == 0) {


+ 2
- 0
src/i18n/zh/pickOrder.json Bestand weergeven

@@ -39,6 +39,8 @@
"Lines Per Pick Order": "每提料單行數",
"Pick Orders Details": "提料單詳情",
"Lines": "行數",
"Before Today": "以前",
"Truck X": "車線-X",
"Finsihed good items": "成品項目",
"kinds": "款",
"Completed Date": "完成日期",


Laden…
Annuleren
Opslaan