diff --git a/src/app/(main)/do/edit/page.tsx b/src/app/(main)/do/edit/page.tsx new file mode 100644 index 0000000..a9d8d94 --- /dev/null +++ b/src/app/(main)/do/edit/page.tsx @@ -0,0 +1,38 @@ +import { SearchParams } from "@/app/utils/fetchUtil"; +import DoDetail from "@/components/DoDetail/DodetailWrapper"; +import { I18nProvider, getServerI18n } from "@/i18n"; +import { Typography } from "@mui/material"; +import { isArray } from "lodash"; +import { Metadata } from "next"; +import { notFound } from "next/navigation"; +import { Suspense } from "react"; + +export const metadata: Metadata = { + title: "Edit Delivery Order Detail" +} + +type Props = SearchParams; + +const DoEdit: React.FC = async ({ searchParams }) => { + const { t } = await getServerI18n("do"); + const id = searchParams["id"]; + + if (!id || isArray(id) || !isFinite(parseInt(id))) { + notFound(); + } + + return ( + <> + + {t("Edit Delivery Order Detail")} + + + }> + + + + + ); +} + +export default DoEdit; \ No newline at end of file diff --git a/src/app/(main)/finishedGood/page.tsx b/src/app/(main)/finishedGood/page.tsx index ee74e4f..ab7ef0f 100644 --- a/src/app/(main)/finishedGood/page.tsx +++ b/src/app/(main)/finishedGood/page.tsx @@ -7,7 +7,7 @@ import { Metadata } from "next"; import { Suspense } from "react"; export const metadata: Metadata = { - title: "Pick Order", + title: "Finished Good Order", }; const PickOrder: React.FC = async () => { diff --git a/src/app/api/do/actions.tsx b/src/app/api/do/actions.tsx index cd8c1bf..ec7e421 100644 --- a/src/app/api/do/actions.tsx +++ b/src/app/api/do/actions.tsx @@ -12,3 +12,60 @@ import { GridRowId, GridRowSelectionModel } from "@mui/x-data-grid"; export interface CreateConsoDoInput { ids: GridRowSelectionModel; } +export interface DoDetail { + id: number; + code: string; + supplierCode: string; + shopCode: string; + currencyCode: string; + orderDate: string; + estimatedArrivalDate: string; + completeDate: string; + status: string; + deliveryOrderLines: DoDetailLine[]; +} + +export interface DoDetailLine { + id: number; + itemNo: string; + qty: number; + price: number; + status: string; + itemName?: string; + uomCode?: string; +} +export interface ReleaseDoRequest { + id: number; +} + +export interface ReleaseDoResponse { + id: number; + entity: { status: string } +} +export const releaseDo = cache(async (data: ReleaseDoRequest) => { + return await serverFetchJson(`${BASE_API_URL}/do/release`, + { + method: "POST", + body: JSON.stringify(data), + headers: { "Content-Type": "application/json" }, + }) +}) +export const preloadDo = () => { + fetchDoList(); +}; + +export const fetchDoList = cache(async () => { + return serverFetchJson(`${BASE_API_URL}/do/list`, { + next: { tags: ["doList"] }, + }); +}); + +export const fetchDoDetail = cache(async (id: number) => { + return serverFetchJson(`${BASE_API_URL}/do/detail/${id}`, { + method: "GET", + headers: { "Content-Type": "application/json" }, + next: { + tags: ["doDetail"] + } + }); +}); \ No newline at end of file diff --git a/src/components/DoDetail/DoDetail.tsx b/src/components/DoDetail/DoDetail.tsx index b994b1a..af855c5 100644 --- a/src/components/DoDetail/DoDetail.tsx +++ b/src/components/DoDetail/DoDetail.tsx @@ -1,3 +1,108 @@ "use client"; -// const doDetail = +import type { DoDetail as DoDetailType } from "@/app/api/do/actions"; +import { useRouter } from "next/navigation"; +import { useTranslation } from "react-i18next"; +import useUploadContext from "../UploadProvider/useUploadContext"; +import { FormProvider, SubmitErrorHandler, SubmitHandler, useForm } from "react-hook-form"; +import { useCallback, useState } from "react"; +import { Button, Stack, Typography } from "@mui/material"; +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; +import StartIcon from "@mui/icons-material/Start"; +import { releaseDo } from "@/app/api/do/actions"; +import DoInfoCard from "./DoInfoCard"; +import DoLineTable from "./DoLineTable"; + +type Props = { + id?: number; + defaultValues: Partial | undefined; +} + +const DoDetail: React.FC = ({ + defaultValues, + id, +}) => { + const { t } = useTranslation("do") + const router = useRouter(); + const { setIsUploading } = useUploadContext(); + const [serverError, setServerError] = useState(""); + + const formProps = useForm({ + defaultValues: defaultValues + }) + + const handleBack = useCallback(() => { + router.replace(`/do`) + }, []) + + const handleRelease = useCallback(async () => { + try { + setIsUploading(true) + if (id) { + console.log(id) + const response = await releaseDo({ id: id }) + console.log(response.entity.status) + if (response) { + formProps.setValue("status", response.entity.status) + console.log(formProps.watch("status")) + } + } + + } catch (e) { + // backend error + setServerError(t("An error has occurred. Please try again later.")); + console.log(e); + } finally { + setIsUploading(false) + } + }, [id, formProps, t, setIsUploading]) + + const onSubmit = useCallback>(async (data, event) => { + console.log(data) + }, [t]) + + const onSubmitError = useCallback>((errors) => { + console.log(errors) + }, [t]) + + return <> + + + {serverError && ( + + {serverError} + + )} + { + formProps.watch("status")?.toLowerCase() === "pending" && ( + + + + )} + + + + + + + + +} + +export default DoDetail; \ No newline at end of file diff --git a/src/components/DoDetail/DoDetailWrapper.tsx b/src/components/DoDetail/DoDetailWrapper.tsx new file mode 100644 index 0000000..0f0f6c7 --- /dev/null +++ b/src/components/DoDetail/DoDetailWrapper.tsx @@ -0,0 +1,26 @@ +import React from "react"; +import GeneralLoading from "../General/GeneralLoading"; +import { fetchDoDetail } from "@/app/api/do/actions"; +import DoDetail from "./DoDetail"; + +interface SubComponents { + Loading: typeof GeneralLoading; +} + +type DoDetailProps = { + id?: number; +} + +type Props = DoDetailProps + +const DoDetailWrapper: React.FC & SubComponents = async ({ + id, +}) => { + const doDetail = id ? await fetchDoDetail(id) : undefined + + return +} + +DoDetailWrapper.Loading = GeneralLoading; + +export default DoDetailWrapper; \ No newline at end of file diff --git a/src/components/DoDetail/DoInfoCard.tsx b/src/components/DoDetail/DoInfoCard.tsx new file mode 100644 index 0000000..316b22a --- /dev/null +++ b/src/components/DoDetail/DoInfoCard.tsx @@ -0,0 +1,97 @@ +import { DoDetail } from "@/app/api/do/actions"; +import { decimalFormatter, integerFormatter } from "@/app/utils/formatUtil"; +import { Box, Card, CardContent, Grid, Stack, TextField } from "@mui/material"; +import { upperFirst } from "lodash"; +import { useFormContext } from "react-hook-form"; +import { useTranslation } from "react-i18next"; + +type Props = { + +}; + +const DoInfoCard: React.FC = ({ + +}) => { + const { t } = useTranslation("do"); + + const { control, getValues, register, watch } = useFormContext(); + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) +} + +export default DoInfoCard; \ No newline at end of file diff --git a/src/components/DoDetail/DoLineTable.tsx b/src/components/DoDetail/DoLineTable.tsx new file mode 100644 index 0000000..a9da68d --- /dev/null +++ b/src/components/DoDetail/DoLineTable.tsx @@ -0,0 +1,98 @@ +import { DoDetail } from "@/app/api/do/actions"; +import { decimalFormatter } from "@/app/utils/formatUtil"; +import { GridColDef } from "@mui/x-data-grid"; +import { isEmpty, upperFirst } from "lodash"; +import { useMemo } from "react"; +import { useFormContext } from "react-hook-form"; +import { useTranslation } from "react-i18next"; +import StyledDataGrid from "../StyledDataGrid/StyledDataGrid"; + +type Props = { + +}; + +const DoLineTable: React.FC = ({ + +}) => { + const { t } = useTranslation("do") + const { + watch + } = useFormContext() + + const columns = useMemo(() => [ + { + field: "itemNo", + headerName: t("Item No."), + flex: 1, + }, + { + field: "itemName", + headerName: t("Item Name"), + flex: 1, + renderCell: (row) => { + return isEmpty(row.value) ? "N/A" : row.value + }, + }, + { + field: "qty", + headerName: t("Quantity"), + flex: 1, + align: "right", + headerAlign: "right", + renderCell: (row) => { + return decimalFormatter.format(row.value) + }, + }, + { + field: "price", + headerName: t("Price"), + flex: 1, + align: "right", + headerAlign: "right", + renderCell: (row) => { + return decimalFormatter.format(row.value) + }, + }, + { + field: "uomCode", + headerName: t("UoM"), + flex: 1, + align: "left", + headerAlign: "left", + renderCell: (row) => { + return isEmpty(row.value) ? "N/A" : row.value + }, + }, + { + field: "status", + headerName: t("Status"), + flex: 1, + renderCell: (row) => { + return t(upperFirst(row.value)) + }, + }, + ], [t]) + + return ( + <> + + + ) +} + +export default DoLineTable; \ No newline at end of file diff --git a/src/components/DoDetail/index.ts b/src/components/DoDetail/index.ts new file mode 100644 index 0000000..4646975 --- /dev/null +++ b/src/components/DoDetail/index.ts @@ -0,0 +1,3 @@ +export { default } from "./DodetailWrapper"; +export { default as DoInfoCard } from './DoInfoCard'; +export { default as DoLineTable } from './DoLineTable'; \ No newline at end of file diff --git a/src/components/DoSearch/DoSearch.tsx b/src/components/DoSearch/DoSearch.tsx index f01c3e0..d855f94 100644 --- a/src/components/DoSearch/DoSearch.tsx +++ b/src/components/DoSearch/DoSearch.tsx @@ -155,6 +155,22 @@ const DoSearch: React.FC = ({ dos }) => { // onClick: onDetailClick, // buttonIcon: , // }, + + { + field: "id", + headerName: t("Details"), + width: 100, + renderCell: (params) => ( + + ), + }, { field: "code", headerName: t("code"), diff --git a/src/components/FinishedGoodSearch/CombinedLotTable.tsx b/src/components/FinishedGoodSearch/CombinedLotTable.tsx index 4342043..8b99721 100644 --- a/src/components/FinishedGoodSearch/CombinedLotTable.tsx +++ b/src/components/FinishedGoodSearch/CombinedLotTable.tsx @@ -85,13 +85,14 @@ const CombinedLotTable: React.FC = ({ {t("Item Code")} {t("Item Name")} {t("Lot No")} - {t("Expiry Date")} + {/* {t("Expiry Date")} */} {t("Location")} + + {t("Current Stock")} + {t("Lot Required Pick Qty")} + {t("Qty Already Picked")} + {t("Lot Actual Pick Qty")} {t("Stock Unit")} - {t("Available Qty")} - {t("Required Qty")} - {t("Actual Pick Qty")} - {t("Pick Qty")} {t("Submit")} {t("Reject")} @@ -128,11 +129,12 @@ const CombinedLotTable: React.FC = ({ {lot.itemCode} {lot.itemName} {lot.lotNo} - + {/* {lot.expiryDate ? new Date(lot.expiryDate).toLocaleDateString() : 'N/A'} + */} {lot.location} - {lot.stockUnit} + {lot.availableQty} {lot.requiredQty} {lot.actualPickQty || 0} @@ -165,6 +167,7 @@ const CombinedLotTable: React.FC = ({ }} /> + {lot.stockUnit}