@nestledjs/data-browser 0.1.24 → 1.0.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.
- package/index.js +2 -0
- package/lib/components/AdminDataStateMessage.d.ts +15 -0
- package/lib/components/AdminDataStateMessage.js +44 -0
- package/lib/components/FilterField.d.ts +14 -0
- package/lib/components/FilterField.js +152 -0
- package/lib/components/filters/EnumFilter.d.ts +8 -0
- package/lib/components/filters/EnumFilter.js +39 -0
- package/lib/components/filters/index.d.ts +1 -0
- package/lib/context/AdminDataContext.d.ts +16 -1
- package/lib/context/AdminDataContext.js +4 -3
- package/lib/pages/AdminDataCreatePage.js +9 -4
- package/lib/pages/AdminDataEditPage.js +313 -246
- package/lib/pages/AdminDataListPage.js +67 -103
- package/lib/utils/graphql-utils.d.ts +12 -1
- package/lib/utils/graphql-utils.js +60 -39
- package/lib/utils/string-utils.js +14 -0
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import { useClickOutside } from "./lib/hooks/useClickOutside.js";
|
|
|
10
10
|
import { useDebounce } from "./lib/hooks/useDebounce.js";
|
|
11
11
|
import { useRelationData } from "./lib/hooks/useRelationData.js";
|
|
12
12
|
import { DateRangeFilter } from "./lib/components/filters/DateRangeFilter.js";
|
|
13
|
+
import { EnumFilter } from "./lib/components/filters/EnumFilter.js";
|
|
13
14
|
import { NumberRangeFilter } from "./lib/components/filters/NumberRangeFilter.js";
|
|
14
15
|
import { RelationDropdownButton, RelationDropdownContent, RelationItem, RelationItemList, RelationSearchInput } from "./lib/components/filters/RelationComponents.js";
|
|
15
16
|
import { RelationFilterField } from "./lib/components/filters/RelationFilterField.js";
|
|
@@ -39,6 +40,7 @@ export {
|
|
|
39
40
|
AdminStatusDisplay,
|
|
40
41
|
AdminUserStatus,
|
|
41
42
|
DateRangeFilter,
|
|
43
|
+
EnumFilter,
|
|
42
44
|
NumberRangeFilter,
|
|
43
45
|
RelationDropdownButton,
|
|
44
46
|
RelationDropdownContent,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
interface AdminDataStateMessageProps {
|
|
2
|
+
readonly type: 'error' | 'loading' | 'not-found' | 'schema-error';
|
|
3
|
+
readonly title: string;
|
|
4
|
+
readonly message: string;
|
|
5
|
+
readonly basePath: string;
|
|
6
|
+
readonly onRetry?: () => void;
|
|
7
|
+
readonly backLinkText?: string;
|
|
8
|
+
readonly backLinkPath?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Reusable component for displaying error, loading, and other state messages
|
|
12
|
+
* Extracted to reduce cognitive complexity in AdminDataEditPage
|
|
13
|
+
*/
|
|
14
|
+
export declare function AdminDataStateMessage({ type, title, message, basePath, onRetry, backLinkText, backLinkPath, }: AdminDataStateMessageProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { ExclamationCircleIcon } from "@heroicons/react/24/outline";
|
|
3
|
+
import { Link } from "react-router";
|
|
4
|
+
function AdminDataStateMessage({
|
|
5
|
+
type,
|
|
6
|
+
title,
|
|
7
|
+
message,
|
|
8
|
+
basePath,
|
|
9
|
+
onRetry,
|
|
10
|
+
backLinkText = "Return to Data Browser",
|
|
11
|
+
backLinkPath
|
|
12
|
+
}) {
|
|
13
|
+
const iconColor = type === "not-found" ? "text-yellow-400" : "text-red-400";
|
|
14
|
+
return /* @__PURE__ */ jsx("div", { className: "flex flex-col justify-center py-12", children: /* @__PURE__ */ jsx("div", { className: "mt-8 mx-auto w-full max-w-md", children: /* @__PURE__ */ jsx("div", { className: "bg-white dark:bg-gray-800 py-8 px-4 shadow sm:rounded-lg sm:px-10", children: /* @__PURE__ */ jsx("div", { className: "text-center", children: type === "loading" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
15
|
+
/* @__PURE__ */ jsx("div", { className: "mx-auto h-12 w-12 border-4 border-green-web border-t-transparent rounded-full animate-spin" }),
|
|
16
|
+
/* @__PURE__ */ jsx("h2", { className: "mt-4 text-lg font-medium text-gray-900 dark:text-gray-100", children: title }),
|
|
17
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-gray-600 dark:text-gray-400", children: message })
|
|
18
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
19
|
+
/* @__PURE__ */ jsx(ExclamationCircleIcon, { className: `mx-auto h-12 w-12 ${iconColor}` }),
|
|
20
|
+
/* @__PURE__ */ jsx("h2", { className: "mt-4 text-lg font-medium text-gray-900 dark:text-gray-100", children: title }),
|
|
21
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-gray-600 dark:text-gray-400", children: message }),
|
|
22
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-6 space-y-3", children: [
|
|
23
|
+
onRetry && /* @__PURE__ */ jsx(
|
|
24
|
+
"button",
|
|
25
|
+
{
|
|
26
|
+
onClick: onRetry,
|
|
27
|
+
className: "w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-green-web hover:bg-green-web-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-web",
|
|
28
|
+
children: "Try Again"
|
|
29
|
+
}
|
|
30
|
+
),
|
|
31
|
+
/* @__PURE__ */ jsx(
|
|
32
|
+
Link,
|
|
33
|
+
{
|
|
34
|
+
to: backLinkPath || basePath,
|
|
35
|
+
className: `w-full flex justify-center py-2 px-4 border ${onRetry ? "border-gray-300 text-gray-700 bg-white hover:bg-gray-50" : "border-transparent text-white bg-green-web hover:bg-green-web-700"} rounded-md shadow-sm text-sm font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-web`,
|
|
36
|
+
children: backLinkText
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
] })
|
|
40
|
+
] }) }) }) }) });
|
|
41
|
+
}
|
|
42
|
+
export {
|
|
43
|
+
AdminDataStateMessage
|
|
44
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface FilterFieldProps {
|
|
2
|
+
readonly fieldName: string;
|
|
3
|
+
readonly model: any;
|
|
4
|
+
readonly databaseModels: any[];
|
|
5
|
+
readonly filters: Record<string, any>;
|
|
6
|
+
readonly onChange: (value: any) => void;
|
|
7
|
+
readonly getEnumValues: (enumType: string) => string[] | null;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Renders a single filter field based on its type
|
|
11
|
+
* Extracted from AdminDataListPage to reduce cognitive complexity
|
|
12
|
+
*/
|
|
13
|
+
export declare function FilterField({ fieldName, model, databaseModels, filters, onChange, getEnumValues, }: FilterFieldProps): import("react/jsx-runtime").JSX.Element | null;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { DateRangeFilter } from "./filters/DateRangeFilter.js";
|
|
3
|
+
import { EnumFilter } from "./filters/EnumFilter.js";
|
|
4
|
+
import { NumberRangeFilter } from "./filters/NumberRangeFilter.js";
|
|
5
|
+
import { formatFieldName } from "../utils/string-utils.js";
|
|
6
|
+
import { RelationFilterField } from "./filters/RelationFilterField.js";
|
|
7
|
+
function getFieldInfo(fieldName, model, databaseModels) {
|
|
8
|
+
const isRelatedEnum = fieldName.includes(".");
|
|
9
|
+
if (isRelatedEnum) {
|
|
10
|
+
const [relationName, enumFieldName] = fieldName.split(".");
|
|
11
|
+
const field2 = model.fields.find((f) => f.name === relationName);
|
|
12
|
+
if (!field2) return null;
|
|
13
|
+
const relatedModel = databaseModels.find((m) => m.name === field2.type);
|
|
14
|
+
if (!relatedModel) return null;
|
|
15
|
+
const relatedEnumField = relatedModel.fields.find((f) => f.name === enumFieldName);
|
|
16
|
+
if (!relatedEnumField) return null;
|
|
17
|
+
return { field: field2, relatedModel, relatedEnumField, isRelatedEnum: true };
|
|
18
|
+
}
|
|
19
|
+
const field = model.fields.find((f) => f.name === fieldName);
|
|
20
|
+
if (!field) return null;
|
|
21
|
+
return { field, relatedModel: null, relatedEnumField: null, isRelatedEnum: false };
|
|
22
|
+
}
|
|
23
|
+
function getRegularFieldValue(fieldName, filters) {
|
|
24
|
+
return filters[fieldName];
|
|
25
|
+
}
|
|
26
|
+
function getRelatedEnumFieldValue(fieldName, filters) {
|
|
27
|
+
var _a;
|
|
28
|
+
const [relationName, enumFieldName] = fieldName.split(".");
|
|
29
|
+
return (_a = filters[relationName]) == null ? void 0 : _a[enumFieldName];
|
|
30
|
+
}
|
|
31
|
+
function BooleanFilter({ fieldName, currentValue, onChange }) {
|
|
32
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
33
|
+
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700", children: formatFieldName(fieldName) }),
|
|
34
|
+
/* @__PURE__ */ jsxs(
|
|
35
|
+
"select",
|
|
36
|
+
{
|
|
37
|
+
value: currentValue === void 0 || currentValue === null ? "" : currentValue.toString(),
|
|
38
|
+
onChange: (e) => {
|
|
39
|
+
const value = e.target.value;
|
|
40
|
+
onChange(value === "" ? void 0 : value === "true");
|
|
41
|
+
},
|
|
42
|
+
className: "w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-web focus:border-green-web text-sm",
|
|
43
|
+
children: [
|
|
44
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "All" }),
|
|
45
|
+
/* @__PURE__ */ jsx("option", { value: "true", children: "Yes" }),
|
|
46
|
+
/* @__PURE__ */ jsx("option", { value: "false", children: "No" })
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
)
|
|
50
|
+
] }, fieldName);
|
|
51
|
+
}
|
|
52
|
+
function StringFilter({ fieldName, currentValue, onChange }) {
|
|
53
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
54
|
+
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700", children: formatFieldName(fieldName) }),
|
|
55
|
+
/* @__PURE__ */ jsx(
|
|
56
|
+
"input",
|
|
57
|
+
{
|
|
58
|
+
type: "text",
|
|
59
|
+
value: typeof currentValue === "string" ? currentValue : "",
|
|
60
|
+
onChange: (e) => onChange(e.target.value || void 0),
|
|
61
|
+
placeholder: `Filter by ${formatFieldName(fieldName).toLowerCase()}...`,
|
|
62
|
+
className: "w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-web focus:border-green-web text-sm"
|
|
63
|
+
}
|
|
64
|
+
)
|
|
65
|
+
] }, fieldName);
|
|
66
|
+
}
|
|
67
|
+
function FilterField({
|
|
68
|
+
fieldName,
|
|
69
|
+
model,
|
|
70
|
+
databaseModels,
|
|
71
|
+
filters,
|
|
72
|
+
onChange,
|
|
73
|
+
getEnumValues
|
|
74
|
+
}) {
|
|
75
|
+
const fieldInfo = getFieldInfo(fieldName, model, databaseModels);
|
|
76
|
+
if (!fieldInfo) return null;
|
|
77
|
+
const { field, relatedEnumField, isRelatedEnum } = fieldInfo;
|
|
78
|
+
const currentValue = isRelatedEnum ? getRelatedEnumFieldValue(fieldName, filters) : getRegularFieldValue(fieldName, filters);
|
|
79
|
+
if (isRelatedEnum && relatedEnumField) {
|
|
80
|
+
const enumValues = getEnumValues(relatedEnumField.type);
|
|
81
|
+
if (enumValues) {
|
|
82
|
+
return /* @__PURE__ */ jsx(
|
|
83
|
+
EnumFilter,
|
|
84
|
+
{
|
|
85
|
+
fieldName,
|
|
86
|
+
currentValue,
|
|
87
|
+
onChange,
|
|
88
|
+
enumValues
|
|
89
|
+
},
|
|
90
|
+
fieldName
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (field.relationName && !field.isList && !isRelatedEnum) {
|
|
95
|
+
return /* @__PURE__ */ jsx(
|
|
96
|
+
RelationFilterField,
|
|
97
|
+
{
|
|
98
|
+
fieldName,
|
|
99
|
+
relatedModelName: field.type,
|
|
100
|
+
currentValue,
|
|
101
|
+
onChange
|
|
102
|
+
},
|
|
103
|
+
fieldName
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
const fieldTypeLower = field.type.toLowerCase();
|
|
107
|
+
if (fieldTypeLower === "datetime" || fieldTypeLower === "date") {
|
|
108
|
+
return /* @__PURE__ */ jsx(
|
|
109
|
+
DateRangeFilter,
|
|
110
|
+
{
|
|
111
|
+
fieldName,
|
|
112
|
+
currentValue,
|
|
113
|
+
onChange
|
|
114
|
+
},
|
|
115
|
+
fieldName
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
if (["int", "bigint", "float", "decimal"].includes(fieldTypeLower)) {
|
|
119
|
+
return /* @__PURE__ */ jsx(
|
|
120
|
+
NumberRangeFilter,
|
|
121
|
+
{
|
|
122
|
+
fieldName,
|
|
123
|
+
fieldType: fieldTypeLower,
|
|
124
|
+
currentValue,
|
|
125
|
+
onChange
|
|
126
|
+
},
|
|
127
|
+
fieldName
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
if (field.kind === "enum") {
|
|
131
|
+
const enumValues = getEnumValues(field.type);
|
|
132
|
+
if (enumValues) {
|
|
133
|
+
return /* @__PURE__ */ jsx(
|
|
134
|
+
EnumFilter,
|
|
135
|
+
{
|
|
136
|
+
fieldName,
|
|
137
|
+
currentValue,
|
|
138
|
+
onChange,
|
|
139
|
+
enumValues
|
|
140
|
+
},
|
|
141
|
+
fieldName
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (fieldTypeLower === "boolean") {
|
|
146
|
+
return /* @__PURE__ */ jsx(BooleanFilter, { fieldName, currentValue, onChange });
|
|
147
|
+
}
|
|
148
|
+
return /* @__PURE__ */ jsx(StringFilter, { fieldName, currentValue, onChange });
|
|
149
|
+
}
|
|
150
|
+
export {
|
|
151
|
+
FilterField
|
|
152
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
interface EnumFilterProps {
|
|
2
|
+
fieldName: string;
|
|
3
|
+
currentValue: string | undefined | null;
|
|
4
|
+
onChange: (value: string | undefined) => void;
|
|
5
|
+
enumValues: string[];
|
|
6
|
+
}
|
|
7
|
+
export declare function EnumFilter({ fieldName, currentValue, onChange, enumValues }: Readonly<EnumFilterProps>): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { formatFieldName } from "../../utils/string-utils.js";
|
|
3
|
+
function EnumFilter({
|
|
4
|
+
fieldName,
|
|
5
|
+
currentValue,
|
|
6
|
+
onChange,
|
|
7
|
+
enumValues
|
|
8
|
+
}) {
|
|
9
|
+
const handleChange = (value) => {
|
|
10
|
+
if (value === "") {
|
|
11
|
+
onChange(void 0);
|
|
12
|
+
} else {
|
|
13
|
+
onChange(value);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
17
|
+
/* @__PURE__ */ jsx("label", { htmlFor: fieldName, className: "block text-sm font-medium text-gray-700", children: formatFieldName(fieldName) }),
|
|
18
|
+
/* @__PURE__ */ jsxs(
|
|
19
|
+
"select",
|
|
20
|
+
{
|
|
21
|
+
id: fieldName,
|
|
22
|
+
value: currentValue ?? "",
|
|
23
|
+
onChange: (e) => handleChange(e.target.value),
|
|
24
|
+
className: "w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-1 focus:ring-green-web focus:border-green-web",
|
|
25
|
+
children: [
|
|
26
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "All values" }),
|
|
27
|
+
enumValues.map((value) => /* @__PURE__ */ jsx("option", { value, children: formatFieldName(value) }, value))
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
),
|
|
31
|
+
currentValue && /* @__PURE__ */ jsxs("div", { className: "text-xs text-gray-500", children: [
|
|
32
|
+
"Filtered by: ",
|
|
33
|
+
formatFieldName(currentValue)
|
|
34
|
+
] })
|
|
35
|
+
] });
|
|
36
|
+
}
|
|
37
|
+
export {
|
|
38
|
+
EnumFilter
|
|
39
|
+
};
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration for how to display and search relation fields
|
|
4
|
+
*/
|
|
5
|
+
export interface DisplayFieldConfig {
|
|
6
|
+
[modelName: string]: {
|
|
7
|
+
/** Fields to display in dropdowns. If multiple, they'll be joined with spaces */
|
|
8
|
+
display?: string[];
|
|
9
|
+
/** Fields to search when typing in the dropdown */
|
|
10
|
+
search?: string[];
|
|
11
|
+
};
|
|
12
|
+
}
|
|
2
13
|
export interface AdminDataContextValue {
|
|
3
14
|
/** The SDK namespace for dynamic GraphQL document lookups */
|
|
4
15
|
sdk: any;
|
|
@@ -8,6 +19,8 @@ export interface AdminDataContextValue {
|
|
|
8
19
|
basePath?: string;
|
|
9
20
|
/** Form theme configuration for @nestledjs/forms */
|
|
10
21
|
formTheme: any;
|
|
22
|
+
/** Optional configuration for display and search fields per model */
|
|
23
|
+
displayFieldConfig?: DisplayFieldConfig;
|
|
11
24
|
}
|
|
12
25
|
export interface AdminDataProviderProps {
|
|
13
26
|
children: ReactNode;
|
|
@@ -19,6 +32,8 @@ export interface AdminDataProviderProps {
|
|
|
19
32
|
basePath?: string;
|
|
20
33
|
/** Form theme configuration for @nestledjs/forms */
|
|
21
34
|
formTheme: any;
|
|
35
|
+
/** Optional configuration for display and search fields per model */
|
|
36
|
+
displayFieldConfig?: DisplayFieldConfig;
|
|
22
37
|
}
|
|
23
38
|
/**
|
|
24
39
|
* Provider component that supplies SDK and database models to admin data components
|
|
@@ -39,7 +54,7 @@ export interface AdminDataProviderProps {
|
|
|
39
54
|
* </AdminDataProvider>
|
|
40
55
|
* ```
|
|
41
56
|
*/
|
|
42
|
-
export declare function AdminDataProvider({ children, sdk, databaseModels, basePath, formTheme }: Readonly<AdminDataProviderProps>): import("react/jsx-runtime").JSX.Element;
|
|
57
|
+
export declare function AdminDataProvider({ children, sdk, databaseModels, basePath, formTheme, displayFieldConfig }: Readonly<AdminDataProviderProps>): import("react/jsx-runtime").JSX.Element;
|
|
43
58
|
/**
|
|
44
59
|
* Hook to access the admin data context
|
|
45
60
|
* @throws Error if used outside of AdminDataProvider
|
|
@@ -6,11 +6,12 @@ function AdminDataProvider({
|
|
|
6
6
|
sdk,
|
|
7
7
|
databaseModels,
|
|
8
8
|
basePath = "/admin/data",
|
|
9
|
-
formTheme
|
|
9
|
+
formTheme,
|
|
10
|
+
displayFieldConfig
|
|
10
11
|
}) {
|
|
11
12
|
const value = useMemo(
|
|
12
|
-
() => ({ sdk, databaseModels, basePath, formTheme }),
|
|
13
|
-
[sdk, databaseModels, basePath, formTheme]
|
|
13
|
+
() => ({ sdk, databaseModels, basePath, formTheme, displayFieldConfig }),
|
|
14
|
+
[sdk, databaseModels, basePath, formTheme, displayFieldConfig]
|
|
14
15
|
);
|
|
15
16
|
return /* @__PURE__ */ jsx(AdminDataContext.Provider, { value, children });
|
|
16
17
|
}
|
|
@@ -20,7 +20,7 @@ const toKebabCase = (str) => {
|
|
|
20
20
|
};
|
|
21
21
|
function AdminDataCreatePage() {
|
|
22
22
|
const { dataType } = useParams();
|
|
23
|
-
const { databaseModels, basePath = "/admin/data", formTheme } = useAdminDataContext();
|
|
23
|
+
const { databaseModels, basePath = "/admin/data", formTheme, displayFieldConfig } = useAdminDataContext();
|
|
24
24
|
const findModelByName = (name) => {
|
|
25
25
|
return databaseModels.find((model2) => model2.name === name);
|
|
26
26
|
};
|
|
@@ -69,9 +69,9 @@ function AdminDataCreatePage() {
|
|
|
69
69
|
) })
|
|
70
70
|
] }) }) }) });
|
|
71
71
|
}
|
|
72
|
-
return /* @__PURE__ */ jsx(AdminDataCreatePageContent, { model, basePath, formTheme });
|
|
72
|
+
return /* @__PURE__ */ jsx(AdminDataCreatePageContent, { model, basePath, formTheme, displayFieldConfig });
|
|
73
73
|
}
|
|
74
|
-
function AdminDataCreatePageContent({ model, basePath, formTheme }) {
|
|
74
|
+
function AdminDataCreatePageContent({ model, basePath, formTheme, displayFieldConfig }) {
|
|
75
75
|
const navigate = useNavigate();
|
|
76
76
|
const { sdk, databaseModels } = useAdminDataContext();
|
|
77
77
|
const [submissionState, setSubmissionState] = useState({ status: "idle" });
|
|
@@ -116,7 +116,12 @@ function AdminDataCreatePageContent({ model, basePath, formTheme }) {
|
|
|
116
116
|
) })
|
|
117
117
|
] }) }) }) });
|
|
118
118
|
}
|
|
119
|
-
const formFields = buildFormFields(sdk, model, "create",
|
|
119
|
+
const formFields = buildFormFields(sdk, model, "create", {
|
|
120
|
+
isSubmitting: submissionState.status === "loading",
|
|
121
|
+
basePath,
|
|
122
|
+
databaseModels,
|
|
123
|
+
displayFieldConfig
|
|
124
|
+
});
|
|
120
125
|
const handleSubmit = async (formData) => {
|
|
121
126
|
try {
|
|
122
127
|
setSubmissionState({ status: "loading" });
|