diff --git a/src/components/PoDetail/PoDetail.tsx b/src/components/PoDetail/PoDetail.tsx index 52028cd..4fba6f3 100644 --- a/src/components/PoDetail/PoDetail.tsx +++ b/src/components/PoDetail/PoDetail.tsx @@ -26,6 +26,8 @@ import { TabsProps, TextField, Typography, + Checkbox, + FormControlLabel, } from "@mui/material"; import { useTranslation } from "react-i18next"; // import InputDataGrid, { TableRow } from "../InputDataGrid/InputDataGrid"; @@ -69,7 +71,7 @@ import { decimalFormatter, integerFormatter } from "@/app/utils/formatUtil"; import { fetchPoListClient } from "@/app/api/po/actions"; import { List, ListItem, ListItemButton, ListItemText, Divider } from "@mui/material"; -import { useRouter } from "next/navigation"; +//import { useRouter } from "next/navigation"; @@ -105,7 +107,7 @@ const PoSearchList: React.FC<{ }, [poList, searchTerm, t]); return ( - + {t("Purchase Orders")} @@ -125,14 +127,15 @@ const PoSearchList: React.FC<{ ), }} /> - + {filteredPoList.map((poItem, index) => (
- + onSelect(poItem)} sx={{ + width: '100%', "&.Mui-selected": { backgroundColor: "primary.light", "&:hover": { @@ -143,7 +146,7 @@ const PoSearchList: React.FC<{ > + {poItem.code} } @@ -191,42 +194,41 @@ const PoDetail: React.FC = ({ po, qc, warehouse }) => { - const router = useRouter(); + //const router = useRouter(); const [poList, setPoList] = useState([]); const [selectedPoId, setSelectedPoId] = useState(po.id); const currentPoId = searchParams.get('id'); - const [searchTerm, setSearchTerm] = useState(''); + const selectedIdsParam = searchParams.get('selectedIds'); - - const filteredPoList = useMemo(() => { - if (searchTerm.trim() === '') { - return poList; - } - return poList.filter(poItem => - poItem.code.toLowerCase().includes(searchTerm.toLowerCase()) || - poItem.supplier?.toLowerCase().includes(searchTerm.toLowerCase()) || - t(`${poItem.status.toLowerCase()}`).toLowerCase().includes(searchTerm.toLowerCase()) - ); - }, [poList, searchTerm, t]); - const fetchPoList = useCallback(async () => { try { - const result = await fetchPoListClient({ limit: 20, offset: 0 }); - if (result && result.records) { - setPoList(result.records); + if (selectedIdsParam) { + + const selectedIds = selectedIdsParam.split(',').map(id => parseInt(id)); + const promises = selectedIds.map(id => fetchPoInClient(id)); + const results = await Promise.all(promises); + setPoList(results.filter(Boolean)); + } else { + + const result = await fetchPoListClient({ limit: 20, offset: 0 }); + if (result && result.records) { + setPoList(result.records); + } } } catch (error) { console.error("Failed to fetch PO list:", error); } - }, []); + }, [selectedIdsParam]); const handlePoSelect = useCallback( (selectedPo: PoResult) => { setSelectedPoId(selectedPo.id); - router.push(`/po/edit?id=${selectedPo.id}&start=true`, { scroll: false }); + + const newSelectedIds = selectedIdsParam || selectedPo.id.toString(); + router.push(`/po/edit?id=${selectedPo.id}&start=true&selectedIds=${newSelectedIds}`, { scroll: false }); }, - [router] + [router, selectedIdsParam] ); const fetchPoDetail = useCallback(async (poId: string) => { @@ -511,9 +513,9 @@ const PoDetail: React.FC = ({ po, qc, warehouse }) => { {/* area2: dn info */} - + {/* left side select po */} - + = ({ po, qc, warehouse }) => { {/* right side po info */} - + {true ? ( diff --git a/src/components/PoSearch/PoSearch.tsx b/src/components/PoSearch/PoSearch.tsx index 6876a74..c930f63 100644 --- a/src/components/PoSearch/PoSearch.tsx +++ b/src/components/PoSearch/PoSearch.tsx @@ -17,6 +17,7 @@ import { fetchPoListClient, testing } from "@/app/api/po/actions"; import dayjs from "dayjs"; import { arrayToDateString, OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; import arraySupport from "dayjs/plugin/arraySupport"; +import { Checkbox, Box } from "@mui/material"; dayjs.extend(arraySupport); type Props = { @@ -34,6 +35,8 @@ const PoSearch: React.FC = ({ warehouse, totalCount: initTotalCount, }) => { + const [selectedPoIds, setSelectedPoIds] = useState([]); + const [selectAll, setSelectAll] = useState(false); const [filteredPo, setFilteredPo] = useState(po); const [filterArgs, setFilterArgs] = useState>({}); const { t } = useTranslation("purchaseOrder"); @@ -44,7 +47,8 @@ const PoSearch: React.FC = ({ const [totalCount, setTotalCount] = useState(initTotalCount); const searchCriteria: Criterion[] = useMemo(() => { const searchCriteria: Criterion[] = [ - { label: t("Po No."), paramName: "code", type: "text" }, + { label: t("Po No."), paramName: "code", type: "text" }, + { label: t("Supplier"), paramName: "supplier", type: "text" }, { label: t("Order Date"), label2: t("Order Date To"), paramName: "orderDate", type: "dateRange" }, { label: t("Escalated"), @@ -69,15 +73,57 @@ const PoSearch: React.FC = ({ const onDetailClick = useCallback( (po: PoResult) => { - router.push(`/po/edit?id=${po.id}&start=true`); + setSelectedPoIds([]); + setSelectAll(false); + router.push(`/po/edit?id=${po.id}&start=true&selectedIds=${po.id}`); }, [router], ); const onDeleteClick = useCallback((po: PoResult) => {}, []); + // handle single checkbox selection + const handleSelectPo = useCallback((poId: number, checked: boolean) => { + if (checked) { + setSelectedPoIds(prev => [...prev, poId]); + } else { + setSelectedPoIds(prev => prev.filter(id => id !== poId)); + } + }, []); + + // 处理全选 + const handleSelectAll = useCallback((checked: boolean) => { + if (checked) { + setSelectedPoIds(filteredPo.map(po => po.id)); + setSelectAll(true); + } else { + setSelectedPoIds([]); + setSelectAll(false); + } + }, [filteredPo]); + + // navigate to PoDetail page + const handleGoToPoDetail = useCallback(() => { + if (selectedPoIds.length > 0) { + const selectedIdsParam = selectedPoIds.join(','); + const firstPoId = selectedPoIds[0]; + router.push(`/po/edit?id=${firstPoId}&start=true&selectedIds=${selectedIdsParam}`); + } + }, [selectedPoIds, router]); const columns = useMemo[]>( () => [ + { + name: "id" as keyof PoResult, + label: "Select", + renderCell: (params) => ( + handleSelectPo(params.id, e.target.checked)} + onClick={(e) => e.stopPropagation()} + /> + ), + width: 60, + }, { name: "id", label: t("Details"), @@ -132,7 +178,7 @@ const PoSearch: React.FC = ({ }, }, ], - [filteredPo], + [selectedPoIds, handleSelectPo, onDetailClick, t], // only keep necessary dependencies ); const onReset = useCallback(() => { @@ -177,6 +223,14 @@ const PoSearch: React.FC = ({ useEffect(() => { newPageFetch(pagingController, filterArgs); }, [newPageFetch, pagingController, filterArgs]); + // when filteredPo changes, update select all state + useEffect(() => { + if (filteredPo.length > 0 && selectedPoIds.length === filteredPo.length) { + setSelectAll(true); + } else { + setSelectAll(false); + } + }, [filteredPo, selectedPoIds]); return ( <> @@ -201,6 +255,7 @@ const PoSearch: React.FC = ({ console.log(query); setFilterArgs({ code: query.code, + supplier: query.supplier, status: query.status === "All" ? "" : query.status, escalated: query.escalated === "All" @@ -211,15 +266,8 @@ const PoSearch: React.FC = ({ orderDate: query.orderDate === "Invalid Date" ? "" : query.orderDate, orderDateTo: query.orderDateTo === "Invalid Date" ? "" : query.orderDateTo, }); - // setFilteredPo((prev) => - // po.filter((p) => { - // return ( - // p.code.toLowerCase().includes(query.code.toLowerCase()) && - // (query.status === "All" || t(`${p.status.toLowerCase()}`) === query.status) && - // (query.escalated === "All" || (p.escalated === Boolean((query.escalated) === t("Escalated")))) - // ) - // }) - // ); + setSelectedPoIds([]); // reset selected po ids + setSelectAll(false); // reset select all }} onReset={onReset} /> @@ -231,6 +279,24 @@ const PoSearch: React.FC = ({ totalCount={totalCount} isAutoPaging={false} /> + {/* add select all and view selected button */} + + + + ); diff --git a/src/components/SearchBox/SearchBox.tsx b/src/components/SearchBox/SearchBox.tsx index 59c5d5d..df1f533 100644 --- a/src/components/SearchBox/SearchBox.tsx +++ b/src/components/SearchBox/SearchBox.tsx @@ -433,7 +433,7 @@ function SearchBox({ ); })} - +