@dashadmin/dash-auto-admin 1.3.25 → 1.3.26
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.
- package/dist/DashAutoAdminComponentRegistry.js +64 -1
- package/dist/DashAutoAdminForm.js +137 -1
- package/dist/DashAutoAdminResizableDrawer.js +63 -1
- package/dist/DashAutoAdminSaveButton.js +57 -1
- package/dist/DashAutoCreate.js +102 -1
- package/dist/DashAutoDrawer.js +256 -1
- package/dist/DashAutoEdit.js +99 -1
- package/dist/DashAutoFiltersGenerator.js +193 -1
- package/dist/DashAutoFormGroups.js +118 -1
- package/dist/DashAutoFormLayout.js +68 -1
- package/dist/DashAutoFormMuiTabs.js +291 -1
- package/dist/DashAutoFormTabs.js +132 -1
- package/dist/DashAutoGroup.js +27 -1
- package/dist/DashAutoLayout.js +31 -1
- package/dist/DashAutoList.js +218 -1
- package/dist/DashAutoPostFilterForm.js +47 -1
- package/dist/DashAutoReferenceTab.js +10 -1
- package/dist/DashAutoResource.js +77 -1
- package/dist/DashAutoResourceLayout.js +16 -1
- package/dist/DashAutoShow.js +195 -1
- package/dist/DashAutoTabbedForm.js +244 -1
- package/dist/DashAutoTabbedForm.review.js +221 -1
- package/dist/DashAutoTabs.js +11 -1
- package/dist/DashFilterWrapper.js +62 -1
- package/dist/DashRedirect.js +56 -1
- package/dist/TabbedLayout.js +38 -1
- package/dist/common/DashAutoTitle.js +33 -1
- package/dist/common/components/FabButton.js +59 -1
- package/dist/common/components/TranslatedLabel.js +60 -1
- package/dist/common/settings/Settings.js +196 -1
- package/dist/common/settings/SettingsBarIcons.js +61 -1
- package/dist/context/DashAutoAdminFormContext.js +15 -1
- package/dist/hooks/useAutoAdminLoadingStateMediator.js +36 -1
- package/dist/hooks/useAutoAdminTranslate.js +61 -1
- package/dist/hooks/useHash.js +24 -1
- package/dist/hooks/useLogger.js +20 -1
- package/dist/hooks/useRoutePathPattern.js +20 -1
- package/dist/hooks/useVirtualHash.js +28 -1
- package/dist/index.js +77 -3406
- package/dist/list/DashAutoListActions.js +57 -1
- package/dist/list/DashAutoListDatagridWrapper.js +68 -1
- package/dist/list/DashAutoListDefaultActionsWrapper.js +14 -1
- package/dist/list/DashAutoListFilterFormWithButton.js +80 -1
- package/dist/list/DashAutoListTopToolbar.js +180 -1
- package/dist/mui/AttributeToField.js +576 -1
- package/dist/mui/AttributeToInput.js +630 -1
- package/dist/mui/AutoAdminResizableDrawer.js +63 -1
- package/dist/mui/AutoDataGrid.js +120 -1
- package/dist/mui/AutoReferenceFormTab.js +53 -1
- package/dist/mui/AutoReferenceTab.js +46 -1
- package/dist/mui/AutoTabs.js +19 -1
- package/dist/mui/components/ExtendedPagination.js +34 -1
- package/dist/mui/components/ListStringField.js +25 -1
- package/dist/providers/DashAuthProvider.js +215 -1
- package/dist/providers/DashDataProvider.js +310 -1
- package/dist/providers/index.js +10 -1
- package/dist/react-admin.d.js +0 -0
- package/dist/toolbar/buttons/BottomToolbarButtons.js +65 -1
- package/dist/toolbar/buttons/DashResourceButton.js +181 -1
- package/dist/toolbar/buttons/ListButtons.js +80 -1
- package/dist/toolbar/buttons/ToolbarButtons.js +135 -1
- package/dist/types/ActionCallback.d.js +0 -0
- package/dist/utils/SimpleLogger.js +52 -1
- package/dist/utils/enumToChoices.js +5 -1
- package/dist/utils/evalActionPermission.js +11 -1
- package/dist/utils/groupByTabs.js +21 -1
- package/dist/utils/hashedGroupByTabs.js +12 -1
- package/dist/utils/invertMap.js +12 -1
- package/dist/utils/isClassComponent.js +7 -1
- package/dist/utils/isComponent.js +29 -1
- package/dist/utils/isEnum.js +5 -1
- package/dist/utils/isFC.js +8 -1
- package/dist/utils/replaceParams.js +9 -1
- package/dist/utils/validate.js +19 -1
- package/dist/wrappers/UserAction.js +60 -1
- package/dist/wrappers/index.js +4 -1
- package/package.json +135 -120
- package/src/DashAutoAdminComponentRegistry.tsx +75 -0
- package/src/DashAutoAdminForm.tsx +155 -0
- package/src/DashAutoAdminResizableDrawer.tsx +69 -0
- package/src/DashAutoAdminSaveButton.tsx +116 -0
- package/src/DashAutoCreate.tsx +118 -0
- package/src/DashAutoDrawer.tsx +288 -0
- package/src/DashAutoEdit.tsx +109 -0
- package/src/DashAutoFiltersGenerator.tsx +208 -0
- package/src/DashAutoFormGroups.tsx +146 -0
- package/src/DashAutoFormLayout.tsx.review +113 -0
- package/src/DashAutoFormMuiTabs.tsx +368 -0
- package/src/DashAutoFormTabs.tsx +177 -0
- package/src/DashAutoGroup.tsx +42 -0
- package/src/DashAutoLayout.tsx +57 -0
- package/src/DashAutoList.tsx +237 -0
- package/src/DashAutoPostFilterForm.tsx +35 -0
- package/src/DashAutoReferenceTab.tsx +9 -0
- package/src/DashAutoResource.tsx +85 -0
- package/src/DashAutoResourceLayout.tsx +50 -0
- package/src/DashAutoShow.tsx +200 -0
- package/src/DashAutoTabbedForm.review.tsx +262 -0
- package/src/DashAutoTabbedForm.tsx +324 -0
- package/src/DashAutoTabs.tsx +19 -0
- package/src/DashFilterWrapper.tsx +43 -0
- package/src/DashRedirect.tsx +46 -0
- package/src/TabbedLayout.tsx +29 -0
- package/src/common/DashAutoTitle.tsx +24 -0
- package/src/common/components/FabButton.tsx +40 -0
- package/src/common/components/TranslatedLabel.tsx +81 -0
- package/src/common/settings/Settings.tsx +236 -0
- package/src/common/settings/SettingsBarIcons.tsx +72 -0
- package/src/context/DashAutoAdminFormContext.tsx +31 -0
- package/src/hooks/useAutoAdminLoadingStateMediator.tsx +42 -0
- package/src/hooks/useAutoAdminTranslate.tsx +106 -0
- package/src/hooks/useHash.tsx +27 -0
- package/src/hooks/useLogger.tsx +23 -0
- package/src/hooks/useRoutePathPattern.tsx +24 -0
- package/src/hooks/useVirtualHash.tsx +34 -0
- package/src/index.ts +85 -0
- package/src/interfaces/ICustomResourceSubComponent.ts +11 -0
- package/src/interfaces/IDashApplicationLayoutMainAction.ts +37 -0
- package/src/interfaces/IDashApplicationLayoutMenuItem.ts +24 -0
- package/src/interfaces/IDashAutoAdminAttribute.ts +104 -0
- package/src/interfaces/IDashAutoAdminCustomFieldComponent.ts +18 -0
- package/src/interfaces/IDashAutoAdminFormOptions.ts +22 -0
- package/src/interfaces/IDashAutoAdminListActions.ts +13 -0
- package/src/interfaces/IDashAutoAdminReference.ts +16 -0
- package/src/interfaces/IDashAutoAdminResourceConfig.ts +360 -0
- package/src/interfaces/IDashAutoForm.ts +24 -0
- package/src/interfaces/IDashAutoGridButton.ts +35 -0
- package/src/interfaces/IDashAutoShow.ts +12 -0
- package/src/interfaces/IGroupExtraData.ts +8 -0
- package/src/interfaces/IRecord.ts +7 -0
- package/src/interfaces/IReferenceFilter.ts +55 -0
- package/src/interfaces/IResourceComponent.ts +8 -0
- package/src/interfaces/IToolbarButton.ts +11 -0
- package/src/list/DashAutoListActions.tsx +88 -0
- package/src/list/DashAutoListDatagridWrapper.tsx +74 -0
- package/src/list/DashAutoListDefaultActionsWrapper.tsx +31 -0
- package/src/list/DashAutoListFilterFormWithButton.tsx +93 -0
- package/src/list/DashAutoListTopToolbar.tsx +240 -0
- package/src/mui/AttributeToField.tsx +657 -0
- package/src/mui/AttributeToInput.tsx +728 -0
- package/src/mui/AutoAdminResizableDrawer.tsx +69 -0
- package/src/mui/AutoDataGrid.tsx +127 -0
- package/src/mui/AutoReferenceFormTab.tsx +51 -0
- package/src/mui/AutoReferenceTab.tsx +45 -0
- package/src/mui/AutoTabs.tsx +32 -0
- package/src/mui/components/ExtendedPagination.tsx +23 -0
- package/src/mui/components/ListStringField.tsx +32 -0
- package/src/providers/DashAuthProvider.ts +341 -0
- package/src/providers/DashDataProvider.ts +466 -0
- package/src/providers/index.ts +17 -0
- package/src/toolbar/buttons/BottomToolbarButtons.tsx +59 -0
- package/src/toolbar/buttons/DashResourceButton.tsx +223 -0
- package/src/toolbar/buttons/ListButtons.tsx +137 -0
- package/src/toolbar/buttons/ToolbarButtons.tsx +186 -0
- package/src/utils/SimpleLogger.tsx +46 -0
- package/src/utils/enumToChoices.tsx +3 -0
- package/src/utils/evalActionPermission.tsx +14 -0
- package/src/utils/groupByTabs.tsx +23 -0
- package/src/utils/hashedGroupByTabs.tsx +16 -0
- package/src/utils/invertMap.tsx +10 -0
- package/src/utils/isClassComponent.tsx +9 -0
- package/src/utils/isComponent.tsx +45 -0
- package/src/utils/isEnum.tsx +3 -0
- package/src/utils/isFC.tsx +6 -0
- package/src/utils/replaceParams.tsx +6 -0
- package/src/utils/validate.tsx +24 -0
- package/src/wrappers/UserAction.tsx +67 -0
- package/src/wrappers/index.tsx +1 -0
- /package/{dist → src}/DashAutoAdminSaveButton.tsx.bkup +0 -0
- /package/{dist/DashAutoFormLayout.tsx.review → src/DashAutoFormLayout.tsx} +0 -0
- /package/{dist → src}/DashAutoFormTabs.review +0 -0
- /package/{dist → src}/DashAutoFormTabs.tsx.formcontext.wip +0 -0
- /package/{dist → src}/mui/AttributeToField.tsx.review +0 -0
- /package/{dist → src}/mui/AttributeToInput.tsx.review +0 -0
- /package/{dist → src}/react-admin.d.ts +0 -0
- /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;
|