@mini_7/material-react-table-mcp 4.0.0 → 4.0.1

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.
Files changed (117) hide show
  1. package/README.md +1 -1
  2. package/data/examples/advanced.js +215 -0
  3. package/data/examples/aggregation-and-grouping.js +116 -0
  4. package/data/examples/aggregation-multi.js +89 -0
  5. package/data/examples/alternate-column-filtering.js +38 -0
  6. package/data/examples/alternate-detail-panel.js +65 -0
  7. package/data/examples/alternate-pagination.js +17 -0
  8. package/data/examples/basic.js +86 -0
  9. package/data/examples/chart-detail-panel.js +69 -0
  10. package/data/examples/column-actions-space.js +34 -0
  11. package/data/examples/column-alignment.js +58 -0
  12. package/data/examples/custom-column-actions.js +74 -0
  13. package/data/examples/custom-column-filtering-ui.js +59 -0
  14. package/data/examples/custom-headless.js +85 -0
  15. package/data/examples/custom-top-toolbar.js +60 -0
  16. package/data/examples/customize-display-columns.js +72 -0
  17. package/data/examples/customize-filter-components.js +78 -0
  18. package/data/examples/customize-filter-modes.js +59 -0
  19. package/data/examples/customize-filter-variants.js +98 -0
  20. package/data/examples/customize-global-filter-component.js +37 -0
  21. package/data/examples/customize-remove-column-grouping.js +75 -0
  22. package/data/examples/customize-row-selection.js +43 -0
  23. package/data/examples/customize-table-styles.js +81 -0
  24. package/data/examples/disable-column-actions.js +26 -0
  25. package/data/examples/disable-column-hiding.js +74 -0
  26. package/data/examples/disable-density-toggle.js +37 -0
  27. package/data/examples/dynamic-columns.js +124 -0
  28. package/data/examples/editing-crud-cell.js +288 -0
  29. package/data/examples/editing-crud-modal.js +275 -0
  30. package/data/examples/editing-crud-row.js +255 -0
  31. package/data/examples/editing-crud-table.js +280 -0
  32. package/data/examples/editing-crud-tree.js +328 -0
  33. package/data/examples/enable-cell-actions.js +52 -0
  34. package/data/examples/enable-click-to-copy.js +33 -0
  35. package/data/examples/enable-column-grouping.js +69 -0
  36. package/data/examples/enable-column-ordering.js +37 -0
  37. package/data/examples/enable-column-pinning.js +58 -0
  38. package/data/examples/enable-column-resizing.js +45 -0
  39. package/data/examples/enable-column-virtualization.js +19 -0
  40. package/data/examples/enable-detail-panel-conditionally.js +61 -0
  41. package/data/examples/enable-detail-panel-virtualized.js +64 -0
  42. package/data/examples/enable-expanding-tree.js +87 -0
  43. package/data/examples/enable-filter-facet-values.js +52 -0
  44. package/data/examples/enable-row-dragging.js +102 -0
  45. package/data/examples/enable-row-numbers-original.js +37 -0
  46. package/data/examples/enable-row-numbers-static.js +37 -0
  47. package/data/examples/enable-row-ordering.js +40 -0
  48. package/data/examples/enable-row-pinning-select.js +64 -0
  49. package/data/examples/enable-row-pinning-static.js +39 -0
  50. package/data/examples/enable-row-pinning-sticky.js +57 -0
  51. package/data/examples/enable-row-selection.js +71 -0
  52. package/data/examples/enable-row-virtualization.js +85 -0
  53. package/data/examples/enable-sticky-header.js +47 -0
  54. package/data/examples/expanding-tree-expanded.js +98 -0
  55. package/data/examples/expanding-tree-flat-parse.js +102 -0
  56. package/data/examples/expanding-tree-root-expanded.js +120 -0
  57. package/data/examples/export-to-csv.js +84 -0
  58. package/data/examples/export-to-pdf.js +76 -0
  59. package/data/examples/external-toolbar.js +82 -0
  60. package/data/examples/font-awesome-icons.js +62 -0
  61. package/data/examples/infinite-scrolling.js +137 -0
  62. package/data/examples/lazy-detail-panel.js +164 -0
  63. package/data/examples/lazy-sub-rows.js +118 -0
  64. package/data/examples/linear-progress.js +48 -0
  65. package/data/examples/loading.js +34 -0
  66. package/data/examples/localization-i18n-ar.js +40 -0
  67. package/data/examples/localization-i18n-az.js +38 -0
  68. package/data/examples/localization-i18n-bg.js +38 -0
  69. package/data/examples/localization-i18n-cs.js +38 -0
  70. package/data/examples/localization-i18n-da.js +38 -0
  71. package/data/examples/localization-i18n-de.js +38 -0
  72. package/data/examples/localization-i18n-el.js +38 -0
  73. package/data/examples/localization-i18n-en.js +38 -0
  74. package/data/examples/localization-i18n-es.js +38 -0
  75. package/data/examples/localization-i18n-et.js +38 -0
  76. package/data/examples/localization-i18n-fa.js +40 -0
  77. package/data/examples/localization-i18n-fi.js +38 -0
  78. package/data/examples/localization-i18n-fr.js +38 -0
  79. package/data/examples/localization-i18n-he.js +40 -0
  80. package/data/examples/localization-i18n-hr.js +38 -0
  81. package/data/examples/localization-i18n-hu.js +38 -0
  82. package/data/examples/localization-i18n-hy.js +40 -0
  83. package/data/examples/localization-i18n-id.js +38 -0
  84. package/data/examples/localization-i18n-it.js +38 -0
  85. package/data/examples/localization-i18n-ja.js +38 -0
  86. package/data/examples/localization-i18n-ko.js +38 -0
  87. package/data/examples/localization-i18n-nl.js +38 -0
  88. package/data/examples/localization-i18n-no.js +38 -0
  89. package/data/examples/localization-i18n-np.js +38 -0
  90. package/data/examples/localization-i18n-pl.js +38 -0
  91. package/data/examples/localization-i18n-pt-BR.js +38 -0
  92. package/data/examples/localization-i18n-pt.js +38 -0
  93. package/data/examples/localization-i18n-ro.js +38 -0
  94. package/data/examples/localization-i18n-ru.js +38 -0
  95. package/data/examples/localization-i18n-sk.js +38 -0
  96. package/data/examples/localization-i18n-sr-Cyrl-RS.js +38 -0
  97. package/data/examples/localization-i18n-sr-Latn-RS.js +38 -0
  98. package/data/examples/localization-i18n-sv.js +38 -0
  99. package/data/examples/localization-i18n-tr.js +38 -0
  100. package/data/examples/localization-i18n-uk.js +38 -0
  101. package/data/examples/localization-i18n-vi.js +38 -0
  102. package/data/examples/localization-i18n-zh-hans.js +38 -0
  103. package/data/examples/localization-i18n-zh-hant.js +38 -0
  104. package/data/examples/manual-selection.js +81 -0
  105. package/data/examples/minimal.js +66 -0
  106. package/data/examples/mui-theme.js +109 -0
  107. package/data/examples/multi-sorting.js +48 -0
  108. package/data/examples/persistent-state.js +126 -0
  109. package/data/examples/react-query.js +122 -0
  110. package/data/examples/remote.js +113 -0
  111. package/data/examples/row-actions-buttons.js +54 -0
  112. package/data/examples/row-actions-menu-items.js +36 -0
  113. package/data/examples/single-row-selection.js +74 -0
  114. package/data/examples/virtualized.js +122 -0
  115. package/dist/data.js +31 -9
  116. package/dist/index.js +18 -6
  117. package/package.json +1 -1
