|
|
|
@@ -32,7 +32,7 @@ import PickExecutionDetail from "./GoodPickExecutiondetail"; |
|
|
|
import GoodPickExecutionRecord from "./GoodPickExecutionRecord"; |
|
|
|
import Swal from "sweetalert2"; |
|
|
|
import { printDN, printDNLabels } from "@/app/api/do/actions"; |
|
|
|
import { FGPickOrderResponse } from "@/app/api/pickOrder/actions"; |
|
|
|
import { FGPickOrderResponse, fetchStoreLaneSummary, assignByLane,type StoreLaneSummary } from "@/app/api/pickOrder/actions"; |
|
|
|
import FGPickOrderCard from "./FGPickOrderCard"; |
|
|
|
|
|
|
|
interface Props { |
|
|
|
@@ -59,6 +59,9 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => { |
|
|
|
const [tabIndex, setTabIndex] = useState(0); |
|
|
|
const [totalCount, setTotalCount] = useState<number>(); |
|
|
|
const [isAssigning, setIsAssigning] = useState(false); |
|
|
|
const [summary2F, setSummary2F] = useState<StoreLaneSummary | null>(null); |
|
|
|
const [summary4F, setSummary4F] = useState<StoreLaneSummary | null>(null); |
|
|
|
const [isLoadingSummary, setIsLoadingSummary] = useState(false); |
|
|
|
const [hideCompletedUntilNext, setHideCompletedUntilNext] = useState<boolean>( |
|
|
|
typeof window !== 'undefined' && localStorage.getItem('hideCompletedUntilNext') === 'true' |
|
|
|
); |
|
|
|
@@ -76,7 +79,27 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => { |
|
|
|
setReleasedOrderCount(0); |
|
|
|
} |
|
|
|
}, []); |
|
|
|
const loadSummaries = useCallback(async () => { |
|
|
|
setIsLoadingSummary(true); |
|
|
|
try { |
|
|
|
const [s2, s4] = await Promise.all([ |
|
|
|
fetchStoreLaneSummary("2/F"), |
|
|
|
fetchStoreLaneSummary("4/F") |
|
|
|
]); |
|
|
|
setSummary2F(s2); |
|
|
|
setSummary4F(s4); |
|
|
|
} catch (error) { |
|
|
|
console.error("Error loading summaries:", error); |
|
|
|
} finally { |
|
|
|
setIsLoadingSummary(false); |
|
|
|
} |
|
|
|
}, []); |
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
loadSummaries(); |
|
|
|
// 每30秒刷新一次 |
|
|
|
|
|
|
|
}, [loadSummaries]); |
|
|
|
const handleDraft = useCallback(async () =>{ |
|
|
|
try{ |
|
|
|
if (fgPickOrdersData.length === 0) { |
|
|
|
@@ -419,10 +442,11 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => { |
|
|
|
const onAssigned = () => { |
|
|
|
localStorage.removeItem('hideCompletedUntilNext'); |
|
|
|
setHideCompletedUntilNext(false); |
|
|
|
loadSummaries(); |
|
|
|
}; |
|
|
|
window.addEventListener('pickOrderAssigned', onAssigned); |
|
|
|
return () => window.removeEventListener('pickOrderAssigned', onAssigned); |
|
|
|
}, []); |
|
|
|
}, [loadSummaries]); |
|
|
|
// ... existing code ... |
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
@@ -453,62 +477,57 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => { |
|
|
|
}, [tabIndex]); |
|
|
|
|
|
|
|
// ... existing code ... |
|
|
|
const handleAssignByStore = async (storeId: "2/F" | "4/F") => { |
|
|
|
if (!currentUserId) { |
|
|
|
console.error("Missing user id in session"); |
|
|
|
return; |
|
|
|
} |
|
|
|
const handleAssignByLane = useCallback(async ( |
|
|
|
storeId: string, |
|
|
|
truckDepartureTime: string, |
|
|
|
truckLanceCode: string |
|
|
|
) => { |
|
|
|
if (!currentUserId) { |
|
|
|
console.error("Missing user id in session"); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
setIsAssigning(true); |
|
|
|
try { |
|
|
|
const res = await assignByLane(currentUserId, storeId, truckLanceCode, truckDepartureTime); |
|
|
|
|
|
|
|
setIsAssigning(true); |
|
|
|
try { |
|
|
|
const res = await autoAssignAndReleasePickOrderByStore(currentUserId, storeId); |
|
|
|
console.log("Assign by store result:", res); |
|
|
|
|
|
|
|
// ✅ Handle different response codes |
|
|
|
if (res.code === "SUCCESS") { |
|
|
|
console.log("✅ Successfully assigned pick order to store", storeId); |
|
|
|
// ✅ Trigger refresh to show newly assigned data |
|
|
|
window.dispatchEvent(new CustomEvent('pickOrderAssigned')); |
|
|
|
} else if (res.code === "USER_BUSY") { |
|
|
|
console.warn("⚠️ User already has pick orders in progress:", res.message); |
|
|
|
Swal.fire({ |
|
|
|
icon: "warning", |
|
|
|
title: t("Warning"), |
|
|
|
text: t("You already have a pick order in progess. Please complete it first before taking next pick order."), |
|
|
|
confirmButtonText: t("Confirm"), |
|
|
|
confirmButtonColor: "#8dba00" |
|
|
|
}); |
|
|
|
// ✅ Show warning but still refresh to show existing orders |
|
|
|
//alert(`Warning: ${res.message}`); |
|
|
|
window.dispatchEvent(new CustomEvent('pickOrderAssigned')); |
|
|
|
} else if (res.code === "NO_ORDERS") { |
|
|
|
console.log("ℹ️ No available pick orders for store", storeId); |
|
|
|
Swal.fire({ |
|
|
|
icon: "info", |
|
|
|
title: t("Info"), |
|
|
|
text: t("No available pick order(s) for this floor."), |
|
|
|
confirmButtonText: t("Confirm"), |
|
|
|
confirmButtonColor: "#8dba00" |
|
|
|
}); |
|
|
|
//alert(`Info: ${res.message}`); |
|
|
|
} else { |
|
|
|
console.log("ℹ️ Assignment result:", res.message); |
|
|
|
alert(`Info: ${res.message}`); |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error("❌ Error assigning by store:", error); |
|
|
|
if (res.code === "SUCCESS") { |
|
|
|
console.log("✅ Successfully assigned pick order from lane", truckLanceCode); |
|
|
|
window.dispatchEvent(new CustomEvent('pickOrderAssigned')); |
|
|
|
loadSummaries(); // 刷新按钮状态 |
|
|
|
} else if (res.code === "USER_BUSY") { |
|
|
|
Swal.fire({ |
|
|
|
icon: "error", |
|
|
|
title: t("Error"), |
|
|
|
text: t("Error occurred during assignment."), |
|
|
|
confirmButtonText: t("Confirm"), |
|
|
|
confirmButtonColor: "#8dba00" |
|
|
|
}); |
|
|
|
//alert("Error occurred during assignment"); |
|
|
|
} finally { |
|
|
|
setIsAssigning(false); |
|
|
|
icon: "warning", |
|
|
|
title: t("Warning"), |
|
|
|
text: t("You already have a pick order in progess. Please complete it first before taking next pick order."), |
|
|
|
confirmButtonText: t("Confirm"), |
|
|
|
confirmButtonColor: "#8dba00" |
|
|
|
}); |
|
|
|
window.dispatchEvent(new CustomEvent('pickOrderAssigned')); |
|
|
|
} else if (res.code === "NO_ORDERS") { |
|
|
|
Swal.fire({ |
|
|
|
icon: "info", |
|
|
|
title: t("Info"), |
|
|
|
text: t("No available pick order(s) for this lane."), |
|
|
|
confirmButtonText: t("Confirm"), |
|
|
|
confirmButtonColor: "#8dba00" |
|
|
|
}); |
|
|
|
} else { |
|
|
|
console.log("ℹ️ Assignment result:", res.message); |
|
|
|
} |
|
|
|
}; |
|
|
|
} catch (error) { |
|
|
|
console.error("❌ Error assigning by lane:", error); |
|
|
|
Swal.fire({ |
|
|
|
icon: "error", |
|
|
|
title: t("Error"), |
|
|
|
text: t("Error occurred during assignment."), |
|
|
|
confirmButtonText: t("Confirm"), |
|
|
|
confirmButtonColor: "#8dba00" |
|
|
|
}); |
|
|
|
} finally { |
|
|
|
setIsAssigning(false); |
|
|
|
} |
|
|
|
}, [currentUserId, t, loadSummaries]); |
|
|
|
// ✅ Manual assignment handler - uses the action function |
|
|
|
|
|
|
|
const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>( |
|
|
|
@@ -711,6 +730,7 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => { |
|
|
|
height: '100vh', // Full viewport height |
|
|
|
overflow: 'auto' // Single scrollbar for the whole page |
|
|
|
}}> |
|
|
|
|
|
|
|
{/* Header section */} |
|
|
|
<Box sx={{ p: 2, borderBottom: '1px solid #e0e0e0' }}> |
|
|
|
<Stack rowGap={2}> |
|
|
|
@@ -724,74 +744,205 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => { |
|
|
|
</Grid> |
|
|
|
|
|
|
|
{/* Last 2 buttons aligned right */} |
|
|
|
<Grid item xs={6} > |
|
|
|
<Stack direction="row" spacing={1}> |
|
|
|
<Button |
|
|
|
variant="contained" |
|
|
|
onClick={() => handleAssignByStore("2/F")} |
|
|
|
disabled={isAssigning} |
|
|
|
> |
|
|
|
{isAssigning ? t("Assigning pick order...") : t("Pick Execution 2/F")} |
|
|
|
</Button> |
|
|
|
<Button |
|
|
|
variant="contained" |
|
|
|
onClick={() => handleAssignByStore("4/F")} |
|
|
|
disabled={isAssigning} |
|
|
|
> |
|
|
|
{isAssigning ? t("Assigning pick order...") : t("Pick Execution 4/F")} |
|
|
|
</Button> |
|
|
|
</Stack> |
|
|
|
</Grid> |
|
|
|
<Grid item xs={12}> |
|
|
|
<Grid container alignItems="flex-start" spacing={1}> |
|
|
|
{/* 2/F 楼层面板 */} |
|
|
|
<Grid item> |
|
|
|
<Box |
|
|
|
sx={{ |
|
|
|
border: '1px solid #e0e0e0', |
|
|
|
borderRadius: 1, |
|
|
|
p: 1, |
|
|
|
minWidth: 320, |
|
|
|
mr: 1, |
|
|
|
backgroundColor: '#fafafa' |
|
|
|
}} |
|
|
|
> |
|
|
|
<Typography variant="subtitle2" sx={{ mb: 0.5, fontWeight: 600, textAlign: 'center' }}> |
|
|
|
2/F |
|
|
|
</Typography> |
|
|
|
{isLoadingSummary ? ( |
|
|
|
<Typography variant="caption">Loading...</Typography> |
|
|
|
) : ( |
|
|
|
<Stack spacing={0.5}> |
|
|
|
{summary2F?.rows.map((row, rowIdx) => ( |
|
|
|
<Box |
|
|
|
key={rowIdx} |
|
|
|
sx={{ |
|
|
|
border: '1px solid #e0e0e0', |
|
|
|
borderRadius: 0.5, |
|
|
|
p: 0.5, |
|
|
|
backgroundColor: '#fff' |
|
|
|
}} |
|
|
|
> |
|
|
|
<Typography |
|
|
|
variant="caption" |
|
|
|
sx={{ |
|
|
|
display: 'block', |
|
|
|
mb: 0.5, |
|
|
|
fontWeight: 500, |
|
|
|
textAlign: 'center', |
|
|
|
fontSize: '0.7rem' |
|
|
|
}} |
|
|
|
> |
|
|
|
{row.truckDepartureTime} |
|
|
|
</Typography> |
|
|
|
<Stack direction="row" spacing={0.25} alignItems="center" justifyContent="center"> |
|
|
|
{row.lanes.map((lane, laneIdx) => ( |
|
|
|
<Button |
|
|
|
key={laneIdx} |
|
|
|
variant="outlined" |
|
|
|
size="small" |
|
|
|
disabled={lane.unassigned === 0 || isAssigning} |
|
|
|
onClick={() => handleAssignByLane("2/F", row.truckDepartureTime, lane.truckLanceCode)} |
|
|
|
sx={{ |
|
|
|
minWidth: 80, |
|
|
|
fontSize: '0.7rem', |
|
|
|
py: 0.25, |
|
|
|
px: 0.5, |
|
|
|
borderWidth: 1, |
|
|
|
borderColor: '#ccc', |
|
|
|
'&:hover': { |
|
|
|
borderColor: '#999', |
|
|
|
backgroundColor: '#f5f5f5' |
|
|
|
} |
|
|
|
}} |
|
|
|
> |
|
|
|
{`${lane.truckLanceCode} (${lane.unassigned}/${lane.total})`} |
|
|
|
</Button> |
|
|
|
))} |
|
|
|
</Stack> |
|
|
|
</Box> |
|
|
|
))} |
|
|
|
</Stack> |
|
|
|
)} |
|
|
|
</Box> |
|
|
|
</Grid> |
|
|
|
|
|
|
|
{/* 4/F 楼层面板 */} |
|
|
|
<Grid item> |
|
|
|
<Box |
|
|
|
sx={{ |
|
|
|
border: '1px solid #e0e0e0', |
|
|
|
borderRadius: 1, |
|
|
|
p: 1, |
|
|
|
minWidth: 320, |
|
|
|
backgroundColor: '#fafafa' |
|
|
|
}} |
|
|
|
> |
|
|
|
<Typography variant="subtitle2" sx={{ mb: 0.5, fontWeight: 600, textAlign: 'center' }}> |
|
|
|
4/F |
|
|
|
</Typography> |
|
|
|
{isLoadingSummary ? ( |
|
|
|
<Typography variant="caption">Loading...</Typography> |
|
|
|
) : ( |
|
|
|
<Stack spacing={0.5}> |
|
|
|
{summary4F?.rows.map((row, rowIdx) => ( |
|
|
|
<Box |
|
|
|
key={rowIdx} |
|
|
|
sx={{ |
|
|
|
border: '1px solid #e0e0e0', |
|
|
|
borderRadius: 0.5, |
|
|
|
p: 0.5, |
|
|
|
backgroundColor: '#fff' |
|
|
|
}} |
|
|
|
> |
|
|
|
<Typography |
|
|
|
variant="caption" |
|
|
|
sx={{ |
|
|
|
display: 'block', |
|
|
|
mb: 0.5, |
|
|
|
fontWeight: 500, |
|
|
|
textAlign: 'center', |
|
|
|
fontSize: '0.7rem' |
|
|
|
}} |
|
|
|
> |
|
|
|
{row.truckDepartureTime} |
|
|
|
</Typography> |
|
|
|
<Stack direction="row" spacing={0.25} alignItems="center" justifyContent="center"> |
|
|
|
{row.lanes.map((lane, laneIdx) => ( |
|
|
|
<Button |
|
|
|
key={laneIdx} |
|
|
|
variant="outlined" |
|
|
|
size="small" |
|
|
|
disabled={lane.unassigned === 0 || isAssigning} |
|
|
|
onClick={() => handleAssignByLane("4/F", row.truckDepartureTime, lane.truckLanceCode)} |
|
|
|
sx={{ |
|
|
|
minWidth: 80, |
|
|
|
fontSize: '0.7rem', |
|
|
|
py: 0.25, |
|
|
|
px: 0.5, |
|
|
|
borderWidth: 1, |
|
|
|
borderColor: '#ccc', |
|
|
|
'&:hover': { |
|
|
|
borderColor: '#999', |
|
|
|
backgroundColor: '#f5f5f5' |
|
|
|
} |
|
|
|
}} |
|
|
|
> |
|
|
|
{`${lane.truckLanceCode} (${lane.unassigned}/${lane.total})`} |
|
|
|
</Button> |
|
|
|
))} |
|
|
|
</Stack> |
|
|
|
</Box> |
|
|
|
))} |
|
|
|
</Stack> |
|
|
|
)} |
|
|
|
</Box> |
|
|
|
</Grid> |
|
|
|
{/* ✅ Updated print buttons with completion status */} |
|
|
|
<Grid item xs={6} display="flex" justifyContent="flex-end"> |
|
|
|
<Stack direction="row" spacing={1}> |
|
|
|
<Button |
|
|
|
variant="contained" |
|
|
|
onClick={handleAllDraft} |
|
|
|
> |
|
|
|
{t("Print All Draft")} ({releasedOrderCount}) |
|
|
|
</Button> |
|
|
|
|
|
|
|
<Button |
|
|
|
variant="contained" |
|
|
|
// disabled={!printButtonsEnabled} |
|
|
|
title={!printButtonsEnabled ? t("All lots must be completed before printing") : ""} |
|
|
|
onClick={handleDraft} |
|
|
|
> |
|
|
|
{t("Print Draft")} |
|
|
|
</Button> |
|
|
|
<Button |
|
|
|
variant="contained" |
|
|
|
disabled={!printButtonsEnabled} |
|
|
|
title={!printButtonsEnabled ? t("All lots must be completed before printing") : ""} |
|
|
|
onClick={handleDNandLabel} |
|
|
|
> |
|
|
|
{t("Print Pick Order and DN Label")} |
|
|
|
</Button> |
|
|
|
<Button |
|
|
|
variant="contained" |
|
|
|
disabled={!printButtonsEnabled} |
|
|
|
title={!printButtonsEnabled ? t("All lots must be completed before printing") : ""} |
|
|
|
onClick={handleDN} |
|
|
|
> |
|
|
|
{t("Print Pick Order")} |
|
|
|
</Button> |
|
|
|
<Button |
|
|
|
variant="contained" |
|
|
|
disabled={!printButtonsEnabled} |
|
|
|
title={!printButtonsEnabled ? t("All lots must be completed before printing") : ""} |
|
|
|
onClick={handleLabel} |
|
|
|
> |
|
|
|
{t("Print DN Label")} |
|
|
|
</Button> |
|
|
|
</Stack> |
|
|
|
</Grid> |
|
|
|
|
|
|
|
|
|
|
|
<Grid item xs> |
|
|
|
<Box sx={{ width: '100%', display: 'flex', justifyContent: 'flex-end' }}> |
|
|
|
<Stack direction="row" spacing={1}> |
|
|
|
<Button variant="contained" onClick={handleAllDraft}> |
|
|
|
{t("Print All Draft")} ({releasedOrderCount}) |
|
|
|
</Button> |
|
|
|
<Button |
|
|
|
variant="contained" |
|
|
|
// disabled={!printButtonsEnabled} |
|
|
|
title={!printButtonsEnabled ? t("All lots must be completed before printing") : ""} |
|
|
|
onClick={handleDraft} |
|
|
|
> |
|
|
|
{t("Print Draft")} |
|
|
|
</Button> |
|
|
|
<Button |
|
|
|
variant="contained" |
|
|
|
disabled={!printButtonsEnabled} |
|
|
|
title={!printButtonsEnabled ? t("All lots must be completed before printing") : ""} |
|
|
|
onClick={handleDNandLabel} |
|
|
|
> |
|
|
|
{t("Print Pick Order and DN Label")} |
|
|
|
</Button> |
|
|
|
<Button |
|
|
|
variant="contained" |
|
|
|
disabled={!printButtonsEnabled} |
|
|
|
title={!printButtonsEnabled ? t("All lots must be completed before printing") : ""} |
|
|
|
onClick={handleDN} |
|
|
|
> |
|
|
|
{t("Print Pick Order")} |
|
|
|
</Button> |
|
|
|
<Button |
|
|
|
variant="contained" |
|
|
|
disabled={!printButtonsEnabled} |
|
|
|
title={!printButtonsEnabled ? t("All lots must be completed before printing") : ""} |
|
|
|
onClick={handleLabel} |
|
|
|
> |
|
|
|
{t("Print DN Label")} |
|
|
|
</Button> |
|
|
|
</Stack> |
|
|
|
</Box> |
|
|
|
</Grid> |
|
|
|
</Grid> |
|
|
|
</Grid> |
|
|
|
</Grid> |
|
|
|
</Stack> |
|
|
|
</Box> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{/* Tabs section - ✅ Move the click handler here */} |
|
|
|
<Box sx={{ |
|
|
|
borderBottom: '1px solid #e0e0e0' |
|
|
|
|