drf-react-by-schema 0.0.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,747 @@
1
+ import React, { useEffect, useState, useRef, forwardRef } from 'react';
2
+ // import moment from 'moment';
3
+ // import Mask from 'string-mask';
4
+ import {
5
+ DataGrid,
6
+ GridEditInputCell,
7
+ GridRowModes,
8
+ GridActionsCellItem,
9
+ gridVisibleSortedRowIdsSelector,
10
+ gridStringOrNumberComparator
11
+ } from '@mui/x-data-grid';
12
+ import * as Yup from 'yup';
13
+ import Box from '@mui/material/Box';
14
+ import CircularProgress from '@mui/material/CircularProgress';
15
+ import EditIcon from '@mui/icons-material/Edit';
16
+ import ClearIcon from '@mui/icons-material/Clear';
17
+ import CheckIcon from '@mui/icons-material/Check';
18
+ import UndoIcon from '@mui/icons-material/Undo';
19
+ import Snackbar from '@mui/material/Snackbar';
20
+ import Alert from '@mui/material/Alert';
21
+
22
+ import { Layout } from '../styles';
23
+ import { getAutoComplete } from '../api';
24
+ import {
25
+ Item,
26
+ SchemaType,
27
+ Choice,
28
+ Id,
29
+ GridEnrichedBySchemaColDef,
30
+ isTmpId,
31
+ emptyByType,
32
+ buildGenericYupValidationSchema
33
+ } from '../utils';
34
+ import { SxProps } from '@mui/material';
35
+ import { ObjectShape } from 'yup/lib/object';
36
+ import { DRFReactBySchemaContext, DRFReactBySchemaContextType } from '../context/DRFReactBySchemaProvider';
37
+ import { quantityOnlyOperators } from './DataGridBySchemaEditable/utils';
38
+ import { CustomToolbar } from './DataGridBySchemaEditable/CustomToolbar';
39
+ import { SelectEditInputCell } from './DataGridBySchemaEditable/SelectEditInputCell';
40
+ import { GridDecimalInput } from './DataGridBySchemaEditable/GridDecimalInput';
41
+ import { GridPatternInput } from './DataGridBySchemaEditable/GridPatternInput';
42
+ import { FooterToolbar } from './DataGridBySchemaEditable/FooterToolbar';
43
+ import { ConfirmDialog } from './DataGridBySchemaEditable/ConfirmDialog';
44
+
45
+ const stringMask = require('string-mask');
46
+ const moment = require('moment');
47
+
48
+ interface DataGridBySchemaEditableProps {
49
+ schema: SchemaType;
50
+ data: Item[];
51
+ columns: GridEnrichedBySchemaColDef[];
52
+ model: string;
53
+ fieldKey?: string;
54
+ labelKey?: string;
55
+ index?: number;
56
+ name?: string;
57
+ indexField?: string;
58
+ addExistingModel?: string;
59
+ indexFieldMinWidth?: number;
60
+ indexFieldBasePath?: string;
61
+ stateToLink?: object;
62
+ minWidth?: number;
63
+ onEditRelatedModelSave?: (p: any) => Id | { data: Item } | { errors: Item };
64
+ onDeleteRelatedModel?: (p: any) => any;
65
+ customColumnOperations?: (p: any) => GridEnrichedBySchemaColDef;
66
+ customLinkDestination?: (p: any) => string;
67
+ LinkComponent?: any;
68
+ onProcessRow?: (p: any) => void;
69
+ onDataChange?: (p: any) => void;
70
+ isEditable?: boolean;
71
+ sx?: SxProps;
72
+ isAutoHeight?: boolean;
73
+ defaultValues?: Item;
74
+ hideFooterPagination?: boolean;
75
+ setVisibleRows?: (p: any) => void;
76
+ };
77
+
78
+ const DataGridBySchemaEditable = forwardRef((
79
+ {
80
+ schema,
81
+ data,
82
+ columns,
83
+ model,
84
+ fieldKey,
85
+ labelKey = 'nome',
86
+ index,
87
+ name = Math.floor(Math.random() * 1000000).toString(),
88
+ indexField = 'nome',
89
+ addExistingModel,
90
+ indexFieldMinWidth = 350,
91
+ indexFieldBasePath = '',
92
+ stateToLink = {},
93
+ minWidth = 80,
94
+ onEditRelatedModelSave,
95
+ onDeleteRelatedModel,
96
+ customColumnOperations,
97
+ customLinkDestination,
98
+ LinkComponent,
99
+ onProcessRow,
100
+ onDataChange,
101
+ isEditable = false,
102
+ sx = { mr: 2 },
103
+ isAutoHeight = false,
104
+ defaultValues = {},
105
+ hideFooterPagination = false,
106
+ setVisibleRows,
107
+ ...other
108
+ }: DataGridBySchemaEditableProps,
109
+ ref
110
+ ) => {
111
+ const { serverEndPoint } = DRFReactBySchemaContext
112
+ ? React.useContext(DRFReactBySchemaContext) as DRFReactBySchemaContextType
113
+ : { serverEndPoint: null };
114
+ const initialSnackBar:Record<string, any> = {
115
+ open: false,
116
+ msg: '',
117
+ severity: 'info'
118
+ };
119
+ const [snackBar, setSnackBar] = useState(initialSnackBar);
120
+ const [dataGrid, setDataGrid] = useState<{ data:Item[] }>({ data: [] });
121
+ const [preparedColumns, setPreparedColumns] = useState<null | GridEnrichedBySchemaColDef[]>(null);
122
+ const [dataGridLoading, setDataGridLoading] = useState(false);
123
+ const [rowModesModel, setRowModesModel] = useState<Record<string, any>>({});
124
+ const [dialogOpen, setDialogOpen] = useState(false);
125
+ const optionsAC = useRef<Record<string, Item[]> | null>(null);
126
+ const emptyItem = useRef<Item>({});
127
+ const yupValidationSchema = useRef<Yup.AnySchema | null>(null);
128
+ const processingRow = useRef<number | string | null>(null);
129
+ const idToRemove = useRef<Id | null>(null);
130
+
131
+ const updateOptionsAC = async () => {
132
+ optionsAC.current = {};
133
+ for (const col of columns) {
134
+ if (!schema[col.field]) {
135
+ continue;
136
+ }
137
+ if (['field', 'nested object'].includes(schema[col.field].type) && !(col.field in optionsAC.current)) {
138
+ const options = await getAutoComplete({ model: col.field, serverEndPoint });
139
+ if (options) {
140
+ optionsAC.current[col.field] = options;
141
+ continue;
142
+ }
143
+ console.log(`Couldn't load autocomplete options from '${col.field}'`);
144
+ continue;
145
+ }
146
+ if (schema[col.field].type === 'choice' && !(col.field in optionsAC.current)) {
147
+ optionsAC.current[col.field] = schema[col.field].choices as Choice[];
148
+ continue;
149
+ }
150
+ if (col.field === indexField && addExistingModel && !optionsAC.current[col.field]) {
151
+ const options = await getAutoComplete({ model: addExistingModel, serverEndPoint });
152
+ if (options) {
153
+ optionsAC.current[col.field] = options;
154
+ continue;
155
+ }
156
+ if (!(col.field in optionsAC.current)) {
157
+ delete optionsAC.current[col.field];
158
+ }
159
+ }
160
+ }
161
+ };
162
+
163
+ const initColumns = () => {
164
+ let cols:GridEnrichedBySchemaColDef[] = [];
165
+ if (isEditable) {
166
+ cols.push({
167
+ field: 'actions',
168
+ headerName: '',
169
+ type: 'actions',
170
+ getActions: ({ id }: { id: Id }) => {
171
+ const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit;
172
+ if (isInEditMode) {
173
+ return [
174
+ <GridActionsCellItem
175
+ key={`save_${id}`}
176
+ icon={<CheckIcon />}
177
+ label="Salvar"
178
+ onClick={handleSaveClick(id)}
179
+ color="success"
180
+ />,
181
+ <GridActionsCellItem
182
+ key={`cancel_${id}`}
183
+ icon={<UndoIcon />}
184
+ label="Cancelar"
185
+ onClick={handleCancelClick(id)}
186
+ color="inherit"
187
+ />
188
+ ];
189
+ }
190
+
191
+ return [
192
+ <GridActionsCellItem
193
+ key={`edit_${id}`}
194
+ icon={<EditIcon />}
195
+ label="Edit"
196
+ onClick={handleEditClick(id)}
197
+ color="inherit"
198
+ />,
199
+ <GridActionsCellItem
200
+ key={`remove_${id}`}
201
+ icon={<ClearIcon />}
202
+ label="Delete"
203
+ onClick={handleDeleteClick(id)}
204
+ color="error"
205
+ />
206
+ ];
207
+ }
208
+ });
209
+ }
210
+ cols = [...cols, ...columns];
211
+ cols = cols.map(col => {
212
+ if (!schema[col.field]) {
213
+ return col;
214
+ }
215
+ const isIndexField = (indexField !== '' && (col.field === indexField));
216
+ let column = col;
217
+ if (isIndexField) {
218
+ col.isIndexField = true;
219
+ }
220
+ column.editable = (isEditable) && (
221
+ !schema[col.field].read_only || ['field', 'nested object'].includes(schema[col.field].type)
222
+ );
223
+ switch (schema[col.field].type) {
224
+ case 'date':
225
+ column.type = 'date';
226
+ column.valueFormatter = params => params.value
227
+ ? moment(params.value).format('DD/MM/YYYY')
228
+ : '';
229
+ column.filterOperators = quantityOnlyOperators({ type: 'date' });
230
+ break;
231
+ case 'datetime':
232
+ column.type = 'dateTime';
233
+ column.minWidth = 180;
234
+ column.valueFormatter = params => params.value
235
+ ? moment(params.value).format('DD/MM/YYYY HH:mm')
236
+ : '';
237
+ break;
238
+ case 'nested object':
239
+ column.minWidth = 150;
240
+ if (isEditable) {
241
+ column.valueFormatter = params => {
242
+ return (!params.value) ? '' : params.value.label;
243
+ };
244
+ column.filterable = false;
245
+ column.sortComparator = (v1, v2, param1, param2) => {
246
+ return gridStringOrNumberComparator(v1.label, v2.label, param1, param2);
247
+ };
248
+ column.renderEditCell = (params) => <SelectEditInputCell
249
+ {...params}
250
+ column={column}
251
+ type={schema[col.field].type}
252
+ optionsAC={optionsAC}
253
+ isIndexField={isIndexField}
254
+ />;
255
+ break;
256
+ }
257
+
258
+ column.valueGetter = params => {
259
+ return (!params.value) ? '' : params.value.label;
260
+ };
261
+ break;
262
+ case 'field':
263
+ column.orderable = false;
264
+
265
+ if (isEditable) {
266
+ column.minWidth = 300;
267
+ column.valueFormatter = params => {
268
+ return (!params.value || !Array.isArray(params.value))
269
+ ? ''
270
+ : params.value.map(val => val.label).join(', ');
271
+ };
272
+ column.filterable = false;
273
+ column.renderEditCell = (params) => <SelectEditInputCell
274
+ {...params}
275
+ column={column}
276
+ type={schema[col.field].type}
277
+ optionsAC={optionsAC}
278
+ isIndexField={isIndexField}
279
+ multiple={true}
280
+ />;
281
+ break;
282
+ }
283
+
284
+ column.valueGetter = params => {
285
+ return (!params.value || !Array.isArray(params.value))
286
+ ? ''
287
+ : params.value.map(val => val.label).join(', ');
288
+ };
289
+ break;
290
+ case 'choice':
291
+ if (isEditable) {
292
+ column.minWidth = 150;
293
+ column.valueFormatter = params => {
294
+ return (!params.value) ? '' : params.value.display_name;
295
+ };
296
+ column.filterable = false;
297
+ column.sortComparator = (v1, v2, param1, param2) => {
298
+ return gridStringOrNumberComparator(v1.display_name, v2.display_name, param1, param2);
299
+ };
300
+ column.renderEditCell = (params) => <SelectEditInputCell
301
+ {...params}
302
+ column={column}
303
+ type={schema[col.field].type}
304
+ optionsAC={optionsAC}
305
+ isIndexField={isIndexField}
306
+ />;
307
+ break;
308
+ }
309
+
310
+ column.valueGetter = params => {
311
+ return (!params.value) ? '' : params.value.display_name;
312
+ };
313
+ break;
314
+ case 'decimal':
315
+ case 'float':
316
+ column.type = 'number';
317
+ column.valueFormatter = params => {
318
+ return (!params.value)
319
+ ? ''
320
+ : parseFloat(params.value).toLocaleString('pt-BR', {
321
+ minimumFractionDigits: 2,
322
+ maximumFractionDigits: 2
323
+ });
324
+ };
325
+ if (isEditable) {
326
+ column.renderEditCell = (params) => <GridDecimalInput
327
+ {...params}
328
+ column={column}
329
+ />;
330
+ }
331
+ column.filterOperators = quantityOnlyOperators({ type: 'float' });
332
+ break;
333
+ case 'number':
334
+ case 'integer':
335
+ column.type = 'number';
336
+ column.filterOperators = quantityOnlyOperators({ type: 'number' });
337
+ break;
338
+ }
339
+
340
+ if (indexFieldMinWidth && !column.minWidth) {
341
+ column.minWidth = (col.field === indexField) ? indexFieldMinWidth : minWidth;
342
+ }
343
+ if (indexField) {
344
+ if (col.field === indexField) {
345
+ column.flex = 1;
346
+ column.renderCell = params => {
347
+ if (LinkComponent && customLinkDestination) {
348
+ return (
349
+ <LinkComponent to={`${customLinkDestination(params)}`} state={stateToLink}>
350
+ {params.formattedValue}
351
+ </LinkComponent>
352
+ );
353
+ }
354
+
355
+ if (
356
+ !LinkComponent ||
357
+ !indexFieldBasePath ||
358
+ (
359
+ ['field', 'nested object'].includes(schema[col.field].type) &&
360
+ (!params.row[col.field] || !params.row[col.field].id)
361
+ )
362
+ ) {
363
+ return (<>{params.formattedValue}</>);
364
+ }
365
+
366
+ const targetId = (['field', 'nested object'].includes(schema[col.field].type))
367
+ ? params.row[col.field].id.toString()
368
+ : params.row.id.toString();
369
+
370
+ return (
371
+ <LinkComponent to={`${indexFieldBasePath}${targetId}`} state={stateToLink}>
372
+ {params.formattedValue}
373
+ </LinkComponent>
374
+ );
375
+ };
376
+ if (
377
+ isEditable &&
378
+ optionsAC.current &&
379
+ col.field in optionsAC.current &&
380
+ addExistingModel
381
+ ) {
382
+ column.renderEditCell = (params) => {
383
+ if (!isTmpId(params.id)) {
384
+ return (
385
+ <GridEditInputCell
386
+ {...params}
387
+ />
388
+ );
389
+ }
390
+ return (
391
+ <SelectEditInputCell
392
+ {...params}
393
+ column={column}
394
+ type={schema[col.field].type}
395
+ optionsAC={optionsAC}
396
+ isIndexField={true}
397
+ />
398
+ );
399
+ };
400
+ }
401
+ }
402
+ }
403
+
404
+ // Custom column operations:
405
+ if (customColumnOperations) {
406
+ column = customColumnOperations(column);
407
+ }
408
+
409
+ // format numbers:
410
+ if (column.patternFormat) {
411
+ column.valueFormatter = params => {
412
+ if (!params.value || typeof params.value !== 'string') {
413
+ return params.value;
414
+ }
415
+ const formattedValue = new stringMask.Mask(column.patternFormat, {}).apply(params.value);
416
+ return formattedValue;
417
+ };
418
+ if (isEditable) {
419
+ column.renderEditCell = params => <GridPatternInput
420
+ {...params}
421
+ patternFormat={column.patternFormat}
422
+ />;
423
+ }
424
+ }
425
+
426
+ // Finally!
427
+ return column;
428
+ });
429
+ setPreparedColumns(cols);
430
+ };
431
+
432
+ const handleDialogClose = () => {
433
+ setDialogOpen(false);
434
+ };
435
+
436
+ const handleRowEditStart = (params: any, event: any) => {
437
+ event.defaultMuiPrevented = true;
438
+ };
439
+
440
+ const handleRowEditStop = (params: any, event: any) => {
441
+ event.defaultMuiPrevented = true;
442
+ };
443
+
444
+ const handleEditClick = (id: Id) => () => {
445
+ setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.Edit } });
446
+ };
447
+
448
+ const handleSaveClick = (id: Id) => () => {
449
+ setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.View } });
450
+ };
451
+
452
+ const handleDeleteClick = (id: Id) => () => {
453
+ idToRemove.current = id;
454
+ setDialogOpen(true);
455
+ };
456
+
457
+ const handleDeleteSave = () => {
458
+ const newData = dataGrid.data.filter(row => row.id !== idToRemove.current);
459
+ setDataGrid({
460
+ ...dataGrid,
461
+ data: newData
462
+ });
463
+ if (onDeleteRelatedModel) {
464
+ onDeleteRelatedModel({
465
+ relatedModel: model,
466
+ relatedModelId: idToRemove.current
467
+ });
468
+ }
469
+
470
+ // Reflect changes to the outside, for example for doing global calculations over multiple rows:
471
+ if (onDataChange) {
472
+ onDataChange(newData);
473
+ }
474
+ idToRemove.current = null;
475
+ setDialogOpen(false);
476
+ };
477
+
478
+ const handleCancelClick = (id: Id) => () => {
479
+ setRowModesModel({
480
+ ...rowModesModel,
481
+ [id]: { mode: GridRowModes.View, ignoreModifications: true }
482
+ });
483
+
484
+ const editedRow = dataGrid.data.find(row => row.id === id);
485
+ if (editedRow && editedRow.isNew) {
486
+ setDataGrid({
487
+ ...dataGrid,
488
+ data: dataGrid.data.filter(row => row.id !== id)
489
+ });
490
+ }
491
+ };
492
+
493
+ useEffect(() => {
494
+ if (!columns) {
495
+ setDataGrid({ data: [] });
496
+ return;
497
+ }
498
+
499
+ if (isEditable) {
500
+ for (const col of columns) {
501
+ if (schema[col.field].model_default) {
502
+ emptyItem.current[col.field] = schema[col.field].model_default;
503
+ continue;
504
+ }
505
+ if (Object.prototype.hasOwnProperty.call(defaultValues, col.field)) {
506
+ emptyItem.current[col.field] = defaultValues[col.field];
507
+ continue;
508
+ }
509
+ emptyItem.current[col.field] = (schema[col.field])
510
+ ? emptyByType(schema[col.field])
511
+ : null;
512
+ }
513
+ }
514
+
515
+ const skipFields = [];
516
+ if (indexField && addExistingModel) {
517
+ skipFields.push(indexField);
518
+ }
519
+ yupValidationSchema.current = buildGenericYupValidationSchema({
520
+ data: emptyItem.current,
521
+ schema,
522
+ skipFields
523
+ });
524
+
525
+ setDataGrid({
526
+ data
527
+ });
528
+ }, [data]);
529
+
530
+ useEffect(() => {
531
+ if (optionsAC.current) {
532
+ initColumns();
533
+ return;
534
+ }
535
+
536
+ updateOptionsAC().then(() => {
537
+ initColumns();
538
+ });
539
+ }, [rowModesModel]);
540
+
541
+ const processRowUpdate = async (newRow: Item) => {
542
+ if (!preparedColumns || !yupValidationSchema.current) {
543
+ return false;
544
+ }
545
+ setDataGridLoading(true);
546
+ const indexCol = preparedColumns.find(col => col.field === indexField);
547
+ processingRow.current = newRow.id;
548
+ await yupValidationSchema.current.validate(newRow);
549
+ const onlyAddExisting = (
550
+ indexField &&
551
+ isTmpId(newRow.id) &&
552
+ (newRow[indexField] && !isTmpId(newRow[indexField].id)) &&
553
+ (!indexCol || !indexCol.valueFormatter)
554
+ );
555
+ const createNewItem = (indexField && isTmpId(newRow.id) && newRow[indexField] && isTmpId(newRow[indexField].id));
556
+ if (onlyAddExisting) {
557
+ const row:Item = {};
558
+ row.id_to_add = newRow[indexField].id;
559
+ for (const [key, value] of Object.entries(newRow[indexField])) {
560
+ if (key === 'id') {
561
+ row[key] = newRow.id;
562
+ continue;
563
+ }
564
+ row[key] = value;
565
+ }
566
+ newRow = { ...row };
567
+ }
568
+ if (createNewItem && newRow[indexField]) {
569
+ if (newRow[indexField].inputValue) {
570
+ newRow[indexField] = newRow[indexField].inputValue;
571
+ }
572
+ if (addExistingModel) {
573
+ newRow[indexField] = newRow[indexField].label;
574
+ }
575
+ }
576
+
577
+ if (onEditRelatedModelSave) {
578
+ const response = await onEditRelatedModelSave({
579
+ relatedModel: model,
580
+ relatedModelId: newRow.id,
581
+ newRow,
582
+ schema,
583
+ onlyAddExisting
584
+ });
585
+
586
+ const responseAsId = response as 'number';
587
+ const responseAsData = response as { data: Item };
588
+ const responseAsErrors = response as { errors: Item }
589
+
590
+ if (
591
+ (onlyAddExisting && !responseAsErrors.errors) ||
592
+ (parseInt(responseAsId) && (createNewItem || !onlyAddExisting))
593
+ ) {
594
+ updateOptionsAC();
595
+ const data = [...dataGrid.data];
596
+ for (const i in data) {
597
+ if (data[i].id === newRow.id) {
598
+ data[i] = onlyAddExisting ? responseAsData.data : newRow;
599
+ if (isTmpId(newRow.id)) {
600
+ const newId = parseInt(responseAsId);
601
+ data[i].id = newId || responseAsData.data.id;
602
+ }
603
+
604
+ // This is for cases where users want to do custom operations after saving the row,
605
+ // like for example make calculations among columns.
606
+ if (onProcessRow) {
607
+ onProcessRow(data[i]);
608
+ }
609
+
610
+ // Reflect the changes to the outside, for example for global calculations over all data
611
+ if (onDataChange) {
612
+ onDataChange(data);
613
+ }
614
+
615
+ setDataGrid({ data });
616
+ break;
617
+ }
618
+ }
619
+ setDataGridLoading(false);
620
+ return newRow;
621
+ }
622
+ }
623
+ setDataGridLoading(false);
624
+ return false;
625
+ };
626
+
627
+ const customPaddings = (isAutoHeight)
628
+ ? {
629
+ '&.MuiDataGrid-root--densityCompact .MuiDataGrid-cell': { py: '8px' },
630
+ '&.MuiDataGrid-root--densityStandard .MuiDataGrid-cell': { py: '15px' },
631
+ '&.MuiDataGrid-root--densityComfortable .MuiDataGrid-cell': { py: '22px' }
632
+ }
633
+ : undefined;
634
+
635
+ return (
636
+ <Box className={`dataGrid_${name}`} sx={{ height: '100%' }}>
637
+ {preparedColumns === null
638
+ ? <Box sx={Layout.loadingBox}>
639
+ <CircularProgress />
640
+ </Box>
641
+ : <DataGrid
642
+ rows={dataGrid.data}
643
+ columns={preparedColumns}
644
+ onStateChange={(state) => {
645
+ if (setVisibleRows) {
646
+ const newRows = gridVisibleSortedRowIdsSelector(state);
647
+ setVisibleRows(newRows);
648
+ }
649
+ }}
650
+ editMode="row"
651
+ loading={dataGridLoading}
652
+ hideFooterPagination={hideFooterPagination}
653
+ getRowHeight={() => {
654
+ if (isAutoHeight) {
655
+ return 'auto';
656
+ }
657
+ return null;
658
+ }}
659
+ isCellEditable={params => (
660
+ rowModesModel[params.row.id as string]?.mode === GridRowModes.Edit &&
661
+ (
662
+ !isTmpId(params.row.id) ||
663
+ (
664
+ isTmpId(params.row.id) &&
665
+ (
666
+ params.field === indexField ||
667
+ !optionsAC.current ||
668
+ !Object.prototype.hasOwnProperty.call(optionsAC.current, indexField) ||
669
+ (
670
+ preparedColumns.find(col => col.field === indexField) &&
671
+ Object.prototype.hasOwnProperty.call(preparedColumns.find(col => col.field === indexField), 'valueFormatter')
672
+ )
673
+ )
674
+ )
675
+ )
676
+ ) as boolean}
677
+ rowModesModel={rowModesModel}
678
+ onRowEditStart={handleRowEditStart}
679
+ onRowEditStop={handleRowEditStop}
680
+ processRowUpdate={processRowUpdate}
681
+ onProcessRowUpdateError={(e) => {
682
+ setDataGridLoading(false);
683
+ if (processingRow.current) {
684
+ setRowModesModel({
685
+ ...rowModesModel,
686
+ [processingRow.current]: { mode: GridRowModes.Edit }
687
+ });
688
+ }
689
+ const msg = `Erro em "${e.path}": ${e.errors}`;
690
+ setSnackBar({
691
+ open: true,
692
+ severity: 'error',
693
+ msg
694
+ });
695
+ console.log(e);
696
+ }}
697
+ components={{ Toolbar: CustomToolbar, Footer: FooterToolbar }}
698
+ componentsProps={
699
+ {
700
+ toolbar: {
701
+ preparedColumns,
702
+ setPreparedColumns,
703
+ showQuickFilter: true,
704
+ quickFilterProps: { debounceMs: 500 }
705
+ },
706
+ footer: {
707
+ name,
708
+ setRowModesModel,
709
+ dataGrid,
710
+ setDataGrid,
711
+ emptyItem,
712
+ indexField,
713
+ isEditable
714
+ },
715
+ filterPanel: {
716
+ sx: {
717
+ '& .MuiDataGrid-filterFormValueInput': { width: 300 }
718
+ }
719
+ }
720
+ }
721
+ }
722
+ experimentalFeatures={{ newEditingApi: isEditable }}
723
+ sx={customPaddings}
724
+ />
725
+ }
726
+ <ConfirmDialog
727
+ open={dialogOpen}
728
+ onClose={handleDialogClose}
729
+ onConfirm={handleDeleteSave}
730
+ />
731
+ <Snackbar
732
+ open={snackBar.open}
733
+ autoHideDuration={5000}
734
+ onClose={() => { setSnackBar({ ...initialSnackBar, open: false }); }}
735
+ anchorOrigin={{ vertical: 'top', horizontal: 'center' }}
736
+ >
737
+ <Alert severity={snackBar.severity}>
738
+ {snackBar.msg}
739
+ </Alert>
740
+ </Snackbar>
741
+ </Box>
742
+ );
743
+ });
744
+
745
+ DataGridBySchemaEditable.displayName = 'DataGridBySchemaEditable';
746
+
747
+ export default DataGridBySchemaEditable;