@firecms/collection_editor 3.0.0-alpha.9 → 3.0.0-beta.10

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 (149) hide show
  1. package/LICENSE +114 -21
  2. package/dist/ConfigControllerProvider.d.ts +13 -3
  3. package/dist/index.d.ts +4 -2
  4. package/dist/index.es.js +5587 -4976
  5. package/dist/index.es.js.map +1 -1
  6. package/dist/index.umd.js +6829 -1
  7. package/dist/index.umd.js.map +1 -1
  8. package/dist/types/collection_editor_controller.d.ts +36 -8
  9. package/dist/types/collection_inference.d.ts +1 -1
  10. package/dist/types/config_controller.d.ts +33 -6
  11. package/dist/types/persisted_collection.d.ts +4 -2
  12. package/dist/ui/CollectionViewHeaderAction.d.ts +11 -0
  13. package/dist/{components → ui}/EditorCollectionAction.d.ts +1 -1
  14. package/dist/ui/EditorCollectionActionStart.d.ts +2 -0
  15. package/dist/ui/MissingReferenceWidget.d.ts +3 -0
  16. package/dist/ui/NewCollectionButton.d.ts +1 -0
  17. package/dist/ui/PropertyAddColumnComponent.d.ts +8 -0
  18. package/dist/{components → ui}/collection_editor/CollectionDetailsForm.d.ts +3 -2
  19. package/dist/{components → ui}/collection_editor/CollectionEditorDialog.d.ts +15 -11
  20. package/dist/{components → ui}/collection_editor/CollectionEditorWelcomeView.d.ts +3 -3
  21. package/dist/{components → ui}/collection_editor/CollectionPropertiesEditorForm.d.ts +8 -6
  22. package/dist/{components → ui}/collection_editor/CollectionYupValidation.d.ts +3 -0
  23. package/dist/ui/collection_editor/EntityCustomViewsSelectDialog.d.ts +4 -0
  24. package/dist/{components → ui}/collection_editor/EnumForm.d.ts +1 -2
  25. package/dist/ui/collection_editor/GetCodeDialog.d.ts +5 -0
  26. package/dist/{components → ui}/collection_editor/PropertyEditView.d.ts +21 -11
  27. package/dist/{components → ui}/collection_editor/PropertyFieldPreview.d.ts +4 -3
  28. package/dist/{components → ui}/collection_editor/PropertyTree.d.ts +11 -7
  29. package/dist/{components → ui}/collection_editor/SubcollectionsEditTab.d.ts +3 -3
  30. package/dist/ui/collection_editor/SwitchControl.d.ts +8 -0
  31. package/dist/{components → ui}/collection_editor/import/CollectionEditorImportDataPreview.d.ts +1 -1
  32. package/dist/ui/collection_editor/import/CollectionEditorImportMapping.d.ts +14 -0
  33. package/dist/{components → ui}/collection_editor/import/clean_import_data.d.ts +1 -1
  34. package/dist/{components → ui}/collection_editor/properties/BlockPropertyField.d.ts +4 -1
  35. package/dist/{components → ui}/collection_editor/properties/CommonPropertyFields.d.ts +1 -1
  36. package/dist/{components → ui}/collection_editor/properties/MapPropertyField.d.ts +4 -1
  37. package/dist/ui/collection_editor/properties/MarkdownPropertyField.d.ts +4 -0
  38. package/dist/{components → ui}/collection_editor/properties/RepeatPropertyField.d.ts +4 -1
  39. package/dist/{components → ui}/collection_editor/properties/StringPropertyField.d.ts +1 -1
  40. package/dist/ui/collection_editor/properties/UrlPropertyField.d.ts +4 -0
  41. package/dist/ui/collection_editor/templates/blog_template.d.ts +2 -0
  42. package/dist/ui/collection_editor/templates/pages_template.d.ts +2 -0
  43. package/dist/ui/collection_editor/templates/products_template.d.ts +2 -0
  44. package/dist/ui/collection_editor/templates/users_template.d.ts +2 -0
  45. package/dist/{components → ui}/collection_editor/util.d.ts +1 -0
  46. package/dist/ui/collection_editor/utils/strings.d.ts +1 -0
  47. package/dist/ui/collection_editor/utils/supported_fields.d.ts +3 -0
  48. package/dist/ui/collection_editor/utils/update_property_for_widget.d.ts +2 -0
  49. package/dist/useCollectionEditorPlugin.d.ts +17 -6
  50. package/dist/utils/collections.d.ts +6 -0
  51. package/dist/utils/entities.d.ts +3 -4
  52. package/package.json +35 -37
  53. package/src/ConfigControllerProvider.tsx +350 -0
  54. package/src/index.ts +36 -0
  55. package/src/types/collection_editor_controller.tsx +53 -0
  56. package/src/types/collection_inference.ts +3 -0
  57. package/src/types/config_controller.tsx +60 -0
  58. package/src/types/config_permissions.ts +20 -0
  59. package/src/types/persisted_collection.ts +9 -0
  60. package/src/ui/CollectionViewHeaderAction.tsx +48 -0
  61. package/src/ui/EditorCollectionAction.tsx +56 -0
  62. package/src/ui/EditorCollectionActionStart.tsx +88 -0
  63. package/src/ui/HomePageEditorCollectionAction.tsx +89 -0
  64. package/src/ui/MissingReferenceWidget.tsx +37 -0
  65. package/src/ui/NewCollectionButton.tsx +18 -0
  66. package/src/ui/NewCollectionCard.tsx +48 -0
  67. package/src/ui/PropertyAddColumnComponent.tsx +47 -0
  68. package/src/ui/collection_editor/CollectionDetailsForm.tsx +426 -0
  69. package/src/ui/collection_editor/CollectionEditorDialog.tsx +826 -0
  70. package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +214 -0
  71. package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +513 -0
  72. package/src/ui/collection_editor/CollectionYupValidation.tsx +7 -0
  73. package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +37 -0
  74. package/src/ui/collection_editor/EnumForm.tsx +357 -0
  75. package/src/ui/collection_editor/GetCodeDialog.tsx +122 -0
  76. package/src/ui/collection_editor/PropertyEditView.tsx +789 -0
  77. package/src/ui/collection_editor/PropertyFieldPreview.tsx +204 -0
  78. package/src/ui/collection_editor/PropertyTree.tsx +254 -0
  79. package/src/ui/collection_editor/SubcollectionsEditTab.tsx +269 -0
  80. package/src/ui/collection_editor/SwitchControl.tsx +39 -0
  81. package/src/ui/collection_editor/UnsavedChangesDialog.tsx +47 -0
  82. package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +53 -0
  83. package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +299 -0
  84. package/src/ui/collection_editor/import/clean_import_data.ts +53 -0
  85. package/src/ui/collection_editor/properties/BlockPropertyField.tsx +144 -0
  86. package/src/ui/collection_editor/properties/BooleanPropertyField.tsx +40 -0
  87. package/src/ui/collection_editor/properties/CommonPropertyFields.tsx +110 -0
  88. package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +89 -0
  89. package/src/ui/collection_editor/properties/EnumPropertyField.tsx +114 -0
  90. package/src/ui/collection_editor/properties/KeyValuePropertyField.tsx +20 -0
  91. package/src/ui/collection_editor/properties/MapPropertyField.tsx +150 -0
  92. package/src/ui/collection_editor/properties/MarkdownPropertyField.tsx +139 -0
  93. package/src/ui/collection_editor/properties/NumberPropertyField.tsx +38 -0
  94. package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +160 -0
  95. package/src/ui/collection_editor/properties/RepeatPropertyField.tsx +108 -0
  96. package/src/ui/collection_editor/properties/StoragePropertyField.tsx +215 -0
  97. package/src/ui/collection_editor/properties/StringPropertyField.tsx +70 -0
  98. package/src/ui/collection_editor/properties/UrlPropertyField.tsx +89 -0
  99. package/src/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +45 -0
  100. package/src/ui/collection_editor/properties/validation/ArrayPropertyValidation.tsx +50 -0
  101. package/src/ui/collection_editor/properties/validation/GeneralPropertyValidation.tsx +61 -0
  102. package/src/ui/collection_editor/properties/validation/NumberPropertyValidation.tsx +115 -0
  103. package/src/ui/collection_editor/properties/validation/StringPropertyValidation.tsx +150 -0
  104. package/src/ui/collection_editor/properties/validation/ValidationPanel.tsx +28 -0
  105. package/src/ui/collection_editor/templates/blog_template.ts +115 -0
  106. package/src/ui/collection_editor/templates/pages_template.ts +183 -0
  107. package/src/ui/collection_editor/templates/products_template.ts +88 -0
  108. package/src/ui/collection_editor/templates/users_template.ts +42 -0
  109. package/src/ui/collection_editor/util.ts +28 -0
  110. package/src/ui/collection_editor/utils/strings.ts +9 -0
  111. package/src/ui/collection_editor/utils/supported_fields.tsx +29 -0
  112. package/src/ui/collection_editor/utils/update_property_for_widget.ts +271 -0
  113. package/src/ui/collection_editor/utils/useTraceUpdate.tsx +23 -0
  114. package/src/useCollectionEditorController.tsx +9 -0
  115. package/src/useCollectionEditorPlugin.tsx +164 -0
  116. package/src/useCollectionsConfigController.tsx +9 -0
  117. package/src/utils/arrays.ts +3 -0
  118. package/src/utils/collections.ts +30 -0
  119. package/src/utils/entities.ts +38 -0
  120. package/src/vite-env.d.ts +1 -0
  121. package/dist/components/collection_editor/PropertySelectItem.d.ts +0 -8
  122. package/dist/components/collection_editor/SelectIcons.d.ts +0 -6
  123. package/dist/components/collection_editor/import/CollectionEditorImportMapping.d.ts +0 -4
  124. package/dist/components/collection_editor/properties/FieldHelperView.d.ts +0 -4
  125. package/dist/components/collection_editor/templates/blog_template.d.ts +0 -10
  126. package/dist/components/collection_editor/templates/products_template.d.ts +0 -12
  127. package/dist/components/collection_editor/templates/users_template.d.ts +0 -7
  128. package/dist/components/collection_editor/utils/supported_fields.d.ts +0 -3
  129. package/dist/components/collection_editor/utils/update_property_for_widget.d.ts +0 -3
  130. package/dist/types/editable_properties.d.ts +0 -10
  131. package/dist/utils/icons.d.ts +0 -2
  132. package/dist/utils/synonyms.d.ts +0 -1951
  133. /package/dist/{components → ui}/HomePageEditorCollectionAction.d.ts +0 -0
  134. /package/dist/{components → ui}/NewCollectionCard.d.ts +0 -0
  135. /package/dist/{components → ui}/collection_editor/UnsavedChangesDialog.d.ts +0 -0
  136. /package/dist/{components → ui}/collection_editor/properties/BooleanPropertyField.d.ts +0 -0
  137. /package/dist/{components → ui}/collection_editor/properties/DateTimePropertyField.d.ts +0 -0
  138. /package/dist/{components → ui}/collection_editor/properties/EnumPropertyField.d.ts +0 -0
  139. /package/dist/{components → ui}/collection_editor/properties/KeyValuePropertyField.d.ts +0 -0
  140. /package/dist/{components → ui}/collection_editor/properties/NumberPropertyField.d.ts +0 -0
  141. /package/dist/{components → ui}/collection_editor/properties/ReferencePropertyField.d.ts +0 -0
  142. /package/dist/{components → ui}/collection_editor/properties/StoragePropertyField.d.ts +0 -0
  143. /package/dist/{components → ui}/collection_editor/properties/advanced/AdvancedPropertyValidation.d.ts +0 -0
  144. /package/dist/{components → ui}/collection_editor/properties/validation/ArrayPropertyValidation.d.ts +0 -0
  145. /package/dist/{components → ui}/collection_editor/properties/validation/GeneralPropertyValidation.d.ts +0 -0
  146. /package/dist/{components → ui}/collection_editor/properties/validation/NumberPropertyValidation.d.ts +0 -0
  147. /package/dist/{components → ui}/collection_editor/properties/validation/StringPropertyValidation.d.ts +0 -0
  148. /package/dist/{components → ui}/collection_editor/properties/validation/ValidationPanel.d.ts +0 -0
  149. /package/dist/{components → ui}/collection_editor/utils/useTraceUpdate.d.ts +0 -0
