@@ -9,7 +9,7 @@ import Link from "next/link"; | |||
import { Suspense } from "react"; | |||
export const metadata: Metadata = { | |||
title: "Claims", | |||
title: "Material", | |||
}; | |||
const materialSetting: React.FC = async () => { | |||
@@ -4,7 +4,24 @@ import { revalidateTag } from "next/cache"; | |||
import { BASE_API_URL } from "@/config/api"; | |||
export type CreateMaterialInputs = { | |||
id?: string | number | |||
code: string; | |||
name: string; | |||
isConsumables: boolean; | |||
description?: string | undefined; | |||
type?: string | undefined; | |||
remarks?: string | undefined; | |||
shelfLife?: Number | undefined; | |||
countryOfOrigin?: string | undefined; | |||
minHumid?: number | undefined; | |||
maxHumid?: number | undefined; | |||
minTemp?: number | undefined; | |||
maxTemp?: number | undefined; | |||
sampleRate?: number | undefined; | |||
passingRate?: number | undefined; | |||
netWeight?: number | undefined; | |||
uom?: any; | |||
weightUnit?: any; | |||
} | |||
export const saveMaterial = async (data: CreateMaterialInputs) => { | |||
@@ -1,6 +1,6 @@ | |||
import CreateStaff from "./CreateMaterial"; | |||
import CreateMaterialLoading from "./CreateMaterialLoading"; | |||
import { cookies } from 'next/headers' | |||
interface SubComponents { | |||
Loading: typeof CreateMaterialLoading; | |||
} | |||
@@ -12,7 +12,9 @@ type CreateMaterialProps = { | |||
type Props = CreateMaterialProps | |||
const CreateMaterialWrapper: React.FC<Props> & SubComponents = async (props) => { | |||
console.log(props) | |||
const cookieStore = await cookies() | |||
// console.log("==================cookieStore==================") | |||
// console.log(cookieStore) | |||
return <CreateStaff isEditMode={Boolean(props.isEditMode)}/> | |||
} | |||
@@ -1,54 +1,87 @@ | |||
"use client"; | |||
import { CreateMaterialInputs } from "@/app/api/settings/material/actions"; | |||
import { Box, Card, CardContent, Grid, Stack, TextField, Typography } from "@mui/material"; | |||
import { | |||
Box, | |||
Card, | |||
CardContent, | |||
Grid, | |||
Stack, | |||
TextField, | |||
Typography, | |||
} from "@mui/material"; | |||
import { useFormContext } from "react-hook-form"; | |||
import { useTranslation } from "react-i18next"; | |||
import ControlledAutoComplete from "../ControlledAutoComplete"; | |||
import InputDataGrid from "../useInputDataGrid"; | |||
import useInputDataGrid from "../useInputDataGrid"; | |||
import { useMemo, useState } from "react"; | |||
import { GridColDef, GridRowModesModel } from "@mui/x-data-grid"; | |||
import { | |||
InputDataGridProps, | |||
ResultWithId, | |||
} from "../useInputDataGrid/useInputDataGrid"; | |||
type Props = { | |||
isEditMode: boolean; | |||
}; | |||
export type EntryError = { | |||
[field in keyof CreateMaterialInputs]?: string; | |||
}; | |||
const MaterialDetails: React.FC<Props> = ({ isEditMode }) => { | |||
const { | |||
t, | |||
i18n: { language }, | |||
} = useTranslation(); | |||
const MaterialDetails: React.FC<Props> = ({ | |||
isEditMode, | |||
}) => { | |||
const { | |||
t, | |||
i18n: { language }, | |||
} = useTranslation(); | |||
const { | |||
register, | |||
formState: { errors, defaultValues, touchedFields }, | |||
watch, | |||
control, | |||
setValue, | |||
getValues, | |||
reset, | |||
resetField, | |||
setError, | |||
clearErrors, | |||
} = useFormContext<CreateMaterialInputs>(); | |||
const columns = useMemo<GridColDef[]>(() => [], []); | |||
const validationTest = (): EntryError => { | |||
const error: EntryError = {}; | |||
return error; | |||
}; | |||
const MaterialTypeInputGridProps: InputDataGridProps< | |||
Partial<CreateMaterialInputs>, | |||
EntryError | |||
> = { | |||
_formKey: "type", | |||
columns, | |||
validation: validationTest, | |||
}; | |||
const MaterialUomInputGridProps: InputDataGridProps< | |||
Partial<CreateMaterialInputs>, | |||
EntryError | |||
> = { | |||
_formKey: "uom", | |||
columns, | |||
validation: validationTest, | |||
}; | |||
const { DataGrid: MaterialTypeInputGrid } = useInputDataGrid( | |||
MaterialTypeInputGridProps | |||
); | |||
const { DataGrid: MaterialUomInputGrid } = useInputDataGrid( | |||
MaterialUomInputGridProps | |||
); | |||
const { | |||
register, | |||
formState: { errors, defaultValues, touchedFields }, | |||
watch, | |||
control, | |||
setValue, | |||
getValues, | |||
reset, | |||
resetField, | |||
setError, | |||
clearErrors | |||
} = useFormContext<CreateMaterialInputs>(); | |||
return ( | |||
<Card sx={{ display: "block" }}> | |||
<CardContent component={Stack} spacing={4}> | |||
<Box> | |||
<Typography variant="overline" display="block" marginBlockEnd={1}> | |||
<Typography variant="overline" display="block" marginBlockEnd={1}> | |||
{t("Material Details")} | |||
</Typography> | |||
<Grid container spacing={2} columns={{ xs: 6, sm: 12 }}> | |||
<Grid item xs={6}> | |||
<ControlledAutoComplete | |||
control={control} | |||
options={[]} | |||
name="name" | |||
label={t("name")} | |||
noOptionsText={t("No Option")} | |||
// disabled={isEditMode} | |||
/> | |||
</Grid> | |||
<Grid item xs={6}> | |||
<Grid item xs={6}> | |||
<TextField | |||
label={t("Name")} | |||
fullWidth | |||
@@ -58,10 +91,137 @@ const MaterialDetails: React.FC<Props> = ({ | |||
error={Boolean(errors.name)} | |||
/> | |||
</Grid> | |||
<Grid item xs={6}> | |||
<TextField | |||
label={t("Code")} | |||
fullWidth | |||
{...register("code", { | |||
required: "code required!", | |||
})} | |||
error={Boolean(errors.code)} | |||
/> | |||
</Grid> | |||
<Grid item xs={6}> | |||
<TextField | |||
label={t("description")} | |||
fullWidth | |||
{...register("description", { | |||
required: "description required!", | |||
})} | |||
error={Boolean(errors.description)} | |||
/> | |||
</Grid> | |||
<Grid item xs={6}> | |||
<TextField | |||
label={t("shelfLife")} | |||
fullWidth | |||
{...register("shelfLife", { | |||
required: "shelfLife required!", | |||
})} | |||
error={Boolean(errors.shelfLife)} | |||
/> | |||
</Grid> | |||
<Grid item xs={6}> | |||
<TextField | |||
label={t("countryOfOrigin")} | |||
fullWidth | |||
{...register("countryOfOrigin", { | |||
required: "countryOfOrigin required!", | |||
})} | |||
error={Boolean(errors.countryOfOrigin)} | |||
/> | |||
</Grid> | |||
<Grid item xs={6}> | |||
<TextField | |||
label={t("minHumid")} | |||
fullWidth | |||
{...register("minHumid", { | |||
required: "minHumid required!", | |||
})} | |||
error={Boolean(errors.minHumid)} | |||
/> | |||
</Grid> | |||
<Grid item xs={6}> | |||
<TextField | |||
label={t("maxHumid")} | |||
fullWidth | |||
{...register("maxHumid", { | |||
required: "maxHumid required!", | |||
})} | |||
error={Boolean(errors.maxHumid)} | |||
/> | |||
</Grid> | |||
<Grid item xs={6}> | |||
<TextField | |||
label={t("minTemp")} | |||
fullWidth | |||
{...register("minTemp", { | |||
required: "minTemp required!", | |||
})} | |||
error={Boolean(errors.minTemp)} | |||
/> | |||
</Grid> | |||
<Grid item xs={6}> | |||
<TextField | |||
label={t("maxTemp")} | |||
fullWidth | |||
{...register("maxTemp", { | |||
required: "maxTemp required!", | |||
})} | |||
error={Boolean(errors.maxTemp)} | |||
/> | |||
</Grid> | |||
<Grid item xs={6}> | |||
<TextField | |||
label={t("sampleRate")} | |||
fullWidth | |||
{...register("sampleRate", { | |||
required: "sampleRate required!", | |||
})} | |||
error={Boolean(errors.sampleRate)} | |||
/> | |||
</Grid> | |||
<Grid item xs={6}> | |||
<TextField | |||
label={t("passingRate")} | |||
fullWidth | |||
{...register("passingRate", { | |||
required: "passingRate required!", | |||
})} | |||
error={Boolean(errors.passingRate)} | |||
/> | |||
</Grid> | |||
<Grid item xs={6} /> | |||
<Grid item xs={6}> | |||
<TextField | |||
label={t("remarks")} | |||
fullWidth | |||
{...register("remarks", { | |||
required: "remarks required!", | |||
})} | |||
error={Boolean(errors.remarks)} | |||
/> | |||
</Grid> | |||
<Grid item xs={6}> | |||
<TextField | |||
label={t("netWeight")} | |||
fullWidth | |||
{...register("netWeight", { | |||
required: "netWeight required!", | |||
})} | |||
error={Boolean(errors.netWeight)} | |||
/> | |||
</Grid> | |||
<Grid item xs={6}> | |||
<MaterialTypeInputGrid /> | |||
</Grid> | |||
<Grid item xs={6}> | |||
<MaterialUomInputGrid /> | |||
</Grid> | |||
</Grid> | |||
</Box> | |||
</CardContent> | |||
</Card> | |||
); | |||
); | |||
}; | |||
export default MaterialDetails; |
@@ -39,6 +39,7 @@ interface Props { | |||
declare module "@mui/x-data-grid" { | |||
interface FooterPropsOverrides { | |||
onAdd: () => void; | |||
child?: React.ReactNode; | |||
} | |||
} | |||
@@ -7,18 +7,14 @@ import React, { useCallback, useEffect, useState } from "react"; | |||
import { useRouter } from "next/navigation"; | |||
type Props = { | |||
// abilities: string[] | |||
} | |||
const DashboardPage: React.FC<Props> = ({ | |||
// abilities | |||
}) => { | |||
const { t } = useTranslation("dashboard"); | |||
const router = useRouter(); | |||
// useEffect(()=> { | |||
// window.localStorage.setItem("abilities", JSON.stringify(abilities)) | |||
// }) | |||
return ( | |||
<ThemeProvider theme={theme}> | |||
<> | |||
@@ -7,6 +7,7 @@ import { useTranslation } from "react-i18next"; | |||
import SearchResults, { Column } from "../SearchResults"; | |||
import { EditNote } from "@mui/icons-material"; | |||
import { useRouter, useSearchParams } from "next/navigation"; | |||
import { GridDeleteIcon } from "@mui/x-data-grid"; | |||
type Props = { | |||
materials: MaterialResult[] | |||
@@ -17,7 +18,7 @@ type SearchParamNames = keyof SearchQuery; | |||
const MaterialSearch: React.FC<Props> = ({ | |||
materials | |||
}) => { | |||
const [filteredMaterials, setFilteredMaterials] = useState<MaterialResult[]>([]) | |||
const [filteredMaterials, setFilteredMaterials] = useState<MaterialResult[]>(materials) | |||
const { t } = useTranslation("materials"); | |||
const router = useRouter(); | |||
@@ -59,6 +60,7 @@ const router = useRouter(); | |||
{ | |||
name: "action", | |||
label: t(""), | |||
buttonIcon: <GridDeleteIcon />, | |||
onClick: onDeleteClick, | |||
}, | |||
@@ -7,7 +7,6 @@ interface SubComponents { | |||
} | |||
const MaterialSearchWrapper: React.FC & SubComponents = async () => { | |||
// const materials: MaterialResult[] = [] | |||
const materials = await fetchMaterials(); | |||
console.log(materials) | |||
@@ -37,7 +37,7 @@ const NavigationContent: React.FC = () => { | |||
{ | |||
icon: <Dashboard />, | |||
label: "Dashboard", | |||
path: "", | |||
path: "/dashboard", | |||
}, | |||
{ | |||
icon: <RequestQuote />, | |||
@@ -97,7 +97,7 @@ function SearchResults<T extends ResultWithId>({ | |||
{column.buttonIcon} | |||
</IconButton> | |||
) : ( | |||
<>{item[columnName]}</> | |||
<>{item[columnName] as string}</> | |||
)} | |||
</TableCell> | |||
); | |||
@@ -0,0 +1,307 @@ | |||
// import { | |||
// Dispatch, | |||
// SetStateAction, | |||
// useCallback, | |||
// useEffect, | |||
// useMemo, | |||
// useState, | |||
// } from "react"; | |||
// import StyledDataGrid from "../StyledDataGrid"; | |||
// import { | |||
// FooterPropsOverrides, | |||
// GridActionsCellItem, | |||
// GridCellParams, | |||
// GridColDef, | |||
// GridRowId, | |||
// GridRowIdGetter, | |||
// GridRowModel, | |||
// GridRowModes, | |||
// GridRowModesModel, | |||
// GridToolbarContainer, | |||
// GridValidRowModel, | |||
// useGridApiRef, | |||
// } from "@mui/x-data-grid"; | |||
// import { useFormContext } from "react-hook-form"; | |||
// import SaveIcon from "@mui/icons-material/Save"; | |||
// import DeleteIcon from "@mui/icons-material/Delete"; | |||
// 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"; | |||
// export interface ResultWithId { | |||
// id: string | number; | |||
// } | |||
// export type TableRow<T extends ResultWithId, E> = Partial< | |||
// T & { | |||
// _isNew: boolean; | |||
// _error: E; | |||
// } | |||
// >; | |||
// export type InputDataGridProps<T extends ResultWithId, E> = { | |||
// formKey: string; | |||
// // items?: TableRow<T, E>[]; | |||
// columns: GridColDef[]; | |||
// _rowModesModel: GridRowModesModel; | |||
// validation: () => E; | |||
// }; | |||
// export class ProcessRowUpdateError<T, E> extends Error { | |||
// public readonly row: T; | |||
// public readonly errors: E | undefined; | |||
// constructor(row: T, message?: string, errors?: E) { | |||
// super(message); | |||
// this.row = row; | |||
// this.errors = errors; | |||
// Object.setPrototypeOf(this, ProcessRowUpdateError.prototype); | |||
// } | |||
// } | |||
// function useInputDataGrid<T extends ResultWithId, E>({ | |||
// formKey = "testing", | |||
// // items, | |||
// columns, | |||
// _rowModesModel, | |||
// validation, | |||
// }: InputDataGridProps<T, E>): { | |||
// DataGrid: React.FC; | |||
// rowModesModel: GridRowModesModel; | |||
// setRowModesModel: Dispatch<SetStateAction<GridRowModesModel>>; | |||
// } { | |||
// const { | |||
// t, | |||
// // i18n: { language }, | |||
// } = useTranslation(); | |||
// const { setValue, getValues } = useFormContext(); | |||
// const [rowModesModel, setRowModesModel] = | |||
// useState<GridRowModesModel>(_rowModesModel); | |||
// const apiRef = useGridApiRef(); | |||
// const getRowId = useCallback<GridRowIdGetter<TableRow<T, E>>>( | |||
// (row) => row.id!!.toString(), | |||
// [] | |||
// ); | |||
// const list: TableRow<T, E>[] = getValues(formKey); | |||
// const [rows, setRows] = useState<TableRow<T, E>[]>(() => { | |||
// const list: TableRow<T, E>[] = getValues(formKey); | |||
// return list && list.length > 0 ? list : []; | |||
// }); | |||
// const originalRows = list && list.length > 0 ? list : []; | |||
// const handleSave = useCallback( | |||
// (id: GridRowId) => () => { | |||
// setRowModesModel((prevRowModesModel) => ({ | |||
// ...prevRowModesModel, | |||
// [id]: { mode: GridRowModes.View }, | |||
// })); | |||
// }, | |||
// [setRowModesModel] | |||
// ); | |||
// const onProcessRowUpdateError = useCallback( | |||
// (updateError: ProcessRowUpdateError<T, E>) => { | |||
// const errors = updateError.errors; | |||
// const row = updateError.row; | |||
// console.log(errors); | |||
// apiRef.current.updateRows([{ ...row, _error: errors }]); | |||
// }, | |||
// [apiRef, rowModesModel] | |||
// ); | |||
// const processRowUpdate = useCallback( | |||
// ( | |||
// newRow: GridRowModel<TableRow<T, E>>, | |||
// originalRow: GridRowModel<TableRow<T, E>> | |||
// ) => { | |||
// ///////////////// | |||
// // validation here | |||
// // const errors = validation(); //repace true with validation method | |||
// // if (errors) { | |||
// // throw new ProcessRowUpdateError( | |||
// // originalRow, | |||
// // "validation error", | |||
// // errors, | |||
// // ); | |||
// // } | |||
// ///////////////// | |||
// const { _isNew, _error, ...updatedRow } = newRow; | |||
// const rowToSave = { | |||
// ...updatedRow, | |||
// } as TableRow<T, E>; /// test | |||
// setRows((rw) => | |||
// rw.map((r) => (getRowId(r) === getRowId(originalRow) ? rowToSave : r)) | |||
// ); | |||
// return rowToSave; | |||
// }, | |||
// [rows] | |||
// ); | |||
// const addRow = useCallback(() => { | |||
// const newEntry = { id: Date.now(), _isNew: true } as TableRow<T, E>; | |||
// setRows((prev) => [...prev, newEntry]); | |||
// setRowModesModel((model) => ({ | |||
// ...model, | |||
// [getRowId(newEntry)]: { | |||
// mode: GridRowModes.Edit, | |||
// // fieldToFocus: "team", /// test | |||
// }, | |||
// })); | |||
// }, [getRowId]); | |||
// const handleCancel = useCallback( | |||
// (id: GridRowId) => () => { | |||
// setRowModesModel((model) => ({ | |||
// ...model, | |||
// [id]: { mode: GridRowModes.View, ignoreModifications: true }, | |||
// })); | |||
// const editedRow = rows.find((row) => getRowId(row) === id); | |||
// console.log(editedRow); | |||
// if (editedRow?._isNew) { | |||
// setRows((rw) => rw.filter((r) => r.id !== id)); | |||
// } else { | |||
// setRows((rw) => | |||
// rw.map((r) => (getRowId(r) === id ? { ...r, _error: undefined } : r)) | |||
// ); | |||
// } | |||
// }, | |||
// [setRowModesModel, rows] | |||
// ); | |||
// const handleDelete = useCallback( | |||
// (id: GridRowId) => () => { | |||
// setRows((prevRows) => prevRows.filter((row) => getRowId(row) !== id)); | |||
// }, | |||
// [] | |||
// ); | |||
// const _columns = useMemo<GridColDef[]>( | |||
// () => [ | |||
// ...columns, | |||
// { | |||
// field: "actions", | |||
// type: "actions", | |||
// headerName: "edit", | |||
// width: 100, | |||
// cellClassName: "actions", | |||
// getActions: ({ id }: { id: GridRowId }) => { | |||
// const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; | |||
// if (isInEditMode) { | |||
// return [ | |||
// <GridActionsCellItem | |||
// icon={<SaveIcon />} | |||
// label="Save" | |||
// key="edit" | |||
// sx={{ | |||
// color: "primary.main", | |||
// }} | |||
// onClick={handleSave(id)} | |||
// />, | |||
// <GridActionsCellItem | |||
// icon={<CancelIcon />} | |||
// label="Cancel" | |||
// key="edit" | |||
// onClick={handleCancel(id)} | |||
// />, | |||
// ]; | |||
// } | |||
// return [ | |||
// <GridActionsCellItem | |||
// icon={<DeleteIcon />} | |||
// label="Delete" | |||
// sx={{ | |||
// color: "error.main", | |||
// }} | |||
// onClick={handleDelete(id)} | |||
// color="inherit" | |||
// key="edit" | |||
// />, | |||
// ]; | |||
// }, | |||
// }, | |||
// ], | |||
// [] | |||
// ); | |||
// // sync useForm | |||
// useEffect(() => { | |||
// setValue(formKey, rows); | |||
// }, [rows]); | |||
// const footer = ( | |||
// <Box display="flex" gap={2} alignItems="center"> | |||
// <Button | |||
// disableRipple | |||
// variant="outlined" | |||
// startIcon={<Add />} | |||
// onClick={addRow} | |||
// size="small" | |||
// > | |||
// {t("Add Record")} | |||
// </Button> | |||
// </Box> | |||
// ); | |||
// const DataGrid: React.FC = ({ ...props }) => ( | |||
// <StyledDataGrid | |||
// getRowId={getRowId as GridRowIdGetter<GridValidRowModel>} | |||
// apiRef={apiRef} | |||
// rows={rows} | |||
// columns={_columns} | |||
// editMode="row" | |||
// autoHeight | |||
// sx={{ | |||
// "--DataGrid-overlayHeight": "100px", | |||
// ".MuiDataGrid-row .MuiDataGrid-cell.hasError": { | |||
// border: "1px solid", | |||
// borderColor: "error.main", | |||
// }, | |||
// ".MuiDataGrid-row .MuiDataGrid-cell.hasWarning": { | |||
// border: "1px solid", | |||
// borderColor: "warning.main", | |||
// }, | |||
// }} | |||
// disableColumnMenu | |||
// rowModesModel={rowModesModel} | |||
// onRowModesModelChange={setRowModesModel} | |||
// processRowUpdate={processRowUpdate as any} | |||
// onProcessRowUpdateError={onProcessRowUpdateError} | |||
// getCellClassName={(params: GridCellParams<TableRow<T, E>>) => { | |||
// let classname = ""; | |||
// if (params.row._error) { | |||
// classname = "hasError"; | |||
// } | |||
// return classname; | |||
// }} | |||
// slots={{ | |||
// footer: FooterToolbar, | |||
// noRowsOverlay: NoRowsOverlay, | |||
// }} | |||
// slotProps={{ | |||
// footer: { child: footer }, | |||
// }} | |||
// /> | |||
// ); | |||
// return { | |||
// DataGrid, | |||
// rowModesModel, | |||
// setRowModesModel, | |||
// }; | |||
// } | |||
// const FooterToolbar: React.FC<FooterPropsOverrides> = ({ child }) => { | |||
// return <GridToolbarContainer sx={{ p: 2 }}>{child}</GridToolbarContainer>; | |||
// }; | |||
// const NoRowsOverlay: React.FC = () => { | |||
// const { t } = useTranslation("home"); | |||
// return ( | |||
// <Box | |||
// display="flex" | |||
// justifyContent="center" | |||
// alignItems="center" | |||
// height="100%" | |||
// > | |||
// <Typography variant="caption">{t("Add some entries!")}</Typography> | |||
// </Box> | |||
// ); | |||
// }; | |||
// export default useInputDataGrid; |
@@ -0,0 +1 @@ | |||
export { default } from "./useInputDataGrid"; |
@@ -0,0 +1,326 @@ | |||
import { | |||
Dispatch, | |||
SetStateAction, | |||
useCallback, | |||
useEffect, | |||
useMemo, | |||
useState, | |||
} from "react"; | |||
import StyledDataGrid from "../StyledDataGrid"; | |||
import { | |||
FooterPropsOverrides, | |||
GridActionsCellItem, | |||
GridCellParams, | |||
GridColDef, | |||
GridRowId, | |||
GridRowIdGetter, | |||
GridRowModel, | |||
GridRowModes, | |||
GridRowModesModel, | |||
GridToolbarContainer, | |||
GridValidRowModel, | |||
useGridApiRef, | |||
} from "@mui/x-data-grid"; | |||
import { useFormContext } from "react-hook-form"; | |||
import SaveIcon from "@mui/icons-material/Save"; | |||
import DeleteIcon from "@mui/icons-material/Delete"; | |||
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"; | |||
export interface ResultWithId { | |||
id: string | number; | |||
} | |||
type DataGridProps = { | |||
[key: string]: any | |||
} | |||
export type TableRow<T, E> = Partial< | |||
T & { | |||
_isNew: boolean; | |||
_error: E; | |||
} & ResultWithId | |||
>; | |||
export type InputDataGridProps<T, E> = { | |||
_formKey: keyof T; | |||
columns: GridColDef[]; | |||
validation: () => E; | |||
}; | |||
export class ProcessRowUpdateError<T, E> extends Error { | |||
public readonly row: T; | |||
public readonly errors: E | undefined; | |||
constructor(row: T, message?: string, errors?: E) { | |||
super(message); | |||
this.row = row; | |||
this.errors = errors; | |||
Object.setPrototypeOf(this, ProcessRowUpdateError.prototype); | |||
} | |||
} | |||
function useInputDataGrid<T, E>({ | |||
_formKey, | |||
columns, | |||
validation, | |||
}: InputDataGridProps<T, E>): { | |||
DataGrid: React.FC; | |||
rowModesModel: GridRowModesModel; | |||
setRowModesModel: Dispatch<SetStateAction<GridRowModesModel>>; | |||
} { | |||
const { | |||
t, | |||
// i18n: { language }, | |||
} = useTranslation(); | |||
const formKey = _formKey.toString() | |||
const { setValue, getValues } = useFormContext(); | |||
const [rowModesModel, setRowModesModel] = | |||
useState<GridRowModesModel>({}); | |||
const apiRef = useGridApiRef(); | |||
const getRowId = useCallback<GridRowIdGetter<TableRow<T, 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); | |||
return list && list.length > 0 ? list : []; | |||
}); | |||
const originalRows = list && list.length > 0 ? list : []; | |||
const handleSave = useCallback( | |||
(id: GridRowId) => () => { | |||
setRowModesModel((prevRowModesModel) => ({ | |||
...prevRowModesModel, | |||
[id]: { mode: GridRowModes.View }, | |||
})); | |||
}, | |||
[setRowModesModel] | |||
); | |||
const onProcessRowUpdateError = useCallback( | |||
(updateError: ProcessRowUpdateError<T, E>) => { | |||
const errors = updateError.errors; | |||
const row = updateError.row; | |||
console.log(errors); | |||
apiRef.current.updateRows([{ ...row, _error: errors }]); | |||
}, | |||
[apiRef, rowModesModel] | |||
); | |||
const processRowUpdate = useCallback( | |||
( | |||
newRow: GridRowModel<TableRow<T, E>>, | |||
originalRow: GridRowModel<TableRow<T, E>> | |||
) => { | |||
///////////////// | |||
// validation here | |||
// const errors = validation(); //repace true with validation method | |||
// if (errors) { | |||
// throw new ProcessRowUpdateError( | |||
// originalRow, | |||
// "validation error", | |||
// errors, | |||
// ); | |||
// } | |||
///////////////// | |||
const { _isNew, _error, ...updatedRow } = newRow; | |||
const rowToSave = { | |||
...updatedRow, | |||
} as TableRow<T, E>; /// test | |||
setRows((rw) => | |||
rw.map((r) => (getRowId(r) === getRowId(originalRow) ? rowToSave : r)) | |||
); | |||
return rowToSave; | |||
}, | |||
[rows] | |||
); | |||
const addRow = useCallback(() => { | |||
const newEntry = { id: Date.now(), _isNew: true } as TableRow<T, E>; | |||
setRows((prev) => [...prev, newEntry]); | |||
// console.log(newEntry) | |||
setRowModesModel((model) => ({ | |||
...model, | |||
[getRowId(newEntry)]: { | |||
mode: GridRowModes.Edit, | |||
// fieldToFocus: "team", /// test | |||
}, | |||
})); | |||
}, [getRowId]); | |||
const reset = useCallback(() => { | |||
setRowModesModel({}) | |||
setRows([]) | |||
}, []); | |||
const handleCancel = useCallback( | |||
(id: GridRowId) => () => { | |||
setRowModesModel((model) => ({ | |||
...model, | |||
[id]: { mode: GridRowModes.View, ignoreModifications: true }, | |||
})); | |||
const editedRow = rows.find((row) => getRowId(row) === id); | |||
if (editedRow?._isNew) { | |||
setRows((rw) => rw.filter((r) => getRowId(r) !== id | |||
)); | |||
} else { | |||
setRows((rw) => | |||
rw.map((r) => (getRowId(r) === id ? { ...r, _error: undefined } : r)) | |||
); | |||
} | |||
}, | |||
[setRowModesModel, rows] | |||
); | |||
useEffect(() => { | |||
console.log(rows) | |||
}, [rows]) | |||
const handleDelete = useCallback( | |||
(id: GridRowId) => () => { | |||
setRows((prevRows) => prevRows.filter((row) => getRowId(row) !== id)); | |||
}, | |||
[] | |||
); | |||
if (columns) { | |||
} | |||
const _columns = useMemo<GridColDef[]>( | |||
() => [ | |||
...columns, | |||
{ | |||
field: "actions", | |||
type: "actions", | |||
headerName: "", | |||
width: 100, | |||
cellClassName: "actions", | |||
getActions: ({ id }: { id: GridRowId }) => { | |||
const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; | |||
if (isInEditMode) { | |||
return [ | |||
<GridActionsCellItem | |||
icon={<SaveIcon />} | |||
label="Save" | |||
key="edit" | |||
sx={{ | |||
color: "primary.main", | |||
}} | |||
onClick={handleSave(id)} | |||
/>, | |||
<GridActionsCellItem | |||
icon={<CancelIcon />} | |||
label="Cancel" | |||
key="edit" | |||
onClick={handleCancel(id)} | |||
/>, | |||
]; | |||
} | |||
return [ | |||
<GridActionsCellItem | |||
icon={<DeleteIcon />} | |||
label="Delete" | |||
sx={{ | |||
color: "error.main", | |||
}} | |||
onClick={handleDelete(id)} | |||
color="inherit" | |||
key="edit" | |||
/>, | |||
]; | |||
}, | |||
}, | |||
], | |||
[rowModesModel, handleSave, handleCancel, handleDelete] | |||
); | |||
// sync useForm | |||
useEffect(() => { | |||
setValue(formKey, rows); | |||
}, [rows]); | |||
const footer = ( | |||
<Box display="flex" gap={2} alignItems="center"> | |||
<Button | |||
disableRipple | |||
variant="outlined" | |||
startIcon={<Add />} | |||
onClick={addRow} | |||
size="small" | |||
> | |||
{t("Add Record")} | |||
</Button> | |||
<Button | |||
disableRipple | |||
variant="outlined" | |||
startIcon={<Add />} | |||
onClick={reset} | |||
size="small" | |||
> | |||
{t("Clean Record")} | |||
</Button> | |||
</Box> | |||
); | |||
const DataGrid: React.FC<DataGridProps> = (props) => ( | |||
<StyledDataGrid | |||
{...props} | |||
getRowId={getRowId as GridRowIdGetter<GridValidRowModel>} | |||
apiRef={apiRef} | |||
rows={rows} | |||
columns={_columns} | |||
editMode="row" | |||
autoHeight | |||
sx={{ | |||
"--DataGrid-overlayHeight": "100px", | |||
".MuiDataGrid-row .MuiDataGrid-cell.hasError": { | |||
border: "1px solid", | |||
borderColor: "error.main", | |||
}, | |||
".MuiDataGrid-row .MuiDataGrid-cell.hasWarning": { | |||
border: "1px solid", | |||
borderColor: "warning.main", | |||
}, | |||
}} | |||
disableColumnMenu | |||
rowModesModel={rowModesModel} | |||
onRowModesModelChange={setRowModesModel} | |||
processRowUpdate={processRowUpdate as any} | |||
onProcessRowUpdateError={onProcessRowUpdateError} | |||
getCellClassName={(params: GridCellParams<TableRow<T, E>>) => { | |||
let classname = ""; | |||
if (params.row._error) { | |||
classname = "hasError"; | |||
} | |||
return classname; | |||
}} | |||
slots={{ | |||
footer: FooterToolbar, | |||
noRowsOverlay: NoRowsOverlay, | |||
}} | |||
slotProps={{ | |||
footer: { child: footer }, | |||
}} | |||
/> | |||
); | |||
return { | |||
DataGrid, | |||
rowModesModel, | |||
setRowModesModel, | |||
}; | |||
} | |||
const FooterToolbar: React.FC<FooterPropsOverrides> = ({ child }) => { | |||
return <GridToolbarContainer sx={{ p: 2 }}>{child}</GridToolbarContainer>; | |||
}; | |||
const NoRowsOverlay: React.FC = () => { | |||
const { t } = useTranslation("home"); | |||
return ( | |||
<Box | |||
display="flex" | |||
justifyContent="center" | |||
alignItems="center" | |||
height="100%" | |||
> | |||
<Typography variant="caption">{t("Add some entries!")}</Typography> | |||
</Box> | |||
); | |||
}; | |||
export default useInputDataGrid; |