| @@ -0,0 +1,24 @@ | |||||
| import { SearchParams } from "@/app/utils/fetchUtil"; | |||||
| import { TypeEnum } from "@/app/utils/typeEnum"; | |||||
| import CreateProductMaterial from "@/components/CreateProductMaterial"; | |||||
| import { I18nProvider, getServerI18n } from "@/i18n"; | |||||
| import { Typography } from "@mui/material"; | |||||
| import { isString } from "lodash"; | |||||
| type Props = {} & SearchParams; | |||||
| const byProductSetting: React.FC<Props> = async ({ searchParams }) => { | |||||
| const type = TypeEnum.BYPRODUCT; | |||||
| const { t } = await getServerI18n(type); | |||||
| console.log(searchParams); | |||||
| return ( | |||||
| <> | |||||
| {/* <Typography variant="h4">{t("Create Material")}</Typography> */} | |||||
| <I18nProvider namespaces={[type]}> | |||||
| <CreateProductMaterial type={type} /> | |||||
| </I18nProvider> | |||||
| </> | |||||
| ); | |||||
| }; | |||||
| export default byProductSetting; | |||||
| @@ -0,0 +1,24 @@ | |||||
| import { SearchParams } from "@/app/utils/fetchUtil"; | |||||
| import { TypeEnum } from "@/app/utils/typeEnum"; | |||||
| import CreateProductMaterial from "@/components/CreateProductMaterial"; | |||||
| import { I18nProvider, getServerI18n } from "@/i18n"; | |||||
| import { Typography } from "@mui/material"; | |||||
| import { isString } from "lodash"; | |||||
| type Props = {} & SearchParams; | |||||
| const byProductSetting: React.FC<Props> = async ({ searchParams }) => { | |||||
| const type = TypeEnum.BYPRODUCT; | |||||
| const { t } = await getServerI18n(type); | |||||
| console.log(searchParams); | |||||
| return ( | |||||
| <> | |||||
| {/* <Typography variant="h4">{t("Create Material")}</Typography> */} | |||||
| <I18nProvider namespaces={[type]}> | |||||
| <CreateProductMaterial type={type} /> | |||||
| </I18nProvider> | |||||
| </> | |||||
| ); | |||||
| }; | |||||
| export default byProductSetting; | |||||
| @@ -0,0 +1,48 @@ | |||||
| import { TypeEnum } from "@/app/utils/typeEnum"; | |||||
| import MaterialSearch from "@/components/ProductMaterialSearch"; | |||||
| import { getServerI18n } from "@/i18n"; | |||||
| import Add from "@mui/icons-material/Add"; | |||||
| import Button from "@mui/material/Button"; | |||||
| import Stack from "@mui/material/Stack"; | |||||
| import Typography from "@mui/material/Typography"; | |||||
| import { Metadata } from "next"; | |||||
| import Link from "next/link"; | |||||
| import { Suspense } from "react"; | |||||
| export const metadata: Metadata = { | |||||
| title: "ByProduct", | |||||
| }; | |||||
| const materialSetting: React.FC = async () => { | |||||
| const byProduct = TypeEnum.BYPRODUCT | |||||
| const { t } = await getServerI18n(byProduct); | |||||
| // preloadClaims(); | |||||
| return ( | |||||
| <> | |||||
| <Stack | |||||
| direction="row" | |||||
| justifyContent="space-between" | |||||
| flexWrap="wrap" | |||||
| rowGap={2} | |||||
| > | |||||
| <Typography variant="h4" marginInlineEnd={2}> | |||||
| {t("ByProduct")} | |||||
| </Typography> | |||||
| <Button | |||||
| variant="contained" | |||||
| startIcon={<Add />} | |||||
| LinkComponent={Link} | |||||
| href="byProduct/create" | |||||
| > | |||||
| {t("Create by-Product")} | |||||
| </Button> | |||||
| </Stack> | |||||
| <Suspense fallback={<MaterialSearch.Loading />}> | |||||
| <MaterialSearch type={byProduct} /> | |||||
| </Suspense> | |||||
| </> | |||||
| ); | |||||
| }; | |||||
| export default materialSetting; | |||||
| @@ -0,0 +1,39 @@ | |||||
| "use server"; | |||||
| import { ServerFetchError, serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil"; | |||||
| import { revalidateTag } from "next/cache"; | |||||
| import { BASE_API_URL } from "@/config/api"; | |||||
| import { TypeInputs, UomInputs, WeightUnitInputs } from "../material/actions"; | |||||
| import { CreateItemResponse } from "../../utils"; | |||||
| export type CreateByProductInputs = { | |||||
| id?: string | number | |||||
| code: string; | |||||
| name: string; | |||||
| // same goes for other props | |||||
| // change backend for null or not null | |||||
| description?: string | undefined; | |||||
| remarks?: string | undefined; | |||||
| shelfLife?: Number | undefined; | |||||
| countryOfOrigin?: string | undefined; | |||||
| minHumid?: number | undefined; | |||||
| maxHumid?: number | undefined; | |||||
| minTemp?: number | undefined; | |||||
| maxTemp?: number | undefined; | |||||
| sampleRate?: number | undefined; | |||||
| passingRate?: number | undefined; | |||||
| netWeight?: number | undefined; | |||||
| type?: TypeInputs[]; | |||||
| uom?: UomInputs[]; | |||||
| weightUnit?: WeightUnitInputs[]; | |||||
| } | |||||
| export const saveByProduct = async (data: CreateByProductInputs) => { | |||||
| // try { | |||||
| const byProduct = await serverFetchJson<CreateItemResponse<CreateByProductInputs>>(`${BASE_API_URL}/byProduct/new`, { | |||||
| method: "POST", | |||||
| body: JSON.stringify(data), | |||||
| headers: { "Content-Type": "application/json" }, | |||||
| }); | |||||
| revalidateTag("byProduct"); | |||||
| return byProduct | |||||
| }; | |||||
| @@ -0,0 +1,40 @@ | |||||
| import { cache } from "react"; | |||||
| import "server-only"; | |||||
| import { serverFetchJson } from "@/app/utils/fetchUtil"; | |||||
| import { BASE_API_URL } from "@/config/api"; | |||||
| import { TypeInputs, UomInputs, WeightUnitInputs } from "../material/actions"; | |||||
| export type ByProductResult = { | |||||
| id: string | number | |||||
| code: string; | |||||
| name: string; | |||||
| description: string | undefined; | |||||
| remarks: string | undefined; | |||||
| shelfLife: Number | undefined; | |||||
| countryOfOrigin: string | undefined; | |||||
| minHumid: number | undefined; | |||||
| maxHumid: number | undefined; | |||||
| minTemp: number | undefined; | |||||
| maxTemp: number | undefined; | |||||
| sampleRate: number | undefined; | |||||
| passingRate: number | undefined; | |||||
| netWeight: number | undefined; | |||||
| uom: UomInputs[]; | |||||
| weightUnit: WeightUnitInputs[]; | |||||
| type: TypeInputs[]; | |||||
| action?: any; | |||||
| } | |||||
| export const fetchAllByProduct = cache(async () => { | |||||
| return serverFetchJson<ByProductResult[]>(`${BASE_API_URL}/byProduct`, { | |||||
| next: { tags: ["byProduct"] }, | |||||
| }); | |||||
| }); | |||||
| export const fetchByProduct = cache(async (id: number) => { | |||||
| return serverFetchJson<ByProductResult>(`${BASE_API_URL}/byProduct/details/${id}`, { | |||||
| next: { tags: ["byProduct"] }, | |||||
| }); | |||||
| }); | |||||
| @@ -3,6 +3,7 @@ import { ServerFetchError, serverFetchJson, serverFetchWithNoContent } from "@/a | |||||
| import { revalidateTag } from "next/cache"; | import { revalidateTag } from "next/cache"; | ||||
| import { BASE_API_URL } from "@/config/api"; | import { BASE_API_URL } from "@/config/api"; | ||||
| import { HTMLInputTypeAttribute } from "react"; | import { HTMLInputTypeAttribute } from "react"; | ||||
| import { CreateItemResponse } from "../../utils"; | |||||
| export type TypeInputs = { | export type TypeInputs = { | ||||
| type: string | type: string | ||||
| @@ -36,17 +37,10 @@ export type CreateMaterialInputs = { | |||||
| uom?: UomInputs[]; | uom?: UomInputs[]; | ||||
| weightUnit?: WeightUnitInputs[]; | weightUnit?: WeightUnitInputs[]; | ||||
| } | } | ||||
| export interface CreateProductMaterialResponse { | |||||
| id: number | null; | |||||
| name: string; | |||||
| code: string; | |||||
| message: string | null; | |||||
| errorPosition: keyof CreateMaterialInputs; | |||||
| } | |||||
| export const saveMaterial = async (data: CreateMaterialInputs) => { | export const saveMaterial = async (data: CreateMaterialInputs) => { | ||||
| // try { | // try { | ||||
| const material = await serverFetchJson<CreateProductMaterialResponse>(`${BASE_API_URL}/material/new`, { | |||||
| const material = await serverFetchJson<CreateItemResponse<CreateMaterialInputs>>(`${BASE_API_URL}/material/new`, { | |||||
| method: "POST", | method: "POST", | ||||
| body: JSON.stringify(data), | body: JSON.stringify(data), | ||||
| headers: { "Content-Type": "application/json" }, | headers: { "Content-Type": "application/json" }, | ||||
| @@ -2,7 +2,8 @@ | |||||
| import { ServerFetchError, serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil"; | import { ServerFetchError, serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil"; | ||||
| import { revalidateTag } from "next/cache"; | import { revalidateTag } from "next/cache"; | ||||
| import { BASE_API_URL } from "@/config/api"; | import { BASE_API_URL } from "@/config/api"; | ||||
| import { CreateProductMaterialResponse, TypeInputs, UomInputs, WeightUnitInputs } from "../material/actions"; | |||||
| import { TypeInputs, UomInputs, WeightUnitInputs } from "../material/actions"; | |||||
| import { CreateItemResponse } from "../../utils"; | |||||
| export type CreateProductInputs = { | export type CreateProductInputs = { | ||||
| id?: string | number | id?: string | number | ||||
| @@ -26,11 +27,11 @@ export type CreateProductInputs = { | |||||
| export const saveProduct = async (data: CreateProductInputs) => { | export const saveProduct = async (data: CreateProductInputs) => { | ||||
| // try { | // try { | ||||
| const material = await serverFetchJson<CreateProductMaterialResponse>(`${BASE_API_URL}/product/new`, { | |||||
| const product = await serverFetchJson<CreateItemResponse<CreateProductInputs>>(`${BASE_API_URL}/product/new`, { | |||||
| method: "POST", | method: "POST", | ||||
| body: JSON.stringify(data), | body: JSON.stringify(data), | ||||
| headers: { "Content-Type": "application/json" }, | headers: { "Content-Type": "application/json" }, | ||||
| }); | }); | ||||
| revalidateTag("product"); | revalidateTag("product"); | ||||
| return material | |||||
| return product | |||||
| }; | }; | ||||
| @@ -2,14 +2,13 @@ import { cache } from "react"; | |||||
| import "server-only"; | import "server-only"; | ||||
| import { serverFetchJson } from "@/app/utils/fetchUtil"; | import { serverFetchJson } from "@/app/utils/fetchUtil"; | ||||
| import { BASE_API_URL } from "@/config/api"; | import { BASE_API_URL } from "@/config/api"; | ||||
| import { TypeInputs, UomInputs, WeightUnitInputs } from "../material/actions"; | |||||
| export type ProductResult = { | export type ProductResult = { | ||||
| id: string | number | id: string | number | ||||
| code: string; | code: string; | ||||
| name: string; | name: string; | ||||
| isConsumables: boolean; | |||||
| description: string | undefined; | description: string | undefined; | ||||
| type: string | undefined; | |||||
| remarks: string | undefined; | remarks: string | undefined; | ||||
| shelfLife: Number | undefined; | shelfLife: Number | undefined; | ||||
| countryOfOrigin: string | undefined; | countryOfOrigin: string | undefined; | ||||
| @@ -20,20 +19,21 @@ export type ProductResult = { | |||||
| sampleRate: number | undefined; | sampleRate: number | undefined; | ||||
| passingRate: number | undefined; | passingRate: number | undefined; | ||||
| netWeight: number | undefined; | netWeight: number | undefined; | ||||
| uom: string[] | any[]; | |||||
| weightUnit: string[] | any[]; | |||||
| uom: UomInputs[]; | |||||
| weightUnit: WeightUnitInputs[]; | |||||
| type: TypeInputs[]; | |||||
| action?: any; | action?: any; | ||||
| } | } | ||||
| export const fetchAllMaterials = cache(async () => { | |||||
| export const fetchAllProduct = cache(async () => { | |||||
| return serverFetchJson<ProductResult[]>(`${BASE_API_URL}/product`, { | return serverFetchJson<ProductResult[]>(`${BASE_API_URL}/product`, { | ||||
| next: { tags: ["product"] }, | next: { tags: ["product"] }, | ||||
| }); | }); | ||||
| }); | }); | ||||
| export const fetchMaterial = cache(async (id: number) => { | |||||
| export const fetchProduct = cache(async (id: number) => { | |||||
| return serverFetchJson<ProductResult>(`${BASE_API_URL}/product/details/${id}`, { | return serverFetchJson<ProductResult>(`${BASE_API_URL}/product/details/${id}`, { | ||||
| next: { tags: ["product"] }, | next: { tags: ["product"] }, | ||||
| }); | }); | ||||
| @@ -0,0 +1,7 @@ | |||||
| export interface CreateItemResponse<T> { | |||||
| id: number | null; | |||||
| name: string; | |||||
| code: string; | |||||
| message: string | null; | |||||
| errorPosition: string | keyof T; | |||||
| } | |||||
| @@ -1,4 +1,5 @@ | |||||
| export enum TypeEnum { | export enum TypeEnum { | ||||
| PRODUCT = "product", | PRODUCT = "product", | ||||
| MATERIAL = "material", | MATERIAL = "material", | ||||
| BYPRODUCT = "byProduct", | |||||
| } | } | ||||
| @@ -0,0 +1,302 @@ | |||||
| "use client"; | |||||
| import { CreateMaterialInputs } from "@/app/api/settings/material/actions"; | |||||
| import { | |||||
| Box, | |||||
| Card, | |||||
| CardContent, | |||||
| Grid, | |||||
| Stack, | |||||
| TextField, | |||||
| Typography, | |||||
| } from "@mui/material"; | |||||
| import { useFormContext } from "react-hook-form"; | |||||
| import { useTranslation } from "react-i18next"; | |||||
| import InputDataGrid from "../InputDataGrid"; | |||||
| import { useCallback, useMemo, useState } from "react"; | |||||
| import { GridColDef, GridRowModel } from "@mui/x-data-grid"; | |||||
| import { InputDataGridProps, TableRow } from "../InputDataGrid/InputDataGrid"; | |||||
| import { TypeEnum } from "@/app/utils/typeEnum"; | |||||
| import { CreateByProductInputs } from "@/app/api/settings/byProduct/actions"; | |||||
| import { NumberInputProps } from "./NumberInputProps"; | |||||
| type Props = { | |||||
| // isEditMode: boolean; | |||||
| // type: TypeEnum; | |||||
| }; | |||||
| export type EntryError = | |||||
| | { | |||||
| [field in keyof CreateByProductInputs]?: string; | |||||
| } | |||||
| | undefined; | |||||
| const ByProductDetails: React.FC<Props> = ({ | |||||
| }) => { | |||||
| const { | |||||
| t, | |||||
| i18n: { language }, | |||||
| } = useTranslation(); | |||||
| const { | |||||
| register, | |||||
| formState: { errors, defaultValues, touchedFields }, | |||||
| watch, | |||||
| control, | |||||
| setValue, | |||||
| getValues, | |||||
| reset, | |||||
| resetField, | |||||
| setError, | |||||
| clearErrors, | |||||
| } = useFormContext<CreateByProductInputs>(); | |||||
| const typeColumns = useMemo<GridColDef[]>( | |||||
| () => [ | |||||
| { | |||||
| field: "type", | |||||
| headerName: "type", | |||||
| flex: 1, | |||||
| editable: true, | |||||
| }, | |||||
| ], | |||||
| [] | |||||
| ); | |||||
| const weightUnitColumns = useMemo<GridColDef[]>( | |||||
| () => [ | |||||
| { | |||||
| field: "weightUnit", | |||||
| headerName: "Weight Unit", | |||||
| flex: 1, | |||||
| editable: true, | |||||
| }, | |||||
| { | |||||
| field: "conversion", | |||||
| headerName: "conversion", // show base unit | |||||
| flex: 1, | |||||
| type: "number", | |||||
| editable: true, | |||||
| }, | |||||
| ], | |||||
| [] | |||||
| ); | |||||
| const uomColumns = useMemo<GridColDef[]>( | |||||
| () => [ | |||||
| { | |||||
| field: "uom", | |||||
| headerName: "uom", | |||||
| flex: 1, | |||||
| editable: true, | |||||
| }, | |||||
| ], | |||||
| [] | |||||
| ); | |||||
| const validationTest = useCallback( | |||||
| ( | |||||
| newRow: GridRowModel<TableRow<Partial<CreateByProductInputs>, EntryError>> | |||||
| ): EntryError => { | |||||
| const error: EntryError = {}; | |||||
| console.log(newRow); | |||||
| return Object.keys(error).length > 0 ? error : undefined; | |||||
| }, | |||||
| [] | |||||
| ); | |||||
| return ( | |||||
| <Card sx={{ display: "block" }}> | |||||
| <CardContent component={Stack} spacing={4}> | |||||
| <Box> | |||||
| <Typography variant="overline" display="block" marginBlockEnd={1}> | |||||
| {t("Product Details")} | |||||
| </Typography> | |||||
| <Grid container spacing={2} columns={{ xs: 6, sm: 12 }}> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("Name")} | |||||
| fullWidth | |||||
| {...register("name", { | |||||
| required: "name required!", | |||||
| })} | |||||
| error={Boolean(errors.name)} | |||||
| helperText={errors.name?.message} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("Code")} | |||||
| fullWidth | |||||
| {...register("code", { | |||||
| required: "code required!", | |||||
| })} | |||||
| error={Boolean(errors.code)} | |||||
| helperText={errors.code?.message} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("description")} | |||||
| fullWidth | |||||
| {...register("description")} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("shelfLife")} | |||||
| type="number" | |||||
| fullWidth | |||||
| {...register("shelfLife", { | |||||
| valueAsNumber: true, | |||||
| required: "shelfLife required!", | |||||
| })} | |||||
| error={Boolean(errors.shelfLife)} | |||||
| helperText={errors.shelfLife?.message} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("countryOfOrigin")} | |||||
| fullWidth | |||||
| {...register("countryOfOrigin", { | |||||
| required: "countryOfOrigin required!", | |||||
| })} | |||||
| error={Boolean(errors.countryOfOrigin)} | |||||
| helperText={errors.countryOfOrigin?.message} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("minHumid")} | |||||
| type="number" | |||||
| fullWidth | |||||
| inputProps={NumberInputProps} | |||||
| {...register("minHumid", { | |||||
| valueAsNumber: true, | |||||
| required: "minHumid required!", | |||||
| })} | |||||
| error={Boolean(errors.minHumid)} | |||||
| helperText={errors.minHumid?.message} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("maxHumid")} | |||||
| type="number" | |||||
| fullWidth | |||||
| inputProps={NumberInputProps} | |||||
| {...register("maxHumid", { | |||||
| valueAsNumber: true, | |||||
| })} | |||||
| error={Boolean(errors.maxHumid)} | |||||
| helperText={errors.maxHumid?.message} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("minTemp")} | |||||
| type="number" | |||||
| fullWidth | |||||
| inputProps={NumberInputProps} | |||||
| {...register("minTemp", { | |||||
| valueAsNumber: true, | |||||
| required: "minTemp required!", | |||||
| })} | |||||
| error={Boolean(errors.minTemp)} | |||||
| helperText={errors.minTemp?.message} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("maxTemp")} | |||||
| type="number" | |||||
| fullWidth | |||||
| inputProps={NumberInputProps} | |||||
| {...register("maxTemp", { | |||||
| valueAsNumber: true, | |||||
| required: "maxTemp required!", | |||||
| })} | |||||
| error={Boolean(errors.maxTemp)} | |||||
| helperText={errors.maxTemp?.message} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("sampleRate")} | |||||
| type="number" | |||||
| fullWidth | |||||
| inputProps={NumberInputProps} | |||||
| {...register("sampleRate", { | |||||
| valueAsNumber: true, | |||||
| required: "sampleRate required!", | |||||
| })} | |||||
| error={Boolean(errors.sampleRate)} | |||||
| helperText={errors.sampleRate?.message} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("passingRate")} | |||||
| type="number" | |||||
| fullWidth | |||||
| inputProps={NumberInputProps} | |||||
| {...register("passingRate", { | |||||
| valueAsNumber: true, | |||||
| required: "passingRate required!", | |||||
| })} | |||||
| error={Boolean(errors.passingRate)} // change backend for null or not null | |||||
| helperText={errors.passingRate?.message} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6} /> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("remarks")} | |||||
| fullWidth | |||||
| {...register("remarks", { | |||||
| // required: "remarks required!", | |||||
| })} | |||||
| error={Boolean(errors.remarks)} | |||||
| helperText={errors.remarks?.message} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("netWeight")} | |||||
| type="number" | |||||
| fullWidth | |||||
| inputProps={NumberInputProps} | |||||
| {...register("netWeight", { | |||||
| valueAsNumber: true, | |||||
| required: "netWeight required!", | |||||
| })} | |||||
| error={Boolean(errors.netWeight)} | |||||
| helperText={errors.netWeight?.message} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <InputDataGrid<CreateByProductInputs, EntryError> | |||||
| _formKey={"type"} | |||||
| columns={typeColumns} | |||||
| validateRow={validationTest} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <InputDataGrid<CreateByProductInputs, EntryError> | |||||
| _formKey={"uom"} | |||||
| columns={uomColumns} | |||||
| validateRow={validationTest} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={12}> | |||||
| <InputDataGrid<CreateByProductInputs, EntryError> | |||||
| _formKey={"weightUnit"} | |||||
| columns={weightUnitColumns} | |||||
| validateRow={validationTest} | |||||
| /> | |||||
| </Grid> | |||||
| </Grid> | |||||
| </Box> | |||||
| </CardContent> | |||||
| </Card> | |||||
| ); | |||||
| }; | |||||
| export default ByProductDetails; | |||||
| @@ -5,7 +5,6 @@ import { useRouter, useSearchParams } from "next/navigation"; | |||||
| import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||
| import { | import { | ||||
| CreateMaterialInputs, | CreateMaterialInputs, | ||||
| CreateProductMaterialResponse, | |||||
| saveMaterial, | saveMaterial, | ||||
| } from "@/app/api/settings/material/actions"; | } from "@/app/api/settings/material/actions"; | ||||
| import { | import { | ||||
| @@ -25,6 +24,9 @@ import { | |||||
| import { CreateInputsFields } from "./CreateProductMaterialWrapper"; | import { CreateInputsFields } from "./CreateProductMaterialWrapper"; | ||||
| import MaterialDetails from "./MaterialDetails"; | import MaterialDetails from "./MaterialDetails"; | ||||
| import ProductDetails from "./ProductDetails"; | import ProductDetails from "./ProductDetails"; | ||||
| import { CreateItemResponse } from "@/app/api/utils"; | |||||
| import { CreateByProductInputs, saveByProduct } from "@/app/api/settings/byProduct/actions"; | |||||
| import ByProductDetails from "./ByProductDetails"; | |||||
| type Props = { | type Props = { | ||||
| isEditMode: boolean; | isEditMode: boolean; | ||||
| @@ -53,6 +55,10 @@ const CreateProductMaterial: React.FC<Props> = ({ | |||||
| title = "Product"; | title = "Product"; | ||||
| redirPath = "/product"; | redirPath = "/product"; | ||||
| } | } | ||||
| if (type === TypeEnum.BYPRODUCT) { | |||||
| title = "By-Product"; | |||||
| redirPath = "/byProduct"; | |||||
| } | |||||
| if (isEditMode) { | if (isEditMode) { | ||||
| mode = "Edit"; | mode = "Edit"; | ||||
| } else { | } else { | ||||
| @@ -99,8 +105,16 @@ const CreateProductMaterial: React.FC<Props> = ({ | |||||
| }); | }); | ||||
| hasErrors = true; | hasErrors = true; | ||||
| } | } | ||||
| // checking temp input | |||||
| if (data.netWeight && data.netWeight < 0) { | |||||
| const message = "netWeight should not be greater than 0"; | |||||
| formProps.setError("netWeight", { | |||||
| message: message, | |||||
| type: "required", | |||||
| }); | |||||
| } | |||||
| // checking passing rate | // checking passing rate | ||||
| if (data.passingRate && data.passingRate > 100) { | |||||
| if (data.passingRate && (data.passingRate < 0 || data.passingRate > 100)) { | |||||
| const message = "passingRate should not be greater than 100%"; | const message = "passingRate should not be greater than 100%"; | ||||
| formProps.setError("passingRate", { | formProps.setError("passingRate", { | ||||
| message: message, | message: message, | ||||
| @@ -108,7 +122,7 @@ const CreateProductMaterial: React.FC<Props> = ({ | |||||
| }); | }); | ||||
| } | } | ||||
| // checking sampling rate | // checking sampling rate | ||||
| if (data.sampleRate && data.sampleRate > 100) { | |||||
| if (data.sampleRate && (data.sampleRate < 0 || data.sampleRate > 100)) { | |||||
| const message = "sampleRate should not be greater than 100%"; | const message = "sampleRate should not be greater than 100%"; | ||||
| formProps.setError("sampleRate", { | formProps.setError("sampleRate", { | ||||
| message: message, | message: message, | ||||
| @@ -122,15 +136,21 @@ const CreateProductMaterial: React.FC<Props> = ({ | |||||
| console.log("data posted"); | console.log("data posted"); | ||||
| console.log(data); | console.log(data); | ||||
| // do api | // do api | ||||
| var response: CreateProductMaterialResponse | undefined; | |||||
| var response; | |||||
| if (type === TypeEnum.MATERIAL) { | if (type === TypeEnum.MATERIAL) { | ||||
| response = await saveMaterial(data as CreateMaterialInputs); | response = await saveMaterial(data as CreateMaterialInputs); | ||||
| } else if (type === TypeEnum.PRODUCT) { | } else if (type === TypeEnum.PRODUCT) { | ||||
| response = await saveProduct(data as CreateProductInputs); | response = await saveProduct(data as CreateProductInputs); | ||||
| } else if (type === TypeEnum.BYPRODUCT) { | |||||
| // response = await saveByProduct(data as CreateByProductInputs); | |||||
| } else { | |||||
| // Handle unexpected type | |||||
| throw new Error("Invalid type provided"); | |||||
| } | } | ||||
| if (response) { | if (response) { | ||||
| if (!Boolean(response.id)) { | if (!Boolean(response.id)) { | ||||
| formProps.setError(response.errorPosition!!, { | |||||
| formProps.setError(response.errorPosition!! as keyof CreateInputsFields, { | |||||
| message: response.message!!, | message: response.message!!, | ||||
| type: "required", | type: "required", | ||||
| }); | }); | ||||
| @@ -176,6 +196,7 @@ const CreateProductMaterial: React.FC<Props> = ({ | |||||
| )} | )} | ||||
| {type === TypeEnum.MATERIAL && <MaterialDetails />} | {type === TypeEnum.MATERIAL && <MaterialDetails />} | ||||
| {type === TypeEnum.PRODUCT && <ProductDetails />} | {type === TypeEnum.PRODUCT && <ProductDetails />} | ||||
| {type === TypeEnum.BYPRODUCT && <ByProductDetails />} | |||||
| <Stack direction="row" justifyContent="flex-end" gap={1}> | <Stack direction="row" justifyContent="flex-end" gap={1}> | ||||
| <Button | <Button | ||||
| name="submit" | name="submit" | ||||
| @@ -3,67 +3,57 @@ import CreateProductMaterial from "./CreateProductMaterial"; | |||||
| import CreateMaterialLoading from "./CreateProductMaterialLoading"; | import CreateMaterialLoading from "./CreateProductMaterialLoading"; | ||||
| import { CreateMaterialInputs } from "@/app/api/settings/material/actions"; | import { CreateMaterialInputs } from "@/app/api/settings/material/actions"; | ||||
| import { CreateProductInputs } from "@/app/api/settings/product/actions"; | import { CreateProductInputs } from "@/app/api/settings/product/actions"; | ||||
| import { fetchMaterial, MaterialResult } from "@/app/api/settings/material"; | |||||
| import { fetchMaterial } from "@/app/api/settings/material"; | |||||
| import { fetchProduct } from "@/app/api/settings/product"; | |||||
| import { fetchByProduct } from "@/app/api/settings/byProduct"; | |||||
| import { notFound } from "next/navigation"; | |||||
| import { CreateByProductInputs } from "@/app/api/settings/byProduct/actions"; | |||||
| interface SubComponents { | interface SubComponents { | ||||
| Loading: typeof CreateMaterialLoading; | Loading: typeof CreateMaterialLoading; | ||||
| } | } | ||||
| type CreateMaterialProps = { | |||||
| // isEditMode: false; | |||||
| type Props = { | |||||
| id?: number | |||||
| type: TypeEnum; | type: TypeEnum; | ||||
| }; | }; | ||||
| type EditMaterialProps = { | |||||
| // isEditMode: true; | |||||
| type: TypeEnum; | |||||
| }; | |||||
| type CreateProductProps = { | |||||
| // isEditMode: false; | |||||
| type: TypeEnum; | |||||
| }; | |||||
| type EditProductProps = { | |||||
| // isEditMode: true; | |||||
| type: TypeEnum; | |||||
| }; | |||||
| type Props = | |||||
| | CreateMaterialProps | |||||
| | EditMaterialProps | |||||
| | CreateProductProps | |||||
| | EditProductProps; | |||||
| export type CreateInputsFields = CreateMaterialInputs | CreateProductInputs; | |||||
| export type CreateInputsFields = CreateMaterialInputs | CreateProductInputs | CreateByProductInputs; | |||||
| const CreateProductMaterialWrapper: React.FC<Props & { id?: number }> & | |||||
| const CreateProductMaterialWrapper: React.FC<Props> & | |||||
| SubComponents = async ({ type, id }) => { | SubComponents = async ({ type, id }) => { | ||||
| var defaultValues: Partial<CreateInputsFields> = {}; | var defaultValues: Partial<CreateInputsFields> = {}; | ||||
| var result | |||||
| if (id && type === TypeEnum.MATERIAL) { | if (id && type === TypeEnum.MATERIAL) { | ||||
| const result = await fetchMaterial(id); | |||||
| console.log(result) | |||||
| result = await fetchMaterial(id); | |||||
| defaultValues = { | defaultValues = { | ||||
| id: result.id, | |||||
| code: result.name, | |||||
| name: result.name, | |||||
| isConsumables: result.isConsumables, | |||||
| description: result.description, | |||||
| type: result.type, | |||||
| remarks: result.remarks, | |||||
| shelfLife: result.shelfLife, | |||||
| countryOfOrigin: result.countryOfOrigin, | |||||
| minHumid: result.minHumid, | |||||
| maxHumid: result.maxHumid, | |||||
| minTemp: result.minTemp, | |||||
| maxTemp: result.maxTemp, | |||||
| sampleRate: result.sampleRate, | |||||
| passingRate: result.passingRate, | |||||
| netWeight: result.netWeight, | |||||
| uom: result.uom, | |||||
| weightUnit: result.weightUnit, | |||||
| }; | |||||
| isConsumables: result.isConsumables | |||||
| } | |||||
| } else if (id && type === TypeEnum.PRODUCT) { | |||||
| result = await fetchProduct(id); | |||||
| } else if (id && type === TypeEnum.BYPRODUCT) { | |||||
| result = await fetchByProduct(id); | |||||
| } | } | ||||
| if (id && type === TypeEnum.PRODUCT) { | |||||
| defaultValues = {}; | |||||
| } | |||||
| defaultValues = { | |||||
| ...defaultValues, | |||||
| id: result?.id, | |||||
| code: result?.code, | |||||
| name: result?.name, | |||||
| description: result?.description, | |||||
| type: result?.type, | |||||
| remarks: result?.remarks, | |||||
| shelfLife: result?.shelfLife, | |||||
| countryOfOrigin: result?.countryOfOrigin, | |||||
| minHumid: result?.minHumid, | |||||
| maxHumid: result?.maxHumid, | |||||
| minTemp: result?.minTemp, | |||||
| maxTemp: result?.maxTemp, | |||||
| sampleRate: result?.sampleRate, | |||||
| passingRate: result?.passingRate, | |||||
| netWeight: result?.netWeight, | |||||
| uom: result?.uom, | |||||
| weightUnit: result?.weightUnit, | |||||
| }; | |||||
| return ( | return ( | ||||
| <CreateProductMaterial | <CreateProductMaterial | ||||
| isEditMode={Boolean(id)} | isEditMode={Boolean(id)} | ||||
| @@ -229,8 +229,6 @@ const ProductDetails: React.FC<Props> = ({ | |||||
| inputProps={NumberInputProps} | inputProps={NumberInputProps} | ||||
| {...register("sampleRate", { | {...register("sampleRate", { | ||||
| valueAsNumber: true, | valueAsNumber: true, | ||||
| min: 0, | |||||
| max: 100, | |||||
| required: "sampleRate required!", | required: "sampleRate required!", | ||||
| })} | })} | ||||
| error={Boolean(errors.sampleRate)} | error={Boolean(errors.sampleRate)} | ||||
| @@ -245,8 +243,6 @@ const ProductDetails: React.FC<Props> = ({ | |||||
| inputProps={NumberInputProps} | inputProps={NumberInputProps} | ||||
| {...register("passingRate", { | {...register("passingRate", { | ||||
| valueAsNumber: true, | valueAsNumber: true, | ||||
| min: 0, | |||||
| max: 100, | |||||
| required: "passingRate required!", | required: "passingRate required!", | ||||
| })} | })} | ||||
| error={Boolean(errors.passingRate)} // change backend for null or not null | error={Boolean(errors.passingRate)} // change backend for null or not null | ||||
| @@ -188,8 +188,8 @@ const NavigationContent: React.FC = () => { | |||||
| }, | }, | ||||
| { | { | ||||
| icon: <RequestQuote />, | icon: <RequestQuote />, | ||||
| label: "Maintain By-product", | |||||
| path: "/settings/user", | |||||
| label: "By-product", | |||||
| path: "/settings/byProduct", | |||||
| }, | }, | ||||
| { | { | ||||
| icon: <RequestQuote />, | icon: <RequestQuote />, | ||||
| @@ -100,6 +100,11 @@ const MaterialSearch: React.FC<Props> = ({ productMaterial, type }) => { | |||||
| pm.code.toLowerCase().includes(query.code.toLowerCase()) && | pm.code.toLowerCase().includes(query.code.toLowerCase()) && | ||||
| pm.name.toLowerCase().includes(query.name.toLowerCase()) | pm.name.toLowerCase().includes(query.name.toLowerCase()) | ||||
| ); | ); | ||||
| } else if (type === TypeEnum.BYPRODUCT) { | |||||
| return ( | |||||
| pm.code.toLowerCase().includes(query.code.toLowerCase()) && | |||||
| pm.name.toLowerCase().includes(query.name.toLowerCase()) | |||||
| ); | |||||
| } | } | ||||
| }) | }) | ||||
| ); | ); | ||||
| @@ -3,6 +3,7 @@ import ProductMaterialSearch, { ListResult } from "./ProductMaterialSearch"; | |||||
| import MaterialSearchLoading from "./ProductMaterialSearchLoading"; | import MaterialSearchLoading from "./ProductMaterialSearchLoading"; | ||||
| import { SearchParams } from "@/app/utils/fetchUtil"; | import { SearchParams } from "@/app/utils/fetchUtil"; | ||||
| import { TypeEnum } from "@/app/utils/typeEnum"; | import { TypeEnum } from "@/app/utils/typeEnum"; | ||||
| import { notFound } from "next/navigation"; | |||||
| interface SubComponents { | interface SubComponents { | ||||
| Loading: typeof MaterialSearchLoading; | Loading: typeof MaterialSearchLoading; | ||||
| @@ -13,16 +14,20 @@ type Props = { | |||||
| }; | }; | ||||
| const ProductMaterialSearchWrapper: React.FC<Props> & SubComponents = async ({ | const ProductMaterialSearchWrapper: React.FC<Props> & SubComponents = async ({ | ||||
| type | |||||
| type, | |||||
| }) => { | }) => { | ||||
| var result: ListResult[] = [] | |||||
| if (TypeEnum.PRODUCT === type) { | |||||
| result = [] | |||||
| } else if (TypeEnum.MATERIAL === type) { | |||||
| var result: ListResult[] = []; | |||||
| if (TypeEnum.PRODUCT === type) { | |||||
| result = []; | |||||
| } else if (TypeEnum.MATERIAL === type) { | |||||
| result = await fetchAllMaterials(); | result = await fetchAllMaterials(); | ||||
| } else if (TypeEnum.BYPRODUCT === type) { | |||||
| result = []; | |||||
| } else { | |||||
| notFound(); | |||||
| } | } | ||||
| return <ProductMaterialSearch productMaterial={result} type={TypeEnum.MATERIAL} />; | |||||
| return <ProductMaterialSearch productMaterial={result} type={type} />; | |||||
| }; | }; | ||||
| ProductMaterialSearchWrapper.Loading = MaterialSearchLoading; | ProductMaterialSearchWrapper.Loading = MaterialSearchLoading; | ||||