| @@ -1,24 +0,0 @@ | |||
| import { SearchParams } from "@/app/utils/fetchUtil"; | |||
| import { TypeEnum } from "@/app/utils/typeEnum"; | |||
| import CreateProductMaterial from "@/components/CreateItem"; | |||
| 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.toString()]}> | |||
| <CreateProductMaterial type={type} /> | |||
| </I18nProvider> | |||
| </> | |||
| ); | |||
| }; | |||
| export default byProductSetting; | |||
| @@ -1,24 +0,0 @@ | |||
| import { SearchParams } from "@/app/utils/fetchUtil"; | |||
| import { TypeEnum } from "@/app/utils/typeEnum"; | |||
| import CreateProductMaterial from "@/components/CreateItem"; | |||
| 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; | |||
| @@ -1,48 +0,0 @@ | |||
| import { TypeEnum } from "@/app/utils/typeEnum"; | |||
| import MaterialSearch from "@/components/ItemsSearch"; | |||
| 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; | |||
| @@ -1,6 +1,6 @@ | |||
| import { SearchParams } from "@/app/utils/fetchUtil"; | |||
| import { TypeEnum } from "@/app/utils/typeEnum"; | |||
| import CreateProductMaterial from "@/components/CreateItem"; | |||
| import CreateItem from "@/components/CreateItem"; | |||
| import { I18nProvider, getServerI18n } from "@/i18n"; | |||
| import { Typography } from "@mui/material"; | |||
| import isString from "lodash/isString"; | |||
| @@ -8,13 +8,13 @@ import isString from "lodash/isString"; | |||
| type Props = {} & SearchParams; | |||
| const materialSetting: React.FC<Props> = async ({ searchParams }) => { | |||
| const type = TypeEnum.PRODUCT; | |||
| const { t } = await getServerI18n(type); | |||
| // const type = TypeEnum.PRODUCT; | |||
| const { t } = await getServerI18n("items"); | |||
| return ( | |||
| <> | |||
| {/* <Typography variant="h4">{t("Create Material")}</Typography> */} | |||
| <I18nProvider namespaces={[type]}> | |||
| <CreateProductMaterial type={type}/> | |||
| <I18nProvider namespaces={["items"]}> | |||
| <CreateItem /> | |||
| </I18nProvider> | |||
| </> | |||
| ); | |||
| @@ -9,7 +9,7 @@ import { notFound } from "next/navigation"; | |||
| type Props = {} & SearchParams; | |||
| const productSetting: React.FC<Props> = async ({ searchParams }) => { | |||
| const type = TypeEnum.PRODUCT; | |||
| const type = "items"; | |||
| const { t } = await getServerI18n(type); | |||
| const id = isString(searchParams["id"]) | |||
| ? parseInt(searchParams["id"]) | |||
| @@ -21,7 +21,7 @@ const productSetting: React.FC<Props> = async ({ searchParams }) => { | |||
| <> | |||
| {/* <Typography variant="h4">{t("Create Material")}</Typography> */} | |||
| <I18nProvider namespaces={[type]}> | |||
| <CreateProductMaterial type={type} id={id} /> | |||
| <CreateProductMaterial id={id} /> | |||
| </I18nProvider> | |||
| </> | |||
| ); | |||
| @@ -1,5 +1,5 @@ | |||
| import { TypeEnum } from "@/app/utils/typeEnum"; | |||
| import MaterialSearch from "@/components/ItemsSearch"; | |||
| import ItemsSearch from "@/components/ItemsSearch"; | |||
| import { getServerI18n } from "@/i18n"; | |||
| import Add from "@mui/icons-material/Add"; | |||
| import Button from "@mui/material/Button"; | |||
| @@ -29,17 +29,17 @@ const productSetting: React.FC = async () => { | |||
| <Typography variant="h4" marginInlineEnd={2}> | |||
| {t("Product")} | |||
| </Typography> | |||
| <Button | |||
| {/* <Button | |||
| variant="contained" | |||
| startIcon={<Add />} | |||
| LinkComponent={Link} | |||
| href="product/create" | |||
| > | |||
| {t("Create product")} | |||
| </Button> | |||
| </Button> */} | |||
| </Stack> | |||
| <Suspense fallback={<MaterialSearch.Loading />}> | |||
| <MaterialSearch type={project} /> | |||
| <Suspense fallback={<ItemsSearch.Loading />}> | |||
| <ItemsSearch /> | |||
| </Suspense> | |||
| </> | |||
| ); | |||
| @@ -1,24 +0,0 @@ | |||
| import { SearchParams } from "@/app/utils/fetchUtil"; | |||
| import { TypeEnum } from "@/app/utils/typeEnum"; | |||
| import CreateProductMaterial from "@/components/CreateItem"; | |||
| import { I18nProvider, getServerI18n } from "@/i18n"; | |||
| import { Typography } from "@mui/material"; | |||
| import { isString } from "lodash"; | |||
| type Props = {} & SearchParams; | |||
| const materialSetting: React.FC<Props> = async ({ searchParams }) => { | |||
| const type = TypeEnum.MATERIAL; | |||
| 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 materialSetting; | |||
| @@ -1,31 +0,0 @@ | |||
| import { SearchParams } from "@/app/utils/fetchUtil"; | |||
| import { TypeEnum } from "@/app/utils/typeEnum"; | |||
| import CreateProductMaterial from "@/components/CreateItem"; | |||
| import { I18nProvider, getServerI18n } from "@/i18n"; | |||
| import { Typography } from "@mui/material"; | |||
| import { isString } from "lodash"; | |||
| import { notFound } from "next/navigation"; | |||
| type Props = {} & SearchParams; | |||
| const materialSetting: React.FC<Props> = async ({ searchParams }) => { | |||
| const type = TypeEnum.MATERIAL; | |||
| const { t } = await getServerI18n(type); | |||
| console.log(searchParams); | |||
| const id = isString(searchParams["id"]) | |||
| ? parseInt(searchParams["id"]) | |||
| : undefined; | |||
| console.log(id) | |||
| if (!id) { | |||
| notFound(); | |||
| } | |||
| return ( | |||
| <> | |||
| {/* <Typography variant="h4">{t("Create Material")}</Typography> */} | |||
| <I18nProvider namespaces={[type.toString()]}> | |||
| <CreateProductMaterial type={type} id={id} /> | |||
| </I18nProvider> | |||
| </> | |||
| ); | |||
| }; | |||
| export default materialSetting; | |||
| @@ -1,48 +0,0 @@ | |||
| import { TypeEnum } from "@/app/utils/typeEnum"; | |||
| import MaterialSearch from "@/components/ItemsSearch"; | |||
| 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: "Material", | |||
| }; | |||
| const materialSetting: React.FC = async () => { | |||
| const material = TypeEnum.MATERIAL | |||
| const { t } = await getServerI18n("material"); | |||
| // preloadClaims(); | |||
| return ( | |||
| <> | |||
| <Stack | |||
| direction="row" | |||
| justifyContent="space-between" | |||
| flexWrap="wrap" | |||
| rowGap={2} | |||
| > | |||
| <Typography variant="h4" marginInlineEnd={2}> | |||
| {t("Material")} | |||
| </Typography> | |||
| <Button | |||
| variant="contained" | |||
| startIcon={<Add />} | |||
| LinkComponent={Link} | |||
| href="material/create" | |||
| > | |||
| {t("Create material")} | |||
| </Button> | |||
| </Stack> | |||
| <Suspense fallback={<MaterialSearch.Loading />}> | |||
| <MaterialSearch type={material} /> | |||
| </Suspense> | |||
| </> | |||
| ); | |||
| }; | |||
| export default materialSetting; | |||
| @@ -2,20 +2,23 @@ | |||
| import { ServerFetchError, serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil"; | |||
| import { revalidateTag } from "next/cache"; | |||
| import { BASE_API_URL } from "@/config/api"; | |||
| import { HTMLInputTypeAttribute } from "react"; | |||
| import { CreateItemResponse } from "../../utils"; | |||
| import { ItemQc } from "."; | |||
| import { QcChecksInputs } from "../qcCheck/actions"; | |||
| // export type TypeInputs = { | |||
| // id: number; | |||
| // name: string | |||
| // } | |||
| // export type UomInputs = { | |||
| // uom: string | |||
| // } | |||
| // export type WeightUnitInputs = { | |||
| // weightUnit: string | |||
| // conversion: number | |||
| // } | |||
| export type TypeInputs = { | |||
| id: number; | |||
| name: string | |||
| } | |||
| export type UomInputs = { | |||
| uom: string | |||
| } | |||
| export type WeightUnitInputs = { | |||
| weightUnit: string | |||
| conversion: number | |||
| } | |||
| export type CreateItemInputs = { | |||
| id?: string | number | |||
| code: string; | |||
| @@ -24,14 +27,10 @@ export type CreateItemInputs = { | |||
| 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; | |||
| typeId: number; | |||
| maxQty: number; | |||
| type: string; | |||
| qcChecks: QcChecksInputs[] | |||
| qcChecks_active: number[] | |||
| } | |||
| export const saveItem = async (data: CreateItemInputs) => { | |||
| @@ -43,4 +42,4 @@ export const saveItem = async (data: CreateItemInputs) => { | |||
| }); | |||
| revalidateTag("items"); | |||
| return item | |||
| }; | |||
| }; | |||
| @@ -2,7 +2,18 @@ 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 "./actions"; | |||
| // import { TypeInputs, UomInputs, WeightUnitInputs } from "./actions"; | |||
| export type ItemQc = { | |||
| id: number; // id = qc_check # | |||
| name: string; | |||
| code: string; | |||
| description: string | undefined; | |||
| instruction: string | undefined; | |||
| lowerLimit: number | undefined; | |||
| upperLimit: number | undefined; | |||
| isActive: boolean | undefined; | |||
| } | |||
| export type ItemsResult = { | |||
| id: string | number | |||
| @@ -12,20 +23,15 @@ export type ItemsResult = { | |||
| 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; | |||
| maxQty: number | undefined; | |||
| type: string; | |||
| qcChecks: ItemQc[] | |||
| action?: any | |||
| } | |||
| export type Result = { | |||
| item: ItemsResult | |||
| qcChecks: ItemQc[] | |||
| } | |||
| export const fetchAllItems = cache(async () => { | |||
| return serverFetchJson<ItemsResult[]>(`${BASE_API_URL}/items`, { | |||
| next: { tags: ["items"] }, | |||
| @@ -34,7 +40,7 @@ export const fetchAllItems = cache(async () => { | |||
| export const fetchItem = cache(async (id: number) => { | |||
| return serverFetchJson<ItemsResult>(`${BASE_API_URL}/items/details/${id}`, { | |||
| return serverFetchJson<Result>(`${BASE_API_URL}/items/details/${id}`, { | |||
| next: { tags: ["items"] }, | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,22 @@ | |||
| "use server"; | |||
| import { ServerFetchError, serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil"; | |||
| import { revalidateTag } from "next/cache"; | |||
| import { BASE_API_URL } from "@/config/api"; | |||
| import { CreateItemResponse } from "../../utils"; | |||
| import { ItemQc } from "../item"; | |||
| export type QcChecksInputs = { | |||
| } & Partial<ItemQc> | |||
| export const saveItemQcChecks = async (data: QcChecksInputs[]) => { | |||
| // try { | |||
| const res = await serverFetchJson<CreateItemResponse<QcChecksInputs>>(`${BASE_API_URL}/qcCheck/new`, { | |||
| method: "POST", | |||
| body: JSON.stringify(data), | |||
| headers: { "Content-Type": "application/json" }, | |||
| }); | |||
| revalidateTag("items"); | |||
| return res | |||
| }; | |||
| @@ -56,6 +56,7 @@ type FetchParams = Parameters<typeof fetch>; | |||
| export async function serverFetchJson<T>(...args: FetchParams) { | |||
| const response = await serverFetch(...args); | |||
| console.log(response.status) | |||
| if (response.ok) { | |||
| if (response.status === 204) { | |||
| return response.status as T | |||
| @@ -1,294 +0,0 @@ | |||
| "use client"; | |||
| import { CreateItemInputs } from "@/app/api/settings/item/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, useEffect, 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 { NumberInputProps } from "./NumberInputProps"; | |||
| type Props = { | |||
| // isEditMode: boolean; | |||
| // type: TypeEnum; | |||
| }; | |||
| export type EntryError = | |||
| | { | |||
| [field in keyof CreateItemInputs]?: 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<CreateItemInputs>(); | |||
| 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<CreateItemInputs>, EntryError>> | |||
| ): EntryError => { | |||
| const error: EntryError = {}; | |||
| console.log(newRow); | |||
| return Object.keys(error).length > 0 ? error : undefined; | |||
| }, | |||
| [] | |||
| ); | |||
| const validateUom = useCallback( | |||
| ( | |||
| newRow: GridRowModel<TableRow<Partial<CreateItemInputs>, EntryError>> | |||
| ): EntryError => { | |||
| const error: EntryError = {}; | |||
| console.log(newRow); | |||
| // if (!newRow.uom) { | |||
| // error.uom = "Uom cannot be empty"; | |||
| // } | |||
| return Object.keys(error).length > 0 ? error : undefined; | |||
| }, | |||
| [] | |||
| ); | |||
| useEffect(() => { | |||
| console.log(errors) | |||
| }, [errors]) | |||
| 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> | |||
| </Box> | |||
| </CardContent> | |||
| </Card> | |||
| ); | |||
| }; | |||
| export default ByProductDetails; | |||
| @@ -14,140 +14,126 @@ import { | |||
| useForm, | |||
| } from "react-hook-form"; | |||
| import { deleteDialog } from "../Swal/CustomAlerts"; | |||
| import { Box, Button, Grid, Stack, Typography } from "@mui/material"; | |||
| import { Box, Button, Grid, Stack, Tab, Tabs, TabsProps, Typography } from "@mui/material"; | |||
| import { Check, Close, EditNote } from "@mui/icons-material"; | |||
| import { TypeEnum } from "@/app/utils/typeEnum"; | |||
| import MaterialDetails from "./MaterialDetails"; | |||
| import ProductDetails from "./ProductDetails"; | |||
| import { CreateItemResponse } from "@/app/api/utils"; | |||
| import ByProductDetails from "./ByProductDetails"; | |||
| import QcDetails from "./QcDetails"; | |||
| import { ItemQc } from "@/app/api/settings/item"; | |||
| import { saveItemQcChecks } from "@/app/api/settings/qcCheck/actions"; | |||
| import { useGridApiRef } from "@mui/x-data-grid"; | |||
| type Props = { | |||
| isEditMode: boolean; | |||
| type: TypeEnum; | |||
| // type: TypeEnum; | |||
| defaultValues: Partial<CreateItemInputs> | undefined; | |||
| qcChecks: ItemQc[] | |||
| }; | |||
| const CreateItem: React.FC<Props> = ({ | |||
| isEditMode, | |||
| type, | |||
| // type, | |||
| defaultValues, | |||
| qcChecks | |||
| }) => { | |||
| console.log(type) | |||
| // console.log(type) | |||
| const apiRef = useGridApiRef(); | |||
| const params = useSearchParams() | |||
| console.log(params.get("id")) | |||
| const [serverError, setServerError] = useState(""); | |||
| const [tabIndex, setTabIndex] = useState(0); | |||
| const { t } = useTranslation(); | |||
| const router = useRouter(); | |||
| const [typeId, title, mode, redirPath] = useMemo(() => { | |||
| var typeId = TypeEnum.CONSUMABLE_ID | |||
| const title = "Product / Material" | |||
| const [mode, redirPath] = useMemo(() => { | |||
| // var typeId = TypeEnum.CONSUMABLE_ID | |||
| var title = ""; | |||
| var mode = ""; | |||
| var redirPath = ""; | |||
| if (type === TypeEnum.MATERIAL) { | |||
| typeId = TypeEnum.MATERIAL_ID | |||
| title = "Material"; | |||
| redirPath = "/settings/material"; | |||
| } | |||
| if (type === TypeEnum.PRODUCT) { | |||
| typeId = TypeEnum.PRODUCT_ID | |||
| // if (type === TypeEnum.MATERIAL) { | |||
| // typeId = TypeEnum.MATERIAL_ID | |||
| // title = "Material"; | |||
| // redirPath = "/settings/material"; | |||
| // } | |||
| // if (type === TypeEnum.PRODUCT) { | |||
| // typeId = TypeEnum.PRODUCT_ID | |||
| title = "Product"; | |||
| redirPath = "/settings/product"; | |||
| } | |||
| if (type === TypeEnum.BYPRODUCT) { | |||
| typeId = TypeEnum.BYPRODUCT_ID | |||
| title = "By-Product"; | |||
| redirPath = "/settings/byProduct"; | |||
| } | |||
| redirPath = "/settings/items"; | |||
| // } | |||
| // if (type === TypeEnum.BYPRODUCT) { | |||
| // typeId = TypeEnum.BYPRODUCT_ID | |||
| // title = "By-Product"; | |||
| // redirPath = "/settings/byProduct"; | |||
| // } | |||
| if (isEditMode) { | |||
| mode = "Edit"; | |||
| } else { | |||
| mode = "Create"; | |||
| } | |||
| return [typeId, title, mode, redirPath]; | |||
| }, [type, isEditMode]); | |||
| console.log(typeId) | |||
| return [mode, redirPath]; | |||
| }, [isEditMode]); | |||
| // console.log(typeId) | |||
| const formProps = useForm<CreateItemInputs>({ | |||
| defaultValues: defaultValues ? defaultValues : { | |||
| typeId: typeId | |||
| }, | |||
| }); | |||
| const errors = formProps.formState.errors; | |||
| const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>( | |||
| (_e, newValue) => { | |||
| setTabIndex(newValue); | |||
| }, | |||
| [], | |||
| ); | |||
| const handleCancel = () => { | |||
| router.replace(`/settings/${type}`); | |||
| router.replace(`/settings/product`); | |||
| }; | |||
| const onSubmit = useCallback<SubmitHandler<CreateItemInputs>>( | |||
| const onSubmit = useCallback<SubmitHandler<CreateItemInputs & {}>>( | |||
| async (data, event) => { | |||
| let hasErrors = false; | |||
| console.log("dasasdasd") | |||
| console.log(errors) | |||
| // console.log(apiRef.current.getCellValue(2, "lowerLimit")) | |||
| // apiRef.current. | |||
| try { | |||
| // checking humid input | |||
| if (data.maxHumid && data.minHumid!! > data.maxHumid!!) { | |||
| const message = "minHumid should not be greater than maxHumid"; | |||
| formProps.setError("minHumid", { | |||
| message: message, | |||
| type: "required", | |||
| }); | |||
| formProps.setError("maxHumid", { | |||
| message: message, | |||
| type: "required", | |||
| }); | |||
| hasErrors = true; | |||
| } | |||
| // checking temp input | |||
| if (data.maxTemp && data.minTemp!! > data.maxTemp!!) { | |||
| const message = "minTemp should not be greater than maxTemp"; | |||
| formProps.setError("minTemp", { | |||
| message: message, | |||
| type: "required", | |||
| }); | |||
| formProps.setError("maxTemp", { | |||
| message: message, | |||
| type: "required", | |||
| }); | |||
| 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 | |||
| if (data.passingRate && (data.passingRate < 0 || data.passingRate > 100)) { | |||
| const message = "passingRate should not be greater than 100%"; | |||
| formProps.setError("passingRate", { | |||
| message: message, | |||
| type: "required", | |||
| }); | |||
| } | |||
| // checking sampling rate | |||
| if (data.sampleRate && (data.sampleRate < 0 || data.sampleRate > 100)) { | |||
| const message = "sampleRate should not be greater than 100%"; | |||
| formProps.setError("sampleRate", { | |||
| message: message, | |||
| type: "required", | |||
| }); | |||
| } | |||
| if (hasErrors) { | |||
| setServerError(t("An error has occurred. Please try again later.")); | |||
| return false; | |||
| } | |||
| console.log("data posted"); | |||
| console.log(data); | |||
| const qcCheck = data.qcChecks.length > 0 ? data.qcChecks.filter((q) => data.qcChecks_active.includes(q.id!!)).map((qc) => { | |||
| return { | |||
| qcItemId: qc.id, | |||
| instruction: qc.instruction, | |||
| lowerLimit: qc.lowerLimit, | |||
| upperLimit: qc.upperLimit, | |||
| itemId: parseInt(params.get("id")!.toString()) | |||
| } | |||
| }) : [] | |||
| const test = data.qcChecks.filter((q) => data.qcChecks_active.includes(q.id!!)) | |||
| // TODO: | |||
| // 1. check field ( directly modify col def / check here ) | |||
| // 2. set error change tab index | |||
| console.log(test) | |||
| console.log(qcCheck) | |||
| // return | |||
| // do api | |||
| var response = await saveItem(data); | |||
| if (response) { | |||
| if (!Boolean(response.id)) { | |||
| formProps.setError(response.errorPosition!! as keyof CreateItemInputs, { | |||
| message: response.message!!, | |||
| console.log("asdad") | |||
| var responseI = await saveItem(data); | |||
| console.log("asdad") | |||
| var responseQ = await saveItemQcChecks(qcCheck) | |||
| if (responseI && responseQ) { | |||
| if (!Boolean(responseI.id)) { | |||
| formProps.setError(responseI.errorPosition!! as keyof CreateItemInputs, { | |||
| message: responseI.message!!, | |||
| type: "required", | |||
| }); | |||
| } else if (Boolean(response.id)) { | |||
| } else if (!Boolean(responseQ.id)) { | |||
| } else if (Boolean(responseI.id) && Boolean(responseQ.id)) { | |||
| router.replace(redirPath); | |||
| } | |||
| } | |||
| @@ -157,7 +143,7 @@ const CreateItem: React.FC<Props> = ({ | |||
| console.log(e); | |||
| } | |||
| }, | |||
| [router, t] | |||
| [apiRef, router, t] | |||
| ); | |||
| // multiple tabs | |||
| @@ -174,19 +160,24 @@ const CreateItem: React.FC<Props> = ({ | |||
| component="form" | |||
| onSubmit={formProps.handleSubmit(onSubmit, onSubmitError)} | |||
| > | |||
| <Grid> | |||
| <Typography mb={2} variant="h4"> | |||
| {t(`${mode} ${title}`)} | |||
| </Typography> | |||
| </Grid> | |||
| <Grid> | |||
| <Typography mb={2} variant="h4"> | |||
| {t(`${mode} ${title}`)} | |||
| </Typography> | |||
| </Grid> | |||
| <Tabs value={tabIndex} onChange={handleTabChange} variant="scrollable"> | |||
| <Tab label={t("Product / Material Details")} iconPosition="end"/> | |||
| <Tab label={t("Qc items")} iconPosition="end" /> | |||
| </Tabs> | |||
| {serverError && ( | |||
| <Typography variant="body2" color="error" alignSelf="flex-end"> | |||
| {serverError} | |||
| </Typography> | |||
| )} | |||
| {type === TypeEnum.MATERIAL && <MaterialDetails />} | |||
| {type === TypeEnum.PRODUCT && <ProductDetails />} | |||
| {type === TypeEnum.BYPRODUCT && <ByProductDetails />} | |||
| {tabIndex === 0 && <ProductDetails />} | |||
| {tabIndex === 1 && <QcDetails apiRef={apiRef} />} | |||
| {/* {type === TypeEnum.MATERIAL && <MaterialDetails />} */} | |||
| {/* {type === TypeEnum.BYPRODUCT && <ByProductDetails />} */} | |||
| <Stack direction="row" justifyContent="flex-end" gap={1}> | |||
| <Button | |||
| name="submit" | |||
| @@ -4,47 +4,48 @@ import CreateItemLoading from "./CreateItemLoading"; | |||
| import { CreateItemInputs } from "@/app/api/settings/item/actions"; | |||
| import { notFound } from "next/navigation"; | |||
| import { fetchItem } from "@/app/api/settings/item"; | |||
| import { fetchQcItems } from "@/app/api/settings/qcItem"; | |||
| interface SubComponents { | |||
| Loading: typeof CreateItemLoading; | |||
| } | |||
| type Props = { | |||
| id?: number | |||
| type: TypeEnum; | |||
| // type: TypeEnum; | |||
| }; | |||
| const CreateItemWrapper: React.FC<Props> & | |||
| SubComponents = async ({ type, id }) => { | |||
| SubComponents = async ({ id }) => { | |||
| var result | |||
| var defaultValues: Partial<CreateItemInputs> | undefined | |||
| console.log(type) | |||
| var defaultValues: Partial<CreateItemInputs> | undefined | |||
| // console.log(type) | |||
| var qcChecks | |||
| if (id) { | |||
| result = await fetchItem(id); | |||
| console.log(result) | |||
| const item = result.item | |||
| qcChecks = result.qcChecks | |||
| const activeRows = qcChecks.filter(it => it.isActive).map(i => i.id) | |||
| console.log(qcChecks) | |||
| defaultValues = { | |||
| typeId: result?.type.id, | |||
| id: result?.id, | |||
| code: result?.code, | |||
| name: result?.name, | |||
| description: result?.description, | |||
| 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 | |||
| type: item?.type, | |||
| id: item?.id, | |||
| code: item?.code, | |||
| name: item?.name, | |||
| description: item?.description, | |||
| remarks: item?.remarks, | |||
| shelfLife: item?.shelfLife, | |||
| countryOfOrigin: item?.countryOfOrigin, | |||
| maxQty: item?.maxQty, | |||
| qcChecks: qcChecks, | |||
| qcChecks_active: activeRows | |||
| }; | |||
| } | |||
| return ( | |||
| <CreateItem | |||
| isEditMode={Boolean(id)} | |||
| type={type} | |||
| defaultValues={defaultValues} | |||
| qcChecks={qcChecks || []} | |||
| /> | |||
| ); | |||
| }; | |||
| @@ -1,325 +0,0 @@ | |||
| "use client"; | |||
| import { CreateItemInputs } from "@/app/api/settings/item/actions"; | |||
| import { | |||
| Box, | |||
| Card, | |||
| CardContent, | |||
| Checkbox, | |||
| FilledInputProps, | |||
| FormControlLabel, | |||
| FormGroup, | |||
| Grid, | |||
| InputBaseComponentProps, | |||
| OutlinedInputProps, | |||
| Stack, | |||
| TextField, | |||
| Typography, | |||
| } from "@mui/material"; | |||
| import { Controller, useFormContext } from "react-hook-form"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import InputDataGrid from "../InputDataGrid"; | |||
| import { useCallback, useEffect, 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 { NumberInputProps } from "./NumberInputProps"; | |||
| type Props = { | |||
| // isEditMode: boolean; | |||
| // type: TypeEnum; | |||
| }; | |||
| export type EntryError = | |||
| | { | |||
| [field in keyof CreateItemInputs]?: string; | |||
| } | |||
| | undefined; | |||
| const MaterialDetails: React.FC<Props> = ({}) => { | |||
| const { | |||
| t, | |||
| i18n: { language }, | |||
| } = useTranslation(); | |||
| const { | |||
| register, | |||
| formState: { errors, defaultValues, touchedFields }, | |||
| watch, | |||
| control, | |||
| setValue, | |||
| getValues, | |||
| reset, | |||
| resetField, | |||
| setError, | |||
| clearErrors, | |||
| } = useFormContext<CreateItemInputs>(); | |||
| // textfield error msg locale problem | |||
| console.log(defaultValues) | |||
| 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<CreateItemInputs>, 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> | |||
| <Stack direction="row" justifyContent="space-between" gap={1}> | |||
| <Typography variant="overline" display="block" marginBlockEnd={1}> | |||
| {t("Material Details")} | |||
| </Typography> | |||
| <FormControlLabel | |||
| control={ | |||
| <Controller | |||
| name={"typeId"} | |||
| control={control} | |||
| render={({ field: props }) => { | |||
| console.log(props) | |||
| return ( | |||
| <Checkbox | |||
| {...props} | |||
| checked={props.value === TypeEnum.CONSUMABLE_ID} | |||
| onChange={(e) => { | |||
| const newValue = e.target.checked ? TypeEnum.CONSUMABLE_ID : TypeEnum.MATERIAL_ID; | |||
| props.onChange(newValue); | |||
| }} | |||
| /> | |||
| )}} | |||
| /> | |||
| } | |||
| label={ | |||
| <Typography | |||
| variant="overline" | |||
| display="block" | |||
| marginBlockEnd={1} | |||
| > | |||
| {t("Consumables")} | |||
| </Typography> | |||
| } | |||
| /> | |||
| </Stack> | |||
| <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, | |||
| // min: 0, | |||
| // max: watch("maxHumid") || 100, | |||
| 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, | |||
| // min: watch("minTemp"), | |||
| 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, | |||
| min: 0, | |||
| max: 100, | |||
| 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, | |||
| min: 0, | |||
| max: 100, | |||
| 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, | |||
| min: 0, | |||
| required: "netWeight required!", | |||
| })} | |||
| error={Boolean(errors.netWeight)} | |||
| helperText={errors.netWeight?.message} | |||
| /> | |||
| </Grid> | |||
| </Grid> | |||
| </Box> | |||
| </CardContent> | |||
| </Card> | |||
| ); | |||
| }; | |||
| export default MaterialDetails; | |||
| @@ -19,19 +19,11 @@ import { NumberInputProps } from "./NumberInputProps"; | |||
| import { CreateItemInputs } from "@/app/api/settings/item/actions"; | |||
| type Props = { | |||
| // isEditMode: boolean; | |||
| // type: TypeEnum; | |||
| // isEditMode: boolean; | |||
| // type: TypeEnum; | |||
| }; | |||
| export type EntryError = | |||
| | { | |||
| [field in keyof CreateItemInputs]?: string; | |||
| } | |||
| | undefined; | |||
| const ProductDetails: React.FC<Props> = ({ | |||
| }) => { | |||
| const ProductDetails: React.FC<Props> = ({}) => { | |||
| const { | |||
| t, | |||
| i18n: { language }, | |||
| @@ -49,57 +41,57 @@ const ProductDetails: React.FC<Props> = ({ | |||
| setError, | |||
| clearErrors, | |||
| } = useFormContext<CreateItemInputs>(); | |||
| 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 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<CreateItemInputs>, EntryError>> | |||
| ): EntryError => { | |||
| const error: EntryError = {}; | |||
| console.log(newRow); | |||
| return Object.keys(error).length > 0 ? error : undefined; | |||
| }, | |||
| [] | |||
| ); | |||
| // const validationTest = useCallback( | |||
| // ( | |||
| // newRow: GridRowModel<TableRow<Partial<CreateItemInputs>, EntryError>> | |||
| // ): EntryError => { | |||
| // const error: EntryError = {}; | |||
| // console.log(newRow); | |||
| // return Object.keys(error).length > 0 ? error : undefined; | |||
| // }, | |||
| // [] | |||
| // ); | |||
| return ( | |||
| <Card sx={{ display: "block" }}> | |||
| @@ -131,6 +123,17 @@ const ProductDetails: React.FC<Props> = ({ | |||
| helperText={errors.code?.message} | |||
| /> | |||
| </Grid> | |||
| <Grid item xs={6}> | |||
| <TextField | |||
| label={t("Type")} | |||
| fullWidth | |||
| {...register("type", { | |||
| required: "type required!", | |||
| })} | |||
| error={Boolean(errors.type)} | |||
| helperText={errors.type?.message} | |||
| /> | |||
| </Grid> | |||
| <Grid item xs={6}> | |||
| <TextField | |||
| label={t("description")} | |||
| @@ -162,93 +165,6 @@ const ProductDetails: React.FC<Props> = ({ | |||
| helperText={errors.countryOfOrigin?.message} | |||
| /> | |||
| </Grid> | |||
| <Grid item xs={6}> | |||
| <TextField | |||
| label={t("minHumid")} | |||
| type="number" | |||
| fullWidth | |||
| inputProps={NumberInputProps} | |||
| {...register("minHumid", { | |||
| valueAsNumber: true, | |||
| // min: 0, | |||
| // max: watch("maxHumid") || 100, | |||
| 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, | |||
| // min: watch("minTemp"), | |||
| 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")} | |||
| @@ -262,17 +178,17 @@ const ProductDetails: React.FC<Props> = ({ | |||
| </Grid> | |||
| <Grid item xs={6}> | |||
| <TextField | |||
| label={t("netWeight")} | |||
| label={t("maxQty")} | |||
| type="number" | |||
| fullWidth | |||
| inputProps={NumberInputProps} | |||
| {...register("netWeight", { | |||
| {...register("maxQty", { | |||
| valueAsNumber: true, | |||
| min: 0, | |||
| required: "netWeight required!", | |||
| required: "maxQty required!", | |||
| })} | |||
| error={Boolean(errors.netWeight)} | |||
| helperText={errors.netWeight?.message} | |||
| error={Boolean(errors.maxQty)} | |||
| helperText={errors.maxQty?.message} | |||
| /> | |||
| </Grid> | |||
| {/* <Grid item xs={6}> | |||
| @@ -296,7 +212,7 @@ const ProductDetails: React.FC<Props> = ({ | |||
| validateRow={validationTest} | |||
| /> | |||
| </Grid>*/} | |||
| </Grid> | |||
| </Grid> | |||
| </Box> | |||
| </CardContent> | |||
| </Card> | |||
| @@ -0,0 +1,163 @@ | |||
| "use client"; | |||
| import { CreateItemInputs } from "@/app/api/settings/item/actions"; | |||
| import { | |||
| GridColDef, | |||
| GridRowModel, | |||
| GridRenderEditCellParams, | |||
| GridEditInputCell, | |||
| GridRowSelectionModel, | |||
| useGridApiRef, | |||
| } from "@mui/x-data-grid"; | |||
| import { MutableRefObject, useCallback, useMemo } from "react"; | |||
| import { useFormContext } from "react-hook-form"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import InputDataGrid, { TableRow } from "../InputDataGrid/InputDataGrid"; | |||
| import { Box, Grid, Tooltip } from "@mui/material"; | |||
| import { ItemQc } from "@/app/api/settings/item"; | |||
| import { QcChecksInputs } from "@/app/api/settings/qcCheck/actions"; | |||
| import { GridApiCommunity } from "@mui/x-data-grid/internals"; | |||
| import { RiceBowl } from "@mui/icons-material"; | |||
| type Props = { | |||
| apiRef: MutableRefObject<GridApiCommunity> | |||
| }; | |||
| type EntryError = | |||
| | { | |||
| [field in keyof QcChecksInputs]?: string; | |||
| } | |||
| | undefined; | |||
| type QcRow = TableRow<Partial<QcChecksInputs>, EntryError> | |||
| const QcDetails: React.FC<Props> = ({ apiRef }) => { | |||
| const { | |||
| t, | |||
| i18n: { language }, | |||
| } = useTranslation(); | |||
| const { | |||
| register, | |||
| formState: { errors, defaultValues, touchedFields }, | |||
| watch, | |||
| control, | |||
| setValue, | |||
| getValues, | |||
| reset, | |||
| resetField, | |||
| setError, | |||
| clearErrors, | |||
| } = useFormContext<CreateItemInputs>(); | |||
| // const apiRef = useGridApiRef(); | |||
| const qcColumns = useMemo<GridColDef[]>( | |||
| () => [ | |||
| { | |||
| field: "name", | |||
| headerName: "name", | |||
| flex: 1, | |||
| // editable: true, | |||
| }, | |||
| { | |||
| field: "code", | |||
| headerName: "code", | |||
| flex: 1, | |||
| // editable: true, | |||
| }, | |||
| { | |||
| field: "description", | |||
| headerName: "Description", | |||
| flex: 1, | |||
| // editable: true, | |||
| }, | |||
| { | |||
| field: "instruction", | |||
| headerName: "Instruction", | |||
| flex: 1, | |||
| editable: true, | |||
| }, | |||
| { | |||
| field: "lowerLimit", | |||
| headerName: "lowerLimit", | |||
| flex: 1, | |||
| editable: true, | |||
| type: "number", | |||
| renderEditCell(params: GridRenderEditCellParams<QcRow>) { | |||
| const errorMessage = params.row._error?.[params.field as keyof QcChecksInputs]; | |||
| const content = ( | |||
| <GridEditInputCell | |||
| {...params} | |||
| // inputProps={{ min: 0 }} | |||
| /> | |||
| ); | |||
| return errorMessage ? ( | |||
| <Tooltip title={t(errorMessage)}> | |||
| <Box width="100%">{content}</Box> | |||
| </Tooltip> | |||
| ) : ( | |||
| content | |||
| ); | |||
| }, | |||
| }, | |||
| { | |||
| field: "upperLimit", | |||
| headerName: "upperLimit", | |||
| flex: 1, | |||
| editable: true, | |||
| type: "number", | |||
| renderEditCell(params: GridRenderEditCellParams<QcRow>) { | |||
| const errorMessage = params.row._error?.[params.field as keyof QcChecksInputs]; | |||
| const content = ( | |||
| <GridEditInputCell | |||
| {...params} | |||
| // inputProps={{ min: 0 }} | |||
| /> | |||
| ); | |||
| return errorMessage ? ( | |||
| <Tooltip title={t(errorMessage)}> | |||
| <Box width="100%">{content}</Box> | |||
| </Tooltip> | |||
| ) : ( | |||
| content | |||
| ); | |||
| }, | |||
| }, | |||
| ], | |||
| [] | |||
| ); | |||
| const validationTest = useCallback( | |||
| ( | |||
| newRow: GridRowModel<TableRow<Partial<QcChecksInputs>, EntryError>>, | |||
| // rowModel: GridRowSelectionModel | |||
| ): EntryError => { | |||
| const error: EntryError = {}; | |||
| console.log(newRow); | |||
| if (!newRow.lowerLimit) { | |||
| error["lowerLimit"] = "lower limit cannot be null" | |||
| } | |||
| // if (rowModel.find(r => r == newRow.id)) { | |||
| // } | |||
| if (newRow.lowerLimit && newRow.upperLimit && newRow.lowerLimit > newRow.upperLimit) { | |||
| error["lowerLimit"] = "lower limit should not be greater than upper limit" | |||
| error["upperLimit"] = "lower limit should not be greater than upper limit" | |||
| } | |||
| return Object.keys(error).length > 0 ? error : undefined; | |||
| }, | |||
| [] | |||
| ); | |||
| return ( | |||
| <Grid container> | |||
| <Grid item xs={12}> | |||
| <InputDataGrid<CreateItemInputs, QcChecksInputs, EntryError> | |||
| apiRef={apiRef} | |||
| checkboxSelection={true} | |||
| _formKey={"qcChecks"} | |||
| columns={qcColumns} | |||
| validateRow={validationTest} | |||
| /> | |||
| </Grid> | |||
| </Grid> | |||
| ); | |||
| }; | |||
| export default QcDetails; | |||
| @@ -1,6 +1,7 @@ | |||
| "use client" | |||
| import { | |||
| Dispatch, | |||
| MutableRefObject, | |||
| SetStateAction, | |||
| useCallback, | |||
| useEffect, | |||
| @@ -20,6 +21,7 @@ import { | |||
| GridRowModel, | |||
| GridRowModes, | |||
| GridRowModesModel, | |||
| GridRowSelectionModel, | |||
| GridToolbarContainer, | |||
| GridValidRowModel, | |||
| useGridApiRef, | |||
| @@ -31,6 +33,7 @@ import CancelIcon from "@mui/icons-material/Cancel"; | |||
| import { Add } from "@mui/icons-material"; | |||
| import { Box, Button, Typography } from "@mui/material"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { GridApiCommunity } from "@mui/x-data-grid/internals"; | |||
| interface ResultWithId { | |||
| id: string | number; | |||
| @@ -38,19 +41,44 @@ interface ResultWithId { | |||
| // export type InputGridProps = { | |||
| // [key: string]: any | |||
| // } | |||
| export type TableRow<T, E> = Partial< | |||
| T & { | |||
| interface DefaultResult<E> { | |||
| _isNew: boolean; | |||
| _error: E; | |||
| } | |||
| interface SelectionResult<E> { | |||
| active: boolean; | |||
| _isNew: boolean; | |||
| _error: E; | |||
| } | |||
| type Result<E> = DefaultResult<E> | SelectionResult<E> | |||
| export type TableRow<V, E> = Partial< | |||
| V & { | |||
| isActive: boolean | undefined; | |||
| _isNew: boolean; | |||
| _error: E; | |||
| } & ResultWithId | |||
| >; | |||
| export type InputDataGridProps<T, E> = { | |||
| export interface InputDataGridProps<T, V, E> { | |||
| // needAdd: boolean | undefined; | |||
| apiRef: MutableRefObject<GridApiCommunity> | |||
| checkboxSelection: false | undefined; | |||
| _formKey: keyof T; | |||
| columns: GridColDef[]; | |||
| validateRow: (newRow: GridRowModel<TableRow<T, E>>) => E; | |||
| validateRow: (newRow: GridRowModel<TableRow<V, E>>) => E; | |||
| }; | |||
| export interface SelectionInputDataGridProps<T, V, E> { // thinking how do | |||
| apiRef: MutableRefObject<GridApiCommunity> | |||
| checkboxSelection: true; | |||
| _formKey: keyof T; | |||
| columns: GridColDef[]; | |||
| validateRow: (newRow: GridRowModel<TableRow<V, E>>) => E; | |||
| } | |||
| export type Props<T, V, E> = InputDataGridProps<T, V, E> | SelectionInputDataGridProps<T, V, E> | |||
| export class ProcessRowUpdateError<T, E> extends Error { | |||
| public readonly row: T; | |||
| public readonly errors: E | undefined; | |||
| @@ -62,12 +90,16 @@ export class ProcessRowUpdateError<T, E> extends Error { | |||
| Object.setPrototypeOf(this, ProcessRowUpdateError.prototype); | |||
| } | |||
| } | |||
| function InputDataGrid<T, E>({ | |||
| // T == CreatexxxInputs | |||
| // V == target field input inside CreatexxxInputs, e.g. qcChecks: ItemQc[], V = ItemQc | |||
| // E == error | |||
| function InputDataGrid<T, V, E>({ | |||
| apiRef, | |||
| checkboxSelection, | |||
| _formKey, | |||
| columns, | |||
| validateRow, | |||
| }: InputDataGridProps<T, E>) { | |||
| }: Props<T, V, E>) { | |||
| const { | |||
| t, | |||
| // i18n: { language }, | |||
| @@ -76,17 +108,24 @@ function InputDataGrid<T, E>({ | |||
| const { setValue, getValues } = useFormContext(); | |||
| const [rowModesModel, setRowModesModel] = | |||
| useState<GridRowModesModel>({}); | |||
| const apiRef = useGridApiRef(); | |||
| const getRowId = useCallback<GridRowIdGetter<TableRow<T, E>>>( | |||
| // const apiRef = useGridApiRef(); | |||
| const getRowId = useCallback<GridRowIdGetter<TableRow<V, E>>>( | |||
| (row) => row.id!! as number, | |||
| [] | |||
| ); | |||
| const list: TableRow<T, E>[] = getValues(formKey); | |||
| const [rows, setRows] = useState<TableRow<T, E>[]>(() => { | |||
| const list: TableRow<T, E>[] = getValues(formKey); | |||
| const list: TableRow<V, E>[] = getValues(formKey); | |||
| const [rows, setRows] = useState<TableRow<V, E>[]>(() => { | |||
| const list: TableRow<V, E>[] = getValues(formKey); | |||
| return list && list.length > 0 ? list : []; | |||
| }); | |||
| const originalRows = list && list.length > 0 ? list : []; | |||
| // const originalRowModel = originalRows.filter((li) => li.isActive).map(i => i.id) as GridRowSelectionModel | |||
| const [rowSelectionModel, setRowSelectionModel] = useState<GridRowSelectionModel>(() => { | |||
| // const rowModel = list.filter((li) => li.isActive).map(i => i.id) as GridRowSelectionModel | |||
| const rowModel: GridRowSelectionModel = getValues(`${formKey}_active`) as GridRowSelectionModel | |||
| console.log(rowModel) | |||
| return rowModel | |||
| }); | |||
| const handleSave = useCallback( | |||
| (id: GridRowId) => () => { | |||
| @@ -109,8 +148,8 @@ function InputDataGrid<T, E>({ | |||
| const processRowUpdate = useCallback( | |||
| ( | |||
| newRow: GridRowModel<TableRow<T, E>>, | |||
| originalRow: GridRowModel<TableRow<T, E>> | |||
| newRow: GridRowModel<TableRow<V, E>>, | |||
| originalRow: GridRowModel<TableRow<V, E>> | |||
| ) => { | |||
| ///////////////// | |||
| // validation here | |||
| @@ -127,7 +166,7 @@ function InputDataGrid<T, E>({ | |||
| const { _isNew, _error, ...updatedRow } = newRow; | |||
| const rowToSave = { | |||
| ...updatedRow, | |||
| } as TableRow<T, E>; /// test | |||
| } as TableRow<V, E>; /// test | |||
| console.log(rowToSave) | |||
| setRows((rw) => | |||
| rw.map((r) => (getRowId(r) === getRowId(originalRow) ? rowToSave : r)) | |||
| @@ -138,7 +177,7 @@ function InputDataGrid<T, E>({ | |||
| ); | |||
| const addRow = useCallback(() => { | |||
| const newEntry = { id: Date.now(), _isNew: true } as TableRow<T, E>; | |||
| const newEntry = { id: Date.now(), _isNew: true } as TableRow<V, E>; | |||
| setRows((prev) => [...prev, newEntry]); | |||
| setRowModesModel((model) => ({ | |||
| ...model, | |||
| @@ -225,7 +264,7 @@ function InputDataGrid<T, E>({ | |||
| }, | |||
| }, | |||
| ], | |||
| [columns,rowModesModel, handleSave, handleCancel, handleDelete] | |||
| [columns, rowModesModel, handleSave, handleCancel, handleDelete] | |||
| ); | |||
| // sync useForm | |||
| useEffect(() => { | |||
| @@ -266,9 +305,20 @@ function InputDataGrid<T, E>({ | |||
| <StyledDataGrid | |||
| // {...props} | |||
| // getRowId={getRowId as GridRowIdGetter<GridValidRowModel>} | |||
| // checkbox selection | |||
| checkboxSelection={checkboxSelection} | |||
| disableRowSelectionOnClick={checkboxSelection} | |||
| onRowSelectionModelChange={(newRowSelectionModel) => { | |||
| if (checkboxSelection) { | |||
| setRowSelectionModel(newRowSelectionModel); | |||
| setValue("qcChecks_active", newRowSelectionModel) | |||
| } | |||
| }} | |||
| rowSelectionModel={rowSelectionModel} | |||
| apiRef={apiRef} | |||
| rows={rows} | |||
| columns={_columns} | |||
| columns={!checkboxSelection ? _columns : columns} | |||
| editMode="row" | |||
| autoHeight | |||
| sx={{ | |||
| @@ -295,13 +345,13 @@ function InputDataGrid<T, E>({ | |||
| } | |||
| return classname; | |||
| }} | |||
| slots={{ | |||
| slots={!checkboxSelection ? { | |||
| footer: FooterToolbar, | |||
| noRowsOverlay: NoRowsOverlay, | |||
| }} | |||
| slotProps={{ | |||
| } : undefined} | |||
| slotProps={!checkboxSelection ? { | |||
| footer: { child: footer }, | |||
| }} | |||
| } : undefined} | |||
| /> | |||
| ) | |||
| } | |||
| @@ -12,28 +12,21 @@ import { TypeEnum } from "@/app/utils/typeEnum"; | |||
| type Props = { | |||
| items: ItemsResult[]; | |||
| type: TypeEnum; | |||
| }; | |||
| type SearchQuery = Partial<Omit<ItemsResult, "id">>; | |||
| type SearchParamNames = keyof SearchQuery; | |||
| const ItemsSearch: React.FC<Props> = ({ items, type }) => { | |||
| const ItemsSearch: React.FC<Props> = ({ items }) => { | |||
| const [filteredItems, setFilteredItems] = useState<ItemsResult[]>(items); | |||
| const { t } = useTranslation(type.toString()); | |||
| const { t } = useTranslation("items"); | |||
| const router = useRouter(); | |||
| const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | |||
| () => { | |||
| var searchCriteria: Criterion<SearchParamNames>[] = [] | |||
| if (type === TypeEnum.MATERIAL) { | |||
| searchCriteria = [ | |||
| { label: t("Code"), paramName: "code", type: "text" }, | |||
| { label: t("Name"), paramName: "name", type: "text" }, | |||
| ] | |||
| } | |||
| if (type === TypeEnum.MATERIAL) { | |||
| } | |||
| var searchCriteria: Criterion<SearchParamNames>[] = [ | |||
| { label: t("Code"), paramName: "code", type: "text" }, | |||
| { label: t("Name"), paramName: "name", type: "text" }, | |||
| ] | |||
| return searchCriteria | |||
| }, | |||
| [t, items] | |||
| @@ -41,9 +34,9 @@ const ItemsSearch: React.FC<Props> = ({ items, type }) => { | |||
| const onDetailClick = useCallback( | |||
| (item: ItemsResult) => { | |||
| router.push(`/settings/${type}/edit?id=${item.id}`); | |||
| router.push(`/settings/items/edit?id=${item.id}`); | |||
| }, | |||
| [type, router] | |||
| [router] | |||
| ); | |||
| const onDeleteClick = useCallback( | |||
| @@ -88,22 +81,10 @@ const ItemsSearch: React.FC<Props> = ({ items, type }) => { | |||
| onSearch={(query) => { | |||
| setFilteredItems( | |||
| items.filter((pm) => { | |||
| if (type === TypeEnum.MATERIAL) { | |||
| return ( | |||
| pm.code.toLowerCase().includes(query.code.toLowerCase()) && | |||
| pm.name.toLowerCase().includes(query.name.toLowerCase()) | |||
| ); | |||
| } else if (type === TypeEnum.PRODUCT) { | |||
| return ( | |||
| pm.code.toLowerCase().includes(query.code.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()) | |||
| ); | |||
| } | |||
| return ( | |||
| pm.code.toLowerCase().includes(query.code.toLowerCase()) && | |||
| pm.name.toLowerCase().includes(query.name.toLowerCase()) | |||
| ); | |||
| }) | |||
| ); | |||
| }} | |||
| @@ -10,15 +10,15 @@ interface SubComponents { | |||
| } | |||
| type Props = { | |||
| type: TypeEnum; | |||
| // type: TypeEnum; | |||
| }; | |||
| const ItemsSearchWrapper: React.FC<Props> & SubComponents = async ({ | |||
| type, | |||
| // type, | |||
| }) => { | |||
| console.log(type) | |||
| // console.log(type) | |||
| var result = await fetchAllItems() | |||
| return <ItemsSearch items={result} type={type} />; | |||
| return <ItemsSearch items={result} />; | |||
| }; | |||
| ItemsSearchWrapper.Loading = ItemsSearchLoading; | |||
| @@ -13,7 +13,7 @@ const Logo: React.FC<Props> = ({ width, height }) => { | |||
| > | |||
| <g | |||
| id="svgGroup" | |||
| stroke-linecap="round" | |||
| strokeLinecap="round" | |||
| fill-rule="evenodd" | |||
| font-size="9pt" | |||
| stroke="#000" | |||
| @@ -181,20 +181,20 @@ const NavigationContent: React.FC = () => { | |||
| label: "User Group", | |||
| path: "/settings/user", | |||
| }, | |||
| // { | |||
| // icon: <RequestQuote />, | |||
| // label: "Material", | |||
| // path: "/settings/material", | |||
| // }, | |||
| // { | |||
| // icon: <RequestQuote />, | |||
| // label: "By-product", | |||
| // path: "/settings/byProduct", | |||
| // }, | |||
| { | |||
| icon: <RequestQuote />, | |||
| label: "Material", | |||
| path: "/settings/material", | |||
| }, | |||
| { | |||
| icon: <RequestQuote />, | |||
| label: "By-product", | |||
| path: "/settings/byProduct", | |||
| }, | |||
| { | |||
| icon: <RequestQuote />, | |||
| label: "Product", | |||
| path: "/settings/product", | |||
| label: "Items", | |||
| path: "/settings/items", | |||
| }, | |||
| { | |||
| icon: <RequestQuote />, | |||