@@ -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 { SearchParams } from "@/app/utils/fetchUtil"; | ||||
import { TypeEnum } from "@/app/utils/typeEnum"; | import { TypeEnum } from "@/app/utils/typeEnum"; | ||||
import CreateProductMaterial from "@/components/CreateItem"; | |||||
import CreateItem from "@/components/CreateItem"; | |||||
import { I18nProvider, getServerI18n } from "@/i18n"; | import { I18nProvider, getServerI18n } from "@/i18n"; | ||||
import { Typography } from "@mui/material"; | import { Typography } from "@mui/material"; | ||||
import isString from "lodash/isString"; | import isString from "lodash/isString"; | ||||
@@ -8,13 +8,13 @@ import isString from "lodash/isString"; | |||||
type Props = {} & SearchParams; | type Props = {} & SearchParams; | ||||
const materialSetting: React.FC<Props> = async ({ 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 ( | return ( | ||||
<> | <> | ||||
{/* <Typography variant="h4">{t("Create Material")}</Typography> */} | {/* <Typography variant="h4">{t("Create Material")}</Typography> */} | ||||
<I18nProvider namespaces={[type]}> | |||||
<CreateProductMaterial type={type}/> | |||||
<I18nProvider namespaces={["items"]}> | |||||
<CreateItem /> | |||||
</I18nProvider> | </I18nProvider> | ||||
</> | </> | ||||
); | ); |
@@ -9,7 +9,7 @@ import { notFound } from "next/navigation"; | |||||
type Props = {} & SearchParams; | type Props = {} & SearchParams; | ||||
const productSetting: React.FC<Props> = async ({ searchParams }) => { | const productSetting: React.FC<Props> = async ({ searchParams }) => { | ||||
const type = TypeEnum.PRODUCT; | |||||
const type = "items"; | |||||
const { t } = await getServerI18n(type); | const { t } = await getServerI18n(type); | ||||
const id = isString(searchParams["id"]) | const id = isString(searchParams["id"]) | ||||
? parseInt(searchParams["id"]) | ? parseInt(searchParams["id"]) | ||||
@@ -21,7 +21,7 @@ const productSetting: React.FC<Props> = async ({ searchParams }) => { | |||||
<> | <> | ||||
{/* <Typography variant="h4">{t("Create Material")}</Typography> */} | {/* <Typography variant="h4">{t("Create Material")}</Typography> */} | ||||
<I18nProvider namespaces={[type]}> | <I18nProvider namespaces={[type]}> | ||||
<CreateProductMaterial type={type} id={id} /> | |||||
<CreateProductMaterial id={id} /> | |||||
</I18nProvider> | </I18nProvider> | ||||
</> | </> | ||||
); | ); |
@@ -1,5 +1,5 @@ | |||||
import { TypeEnum } from "@/app/utils/typeEnum"; | import { TypeEnum } from "@/app/utils/typeEnum"; | ||||
import MaterialSearch from "@/components/ItemsSearch"; | |||||
import ItemsSearch from "@/components/ItemsSearch"; | |||||
import { getServerI18n } from "@/i18n"; | import { getServerI18n } from "@/i18n"; | ||||
import Add from "@mui/icons-material/Add"; | import Add from "@mui/icons-material/Add"; | ||||
import Button from "@mui/material/Button"; | import Button from "@mui/material/Button"; | ||||
@@ -29,17 +29,17 @@ const productSetting: React.FC = async () => { | |||||
<Typography variant="h4" marginInlineEnd={2}> | <Typography variant="h4" marginInlineEnd={2}> | ||||
{t("Product")} | {t("Product")} | ||||
</Typography> | </Typography> | ||||
<Button | |||||
{/* <Button | |||||
variant="contained" | variant="contained" | ||||
startIcon={<Add />} | startIcon={<Add />} | ||||
LinkComponent={Link} | LinkComponent={Link} | ||||
href="product/create" | href="product/create" | ||||
> | > | ||||
{t("Create product")} | {t("Create product")} | ||||
</Button> | |||||
</Button> */} | |||||
</Stack> | </Stack> | ||||
<Suspense fallback={<MaterialSearch.Loading />}> | |||||
<MaterialSearch type={project} /> | |||||
<Suspense fallback={<ItemsSearch.Loading />}> | |||||
<ItemsSearch /> | |||||
</Suspense> | </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 { 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 { HTMLInputTypeAttribute } from "react"; | |||||
import { CreateItemResponse } from "../../utils"; | 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 = { | export type CreateItemInputs = { | ||||
id?: string | number | id?: string | number | ||||
code: string; | code: string; | ||||
@@ -24,14 +27,10 @@ export type CreateItemInputs = { | |||||
remarks?: string | undefined; | remarks?: string | undefined; | ||||
shelfLife?: Number | undefined; | shelfLife?: Number | undefined; | ||||
countryOfOrigin?: string | 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) => { | export const saveItem = async (data: CreateItemInputs) => { | ||||
@@ -43,4 +42,4 @@ export const saveItem = async (data: CreateItemInputs) => { | |||||
}); | }); | ||||
revalidateTag("items"); | revalidateTag("items"); | ||||
return item | return item | ||||
}; | |||||
}; |
@@ -2,7 +2,18 @@ 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 "./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 = { | export type ItemsResult = { | ||||
id: string | number | id: string | number | ||||
@@ -12,20 +23,15 @@ export type ItemsResult = { | |||||
remarks: string | undefined; | remarks: string | undefined; | ||||
shelfLife: Number | undefined; | shelfLife: Number | undefined; | ||||
countryOfOrigin: string | 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 | action?: any | ||||
} | } | ||||
export type Result = { | |||||
item: ItemsResult | |||||
qcChecks: ItemQc[] | |||||
} | |||||
export const fetchAllItems = cache(async () => { | export const fetchAllItems = cache(async () => { | ||||
return serverFetchJson<ItemsResult[]>(`${BASE_API_URL}/items`, { | return serverFetchJson<ItemsResult[]>(`${BASE_API_URL}/items`, { | ||||
next: { tags: ["items"] }, | next: { tags: ["items"] }, | ||||
@@ -34,7 +40,7 @@ export const fetchAllItems = cache(async () => { | |||||
export const fetchItem = cache(async (id: number) => { | 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"] }, | 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) { | export async function serverFetchJson<T>(...args: FetchParams) { | ||||
const response = await serverFetch(...args); | const response = await serverFetch(...args); | ||||
console.log(response.status) | |||||
if (response.ok) { | if (response.ok) { | ||||
if (response.status === 204) { | if (response.status === 204) { | ||||
return response.status as T | 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, | useForm, | ||||
} from "react-hook-form"; | } from "react-hook-form"; | ||||
import { deleteDialog } from "../Swal/CustomAlerts"; | 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 { Check, Close, EditNote } from "@mui/icons-material"; | ||||
import { TypeEnum } from "@/app/utils/typeEnum"; | import { TypeEnum } from "@/app/utils/typeEnum"; | ||||
import MaterialDetails from "./MaterialDetails"; | |||||
import ProductDetails from "./ProductDetails"; | import ProductDetails from "./ProductDetails"; | ||||
import { CreateItemResponse } from "@/app/api/utils"; | 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 = { | type Props = { | ||||
isEditMode: boolean; | isEditMode: boolean; | ||||
type: TypeEnum; | |||||
// type: TypeEnum; | |||||
defaultValues: Partial<CreateItemInputs> | undefined; | defaultValues: Partial<CreateItemInputs> | undefined; | ||||
qcChecks: ItemQc[] | |||||
}; | }; | ||||
const CreateItem: React.FC<Props> = ({ | const CreateItem: React.FC<Props> = ({ | ||||
isEditMode, | isEditMode, | ||||
type, | |||||
// type, | |||||
defaultValues, | defaultValues, | ||||
qcChecks | |||||
}) => { | }) => { | ||||
console.log(type) | |||||
// console.log(type) | |||||
const apiRef = useGridApiRef(); | |||||
const params = useSearchParams() | |||||
console.log(params.get("id")) | |||||
const [serverError, setServerError] = useState(""); | const [serverError, setServerError] = useState(""); | ||||
const [tabIndex, setTabIndex] = useState(0); | const [tabIndex, setTabIndex] = useState(0); | ||||
const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
const router = useRouter(); | 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 title = ""; | ||||
var mode = ""; | var mode = ""; | ||||
var redirPath = ""; | 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"; | 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) { | if (isEditMode) { | ||||
mode = "Edit"; | mode = "Edit"; | ||||
} else { | } else { | ||||
mode = "Create"; | mode = "Create"; | ||||
} | } | ||||
return [typeId, title, mode, redirPath]; | |||||
}, [type, isEditMode]); | |||||
console.log(typeId) | |||||
return [mode, redirPath]; | |||||
}, [isEditMode]); | |||||
// console.log(typeId) | |||||
const formProps = useForm<CreateItemInputs>({ | const formProps = useForm<CreateItemInputs>({ | ||||
defaultValues: defaultValues ? defaultValues : { | defaultValues: defaultValues ? defaultValues : { | ||||
typeId: typeId | |||||
}, | }, | ||||
}); | }); | ||||
const errors = formProps.formState.errors; | const errors = formProps.formState.errors; | ||||
const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>( | |||||
(_e, newValue) => { | |||||
setTabIndex(newValue); | |||||
}, | |||||
[], | |||||
); | |||||
const handleCancel = () => { | const handleCancel = () => { | ||||
router.replace(`/settings/${type}`); | |||||
router.replace(`/settings/product`); | |||||
}; | }; | ||||
const onSubmit = useCallback<SubmitHandler<CreateItemInputs>>( | |||||
const onSubmit = useCallback<SubmitHandler<CreateItemInputs & {}>>( | |||||
async (data, event) => { | async (data, event) => { | ||||
let hasErrors = false; | let hasErrors = false; | ||||
console.log("dasasdasd") | |||||
console.log(errors) | |||||
// console.log(apiRef.current.getCellValue(2, "lowerLimit")) | |||||
// apiRef.current. | |||||
try { | 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) { | if (hasErrors) { | ||||
setServerError(t("An error has occurred. Please try again later.")); | setServerError(t("An error has occurred. Please try again later.")); | ||||
return false; | return false; | ||||
} | } | ||||
console.log("data posted"); | console.log("data posted"); | ||||
console.log(data); | 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 | // return | ||||
// do api | // 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", | type: "required", | ||||
}); | }); | ||||
} else if (Boolean(response.id)) { | |||||
} else if (!Boolean(responseQ.id)) { | |||||
} else if (Boolean(responseI.id) && Boolean(responseQ.id)) { | |||||
router.replace(redirPath); | router.replace(redirPath); | ||||
} | } | ||||
} | } | ||||
@@ -157,7 +143,7 @@ const CreateItem: React.FC<Props> = ({ | |||||
console.log(e); | console.log(e); | ||||
} | } | ||||
}, | }, | ||||
[router, t] | |||||
[apiRef, router, t] | |||||
); | ); | ||||
// multiple tabs | // multiple tabs | ||||
@@ -174,19 +160,24 @@ const CreateItem: React.FC<Props> = ({ | |||||
component="form" | component="form" | ||||
onSubmit={formProps.handleSubmit(onSubmit, onSubmitError)} | 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 && ( | {serverError && ( | ||||
<Typography variant="body2" color="error" alignSelf="flex-end"> | <Typography variant="body2" color="error" alignSelf="flex-end"> | ||||
{serverError} | {serverError} | ||||
</Typography> | </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}> | <Stack direction="row" justifyContent="flex-end" gap={1}> | ||||
<Button | <Button | ||||
name="submit" | name="submit" | ||||
@@ -4,47 +4,48 @@ import CreateItemLoading from "./CreateItemLoading"; | |||||
import { CreateItemInputs } from "@/app/api/settings/item/actions"; | import { CreateItemInputs } from "@/app/api/settings/item/actions"; | ||||
import { notFound } from "next/navigation"; | import { notFound } from "next/navigation"; | ||||
import { fetchItem } from "@/app/api/settings/item"; | import { fetchItem } from "@/app/api/settings/item"; | ||||
import { fetchQcItems } from "@/app/api/settings/qcItem"; | |||||
interface SubComponents { | interface SubComponents { | ||||
Loading: typeof CreateItemLoading; | Loading: typeof CreateItemLoading; | ||||
} | } | ||||
type Props = { | type Props = { | ||||
id?: number | id?: number | ||||
type: TypeEnum; | |||||
// type: TypeEnum; | |||||
}; | }; | ||||
const CreateItemWrapper: React.FC<Props> & | const CreateItemWrapper: React.FC<Props> & | ||||
SubComponents = async ({ type, id }) => { | |||||
SubComponents = async ({ id }) => { | |||||
var result | var result | ||||
var defaultValues: Partial<CreateItemInputs> | undefined | |||||
console.log(type) | |||||
var defaultValues: Partial<CreateItemInputs> | undefined | |||||
// console.log(type) | |||||
var qcChecks | |||||
if (id) { | if (id) { | ||||
result = await fetchItem(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 = { | 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 ( | return ( | ||||
<CreateItem | <CreateItem | ||||
isEditMode={Boolean(id)} | isEditMode={Boolean(id)} | ||||
type={type} | |||||
defaultValues={defaultValues} | 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"; | import { CreateItemInputs } from "@/app/api/settings/item/actions"; | ||||
type Props = { | 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 { | const { | ||||
t, | t, | ||||
i18n: { language }, | i18n: { language }, | ||||
@@ -49,57 +41,57 @@ const ProductDetails: React.FC<Props> = ({ | |||||
setError, | setError, | ||||
clearErrors, | clearErrors, | ||||
} = useFormContext<CreateItemInputs>(); | } = 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 ( | return ( | ||||
<Card sx={{ display: "block" }}> | <Card sx={{ display: "block" }}> | ||||
@@ -131,6 +123,17 @@ const ProductDetails: React.FC<Props> = ({ | |||||
helperText={errors.code?.message} | helperText={errors.code?.message} | ||||
/> | /> | ||||
</Grid> | </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}> | <Grid item xs={6}> | ||||
<TextField | <TextField | ||||
label={t("description")} | label={t("description")} | ||||
@@ -162,93 +165,6 @@ const ProductDetails: React.FC<Props> = ({ | |||||
helperText={errors.countryOfOrigin?.message} | helperText={errors.countryOfOrigin?.message} | ||||
/> | /> | ||||
</Grid> | </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}> | <Grid item xs={6}> | ||||
<TextField | <TextField | ||||
label={t("remarks")} | label={t("remarks")} | ||||
@@ -262,17 +178,17 @@ const ProductDetails: React.FC<Props> = ({ | |||||
</Grid> | </Grid> | ||||
<Grid item xs={6}> | <Grid item xs={6}> | ||||
<TextField | <TextField | ||||
label={t("netWeight")} | |||||
label={t("maxQty")} | |||||
type="number" | type="number" | ||||
fullWidth | fullWidth | ||||
inputProps={NumberInputProps} | inputProps={NumberInputProps} | ||||
{...register("netWeight", { | |||||
{...register("maxQty", { | |||||
valueAsNumber: true, | valueAsNumber: true, | ||||
min: 0, | 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> | ||||
{/* <Grid item xs={6}> | {/* <Grid item xs={6}> | ||||
@@ -296,7 +212,7 @@ const ProductDetails: React.FC<Props> = ({ | |||||
validateRow={validationTest} | validateRow={validationTest} | ||||
/> | /> | ||||
</Grid>*/} | </Grid>*/} | ||||
</Grid> | |||||
</Grid> | |||||
</Box> | </Box> | ||||
</CardContent> | </CardContent> | ||||
</Card> | </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" | "use client" | ||||
import { | import { | ||||
Dispatch, | Dispatch, | ||||
MutableRefObject, | |||||
SetStateAction, | SetStateAction, | ||||
useCallback, | useCallback, | ||||
useEffect, | useEffect, | ||||
@@ -20,6 +21,7 @@ import { | |||||
GridRowModel, | GridRowModel, | ||||
GridRowModes, | GridRowModes, | ||||
GridRowModesModel, | GridRowModesModel, | ||||
GridRowSelectionModel, | |||||
GridToolbarContainer, | GridToolbarContainer, | ||||
GridValidRowModel, | GridValidRowModel, | ||||
useGridApiRef, | useGridApiRef, | ||||
@@ -31,6 +33,7 @@ import CancelIcon from "@mui/icons-material/Cancel"; | |||||
import { Add } from "@mui/icons-material"; | import { Add } from "@mui/icons-material"; | ||||
import { Box, Button, Typography } from "@mui/material"; | import { Box, Button, Typography } from "@mui/material"; | ||||
import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||
import { GridApiCommunity } from "@mui/x-data-grid/internals"; | |||||
interface ResultWithId { | interface ResultWithId { | ||||
id: string | number; | id: string | number; | ||||
@@ -38,19 +41,44 @@ interface ResultWithId { | |||||
// export type InputGridProps = { | // export type InputGridProps = { | ||||
// [key: string]: any | // [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; | _isNew: boolean; | ||||
_error: E; | _error: E; | ||||
} & ResultWithId | } & ResultWithId | ||||
>; | >; | ||||
export type InputDataGridProps<T, E> = { | |||||
export interface InputDataGridProps<T, V, E> { | |||||
// needAdd: boolean | undefined; | |||||
apiRef: MutableRefObject<GridApiCommunity> | |||||
checkboxSelection: false | undefined; | |||||
_formKey: keyof T; | _formKey: keyof T; | ||||
columns: GridColDef[]; | 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 { | export class ProcessRowUpdateError<T, E> extends Error { | ||||
public readonly row: T; | public readonly row: T; | ||||
public readonly errors: E | undefined; | public readonly errors: E | undefined; | ||||
@@ -62,12 +90,16 @@ export class ProcessRowUpdateError<T, E> extends Error { | |||||
Object.setPrototypeOf(this, ProcessRowUpdateError.prototype); | 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, | _formKey, | ||||
columns, | columns, | ||||
validateRow, | validateRow, | ||||
}: InputDataGridProps<T, E>) { | |||||
}: Props<T, V, E>) { | |||||
const { | const { | ||||
t, | t, | ||||
// i18n: { language }, | // i18n: { language }, | ||||
@@ -76,17 +108,24 @@ function InputDataGrid<T, E>({ | |||||
const { setValue, getValues } = useFormContext(); | const { setValue, getValues } = useFormContext(); | ||||
const [rowModesModel, setRowModesModel] = | const [rowModesModel, setRowModesModel] = | ||||
useState<GridRowModesModel>({}); | 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, | (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 : []; | return list && list.length > 0 ? list : []; | ||||
}); | }); | ||||
const originalRows = 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( | const handleSave = useCallback( | ||||
(id: GridRowId) => () => { | (id: GridRowId) => () => { | ||||
@@ -109,8 +148,8 @@ function InputDataGrid<T, E>({ | |||||
const processRowUpdate = useCallback( | 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 | // validation here | ||||
@@ -127,7 +166,7 @@ function InputDataGrid<T, E>({ | |||||
const { _isNew, _error, ...updatedRow } = newRow; | const { _isNew, _error, ...updatedRow } = newRow; | ||||
const rowToSave = { | const rowToSave = { | ||||
...updatedRow, | ...updatedRow, | ||||
} as TableRow<T, E>; /// test | |||||
} as TableRow<V, E>; /// test | |||||
console.log(rowToSave) | console.log(rowToSave) | ||||
setRows((rw) => | setRows((rw) => | ||||
rw.map((r) => (getRowId(r) === getRowId(originalRow) ? rowToSave : r)) | rw.map((r) => (getRowId(r) === getRowId(originalRow) ? rowToSave : r)) | ||||
@@ -138,7 +177,7 @@ function InputDataGrid<T, E>({ | |||||
); | ); | ||||
const addRow = useCallback(() => { | 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]); | setRows((prev) => [...prev, newEntry]); | ||||
setRowModesModel((model) => ({ | setRowModesModel((model) => ({ | ||||
...model, | ...model, | ||||
@@ -225,7 +264,7 @@ function InputDataGrid<T, E>({ | |||||
}, | }, | ||||
}, | }, | ||||
], | ], | ||||
[columns,rowModesModel, handleSave, handleCancel, handleDelete] | |||||
[columns, rowModesModel, handleSave, handleCancel, handleDelete] | |||||
); | ); | ||||
// sync useForm | // sync useForm | ||||
useEffect(() => { | useEffect(() => { | ||||
@@ -266,9 +305,20 @@ function InputDataGrid<T, E>({ | |||||
<StyledDataGrid | <StyledDataGrid | ||||
// {...props} | // {...props} | ||||
// getRowId={getRowId as GridRowIdGetter<GridValidRowModel>} | // 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} | apiRef={apiRef} | ||||
rows={rows} | rows={rows} | ||||
columns={_columns} | |||||
columns={!checkboxSelection ? _columns : columns} | |||||
editMode="row" | editMode="row" | ||||
autoHeight | autoHeight | ||||
sx={{ | sx={{ | ||||
@@ -295,13 +345,13 @@ function InputDataGrid<T, E>({ | |||||
} | } | ||||
return classname; | return classname; | ||||
}} | }} | ||||
slots={{ | |||||
slots={!checkboxSelection ? { | |||||
footer: FooterToolbar, | footer: FooterToolbar, | ||||
noRowsOverlay: NoRowsOverlay, | noRowsOverlay: NoRowsOverlay, | ||||
}} | |||||
slotProps={{ | |||||
} : undefined} | |||||
slotProps={!checkboxSelection ? { | |||||
footer: { child: footer }, | footer: { child: footer }, | ||||
}} | |||||
} : undefined} | |||||
/> | /> | ||||
) | ) | ||||
} | } | ||||
@@ -12,28 +12,21 @@ import { TypeEnum } from "@/app/utils/typeEnum"; | |||||
type Props = { | type Props = { | ||||
items: ItemsResult[]; | items: ItemsResult[]; | ||||
type: TypeEnum; | |||||
}; | }; | ||||
type SearchQuery = Partial<Omit<ItemsResult, "id">>; | type SearchQuery = Partial<Omit<ItemsResult, "id">>; | ||||
type SearchParamNames = keyof SearchQuery; | type SearchParamNames = keyof SearchQuery; | ||||
const ItemsSearch: React.FC<Props> = ({ items, type }) => { | |||||
const ItemsSearch: React.FC<Props> = ({ items }) => { | |||||
const [filteredItems, setFilteredItems] = useState<ItemsResult[]>(items); | const [filteredItems, setFilteredItems] = useState<ItemsResult[]>(items); | ||||
const { t } = useTranslation(type.toString()); | |||||
const { t } = useTranslation("items"); | |||||
const router = useRouter(); | const router = useRouter(); | ||||
const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | 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 | return searchCriteria | ||||
}, | }, | ||||
[t, items] | [t, items] | ||||
@@ -41,9 +34,9 @@ const ItemsSearch: React.FC<Props> = ({ items, type }) => { | |||||
const onDetailClick = useCallback( | const onDetailClick = useCallback( | ||||
(item: ItemsResult) => { | (item: ItemsResult) => { | ||||
router.push(`/settings/${type}/edit?id=${item.id}`); | |||||
router.push(`/settings/items/edit?id=${item.id}`); | |||||
}, | }, | ||||
[type, router] | |||||
[router] | |||||
); | ); | ||||
const onDeleteClick = useCallback( | const onDeleteClick = useCallback( | ||||
@@ -88,22 +81,10 @@ const ItemsSearch: React.FC<Props> = ({ items, type }) => { | |||||
onSearch={(query) => { | onSearch={(query) => { | ||||
setFilteredItems( | setFilteredItems( | ||||
items.filter((pm) => { | 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 Props = { | ||||
type: TypeEnum; | |||||
// type: TypeEnum; | |||||
}; | }; | ||||
const ItemsSearchWrapper: React.FC<Props> & SubComponents = async ({ | const ItemsSearchWrapper: React.FC<Props> & SubComponents = async ({ | ||||
type, | |||||
// type, | |||||
}) => { | }) => { | ||||
console.log(type) | |||||
// console.log(type) | |||||
var result = await fetchAllItems() | var result = await fetchAllItems() | ||||
return <ItemsSearch items={result} type={type} />; | |||||
return <ItemsSearch items={result} />; | |||||
}; | }; | ||||
ItemsSearchWrapper.Loading = ItemsSearchLoading; | ItemsSearchWrapper.Loading = ItemsSearchLoading; | ||||
@@ -13,7 +13,7 @@ const Logo: React.FC<Props> = ({ width, height }) => { | |||||
> | > | ||||
<g | <g | ||||
id="svgGroup" | id="svgGroup" | ||||
stroke-linecap="round" | |||||
strokeLinecap="round" | |||||
fill-rule="evenodd" | fill-rule="evenodd" | ||||
font-size="9pt" | font-size="9pt" | ||||
stroke="#000" | stroke="#000" | ||||
@@ -181,20 +181,20 @@ const NavigationContent: React.FC = () => { | |||||
label: "User Group", | label: "User Group", | ||||
path: "/settings/user", | path: "/settings/user", | ||||
}, | }, | ||||
// { | |||||
// icon: <RequestQuote />, | |||||
// label: "Material", | |||||
// path: "/settings/material", | |||||
// }, | |||||
// { | |||||
// icon: <RequestQuote />, | |||||
// label: "By-product", | |||||
// path: "/settings/byProduct", | |||||
// }, | |||||
{ | { | ||||
icon: <RequestQuote />, | 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 />, | icon: <RequestQuote />, | ||||