@dashadmin/dash-auto-admin 1.3.25 → 1.3.28

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 (177) hide show
  1. package/README.md +32 -6
  2. package/dist/DashAutoAdminComponentRegistry.js +64 -1
  3. package/dist/DashAutoAdminForm.js +137 -1
  4. package/dist/DashAutoAdminResizableDrawer.js +63 -1
  5. package/dist/DashAutoAdminSaveButton.js +57 -1
  6. package/dist/DashAutoCreate.js +102 -1
  7. package/dist/DashAutoDrawer.js +256 -1
  8. package/dist/DashAutoEdit.js +99 -1
  9. package/dist/DashAutoFiltersGenerator.js +193 -1
  10. package/dist/DashAutoFormGroups.js +118 -1
  11. package/dist/DashAutoFormLayout.js +68 -1
  12. package/dist/DashAutoFormMuiTabs.js +291 -1
  13. package/dist/DashAutoFormTabs.js +132 -1
  14. package/dist/DashAutoGroup.js +27 -1
  15. package/dist/DashAutoLayout.js +31 -1
  16. package/dist/DashAutoList.js +218 -1
  17. package/dist/DashAutoPostFilterForm.js +47 -1
  18. package/dist/DashAutoReferenceTab.js +10 -1
  19. package/dist/DashAutoResource.js +77 -1
  20. package/dist/DashAutoResourceLayout.js +16 -1
  21. package/dist/DashAutoShow.js +195 -1
  22. package/dist/DashAutoTabbedForm.js +244 -1
  23. package/dist/DashAutoTabbedForm.review.js +221 -1
  24. package/dist/DashAutoTabs.js +11 -1
  25. package/dist/DashFilterWrapper.js +62 -1
  26. package/dist/DashRedirect.js +56 -1
  27. package/dist/TabbedLayout.js +38 -1
  28. package/dist/common/DashAutoTitle.js +33 -1
  29. package/dist/common/components/FabButton.js +59 -1
  30. package/dist/common/components/TranslatedLabel.js +60 -1
  31. package/dist/common/settings/Settings.js +196 -1
  32. package/dist/common/settings/SettingsBarIcons.js +61 -1
  33. package/dist/context/DashAutoAdminFormContext.js +15 -1
  34. package/dist/hooks/useAutoAdminLoadingStateMediator.js +36 -1
  35. package/dist/hooks/useAutoAdminTranslate.js +61 -1
  36. package/dist/hooks/useHash.js +24 -1
  37. package/dist/hooks/useLogger.js +20 -1
  38. package/dist/hooks/useRoutePathPattern.js +20 -1
  39. package/dist/hooks/useVirtualHash.js +28 -1
  40. package/dist/index.js +77 -3406
  41. package/dist/list/DashAutoListActions.js +57 -1
  42. package/dist/list/DashAutoListDatagridWrapper.js +68 -1
  43. package/dist/list/DashAutoListDefaultActionsWrapper.js +14 -1
  44. package/dist/list/DashAutoListFilterFormWithButton.js +80 -1
  45. package/dist/list/DashAutoListTopToolbar.js +180 -1
  46. package/dist/mui/AttributeToField.js +576 -1
  47. package/dist/mui/AttributeToInput.js +630 -1
  48. package/dist/mui/AutoAdminResizableDrawer.js +63 -1
  49. package/dist/mui/AutoDataGrid.js +120 -1
  50. package/dist/mui/AutoReferenceFormTab.js +53 -1
  51. package/dist/mui/AutoReferenceTab.js +46 -1
  52. package/dist/mui/AutoTabs.js +19 -1
  53. package/dist/mui/components/ExtendedPagination.js +34 -1
  54. package/dist/mui/components/ListStringField.js +25 -1
  55. package/dist/providers/DashAuthProvider.js +215 -1
  56. package/dist/providers/DashDataProvider.js +310 -1
  57. package/dist/providers/index.js +10 -1
  58. package/dist/react-admin.d.js +0 -0
  59. package/dist/toolbar/buttons/BottomToolbarButtons.js +65 -1
  60. package/dist/toolbar/buttons/DashResourceButton.js +181 -1
  61. package/dist/toolbar/buttons/ListButtons.js +80 -1
  62. package/dist/toolbar/buttons/ToolbarButtons.js +135 -1
  63. package/dist/types/ActionCallback.d.js +0 -0
  64. package/dist/utils/SimpleLogger.js +52 -1
  65. package/dist/utils/enumToChoices.js +5 -1
  66. package/dist/utils/evalActionPermission.js +11 -1
  67. package/dist/utils/groupByTabs.js +21 -1
  68. package/dist/utils/hashedGroupByTabs.js +12 -1
  69. package/dist/utils/invertMap.js +12 -1
  70. package/dist/utils/isClassComponent.js +7 -1
  71. package/dist/utils/isComponent.js +29 -1
  72. package/dist/utils/isEnum.js +5 -1
  73. package/dist/utils/isFC.js +8 -1
  74. package/dist/utils/replaceParams.js +9 -1
  75. package/dist/utils/validate.js +19 -1
  76. package/dist/wrappers/UserAction.js +60 -1
  77. package/dist/wrappers/index.js +4 -1
  78. package/package.json +134 -120
  79. package/src/DashAutoAdminComponentRegistry.tsx +75 -0
  80. package/src/DashAutoAdminForm.tsx +155 -0
  81. package/src/DashAutoAdminResizableDrawer.tsx +69 -0
  82. package/src/DashAutoAdminSaveButton.tsx +116 -0
  83. package/src/DashAutoCreate.tsx +118 -0
  84. package/src/DashAutoDrawer.tsx +288 -0
  85. package/src/DashAutoEdit.tsx +109 -0
  86. package/src/DashAutoFiltersGenerator.tsx +208 -0
  87. package/src/DashAutoFormGroups.tsx +146 -0
  88. package/src/DashAutoFormLayout.tsx.review +113 -0
  89. package/src/DashAutoFormMuiTabs.tsx +368 -0
  90. package/src/DashAutoFormTabs.tsx +177 -0
  91. package/src/DashAutoGroup.tsx +42 -0
  92. package/src/DashAutoLayout.tsx +57 -0
  93. package/src/DashAutoList.tsx +237 -0
  94. package/src/DashAutoPostFilterForm.tsx +35 -0
  95. package/src/DashAutoReferenceTab.tsx +9 -0
  96. package/src/DashAutoResource.tsx +85 -0
  97. package/src/DashAutoResourceLayout.tsx +50 -0
  98. package/src/DashAutoShow.tsx +200 -0
  99. package/src/DashAutoTabbedForm.review.tsx +262 -0
  100. package/src/DashAutoTabbedForm.tsx +324 -0
  101. package/src/DashAutoTabs.tsx +19 -0
  102. package/src/DashFilterWrapper.tsx +43 -0
  103. package/src/DashRedirect.tsx +46 -0
  104. package/src/TabbedLayout.tsx +29 -0
  105. package/src/common/DashAutoTitle.tsx +24 -0
  106. package/src/common/components/FabButton.tsx +40 -0
  107. package/src/common/components/TranslatedLabel.tsx +81 -0
  108. package/src/common/settings/Settings.tsx +236 -0
  109. package/src/common/settings/SettingsBarIcons.tsx +72 -0
  110. package/src/context/DashAutoAdminFormContext.tsx +31 -0
  111. package/src/hooks/useAutoAdminLoadingStateMediator.tsx +42 -0
  112. package/src/hooks/useAutoAdminTranslate.tsx +106 -0
  113. package/src/hooks/useHash.tsx +27 -0
  114. package/src/hooks/useLogger.tsx +23 -0
  115. package/src/hooks/useRoutePathPattern.tsx +24 -0
  116. package/src/hooks/useVirtualHash.tsx +34 -0
  117. package/src/index.ts +85 -0
  118. package/src/interfaces/ICustomResourceSubComponent.ts +11 -0
  119. package/src/interfaces/IDashApplicationLayoutMainAction.ts +37 -0
  120. package/src/interfaces/IDashApplicationLayoutMenuItem.ts +24 -0
  121. package/src/interfaces/IDashAutoAdminAttribute.ts +104 -0
  122. package/src/interfaces/IDashAutoAdminCustomFieldComponent.ts +18 -0
  123. package/src/interfaces/IDashAutoAdminFormOptions.ts +22 -0
  124. package/src/interfaces/IDashAutoAdminListActions.ts +13 -0
  125. package/src/interfaces/IDashAutoAdminReference.ts +16 -0
  126. package/src/interfaces/IDashAutoAdminResourceConfig.ts +360 -0
  127. package/src/interfaces/IDashAutoForm.ts +24 -0
  128. package/src/interfaces/IDashAutoGridButton.ts +35 -0
  129. package/src/interfaces/IDashAutoShow.ts +12 -0
  130. package/src/interfaces/IGroupExtraData.ts +8 -0
  131. package/src/interfaces/IRecord.ts +7 -0
  132. package/src/interfaces/IReferenceFilter.ts +55 -0
  133. package/src/interfaces/IResourceComponent.ts +8 -0
  134. package/src/interfaces/IToolbarButton.ts +11 -0
  135. package/src/list/DashAutoListActions.tsx +88 -0
  136. package/src/list/DashAutoListDatagridWrapper.tsx +74 -0
  137. package/src/list/DashAutoListDefaultActionsWrapper.tsx +31 -0
  138. package/src/list/DashAutoListFilterFormWithButton.tsx +93 -0
  139. package/src/list/DashAutoListTopToolbar.tsx +240 -0
  140. package/src/mui/AttributeToField.tsx +657 -0
  141. package/src/mui/AttributeToInput.tsx +728 -0
  142. package/src/mui/AutoAdminResizableDrawer.tsx +69 -0
  143. package/src/mui/AutoDataGrid.tsx +127 -0
  144. package/src/mui/AutoReferenceFormTab.tsx +51 -0
  145. package/src/mui/AutoReferenceTab.tsx +45 -0
  146. package/src/mui/AutoTabs.tsx +32 -0
  147. package/src/mui/components/ExtendedPagination.tsx +23 -0
  148. package/src/mui/components/ListStringField.tsx +32 -0
  149. package/src/providers/DashAuthProvider.ts +341 -0
  150. package/src/providers/DashDataProvider.ts +466 -0
  151. package/src/providers/index.ts +17 -0
  152. package/src/toolbar/buttons/BottomToolbarButtons.tsx +59 -0
  153. package/src/toolbar/buttons/DashResourceButton.tsx +223 -0
  154. package/src/toolbar/buttons/ListButtons.tsx +137 -0
  155. package/src/toolbar/buttons/ToolbarButtons.tsx +186 -0
  156. package/src/utils/SimpleLogger.tsx +46 -0
  157. package/src/utils/enumToChoices.tsx +3 -0
  158. package/src/utils/evalActionPermission.tsx +14 -0
  159. package/src/utils/groupByTabs.tsx +23 -0
  160. package/src/utils/hashedGroupByTabs.tsx +16 -0
  161. package/src/utils/invertMap.tsx +10 -0
  162. package/src/utils/isClassComponent.tsx +9 -0
  163. package/src/utils/isComponent.tsx +45 -0
  164. package/src/utils/isEnum.tsx +3 -0
  165. package/src/utils/isFC.tsx +6 -0
  166. package/src/utils/replaceParams.tsx +6 -0
  167. package/src/utils/validate.tsx +24 -0
  168. package/src/wrappers/UserAction.tsx +67 -0
  169. package/src/wrappers/index.tsx +1 -0
  170. /package/{dist → src}/DashAutoAdminSaveButton.tsx.bkup +0 -0
  171. /package/{dist/DashAutoFormLayout.tsx.review → src/DashAutoFormLayout.tsx} +0 -0
  172. /package/{dist → src}/DashAutoFormTabs.review +0 -0
  173. /package/{dist → src}/DashAutoFormTabs.tsx.formcontext.wip +0 -0
  174. /package/{dist → src}/mui/AttributeToField.tsx.review +0 -0
  175. /package/{dist → src}/mui/AttributeToInput.tsx.review +0 -0
  176. /package/{dist → src}/react-admin.d.ts +0 -0
  177. /package/{dist → src}/types/ActionCallback.d.ts +0 -0
