diff --git a/src/app/(main)/do/page.tsx b/src/app/(main)/do/page.tsx index 3ec9163..d58862d 100644 --- a/src/app/(main)/do/page.tsx +++ b/src/app/(main)/do/page.tsx @@ -19,9 +19,6 @@ const DeliveryOrder: React.FC = async () => { flexWrap={"wrap"} rowGap={2} > - - {t("Delivery Order")} - }> diff --git a/src/app/api/do/actions.tsx b/src/app/api/do/actions.tsx index e69de29..96f1717 100644 --- a/src/app/api/do/actions.tsx +++ b/src/app/api/do/actions.tsx @@ -0,0 +1,14 @@ +"use server"; +import { BASE_API_URL } from "@/config/api"; +// import { ServerFetchError, serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil"; +import { revalidateTag } from "next/cache"; +import { cache } from "react"; +import { serverFetchJson } from "@/app/utils/fetchUtil"; +import { QcItemResult } from "../settings/qcItem"; +import { RecordsRes } from "../utils"; +import { DoResult } from "."; +import { GridRowId, GridRowSelectionModel } from "@mui/x-data-grid"; + +export interface CreateConsoDoInput { + ids: GridRowSelectionModel +} diff --git a/src/app/api/do/index.tsx b/src/app/api/do/index.tsx index 31494a8..624558c 100644 --- a/src/app/api/do/index.tsx +++ b/src/app/api/do/index.tsx @@ -6,6 +6,7 @@ export interface DoResult { id: number, code: string, orderDate: string, + estimatedArrivalDate: string, status: string, shopName: string, } diff --git a/src/components/DoDetail/DoDetail.tsx b/src/components/DoDetail/DoDetail.tsx new file mode 100644 index 0000000..7ea87e2 --- /dev/null +++ b/src/components/DoDetail/DoDetail.tsx @@ -0,0 +1,3 @@ +"use client" + +// const doDetail = \ No newline at end of file diff --git a/src/components/DoSearch/DoSearch.tsx b/src/components/DoSearch/DoSearch.tsx index 8230620..db94c5b 100644 --- a/src/components/DoSearch/DoSearch.tsx +++ b/src/components/DoSearch/DoSearch.tsx @@ -1,6 +1,6 @@ "use client"; -import { DoResult } from "@/app/api/do" +import { DoResult } from "@/app/api/do"; import { useRouter } from "next/navigation"; import { useCallback, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; @@ -11,105 +11,289 @@ 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"; +import { TableRow } from "../InputDataGrid/InputDataGrid"; +import { FooterPropsOverrides, GridColDef, GridRowModel, GridToolbarContainer, useGridApiRef } from "@mui/x-data-grid"; +import { + FormProvider, + SubmitErrorHandler, + SubmitHandler, + useForm, +} from "react-hook-form"; +import { Box, Button, Grid, Stack, Typography } from "@mui/material"; +import StyledDataGrid from "../StyledDataGrid"; +import { GridRowSelectionModel } from "@mui/x-data-grid"; type Props = { - dos: DoResult[] -} + dos: DoResult[]; +}; type SearchQuery = Partial>; type SearchParamNames = keyof SearchQuery; +// put all this into a new component +// ConsoDoForm +type EntryError = + | { + [field in keyof DoResult]?: string; + } + | undefined; +type DoRow = TableRow, EntryError>; -const DoSearch: React.FC = ({ - dos -}) => { +const DoSearch: React.FC = ({ dos }) => { + const apiRef = useGridApiRef(); - const [filteredDos, setFilteredDos] = useState(dos); - const { t } = useTranslation("do"); - const router = useRouter(); + const formProps = useForm({ + defaultValues: {}, + }); + const errors = formProps.formState.errors; - const searchCriteria: Criterion[] = useMemo(() => [ - { label: t("Code"), paramName: "code", type: "text" }, - - { label: t("Order Date From"), label2: t("Order Date To"), paramName: "orderDate", type: "dateRange" }, - { label: t("Shop Name"), paramName: "shopName", type: "text" }, - { - label: t("Status"), paramName: "status", type: "autocomplete", options: sortBy( - uniqBy(dos.map((_do) => ({ value: _do.status, label: t(upperFirst(_do.status)) })), "value"), - "label") - }, - ], [t, dos]) + 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 onReset = useCallback(() => { - setFilteredDos(dos) - }, [dos]) + const searchCriteria: Criterion[] = useMemo( + () => [ + { label: t("Code"), paramName: "code", type: "text" }, - const onDetailClick = useCallback( - (doResult: DoResult) => { - router.push(`/do/edit?id=${doResult.id}`); - }, - [router] - ); + { + label: t("Order Date From"), + label2: t("Order Date To"), + paramName: "orderDate", + type: "dateRange", + }, + { label: t("Shop Name"), paramName: "shopName", type: "text" }, + { + label: t("Status"), + paramName: "status", + type: "autocomplete", + options: sortBy( + uniqBy( + dos.map((_do) => ({ + value: _do.status, + label: t(upperFirst(_do.status)), + })), + "value" + ), + "label" + ), + }, + ], + [t, dos] + ); + const onReset = useCallback(() => { + setFilteredDos(dos); + }, [dos]); - const columns = useMemo[]>(() => [ - { - name: "id", - label: t("Details"), - onClick: onDetailClick, - buttonIcon: , - }, - { - name: "code", - label: t("Code") - }, - { - name: "shopName", - label: t("Shop Name") + const onDetailClick = useCallback( + (doResult: DoResult) => { + router.push(`/do/edit?id=${doResult.id}`); + }, + [router] + ); + + const validationTest = useCallback( + ( + newRow: GridRowModel + // rowModel: GridRowSelectionModel + ): EntryError => { + const error: EntryError = {}; + console.log(newRow); + // if (!newRow.lowerLimit) { + // error["lowerLimit"] = "lower limit cannot be null" + // } + // if (newRow.lowerLimit && newRow.upperLimit && newRow.lowerLimit > newRow.upperLimit) { + // error["lowerLimit"] = "lower limit should not be greater than upper limit" + // error["upperLimit"] = "lower limit should not be greater than upper limit" + // } + return Object.keys(error).length > 0 ? error : undefined; + }, + [] + ); + + const columns = useMemo( + () => [ + // { + // name: "id", + // label: t("Details"), + // onClick: onDetailClick, + // buttonIcon: , + // }, + { + field: "code", + headerName: "code", + flex: 1, + }, + { + field: "shopName", + headerName: t("Shop Name"), + flex: 1, + }, + { + field: "orderDate", + headerName: t("Order Date"), + flex: 1, + renderCell: (params) => { + return params.row.orderDate + ? arrayToDateString(params.row.orderDate) + : "N/A"; }, - { - name: "orderDate", - label: t("Order Date"), - renderCell: (params) => { - return params.orderDate ? arrayToDateString(params.orderDate) : "N/A" - }, + }, + { + field: "estimatedArrivalDate", + headerName: t("Estimated Arrival"), + flex: 1, + renderCell: (params) => { + return params.row.estimatedArrivalDate + ? arrayToDateString(params.row.estimatedArrivalDate) + : "N/A"; }, - { - name: "status", - label: t("Status"), - renderCell: (params) => { - return t(upperFirst(params.status)) - }, + }, + { + field: "status", + headerName: t("Status"), + flex: 1, + renderCell: (params) => { + return t(upperFirst(params.row.status)); }, - ], [t]) - - return ( - <> - { - setFilteredDos( - dos.filter( - (_do) => { - const doOrderDateStr = arrayToDayjs(_do.orderDate) + }, + ], + [t, arrayToDateString] + ); - 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)) - } - ) - ) - }} - onReset={onReset} - /> - items={filteredDos} columns={columns} pagingController={{ - pageNum: 0, - pageSize: 0, - totalCount: 0, - }} /> - - ) -} + const onSubmit = useCallback>( + async (data, event) => { + let hasErrors = false; + console.log(errors); + console.log(data); + }, + [] + ); + const onSubmitError = useCallback>( + (errors) => {}, + [] + ); + return ( + <> + + + + + + {t("Delivery Order")} + + + + + + + { + 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)) + ); + }) + ); + }} + onReset={onReset} + /> + { + setRowSelectionModel(newRowSelectionModel); + formProps.setValue("ids", newRowSelectionModel) + }} + slots={{ + footer: FooterToolbar, + noRowsOverlay: NoRowsOverlay, + }} + /> + + + + ); +}; -export default DoSearch; \ No newline at end of file +const FooterToolbar: React.FC = ({ child }) => { + return {child}; +}; +const NoRowsOverlay: React.FC = () => { + const { t } = useTranslation("home"); + return ( + + {t("Add some entries!")} + + ); +}; +export default DoSearch; diff --git a/src/components/PoSearch/PoSearch.tsx b/src/components/PoSearch/PoSearch.tsx index 816c0fb..67db55c 100644 --- a/src/components/PoSearch/PoSearch.tsx +++ b/src/components/PoSearch/PoSearch.tsx @@ -48,8 +48,12 @@ const PoSearch: React.FC = ({ { label: t("Status"), paramName: "status", - type: "select", - options: [t(`pending`), t(`receiving`), t(`completed`)], + type: "select-labelled", + options: [ + {label: t(`pending`), value: `pending`}, + {label: t(`receiving`), value: `receiving`}, + {label: t(`completed`), value: `completed`}, + ] }, { label: t("Escalated"), @@ -87,7 +91,7 @@ const PoSearch: React.FC = ({ label: t("OrderDate"), renderCell: (params) => { return dayjs(params.orderDate) - .add(-1, "month") + // .add(-1, "month") .format(OUTPUT_DATE_FORMAT); }, }, @@ -106,21 +110,12 @@ const PoSearch: React.FC = ({ name: "escalated", label: t("Escalated"), renderCell: (params) => { + console.log(params.escalated) return params.escalated ? ( ) : undefined; }, }, - // { - // name: "name", - // label: t("Name"), - // }, - // { - // name: "action", - // label: t(""), - // buttonIcon: , - // onClick: onDeleteClick, - // }, ], [filteredPo] ); @@ -144,6 +139,7 @@ const PoSearch: React.FC = ({ filterArgs: Record ) => { console.log(pagingController); + console.log(filterArgs); const params = { ...pagingController, ...filterArgs, @@ -152,16 +148,16 @@ const PoSearch: React.FC = ({ // const res = await testing(params); if (res) { console.log(res); - // setFilteredPo(res.records); - // setTotalCount(res.total); + setFilteredPo(res.records); + setTotalCount(res.total); } }, [fetchPoListClient, pagingController] ); - // useEffect(() => { - // newPageFetch(pagingController, filterArgs); - // }, [newPageFetch, pagingController, filterArgs]); + useEffect(() => { + newPageFetch(pagingController, filterArgs); + }, [newPageFetch, pagingController, filterArgs]); return ( <> @@ -183,26 +179,31 @@ const PoSearch: React.FC = ({ { - // setFilterArgs({ ...query }); - 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")))) - ) - }) - ); + console.log(query) + setFilterArgs({ + code: query.code, + status: query.status === "All" ? "" : query.status, + escalated: query.escalated === 'All' ? undefined : query.escalated === t("Escalated") + }); + // 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")))) + // ) + // }) + // ); }} onReset={onReset} /> items={filteredPo} columns={columns} - // pagingController={pagingController} - // setPagingController={setPagingController} - // totalCount={totalCount} - // isAutoPaging={false} + pagingController={pagingController} + setPagingController={setPagingController} + totalCount={totalCount} + isAutoPaging={false} /> diff --git a/src/components/PoSearch/PoSearchWrapper.tsx b/src/components/PoSearch/PoSearchWrapper.tsx index b244da3..4505f2d 100644 --- a/src/components/PoSearch/PoSearchWrapper.tsx +++ b/src/components/PoSearch/PoSearchWrapper.tsx @@ -32,11 +32,11 @@ const PoSearchWrapper: React.FC & SubComponents = async ( po, warehouse, ] = await Promise.all([ - // fetchPoList({ - // "pageNum": 1, - // "pageSize": 10, - // }), - fetchPoList(), + fetchPoList({ + "pageNum": 1, + "pageSize": 10, + }), + // fetchPoList(), fetchWarehouseList(), ]); const fixPoDate = po.records.map((p) => { diff --git a/src/components/SearchBox/SearchBox.tsx b/src/components/SearchBox/SearchBox.tsx index 7cb767c..34281e5 100644 --- a/src/components/SearchBox/SearchBox.tsx +++ b/src/components/SearchBox/SearchBox.tsx @@ -34,6 +34,11 @@ interface BaseCriterion { handleSelectionChange?: (selectedOptions: T[]) => void; } +interface OptionWithLabel { + label: string, + value: any, +} + interface TextCriterion extends BaseCriterion { type: "text"; } @@ -43,6 +48,11 @@ interface SelectCriterion extends BaseCriterion { options: string[]; } +interface SelectWithLabelCriterion extends BaseCriterion { + type: "select-labelled"; + options: OptionWithLabel[]; +} + interface MultiSelectCriterion extends BaseCriterion { type: "multi-select"; options: T[]; @@ -75,6 +85,7 @@ interface DateCriterion extends BaseCriterion { export type Criterion = | TextCriterion | SelectCriterion + | SelectWithLabelCriterion | DateRangeCriterion | DateCriterion | MultiSelectCriterion @@ -102,7 +113,7 @@ function SearchBox({ (acc, c) => { return { ...acc, - [c.paramName]: (c.type === "select" || (c.type === "autocomplete" && !Boolean(c.multiple)) ? "All" + [c.paramName]: (c.type === "select" || c.type === "select-labelled" || (c.type === "autocomplete" && !Boolean(c.multiple)) ? "All" : (c.type === "autocomplete" && Boolean(c.multiple)) ? [defaultAll.value]: "") }; }, @@ -200,13 +211,30 @@ function SearchBox({ > {t("All")} {c.options.map((option) => ( - + {option} ))} )} + {c.type === "select-labelled" && ( + + {c.label} + + + )} {c.type === "autocomplete" && (