| @@ -0,0 +1,36 @@ | |||||
| import { PreloadPickOrder } from "@/app/api/pickOrder"; | |||||
| import PickOrderSearch from "@/components/PickOrderSearch"; | |||||
| import { getServerI18n } from "@/i18n"; | |||||
| import { Stack, Typography } from "@mui/material"; | |||||
| import { Metadata } from "next"; | |||||
| import { Suspense } from "react"; | |||||
| export const metadata: Metadata = { | |||||
| title: "Pick Order" | |||||
| } | |||||
| const PickOrder: React.FC = async () => { | |||||
| const { t } = await getServerI18n("pickOrder") | |||||
| PreloadPickOrder() | |||||
| return ( | |||||
| <> | |||||
| <Stack | |||||
| direction={"row"} | |||||
| justifyContent={"space-between"} | |||||
| flexWrap={"wrap"} | |||||
| rowGap={2} | |||||
| > | |||||
| <Typography variant="h4" marginInlineEnd={2}> | |||||
| {t("Pick Order")} | |||||
| </Typography> | |||||
| </Stack> | |||||
| <Suspense fallback={<PickOrderSearch.Loading />}> | |||||
| <PickOrderSearch /> | |||||
| </Suspense> | |||||
| </> | |||||
| ) | |||||
| } | |||||
| export default PickOrder; | |||||
| @@ -0,0 +1,33 @@ | |||||
| import "server-only"; | |||||
| import { serverFetchJson } from "@/app/utils/fetchUtil"; | |||||
| import { BASE_API_URL } from "@/config/api"; | |||||
| import { cache } from "react"; | |||||
| interface PickOrderItemInfo { | |||||
| name: string, | |||||
| type: string, | |||||
| } | |||||
| export interface PickOrderResult{ | |||||
| id: number, | |||||
| code: string, | |||||
| consoCode?: string, | |||||
| targetDate: string, | |||||
| completeDate?: string, | |||||
| type: string, | |||||
| status: string, | |||||
| releasedBy: string, | |||||
| items?: PickOrderItemInfo[] | null, | |||||
| } | |||||
| export const PreloadPickOrder = () => { | |||||
| fetchPickOrders() | |||||
| } | |||||
| export const fetchPickOrders = cache(async () => { | |||||
| return serverFetchJson<PickOrderResult[]>(`${BASE_API_URL}/pickOrder/list`, { | |||||
| next: { | |||||
| tags: ["pickOrders"] | |||||
| } | |||||
| }) | |||||
| }) | |||||
| @@ -113,8 +113,8 @@ const InventorySearch: React.FC<Props> = ({ | |||||
| <SearchBox | <SearchBox | ||||
| criteria={searchCriteria} | criteria={searchCriteria} | ||||
| onSearch={(query) => { | onSearch={(query) => { | ||||
| console.log(query) | |||||
| console.log(inventories) | |||||
| // console.log(query) | |||||
| // console.log(inventories) | |||||
| setFilteredInventories( | setFilteredInventories( | ||||
| inventories.filter( | inventories.filter( | ||||
| (i) => | (i) => | ||||
| @@ -52,7 +52,7 @@ const NavigationContent: React.FC = () => { | |||||
| { | { | ||||
| icon: <RequestQuote />, | icon: <RequestQuote />, | ||||
| label: "Pick Order", | label: "Pick Order", | ||||
| path: "", | |||||
| path: "/pickOrder", | |||||
| }, | }, | ||||
| { | { | ||||
| icon: <RequestQuote />, | icon: <RequestQuote />, | ||||
| @@ -0,0 +1,108 @@ | |||||
| "use client" | |||||
| import { PickOrderResult } from "@/app/api/pickOrder"; | |||||
| import { SearchParams } from "@/app/utils/fetchUtil"; | |||||
| import { useCallback, useMemo, useState } from "react"; | |||||
| import { useTranslation } from "react-i18next"; | |||||
| import SearchBox, { Criterion } from "../SearchBox"; | |||||
| import SearchResults, { Column } from "../SearchResults"; | |||||
| import { groupBy, map, sortBy, sortedUniq, uniqBy, upperCase, upperFirst } from "lodash"; | |||||
| interface Props { | |||||
| pickOrders: PickOrderResult[]; | |||||
| } | |||||
| type SearchQuery = Partial<Omit<PickOrderResult, | |||||
| | "id" | |||||
| | "consoCode" | |||||
| | "completeDate">> | |||||
| type SearchParamNames = keyof SearchQuery; | |||||
| const PickOrderSearch: React.FC<Props> = ({ | |||||
| pickOrders, | |||||
| }) => { | |||||
| const { t } = useTranslation("pickOrders"); | |||||
| const [filteredPickOrders, setFilteredPickOrders] = useState(pickOrders) | |||||
| const searchCriteria: Criterion<SearchParamNames>[] = useMemo(() => [ | |||||
| { label: t("Code"), paramName: "code", type: "text" }, | |||||
| { label: t("Target Date From"), label2: t("Target Date To"), paramName: "targetDate", type: "dateRange" }, | |||||
| { | |||||
| label: t("Type"), paramName: "type", type: "autocomplete", | |||||
| options: sortBy( | |||||
| uniqBy(pickOrders.map((po) => ({ value: po.type, label: t(upperCase(po.type)) })), "value"), | |||||
| "label").map((po) => (po)) | |||||
| }, | |||||
| { | |||||
| label: t("Status"), paramName: "status", type: "autocomplete", | |||||
| options: sortBy( | |||||
| uniqBy(pickOrders.map((po) => ({ value: po.status, label: t(upperFirst(po.status)) })), "value"), | |||||
| "label").map((po) => (po)) | |||||
| }, | |||||
| // { | |||||
| // label: t("Items"), paramName: "items", type: "autocomplete", multiple: true, | |||||
| // options: sortBy( | |||||
| // uniqBy(pickOrders.map((po) => po.items?.map((item) => ({ value: item.name, label: item.name, group: item.type }))), "value"), | |||||
| // "label") | |||||
| // }, | |||||
| ], [t]) | |||||
| const onReset = useCallback(() => { | |||||
| setFilteredPickOrders(pickOrders) | |||||
| }, [pickOrders]) | |||||
| const columns = useMemo<Column<PickOrderResult>[]>(() => [ | |||||
| { | |||||
| name: "code", | |||||
| label: t("Code"), | |||||
| }, | |||||
| { | |||||
| name: "consoCode", | |||||
| label: t("Consolidated Code"), | |||||
| }, | |||||
| { | |||||
| name: "type", | |||||
| label: t("type"), | |||||
| }, | |||||
| { | |||||
| name: "items", | |||||
| label: t("Items"), | |||||
| renderCell: (params) => { | |||||
| return params.items?.map((i) => i.name).join(", ") | |||||
| } | |||||
| }, | |||||
| { | |||||
| name: "releasedBy", | |||||
| label: t("Released By"), | |||||
| }, | |||||
| { | |||||
| name: "status", | |||||
| label: t("Status"), | |||||
| }, | |||||
| ], [t]) | |||||
| return ( | |||||
| <> | |||||
| <SearchBox | |||||
| criteria={searchCriteria} | |||||
| onSearch={(query) => { | |||||
| setFilteredPickOrders( | |||||
| pickOrders.filter( | |||||
| (po) => | |||||
| po.code.toLowerCase().includes(query.code.toLowerCase()) | |||||
| ) | |||||
| ) | |||||
| }} | |||||
| onReset={onReset} | |||||
| /> | |||||
| <SearchResults<PickOrderResult> items={filteredPickOrders} columns={columns} pagingController={{ | |||||
| pageNum: 0, | |||||
| pageSize: 0, | |||||
| totalCount: 0, | |||||
| }} /> | |||||
| </> | |||||
| ) | |||||
| } | |||||
| export default PickOrderSearch; | |||||
| @@ -0,0 +1,21 @@ | |||||
| import { fetchPickOrders } from "@/app/api/pickOrder"; | |||||
| import GeneralLoading from "../General/GeneralLoading"; | |||||
| import PickOrderSearch from "./PickOrderSearch"; | |||||
| interface SubComponents { | |||||
| Loading: typeof GeneralLoading; | |||||
| } | |||||
| const PickOrderSearchWrapper: React.FC & SubComponents = async () => { | |||||
| const [ | |||||
| pickOrders | |||||
| ] = await Promise.all([ | |||||
| fetchPickOrders() | |||||
| ]) | |||||
| return <PickOrderSearch pickOrders={pickOrders}/> | |||||
| } | |||||
| PickOrderSearchWrapper.Loading = GeneralLoading; | |||||
| export default PickOrderSearchWrapper; | |||||
| @@ -0,0 +1 @@ | |||||
| export { default } from './PickOrderSearchWrapper' | |||||