@@ -0,0 +1,262 @@
1
+ /* TODO: commented code note - handling of axios error response to parse the error to the appropiate format the react-hook-form was disabled without further testing. */
2
+ import { useCallback, useEffect, useState } from 'react';
3
+ import {
4
+ useResourceContext,
5
+ useRecordContext,
6
+ TabbedForm,
7
+ SimpleForm,
8
+ } from 'react-admin';
9
+
10
+ import { DashAutoFormGroups, IDashAutoAdminResourceConfig, groupByTabs, validate } from '.';
11
+ import { DashAutoFormTabs } from '.';
12
+ import { DashAutoFormLayout } from '.';
13
+ import { useDataProvider } from 'react-admin';
14
+
15
+ import { IDashAutoAdminForm } from './DashAutoAdminForm';
16
+ import axios from 'axios';
17
+ import { useSelector } from 'react-redux';
18
+ import { IDASHAppState } from 'dash-admin-state';
19
+ import { Loading } from 'react-admin';
20
+ import { useFormContext, useFormState } from 'react-hook-form';
21
+
22
+
23
+ export type IDashAutoTabbedForm = IDashAutoAdminForm;
24
+
25
+ /**
26
+ * The `DashAutoTabbedForm` component is a React functional component that renders a tabbed form for a Dash Auto Admin resource.
27
+ * It handles the logic for creating, updating, and submitting the form data, as well as managing the form mode (create or edit).
28
+ * The component uses the `react-admin` library to provide the necessary functionality for the form.
29
+ *
30
+ * @param {IDashAutoTabbedForm} props - The component props, including the resource configuration, submit and error handlers, toolbar, mode, and whether the form is being rendered in a drawer.
31
+ * @returns {JSX.Element} - The rendered tabbed form component.
32
+ */
33
+ const DashAutoTabbedForm: React.FC<IDashAutoTabbedForm> = ({
34
+ resourceConfig,
35
+ onSubmit,
36
+ beforeSubmit,
37
+ onError,
38
+ toolbar,
39
+ mode,
40
+ isDrawer = false,
41
+ }) => {
42
+
43
+ const resource = useResourceContext();
44
+ const record = useRecordContext();
45
+ const debug = true;
46
+
47
+ useEffect(() => {
48
+
49
+ console.log("Dash Auto Tabbed rerendered");
50
+ },[]);
51
+
52
+ // Add this to access form methods including setError
53
+ const formContext = useFormContext();
54
+
55
+ const dataProvider = useDataProvider();
56
+
57
+ let formGroupMode = mode === 'create' && resourceConfig?.formGroupModes?.create
58
+ ? resourceConfig.formGroupModes.create
59
+ : mode === 'edit' && resourceConfig?.formGroupModes?.edit
60
+ ? resourceConfig.formGroupModes.edit
61
+ : resourceConfig?.formGroupMode
62
+ ? resourceConfig.formGroupMode
63
+ : 'tabs';
64
+
65
+ // Fallback to layout if only one tab
66
+ if (formGroupMode === "tabs" && groupByTabs(resourceConfig.schema).length === 1) {
67
+ formGroupMode = 'layout';
68
+ }
69
+ const formData = useSelector(
70
+ (
71
+ state: IDASHAppState<any, any, IDashAutoAdminResourceConfig>,
72
+ ) => {
73
+
74
+ return state.formData || {};
75
+ },
76
+ );
77
+
78
+ const onCreateSave = useCallback(
79
+ async (values) => {
80
+
81
+ if (debug) console.log('onCreateSave called with values:', values);
82
+ if (!resource) {
83
+ throw new Error('Resource is required');
84
+ }
85
+
86
+ try {
87
+ if (beforeSubmit) {
88
+ values = beforeSubmit(values);
89
+ }
90
+
91
+
92
+
93
+ const { data } = await dataProvider.create(resource, {
94
+ data: values
95
+ });
96
+
97
+ if (onSubmit) {
98
+ onSubmit(data);
99
+ }
100
+ } catch (error) {
101
+
102
+ if (onError) {
103
+ onError(error);
104
+ }
105
+
106
+
107
+ }
108
+ },
109
+ [resource, beforeSubmit, dataProvider, onSubmit, onError]
110
+ );
111
+
112
+ const onUpdateSave = useCallback(
113
+ async (values) => {
114
+
115
+ if (debug) console.log('onUpdateSave called with values:', values);
116
+ try {
117
+ if (beforeSubmit) {
118
+ values = beforeSubmit(values);
119
+ }
120
+
121
+
122
+ const { data } = await dataProvider.update(resource+"/"+record.id, {
123
+ data: values
124
+ });
125
+
126
+ if (onSubmit) {
127
+ onSubmit(data);
128
+ }
129
+ } catch (error) {
130
+
131
+ if (onError) {
132
+ onError(error);
133
+ }
134
+
135
+ }
136
+ },
137
+ [resource, record, beforeSubmit, dataProvider, onSubmit, onError]
138
+ );
139
+
140
+ let onSave = onCreateSave;
141
+ switch (mode) {
142
+ case 'edit':
143
+ onSave = onUpdateSave;
144
+ break;
145
+ case 'create':
146
+ onSave = onCreateSave;
147
+ break;
148
+ }
149
+
150
+
151
+ if(!formData) return <Loading/>
152
+
153
+ if (
154
+ mode === 'create' &&
155
+ typeof resourceConfig?.createComponent === 'function'
156
+ ) {
157
+ return (
158
+ <SimpleForm
159
+ key="create-form"
160
+ //redirect={false}
161
+ toolbar={toolbar || null}
162
+ onSubmit={onSave}
163
+ validate={validate(resourceConfig.schema)}
164
+ reValidateMode="onBlur"
165
+ className={'auto-admin-grouped-form'}
166
+
167
+ >
168
+ {resourceConfig.createComponent(resourceConfig)}
169
+ </SimpleForm>
170
+ );
171
+ }
172
+
173
+ if (mode === 'edit' && typeof resourceConfig?.editComponent === 'function') {
174
+ return (
175
+ <SimpleForm
176
+ key="edit-form"
177
+ warnWhenUnsavedChanges
178
+ //redirect={false}
179
+ toolbar={toolbar || null}
180
+ onSubmit={onSave}
181
+ validate={validate(resourceConfig.schema)}
182
+ reValidateMode="onBlur"
183
+ className={'auto-admin-grouped-form'}
184
+
185
+ >
186
+ {resourceConfig.editComponent(resourceConfig)}
187
+ </SimpleForm>
188
+ );
189
+ }
190
+
191
+ if (formGroupMode === 'tabs' && groupByTabs(resourceConfig.schema).length > 1) {
192
+ //console.log(record,formData, {...record,...formData});
193
+
194
+ return (
195
+ <>
196
+ <TabbedForm
197
+ key="tabbed-form"
198
+ toolbar={toolbar || null}
199
+ onSubmit={onSave}
200
+ validate={validate(resourceConfig.schema)}
201
+ className={'tabbed-form-custom dash-auto-admin-tabbed-form'}
202
+ resetOptions={{ keepDirtyValues: true }}
203
+ syncWithLocation={resourceConfig.syncTabsWithLocation || false}
204
+
205
+ defaultValues={mode === "create" ? formData : {...record,...formData}}
206
+
207
+ >
208
+ {/* It has to be a function and not a Functional component, because it returns an array of JSX elements without parent container */}
209
+ {/* The downside, is can't implement hooks within the DashAutoFormTabs component */}
210
+ {DashAutoFormTabs({
211
+ schema: resourceConfig.schema,
212
+ resourceConfig: resourceConfig,
213
+ options: { mode: mode, isDrawer: isDrawer },
214
+ })}
215
+ </TabbedForm>
216
+
217
+ </>
218
+ );
219
+ }
220
+
221
+ if (formGroupMode === 'groups') {
222
+ return (
223
+ <SimpleForm
224
+ key="groups-form"
225
+ //redirect={false}
226
+ toolbar={toolbar || null}
227
+ onSubmit={onSave}
228
+ validate={validate(resourceConfig.schema)}
229
+ reValidateMode="onBlur"
230
+ className={'auto-admin-grouped-form'}
231
+
232
+ >
233
+ {DashAutoFormGroups({schema:resourceConfig.schema, resourceConfig, options:{
234
+ mode: mode,
235
+ isDrawer: isDrawer,
236
+ }})}
237
+ </SimpleForm>
238
+ );
239
+ }
240
+
241
+ if (formGroupMode === 'layout') {
242
+ return (
243
+ <SimpleForm
244
+ key="layout-form"
245
+ //redirect={false}
246
+ toolbar={toolbar || null}
247
+ onSubmit={onSave}
248
+ validate={validate(resourceConfig.schema)}
249
+ reValidateMode="onBlur"
250
+ className={'auto-admin-grouped-form'}
251
+
252
+ >
253
+ {DashAutoFormLayout({schema:resourceConfig.schema, resourceConfig:resourceConfig, options:{
254
+ mode: mode,
255
+ isDrawer: isDrawer,
256
+ }})}
257
+ </SimpleForm>
258
+ );
259
+ }
260
+ };
261
+
262
+ export default DashAutoTabbedForm;
@@ -0,0 +1,324 @@
1
+ /* TODO: commented code note - handling of axios error response to parse the error to the appropiate format the react-hook-form was disabled without further testing. */
2
+ import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react';
3
+ import {
4
+ useResourceContext,
5
+ useRecordContext,
6
+ TabbedForm,
7
+ SimpleForm,
8
+ } from 'react-admin';
9
+
10
+ import { DashAutoFormGroups, IDashAutoAdminResourceConfig, groupByTabs, validate } from '.';
11
+ import { DashAutoFormTabs } from '.';
12
+ import { DashAutoFormLayout } from '.';
13
+ import { useDataProvider } from 'react-admin';
14
+
15
+ import { IDashAutoAdminForm } from './DashAutoAdminForm';
16
+ import { DashAutoAdminFormProvider } from './context/DashAutoAdminFormContext';
17
+
18
+ import { useSelector } from 'react-redux';
19
+ //import { IDASHAppState } from 'dash-admin-state';
20
+ import { Loading } from 'react-admin';
21
+ import { useFormContext, useFormState } from 'react-hook-form';
22
+
23
+
24
+ export type IDashAutoTabbedForm = IDashAutoAdminForm;
25
+
26
+ /**
27
+ * The `DashAutoTabbedForm` component is a React functional component that renders a tabbed form for a Dash Auto Admin resource.
28
+ * It handles the logic for creating, updating, and submitting the form data, as well as managing the form mode (create or edit).
29
+ * The component uses the `react-admin` library to provide the necessary functionality for the form.
30
+ *
31
+ * @param {IDashAutoTabbedForm} props - The component props, including the resource configuration, submit and error handlers, toolbar, mode, and whether the form is being rendered in a drawer.
32
+ * @returns {JSX.Element} - The rendered tabbed form component.
33
+ */
34
+ const DashAutoTabbedForm: React.FC<IDashAutoTabbedForm> = ({
35
+ resourceConfig,
36
+ onSubmit,
37
+ beforeSubmit,
38
+ onError,
39
+ toolbar,
40
+ mode,
41
+ isDrawer = false,
42
+ locale,
43
+ }) => {
44
+
45
+ const resource = useResourceContext();
46
+ const record = useRecordContext();
47
+ const debug = true;
48
+ const dataProvider = useDataProvider();
49
+
50
+ let formGroupMode = mode === 'create' && resourceConfig?.formGroupModes?.create
51
+ ? resourceConfig.formGroupModes.create
52
+ : mode === 'edit' && resourceConfig?.formGroupModes?.edit
53
+ ? resourceConfig.formGroupModes.edit
54
+ : resourceConfig?.formGroupMode
55
+ ? resourceConfig.formGroupMode
56
+ : 'tabs';
57
+
58
+ // Calculate the number of visible tabs for the current mode
59
+ const getVisibleTabCount = () => {
60
+ const tabGroups = groupByTabs(resourceConfig.schema);
61
+ return tabGroups.filter((group) => {
62
+ const filteredAttributes = group.filter((attr) => {
63
+ if (mode === 'create') {
64
+ return attr?.inCreate !== false;
65
+ } else if (mode === 'edit') {
66
+ return attr?.inEdit !== false;
67
+ }
68
+ // For any other mode, include all
69
+ return true;
70
+ });
71
+ return filteredAttributes.length > 0;
72
+ }).length;
73
+ };
74
+
75
+ const visibleTabCount = useMemo(() => getVisibleTabCount(), [resourceConfig.schema, mode, locale]);
76
+
77
+ // Fallback to layout if only one visible tab for the current mode
78
+ if (formGroupMode === "tabs" && visibleTabCount <= 1) {
79
+ formGroupMode = 'layout';
80
+ }
81
+ const formData = useSelector(
82
+ (
83
+ //state: IDASHAppState<any, any, IDashAutoAdminResourceConfig>,
84
+ state: any
85
+ ) => {
86
+
87
+ return state.formData || {};
88
+ },
89
+ );
90
+
91
+ const onCreateSave = useCallback(
92
+ async (values) => {
93
+
94
+ if (debug) console.log('onCreateSave called with values:', values);
95
+ if (!resource) {
96
+ throw new Error('Resource is required');
97
+ }
98
+
99
+ try {
100
+ if (beforeSubmit) {
101
+ values = beforeSubmit(values);
102
+ }
103
+
104
+
105
+
106
+ const { data } = await dataProvider.create(resource, {
107
+ data: values
108
+ });
109
+
110
+ if (onSubmit) {
111
+ onSubmit(data);
112
+ }
113
+ } catch (error) {
114
+
115
+ if (onError) {
116
+ onError(error);
117
+ }
118
+
119
+
120
+ }
121
+ },
122
+ [resource, beforeSubmit, dataProvider, onSubmit, onError]
123
+ );
124
+
125
+ const onUpdateSave = useCallback(
126
+ async (values) => {
127
+
128
+ if (debug) console.log('onUpdateSave called with values:', values);
129
+ try {
130
+ if (beforeSubmit) {
131
+ values = beforeSubmit(values);
132
+ }
133
+
134
+
135
+ const { data } = await dataProvider.update(resource+"/"+record.id, {
136
+ id: record?.id,
137
+ data: values,
138
+ previousData: record
139
+ });
140
+
141
+ if (onSubmit) {
142
+ onSubmit(data);
143
+ }
144
+ } catch (error) {
145
+
146
+ if (onError) {
147
+ onError(error);
148
+ }
149
+
150
+ }
151
+ },
152
+ [resource, record, beforeSubmit, dataProvider, onSubmit, onError]
153
+ );
154
+
155
+ let onSave = onCreateSave;
156
+ switch (mode) {
157
+ case 'edit':
158
+ onSave = onUpdateSave;
159
+ break;
160
+ case 'create':
161
+ onSave = onCreateSave;
162
+ break;
163
+ }
164
+
165
+ // 🔧 FIX: Memoize ContextComponent to prevent unstable reference on each render
166
+ // Previously this was creating a new component reference on each render,
167
+ // causing React to unmount and remount the entire component tree
168
+ const ContextComponent = useMemo(() => {
169
+ return resourceConfig.contextComponent || (({children}: {children: ReactNode}) => <>{children}</>);
170
+ }, [resourceConfig.contextComponent, locale]);
171
+
172
+ // 🔧 FIX: Memoize the context value to prevent unnecessary re-renders
173
+ // The onSave and mode values only change when their dependencies change
174
+ const formContextValue = useMemo(() => ({ onSave, mode, locale }), [onSave, mode, locale]);
175
+
176
+
177
+ if(!formData) return <Loading/>
178
+
179
+ if (
180
+ mode === 'create' &&
181
+ typeof resourceConfig?.createComponent === 'function'
182
+ ) {
183
+ return (
184
+ <SimpleForm
185
+ key="create-form"
186
+ //redirect={false}
187
+
188
+ toolbar={toolbar || null}
189
+ onSubmit={onSave}
190
+ validate={validate(resourceConfig.schema)}
191
+ reValidateMode="onBlur"
192
+ className={'auto-admin-grouped-form'}
193
+
194
+ >
195
+ <DashAutoAdminFormProvider value={formContextValue}>
196
+ <ContextComponent mode={mode} resourceConfig={resourceConfig}>
197
+ {resourceConfig.createComponent(resourceConfig)}
198
+ </ContextComponent>
199
+ </DashAutoAdminFormProvider>
200
+
201
+ </SimpleForm>
202
+ );
203
+ }
204
+
205
+ if (mode === 'edit' && typeof resourceConfig?.editComponent === 'function') {
206
+ return (
207
+ <SimpleForm
208
+ key="edit-form"
209
+ warnWhenUnsavedChanges
210
+ //redirect={false}
211
+ toolbar={toolbar || null}
212
+ onSubmit={onSave}
213
+ validate={validate(resourceConfig.schema)}
214
+ reValidateMode="onBlur"
215
+ className={'auto-admin-grouped-form'}
216
+
217
+ >
218
+ <DashAutoAdminFormProvider value={formContextValue}>
219
+ <ContextComponent mode={mode} resourceConfig={resourceConfig}>
220
+ {resourceConfig.editComponent(resourceConfig)}
221
+ </ContextComponent>
222
+ </DashAutoAdminFormProvider>
223
+
224
+ </SimpleForm>
225
+ );
226
+ }
227
+
228
+ // 🔧 FIX: Create a memoized wrapper component for TabbedForm
229
+ // This prevents React from treating it as a new component on each render
230
+ const TabbedFormWrapper = useMemo(() => {
231
+ const Wrapper = ({ children }: { children: ReactNode }) => (
232
+ <DashAutoAdminFormProvider value={formContextValue}>
233
+ <ContextComponent mode={mode} resourceConfig={resourceConfig}>
234
+ {children}
235
+ </ContextComponent>
236
+ </DashAutoAdminFormProvider>
237
+ );
238
+ return Wrapper;
239
+ }, [formContextValue, ContextComponent, mode, resourceConfig, locale]);
240
+
241
+ if (formGroupMode === 'tabs' && groupByTabs(resourceConfig.schema).length > 1) {
242
+ //console.log(record,formData, {...record,...formData});
243
+
244
+ return (
245
+ <>
246
+
247
+ <TabbedForm
248
+ key="tabbed-form"
249
+ toolbar={toolbar || null}
250
+ onSubmit={onSave}
251
+ validate={validate(resourceConfig.schema)}
252
+ className={'tabbed-form-custom dash-auto-admin-tabbed-form'}
253
+ resetOptions={{ keepDirtyValues: true }}
254
+ syncWithLocation={resourceConfig.syncTabsWithLocation || false}
255
+
256
+ defaultValues={mode === "create" ? formData : {...record,...formData}}
257
+ component={TabbedFormWrapper}
258
+ //component={(props) => props.children}
259
+ >
260
+
261
+ {/* It has to be a function and not a Functional component, because it returns an array of JSX elements without parent container */}
262
+ {/* The downside, is can't implement hooks within the DashAutoFormTabs component */}
263
+ {DashAutoFormTabs({
264
+ schema: resourceConfig.schema,
265
+ resourceConfig: resourceConfig,
266
+ options: { mode: mode, isDrawer: isDrawer, locale: locale },
267
+ })}
268
+
269
+ </TabbedForm>
270
+
271
+ </>
272
+ );
273
+ }
274
+
275
+ if (formGroupMode === 'groups') {
276
+ return (
277
+ <SimpleForm
278
+ key="groups-form"
279
+ //redirect={false}
280
+ toolbar={toolbar || null}
281
+ onSubmit={onSave}
282
+ validate={validate(resourceConfig.schema)}
283
+ reValidateMode="onBlur"
284
+ className={'auto-admin-grouped-form'}
285
+
286
+ >
287
+ <DashAutoAdminFormProvider value={formContextValue}>
288
+ <ContextComponent mode={mode} resourceConfig={resourceConfig}>
289
+ {DashAutoFormGroups({schema:resourceConfig.schema, resourceConfig, options:{
290
+ mode: mode,
291
+ isDrawer: isDrawer,
292
+ }})}
293
+ </ContextComponent>
294
+ </DashAutoAdminFormProvider>
295
+ </SimpleForm>
296
+ );
297
+ }
298
+
299
+ if (formGroupMode === 'layout') {
300
+ return (
301
+ <SimpleForm
302
+ key="layout-form"
303
+ //redirect={false}
304
+ toolbar={toolbar || null}
305
+ onSubmit={onSave}
306
+ validate={validate(resourceConfig.schema)}
307
+ reValidateMode="onBlur"
308
+ className={'auto-admin-grouped-form'}
309
+
310
+ >
311
+ <DashAutoAdminFormProvider value={formContextValue}>
312
+ <ContextComponent mode={mode} resourceConfig={resourceConfig}>
313
+ {DashAutoFormLayout({schema:resourceConfig.schema, resourceConfig:resourceConfig, options:{
314
+ mode: mode,
315
+ isDrawer: isDrawer,
316
+ }})}
317
+ </ContextComponent>
318
+ </DashAutoAdminFormProvider>
319
+ </SimpleForm>
320
+ );
321
+ }
322
+ };
323
+
324
+ export default DashAutoTabbedForm;
@@ -0,0 +1,19 @@
1
+ import { TabbedShowLayout } from 'react-admin';
2
+ import { IDashAutoAdminResourceConfig } from '.';
3
+ import IDashAutoAdminFormOptions from './interfaces/IDashAutoAdminFormOptions';
4
+ import { AutoTabs as MuiAutoTabbedForm } from './mui/AutoTabs';
5
+
6
+ const AutoTabs = (
7
+ resourceConfig: IDashAutoAdminResourceConfig,
8
+ options?: IDashAutoAdminFormOptions,
9
+ ) => {
10
+ const _AutoTabs = MuiAutoTabbedForm;
11
+
12
+ return (
13
+ <TabbedShowLayout >
14
+ {_AutoTabs(resourceConfig, resourceConfig.schema, options)}
15
+ </TabbedShowLayout>
16
+ );
17
+ };
18
+
19
+ export default AutoTabs;
@@ -0,0 +1,43 @@
1
+ import React from 'react';
2
+ import { useInput, InputProps } from 'react-admin';
3
+
4
+ interface DashFilterWrapperProps extends InputProps {
5
+ children: React.ReactElement;
6
+ source: string;
7
+ label?: string;
8
+ alwaysOn?: boolean;
9
+ }
10
+
11
+ /**
12
+ * Wrapper component that makes custom components work with React Admin's filter system
13
+ */
14
+ const DashFilterWrapper: React.FC<DashFilterWrapperProps> = ({
15
+ children,
16
+ source,
17
+ label,
18
+ alwaysOn,
19
+ ...props
20
+ }) => {
21
+ const {
22
+ field: { onChange, onBlur, value },
23
+ fieldState: { error, invalid },
24
+ formState: { isSubmitted }
25
+ } = useInput({ source, ...props });
26
+
27
+ // Clone the child component and pass the necessary props
28
+ const childWithProps = React.cloneElement(children, {
29
+ value,
30
+ onChange,
31
+ onBlur,
32
+ error: error?.message,
33
+ helperText: error?.message,
34
+ source,
35
+ label,
36
+ /* @ts-ignore */
37
+ ...children.props, // Preserve original props
38
+ });
39
+
40
+ return childWithProps;
41
+ };
42
+
43
+ export default DashFilterWrapper;
@@ -0,0 +1,46 @@
1
+ import {DASHAdminSystemConstants} from "dash-constants";
2
+ import { IDashAutoAdminResourceConfig } from "../";
3
+ import { useEffect } from "react";
4
+ import { useRedirect, Loading } from "react-admin";
5
+ import { useParams } from "react-router";
6
+
7
+ export interface IDashRedirect {
8
+ method: 'edit' | 'list' | 'create' | 'show';
9
+ drawerMethod: 'edit' | 'list' | 'create' | 'show';
10
+ resourceConfig: IDashAutoAdminResourceConfig;
11
+ stateHashPattern?: string;
12
+ }
13
+
14
+ const URL_PREFIX = DASHAdminSystemConstants.system.URL_PREFIX;
15
+ const DashRedirect: React.FC<IDashRedirect> = ({
16
+ method,
17
+ resourceConfig,
18
+ stateHashPattern = URL_PREFIX,
19
+ drawerMethod = 'edit',
20
+ ..._props
21
+ }) => {
22
+ const params = useParams();
23
+ const redirect = useRedirect();
24
+
25
+ const idParamName = resourceConfig?.idParamName || 'id';
26
+
27
+ useEffect(() => {
28
+ let _stateHash = stateHashPattern.replace(
29
+ `:${idParamName}`,
30
+ params[idParamName],
31
+ );
32
+ _stateHash = _stateHash.replace(':method', drawerMethod);
33
+
34
+ redirect(
35
+ method,
36
+ resourceConfig.model,
37
+ method === 'edit' ? params.id : null,
38
+ null,
39
+ { hash: _stateHash },
40
+ );
41
+ }, []);
42
+
43
+ return <Loading />;
44
+ };
45
+
46
+ export default DashRedirect;