@@ -0,0 +1,513 @@
1
+ import React, { useEffect, useMemo, useState } from "react";
2
+
3
+ import { Field, getIn, useFormex } from "@firecms/formex";
4
+ import {
5
+ EntityCollection,
6
+ ErrorBoundary,
7
+ isPropertyBuilder,
8
+ makePropertiesEditable,
9
+ Properties,
10
+ Property,
11
+ PropertyConfig,
12
+ PropertyOrBuilder,
13
+ useLargeLayout,
14
+ User,
15
+ useSnackbarController
16
+ } from "@firecms/core";
17
+ import {
18
+ AddIcon,
19
+ AutoAwesomeIcon,
20
+ Button,
21
+ CircularProgress,
22
+ cls,
23
+ CodeIcon,
24
+ DebouncedTextField,
25
+ defaultBorderMixin,
26
+ IconButton,
27
+ Paper,
28
+ Tooltip,
29
+ Typography,
30
+ } from "@firecms/ui";
31
+
32
+ import { getFullId, idToPropertiesPath, namespaceToPropertiesOrderPath } from "./util";
33
+ import { OnPropertyChangedParams, PropertyForm, PropertyFormDialog } from "./PropertyEditView";
34
+ import { PropertyTree } from "./PropertyTree";
35
+ import { PersistedCollection } from "../../types/persisted_collection";
36
+ import { GetCodeDialog } from "./GetCodeDialog";
37
+
38
+ type CollectionEditorFormProps = {
39
+ showErrors: boolean;
40
+ isNewCollection: boolean;
41
+ propertyErrorsRef?: React.MutableRefObject<any>;
42
+ onPropertyError: (propertyKey: string, namespace: string | undefined, error?: Record<string, any>) => void;
43
+ setDirty?: (dirty: boolean) => void;
44
+ reservedGroups?: string[];
45
+ extraIcon: React.ReactNode;
46
+ getUser?: (uid: string) => User | null;
47
+ getData?: () => Promise<object[]>;
48
+ doCollectionInference: (collection: PersistedCollection) => Promise<Partial<EntityCollection> | null> | undefined;
49
+ propertyConfigs: Record<string, PropertyConfig>;
50
+ collectionEditable: boolean;
51
+ };
52
+
53
+ export function CollectionPropertiesEditorForm({
54
+ showErrors,
55
+ isNewCollection,
56
+ propertyErrorsRef,
57
+ onPropertyError,
58
+ setDirty,
59
+ reservedGroups,
60
+ extraIcon,
61
+ getUser,
62
+ getData,
63
+ doCollectionInference,
64
+ propertyConfigs,
65
+ collectionEditable
66
+ }: CollectionEditorFormProps) {
67
+
68
+ const {
69
+ values,
70
+ setFieldValue,
71
+ setFieldError,
72
+ setFieldTouched,
73
+ errors,
74
+ dirty
75
+ } = useFormex<PersistedCollection>();
76
+
77
+ const snackbarController = useSnackbarController();
78
+
79
+ const largeLayout = useLargeLayout();
80
+ const asDialog = !largeLayout
81
+
82
+ // index of the selected property within the namespace
83
+ const [selectedPropertyIndex, setSelectedPropertyIndex] = useState<number | undefined>();
84
+ const [selectedPropertyKey, setSelectedPropertyKey] = useState<string | undefined>();
85
+ const [selectedPropertyNamespace, setSelectedPropertyNamespace] = useState<string | undefined>();
86
+
87
+ const selectedPropertyFullId = selectedPropertyKey ? getFullId(selectedPropertyKey, selectedPropertyNamespace) : undefined;
88
+ const selectedProperty = selectedPropertyFullId ? getIn(values.properties, selectedPropertyFullId.replaceAll(".", ".properties.")) : undefined;
89
+ const [codeDialogOpen, setCodeDialogOpen] = useState<boolean>(false);
90
+
91
+ const [inferringProperties, setInferringProperties] = useState<boolean>(false);
92
+
93
+ const [newPropertyDialogOpen, setNewPropertyDialogOpen] = useState<boolean>(false);
94
+ const [inferredPropertyKeys, setInferredPropertyKeys] = useState<string[]>([]);
95
+
96
+ const currentPropertiesOrderRef = React.useRef<{
97
+ [key: string]: string[]
98
+ }>(values.propertiesOrder ? { "": values.propertiesOrder } : {});
99
+
100
+ useEffect(() => {
101
+ if (setDirty)
102
+ setDirty(dirty);
103
+ }, [dirty]);
104
+
105
+ const inferPropertiesFromData = doCollectionInference
106
+ ? (): void => {
107
+ if (!doCollectionInference)
108
+ return;
109
+
110
+ setInferringProperties(true);
111
+ // @ts-ignore
112
+ doCollectionInference(values)
113
+ .then((newCollection) => {
114
+
115
+ if (newCollection)
116
+ makePropertiesEditable(newCollection.properties as Properties);
117
+
118
+ if (!newCollection) {
119
+ snackbarController.open({
120
+ type: "error",
121
+ message: "Could not infer properties from data"
122
+ });
123
+ return;
124
+ }
125
+ // find properties in the new collection, not present in the current one
126
+ const newPropertyKeys = (newCollection.properties ? Object.keys(newCollection.properties) : [])
127
+ .filter((propertyKey) => !values.properties[propertyKey]);
128
+ if (newPropertyKeys.length === 0) {
129
+ snackbarController.open({
130
+ type: "info",
131
+ message: "No new properties found in existing data"
132
+ });
133
+ return;
134
+ }
135
+ // add them to the current collection
136
+ const updatedProperties = {
137
+ ...newPropertyKeys.reduce((acc, propertyKey) => {
138
+ acc[propertyKey] = (newCollection.properties ?? {})[propertyKey];
139
+ return acc;
140
+ }, {} as {
141
+ [key: string]: PropertyOrBuilder
142
+ }),
143
+ ...values.properties
144
+ };
145
+ const updatedPropertiesOrder = [
146
+ ...newPropertyKeys,
147
+ ...(values.propertiesOrder ?? [])
148
+ ];
149
+ setFieldValue("properties", updatedProperties, false);
150
+
151
+ updatePropertiesOrder(updatedPropertiesOrder);
152
+
153
+ setInferredPropertyKeys(newPropertyKeys);
154
+ })
155
+ .finally(() => {
156
+ setInferringProperties(false);
157
+ })
158
+ }
159
+ : undefined;
160
+
161
+ const getCurrentPropertiesOrder = (namespace?: string) => {
162
+ if (!namespace) return currentPropertiesOrderRef.current[""] ?? getIn(values, namespaceToPropertiesOrderPath());
163
+ return currentPropertiesOrderRef.current[namespace] ?? getIn(values, namespaceToPropertiesOrderPath(namespace));
164
+ };
165
+
166
+ const updatePropertiesOrder = (newPropertiesOrder: string[], namespace?: string) => {
167
+ const propertiesOrderPath = namespaceToPropertiesOrderPath(namespace);
168
+
169
+ setFieldValue(propertiesOrderPath, newPropertiesOrder, false);
170
+ currentPropertiesOrderRef.current[namespace ?? ""] = newPropertiesOrder;
171
+
172
+ };
173
+
174
+ const deleteProperty = (propertyKey?: string, namespace?: string) => {
175
+ const fullId = propertyKey ? getFullId(propertyKey, namespace) : undefined;
176
+ if (!fullId)
177
+ throw Error("collection editor miss config");
178
+
179
+ setFieldValue(idToPropertiesPath(fullId), undefined, false);
180
+
181
+ const currentPropertiesOrder = getCurrentPropertiesOrder(namespace);
182
+ if (currentPropertiesOrder) {
183
+ const newPropertiesOrder = currentPropertiesOrder.filter((p) => p !== propertyKey);
184
+ updatePropertiesOrder(newPropertiesOrder, namespace);
185
+ }
186
+
187
+ setNewPropertyDialogOpen(false);
188
+
189
+ setSelectedPropertyIndex(undefined);
190
+ setSelectedPropertyKey(undefined);
191
+ setSelectedPropertyNamespace(undefined);
192
+ };
193
+
194
+ const onPropertyMove = (propertiesOrder: string[], namespace?: string) => {
195
+ setFieldValue(namespaceToPropertiesOrderPath(namespace), propertiesOrder, false);
196
+ };
197
+
198
+ const onPropertyCreated = ({
199
+ id,
200
+ property
201
+ }: {
202
+ id?: string,
203
+ property: Property
204
+ }) => {
205
+ if (!id) {
206
+ throw Error("Need to include an ID when creating a new property")
207
+ }
208
+ setFieldValue("properties", {
209
+ ...(values.properties ?? {}),
210
+ [id]: property
211
+ }, false);
212
+
213
+ const newPropertiesOrder = [...(values.propertiesOrder ?? Object.keys(values.properties)), id];
214
+ updatePropertiesOrder(newPropertiesOrder);
215
+
216
+ setNewPropertyDialogOpen(false);
217
+ if (largeLayout) {
218
+ setSelectedPropertyIndex(newPropertiesOrder.indexOf(id));
219
+ setSelectedPropertyKey(id);
220
+ }
221
+ setSelectedPropertyNamespace(undefined);
222
+ };
223
+
224
+ const onPropertyChanged = ({
225
+ id,
226
+ property,
227
+ previousId,
228
+ namespace
229
+ }: OnPropertyChangedParams) => {
230
+
231
+ const fullId = id ? getFullId(id, namespace) : undefined;
232
+ const propertyPath = fullId ? idToPropertiesPath(fullId) : undefined;
233
+
234
+ // If the id has changed we need to a little cleanup
235
+ if (previousId && previousId !== id) {
236
+ console.debug("onPropertyChanged, id change", {
237
+ id,
238
+ property,
239
+ previousId,
240
+ namespace
241
+ })
242
+
243
+ const previousFullId = getFullId(previousId, namespace);
244
+ const previousPropertyPath = idToPropertiesPath(previousFullId);
245
+
246
+ const currentPropertiesOrder = getCurrentPropertiesOrder(namespace);
247
+
248
+ // replace previousId with id in propertiesOrder
249
+ const newPropertiesOrder = currentPropertiesOrder
250
+ .map((p) => p === previousId ? id : p)
251
+ .filter((p) => p !== undefined) as string[];
252
+
253
+ updatePropertiesOrder(newPropertiesOrder, namespace);
254
+
255
+ if (id) {
256
+ setSelectedPropertyIndex(newPropertiesOrder.indexOf(id));
257
+ setSelectedPropertyKey(id);
258
+ }
259
+ setFieldValue(previousPropertyPath, undefined, false);
260
+ setFieldTouched(previousPropertyPath, false, false);
261
+ }
262
+
263
+ console.debug("onPropertyChanged", {
264
+ id,
265
+ property,
266
+ previousId,
267
+ namespace,
268
+ propertyPath
269
+ })
270
+
271
+ if (propertyPath) {
272
+ setFieldValue(propertyPath, property, false);
273
+ setFieldTouched(propertyPath, true, false);
274
+ }
275
+
276
+ };
277
+
278
+ const onPropertyErrorInternal = (id: string, namespace?: string, error?: Record<string, any>) => {
279
+ const propertyPath = id ? getFullId(id, namespace) : undefined;
280
+ console.debug("onPropertyErrorInternal", {
281
+ id,
282
+ namespace,
283
+ error,
284
+ propertyPath
285
+ });
286
+ if (propertyPath) {
287
+ const hasError = error && Object.keys(error).length > 0;
288
+ onPropertyError(id, namespace, hasError ? error : undefined);
289
+ setFieldError(idToPropertiesPath(propertyPath), hasError ? "Property error" : undefined);
290
+ }
291
+ }
292
+
293
+ const closePropertyDialog = () => {
294
+ setSelectedPropertyIndex(undefined);
295
+ setSelectedPropertyKey(undefined);
296
+ };
297
+
298
+ const initialErrors = selectedPropertyKey && propertyErrorsRef?.current?.properties ? propertyErrorsRef.current.properties[selectedPropertyKey] : undefined;
299
+
300
+ const emptyCollection = values?.propertiesOrder === undefined || values.propertiesOrder.length === 0;
301
+
302
+ const usedPropertiesOrder = (values.propertiesOrder
303
+ ? values.propertiesOrder
304
+ : Object.keys(values.properties)) as string[];
305
+
306
+ const owner = useMemo(() => values.ownerId && getUser ? getUser(values.ownerId) : null, [getUser, values.ownerId]);
307
+
308
+ const onPropertyClick = (propertyKey: string, namespace?: string) => {
309
+ console.debug("CollectionEditor: onPropertyClick", {
310
+ propertyKey,
311
+ namespace
312
+ });
313
+ setSelectedPropertyIndex(usedPropertiesOrder.indexOf(propertyKey));
314
+ setSelectedPropertyKey(propertyKey);
315
+ setSelectedPropertyNamespace(namespace);
316
+ };
317
+
318
+ const body = (
319
+ <div className={"grid grid-cols-12 gap-2 h-full bg-gray-50 dark:bg-gray-900"}>
320
+ <div className={cls(
321
+ "p-4 md:p-8 pb-20 md:pb-20",
322
+ "col-span-12 lg:col-span-5 h-full overflow-auto",
323
+ !asDialog && "border-r " + defaultBorderMixin
324
+ )}>
325
+
326
+ <div className="flex my-2">
327
+
328
+ <div className="flex-grow mb-4">
329
+
330
+ <Field
331
+ name={"name"}
332
+ as={DebouncedTextField}
333
+ invisible={true}
334
+ className="-ml-1"
335
+ inputClassName="text-2xl font-headers"
336
+ placeholder={"Collection name"}
337
+ size={"small"}
338
+ required
339
+ error={Boolean(errors?.name)}/>
340
+
341
+ {owner &&
342
+ <Typography variant={"body2"}
343
+ className={"ml-2"}
344
+ color={"secondary"}>
345
+ Created by {owner.displayName}
346
+ </Typography>}
347
+ </div>
348
+
349
+ {extraIcon && <div className="ml-4">
350
+ {extraIcon}
351
+ </div>}
352
+
353
+ <div className="ml-1 mt-2 flex flex-row gap-2">
354
+ <Tooltip title={"Get the code for this collection"}
355
+ asChild={true}>
356
+ <IconButton
357
+ variant={"filled"}
358
+ disabled={inferringProperties}
359
+ onClick={() => setCodeDialogOpen(true)}>
360
+ <CodeIcon/>
361
+ </IconButton>
362
+ </Tooltip>
363
+ {inferPropertiesFromData && <Tooltip title={"Add new properties based on data"}
364
+ asChild={true}>
365
+ <IconButton
366
+ variant={"filled"}
367
+ disabled={inferringProperties}
368
+ onClick={inferPropertiesFromData}>
369
+ {inferringProperties ? <CircularProgress size={"small"}/> : <AutoAwesomeIcon/>}
370
+ </IconButton>
371
+ </Tooltip>}
372
+ <Tooltip title={"Add new property"}
373
+ asChild={true}>
374
+ <Button
375
+ variant={"outlined"}
376
+ onClick={() => setNewPropertyDialogOpen(true)}>
377
+ <AddIcon/>
378
+ </Button>
379
+ </Tooltip>
380
+ </div>
381
+ </div>
382
+
383
+ <ErrorBoundary>
384
+ <PropertyTree
385
+ className={"pl-8"}
386
+ inferredPropertyKeys={inferredPropertyKeys}
387
+ selectedPropertyKey={selectedPropertyKey ? getFullId(selectedPropertyKey, selectedPropertyNamespace) : undefined}
388
+ properties={values.properties}
389
+ additionalFields={values.additionalFields}
390
+ propertiesOrder={usedPropertiesOrder}
391
+ onPropertyClick={onPropertyClick}
392
+ onPropertyMove={onPropertyMove}
393
+ onPropertyRemove={isNewCollection ? deleteProperty : undefined}
394
+ collectionEditable={collectionEditable}
395
+ errors={errors}/>
396
+ </ErrorBoundary>
397
+
398
+ <Button className={"mt-8 w-full"}
399
+ color="primary"
400
+ variant={"outlined"}
401
+ size={"large"}
402
+ onClick={() => setNewPropertyDialogOpen(true)}
403
+ startIcon={<AddIcon/>}>
404
+ Add new property
405
+ </Button>
406
+ </div>
407
+
408
+ {!asDialog &&
409
+ <div className={"col-span-12 lg:col-span-7 p-4 md:py-8 md:px-4 h-full overflow-auto pb-20 md:pb-20"}>
410
+ <Paper
411
+ className="sticky top-8 p-4 min-h-full border border-transparent w-full flex flex-col justify-center ">
412
+
413
+ {selectedPropertyFullId &&
414
+ selectedProperty &&
415
+ !isPropertyBuilder(selectedProperty) &&
416
+ <PropertyForm
417
+ inArray={false}
418
+ key={`edit_view_${selectedPropertyIndex}`}
419
+ existingProperty={!isNewCollection}
420
+ autoUpdateId={false}
421
+ allowDataInference={!isNewCollection}
422
+ autoOpenTypeSelect={false}
423
+ propertyKey={selectedPropertyKey}
424
+ propertyNamespace={selectedPropertyNamespace}
425
+ property={selectedProperty}
426
+ onPropertyChanged={onPropertyChanged}
427
+ onDelete={deleteProperty}
428
+ onError={onPropertyErrorInternal}
429
+ forceShowErrors={showErrors}
430
+ initialErrors={initialErrors}
431
+ getData={getData}
432
+ propertyConfigs={propertyConfigs}
433
+ collectionEditable={collectionEditable}
434
+ />}
435
+
436
+ {!selectedProperty &&
437
+ <div className={"w-full flex flex-col items-center justify-center h-full gap-4"}>
438
+ <Typography variant={"label"} className="">
439
+ {emptyCollection
440
+ ? "Now you can add your first property"
441
+ : "Select a property to edit it"}
442
+ </Typography>
443
+ <Button variant={"outlined"}
444
+ onClick={() => setNewPropertyDialogOpen(true)}
445
+ >
446
+ <AddIcon/>
447
+ Add new property
448
+ </Button>
449
+ </div>}
450
+
451
+ {selectedProperty && isPropertyBuilder(selectedProperty) &&
452
+ <Typography variant={"label"} className="flex items-center justify-center">
453
+ {"This property is defined as a property builder in code"}
454
+ </Typography>}
455
+ </Paper>
456
+ </div>}
457
+
458
+ {asDialog && <PropertyFormDialog
459
+ inArray={false}
460
+ open={selectedPropertyIndex !== undefined}
461
+ key={`edit_view_${selectedPropertyIndex}`}
462
+ autoUpdateId={!selectedProperty}
463
+ allowDataInference={!isNewCollection}
464
+ existingProperty={true}
465
+ autoOpenTypeSelect={false}
466
+ propertyKey={selectedPropertyKey}
467
+ propertyNamespace={selectedPropertyNamespace}
468
+ property={selectedProperty}
469
+ onPropertyChanged={onPropertyChanged}
470
+ onDelete={deleteProperty}
471
+ onError={onPropertyErrorInternal}
472
+ forceShowErrors={showErrors}
473
+ initialErrors={initialErrors}
474
+ getData={getData}
475
+ propertyConfigs={propertyConfigs}
476
+ collectionEditable={collectionEditable}
477
+ onCancel={closePropertyDialog}
478
+ onOkClicked={asDialog
479
+ ? closePropertyDialog
480
+ : undefined
481
+ }/>}
482
+
483
+ </div>);
484
+
485
+ return (<>
486
+
487
+ {body}
488
+
489
+ {/* This is the dialog used for new properties*/}
490
+ <PropertyFormDialog
491
+ inArray={false}
492
+ existingProperty={false}
493
+ autoOpenTypeSelect={true}
494
+ autoUpdateId={true}
495
+ forceShowErrors={showErrors}
496
+ open={newPropertyDialogOpen}
497
+ onCancel={() => setNewPropertyDialogOpen(false)}
498
+ onPropertyChanged={onPropertyCreated}
499
+ getData={getData}
500
+ allowDataInference={!isNewCollection}
501
+ propertyConfigs={propertyConfigs}
502
+ collectionEditable={collectionEditable}
503
+ existingPropertyKeys={values.propertiesOrder as string[]}/>
504
+
505
+ <ErrorBoundary>
506
+ <GetCodeDialog
507
+ collection={values}
508
+ open={codeDialogOpen}
509
+ onOpenChange={setCodeDialogOpen}/>
510
+ </ErrorBoundary>
511
+ </>
512
+ );
513
+ }
@@ -0,0 +1,7 @@
1
+ import * as Yup from "yup";
2
+
3
+ export const YupSchema = Yup.object().shape({
4
+ id: Yup.string().required("Required"),
5
+ name: Yup.string().required("Required"),
6
+ path: Yup.string().required("Required")
7
+ });
@@ -0,0 +1,37 @@
1
+ import { useCustomizationController } from "@firecms/core";
2
+ import { Button, Dialog, DialogActions, DialogContent, Typography } from "@firecms/ui";
3
+ import React from "react";
4
+
5
+ export function EntityCustomViewsSelectDialog({ open, onClose }: { open: boolean, onClose: (selectedViewKey?: string) => void }) {
6
+ const {
7
+ entityViews,
8
+ } = useCustomizationController();
9
+
10
+ return <Dialog
11
+ maxWidth={"md"}
12
+ open={open}>
13
+ <DialogContent className={"flex flex-col gap-4"}>
14
+ <Typography variant={"h6"}>
15
+ Select view
16
+ </Typography>
17
+ {entityViews?.map((view) => {
18
+ return <Button
19
+ key={view.key}
20
+ onClick={() => onClose(view.key)}
21
+ fullWidth
22
+ variant={"text"}
23
+ >
24
+ {view.name} ({view.key})
25
+ </Button>;
26
+ })}
27
+ {(entityViews ?? []).length === 0 &&
28
+ <Typography variant={"body2"}>
29
+ No custom views defined
30
+ </Typography>
31
+ }
32
+ </DialogContent>
33
+ <DialogActions>
34
+ <Button variant={"outlined"} onClick={() => onClose()}>Cancel</Button>
35
+ </DialogActions>
36
+ </Dialog>
37
+ }