From aae7dbdd03bf6edb8d3f3319e97ae06eb26671f1 Mon Sep 17 00:00:00 2001 From: "kelvin.yau" Date: Wed, 17 Sep 2025 14:58:57 +0800 Subject: [PATCH] DO search, pagination, translation update --- src/app/api/do/actions.tsx | 24 ++- src/app/api/do/index.tsx | 15 ++ src/components/DoSearch/DoSearch.tsx | 266 +++++++++++++++++++-------- src/i18n/zh/do.json | 12 +- 4 files changed, 235 insertions(+), 82 deletions(-) diff --git a/src/app/api/do/actions.tsx b/src/app/api/do/actions.tsx index ec7e421..09bed8e 100644 --- a/src/app/api/do/actions.tsx +++ b/src/app/api/do/actions.tsx @@ -34,6 +34,18 @@ export interface DoDetailLine { itemName?: string; uomCode?: string; } + +export interface DoSearchAll { + id: number; + code: string; + status: string; + estimatedArrivalDate: string; + orderDate: string; + supplierName: string; + shopName: string; + deliveryOrderLines: DoDetailLine[]; +} + export interface ReleaseDoRequest { id: number; } @@ -68,4 +80,14 @@ export const fetchDoDetail = cache(async (id: number) => { tags: ["doDetail"] } }); -}); \ No newline at end of file +}); + +export const fetchDoSearch = cache(async (code: string, shopName: string, status: string, orderStartDate: string, orderEndDate: string, estArrStartDate: string, estArrEndDate: string)=>{ + console.log(`${BASE_API_URL}/do/search-DO/${code}&${shopName}&${status}&${orderStartDate}&${orderEndDate}&${estArrStartDate}&${estArrEndDate}`); + return serverFetchJson(`${BASE_API_URL}/do/search-DO/${code}&${shopName}&${status}&${orderStartDate}&${orderEndDate}&${estArrStartDate}&${estArrEndDate}`,{ + method: "GET", + next: { tags: ["doSearch"] } + }); + +}); + diff --git a/src/app/api/do/index.tsx b/src/app/api/do/index.tsx index abef21d..4aa1bc5 100644 --- a/src/app/api/do/index.tsx +++ b/src/app/api/do/index.tsx @@ -13,6 +13,19 @@ export interface DoResult { shopName: string; } +export interface DoDetailLine { + id: number; + itemNo: string; + qty: number; + price: number; + status: string; + itemName?: string; + uomCode?: string; +} + + + + export const preloadDo = () => { fetchDoList(); }; @@ -22,3 +35,5 @@ export const fetchDoList = cache(async () => { next: { tags: ["doList"] }, }); }); + + diff --git a/src/components/DoSearch/DoSearch.tsx b/src/components/DoSearch/DoSearch.tsx index d855f94..e690584 100644 --- a/src/components/DoSearch/DoSearch.tsx +++ b/src/components/DoSearch/DoSearch.tsx @@ -1,15 +1,14 @@ "use client"; import { DoResult } from "@/app/api/do"; +import { DoSearchAll, fetchDoSearch } from "@/app/api/do/actions"; import { useRouter } from "next/navigation"; -import { useCallback, useMemo, useState } from "react"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { Criterion } from "../SearchBox"; import { isEmpty, sortBy, uniqBy, upperFirst } from "lodash"; -import { Column } from "../SearchResults"; import { arrayToDateString, arrayToDayjs } from "@/app/utils/formatUtil"; import SearchBox from "../SearchBox/SearchBox"; -import SearchResults from "../SearchResults/SearchResults"; import { EditNote } from "@mui/icons-material"; import InputDataGrid from "../InputDataGrid"; import { CreateConsoDoInput } from "@/app/api/do/actions"; @@ -26,17 +25,22 @@ import { SubmitErrorHandler, SubmitHandler, useForm, -} from "react-hook-form"; +} from "react-hook-form"; import { Box, Button, Grid, Stack, Typography } from "@mui/material"; import StyledDataGrid from "../StyledDataGrid"; import { GridRowSelectionModel } from "@mui/x-data-grid"; +import { TablePagination } from "@mui/material"; + + type Props = { - dos: DoResult[]; + filterArgs?: Record; + searchQuery?: Record; + onDeliveryOrderSearch: () => void; }; +type SearchBoxInputs = Record<"code" | "status" | "estimatedArrivalDate" | "orderDate" | "supplierName" | "shopName" | "deliveryOrderLines" | "codeTo" | "statusTo" | "estimatedArrivalDateTo" | "orderDateTo" | "supplierNameTo" | "shopNameTo" | "deliveryOrderLinesTo" , string>; +type SearchParamNames = keyof SearchBoxInputs; -type SearchQuery = Partial>; -type SearchParamNames = keyof SearchQuery; // put all this into a new component // ConsoDoForm type EntryError = @@ -46,7 +50,8 @@ type EntryError = | undefined; type DoRow = TableRow, EntryError>; -const DoSearch: React.FC = ({ dos }) => { + +const DoSearch: React.FC = ({filterArgs, searchQuery, onDeliveryOrderSearch}) => { const apiRef = useGridApiRef(); const formProps = useForm({ @@ -54,39 +59,66 @@ const DoSearch: React.FC = ({ dos }) => { }); const errors = formProps.formState.errors; - const tttt = [ - { - id: 1, - code: "string", - orderDate: "2025-01-01", - estimatedArrivalDate: "2025-01-01", - status: "string", - shopName: "string", - }, - { - id: 2, - code: "string1", - orderDate: "2025-01-01", - estimatedArrivalDate: "2025-01-01", - status: "string", - shopName: "string", - }, - { - id: 3, - code: "string2", - orderDate: "2025-01-01", - estimatedArrivalDate: "2025-01-01", - status: "string", - shopName: "string", - }, - ]; - // const [filteredDos, setFilteredDos] = useState(dos); - const [filteredDos, setFilteredDos] = useState(tttt); + const { t } = useTranslation("do"); const router = useRouter(); const [rowSelectionModel, setRowSelectionModel] = useState([]); + const [searchAllDos, setSearchAllDos] = useState([]); + + const [pagingController, setPagingController] = useState({ + pageNum: 1, + pageSize: 10, + }); + + const handlePageChange = useCallback((event: unknown, newPage: number) => { + const newPagingController = { + ...pagingController, + pageNum: newPage + 1, + }; + setPagingController(newPagingController); + },[pagingController]); + + const handlePageSizeChange = useCallback((event: React.ChangeEvent) => { + const newPageSize = parseInt(event.target.value, 10); + const newPagingController = { + pageNum: 1, + pageSize: newPageSize, + }; + setPagingController(newPagingController); + }, []); + + const pagedRows = useMemo(() => { + const start = (pagingController.pageNum - 1) * pagingController.pageSize; + return searchAllDos.slice(start, start + pagingController.pageSize); +}, [searchAllDos, pagingController]); + +useEffect(() =>{ + setPagingController(p => ({ + ...p, + pageNum: 1, + })); +}, [searchAllDos]); + + + //INITIALIZATION + useEffect(() => { + const loadItems = async () => { + try{ + const itemsData = await fetchDoSearch("","","","","","",""); + setSearchAllDos(itemsData); + } + catch (error){ + console.error("Loading Error: ", error); + setSearchAllDos([]); + }; + }; + loadItems(); + console.log("success"); + },[]); + + const searchCriteria: Criterion[] = useMemo( () => [ { label: t("Code"), paramName: "code", type: "text" }, @@ -98,28 +130,39 @@ const DoSearch: React.FC = ({ dos }) => { type: "dateRange", }, { label: t("Shop Name"), paramName: "shopName", type: "text" }, + + { + label: t("Estimated Arrival From"), + label2: t("Estimated Arrival To"), + paramName: "estimatedArrivalDate", + type: "dateRange", + }, + { label: t("Status"), paramName: "status", type: "autocomplete", - options: sortBy( - uniqBy( - dos.map((_do) => ({ - value: _do.status, - label: t(upperFirst(_do.status)), - })), - "value", - ), - "label", - ), - }, + options:[ + {label: t('Pending'), value: 'pending'}, + {label: t('Receiving'), value: 'receiving'}, + {label: t('Completed'), value: 'completed'} + ] + } ], - [t, dos], + [t], ); - const onReset = useCallback(() => { - setFilteredDos(dos); - }, [dos]); + const onReset = useCallback(async () => { + try { + const data = await fetchDoSearch("", "", "", "", "","",""); + setSearchAllDos(data); + + } + catch (error) { + console.error("Error: ", error); + setSearchAllDos([]); + } + }, []); const onDetailClick = useCallback( (doResult: DoResult) => { @@ -155,7 +198,6 @@ const DoSearch: React.FC = ({ dos }) => { // onClick: onDetailClick, // buttonIcon: , // }, - { field: "id", headerName: t("Details"), @@ -174,13 +216,18 @@ const DoSearch: React.FC = ({ dos }) => { { field: "code", headerName: t("code"), - flex: 1, + flex: 1.5, }, { field: "shopName", headerName: t("Shop Name"), flex: 1, }, + { + field: "supplierName", + headerName: t("Supplier Name"), + flex: 1, + }, { field: "orderDate", headerName: t("Order Date"), @@ -217,7 +264,6 @@ const DoSearch: React.FC = ({ dos }) => { async (data, event) => { const hasErrors = false; console.log(errors); - console.log(data); }, [], ); @@ -225,6 +271,64 @@ const DoSearch: React.FC = ({ dos }) => { (errors) => {}, [], ); + //SEARCH FUNCTION + const handleSearch = useCallback(async (query: SearchBoxInputs) => { + + try { + + let orderStartDate = query.orderDate; + let orderEndDate = query.orderDateTo; + let estArrStartDate = query.estimatedArrivalDate; + let estArrEndDate = query.estimatedArrivalDateTo; + const time = "T00:00:00"; + + if(orderStartDate != ""){ + orderStartDate = query.orderDate + time; + } + if(orderEndDate != ""){ + orderEndDate = query.orderDateTo + time; + } + if(estArrStartDate != ""){ + estArrStartDate = query.estimatedArrivalDate + time; + } + if(estArrEndDate != ""){ + estArrEndDate = query.estimatedArrivalDateTo + time; + } + + let status = ""; + if(query.status == "All"){ + status = ""; + } + else{ + status = query.status; + } + + const data = await fetchDoSearch( + query.code || "", + query.shopName || "", + status, + orderStartDate, + orderEndDate, + estArrStartDate, + estArrEndDate + ); + + console.log("Search parameters:", { + code: query.code, + shopName: query.shopName, + status: query.status, + orderStartDate, + orderEndDate, + estArrStartDate, + estArrEndDate + }); + + setSearchAllDos(data); + } catch (error) { + console.error("Error: ", error); + } + }, []); + return ( <> @@ -246,6 +350,7 @@ const DoSearch: React.FC = ({ dos }) => { justifyContent="end" alignItems="end" > + + + + + { - setFilteredDos( - dos.filter((_do) => { - const doOrderDateStr = arrayToDayjs(_do.orderDate); - return ( - _do.code.toLowerCase().includes(query.code.toLowerCase()) && - _do.shopName - .toLowerCase() - .includes(query.shopName.toLowerCase()) && - (query.status == "All" || - _do.status - .toLowerCase() - .includes(query.status.toLowerCase())) && - (isEmpty(query.orderDate) || - doOrderDateStr.isSame(query.orderDate) || - doOrderDateStr.isAfter(query.orderDate)) && - (isEmpty(query.orderDateTo) || - doOrderDateStr.isSame(query.orderDateTo) || - doOrderDateStr.isBefore(query.orderDateTo)) - ); - }), - ); - }} + + onSearch={handleSearch} + onReset={onReset} /> { setRowSelectionModel(newRowSelectionModel); @@ -297,7 +392,19 @@ const DoSearch: React.FC = ({ dos }) => { noRowsOverlay: NoRowsOverlay, }} /> + + + + ); @@ -319,4 +426,5 @@ const NoRowsOverlay: React.FC = () => { ); }; + export default DoSearch; diff --git a/src/i18n/zh/do.json b/src/i18n/zh/do.json index 879fcdd..596ad9b 100644 --- a/src/i18n/zh/do.json +++ b/src/i18n/zh/do.json @@ -6,10 +6,18 @@ "Delivery Order Status": "交貨單狀態", "Order Date": "訂單日期", "Estimated Arrival": "預計到貨日期", + "Estimated Arrival From": "預計到貨日期從", + "Estimated Arrival To": "預計到貨日期到", "Status": "來貨狀態", - "Order Date From": "訂單日期從", + "Order Date From": "訂單日期", "Order Date To": "訂單日期到", "Code": "編號", "code": "編號", - "Create": "新增" + "Create": "新增", + "Supplier Name": "供應商名稱", + "Details": "詳情", + "Pending": "待處理", + "Receiving": "接收中", + "Completed": "已完成" + } \ No newline at end of file