| @@ -44,6 +44,10 @@ export interface InventoryLotDetailResponse { | |||||
| stockTakeSection?: string | null; | stockTakeSection?: string | null; | ||||
| stockTakeSectionDescription?: string | null; | stockTakeSectionDescription?: string | null; | ||||
| stockTakerName?: string | null; | stockTakerName?: string | null; | ||||
| /** ISO string or backend LocalDateTime array */ | |||||
| stockTakeEndTime?: string | string[] | null; | |||||
| /** ISO string or backend LocalDateTime array */ | |||||
| approverTime?: string | string[] | null; | |||||
| } | } | ||||
| export const getInventoryLotDetailsBySection = async ( | export const getInventoryLotDetailsBySection = async ( | ||||
| @@ -17,6 +17,7 @@ import { | |||||
| TextField, | TextField, | ||||
| Radio, | Radio, | ||||
| TablePagination, | TablePagination, | ||||
| TableSortLabel, | |||||
| } from "@mui/material"; | } from "@mui/material"; | ||||
| import { useState, useCallback, useEffect, useMemo } from "react"; | import { useState, useCallback, useEffect, useMemo } from "react"; | ||||
| import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||
| @@ -45,6 +46,25 @@ interface ApproverStockTakeAllProps { | |||||
| type QtySelectionType = "first" | "second" | "approver"; | type QtySelectionType = "first" | "second" | "approver"; | ||||
| type ApprovedSortKey = | |||||
| | "stockTakeEndTime" | |||||
| | "stockTakeSection" | |||||
| | "item" | |||||
| | "stockTakerName" | |||||
| | "variance"; | |||||
| function parseDateTimeMs( | |||||
| v: string | string[] | null | undefined | |||||
| ): number { | |||||
| if (v == null) return 0; | |||||
| if (Array.isArray(v)) { | |||||
| const arr = v as unknown as number[]; | |||||
| const [y, m, d, h = 0, min = 0, s = 0] = arr; | |||||
| return dayjs(`${y}-${String(m).padStart(2, "0")}-${String(d).padStart(2, "0")} ${h}:${min}:${s}`).valueOf(); | |||||
| } | |||||
| return dayjs(v as string).valueOf(); | |||||
| } | |||||
| const ApproverStockTakeAll: React.FC<ApproverStockTakeAllProps> = ({ | const ApproverStockTakeAll: React.FC<ApproverStockTakeAllProps> = ({ | ||||
| selectedSession, | selectedSession, | ||||
| mode, | mode, | ||||
| @@ -66,6 +86,8 @@ const ApproverStockTakeAll: React.FC<ApproverStockTakeAllProps> = ({ | |||||
| const [page, setPage] = useState(0); | const [page, setPage] = useState(0); | ||||
| const [pageSize, setPageSize] = useState<number | string>(50); | const [pageSize, setPageSize] = useState<number | string>(50); | ||||
| const [total, setTotal] = useState(0); | const [total, setTotal] = useState(0); | ||||
| const [approvedSortKey, setApprovedSortKey] = useState<ApprovedSortKey | null>(null); | |||||
| const [approvedSortDir, setApprovedSortDir] = useState<"asc" | "desc">("asc"); | |||||
| const currentUserId = session?.id ? parseInt(session.id) : undefined; | const currentUserId = session?.id ? parseInt(session.id) : undefined; | ||||
| @@ -200,12 +222,62 @@ const ApproverStockTakeAll: React.FC<ApproverStockTakeAllProps> = ({ | |||||
| ]); | ]); | ||||
| const sortedDetails = useMemo(() => { | const sortedDetails = useMemo(() => { | ||||
| return [...filteredDetails].sort((a, b) => { | |||||
| const sectionA = (a.stockTakeSection || "").trim(); | |||||
| const sectionB = (b.stockTakeSection || "").trim(); | |||||
| return sectionA.localeCompare(sectionB, undefined, { numeric: true, sensitivity: "base" }); | |||||
| const list = [...filteredDetails]; | |||||
| if (mode !== "approved") { | |||||
| return list.sort((a, b) => | |||||
| (a.stockTakeSection || "").localeCompare(b.stockTakeSection || "", undefined, { | |||||
| numeric: true, | |||||
| sensitivity: "base", | |||||
| }) | |||||
| ); | |||||
| } | |||||
| const key = approvedSortKey ?? "stockTakeSection"; | |||||
| const mul = approvedSortDir === "asc" ? 1 : -1; | |||||
| return list.sort((a, b) => { | |||||
| let cmp = 0; | |||||
| switch (key) { | |||||
| case "stockTakeEndTime": | |||||
| cmp = | |||||
| parseDateTimeMs(a.approverTime ?? a.stockTakeEndTime) - | |||||
| parseDateTimeMs(b.approverTime ?? b.stockTakeEndTime); | |||||
| break; | |||||
| case "stockTakeSection": | |||||
| cmp = (a.stockTakeSection || "").localeCompare(b.stockTakeSection || "", undefined, { | |||||
| numeric: true, | |||||
| sensitivity: "base", | |||||
| }); | |||||
| break; | |||||
| case "item": | |||||
| cmp = `${a.itemCode || ""} ${a.itemName || ""}`.localeCompare( | |||||
| `${b.itemCode || ""} ${b.itemName || ""}`, | |||||
| undefined, | |||||
| { numeric: true, sensitivity: "base" } | |||||
| ); | |||||
| break; | |||||
| case "stockTakerName": | |||||
| cmp = (a.stockTakerName || "").localeCompare(b.stockTakerName || "", undefined, { | |||||
| numeric: true, | |||||
| sensitivity: "base", | |||||
| }); | |||||
| break; | |||||
| case "variance": | |||||
| cmp = Number(a.varianceQty ?? 0) - Number(b.varianceQty ?? 0); | |||||
| break; | |||||
| default: | |||||
| cmp = 0; | |||||
| } | |||||
| return cmp * mul; | |||||
| }); | }); | ||||
| }, [filteredDetails]); | |||||
| }, [filteredDetails, mode, approvedSortKey, approvedSortDir]); | |||||
| const handleApprovedSort = useCallback((property: ApprovedSortKey) => { | |||||
| if (approvedSortKey === property) { | |||||
| setApprovedSortDir((d) => (d === "asc" ? "desc" : "asc")); | |||||
| } else { | |||||
| setApprovedSortKey(property); | |||||
| setApprovedSortDir("asc"); | |||||
| } | |||||
| }, [approvedSortKey]); | |||||
| const handleSaveApproverStockTake = useCallback( | const handleSaveApproverStockTake = useCallback( | ||||
| async (detail: InventoryLotDetailResponse) => { | async (detail: InventoryLotDetailResponse) => { | ||||
| @@ -410,6 +482,12 @@ const ApproverStockTakeAll: React.FC<ApproverStockTakeAllProps> = ({ | |||||
| [inventoryLotDetails] | [inventoryLotDetails] | ||||
| ); | ); | ||||
| const formatRecordEndTime = (detail: InventoryLotDetailResponse) => { | |||||
| const ms = parseDateTimeMs(detail.approverTime ?? detail.stockTakeEndTime); | |||||
| if (!ms) return "-"; | |||||
| return dayjs(ms).format("YYYY-MM-DD HH:mm"); | |||||
| }; | |||||
| return ( | return ( | ||||
| <Box> | <Box> | ||||
| {onBack && ( | {onBack && ( | ||||
| @@ -482,23 +560,110 @@ const ApproverStockTakeAll: React.FC<ApproverStockTakeAllProps> = ({ | |||||
| <Table> | <Table> | ||||
| <TableHead> | <TableHead> | ||||
| <TableRow> | <TableRow> | ||||
| <TableCell>{t("Warehouse Location")}</TableCell> | |||||
| <TableCell>{t("Item-lotNo-ExpiryDate")}</TableCell> | |||||
| {mode === "approved" && ( | |||||
| <TableCell | |||||
| sortDirection={ | |||||
| approvedSortKey === "stockTakeEndTime" ? approvedSortDir : false | |||||
| } | |||||
| > | |||||
| <TableSortLabel | |||||
| active={approvedSortKey === "stockTakeEndTime"} | |||||
| direction={ | |||||
| approvedSortKey === "stockTakeEndTime" ? approvedSortDir : "asc" | |||||
| } | |||||
| onClick={() => handleApprovedSort("stockTakeEndTime")} | |||||
| > | |||||
| {t("Approver Time")} | |||||
| </TableSortLabel> | |||||
| </TableCell> | |||||
| )} | |||||
| <TableCell | |||||
| sortDirection={ | |||||
| mode === "approved" && (approvedSortKey === "stockTakeSection" || approvedSortKey === null) | |||||
| ? approvedSortDir | |||||
| : false | |||||
| } | |||||
| > | |||||
| {mode === "approved" ? ( | |||||
| <TableSortLabel | |||||
| active={ | |||||
| approvedSortKey === "stockTakeSection" || approvedSortKey === null | |||||
| } | |||||
| direction={approvedSortDir} | |||||
| onClick={() => handleApprovedSort("stockTakeSection")} | |||||
| > | |||||
| {t("Warehouse Location")} | |||||
| </TableSortLabel> | |||||
| ) : ( | |||||
| t("Warehouse Location") | |||||
| )} | |||||
| </TableCell> | |||||
| <TableCell | |||||
| sortDirection={ | |||||
| mode === "approved" && approvedSortKey === "item" ? approvedSortDir : false | |||||
| } | |||||
| > | |||||
| {mode === "approved" ? ( | |||||
| <TableSortLabel | |||||
| active={approvedSortKey === "item"} | |||||
| direction={approvedSortKey === "item" ? approvedSortDir : "asc"} | |||||
| onClick={() => handleApprovedSort("item")} | |||||
| > | |||||
| {t("Item-lotNo-ExpiryDate")} | |||||
| </TableSortLabel> | |||||
| ) : ( | |||||
| t("Item-lotNo-ExpiryDate") | |||||
| )} | |||||
| </TableCell> | |||||
| <TableCell>{t("UOM")}</TableCell> | <TableCell>{t("UOM")}</TableCell> | ||||
| <TableCell> | <TableCell> | ||||
| {t("Stock Take Qty(include Bad Qty)= Available Qty")} | {t("Stock Take Qty(include Bad Qty)= Available Qty")} | ||||
| </TableCell> | </TableCell> | ||||
| {mode === "approved" && ( | |||||
| <TableCell | |||||
| sortDirection={ | |||||
| approvedSortKey === "variance" ? approvedSortDir : false | |||||
| } | |||||
| > | |||||
| <TableSortLabel | |||||
| active={approvedSortKey === "variance"} | |||||
| direction={approvedSortKey === "variance" ? approvedSortDir : "asc"} | |||||
| onClick={() => handleApprovedSort("variance")} | |||||
| > | |||||
| {t("Variance")} | |||||
| </TableSortLabel> | |||||
| </TableCell> | |||||
| )} | |||||
| <TableCell>{t("Remark")}</TableCell> | <TableCell>{t("Remark")}</TableCell> | ||||
| <TableCell>{t("Record Status")}</TableCell> | <TableCell>{t("Record Status")}</TableCell> | ||||
| <TableCell>{t("Picker")}</TableCell> | |||||
| <TableCell | |||||
| sortDirection={ | |||||
| mode === "approved" && approvedSortKey === "stockTakerName" | |||||
| ? approvedSortDir | |||||
| : false | |||||
| } | |||||
| > | |||||
| {mode === "approved" ? ( | |||||
| <TableSortLabel | |||||
| active={approvedSortKey === "stockTakerName"} | |||||
| direction={ | |||||
| approvedSortKey === "stockTakerName" ? approvedSortDir : "asc" | |||||
| } | |||||
| onClick={() => handleApprovedSort("stockTakerName")} | |||||
| > | |||||
| {t("Picker")} | |||||
| </TableSortLabel> | |||||
| ) : ( | |||||
| t("Picker") | |||||
| )} | |||||
| </TableCell> | |||||
| <TableCell>{t("Action")}</TableCell> | <TableCell>{t("Action")}</TableCell> | ||||
| </TableRow> | </TableRow> | ||||
| </TableHead> | </TableHead> | ||||
| <TableBody> | <TableBody> | ||||
| {sortedDetails.length === 0 ? ( | {sortedDetails.length === 0 ? ( | ||||
| <TableRow> | <TableRow> | ||||
| <TableCell colSpan={8} align="center"> | |||||
| <TableCell colSpan={mode === "approved" ? 10 : 8} align="center"> | |||||
| <Typography variant="body2" color="text.secondary"> | <Typography variant="body2" color="text.secondary"> | ||||
| {t("No data")} | {t("No data")} | ||||
| </Typography> | </Typography> | ||||
| @@ -515,6 +680,16 @@ const ApproverStockTakeAll: React.FC<ApproverStockTakeAllProps> = ({ | |||||
| return ( | return ( | ||||
| <TableRow key={detail.id}> | <TableRow key={detail.id}> | ||||
| {mode === "approved" && ( | |||||
| <TableCell> | |||||
| <Stack spacing={0.5}> | |||||
| <Typography variant="caption" color="text.secondary"> | |||||
| {formatRecordEndTime(detail)} | |||||
| </Typography> | |||||
| </Stack> | |||||
| </TableCell> | |||||
| )} | |||||
| <TableCell> | <TableCell> | ||||
| <Stack spacing={0.5}> | <Stack spacing={0.5}> | ||||
| <Typography variant="body2"><strong>{detail.stockTakeSection || "-"} {detail.stockTakeSectionDescription || "-"}</strong></Typography> | <Typography variant="body2"><strong>{detail.stockTakeSection || "-"} {detail.stockTakeSectionDescription || "-"}</strong></Typography> | ||||
| @@ -759,6 +934,14 @@ const ApproverStockTakeAll: React.FC<ApproverStockTakeAllProps> = ({ | |||||
| )} | )} | ||||
| </TableCell> | </TableCell> | ||||
| {mode === "approved" && ( | |||||
| <TableCell> | |||||
| <Typography variant="body2"> | |||||
| {formatNumber(detail.varianceQty)} | |||||
| </Typography> | |||||
| </TableCell> | |||||
| )} | |||||
| <TableCell> | <TableCell> | ||||
| <Typography variant="body2"> | <Typography variant="body2"> | ||||
| {detail.remarks || "-"} | {detail.remarks || "-"} | ||||
| @@ -37,20 +37,30 @@ import { OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; | |||||
| const PER_PAGE = 6; | const PER_PAGE = 6; | ||||
| interface PickerCardListProps { | interface PickerCardListProps { | ||||
| /** 由父層保存,從明細返回時仍回到同一頁 */ | |||||
| page: number; | |||||
| pageSize: number; | |||||
| onListPageChange: (page: number) => void; | |||||
| onCardClick: (session: AllPickedStockTakeListReponse) => void; | onCardClick: (session: AllPickedStockTakeListReponse) => void; | ||||
| onReStockTakeClick: (session: AllPickedStockTakeListReponse) => void; | onReStockTakeClick: (session: AllPickedStockTakeListReponse) => void; | ||||
| } | } | ||||
| const PickerCardList: React.FC<PickerCardListProps> = ({ onCardClick, onReStockTakeClick }) => { | |||||
| const PickerCardList: React.FC<PickerCardListProps> = ({ | |||||
| page, | |||||
| pageSize, | |||||
| onListPageChange, | |||||
| onCardClick, | |||||
| onReStockTakeClick, | |||||
| }) => { | |||||
| const { t } = useTranslation(["inventory", "common"]); | const { t } = useTranslation(["inventory", "common"]); | ||||
| dayjs.extend(duration); | dayjs.extend(duration); | ||||
| const PER_PAGE = 6; | const PER_PAGE = 6; | ||||
| const [loading, setLoading] = useState(false); | const [loading, setLoading] = useState(false); | ||||
| const [stockTakeSessions, setStockTakeSessions] = useState<AllPickedStockTakeListReponse[]>([]); | const [stockTakeSessions, setStockTakeSessions] = useState<AllPickedStockTakeListReponse[]>([]); | ||||
| const [page, setPage] = useState(0); | |||||
| const [pageSize, setPageSize] = useState(6); // 每页 6 条 | |||||
| const [total, setTotal] = useState(0); | |||||
| const [total, setTotal] = useState(0); | |||||
| /** 建立盤點後若仍在 page 0,仍強制重新載入 */ | |||||
| const [listRefreshNonce, setListRefreshNonce] = useState(0); | |||||
| const [creating, setCreating] = useState(false); | const [creating, setCreating] = useState(false); | ||||
| const [openConfirmDialog, setOpenConfirmDialog] = useState(false); | const [openConfirmDialog, setOpenConfirmDialog] = useState(false); | ||||
| const [filterSectionDescription, setFilterSectionDescription] = useState<string>("All"); | const [filterSectionDescription, setFilterSectionDescription] = useState<string>("All"); | ||||
| @@ -106,41 +116,40 @@ const criteria: Criterion<PickerSearchKey>[] = [ | |||||
| const handleSearch = (inputs: Record<PickerSearchKey | `${PickerSearchKey}To`, string>) => { | const handleSearch = (inputs: Record<PickerSearchKey | `${PickerSearchKey}To`, string>) => { | ||||
| setFilterSectionDescription(inputs.sectionDescription || "All"); | setFilterSectionDescription(inputs.sectionDescription || "All"); | ||||
| setFilterStockTakeSession(inputs.stockTakeSession || ""); | setFilterStockTakeSession(inputs.stockTakeSession || ""); | ||||
| fetchStockTakeSessions(0, pageSize, { | |||||
| sectionDescription: inputs.sectionDescription || "All", | |||||
| stockTakeSections: inputs.stockTakeSession ?? "", | |||||
| }); | |||||
| onListPageChange(0); | |||||
| }; | }; | ||||
| const handleResetSearch = () => { | const handleResetSearch = () => { | ||||
| setFilterSectionDescription("All"); | setFilterSectionDescription("All"); | ||||
| setFilterStockTakeSession(""); | setFilterStockTakeSession(""); | ||||
| fetchStockTakeSessions(0, pageSize, { | |||||
| sectionDescription: "All", | |||||
| stockTakeSections: "", | |||||
| }); | |||||
| onListPageChange(0); | |||||
| }; | }; | ||||
| const fetchStockTakeSessions = useCallback( | |||||
| async (pageNum: number, size: number, filterOverrides?: { sectionDescription: string; stockTakeSections: string }) => { | |||||
| setLoading(true); | |||||
| try { | |||||
| const res = await getStockTakeRecordsPaged(pageNum, size, filterOverrides); | |||||
| useEffect(() => { | |||||
| let cancelled = false; | |||||
| setLoading(true); | |||||
| getStockTakeRecordsPaged(page, pageSize, { | |||||
| sectionDescription: filterSectionDescription, | |||||
| stockTakeSections: filterStockTakeSession, | |||||
| }) | |||||
| .then((res) => { | |||||
| if (cancelled) return; | |||||
| setStockTakeSessions(Array.isArray(res.records) ? res.records : []); | setStockTakeSessions(Array.isArray(res.records) ? res.records : []); | ||||
| setTotal(res.total || 0); | setTotal(res.total || 0); | ||||
| setPage(pageNum); | |||||
| } catch (e) { | |||||
| }) | |||||
| .catch((e) => { | |||||
| console.error(e); | console.error(e); | ||||
| setStockTakeSessions([]); | |||||
| setTotal(0); | |||||
| } finally { | |||||
| setLoading(false); | |||||
| } | |||||
| }, | |||||
| [] | |||||
| ); | |||||
| useEffect(() => { | |||||
| fetchStockTakeSessions(0, pageSize); | |||||
| }, [fetchStockTakeSessions, pageSize]); | |||||
| if (!cancelled) { | |||||
| setStockTakeSessions([]); | |||||
| setTotal(0); | |||||
| } | |||||
| }) | |||||
| .finally(() => { | |||||
| if (!cancelled) setLoading(false); | |||||
| }); | |||||
| return () => { | |||||
| cancelled = true; | |||||
| }; | |||||
| }, [page, pageSize, filterSectionDescription, filterStockTakeSession, listRefreshNonce]); | |||||
| //const startIdx = page * PER_PAGE; | //const startIdx = page * PER_PAGE; | ||||
| //const paged = stockTakeSessions.slice(startIdx, startIdx + PER_PAGE); | //const paged = stockTakeSessions.slice(startIdx, startIdx + PER_PAGE); | ||||
| @@ -161,13 +170,14 @@ const handleResetSearch = () => { | |||||
| console.log(message); | console.log(message); | ||||
| await fetchStockTakeSessions(0, pageSize); | |||||
| onListPageChange(0); | |||||
| setListRefreshNonce((n) => n + 1); | |||||
| } catch (e) { | } catch (e) { | ||||
| console.error(e); | console.error(e); | ||||
| } finally { | } finally { | ||||
| setCreating(false); | setCreating(false); | ||||
| } | } | ||||
| }, [fetchStockTakeSessions, t]); | |||||
| }, [onListPageChange, t]); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| fetchStockTakeSections() | fetchStockTakeSections() | ||||
| .then((sections) => { | .then((sections) => { | ||||
| @@ -376,7 +386,7 @@ const handleResetSearch = () => { | |||||
| page={page} | page={page} | ||||
| rowsPerPage={pageSize} | rowsPerPage={pageSize} | ||||
| onPageChange={(e, newPage) => { | onPageChange={(e, newPage) => { | ||||
| fetchStockTakeSessions(newPage, pageSize); | |||||
| onListPageChange(newPage); | |||||
| }} | }} | ||||
| rowsPerPageOptions={[pageSize]} // 如果暂时不让用户改 pageSize,就写死 | rowsPerPageOptions={[pageSize]} // 如果暂时不让用户改 pageSize,就写死 | ||||
| /> | /> | ||||
| @@ -599,13 +599,13 @@ const PickerReStockTake: React.FC<PickerReStockTakeProps> = ({ | |||||
| <TextField | <TextField | ||||
| size="small" | size="small" | ||||
| value={inputs.remark} | value={inputs.remark} | ||||
| onKeyDown={blockNonIntegerKeys} | |||||
| inputProps={{ inputMode: "text", pattern: "[0-9]*" }} | |||||
| // onKeyDown={blockNonIntegerKeys} | |||||
| //inputProps={{ inputMode: "text", pattern: "[0-9]*" }} | |||||
| onChange={(e) => { | onChange={(e) => { | ||||
| const clean = sanitizeIntegerInput(e.target.value); | |||||
| // const clean = sanitizeIntegerInput(e.target.value); | |||||
| setRecordInputs(prev => ({ | setRecordInputs(prev => ({ | ||||
| ...prev, | ...prev, | ||||
| [detail.id]: { ...(prev[detail.id] ?? defaultInputs), remark: clean } | |||||
| [detail.id]: { ...(prev[detail.id] ?? defaultInputs), remark: e.target.value } | |||||
| })); | })); | ||||
| }} | }} | ||||
| sx={{ width: 150 }} | sx={{ width: 150 }} | ||||
| @@ -771,15 +771,15 @@ const PickerStockTake: React.FC<PickerStockTakeProps> = ({ | |||||
| <TextField | <TextField | ||||
| size="small" | size="small" | ||||
| value={recordInputs[detail.id]?.remark || ""} | value={recordInputs[detail.id]?.remark || ""} | ||||
| onKeyDown={blockNonIntegerKeys} | |||||
| inputProps={{ inputMode: "text", pattern: "[0-9]*" }} | |||||
| // onKeyDown={blockNonIntegerKeys} | |||||
| //inputProps={{ inputMode: "text", pattern: "[0-9]*" }} | |||||
| onChange={(e) => { | onChange={(e) => { | ||||
| const clean = sanitizeIntegerInput(e.target.value); | |||||
| // const clean = sanitizeIntegerInput(e.target.value); | |||||
| setRecordInputs(prev => ({ | setRecordInputs(prev => ({ | ||||
| ...prev, | ...prev, | ||||
| [detail.id]: { | [detail.id]: { | ||||
| ...(prev[detail.id] ?? { firstQty: "", secondQty: "", firstBadQty: "", secondBadQty: "", remark: "" }), | ...(prev[detail.id] ?? { firstQty: "", secondQty: "", firstBadQty: "", secondBadQty: "", remark: "" }), | ||||
| remark: clean | |||||
| remark: e.target.value | |||||
| } | } | ||||
| })); | })); | ||||
| }} | }} | ||||
| @@ -19,6 +19,9 @@ const StockTakeTab: React.FC = () => { | |||||
| const [viewScope, setViewScope] = useState<ViewScope>("picker"); | const [viewScope, setViewScope] = useState<ViewScope>("picker"); | ||||
| const [approverSession, setApproverSession] = useState<AllPickedStockTakeListReponse | null>(null); | const [approverSession, setApproverSession] = useState<AllPickedStockTakeListReponse | null>(null); | ||||
| const [approverLoading, setApproverLoading] = useState(false); | const [approverLoading, setApproverLoading] = useState(false); | ||||
| /** 從卡片列表進入明細後返回時保留分頁 */ | |||||
| const [pickerListPage, setPickerListPage] = useState(0); | |||||
| const [pickerListPageSize] = useState(6); | |||||
| const [snackbar, setSnackbar] = useState<{ | const [snackbar, setSnackbar] = useState<{ | ||||
| open: boolean; | open: boolean; | ||||
| message: string; | message: string; | ||||
| @@ -120,7 +123,10 @@ const StockTakeTab: React.FC = () => { | |||||
| </Tabs> | </Tabs> | ||||
| {tabValue === 0 && ( | {tabValue === 0 && ( | ||||
| <PickerCardList | |||||
| <PickerCardList | |||||
| page={pickerListPage} | |||||
| pageSize={pickerListPageSize} | |||||
| onListPageChange={setPickerListPage} | |||||
| onCardClick={(session) => { | onCardClick={(session) => { | ||||
| setViewScope("picker"); | setViewScope("picker"); | ||||
| handleCardClick(session); | handleCardClick(session); | ||||
| @@ -8,6 +8,7 @@ | |||||
| "UoM": "單位", | "UoM": "單位", | ||||
| "Approver Pending": "審核待處理", | "Approver Pending": "審核待處理", | ||||
| "Approver Approved": "審核通過", | "Approver Approved": "審核通過", | ||||
| "Approver Time": "審核時間", | |||||
| "mat": "物料", | "mat": "物料", | ||||
| "variance": "差異", | "variance": "差異", | ||||
| "Plan Start Date": "計劃開始日期", | "Plan Start Date": "計劃開始日期", | ||||