Browse Source

update inventory & import testing

production_process
cyril.tsui 2 months ago
parent
commit
6aaca3c686
9 changed files with 221 additions and 28 deletions
  1. +7
    -5
      src/app/(main)/inventory/page.tsx
  2. +3
    -3
      src/app/api/inventory/index.ts
  3. +14
    -0
      src/app/api/settings/m18ImportTesting/actions.ts
  4. +15
    -12
      src/components/InventorySearch/InventorySearch.tsx
  5. +126
    -0
      src/components/M18ImportTesting/M18ImportDo.tsx
  6. +38
    -7
      src/components/M18ImportTesting/M18ImportTesting.tsx
  7. +3
    -0
      src/i18n/en/inventory.json
  8. +4
    -1
      src/i18n/zh/common.json
  9. +11
    -0
      src/i18n/zh/inventory.json

+ 7
- 5
src/app/(main)/inventory/page.tsx View File

@@ -1,6 +1,6 @@
import { preloadInventory } from "@/app/api/inventory";
import InventorySearch from "@/components/InventorySearch";
import { getServerI18n } from "@/i18n";
import { I18nProvider, getServerI18n } from "@/i18n";
import { Stack, Typography } from "@mui/material";
import { Metadata } from "next";
import { Suspense } from "react";
@@ -10,7 +10,7 @@ export const metadata: Metadata = {
}

