|
@@ -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={{ |
|
|