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