From 3b2495744f5634a21aaa9149624abc01d04dbd67 Mon Sep 17 00:00:00 2001 From: "CANCERYS\\kw093" Date: Tue, 2 Sep 2025 20:20:45 +0800 Subject: [PATCH] update --- src/app/api/settings/item/actions.ts | 28 ++ .../PickOrderSearch/PickOrderSearch.tsx | 173 ++++-------- .../PickOrderSearch/newcreatitem.tsx | 258 +++++++++++------- 3 files changed, 241 insertions(+), 218 deletions(-) diff --git a/src/app/api/settings/item/actions.ts b/src/app/api/settings/item/actions.ts index 6682e16..154f14c 100644 --- a/src/app/api/settings/item/actions.ts +++ b/src/app/api/settings/item/actions.ts @@ -61,6 +61,34 @@ export interface ItemCombo { group?: string, currentStockBalance?: number, } +export interface ItemWithDetails { + id: number; + code: string; + name: string; + description?: string; + uomId: number; + uom: string; + uomDesc: string; + currentStockBalance: number; +} +export const fetchItemsWithDetails = cache(async (searchParams?: Record) => { + if (searchParams) { + const queryString = new URLSearchParams(searchParams).toString(); + return serverFetchJson>( + `${BASE_API_URL}/items/itemsWithDetails?${queryString}`, + { + next: { tags: ["items"] }, + } + ); + } else { + return serverFetchJson>( + `${BASE_API_URL}/items/itemsWithDetails`, + { + next: { tags: ["items"] }, + } + ); + } +}); export const fetchAllItemsInClient = cache(async () => { return serverFetchJson(`${BASE_API_URL}/items/consumables`, { diff --git a/src/components/PickOrderSearch/PickOrderSearch.tsx b/src/components/PickOrderSearch/PickOrderSearch.tsx index 943b681..e7dfb7d 100644 --- a/src/components/PickOrderSearch/PickOrderSearch.tsx +++ b/src/components/PickOrderSearch/PickOrderSearch.tsx @@ -15,7 +15,7 @@ import { import { arrayToDayjs, } from "@/app/utils/formatUtil"; -import { Button, Grid, Stack, Tab, Tabs, TabsProps, Typography } from "@mui/material"; +import { Button, Grid, Stack, Tab, Tabs, TabsProps, Typography, Box } from "@mui/material"; import PickOrders from "./PickOrders"; import ConsolidatedPickOrders from "./ConsolidatedPickOrders"; import PickExecution from "./PickExecution"; @@ -232,121 +232,68 @@ const PickOrderSearch: React.FC = ({ pickOrders }) => { }, []); return ( - <> - - - - - {t("Pick Order")} - + + {/* Header section */} + + + + + + {t("Pick Order")} + + + + + {isOpenCreateModal && + + } + - - - {isOpenCreateModal && - } - - - - {/* - { - console.log("SearchBox search triggered with query:", query); - setSearchQuery({ ...query }); - - // when tabIndex === 3, do not execute any search logic, only store query conditions - if (tabIndex !== 3) { - // only execute search logic when other tabs - setFilterArgs({ ...query }); - setFilteredPickOrders( - pickOrders.filter((po) => { - const poTargetDateStr = arrayToDayjs(po.targetDate); - - // safely check search conditions - const codeMatch = !query.code || - po.code.toLowerCase().includes((query.code || "").toLowerCase()); - - const dateMatch = !query.targetDate || - poTargetDateStr.isSame(query.targetDate) || - poTargetDateStr.isAfter(query.targetDate); - - const dateToMatch = !query.targetDateTo || - poTargetDateStr.isSame(query.targetDateTo) || - poTargetDateStr.isBefore(query.targetDateTo); - - const itemsMatch = !query.items || - Array.isArray(query.items) && ( - intersectionWith(["All"], query.items).length > 0 || - intersectionWith( - po.items?.map((item) => item.name) || [], - query.items, - ).length > 0 - ); - - const statusMatch = !query.status || - query.status.toLowerCase() === "all" || - po.status.toLowerCase().includes((query.status || "").toLowerCase()); - - const typeMatch = !query.type || - query.type.toLowerCase() === "all" || - po.type.toLowerCase().includes((query.type || "").toLowerCase()); + + - return codeMatch && dateMatch && dateToMatch && itemsMatch && statusMatch && typeMatch; - }), - ); - } - // when tabIndex === 3, SearchBox's search will not trigger any filtering, only pass data to NewCreateItem - }} - onReset={() => { - console.log("SearchBox reset triggered"); - setSearchQuery({}); - if (tabIndex !== 3) { - onReset(); - } - }} - /> - */} - + {/* Tabs section */} + + + + + + + + + - - - - - - - - {/**/} - {/**/} - - {/*{tabIndex === 4 && ( - - )}*/} - {/*{tabIndex === 5 && }*/} - {tabIndex === 4 && } - {tabIndex === 0 && ( - - )} - {tabIndex === 1 && } - {tabIndex === 2&& } - {tabIndex === 3 && } - + {/* Content section - NO overflow: 'auto' here */} + + {tabIndex === 4 && } + {tabIndex === 0 && ( + + )} + {tabIndex === 1 && } + {tabIndex === 2 && } + {tabIndex === 3 && } + + ); }; diff --git a/src/components/PickOrderSearch/newcreatitem.tsx b/src/components/PickOrderSearch/newcreatitem.tsx index 0ef22cb..68d4ff3 100644 --- a/src/components/PickOrderSearch/newcreatitem.tsx +++ b/src/components/PickOrderSearch/newcreatitem.tsx @@ -32,7 +32,7 @@ import { DatePicker, LocalizationProvider } from "@mui/x-date-pickers"; import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; import dayjs from "dayjs"; import { Check, Search, RestartAlt } from "@mui/icons-material"; -import { ItemCombo, fetchAllItemsInClient } from "@/app/api/settings/item/actions"; +import { ItemCombo, fetchAllItemsInClient,fetchItemsWithDetails } from "@/app/api/settings/item/actions"; import { INPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; import SearchResults, { Column } from "../SearchResults/SearchResults"; import { fetchJobOrderDetailByCode } from "@/app/api/jo/actions"; @@ -40,6 +40,7 @@ import SearchBox, { Criterion } from "../SearchBox"; import VerticalSearchBox from "./VerticalSearchBox"; import SearchResultsTable from './SearchResultsTable'; import CreatedItemsTable from './CreatedItemsTable'; + type Props = { filterArgs?: Record; searchQuery?: Record; @@ -133,9 +134,22 @@ const NewCreateItem: React.FC = ({ filterArgs, searchQuery, onPickOrderCr useEffect(() => { const loadItems = async () => { try { - const itemsData = await fetchAllItemsInClient(); + // Change from fetchAllItemsInClient to fetchItemsWithDetails + const itemsData = await fetchItemsWithDetails(); console.log("Loaded items:", itemsData); - setItems(itemsData); + + // Transform the data to match the expected ItemCombo format + // Fix: Access the records property correctly + const transformedItems: ItemCombo[] = (itemsData as any).records?.map((item: any) => ({ + id: item.id, + label: `${item.code} - ${item.name}`, + uomId: item.uomId, + uom: item.uom, + uomDesc: item.uomDesc, + currentStockBalance: item.currentStockBalance + })) || []; + + setItems(transformedItems); setFilteredItems([]); } catch (error) { console.error("Error loading items:", error); @@ -159,9 +173,10 @@ const NewCreateItem: React.FC = ({ filterArgs, searchQuery, onPickOrderCr qty: item.reqQty, uom: item.uom, uomId: 0, - uomDesc: item.uomDesc, // Add missing uomDesc + uomDesc: item.uomDesc || "", // Add missing uomDesc jobOrderCode: jobOrderDetail.code, jobOrderId: jobOrderDetail.id, + currentStockBalance: 0, // Add default value })); setFilteredItems(convertedItems); @@ -221,46 +236,8 @@ const NewCreateItem: React.FC = ({ filterArgs, searchQuery, onPickOrderCr [formProps], ); - const handleSearch = useCallback(() => { - - if (!searchCode && !searchName) { - alert(t("Please enter at least code or name")); - return; - } - - setIsLoading(true); - setHasSearched(true); - - console.log("Searching with:", { type, searchCode, searchName, targetDate, itemsCount: items.length }); - - setTimeout(() => { - let filtered = items; - - if (searchCode && searchCode.trim()) { - filtered = filtered.filter(item => - item.label.toLowerCase().includes(searchCode.toLowerCase()) - ); - console.log("After code filter:", filtered.length); - } - - if (searchName && searchName.trim()) { - filtered = filtered.filter(item => - item.label.toLowerCase().includes(searchName.toLowerCase()) - ); - console.log("After name filter:", filtered.length); - } - - // Convert to SearchItemWithQty with default qty = null and include targetDate - const filteredWithQty = filtered.slice(0, 100).map(item => ({ - ...item, - qty: null, - targetDate: targetDate, // Add target date to each item - })); - console.log("Final filtered results:", filteredWithQty.length); - setFilteredItems(filteredWithQty); - setIsLoading(false); - }, 500); - }, [type, searchCode, searchName, targetDate, items, t]); // Add targetDate back to dependencies + // Remove the duplicate handleSearch function and keep only this one + // Handle quantity change in search results const handleSearchQtyChange = useCallback((itemId: number, newQty: number | null) => { @@ -508,33 +485,78 @@ const handleQtyBlur = useCallback((itemId: number) => { }, 0); }, [groups, checkAndAutoAddItem]); // 5) 选中新增的待选项:依然按“当前 Group”赋 groupId + targetDate(新加入的应随 Group) - const handleSecondSearchItemSelect = useCallback((itemId: number, isSelected: boolean) => { - if (!isSelected) return; - const item = secondSearchResults.find(i => i.id === itemId); - if (!item) return; - const exists = createdItems.find(c => c.itemId === item.id); - if (exists) { alert(t("Item already exists in created items")); return; } - - // 找到项目所属的组,使用该组的 targetDate - const itemGroup = groups.find(g => g.id === item.groupId); - const itemTargetDate = itemGroup?.targetDate || item.targetDate || targetDate; - - const newCreatedItem: CreatedItem = { - itemId: item.id, - itemName: item.label, - itemCode: item.label, - qty: item.qty || 1, - uom: item.uom || "", - uomId: item.uomId || 0, - uomDesc: item.uomDesc || "", - isSelected: true, - currentStockBalance: item.currentStockBalance, - targetDate: itemTargetDate, // 使用项目所属组的 targetDate - groupId: item.groupId || undefined, // 使用项目自身的 groupId - }; - setCreatedItems(prev => [...prev, newCreatedItem]); - }, [secondSearchResults, createdItems, groups, targetDate, t]); - + const handleSearch = useCallback(() => { + if (!searchCode && !searchName) { + alert(t("Please enter at least code or name")); + return; + } + + setIsLoading(true); + setHasSearched(true); + + console.log("Searching with:", { type, searchCode, searchName, targetDate }); + + // Use the new API with search parameters + const searchParams: Record = {}; + + if (searchCode && searchCode.trim()) { + searchParams.code = searchCode.trim(); + } + + if (searchName && searchName.trim()) { + searchParams.name = searchName.trim(); + } + + if (type && type.trim()) { + searchParams.type = type.trim(); + } + + // Add pagination parameters + searchParams.pageSize = 100; + searchParams.pageNum = 1; + + // Fetch items using the new API + fetchItemsWithDetails(searchParams) + .then(response => { + try { + // Fix: Handle the response type correctly and safely + let itemsToTransform: any[] = []; + + // Safely check and extract data from response + if (response && typeof response === 'object') { + if ('records' in response && Array.isArray((response as any).records)) { + itemsToTransform = (response as any).records; + } else if (Array.isArray(response)) { + itemsToTransform = response; + } + } + + const transformedItems: SearchItemWithQty[] = itemsToTransform.map((item: any) => ({ + id: item.id, + label: `${item.code} - ${item.name}`, + uomId: item.uomId, + uom: item.uom, + uomDesc: item.uomDesc, + currentStockBalance: item.currentStockBalance, + qty: null, + targetDate: targetDate, + })); + + console.log("Search results:", transformedItems.length); + setFilteredItems(transformedItems); + } catch (error) { + console.error("Error processing response:", error); + setFilteredItems([]); + } finally { + setIsLoading(false); + } + }) + .catch(error => { + console.error("Error searching items:", error); + setFilteredItems([]); + setIsLoading(false); + }); + }, [type, searchCode, searchName, targetDate, t]); // 修改提交函数,按组分别创建提料单 const onSubmit = useCallback>( async (data, event) => { @@ -1052,8 +1074,6 @@ const handleQtyBlur = useCallback((itemId: number) => { // 修改搜索条件为3行,每行一个 - 确保SearchBox组件能正确处理 const pickOrderSearchCriteria: Criterion[] = useMemo( () => [ - - { label: t("Item Code"), paramName: "code", @@ -1258,7 +1278,6 @@ const handleQtyBlur = useCallback((itemId: number) => { // Sync second search box info to form - ensure type value is correct if (query.type) { - // Ensure type value matches backend enum format let correctType = query.type; if (query.type === "consumable") { correctType = "Consumable"; @@ -1270,39 +1289,68 @@ const handleQtyBlur = useCallback((itemId: number) => { formProps.setValue("type", correctType); } - setTimeout(() => { - let filtered = items; - - // Same filtering logic as first search - if (query.code && query.code.trim()) { - filtered = filtered.filter(item => - item.label.toLowerCase().includes(query.code.toLowerCase()) - ); - } - - if (query.name && query.name.trim()) { - filtered = filtered.filter(item => - item.label.toLowerCase().includes(query.name.toLowerCase()) - ); - } - - if (query.type && query.type !== "All") { - // Filter by type if needed + // Build search parameters for the new API + const searchParams: Record = {}; + + if (query.code && query.code.trim()) { + searchParams.code = query.code.trim(); + } + + if (query.name && query.name.trim()) { + searchParams.name = query.name.trim(); + } + + if (query.type && query.type !== "All") { + searchParams.type = query.type; + } + + // Add pagination parameters + searchParams.pageSize = 100; + searchParams.pageNum = 1; + + // Use the new API + fetchItemsWithDetails(searchParams) + .then(response => { + try { + // Fix: Handle the response type correctly and safely + let itemsToTransform: any[] = []; + + // Safely check and extract data from response + if (response && typeof response === 'object') { + if ('records' in response && Array.isArray((response as any).records)) { + itemsToTransform = (response as any).records; + } else if (Array.isArray(response)) { + itemsToTransform = response; + } + } + + const transformedItems: SearchItemWithQty[] = itemsToTransform.map((item: any) => ({ + id: item.id, + label: `${item.code} - ${item.name}`, + uomId: item.uomId, + uom: item.uom, + uomDesc: item.uomDesc, + currentStockBalance: item.currentStockBalance, + qty: null, + targetDate: undefined, + groupId: undefined, + })); + + setSecondSearchResults(transformedItems); + setHasSearchedSecond(true); + } catch (error) { + console.error("Error processing response:", error); + setSecondSearchResults([]); + } finally { + setIsLoadingSecondSearch(false); } - - // Convert to SearchItemWithQty with NO group/targetDate initially - const filteredWithQty = filtered.slice(0, 100).map(item => ({ - ...item, - qty: null, - targetDate: undefined, // No target date initially - groupId: undefined, // No group initially - })); - - setSecondSearchResults(filteredWithQty); - setHasSearchedSecond(true); + }) + .catch(error => { + console.error("Error in second search:", error); + setSecondSearchResults([]); setIsLoadingSecondSearch(false); - }, 500); - }, [items, formProps]); + }); + }, [formProps, t]); /* // Create a custom search box component that displays fields vertically const VerticalSearchBox = ({ criteria, onSearch, onReset }: {