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