From a85b1f9b32edad31ffe9c7fe0c552f5f10d28516 Mon Sep 17 00:00:00 2001 From: "cyril.tsui" Date: Mon, 16 Jun 2025 13:23:41 +0800 Subject: [PATCH] add delivery order --- src/app/(main)/do/edit/page.tsx | 0 src/app/(main)/do/page.tsx | 33 +++++ src/app/api/do/actions.tsx | 0 src/app/api/do/index.tsx | 21 ++++ src/components/Breadcrumb/Breadcrumb.tsx | 1 + src/components/DoSave/DoSaveWrapper.tsx | 0 src/components/DoSave/index.ts | 1 + src/components/DoSearch/DoSearch.tsx | 115 ++++++++++++++++++ src/components/DoSearch/DoSearchWrapper.tsx | 22 ++++ src/components/DoSearch/index.ts | 1 + .../NavigationContent/NavigationContent.tsx | 2 +- src/components/SearchBox/SearchBox.tsx | 2 +- 12 files changed, 196 insertions(+), 2 deletions(-) create mode 100644 src/app/(main)/do/edit/page.tsx create mode 100644 src/app/(main)/do/page.tsx create mode 100644 src/app/api/do/actions.tsx create mode 100644 src/app/api/do/index.tsx create mode 100644 src/components/DoSave/DoSaveWrapper.tsx create mode 100644 src/components/DoSave/index.ts create mode 100644 src/components/DoSearch/DoSearch.tsx create mode 100644 src/components/DoSearch/DoSearchWrapper.tsx create mode 100644 src/components/DoSearch/index.ts diff --git a/src/app/(main)/do/edit/page.tsx b/src/app/(main)/do/edit/page.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/app/(main)/do/page.tsx b/src/app/(main)/do/page.tsx new file mode 100644 index 0000000..3ec9163 --- /dev/null +++ b/src/app/(main)/do/page.tsx @@ -0,0 +1,33 @@ +import DoSearch from "@/components/DoSearch"; +import { getServerI18n } from "@/i18n" +import { Stack, Typography } from "@mui/material"; +import { Metadata } from "next"; +import { Suspense } from "react"; + +export const metadata: Metadata = { + title: "Delivery Order" +} + +const DeliveryOrder: React.FC = async () => { + const { t } = await getServerI18n("do"); + + return ( + <> + + + {t("Delivery Order")} + + + }> + + + + ) +} + +export default DeliveryOrder; \ No newline at end of file diff --git a/src/app/api/do/actions.tsx b/src/app/api/do/actions.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/app/api/do/index.tsx b/src/app/api/do/index.tsx new file mode 100644 index 0000000..31494a8 --- /dev/null +++ b/src/app/api/do/index.tsx @@ -0,0 +1,21 @@ +import { serverFetchJson } from "@/app/utils/fetchUtil"; +import { BASE_API_URL } from "@/config/api"; +import { cache } from "react"; + +export interface DoResult { + id: number, + code: string, + orderDate: string, + status: string, + shopName: string, +} + +export const preloadDo = () => { + fetchDoList(); +} + +export const fetchDoList = cache(async () => { + return serverFetchJson(`${BASE_API_URL}/do/list`, { + next: { tags: ["doList"] } + }) +}) \ No newline at end of file diff --git a/src/components/Breadcrumb/Breadcrumb.tsx b/src/components/Breadcrumb/Breadcrumb.tsx index 863c5e2..3539937 100644 --- a/src/components/Breadcrumb/Breadcrumb.tsx +++ b/src/components/Breadcrumb/Breadcrumb.tsx @@ -20,6 +20,7 @@ const pathToLabelMap: { [path: string]: string } = { "/scheduling/detail/edit": "FG Production Schedule", "/inventory": "Inventory", "/settings/importTesting": "Import Testing", + "/do": "Delivery Order", }; const Breadcrumb = () => { diff --git a/src/components/DoSave/DoSaveWrapper.tsx b/src/components/DoSave/DoSaveWrapper.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/DoSave/index.ts b/src/components/DoSave/index.ts new file mode 100644 index 0000000..2800028 --- /dev/null +++ b/src/components/DoSave/index.ts @@ -0,0 +1 @@ +export default from "./DoSaveWrapper" \ No newline at end of file diff --git a/src/components/DoSearch/DoSearch.tsx b/src/components/DoSearch/DoSearch.tsx new file mode 100644 index 0000000..8230620 --- /dev/null +++ b/src/components/DoSearch/DoSearch.tsx @@ -0,0 +1,115 @@ +"use client"; + +import { DoResult } from "@/app/api/do" +import { useRouter } from "next/navigation"; +import { useCallback, 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"; + +type Props = { + dos: DoResult[] +} + +type SearchQuery = Partial>; +type SearchParamNames = keyof SearchQuery; + +const DoSearch: React.FC = ({ + dos +}) => { + + const [filteredDos, setFilteredDos] = useState(dos); + const { t } = useTranslation("do"); + const router = useRouter(); + + 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 onReset = useCallback(() => { + setFilteredDos(dos) + }, [dos]) + + const onDetailClick = useCallback( + (doResult: DoResult) => { + router.push(`/do/edit?id=${doResult.id}`); + }, + [router] + ); + + + const columns = useMemo[]>(() => [ + { + name: "id", + label: t("Details"), + onClick: onDetailClick, + buttonIcon: , + }, + { + name: "code", + label: t("Code") + }, + { + name: "shopName", + label: t("Shop Name") + }, + { + name: "orderDate", + label: t("Order Date"), + renderCell: (params) => { + return params.orderDate ? arrayToDateString(params.orderDate) : "N/A" + }, + }, + { + name: "status", + label: t("Status"), + renderCell: (params) => { + return t(upperFirst(params.status)) + }, + }, + ], [t]) + + return ( + <> + { + 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} + /> + items={filteredDos} columns={columns} pagingController={{ + pageNum: 0, + pageSize: 0, + totalCount: 0, + }} /> + + ) +} + +export default DoSearch; \ No newline at end of file diff --git a/src/components/DoSearch/DoSearchWrapper.tsx b/src/components/DoSearch/DoSearchWrapper.tsx new file mode 100644 index 0000000..4cf3631 --- /dev/null +++ b/src/components/DoSearch/DoSearchWrapper.tsx @@ -0,0 +1,22 @@ +import React from "react"; +import GeneralLoading from "../General/GeneralLoading" +import { fetchDoList } from "@/app/api/do"; +import DoSearch from "./DoSearch"; + +interface SubComponents { + Loading: typeof GeneralLoading; +} + +const DoSearchWrapper: React.FC & SubComponents = async () => { + + const [ + dos + ] = await Promise.all([ + fetchDoList() + ]); + return +} + +DoSearchWrapper.Loading = GeneralLoading; + +export default DoSearchWrapper; \ No newline at end of file diff --git a/src/components/DoSearch/index.ts b/src/components/DoSearch/index.ts new file mode 100644 index 0000000..4cb8a1d --- /dev/null +++ b/src/components/DoSearch/index.ts @@ -0,0 +1 @@ +export { default } from "./DoSearchWrapper" \ No newline at end of file diff --git a/src/components/NavigationContent/NavigationContent.tsx b/src/components/NavigationContent/NavigationContent.tsx index 29fdf4e..4817500 100644 --- a/src/components/NavigationContent/NavigationContent.tsx +++ b/src/components/NavigationContent/NavigationContent.tsx @@ -128,7 +128,7 @@ const NavigationContent: React.FC = () => { { icon: , label: "Delivery Order", - path: "", + path: "/do", }, ], }, diff --git a/src/components/SearchBox/SearchBox.tsx b/src/components/SearchBox/SearchBox.tsx index a0f0e54..7cb767c 100644 --- a/src/components/SearchBox/SearchBox.tsx +++ b/src/components/SearchBox/SearchBox.tsx @@ -210,7 +210,7 @@ function SearchBox({ {c.type === "autocomplete" && ( option.group) + c.options.filter((option) => option.group !== "All").length > 0 && c.options.every((option) => option.group) ? (option) => (option.group && option.group.trim() !== '' ? option.group : 'Ungrouped') : undefined }