|
|
@@ -1,108 +1,378 @@ |
|
|
"use client"; |
|
|
"use client"; |
|
|
|
|
|
|
|
|
import { FGPickOrderResponse } from "@/app/api/pickOrder/actions"; |
|
|
|
|
|
import { Box, Card, CardContent, Grid, Stack, TextField, Button } from "@mui/material"; |
|
|
|
|
|
|
|
|
import { Box, Button, Grid, Stack, Typography, Select, MenuItem, FormControl, InputLabel, Card, CardContent } from "@mui/material"; |
|
|
|
|
|
import { useCallback, useEffect, useState } from "react"; |
|
|
import { useTranslation } from "react-i18next"; |
|
|
import { useTranslation } from "react-i18next"; |
|
|
import QrCodeIcon from '@mui/icons-material/QrCode'; |
|
|
|
|
|
import { OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; |
|
|
|
|
|
|
|
|
import { useSession } from "next-auth/react"; |
|
|
|
|
|
import { SessionWithTokens } from "@/config/authConfig"; |
|
|
|
|
|
import { fetchStoreLaneSummary, assignByLane, type StoreLaneSummary } from "@/app/api/pickOrder/actions"; |
|
|
|
|
|
import Swal from "sweetalert2"; |
|
|
import dayjs from "dayjs"; |
|
|
import dayjs from "dayjs"; |
|
|
|
|
|
|
|
|
type Props = { |
|
|
|
|
|
fgOrder: FGPickOrderResponse; |
|
|
|
|
|
onQrCodeClick: (pickOrderId: number) => void; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
interface Props { |
|
|
|
|
|
onPickOrderAssigned?: () => void; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
const FGPickOrderCard: React.FC<Props> = ({ fgOrder, onQrCodeClick }) => { |
|
|
|
|
|
|
|
|
const FinishedGoodFloorLanePanel: React.FC<Props> = ({ onPickOrderAssigned }) => { |
|
|
const { t } = useTranslation("pickOrder"); |
|
|
const { t } = useTranslation("pickOrder"); |
|
|
|
|
|
const { data: session } = useSession() as { data: SessionWithTokens | null }; |
|
|
|
|
|
const currentUserId = session?.id ? parseInt(session.id) : undefined; |
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
|
<Card sx={{ display: "block" }}> |
|
|
|
|
|
<CardContent component={Stack} spacing={4}> |
|
|
|
|
|
<Box> |
|
|
|
|
|
<Grid container spacing={2} columns={{ xs: 6, sm: 12 }}> |
|
|
|
|
|
<Grid item xs={6}> |
|
|
|
|
|
<TextField |
|
|
|
|
|
label={t("Delivery Code")} |
|
|
|
|
|
fullWidth |
|
|
|
|
|
disabled={true} |
|
|
|
|
|
value={fgOrder.deliveryNo} |
|
|
|
|
|
/> |
|
|
|
|
|
</Grid> |
|
|
|
|
|
<Grid item xs={6}> |
|
|
|
|
|
<TextField |
|
|
|
|
|
label={t("Pick Order Code")} |
|
|
|
|
|
fullWidth |
|
|
|
|
|
disabled={true} |
|
|
|
|
|
value={fgOrder.pickOrderCode} |
|
|
|
|
|
//helperText={fgOrder.pickOrderConsoCode} |
|
|
|
|
|
/> |
|
|
|
|
|
</Grid> |
|
|
|
|
|
|
|
|
const [summary2F, setSummary2F] = useState<StoreLaneSummary | null>(null); |
|
|
|
|
|
const [summary4F, setSummary4F] = useState<StoreLaneSummary | null>(null); |
|
|
|
|
|
const [isLoadingSummary, setIsLoadingSummary] = useState(false); |
|
|
|
|
|
const [isAssigning, setIsAssigning] = useState(false); |
|
|
|
|
|
const [selectedDate, setSelectedDate] = useState<string>("today"); |
|
|
|
|
|
|
|
|
<Grid item xs={6}> |
|
|
|
|
|
<TextField |
|
|
|
|
|
label={t("Store ID")} |
|
|
|
|
|
fullWidth |
|
|
|
|
|
disabled={true} |
|
|
|
|
|
value={fgOrder.storeId} |
|
|
|
|
|
/> |
|
|
|
|
|
</Grid> |
|
|
|
|
|
|
|
|
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); |
|
|
|
|
|
} |
|
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
<Grid item xs={6}> |
|
|
|
|
|
<TextField |
|
|
|
|
|
label={t("Shop Name")} |
|
|
|
|
|
fullWidth |
|
|
|
|
|
disabled={true} |
|
|
|
|
|
value={fgOrder.shopName} |
|
|
|
|
|
/> |
|
|
|
|
|
</Grid> |
|
|
|
|
|
<Grid item xs={6}> |
|
|
|
|
|
<TextField |
|
|
|
|
|
label={t("Delivery Date")} |
|
|
|
|
|
fullWidth |
|
|
|
|
|
disabled={true} |
|
|
|
|
|
value={dayjs(fgOrder.deliveryDate).format(OUTPUT_DATE_FORMAT)} |
|
|
|
|
|
/> |
|
|
|
|
|
</Grid> |
|
|
|
|
|
<Grid item xs={12}> |
|
|
|
|
|
<TextField |
|
|
|
|
|
label={t("Shop Address")} |
|
|
|
|
|
fullWidth |
|
|
|
|
|
disabled={true} |
|
|
|
|
|
value={fgOrder.shopAddress} |
|
|
|
|
|
multiline |
|
|
|
|
|
rows={2} |
|
|
|
|
|
/> |
|
|
|
|
|
</Grid> |
|
|
|
|
|
<Grid item xs={6}> |
|
|
|
|
|
<TextField |
|
|
|
|
|
label={t("Departure Time")} |
|
|
|
|
|
fullWidth |
|
|
|
|
|
disabled={true} |
|
|
|
|
|
value={fgOrder.DepartureTime} |
|
|
|
|
|
/> |
|
|
|
|
|
</Grid> |
|
|
|
|
|
<Grid item xs={6}> |
|
|
|
|
|
<TextField |
|
|
|
|
|
label={t("Truck No.")} |
|
|
|
|
|
fullWidth |
|
|
|
|
|
disabled={true} |
|
|
|
|
|
value={fgOrder.truckLanceCode} |
|
|
|
|
|
/> |
|
|
|
|
|
</Grid> |
|
|
|
|
|
<Grid item xs={6}> |
|
|
|
|
|
<TextField |
|
|
|
|
|
label={t("Ticket No.")} |
|
|
|
|
|
fullWidth |
|
|
|
|
|
disabled={true} |
|
|
|
|
|
value={fgOrder.ticketNo} |
|
|
|
|
|
/> |
|
|
|
|
|
</Grid> |
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
|
loadSummaries(); |
|
|
|
|
|
}, [loadSummaries]); |
|
|
|
|
|
|
|
|
</Grid> |
|
|
|
|
|
|
|
|
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); |
|
|
|
|
|
|
|
|
|
|
|
if (res.code === "SUCCESS") { |
|
|
|
|
|
console.log("✅ Successfully assigned pick order from lane", truckLanceCode); |
|
|
|
|
|
window.dispatchEvent(new CustomEvent('pickOrderAssigned')); |
|
|
|
|
|
loadSummaries(); // 刷新按钮状态 |
|
|
|
|
|
onPickOrderAssigned?.(); |
|
|
|
|
|
} else if (res.code === "USER_BUSY") { |
|
|
|
|
|
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" |
|
|
|
|
|
}); |
|
|
|
|
|
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, onPickOrderAssigned]); |
|
|
|
|
|
|
|
|
|
|
|
const getDateLabel = (offset: number) => { |
|
|
|
|
|
return dayjs().add(offset, 'day').format('YYYY-MM-DD'); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// Flatten rows to create one box per lane |
|
|
|
|
|
const flattenRows = (rows: any[]) => { |
|
|
|
|
|
const flattened: any[] = []; |
|
|
|
|
|
rows.forEach(row => { |
|
|
|
|
|
row.lanes.forEach((lane: any) => { |
|
|
|
|
|
flattened.push({ |
|
|
|
|
|
truckDepartureTime: row.truckDepartureTime, |
|
|
|
|
|
lane: lane |
|
|
|
|
|
}); |
|
|
|
|
|
}); |
|
|
|
|
|
}); |
|
|
|
|
|
return flattened; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
|
<Card sx={{ mb: 2 }}> |
|
|
|
|
|
<CardContent> |
|
|
|
|
|
{/* Date Selector Dropdown */} |
|
|
|
|
|
<Box sx={{ maxWidth: 300, mb: 2 }}> |
|
|
|
|
|
<FormControl fullWidth size="small"> |
|
|
|
|
|
<InputLabel id="date-select-label">{t("Select Date")}</InputLabel> |
|
|
|
|
|
<Select |
|
|
|
|
|
labelId="date-select-label" |
|
|
|
|
|
id="date-select" |
|
|
|
|
|
value={selectedDate} |
|
|
|
|
|
label={t("Select Date")} |
|
|
|
|
|
onChange={(e) => setSelectedDate(e.target.value)} |
|
|
|
|
|
> |
|
|
|
|
|
<MenuItem value="today"> |
|
|
|
|
|
{t("Today")} ({getDateLabel(0)}) |
|
|
|
|
|
</MenuItem> |
|
|
|
|
|
<MenuItem value="tomorrow"> |
|
|
|
|
|
{t("Tomorrow")} ({getDateLabel(1)}) |
|
|
|
|
|
</MenuItem> |
|
|
|
|
|
<MenuItem value="dayAfterTomorrow"> |
|
|
|
|
|
{t("Day After Tomorrow")} ({getDateLabel(2)}) |
|
|
|
|
|
</MenuItem> |
|
|
|
|
|
</Select> |
|
|
|
|
|
</FormControl> |
|
|
</Box> |
|
|
</Box> |
|
|
|
|
|
|
|
|
|
|
|
{/* Grid containing both floors */} |
|
|
|
|
|
<Grid container spacing={2}> |
|
|
|
|
|
{/* 2/F 楼层面板 */} |
|
|
|
|
|
<Grid item xs={12}> |
|
|
|
|
|
<Stack direction="row" spacing={2} alignItems="flex-start"> |
|
|
|
|
|
{/* Floor Label */} |
|
|
|
|
|
<Box |
|
|
|
|
|
sx={{ |
|
|
|
|
|
border: '2px solid #1976d2', |
|
|
|
|
|
borderRadius: 1, |
|
|
|
|
|
backgroundColor: '#e3f2fd', |
|
|
|
|
|
px: 2, |
|
|
|
|
|
py: 1, |
|
|
|
|
|
minWidth: 80, |
|
|
|
|
|
display: 'flex', |
|
|
|
|
|
alignItems: 'center', |
|
|
|
|
|
justifyContent: 'center' |
|
|
|
|
|
}} |
|
|
|
|
|
> |
|
|
|
|
|
<Typography |
|
|
|
|
|
variant="h6" |
|
|
|
|
|
sx={{ |
|
|
|
|
|
fontWeight: 600, |
|
|
|
|
|
color: '#1976d2' |
|
|
|
|
|
}} |
|
|
|
|
|
> |
|
|
|
|
|
2/F |
|
|
|
|
|
</Typography> |
|
|
|
|
|
</Box> |
|
|
|
|
|
|
|
|
|
|
|
{/* Content Box */} |
|
|
|
|
|
<Box |
|
|
|
|
|
sx={{ |
|
|
|
|
|
border: '1px solid #e0e0e0', |
|
|
|
|
|
borderRadius: 1, |
|
|
|
|
|
p: 1, |
|
|
|
|
|
backgroundColor: '#fafafa', |
|
|
|
|
|
flex: 1 |
|
|
|
|
|
}} |
|
|
|
|
|
> |
|
|
|
|
|
{isLoadingSummary ? ( |
|
|
|
|
|
<Typography variant="caption">Loading...</Typography> |
|
|
|
|
|
) : !summary2F?.rows || summary2F.rows.length === 0 ? ( |
|
|
|
|
|
<Typography |
|
|
|
|
|
variant="body2" |
|
|
|
|
|
color="text.secondary" |
|
|
|
|
|
sx={{ |
|
|
|
|
|
fontWeight: 600, |
|
|
|
|
|
fontSize: '1rem', |
|
|
|
|
|
textAlign: 'center', |
|
|
|
|
|
py: 1 |
|
|
|
|
|
}} |
|
|
|
|
|
> |
|
|
|
|
|
{t("No entries available")} |
|
|
|
|
|
</Typography> |
|
|
|
|
|
) : ( |
|
|
|
|
|
<Grid container spacing={1}> |
|
|
|
|
|
{flattenRows(summary2F.rows).slice(0, 4).map((item, idx) => ( |
|
|
|
|
|
<Grid item xs={12} sm={6} md={3} key={idx}> |
|
|
|
|
|
<Stack |
|
|
|
|
|
direction="row" |
|
|
|
|
|
spacing={1} |
|
|
|
|
|
alignItems="center" |
|
|
|
|
|
sx={{ |
|
|
|
|
|
border: '1px solid #e0e0e0', |
|
|
|
|
|
borderRadius: 0.5, |
|
|
|
|
|
p: 1, |
|
|
|
|
|
backgroundColor: '#fff', |
|
|
|
|
|
height: '100%' |
|
|
|
|
|
}} |
|
|
|
|
|
> |
|
|
|
|
|
{/* Time on the left */} |
|
|
|
|
|
<Typography |
|
|
|
|
|
variant="body2" |
|
|
|
|
|
sx={{ |
|
|
|
|
|
fontWeight: 600, |
|
|
|
|
|
fontSize: '1rem', |
|
|
|
|
|
minWidth: 50, |
|
|
|
|
|
whiteSpace: 'nowrap' |
|
|
|
|
|
}} |
|
|
|
|
|
> |
|
|
|
|
|
{item.truckDepartureTime} |
|
|
|
|
|
</Typography> |
|
|
|
|
|
|
|
|
|
|
|
{/* Single Button on the right */} |
|
|
|
|
|
<Button |
|
|
|
|
|
variant="outlined" |
|
|
|
|
|
size="medium" |
|
|
|
|
|
disabled={item.lane.unassigned === 0 || isAssigning} |
|
|
|
|
|
onClick={() => handleAssignByLane("2/F", item.truckDepartureTime, item.lane.truckLanceCode)} |
|
|
|
|
|
sx={{ |
|
|
|
|
|
flex: 1, |
|
|
|
|
|
fontSize: '1.1rem', |
|
|
|
|
|
py: 1, |
|
|
|
|
|
px: 1.5, |
|
|
|
|
|
borderWidth: 1, |
|
|
|
|
|
borderColor: '#ccc', |
|
|
|
|
|
fontWeight: 500, |
|
|
|
|
|
'&:hover': { |
|
|
|
|
|
borderColor: '#999', |
|
|
|
|
|
backgroundColor: '#f5f5f5' |
|
|
|
|
|
} |
|
|
|
|
|
}} |
|
|
|
|
|
> |
|
|
|
|
|
{`${item.lane.truckLanceCode} (${item.lane.unassigned}/${item.lane.total})`} |
|
|
|
|
|
</Button> |
|
|
|
|
|
</Stack> |
|
|
|
|
|
</Grid> |
|
|
|
|
|
))} |
|
|
|
|
|
</Grid> |
|
|
|
|
|
)} |
|
|
|
|
|
</Box> |
|
|
|
|
|
</Stack> |
|
|
|
|
|
</Grid> |
|
|
|
|
|
|
|
|
|
|
|
{/* 4/F 楼层面板 */} |
|
|
|
|
|
<Grid item xs={12}> |
|
|
|
|
|
<Stack direction="row" spacing={2} alignItems="flex-start"> |
|
|
|
|
|
{/* Floor Label */} |
|
|
|
|
|
<Box |
|
|
|
|
|
sx={{ |
|
|
|
|
|
border: '2px solid #1976d2', |
|
|
|
|
|
borderRadius: 1, |
|
|
|
|
|
backgroundColor: '#e3f2fd', |
|
|
|
|
|
px: 2, |
|
|
|
|
|
py: 1, |
|
|
|
|
|
minWidth: 80, |
|
|
|
|
|
display: 'flex', |
|
|
|
|
|
alignItems: 'center', |
|
|
|
|
|
justifyContent: 'center' |
|
|
|
|
|
}} |
|
|
|
|
|
> |
|
|
|
|
|
<Typography |
|
|
|
|
|
variant="h6" |
|
|
|
|
|
sx={{ |
|
|
|
|
|
fontWeight: 600, |
|
|
|
|
|
color: '#1976d2' |
|
|
|
|
|
}} |
|
|
|
|
|
> |
|
|
|
|
|
4/F |
|
|
|
|
|
</Typography> |
|
|
|
|
|
</Box> |
|
|
|
|
|
|
|
|
|
|
|
{/* Content Box */} |
|
|
|
|
|
<Box |
|
|
|
|
|
sx={{ |
|
|
|
|
|
border: '1px solid #e0e0e0', |
|
|
|
|
|
borderRadius: 1, |
|
|
|
|
|
p: 1, |
|
|
|
|
|
backgroundColor: '#fafafa', |
|
|
|
|
|
flex: 1 |
|
|
|
|
|
}} |
|
|
|
|
|
> |
|
|
|
|
|
{isLoadingSummary ? ( |
|
|
|
|
|
<Typography variant="caption">Loading...</Typography> |
|
|
|
|
|
) : !summary4F?.rows || summary4F.rows.length === 0 ? ( |
|
|
|
|
|
<Typography |
|
|
|
|
|
variant="body2" |
|
|
|
|
|
color="text.secondary" |
|
|
|
|
|
sx={{ |
|
|
|
|
|
fontWeight: 600, |
|
|
|
|
|
fontSize: '1rem', |
|
|
|
|
|
textAlign: 'center', |
|
|
|
|
|
py: 1 |
|
|
|
|
|
}} |
|
|
|
|
|
> |
|
|
|
|
|
{t("No entries available")} |
|
|
|
|
|
</Typography> |
|
|
|
|
|
) : ( |
|
|
|
|
|
<Grid container spacing={1}> |
|
|
|
|
|
{flattenRows(summary4F.rows).slice(0, 4).map((item, idx) => ( |
|
|
|
|
|
<Grid item xs={12} sm={6} md={3} key={idx}> |
|
|
|
|
|
<Stack |
|
|
|
|
|
direction="row" |
|
|
|
|
|
spacing={1} |
|
|
|
|
|
alignItems="center" |
|
|
|
|
|
sx={{ |
|
|
|
|
|
border: '1px solid #e0e0e0', |
|
|
|
|
|
borderRadius: 0.5, |
|
|
|
|
|
p: 1, |
|
|
|
|
|
backgroundColor: '#fff', |
|
|
|
|
|
height: '100%' |
|
|
|
|
|
}} |
|
|
|
|
|
> |
|
|
|
|
|
{/* Time on the left */} |
|
|
|
|
|
<Typography |
|
|
|
|
|
variant="body2" |
|
|
|
|
|
sx={{ |
|
|
|
|
|
fontWeight: 600, |
|
|
|
|
|
fontSize: '1rem', |
|
|
|
|
|
minWidth: 50, |
|
|
|
|
|
whiteSpace: 'nowrap' |
|
|
|
|
|
}} |
|
|
|
|
|
> |
|
|
|
|
|
{item.truckDepartureTime} |
|
|
|
|
|
</Typography> |
|
|
|
|
|
|
|
|
|
|
|
{/* Single Button on the right */} |
|
|
|
|
|
<Button |
|
|
|
|
|
variant="outlined" |
|
|
|
|
|
size="medium" |
|
|
|
|
|
disabled={item.lane.unassigned === 0 || isAssigning} |
|
|
|
|
|
onClick={() => handleAssignByLane("4/F", item.truckDepartureTime, item.lane.truckLanceCode)} |
|
|
|
|
|
sx={{ |
|
|
|
|
|
flex: 1, |
|
|
|
|
|
fontSize: '1.1rem', |
|
|
|
|
|
py: 1, |
|
|
|
|
|
px: 1.5, |
|
|
|
|
|
borderWidth: 1, |
|
|
|
|
|
borderColor: '#ccc', |
|
|
|
|
|
fontWeight: 500, |
|
|
|
|
|
'&:hover': { |
|
|
|
|
|
borderColor: '#999', |
|
|
|
|
|
backgroundColor: '#f5f5f5' |
|
|
|
|
|
} |
|
|
|
|
|
}} |
|
|
|
|
|
> |
|
|
|
|
|
{`${item.lane.truckLanceCode} (${item.lane.unassigned}/${item.lane.total})`} |
|
|
|
|
|
</Button> |
|
|
|
|
|
</Stack> |
|
|
|
|
|
</Grid> |
|
|
|
|
|
))} |
|
|
|
|
|
</Grid> |
|
|
|
|
|
)} |
|
|
|
|
|
</Box> |
|
|
|
|
|
</Stack> |
|
|
|
|
|
</Grid> |
|
|
|
|
|
</Grid> |
|
|
</CardContent> |
|
|
</CardContent> |
|
|
</Card> |
|
|
</Card> |
|
|
); |
|
|
); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
export default FGPickOrderCard; |
|
|
|
|
|
|
|
|
export default FinishedGoodFloorLanePanel; |