const Inventory: React.FC = async () => {
const { t } = await getServerI18n("inventory")
const { t } = await getServerI18n("inventory", "common")

preloadInventory()

@@ -25,9 +25,11 @@ const Inventory: React.FC = async () => {
{t("Inventory")}
</Typography>
</Stack>
<Suspense fallback={<InventorySearch.Loading />}>
<InventorySearch />
</Suspense>
<I18nProvider namespaces={["common", "inventory"]}>
<Suspense fallback={<InventorySearch.Loading />}>
<InventorySearch />
</Suspense>
</I18nProvider>
</>;
}


+ 3
- 3
src/app/api/inventory/index.ts View File

@@ -11,9 +11,9 @@ export interface InventoryResult {
qty: number;
uomCode: string;
uomUdfudesc: string;
germPerSmallestUnit: number;
qtyPerSmallestUnit: number;
smallestUnit: string;
// germPerSmallestUnit: number;
// qtyPerSmallestUnit: number;
// smallestUnit: string;
price: number;
currencyName: string;
status: string;


+ 14
- 0
src/app/api/settings/m18ImportTesting/actions.ts View File

@@ -8,6 +8,11 @@ export interface M18ImportPoForm {
modifiedDateTo: string,
}

export interface M18ImportDoForm {
modifiedDateFrom: string,
modifiedDateTo: string,
}

export interface M18ImportPqForm {
modifiedDateFrom: string,
modifiedDateTo: string,
@@ -20,6 +25,7 @@ export interface M18ImportMasterDataForm {

export interface M18ImportTestingForm {
po: M18ImportPoForm,
do: M18ImportDoForm,
pq: M18ImportPqForm,
masterData: M18ImportMasterDataForm,
}
@@ -32,6 +38,14 @@ export const testM18ImportPo = async (data: M18ImportPoForm) => {
})
}

export const testM18ImportDo = async (data: M18ImportDoForm) => {
return serverFetchWithNoContent(`${BASE_API_URL}/m18/do`, {
method: "POST",
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
})
}

export const testM18ImportPq = async (data: M18ImportPqForm) => {
return serverFetchWithNoContent(`${BASE_API_URL}/m18/pq`, {
method: "POST",


+ 15
- 12
src/components/InventorySearch/InventorySearch.tsx View File

@@ -27,7 +27,7 @@ type SearchParamNames = keyof SearchQuery;
const InventorySearch: React.FC<Props> = ({
inventories,
}) => {
const { t } = useTranslation("inventories");
const { t } = useTranslation(["inventory", "common"]);

const [filteredInventories, setFilteredInventories] = useState(inventories)

@@ -56,6 +56,9 @@ const InventorySearch: React.FC<Props> = ({
{
name: "type",
label: t("Type"),
renderCell: (params) => {
return t(params.type)
}
},
{
name: "qty",
@@ -68,17 +71,17 @@ const InventorySearch: React.FC<Props> = ({
name: "uomUdfudesc",
label: t("UoM"),
},
{
name: "qtyPerSmallestUnit",
label: t("Qty Per Smallest Unit"),
align: "right",
headerAlign: "right",
type: "decimal"
},
{
name: "smallestUnit",
label: t("Smallest Unit"),
},
// {
// name: "qtyPerSmallestUnit",
// label: t("Qty Per Smallest Unit"),
// align: "right",
// headerAlign: "right",
// type: "decimal"
// },
// {
// name: "smallestUnit",
// label: t("Smallest Unit"),
// },
// {
// name: "price",
// label: t("Price"),


+ 126
- 0
src/components/M18ImportTesting/M18ImportDo.tsx View File

@@ -0,0 +1,126 @@
"use client"
import { M18ImportTestingForm, M18ImportDoForm } from "@/app/api/settings/m18ImportTesting/actions";
import { INPUT_DATE_FORMAT, OUTPUT_DATE_FORMAT, OUTPUT_TIME_FORMAT, dateTimeStringToDayjs } from "@/app/utils/formatUtil";
import { Check } from "@mui/icons-material";
import { Box, Button, Card, CardContent, FormControl, Grid, Stack, Typography } from "@mui/material";
import { DatePicker, DateTimePicker, LocalizationProvider } from "@mui/x-date-pickers";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import dayjs, { Dayjs } from "dayjs";
import React, { useCallback, useState } from "react";
import { Controller, useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";

interface Props {
}

const M18ImportDo: React.FC<Props> = ({
}) => {

const { t } = useTranslation()
const [isLoading, setIsLoading] = useState(false)
const {
control,
formState: { errors },
watch
} = useFormContext<M18ImportTestingForm>()

const handleDateTimePickerOnChange = useCallback((value: Dayjs | null, onChange: (value: any) => void) => {
const formattedValue = value ? value.format(`${INPUT_DATE_FORMAT} ${OUTPUT_TIME_FORMAT}`) : null
onChange(formattedValue)
}, [])



return (
<Card sx={{ width: '100%' }}>
<CardContent sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
<Grid container>
<Grid container>
<Typography variant="overline">{t("Import Delivery Order")}</Typography>
</Grid>
<Grid item xs={8}>
<LocalizationProvider
dateAdapter={AdapterDayjs}
// TODO: Should maybe use a custom adapterLocale here to support YYYY-MM-DD
adapterLocale="zh-hk"
>
<Box display="flex">
<Controller
control={control}
name="do.modifiedDateFrom"
// rules={{
// required: "Please input the date From!",
// validate: {
// isValid: (value) =>
// value && dateTimeStringToDayjs(value).isValid() ? true : "Invalid date-time"
// },
// }}
render={({ field, fieldState: { error } }) => (
<DateTimePicker
label={t("Modified Date From *")}
format={`${OUTPUT_DATE_FORMAT} ${OUTPUT_TIME_FORMAT}`}
onChange={(newValue: Dayjs | null) => (handleDateTimePickerOnChange(newValue, field.onChange))}
slotProps={{
textField: {
error: !!error,
helperText: error ? error.message : null
}
}}
/>
)}
/>
<Box
display="flex"
alignItems="center"
justifyContent="center"
marginInline={2}
>
{"-"}
</Box>
<Controller
control={control}
name="do.modifiedDateTo"
// rules={{
// required: "Please input the date to!",
// validate: {
// isValid: (value) =>
// value && dateTimeStringToDayjs(value).isValid() ? true : "Invalid date-time",
// isFuture: (value) =>
// dateTimeStringToDayjs(value).isAfter(watch("do.dateFrom")) || "Date must be in the future",
// },
// }}
render={({ field, fieldState: { error } }) => (
<DateTimePicker
label={t("Modified Date To *")}
format={`${OUTPUT_DATE_FORMAT} ${OUTPUT_TIME_FORMAT}`}
onChange={(newValue: Dayjs | null) => (handleDateTimePickerOnChange(newValue, field.onChange))}
slotProps={{
textField: {
error: !!error,
helperText: error ? error.message : null
}
}}
/>
)}
/>
</Box>
</LocalizationProvider>
</Grid>
</Grid>
<Stack direction="row" justifyContent="flex-end" gap={1}>
<Button
name="m18ImportDo"
id="m18ImportDo"
variant="contained"
startIcon={<Check />}
type="submit"
>
{t("Import Do")}
</Button>
</Stack>
</CardContent>
</Card>
)
}

export default M18ImportDo;

+ 38
- 7
src/components/M18ImportTesting/M18ImportTesting.tsx View File

@@ -1,6 +1,6 @@
"use client"

import { M18ImportTestingForm, testM18ImportPo, M18ImportPoForm, testM18ImportPq, testM18ImportMasterData } from "@/app/api/settings/m18ImportTesting/actions";
import { M18ImportTestingForm, testM18ImportPo, M18ImportPoForm, testM18ImportPq, testM18ImportMasterData, testM18ImportDo } from "@/app/api/settings/m18ImportTesting/actions";
import { Card, CardContent, Grid, Stack, Typography } from "@mui/material";
import React, { BaseSyntheticEvent, FormEvent, useCallback, useState } from "react";
import { FormProvider, SubmitErrorHandler, useForm } from "react-hook-form";
@@ -9,6 +9,7 @@ import M18ImportPo from "./M18ImportPo";
import M18ImportPq from "./M18ImportPq";
import { dateTimeStringToDayjs } from "@/app/utils/formatUtil";
import M18ImportMasterData from "./M18ImportMasterData";
import M18ImportDo from "./M18ImportDo";

interface Props {

@@ -20,6 +21,7 @@ const M18ImportTesting: React.FC<Props> = ({

const { t } = useTranslation()
const [isLoading, setIsLoading] = useState(false)
const [loadingType, setLoadingType] = useState<String | null>(null)
const formProps = useForm<M18ImportTestingForm>()

const onSubmit = useCallback(async (data: M18ImportTestingForm, event?: BaseSyntheticEvent) => {
@@ -37,11 +39,11 @@ const M18ImportTesting: React.FC<Props> = ({
case "m18ImportMasterData":
const mdDateFrom = data.masterData.modifiedDateFrom
const mdDateTo = data.masterData.modifiedDateTo
if (!(!!mdDateFrom && dateTimeStringToDayjs(mdDateFrom).isValid())) {
if (!(!mdDateFrom || dateTimeStringToDayjs(mdDateFrom).isValid())) {
formProps.setError("masterData.modifiedDateFrom", { message: "Invalid DateTime Format" })
}

if (!(!!mdDateTo && dateTimeStringToDayjs(mdDateTo).isValid())) {
if (!(!mdDateTo || dateTimeStringToDayjs(mdDateTo).isValid())) {
formProps.setError("masterData.modifiedDateTo", { message: "Invalid DateTime Format" })
}

@@ -50,6 +52,7 @@ const M18ImportTesting: React.FC<Props> = ({
}

setIsLoading(() => true)
setLoadingType(() => "Master Data")
const mdResponse = await testM18ImportMasterData(data.masterData)
console.log(mdResponse)
if (mdResponse) {
@@ -72,20 +75,44 @@ const M18ImportTesting: React.FC<Props> = ({
}

setIsLoading(() => true)
const poResponse = await testM18ImportMasterData(data.po)
setLoadingType(() => "Purchase Order")
const poResponse = await testM18ImportPo(data.po)
console.log(poResponse)
if (poResponse) {
setIsLoading(() => false)
}
break;
case "m18ImportDo":
const doDateFrom = data.do.modifiedDateFrom
const doDateTo = data.do.modifiedDateTo
if (!(doDateFrom && dateTimeStringToDayjs(doDateFrom).isValid())) {
formProps.setError("do.modifiedDateFrom", { message: "Invalid DateTime Format" })
}

if (!(doDateTo && dateTimeStringToDayjs(doDateTo).isValid())) {
formProps.setError("do.modifiedDateTo", { message: "Invalid DateTime Format" })
}

if (formProps.formState.errors.do) {
return;
}

setIsLoading(() => true)
setLoadingType(() => "Delivery Order")
const doResponse = await testM18ImportDo(data.po)
console.log(doResponse)
if (doResponse) {
setIsLoading(() => false)
}
break;
case "m18ImportPq":
const pqDateFrom = data.pq.modifiedDateFrom
const pqDateTo = data.pq.modifiedDateTo
if (!(!!pqDateFrom && dateTimeStringToDayjs(pqDateFrom).isValid())) {
if (!(pqDateFrom || dateTimeStringToDayjs(pqDateFrom).isValid())) {
formProps.setError("pq.modifiedDateFrom", { message: "Invalid DateTime Format" })
}

if (!(!!pqDateTo && dateTimeStringToDayjs(pqDateTo).isValid())) {
if (!(pqDateTo || dateTimeStringToDayjs(pqDateTo).isValid())) {
formProps.setError("pq.modifiedDateTo", { message: "Invalid DateTime Format" })
}

@@ -94,6 +121,7 @@ const M18ImportTesting: React.FC<Props> = ({
}

setIsLoading(() => true)
setLoadingType(() => "Purchase Quotation")
const pqResponse = await testM18ImportPq(data.pq)
console.log(pqResponse)
if (pqResponse) {
@@ -115,7 +143,7 @@ const M18ImportTesting: React.FC<Props> = ({
return (
<Card>
<CardContent sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
<Typography variant="overline">{t("Status: ")}{isLoading ? t("Importing...") : t("Ready to import")}</Typography>
<Typography variant="overline">{t("Status: ")}{isLoading ? t(`Importing ${loadingType}...`) : t("Ready to import")}</Typography>
<FormProvider {...formProps}>
<Stack
spacing={2}
@@ -129,6 +157,9 @@ const M18ImportTesting: React.FC<Props> = ({
<Grid item xs={12}>
<M18ImportPo />
</Grid>
<Grid item xs={12}>
<M18ImportDo />
</Grid>
<Grid item xs={12}>
<M18ImportPq />
</Grid>


+ 3
- 0
src/i18n/en/inventory.json View File

@@ -0,0 +1,3 @@
{
}

+ 4
- 1
src/i18n/zh/common.json View File

@@ -3,5 +3,8 @@
"All": "全部",
"No options": "沒有選項",
"Reset": "重置",
"Search": "搜尋"
"Search": "搜尋",
"Code": "編號",
"Name": "名稱",
"Type": "類型"
}

+ 11
- 0
src/i18n/zh/inventory.json View File

@@ -0,0 +1,11 @@
{
"Inventory": "存貨",
"Code": "編號",
"Name": "名稱",
"Type": "類型",
"Status": "狀態",
"Qty": "數量",
"UoM": "單位",
"mat": "物料",
"fg": "成品"
}

Loading…
Cancel
Save