@@ -0,0 +1,255 @@
1
+ import { lazy, Suspense, useMemo, useState } from 'react';
2
+ import { MaterialReactTable, useMaterialReactTable, } from 'material-react-table';
3
+ import { Box, Button, IconButton, Tooltip } from '@mui/material';
4
+ import { QueryClient, QueryClientProvider, useMutation, useQuery, useQueryClient, } from '@tanstack/react-query';
5
+ import { fakeData, usStates } from './makeData';
6
+ import EditIcon from '@mui/icons-material/Edit';
7
+ import DeleteIcon from '@mui/icons-material/Delete';
8
+ const Example = () => {
9
+ const [validationErrors, setValidationErrors] = useState({});
10
+ const columns = useMemo(() => [
11
+ {
12
+ accessorKey: 'id',
13
+ header: 'Id',
14
+ enableEditing: false,
15
+ size: 80,
16
+ },
17
+ {
18
+ accessorKey: 'firstName',
19
+ header: 'First Name',
20
+ muiEditTextFieldProps: {
21
+ required: true,
22
+ error: !!validationErrors?.firstName,
23
+ helperText: validationErrors?.firstName,
24
+ //remove any previous validation errors when user focuses on the input
25
+ onFocus: () => setValidationErrors({
26
+ ...validationErrors,
27
+ firstName: undefined,
28
+ }),
29
+ //optionally add validation checking for onBlur or onChange
30
+ },
31
+ },
32
+ {
33
+ accessorKey: 'lastName',
34
+ header: 'Last Name',
35
+ muiEditTextFieldProps: {
36
+ required: true,
37
+ error: !!validationErrors?.lastName,
38
+ helperText: validationErrors?.lastName,
39
+ //remove any previous validation errors when user focuses on the input
40
+ onFocus: () => setValidationErrors({
41
+ ...validationErrors,
42
+ lastName: undefined,
43
+ }),
44
+ },
45
+ },
46
+ {
47
+ accessorKey: 'email',
48
+ header: 'Email',
49
+ muiEditTextFieldProps: {
50
+ type: 'email',
51
+ required: true,
52
+ error: !!validationErrors?.email,
53
+ helperText: validationErrors?.email,
54
+ //remove any previous validation errors when user focuses on the input
55
+ onFocus: () => setValidationErrors({
56
+ ...validationErrors,
57
+ email: undefined,
58
+ }),
59
+ },
60
+ },
61
+ {
62
+ accessorKey: 'state',
63
+ header: 'State',
64
+ editVariant: 'select',
65
+ editSelectOptions: usStates,
66
+ muiEditTextFieldProps: {
67
+ select: true,
68
+ error: !!validationErrors?.state,
69
+ helperText: validationErrors?.state,
70
+ },
71
+ },
72
+ ], [validationErrors]);
73
+ //call CREATE hook
74
+ const { mutateAsync: createUser, isPending: isCreatingUser } = useCreateUser();
75
+ //call READ hook
76
+ const { data: fetchedUsers = [], isError: isLoadingUsersError, isFetching: isFetchingUsers, isLoading: isLoadingUsers, } = useGetUsers();
77
+ //call UPDATE hook
78
+ const { mutateAsync: updateUser, isPending: isUpdatingUser } = useUpdateUser();
79
+ //call DELETE hook
80
+ const { mutateAsync: deleteUser, isPending: isDeletingUser } = useDeleteUser();
81
+ //CREATE action
82
+ const handleCreateUser = async ({ values, table, }) => {
83
+ const newValidationErrors = validateUser(values);
84
+ if (Object.values(newValidationErrors).some((error) => error)) {
85
+ setValidationErrors(newValidationErrors);
86
+ return;
87
+ }
88
+ setValidationErrors({});
89
+ await createUser(values);
90
+ table.setCreatingRow(null); //exit creating mode
91
+ };
92
+ //UPDATE action
93
+ const handleSaveUser = async ({ values, table, }) => {
94
+ const newValidationErrors = validateUser(values);
95
+ if (Object.values(newValidationErrors).some((error) => error)) {
96
+ setValidationErrors(newValidationErrors);
97
+ return;
98
+ }
99
+ setValidationErrors({});
100
+ await updateUser(values);
101
+ table.setEditingRow(null); //exit editing mode
102
+ };
103
+ //DELETE action
104
+ const openDeleteConfirmModal = (row) => {
105
+ if (window.confirm('Are you sure you want to delete this user?')) {
106
+ deleteUser(row.original.id);
107
+ }
108
+ };
109
+ const table = useMaterialReactTable({
110
+ columns,
111
+ data: fetchedUsers,
112
+ createDisplayMode: 'row', // ('modal', and 'custom' are also available)
113
+ editDisplayMode: 'row', // ('modal', 'cell', 'table', and 'custom' are also available)
114
+ enableEditing: true,
115
+ getRowId: (row) => row.id,
116
+ muiToolbarAlertBannerProps: isLoadingUsersError
117
+ ? {
118
+ color: 'error',
119
+ children: 'Error loading data',
120
+ }
121
+ : undefined,
122
+ muiTableContainerProps: {
123
+ sx: {
124
+ minHeight: '500px',
125
+ },
126
+ },
127
+ onCreatingRowCancel: () => setValidationErrors({}),
128
+ onCreatingRowSave: handleCreateUser,
129
+ onEditingRowCancel: () => setValidationErrors({}),
130
+ onEditingRowSave: handleSaveUser,
131
+ renderRowActions: ({ row, table }) => (<Box sx={{ display: 'flex', gap: '1rem' }}>
132
+ <Tooltip title="Edit">
133
+ <IconButton onClick={() => table.setEditingRow(row)}>
134
+ <EditIcon />
135
+ </IconButton>
136
+ </Tooltip>
137
+ <Tooltip title="Delete">
138
+ <IconButton color="error" onClick={() => openDeleteConfirmModal(row)}>
139
+ <DeleteIcon />
140
+ </IconButton>
141
+ </Tooltip>
142
+ </Box>),
143
+ renderTopToolbarCustomActions: ({ table }) => (<Button variant="contained" onClick={() => {
144
+ table.setCreatingRow(true); //simplest way to open the create row modal with no default values
145
+ //or you can pass in a row object to set default values with the `createRow` helper function
146
+ // table.setCreatingRow(
147
+ // createRow(table, {
148
+ // //optionally pass in default values for the new row, useful for nested data or other complex scenarios
149
+ // }),
150
+ // );
151
+ }}>
152
+ Create New User
153
+ </Button>),
154
+ state: {
155
+ isLoading: isLoadingUsers,
156
+ isSaving: isCreatingUser || isUpdatingUser || isDeletingUser,
157
+ showAlertBanner: isLoadingUsersError,
158
+ showProgressBars: isFetchingUsers,
159
+ },
160
+ });
161
+ return <MaterialReactTable table={table}/>;
162
+ };
163
+ //CREATE hook (post new user to api)
164
+ function useCreateUser() {
165
+ const queryClient = useQueryClient();
166
+ return useMutation({
167
+ mutationFn: async (user) => {
168
+ //send api update request here
169
+ await new Promise((resolve) => setTimeout(resolve, 1000)); //fake api call
170
+ return Promise.resolve();
171
+ },
172
+ //client side optimistic update
173
+ onMutate: (newUserInfo) => {
174
+ queryClient.setQueryData(['users'], (prevUsers) => [
175
+ ...prevUsers,
176
+ {
177
+ ...newUserInfo,
178
+ id: (Math.random() + 1).toString(36).substring(7),
179
+ },
180
+ ]);
181
+ },
182
+ // onSettled: () => queryClient.invalidateQueries({ queryKey: ['users'] }), //refetch users after mutation, disabled for demo
183
+ });
184
+ }
185
+ //READ hook (get users from api)
186
+ function useGetUsers() {
187
+ return useQuery({
188
+ queryKey: ['users'],
189
+ queryFn: async () => {
190
+ //send api request here
191
+ await new Promise((resolve) => setTimeout(resolve, 1000)); //fake api call
192
+ return Promise.resolve(fakeData);
193
+ },
194
+ refetchOnWindowFocus: false,
195
+ });
196
+ }
197
+ //UPDATE hook (put user in api)
198
+ function useUpdateUser() {
199
+ const queryClient = useQueryClient();
200
+ return useMutation({
201
+ mutationFn: async (user) => {
202
+ //send api update request here
203
+ await new Promise((resolve) => setTimeout(resolve, 1000)); //fake api call
204
+ return Promise.resolve();
205
+ },
206
+ //client side optimistic update
207
+ onMutate: (newUserInfo) => {
208
+ queryClient.setQueryData(['users'], (prevUsers) => prevUsers?.map((prevUser) => prevUser.id === newUserInfo.id ? newUserInfo : prevUser));
209
+ },
210
+ // onSettled: () => queryClient.invalidateQueries({ queryKey: ['users'] }), //refetch users after mutation, disabled for demo
211
+ });
212
+ }
213
+ //DELETE hook (delete user in api)
214
+ function useDeleteUser() {
215
+ const queryClient = useQueryClient();
216
+ return useMutation({
217
+ mutationFn: async (userId) => {
218
+ //send api update request here
219
+ await new Promise((resolve) => setTimeout(resolve, 1000)); //fake api call
220
+ return Promise.resolve();
221
+ },
222
+ //client side optimistic update
223
+ onMutate: (userId) => {
224
+ queryClient.setQueryData(['users'], (prevUsers) => prevUsers?.filter((user) => user.id !== userId));
225
+ },
226
+ // onSettled: () => queryClient.invalidateQueries({ queryKey: ['users'] }), //refetch users after mutation, disabled for demo
227
+ });
228
+ }
229
+ //react query setup in App.tsx
230
+ const ReactQueryDevtoolsProduction = lazy(() => import('@tanstack/react-query-devtools/build/modern/production.js').then((d) => ({
231
+ default: d.ReactQueryDevtools,
232
+ })));
233
+ const queryClient = new QueryClient();
234
+ export default function App() {
235
+ return (<QueryClientProvider client={queryClient}>
236
+ <Example />
237
+ <Suspense fallback={null}>
238
+ <ReactQueryDevtoolsProduction />
239
+ </Suspense>
240
+ </QueryClientProvider>);
241
+ }
242
+ const validateRequired = (value) => !!value.length;
243
+ const validateEmail = (email) => !!email.length &&
244
+ email
245
+ .toLowerCase()
246
+ .match(/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/);
247
+ function validateUser(user) {
248
+ return {
249
+ firstName: !validateRequired(user.firstName)
250
+ ? 'First Name is Required'
251
+ : '',
252
+ lastName: !validateRequired(user.lastName) ? 'Last Name is Required' : '',
253
+ email: !validateEmail(user.email) ? 'Incorrect Email Format' : '',
254
+ };
255
+ }
@@ -0,0 +1,280 @@
1
+ import { lazy, Suspense, useMemo, useState } from 'react';
2
+ import { MaterialReactTable, useMaterialReactTable, } from 'material-react-table';
3
+ import { Box, Button, CircularProgress, IconButton, Tooltip, Typography, } from '@mui/material';
4
+ import { QueryClient, QueryClientProvider, useMutation, useQuery, useQueryClient, } from '@tanstack/react-query';
5
+ import { fakeData, usStates } from './makeData';
6
+ import DeleteIcon from '@mui/icons-material/Delete';
7
+ const Example = () => {
8
+ const [validationErrors, setValidationErrors] = useState({});
9
+ //keep track of rows that have been edited
10
+ const [editedUsers, setEditedUsers] = useState({});
11
+ const columns = useMemo(() => [
12
+ {
13
+ accessorKey: 'id',
14
+ header: 'Id',
15
+ enableEditing: false,
16
+ size: 80,
17
+ },
18
+ {
19
+ accessorKey: 'firstName',
20
+ header: 'First Name',
21
+ muiEditTextFieldProps: ({ cell, row }) => ({
22
+ type: 'text',
23
+ required: true,
24
+ error: !!validationErrors?.[cell.id],
25
+ helperText: validationErrors?.[cell.id],
26
+ //store edited user in state to be saved later
27
+ onBlur: (event) => {
28
+ const validationError = !validateRequired(event.currentTarget.value)
29
+ ? 'Required'
30
+ : undefined;
31
+ setValidationErrors({
32
+ ...validationErrors,
33
+ [cell.id]: validationError,
34
+ });
35
+ setEditedUsers({ ...editedUsers, [row.id]: row.original });
36
+ },
37
+ }),
38
+ },
39
+ {
40
+ accessorKey: 'lastName',
41
+ header: 'Last Name',
42
+ muiEditTextFieldProps: ({ cell, row }) => ({
43
+ type: 'text',
44
+ required: true,
45
+ error: !!validationErrors?.[cell.id],
46
+ helperText: validationErrors?.[cell.id],
47
+ //store edited user in state to be saved later
48
+ onBlur: (event) => {
49
+ const validationError = !validateRequired(event.currentTarget.value)
50
+ ? 'Required'
51
+ : undefined;
52
+ setValidationErrors({
53
+ ...validationErrors,
54
+ [cell.id]: validationError,
55
+ });
56
+ setEditedUsers({ ...editedUsers, [row.id]: row.original });
57
+ },
58
+ }),
59
+ },
60
+ {
61
+ accessorKey: 'email',
62
+ header: 'Email',
63
+ muiEditTextFieldProps: ({ cell, row }) => ({
64
+ type: 'email',
65
+ required: true,
66
+ error: !!validationErrors?.[cell.id],
67
+ helperText: validationErrors?.[cell.id],
68
+ //store edited user in state to be saved later
69
+ onBlur: (event) => {
70
+ const validationError = !validateEmail(event.currentTarget.value)
71
+ ? 'Incorrect Email Format'
72
+ : undefined;
73
+ setValidationErrors({
74
+ ...validationErrors,
75
+ [cell.id]: validationError,
76
+ });
77
+ setEditedUsers({ ...editedUsers, [row.id]: row.original });
78
+ },
79
+ }),
80
+ },
81
+ {
82
+ accessorKey: 'state',
83
+ header: 'State',
84
+ editVariant: 'select',
85
+ editSelectOptions: usStates,
86
+ muiEditTextFieldProps: ({ row }) => ({
87
+ select: true,
88
+ error: !!validationErrors?.state,
89
+ helperText: validationErrors?.state,
90
+ onChange: (event) => setEditedUsers({
91
+ ...editedUsers,
92
+ [row.id]: { ...row.original, state: event.target.value },
93
+ }),
94
+ }),
95
+ },
96
+ ], [editedUsers, validationErrors]);
97
+ //call CREATE hook
98
+ const { mutateAsync: createUser, isPending: isCreatingUser } = useCreateUser();
99
+ //call READ hook
100
+ const { data: fetchedUsers = [], isError: isLoadingUsersError, isFetching: isFetchingUsers, isLoading: isLoadingUsers, } = useGetUsers();
101
+ //call UPDATE hook
102
+ const { mutateAsync: updateUsers, isPending: isUpdatingUsers } = useUpdateUsers();
103
+ //call DELETE hook
104
+ const { mutateAsync: deleteUser, isPending: isDeletingUser } = useDeleteUser();
105
+ //CREATE action
106
+ const handleCreateUser = async ({ values, table, }) => {
107
+ const newValidationErrors = validateUser(values);
108
+ if (Object.values(newValidationErrors).some((error) => error)) {
109
+ setValidationErrors(newValidationErrors);
110
+ return;
111
+ }
112
+ setValidationErrors({});
113
+ await createUser(values);
114
+ table.setCreatingRow(null); //exit creating mode
115
+ };
116
+ //UPDATE action
117
+ const handleSaveUsers = async () => {
118
+ if (Object.values(validationErrors).some((error) => !!error))
119
+ return;
120
+ await updateUsers(Object.values(editedUsers));
121
+ setEditedUsers({});
122
+ };
123
+ //DELETE action
124
+ const openDeleteConfirmModal = (row) => {
125
+ if (window.confirm('Are you sure you want to delete this user?')) {
126
+ deleteUser(row.original.id);
127
+ }
128
+ };
129
+ const table = useMaterialReactTable({
130
+ columns,
131
+ data: fetchedUsers,
132
+ createDisplayMode: 'row', // ('modal', and 'custom' are also available)
133
+ editDisplayMode: 'table', // ('modal', 'row', 'cell', and 'custom' are also
134
+ enableEditing: true,
135
+ enableRowActions: true,
136
+ positionActionsColumn: 'last',
137
+ getRowId: (row) => row.id,
138
+ muiToolbarAlertBannerProps: isLoadingUsersError
139
+ ? {
140
+ color: 'error',
141
+ children: 'Error loading data',
142
+ }
143
+ : undefined,
144
+ muiTableContainerProps: {
145
+ sx: {
146
+ minHeight: '500px',
147
+ },
148
+ },
149
+ onCreatingRowCancel: () => setValidationErrors({}),
150
+ onCreatingRowSave: handleCreateUser,
151
+ renderRowActions: ({ row }) => (<Box sx={{ display: 'flex', gap: '1rem' }}>
152
+ <Tooltip title="Delete">
153
+ <IconButton color="error" onClick={() => openDeleteConfirmModal(row)}>
154
+ <DeleteIcon />
155
+ </IconButton>
156
+ </Tooltip>
157
+ </Box>),
158
+ renderBottomToolbarCustomActions: () => (<Box sx={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
159
+ <Button color="success" variant="contained" onClick={handleSaveUsers} disabled={Object.keys(editedUsers).length === 0 ||
160
+ Object.values(validationErrors).some((error) => !!error)}>
161
+ {isUpdatingUsers ? <CircularProgress size={25}/> : 'Save'}
162
+ </Button>
163
+ {Object.values(validationErrors).some((error) => !!error) && (<Typography color="error">Fix errors before submitting</Typography>)}
164
+ </Box>),
165
+ renderTopToolbarCustomActions: ({ table }) => (<Button variant="contained" onClick={() => {
166
+ table.setCreatingRow(true); //simplest way to open the create row modal with no default values
167
+ //or you can pass in a row object to set default values with the `createRow` helper function
168
+ // table.setCreatingRow(
169
+ // createRow(table, {
170
+ // //optionally pass in default values for the new row, useful for nested data or other complex scenarios
171
+ // }),
172
+ // );
173
+ }}>
174
+ Create New User
175
+ </Button>),
176
+ state: {
177
+ isLoading: isLoadingUsers,
178
+ isSaving: isCreatingUser || isUpdatingUsers || isDeletingUser,
179
+ showAlertBanner: isLoadingUsersError,
180
+ showProgressBars: isFetchingUsers,
181
+ },
182
+ });
183
+ return <MaterialReactTable table={table}/>;
184
+ };
185
+ //CREATE hook (post new user to api)
186
+ function useCreateUser() {
187
+ const queryClient = useQueryClient();
188
+ return useMutation({
189
+ mutationFn: async (user) => {
190
+ //send api update request here
191
+ await new Promise((resolve) => setTimeout(resolve, 1000)); //fake api call
192
+ return Promise.resolve();
193
+ },
194
+ //client side optimistic update
195
+ onMutate: (newUserInfo) => {
196
+ queryClient.setQueryData(['users'], (prevUsers) => [
197
+ ...prevUsers,
198
+ {
199
+ ...newUserInfo,
200
+ id: (Math.random() + 1).toString(36).substring(7),
201
+ },
202
+ ]);
203
+ },
204
+ // onSettled: () => queryClient.invalidateQueries({ queryKey: ['users'] }), //refetch users after mutation, disabled for demo
205
+ });
206
+ }
207
+ //READ hook (get users from api)
208
+ function useGetUsers() {
209
+ return useQuery({
210
+ queryKey: ['users'],
211
+ queryFn: async () => {
212
+ //send api request here
213
+ await new Promise((resolve) => setTimeout(resolve, 1000)); //fake api call
214
+ return Promise.resolve(fakeData);
215
+ },
216
+ refetchOnWindowFocus: false,
217
+ });
218
+ }
219
+ //UPDATE hook (put user in api)
220
+ function useUpdateUsers() {
221
+ const queryClient = useQueryClient();
222
+ return useMutation({
223
+ mutationFn: async (users) => {
224
+ //send api update request here
225
+ await new Promise((resolve) => setTimeout(resolve, 1000)); //fake api call
226
+ return Promise.resolve();
227
+ },
228
+ //client side optimistic update
229
+ onMutate: (newUsers) => {
230
+ queryClient.setQueryData(['users'], (prevUsers) => prevUsers?.map((user) => {
231
+ const newUser = newUsers.find((u) => u.id === user.id);
232
+ return newUser ? newUser : user;
233
+ }));
234
+ },
235
+ // onSettled: () => queryClient.invalidateQueries({ queryKey: ['users'] }), //refetch users after mutation, disabled for demo
236
+ });
237
+ }
238
+ //DELETE hook (delete user in api)
239
+ function useDeleteUser() {
240
+ const queryClient = useQueryClient();
241
+ return useMutation({
242
+ mutationFn: async (userId) => {
243
+ //send api update request here
244
+ await new Promise((resolve) => setTimeout(resolve, 1000)); //fake api call
245
+ return Promise.resolve();
246
+ },
247
+ //client side optimistic update
248
+ onMutate: (userId) => {
249
+ queryClient.setQueryData(['users'], (prevUsers) => prevUsers?.filter((user) => user.id !== userId));
250
+ },
251
+ // onSettled: () => queryClient.invalidateQueries({ queryKey: ['users'] }), //refetch users after mutation, disabled for demo
252
+ });
253
+ }
254
+ //react query setup in App.tsx
255
+ const ReactQueryDevtoolsProduction = lazy(() => import('@tanstack/react-query-devtools/build/modern/production.js').then((d) => ({
256
+ default: d.ReactQueryDevtools,
257
+ })));
258
+ const queryClient = new QueryClient();
259
+ export default function App() {
260
+ return (<QueryClientProvider client={queryClient}>
261
+ <Example />
262
+ <Suspense fallback={null}>
263
+ <ReactQueryDevtoolsProduction />
264
+ </Suspense>
265
+ </QueryClientProvider>);
266
+ }
267
+ const validateRequired = (value) => !!value.length;
268
+ const validateEmail = (email) => !!email.length &&
269
+ email
270
+ .toLowerCase()
271
+ .match(/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/);
272
+ function validateUser(user) {
273
+ return {
274
+ firstName: !validateRequired(user.firstName)
275
+ ? 'First Name is Required'
276
+ : '',
277
+ lastName: !validateRequired(user.lastName) ? 'Last Name is Required' : '',
278
+ email: !validateEmail(user.email) ? 'Incorrect Email Format' : '',
279
+ };
280
+ }