| @@ -9,6 +9,7 @@ import { | |||
| GridRenderCellParams, | |||
| GridRenderEditCellParams, | |||
| GridRowId, | |||
| GridRowIdGetter, | |||
| GridRowModel, | |||
| GridRowModes, | |||
| GridRowModesModel, | |||
| @@ -69,15 +70,15 @@ export type TimeLeaveRow = Partial< | |||
| >; | |||
| class ProcessRowUpdateError extends Error { | |||
| public readonly rowId: GridRowId; | |||
| public readonly row: TimeLeaveRow; | |||
| public readonly errors: TimeEntryError | LeaveEntryError | undefined; | |||
| constructor( | |||
| rowId: GridRowId, | |||
| row: TimeLeaveRow, | |||
| message?: string, | |||
| errors?: TimeEntryError | LeaveEntryError, | |||
| ) { | |||
| super(message); | |||
| this.rowId = rowId; | |||
| this.row = row; | |||
| this.errors = errors; | |||
| Object.setPrototypeOf(this, ProcessRowUpdateError.prototype); | |||
| @@ -135,19 +136,28 @@ const TimeLeaveInputTable: React.FC<Props> = ({ | |||
| useFormContext<RecordTimeLeaveInput>(); | |||
| const currentEntries = getValues(day); | |||
| const getRowId = useCallback<GridRowIdGetter<TimeLeaveRow>>( | |||
| (row) => `${row.type}-${row.id}`, | |||
| [], | |||
| ); | |||
| const [entries, setEntries] = useState<TimeLeaveRow[]>(currentEntries || []); | |||
| const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({}); | |||
| const apiRef = useGridApiRef(); | |||
| const addRow = useCallback(() => { | |||
| const id = Date.now(); | |||
| setEntries((e) => [...e, { id, _isNew: true, type: "timeEntry" }]); | |||
| const type = "timeEntry" as const; | |||
| const newEntry = { id: Date.now(), _isNew: true, type }; | |||
| setEntries((e) => [...e, newEntry]); | |||
| setRowModesModel((model) => ({ | |||
| ...model, | |||
| [id]: { mode: GridRowModes.Edit, fieldToFocus: "projectId" }, | |||
| [getRowId(newEntry)]: { | |||
| mode: GridRowModes.Edit, | |||
| fieldToFocus: "projectId", | |||
| }, | |||
| })); | |||
| }, []); | |||
| }, [getRowId]); | |||
| const validateRow = useCallback( | |||
| (row: TimeLeaveRow) => { | |||
| @@ -186,25 +196,25 @@ const TimeLeaveInputTable: React.FC<Props> = ({ | |||
| })); | |||
| const editedRow = entries.find((entry) => entry.id === id); | |||
| if (editedRow?._isNew) { | |||
| setEntries((es) => es.filter((e) => e.id !== id)); | |||
| setEntries((es) => es.filter((e) => getRowId(e) !== id)); | |||
| } else { | |||
| setEntries((es) => | |||
| es.map((e) => | |||
| e.id === id | |||
| getRowId(e) === id | |||
| ? { ...e, _error: undefined, _isPlanned: undefined } | |||
| : e, | |||
| ), | |||
| ); | |||
| } | |||
| }, | |||
| [entries], | |||
| [entries, getRowId], | |||
| ); | |||
| const handleDelete = useCallback( | |||
| (id: GridRowId) => () => { | |||
| setEntries((es) => es.filter((e) => e.id !== id)); | |||
| setEntries((es) => es.filter((e) => getRowId(e) !== id)); | |||
| }, | |||
| [], | |||
| [getRowId], | |||
| ); | |||
| const handleSave = useCallback( | |||
| @@ -218,10 +228,17 @@ const TimeLeaveInputTable: React.FC<Props> = ({ | |||
| ); | |||
| const processRowUpdate = useCallback( | |||
| (newRow: GridRowModel<TimeLeaveRow>) => { | |||
| ( | |||
| newRow: GridRowModel<TimeLeaveRow>, | |||
| originalRow: GridRowModel<TimeLeaveRow>, | |||
| ) => { | |||
| const errors = validateRow(newRow); | |||
| if (errors) { | |||
| throw new ProcessRowUpdateError(newRow.id!, "validation error", errors); | |||
| throw new ProcessRowUpdateError( | |||
| originalRow, | |||
| "validation error", | |||
| errors, | |||
| ); | |||
| } | |||
| // eslint-disable-next-line @typescript-eslint/no-unused-vars | |||
| @@ -242,24 +259,19 @@ const TimeLeaveInputTable: React.FC<Props> = ({ | |||
| _isPlanned: newIsPlanned, | |||
| } satisfies TimeLeaveRow; | |||
| setEntries((es) => | |||
| es.map((e) => (e.id === rowToSave.id ? rowToSave : e)), | |||
| es.map((e) => (getRowId(e) === getRowId(originalRow) ? rowToSave : e)), | |||
| ); | |||
| return rowToSave; | |||
| }, | |||
| [validateRow, verifyIsPlanned], | |||
| [validateRow, verifyIsPlanned, getRowId], | |||
| ); | |||
| const onProcessRowUpdateError = useCallback( | |||
| (updateError: ProcessRowUpdateError) => { | |||
| const errors = updateError.errors; | |||
| const rowId = updateError.rowId; | |||
| const oldRow = updateError.row; | |||
| apiRef.current.updateRows([ | |||
| { | |||
| id: rowId, | |||
| _error: errors, | |||
| }, | |||
| ]); | |||
| apiRef.current.updateRows([{ ...oldRow, _error: errors }]); | |||
| }, | |||
| [apiRef], | |||
| ); | |||
| @@ -685,6 +697,7 @@ const TimeLeaveInputTable: React.FC<Props> = ({ | |||
| return ( | |||
| <> | |||
| <StyledDataGrid | |||
| getRowId={getRowId} | |||
| apiRef={apiRef} | |||
| autoHeight | |||
| sx={{ | |||