@firecms/collection_editor 3.0.1 → 3.1.0-canary.02232f4
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/ConfigControllerProvider.d.ts +6 -0
- package/dist/api/generateCollectionApi.d.ts +71 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.es.js +15260 -8173
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +15257 -8170
- package/dist/index.umd.js.map +1 -1
- package/dist/locales/de.d.ts +120 -0
- package/dist/locales/en.d.ts +120 -0
- package/dist/locales/es.d.ts +120 -0
- package/dist/locales/fr.d.ts +120 -0
- package/dist/locales/hi.d.ts +120 -0
- package/dist/locales/it.d.ts +120 -0
- package/dist/locales/pt.d.ts +120 -0
- package/dist/types/collection_editor_controller.d.ts +14 -0
- package/dist/types/collection_inference.d.ts +8 -2
- package/dist/types/config_controller.d.ts +23 -2
- package/dist/ui/AddKanbanColumnAction.d.ts +11 -0
- package/dist/ui/KanbanSetupAction.d.ts +10 -0
- package/dist/ui/collection_editor/AICollectionGeneratorPopover.d.ts +37 -0
- package/dist/ui/collection_editor/AIModifiedPathsContext.d.ts +20 -0
- package/dist/ui/collection_editor/CollectionDetailsForm.d.ts +2 -3
- package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +24 -0
- package/dist/ui/collection_editor/CollectionEditorWelcomeView.d.ts +4 -1
- package/dist/ui/collection_editor/CollectionJsonImportDialog.d.ts +7 -0
- package/dist/ui/collection_editor/CollectionYupValidation.d.ts +9 -13
- package/dist/ui/collection_editor/DisplaySettingsForm.d.ts +3 -0
- package/dist/ui/collection_editor/EntityActionsEditTab.d.ts +2 -1
- package/dist/ui/collection_editor/ExtendSettingsForm.d.ts +14 -0
- package/dist/ui/collection_editor/GeneralSettingsForm.d.ts +7 -0
- package/dist/ui/collection_editor/KanbanConfigSection.d.ts +4 -0
- package/dist/ui/collection_editor/PropertyEditView.d.ts +6 -1
- package/dist/ui/collection_editor/PropertyTree.d.ts +2 -1
- package/dist/ui/collection_editor/SubcollectionsEditTab.d.ts +2 -1
- package/dist/ui/collection_editor/ViewModeSwitch.d.ts +6 -0
- package/dist/ui/collection_editor/properties/EnumPropertyField.d.ts +2 -1
- package/dist/ui/collection_editor/properties/conditions/ConditionsEditor.d.ts +10 -0
- package/dist/ui/collection_editor/properties/conditions/ConditionsPanel.d.ts +2 -0
- package/dist/ui/collection_editor/properties/conditions/EnumConditionsEditor.d.ts +6 -0
- package/dist/ui/collection_editor/properties/conditions/index.d.ts +6 -0
- package/dist/ui/collection_editor/properties/conditions/property_paths.d.ts +19 -0
- package/dist/useCollectionEditorPlugin.d.ts +7 -1
- package/dist/utils/validateCollectionJson.d.ts +22 -0
- package/package.json +15 -15
- package/src/ConfigControllerProvider.tsx +82 -47
- package/src/api/generateCollectionApi.ts +119 -0
- package/src/api/index.ts +1 -0
- package/src/index.ts +28 -1
- package/src/locales/de.ts +125 -0
- package/src/locales/en.ts +145 -0
- package/src/locales/es.ts +125 -0
- package/src/locales/fr.ts +125 -0
- package/src/locales/hi.ts +125 -0
- package/src/locales/it.ts +125 -0
- package/src/locales/pt.ts +125 -0
- package/src/types/collection_editor_controller.tsx +16 -3
- package/src/types/collection_inference.ts +15 -2
- package/src/types/config_controller.tsx +27 -2
- package/src/ui/AddKanbanColumnAction.tsx +203 -0
- package/src/ui/EditorCollectionAction.tsx +3 -3
- package/src/ui/EditorCollectionActionStart.tsx +1 -2
- package/src/ui/EditorEntityAction.tsx +3 -2
- package/src/ui/HomePageEditorCollectionAction.tsx +41 -13
- package/src/ui/KanbanSetupAction.tsx +38 -0
- package/src/ui/MissingReferenceWidget.tsx +1 -1
- package/src/ui/NewCollectionButton.tsx +4 -2
- package/src/ui/NewCollectionCard.tsx +7 -4
- package/src/ui/PropertyAddColumnComponent.tsx +4 -3
- package/src/ui/collection_editor/AICollectionGeneratorPopover.tsx +243 -0
- package/src/ui/collection_editor/AIModifiedPathsContext.tsx +88 -0
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +222 -267
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +270 -198
- package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +138 -71
- package/src/ui/collection_editor/CollectionJsonImportDialog.tsx +171 -0
- package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +202 -101
- package/src/ui/collection_editor/DisplaySettingsForm.tsx +335 -0
- package/src/ui/collection_editor/EntityActionsEditTab.tsx +106 -97
- package/src/ui/collection_editor/EntityActionsSelectDialog.tsx +8 -10
- package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +5 -7
- package/src/ui/collection_editor/EnumForm.tsx +153 -102
- package/src/ui/collection_editor/ExtendSettingsForm.tsx +94 -0
- package/src/ui/collection_editor/GeneralSettingsForm.tsx +335 -0
- package/src/ui/collection_editor/GetCodeDialog.tsx +63 -41
- package/src/ui/collection_editor/KanbanConfigSection.tsx +209 -0
- package/src/ui/collection_editor/LayoutModeSwitch.tsx +27 -43
- package/src/ui/collection_editor/PropertyEditView.tsx +272 -199
- package/src/ui/collection_editor/PropertyFieldPreview.tsx +1 -1
- package/src/ui/collection_editor/PropertyTree.tsx +130 -58
- package/src/ui/collection_editor/SubcollectionsEditTab.tsx +169 -163
- package/src/ui/collection_editor/UnsavedChangesDialog.tsx +0 -2
- package/src/ui/collection_editor/ViewModeSwitch.tsx +43 -0
- package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +6 -3
- package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +5 -2
- package/src/ui/collection_editor/properties/BlockPropertyField.tsx +0 -2
- package/src/ui/collection_editor/properties/BooleanPropertyField.tsx +4 -1
- package/src/ui/collection_editor/properties/CommonPropertyFields.tsx +6 -4
- package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +126 -42
- package/src/ui/collection_editor/properties/EnumPropertyField.tsx +32 -24
- package/src/ui/collection_editor/properties/MapPropertyField.tsx +8 -9
- package/src/ui/collection_editor/properties/MarkdownPropertyField.tsx +128 -53
- package/src/ui/collection_editor/properties/NumberPropertyField.tsx +3 -1
- package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +5 -4
- package/src/ui/collection_editor/properties/StoragePropertyField.tsx +47 -52
- package/src/ui/collection_editor/properties/StringPropertyField.tsx +3 -1
- package/src/ui/collection_editor/properties/UrlPropertyField.tsx +12 -10
- package/src/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +23 -4
- package/src/ui/collection_editor/properties/conditions/ConditionsEditor.tsx +866 -0
- package/src/ui/collection_editor/properties/conditions/ConditionsPanel.tsx +28 -0
- package/src/ui/collection_editor/properties/conditions/EnumConditionsEditor.tsx +599 -0
- package/src/ui/collection_editor/properties/conditions/index.ts +6 -0
- package/src/ui/collection_editor/properties/conditions/property_paths.ts +92 -0
- package/src/ui/collection_editor/properties/validation/ArrayPropertyValidation.tsx +5 -2
- package/src/ui/collection_editor/properties/validation/GeneralPropertyValidation.tsx +7 -5
- package/src/ui/collection_editor/properties/validation/NumberPropertyValidation.tsx +10 -7
- package/src/ui/collection_editor/properties/validation/StringPropertyValidation.tsx +11 -9
- package/src/ui/collection_editor/properties/validation/ValidationPanel.tsx +5 -2
- package/src/useCollectionEditorPlugin.tsx +53 -22
- package/src/utils/validateCollectionJson.ts +380 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
import { EntityCollection, FieldCaption, IconForView, SearchIconsView, singular, toSnakeCase, } from "@firecms/core";
|
|
1
|
+
import React, { useMemo, useState } from "react";
|
|
2
|
+
import { EntityCollection, FieldCaption, getFieldConfig, IconForView, Property, PropertyConfigBadge, resolveCollection, SearchIconsView, singular, toSnakeCase, unslugify, useAuthController, useCustomizationController, useTranslation } from "@firecms/core";
|
|
3
3
|
import {
|
|
4
4
|
BooleanSwitchWithLabel,
|
|
5
5
|
Chip,
|
|
@@ -8,11 +8,10 @@ import {
|
|
|
8
8
|
Container,
|
|
9
9
|
DebouncedTextField,
|
|
10
10
|
Dialog,
|
|
11
|
-
|
|
11
|
+
HistoryIcon,
|
|
12
12
|
IconButton,
|
|
13
13
|
Select,
|
|
14
14
|
SelectItem,
|
|
15
|
-
SettingsIcon,
|
|
16
15
|
TextField,
|
|
17
16
|
Tooltip,
|
|
18
17
|
Typography,
|
|
@@ -22,16 +21,19 @@ import {
|
|
|
22
21
|
import { Field, getIn, useFormex } from "@firecms/formex";
|
|
23
22
|
import { useCollectionEditorController } from "../../useCollectionEditorController";
|
|
24
23
|
import { LayoutModeSwitch } from "./LayoutModeSwitch";
|
|
24
|
+
import { ViewModeSwitch } from "./ViewModeSwitch";
|
|
25
|
+
import { KanbanConfigSection } from "./KanbanConfigSection";
|
|
26
|
+
import { PropertyFormDialog } from "./PropertyEditView";
|
|
25
27
|
|
|
26
28
|
export function CollectionDetailsForm({
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
isNewCollection,
|
|
30
|
+
reservedGroups,
|
|
31
|
+
existingPaths,
|
|
32
|
+
existingIds,
|
|
33
|
+
groups,
|
|
34
|
+
parentCollection,
|
|
35
|
+
expandKanban
|
|
36
|
+
}: {
|
|
35
37
|
isNewCollection: boolean,
|
|
36
38
|
reservedGroups?: string[];
|
|
37
39
|
existingPaths?: string[];
|
|
@@ -39,7 +41,7 @@ export function CollectionDetailsForm({
|
|
|
39
41
|
groups: string[] | null;
|
|
40
42
|
parentCollection?: EntityCollection;
|
|
41
43
|
parentCollectionIds?: string[];
|
|
42
|
-
|
|
44
|
+
expandKanban?: boolean;
|
|
43
45
|
}) {
|
|
44
46
|
|
|
45
47
|
const groupRef = React.useRef<HTMLInputElement>(null);
|
|
@@ -57,7 +59,37 @@ export function CollectionDetailsForm({
|
|
|
57
59
|
const collectionEditor = useCollectionEditorController();
|
|
58
60
|
|
|
59
61
|
const [iconDialogOpen, setIconDialogOpen] = useState(false);
|
|
60
|
-
const [
|
|
62
|
+
const [orderPropertyDialogOpen, setOrderPropertyDialogOpen] = useState(false);
|
|
63
|
+
|
|
64
|
+
const { t } = useTranslation();
|
|
65
|
+
|
|
66
|
+
const authController = useAuthController();
|
|
67
|
+
const customizationController = useCustomizationController();
|
|
68
|
+
|
|
69
|
+
// Resolve collection to get properties for order property select
|
|
70
|
+
const resolvedCollection = useMemo(() => resolveCollection({
|
|
71
|
+
collection: values,
|
|
72
|
+
path: values.path,
|
|
73
|
+
propertyConfigs: customizationController.propertyConfigs,
|
|
74
|
+
authController
|
|
75
|
+
}), [values, customizationController.propertyConfigs, authController]);
|
|
76
|
+
|
|
77
|
+
// Get number properties (for orderProperty)
|
|
78
|
+
const numberProperties = useMemo(() => {
|
|
79
|
+
const result: { key: string; label: string; property: Property; }[] = [];
|
|
80
|
+
if (!resolvedCollection.properties) return result;
|
|
81
|
+
|
|
82
|
+
Object.entries(resolvedCollection.properties).forEach(([key, prop]) => {
|
|
83
|
+
if (prop && 'dataType' in prop && prop.dataType === 'number') {
|
|
84
|
+
result.push({
|
|
85
|
+
key,
|
|
86
|
+
label: (prop as Property).name || key,
|
|
87
|
+
property: prop as Property
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
return result;
|
|
92
|
+
}, [resolvedCollection.properties]);
|
|
61
93
|
|
|
62
94
|
const updateDatabaseId = (databaseId: string) => {
|
|
63
95
|
setFieldValue("databaseId", databaseId ?? undefined);
|
|
@@ -83,13 +115,7 @@ export function CollectionDetailsForm({
|
|
|
83
115
|
|
|
84
116
|
};
|
|
85
117
|
|
|
86
|
-
|
|
87
|
-
if (errors.id) {
|
|
88
|
-
setAdvancedPanelExpanded(true);
|
|
89
|
-
}
|
|
90
|
-
}, [errors.id]);
|
|
91
|
-
|
|
92
|
-
const collectionIcon = <IconForView collectionOrView={values}/>;
|
|
118
|
+
const collectionIcon = <IconForView collectionOrView={values} />;
|
|
93
119
|
|
|
94
120
|
const groupOptions = groups?.filter((group) => !reservedGroups?.includes(group));
|
|
95
121
|
|
|
@@ -103,17 +129,6 @@ export function CollectionDetailsForm({
|
|
|
103
129
|
|
|
104
130
|
const isSubcollection = !!parentCollection;
|
|
105
131
|
|
|
106
|
-
let customIdValue: "true" | "false" | "optional" | "code_defined" | undefined;
|
|
107
|
-
if (typeof values.customId === "object") {
|
|
108
|
-
customIdValue = "code_defined";
|
|
109
|
-
} else if (values.customId === true) {
|
|
110
|
-
customIdValue = "true";
|
|
111
|
-
} else if (values.customId === false) {
|
|
112
|
-
customIdValue = "false";
|
|
113
|
-
} else if (values.customId === "optional") {
|
|
114
|
-
customIdValue = "optional";
|
|
115
|
-
}
|
|
116
|
-
|
|
117
132
|
const showErrors = submitCount > 0;
|
|
118
133
|
|
|
119
134
|
return (
|
|
@@ -124,13 +139,13 @@ export function CollectionDetailsForm({
|
|
|
124
139
|
<div
|
|
125
140
|
className="flex flex-row gap-2 py-2 pt-3 items-center">
|
|
126
141
|
<Typography variant={!isNewCollection ? "h5" : "h4"} className={"flex-grow"}>
|
|
127
|
-
{isNewCollection ? "
|
|
142
|
+
{isNewCollection ? t("new_collection") : t("collection_with_name", { name: values?.name || "" })}
|
|
128
143
|
</Typography>
|
|
129
144
|
<DefaultDatabaseField databaseId={values.databaseId}
|
|
130
|
-
|
|
145
|
+
onDatabaseIdUpdate={updateDatabaseId} />
|
|
131
146
|
|
|
132
|
-
<Tooltip title={"
|
|
133
|
-
|
|
147
|
+
<Tooltip title={t("change_icon")}
|
|
148
|
+
asChild={true}>
|
|
134
149
|
<IconButton
|
|
135
150
|
shape={"square"}
|
|
136
151
|
onClick={() => setIconDialogOpen(true)}>
|
|
@@ -141,7 +156,7 @@ export function CollectionDetailsForm({
|
|
|
141
156
|
|
|
142
157
|
{parentCollection && <Chip colorScheme={"tealDarker"}>
|
|
143
158
|
<Typography variant={"caption"}>
|
|
144
|
-
|
|
159
|
+
{t("is_subcollection_of")} <b>{parentCollection.name}</b>
|
|
145
160
|
</Typography>
|
|
146
161
|
</Chip>}
|
|
147
162
|
|
|
@@ -152,26 +167,26 @@ export function CollectionDetailsForm({
|
|
|
152
167
|
<TextField
|
|
153
168
|
value={values.name ?? ""}
|
|
154
169
|
onChange={(e: any) => updateName(e.target.value)}
|
|
155
|
-
label={"
|
|
170
|
+
label={t("name")}
|
|
156
171
|
autoFocus={true}
|
|
157
172
|
required
|
|
158
|
-
error={showErrors && Boolean(errors.name)}/>
|
|
173
|
+
error={showErrors && Boolean(errors.name)} />
|
|
159
174
|
<FieldCaption error={touched.name && Boolean(errors.name)}>
|
|
160
|
-
{touched.name && Boolean(errors.name) ? errors.name : "
|
|
175
|
+
{touched.name && Boolean(errors.name) ? errors.name : t("collection_name_description")}
|
|
161
176
|
</FieldCaption>
|
|
162
177
|
</div>
|
|
163
178
|
|
|
164
179
|
<div className={cls("col-span-12 ")}>
|
|
165
180
|
<Field name={"path"}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
181
|
+
as={DebouncedTextField}
|
|
182
|
+
label={t("path")}
|
|
183
|
+
required
|
|
184
|
+
error={showErrors && Boolean(errors.path)} />
|
|
170
185
|
|
|
171
186
|
<FieldCaption error={touched.path && Boolean(errors.path)}>
|
|
172
187
|
{touched.path && Boolean(errors.path)
|
|
173
188
|
? errors.path
|
|
174
|
-
: isSubcollection ? "
|
|
189
|
+
: isSubcollection ? t("relative_path_to_parent") : t("path_in_database")}
|
|
175
190
|
</FieldCaption>
|
|
176
191
|
|
|
177
192
|
</div>
|
|
@@ -214,236 +229,174 @@ export function CollectionDetailsForm({
|
|
|
214
229
|
<LayoutModeSwitch
|
|
215
230
|
className={"col-span-12"}
|
|
216
231
|
value={values.openEntityMode ?? "side_panel"}
|
|
217
|
-
onChange={(value) => setFieldValue("openEntityMode", value)}/>
|
|
232
|
+
onChange={(value) => setFieldValue("openEntityMode", value)} />
|
|
218
233
|
|
|
219
|
-
<
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
allowIndeterminate={true}
|
|
224
|
-
label={values.history === null || values.history === undefined ? "Document history revisions enabled if enabled globally" : (
|
|
225
|
-
values.history ? "Document history revisions ENABLED" : "Document history revisions NOT enabled"
|
|
226
|
-
)}
|
|
227
|
-
onValueChange={(v) => setFieldValue("history", v)}
|
|
228
|
-
value={values.history === undefined ? null : values.history}
|
|
229
|
-
/>
|
|
230
|
-
<FieldCaption>
|
|
231
|
-
When enabled, each document in this collection will have a history of changes.
|
|
232
|
-
This is useful for auditing purposes. The data is stored in a subcollection of the document
|
|
233
|
-
in your database, called <b>__history</b>.
|
|
234
|
-
</FieldCaption>
|
|
235
|
-
</div>
|
|
234
|
+
<ViewModeSwitch
|
|
235
|
+
className={"col-span-12"}
|
|
236
|
+
value={values.defaultViewMode ?? "table"}
|
|
237
|
+
onChange={(value) => setFieldValue("defaultViewMode", value)} />
|
|
236
238
|
|
|
239
|
+
<KanbanConfigSection className={"col-span-12"} forceExpanded={expandKanban} />
|
|
237
240
|
|
|
238
|
-
<div className={"col-span-12 mt-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
<div className="flex flex-row text-surface-500">
|
|
244
|
-
<SettingsIcon/>
|
|
245
|
-
<Typography variant={"subtitle2"}
|
|
246
|
-
className="ml-2">
|
|
247
|
-
Advanced
|
|
248
|
-
</Typography>
|
|
249
|
-
</div>}
|
|
250
|
-
initiallyExpanded={false}>
|
|
251
|
-
<div className={"grid grid-cols-12 gap-4 p-4"}>
|
|
252
|
-
|
|
253
|
-
<div className={"col-span-12"}>
|
|
254
|
-
<Field name={"id"}
|
|
255
|
-
as={DebouncedTextField}
|
|
256
|
-
disabled={!isNewCollection}
|
|
257
|
-
label={"Collection id"}
|
|
258
|
-
error={showErrors && Boolean(errors.id)}/>
|
|
259
|
-
<FieldCaption error={touched.id && Boolean(errors.id)}>
|
|
260
|
-
{touched.id && Boolean(errors.id) ? errors.id : "This id identifies this collection. Typically the same as the path."}
|
|
261
|
-
</FieldCaption>
|
|
262
|
-
</div>
|
|
263
|
-
|
|
264
|
-
<div className={"col-span-12"}>
|
|
265
|
-
<TextField
|
|
266
|
-
error={showErrors && Boolean(errors.singularName)}
|
|
267
|
-
name={"singularName"}
|
|
268
|
-
aria-describedby={"singularName-helper"}
|
|
269
|
-
onChange={(e) => {
|
|
270
|
-
setFieldTouched("singularName", true);
|
|
271
|
-
return handleChange(e);
|
|
272
|
-
}}
|
|
273
|
-
value={values.singularName ?? ""}
|
|
274
|
-
label={"Singular name"}/>
|
|
275
|
-
<FieldCaption error={showErrors && Boolean(errors.singularName)}>
|
|
276
|
-
{showErrors && Boolean(errors.singularName) ? errors.singularName : "Optionally define a singular name for your entities"}
|
|
277
|
-
</FieldCaption>
|
|
278
|
-
</div>
|
|
279
|
-
<div className={"col-span-12"}>
|
|
280
|
-
<TextField
|
|
281
|
-
error={showErrors && Boolean(errors.sideDialogWidth)}
|
|
282
|
-
name={"sideDialogWidth"}
|
|
283
|
-
type={"number"}
|
|
284
|
-
aria-describedby={"sideDialogWidth-helper"}
|
|
285
|
-
onChange={(e) => {
|
|
286
|
-
setFieldTouched("sideDialogWidth", true);
|
|
287
|
-
const value = e.target.value;
|
|
288
|
-
if (!value) {
|
|
289
|
-
setFieldValue("sideDialogWidth", null);
|
|
290
|
-
} else if (!isNaN(Number(value))) {
|
|
291
|
-
setFieldValue("sideDialogWidth", Number(value));
|
|
292
|
-
}
|
|
293
|
-
}}
|
|
294
|
-
endAdornment={<IconButton
|
|
295
|
-
size={"small"}
|
|
296
|
-
onClick={() => {
|
|
297
|
-
setFieldValue("sideDialogWidth", null);
|
|
298
|
-
}}
|
|
299
|
-
disabled={!values.sideDialogWidth}>
|
|
300
|
-
<CloseIcon size={"small"}/>
|
|
301
|
-
</IconButton>}
|
|
302
|
-
value={values.sideDialogWidth ?? ""}
|
|
303
|
-
label={"Side dialog width"}/>
|
|
304
|
-
<FieldCaption error={showErrors && Boolean(errors.singularName)}>
|
|
305
|
-
{showErrors && Boolean(errors.singularName) ? errors.singularName : "Optionally define the width (in pixels) of entities side dialog. Default is 768px"}
|
|
306
|
-
</FieldCaption>
|
|
307
|
-
</div>
|
|
308
|
-
<div className={"col-span-12"}>
|
|
309
|
-
<TextField
|
|
310
|
-
error={showErrors && Boolean(errors.description)}
|
|
311
|
-
name="description"
|
|
312
|
-
value={values.description ?? ""}
|
|
313
|
-
onChange={handleChange}
|
|
314
|
-
multiline
|
|
315
|
-
minRows={2}
|
|
316
|
-
aria-describedby="description-helper-text"
|
|
317
|
-
label="Description"
|
|
318
|
-
/>
|
|
319
|
-
<FieldCaption error={showErrors && Boolean(errors.description)}>
|
|
320
|
-
{showErrors && Boolean(errors.description) ? errors.description : "Description of the collection, you can use markdown"}
|
|
321
|
-
</FieldCaption>
|
|
322
|
-
</div>
|
|
241
|
+
<div className={"col-span-12 mt-4"}>
|
|
242
|
+
{(() => {
|
|
243
|
+
// Check if orderProperty references a non-existent property
|
|
244
|
+
const orderPropertyMissing = Boolean(values.orderProperty) &&
|
|
245
|
+
!numberProperties.some(p => p.key === values.orderProperty);
|
|
323
246
|
|
|
324
|
-
|
|
247
|
+
return (
|
|
248
|
+
<>
|
|
325
249
|
<Select
|
|
326
|
-
|
|
250
|
+
key={`order-select-${numberProperties.length}`}
|
|
251
|
+
name="orderProperty"
|
|
252
|
+
label={t("order_property")}
|
|
327
253
|
size={"large"}
|
|
328
254
|
fullWidth={true}
|
|
329
|
-
label="Default row size"
|
|
330
255
|
position={"item-aligned"}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
>
|
|
335
|
-
{["xs", "s", "m", "l", "xl"].map((value) => (
|
|
336
|
-
<SelectItem
|
|
337
|
-
key={`size-select-${value}`}
|
|
338
|
-
value={value}>
|
|
339
|
-
{value.toUpperCase()}
|
|
340
|
-
</SelectItem>
|
|
341
|
-
))}
|
|
342
|
-
</Select>
|
|
343
|
-
</div>
|
|
344
|
-
|
|
345
|
-
<div className={"col-span-12"}>
|
|
346
|
-
<BooleanSwitchWithLabel
|
|
347
|
-
position={"start"}
|
|
348
|
-
size={"large"}
|
|
349
|
-
label={values.includeJsonView === undefined || values.includeJsonView ? "Include JSON view" : "Do not include JSON view"}
|
|
350
|
-
onValueChange={(v) => setFieldValue("includeJsonView", v)}
|
|
351
|
-
value={values.includeJsonView === undefined ? true : values.includeJsonView}
|
|
352
|
-
/>
|
|
353
|
-
<FieldCaption>
|
|
354
|
-
Include the JSON representation of the document.
|
|
355
|
-
</FieldCaption>
|
|
356
|
-
</div>
|
|
357
|
-
|
|
358
|
-
<div className={"col-span-12"}>
|
|
359
|
-
<BooleanSwitchWithLabel
|
|
360
|
-
position={"start"}
|
|
361
|
-
size={"large"}
|
|
362
|
-
label={values.inlineEditing === undefined || values.inlineEditing ? "Data can be edited directly in the table view" : "Data can be edited only in the form view"}
|
|
363
|
-
onValueChange={(v) => setFieldValue("inlineEditing", v)}
|
|
364
|
-
value={values.inlineEditing === undefined ? true : values.inlineEditing}
|
|
365
|
-
/>
|
|
366
|
-
<FieldCaption>
|
|
367
|
-
Allow editing data directly in the table view, without opening the form view.
|
|
368
|
-
</FieldCaption>
|
|
369
|
-
</div>
|
|
370
|
-
|
|
371
|
-
<div className={"col-span-12"}>
|
|
372
|
-
<Select
|
|
373
|
-
name="customId"
|
|
374
|
-
label="Document IDs generation"
|
|
375
|
-
position={"item-aligned"}
|
|
376
|
-
size={"large"}
|
|
377
|
-
fullWidth={true}
|
|
378
|
-
disabled={customIdValue === "code_defined"}
|
|
256
|
+
disabled={numberProperties.length === 0}
|
|
257
|
+
error={orderPropertyMissing}
|
|
258
|
+
value={values.orderProperty ?? ""}
|
|
379
259
|
onValueChange={(v) => {
|
|
380
|
-
|
|
381
|
-
throw new Error("This should not happen");
|
|
382
|
-
setFieldValue("customId", v);
|
|
260
|
+
setFieldValue("orderProperty", v || undefined);
|
|
383
261
|
}}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
262
|
+
renderValue={(value) => {
|
|
263
|
+
if (orderPropertyMissing) {
|
|
264
|
+
return <span className="text-red-500">{value} ({t("not_found_suffix")})</span>;
|
|
265
|
+
}
|
|
266
|
+
const prop = numberProperties.find(p => p.key === value);
|
|
267
|
+
if (!prop) return t("select_a_property");
|
|
268
|
+
const fieldConfig = getFieldConfig(prop.property, customizationController.propertyConfigs);
|
|
269
|
+
return (
|
|
270
|
+
<div className="flex items-center gap-2">
|
|
271
|
+
<PropertyConfigBadge propertyConfig={fieldConfig} />
|
|
272
|
+
<span>{prop.label}</span>
|
|
273
|
+
</div>
|
|
274
|
+
);
|
|
394
275
|
}}
|
|
276
|
+
endAdornment={values.orderProperty ? (
|
|
277
|
+
<IconButton
|
|
278
|
+
size="small"
|
|
279
|
+
onClick={(e) => {
|
|
280
|
+
e.stopPropagation();
|
|
281
|
+
setFieldValue("orderProperty", undefined);
|
|
282
|
+
}}
|
|
283
|
+
>
|
|
284
|
+
<CloseIcon size="small" />
|
|
285
|
+
</IconButton>
|
|
286
|
+
) : undefined}
|
|
395
287
|
>
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
288
|
+
{numberProperties.map((prop) => {
|
|
289
|
+
const fieldConfig = getFieldConfig(prop.property, customizationController.propertyConfigs);
|
|
290
|
+
return (
|
|
291
|
+
<SelectItem key={prop.key} value={prop.key}>
|
|
292
|
+
<div className="flex items-center gap-3">
|
|
293
|
+
<PropertyConfigBadge propertyConfig={fieldConfig} />
|
|
294
|
+
<div>
|
|
295
|
+
<div>{prop.label}</div>
|
|
296
|
+
<Typography variant="caption" color="secondary">
|
|
297
|
+
{fieldConfig?.name || t("number")}
|
|
298
|
+
</Typography>
|
|
299
|
+
</div>
|
|
300
|
+
</div>
|
|
301
|
+
</SelectItem>
|
|
302
|
+
);
|
|
303
|
+
})}
|
|
405
304
|
</Select>
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
value={values.collectionGroup ?? false}
|
|
414
|
-
/>
|
|
415
|
-
<FieldCaption>
|
|
416
|
-
A collection group consists of all collections with the same path. This allows
|
|
417
|
-
you
|
|
418
|
-
to query over multiple collections at once.
|
|
305
|
+
<FieldCaption error={orderPropertyMissing}>
|
|
306
|
+
{orderPropertyMissing
|
|
307
|
+
? t("order_property_not_found", { property: values.orderProperty ?? "" })
|
|
308
|
+
: numberProperties.length === 0
|
|
309
|
+
? t("no_number_properties")
|
|
310
|
+
: t("order_property_description")
|
|
311
|
+
}
|
|
419
312
|
</FieldCaption>
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
313
|
+
</>
|
|
314
|
+
);
|
|
315
|
+
})()}
|
|
316
|
+
{(() => {
|
|
317
|
+
// Check if orderProperty references a non-existent property
|
|
318
|
+
const orderPropertyMissing = Boolean(values.orderProperty) &&
|
|
319
|
+
!numberProperties.some(p => p.key === values.orderProperty);
|
|
320
|
+
const showCreateButton = !values.orderProperty || orderPropertyMissing;
|
|
321
|
+
|
|
322
|
+
// Pre-fill with missing property id or default "__order"
|
|
323
|
+
const dialogPropertyKey = orderPropertyMissing && values.orderProperty
|
|
324
|
+
? values.orderProperty
|
|
325
|
+
: "__order";
|
|
326
|
+
const dialogPropertyName = orderPropertyMissing && values.orderProperty
|
|
327
|
+
? unslugify(values.orderProperty)
|
|
328
|
+
: t("order_label");
|
|
329
|
+
|
|
330
|
+
if (!showCreateButton) return null;
|
|
331
|
+
|
|
332
|
+
return (
|
|
333
|
+
<>
|
|
334
|
+
<button
|
|
335
|
+
type="button"
|
|
336
|
+
className="ml-3.5 text-sm text-primary hover:text-primary-dark mt-2"
|
|
337
|
+
onClick={() => setOrderPropertyDialogOpen(true)}
|
|
338
|
+
>
|
|
339
|
+
{t("create_property", { property: dialogPropertyKey })}
|
|
340
|
+
</button>
|
|
341
|
+
<PropertyFormDialog
|
|
342
|
+
open={orderPropertyDialogOpen}
|
|
343
|
+
onCancel={() => setOrderPropertyDialogOpen(false)}
|
|
344
|
+
property={{
|
|
345
|
+
dataType: "number",
|
|
346
|
+
name: dialogPropertyName,
|
|
347
|
+
disabled: true,
|
|
348
|
+
hideFromCollection: true
|
|
349
|
+
}}
|
|
350
|
+
propertyKey={dialogPropertyKey}
|
|
351
|
+
existingProperty={false}
|
|
352
|
+
autoOpenTypeSelect={false}
|
|
353
|
+
autoUpdateId={false}
|
|
354
|
+
inArray={false}
|
|
355
|
+
allowDataInference={false}
|
|
356
|
+
propertyConfigs={customizationController.propertyConfigs}
|
|
357
|
+
collectionEditable={true}
|
|
358
|
+
existingPropertyKeys={Object.keys(values.properties ?? {})}
|
|
359
|
+
onPropertyChanged={({ id, property }) => {
|
|
360
|
+
const newProperties = {
|
|
361
|
+
...values.properties,
|
|
362
|
+
[id!]: property
|
|
363
|
+
};
|
|
364
|
+
const newPropertiesOrder = [...(values.propertiesOrder ?? Object.keys(values.properties ?? {})), id];
|
|
365
|
+
setFieldValue("properties", newProperties);
|
|
366
|
+
setFieldValue("propertiesOrder", newPropertiesOrder);
|
|
367
|
+
setFieldValue("orderProperty", id);
|
|
368
|
+
setOrderPropertyDialogOpen(false);
|
|
369
|
+
}}
|
|
428
370
|
/>
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
</FieldCaption>
|
|
434
|
-
</div>
|
|
371
|
+
</>
|
|
372
|
+
);
|
|
373
|
+
})()}
|
|
374
|
+
</div>
|
|
435
375
|
|
|
376
|
+
<div className={"col-span-12"}>
|
|
377
|
+
<BooleanSwitchWithLabel
|
|
378
|
+
position={"start"}
|
|
379
|
+
size={"large"}
|
|
380
|
+
allowIndeterminate={true}
|
|
381
|
+
label={<span className="flex items-center gap-2"><HistoryIcon size={"smallest"} />{values.history === null || values.history === undefined ? t("doc_history_global") : (
|
|
382
|
+
values.history ? t("doc_history_enabled") : t("doc_history_not_enabled")
|
|
383
|
+
)}</span>}
|
|
384
|
+
onValueChange={(v) => setFieldValue("history", v)}
|
|
385
|
+
value={values.history === undefined ? null : values.history}
|
|
386
|
+
/>
|
|
387
|
+
<FieldCaption>
|
|
388
|
+
{t("doc_history_description")}
|
|
389
|
+
</FieldCaption>
|
|
390
|
+
</div>
|
|
436
391
|
|
|
437
|
-
</div>
|
|
438
|
-
</ExpandablePanel>
|
|
439
392
|
|
|
440
|
-
|
|
393
|
+
<div className={"col-span-12 mt-8"}>
|
|
441
394
|
|
|
442
395
|
</div>
|
|
443
396
|
|
|
444
397
|
</div>
|
|
445
398
|
|
|
446
|
-
<div style={{ height: "52px" }}/>
|
|
399
|
+
<div style={{ height: "52px" }} />
|
|
447
400
|
|
|
448
401
|
<Dialog
|
|
449
402
|
open={iconDialogOpen}
|
|
@@ -453,10 +406,10 @@ export function CollectionDetailsForm({
|
|
|
453
406
|
>
|
|
454
407
|
<div className={"p-4 overflow-auto min-h-[200px]"}>
|
|
455
408
|
<SearchIconsView selectedIcon={typeof values.icon === "string" ? values.icon : undefined}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
409
|
+
onIconSelected={(icon: string) => {
|
|
410
|
+
setIconDialogOpen(false);
|
|
411
|
+
setFieldValue("icon", icon);
|
|
412
|
+
}} />
|
|
460
413
|
</div>
|
|
461
414
|
|
|
462
415
|
</Dialog>
|
|
@@ -467,18 +420,20 @@ export function CollectionDetailsForm({
|
|
|
467
420
|
}
|
|
468
421
|
|
|
469
422
|
function DefaultDatabaseField({
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
423
|
+
databaseId,
|
|
424
|
+
onDatabaseIdUpdate
|
|
425
|
+
}: { databaseId?: string, onDatabaseIdUpdate: (databaseId: string) => void }) {
|
|
426
|
+
|
|
427
|
+
const { t } = useTranslation();
|
|
473
428
|
|
|
474
|
-
return <Tooltip title={"
|
|
475
|
-
|
|
476
|
-
|
|
429
|
+
return <Tooltip title={t("database_id")}
|
|
430
|
+
side={"top"}
|
|
431
|
+
align={"start"}>
|
|
477
432
|
<TextField size={"small"}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
433
|
+
invisible={true}
|
|
434
|
+
inputClassName={"text-end"}
|
|
435
|
+
value={databaseId ?? ""}
|
|
436
|
+
onChange={(e: any) => onDatabaseIdUpdate(e.target.value)}
|
|
437
|
+
placeholder={t("default_text")}></TextField>
|
|
483
438
|
</Tooltip>
|
|
484
439
|
}
|