@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.
Files changed (119) hide show
  1. package/dist/ConfigControllerProvider.d.ts +6 -0
  2. package/dist/api/generateCollectionApi.d.ts +71 -0
  3. package/dist/api/index.d.ts +1 -0
  4. package/dist/index.d.ts +5 -1
  5. package/dist/index.es.js +15260 -8173
  6. package/dist/index.es.js.map +1 -1
  7. package/dist/index.umd.js +15257 -8170
  8. package/dist/index.umd.js.map +1 -1
  9. package/dist/locales/de.d.ts +120 -0
  10. package/dist/locales/en.d.ts +120 -0
  11. package/dist/locales/es.d.ts +120 -0
  12. package/dist/locales/fr.d.ts +120 -0
  13. package/dist/locales/hi.d.ts +120 -0
  14. package/dist/locales/it.d.ts +120 -0
  15. package/dist/locales/pt.d.ts +120 -0
  16. package/dist/types/collection_editor_controller.d.ts +14 -0
  17. package/dist/types/collection_inference.d.ts +8 -2
  18. package/dist/types/config_controller.d.ts +23 -2
  19. package/dist/ui/AddKanbanColumnAction.d.ts +11 -0
  20. package/dist/ui/KanbanSetupAction.d.ts +10 -0
  21. package/dist/ui/collection_editor/AICollectionGeneratorPopover.d.ts +37 -0
  22. package/dist/ui/collection_editor/AIModifiedPathsContext.d.ts +20 -0
  23. package/dist/ui/collection_editor/CollectionDetailsForm.d.ts +2 -3
  24. package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +24 -0
  25. package/dist/ui/collection_editor/CollectionEditorWelcomeView.d.ts +4 -1
  26. package/dist/ui/collection_editor/CollectionJsonImportDialog.d.ts +7 -0
  27. package/dist/ui/collection_editor/CollectionYupValidation.d.ts +9 -13
  28. package/dist/ui/collection_editor/DisplaySettingsForm.d.ts +3 -0
  29. package/dist/ui/collection_editor/EntityActionsEditTab.d.ts +2 -1
  30. package/dist/ui/collection_editor/ExtendSettingsForm.d.ts +14 -0
  31. package/dist/ui/collection_editor/GeneralSettingsForm.d.ts +7 -0
  32. package/dist/ui/collection_editor/KanbanConfigSection.d.ts +4 -0
  33. package/dist/ui/collection_editor/PropertyEditView.d.ts +6 -1
  34. package/dist/ui/collection_editor/PropertyTree.d.ts +2 -1
  35. package/dist/ui/collection_editor/SubcollectionsEditTab.d.ts +2 -1
  36. package/dist/ui/collection_editor/ViewModeSwitch.d.ts +6 -0
  37. package/dist/ui/collection_editor/properties/EnumPropertyField.d.ts +2 -1
  38. package/dist/ui/collection_editor/properties/conditions/ConditionsEditor.d.ts +10 -0
  39. package/dist/ui/collection_editor/properties/conditions/ConditionsPanel.d.ts +2 -0
  40. package/dist/ui/collection_editor/properties/conditions/EnumConditionsEditor.d.ts +6 -0
  41. package/dist/ui/collection_editor/properties/conditions/index.d.ts +6 -0
  42. package/dist/ui/collection_editor/properties/conditions/property_paths.d.ts +19 -0
  43. package/dist/useCollectionEditorPlugin.d.ts +7 -1
  44. package/dist/utils/validateCollectionJson.d.ts +22 -0
  45. package/package.json +15 -15
  46. package/src/ConfigControllerProvider.tsx +82 -47
  47. package/src/api/generateCollectionApi.ts +119 -0
  48. package/src/api/index.ts +1 -0
  49. package/src/index.ts +28 -1
  50. package/src/locales/de.ts +125 -0
  51. package/src/locales/en.ts +145 -0
  52. package/src/locales/es.ts +125 -0
  53. package/src/locales/fr.ts +125 -0
  54. package/src/locales/hi.ts +125 -0
  55. package/src/locales/it.ts +125 -0
  56. package/src/locales/pt.ts +125 -0
  57. package/src/types/collection_editor_controller.tsx +16 -3
  58. package/src/types/collection_inference.ts +15 -2
  59. package/src/types/config_controller.tsx +27 -2
  60. package/src/ui/AddKanbanColumnAction.tsx +203 -0
  61. package/src/ui/EditorCollectionAction.tsx +3 -3
  62. package/src/ui/EditorCollectionActionStart.tsx +1 -2
  63. package/src/ui/EditorEntityAction.tsx +3 -2
  64. package/src/ui/HomePageEditorCollectionAction.tsx +41 -13
  65. package/src/ui/KanbanSetupAction.tsx +38 -0
  66. package/src/ui/MissingReferenceWidget.tsx +1 -1
  67. package/src/ui/NewCollectionButton.tsx +4 -2
  68. package/src/ui/NewCollectionCard.tsx +7 -4
  69. package/src/ui/PropertyAddColumnComponent.tsx +4 -3
  70. package/src/ui/collection_editor/AICollectionGeneratorPopover.tsx +243 -0
  71. package/src/ui/collection_editor/AIModifiedPathsContext.tsx +88 -0
  72. package/src/ui/collection_editor/CollectionDetailsForm.tsx +222 -267
  73. package/src/ui/collection_editor/CollectionEditorDialog.tsx +270 -198
  74. package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +138 -71
  75. package/src/ui/collection_editor/CollectionJsonImportDialog.tsx +171 -0
  76. package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +202 -101
  77. package/src/ui/collection_editor/DisplaySettingsForm.tsx +335 -0
  78. package/src/ui/collection_editor/EntityActionsEditTab.tsx +106 -97
  79. package/src/ui/collection_editor/EntityActionsSelectDialog.tsx +8 -10
  80. package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +5 -7
  81. package/src/ui/collection_editor/EnumForm.tsx +153 -102
  82. package/src/ui/collection_editor/ExtendSettingsForm.tsx +94 -0
  83. package/src/ui/collection_editor/GeneralSettingsForm.tsx +335 -0
  84. package/src/ui/collection_editor/GetCodeDialog.tsx +63 -41
  85. package/src/ui/collection_editor/KanbanConfigSection.tsx +209 -0
  86. package/src/ui/collection_editor/LayoutModeSwitch.tsx +27 -43
  87. package/src/ui/collection_editor/PropertyEditView.tsx +272 -199
  88. package/src/ui/collection_editor/PropertyFieldPreview.tsx +1 -1
  89. package/src/ui/collection_editor/PropertyTree.tsx +130 -58
  90. package/src/ui/collection_editor/SubcollectionsEditTab.tsx +169 -163
  91. package/src/ui/collection_editor/UnsavedChangesDialog.tsx +0 -2
  92. package/src/ui/collection_editor/ViewModeSwitch.tsx +43 -0
  93. package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +6 -3
  94. package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +5 -2
  95. package/src/ui/collection_editor/properties/BlockPropertyField.tsx +0 -2
  96. package/src/ui/collection_editor/properties/BooleanPropertyField.tsx +4 -1
  97. package/src/ui/collection_editor/properties/CommonPropertyFields.tsx +6 -4
  98. package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +126 -42
  99. package/src/ui/collection_editor/properties/EnumPropertyField.tsx +32 -24
  100. package/src/ui/collection_editor/properties/MapPropertyField.tsx +8 -9
  101. package/src/ui/collection_editor/properties/MarkdownPropertyField.tsx +128 -53
  102. package/src/ui/collection_editor/properties/NumberPropertyField.tsx +3 -1
  103. package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +5 -4
  104. package/src/ui/collection_editor/properties/StoragePropertyField.tsx +47 -52
  105. package/src/ui/collection_editor/properties/StringPropertyField.tsx +3 -1
  106. package/src/ui/collection_editor/properties/UrlPropertyField.tsx +12 -10
  107. package/src/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +23 -4
  108. package/src/ui/collection_editor/properties/conditions/ConditionsEditor.tsx +866 -0
  109. package/src/ui/collection_editor/properties/conditions/ConditionsPanel.tsx +28 -0
  110. package/src/ui/collection_editor/properties/conditions/EnumConditionsEditor.tsx +599 -0
  111. package/src/ui/collection_editor/properties/conditions/index.ts +6 -0
  112. package/src/ui/collection_editor/properties/conditions/property_paths.ts +92 -0
  113. package/src/ui/collection_editor/properties/validation/ArrayPropertyValidation.tsx +5 -2
  114. package/src/ui/collection_editor/properties/validation/GeneralPropertyValidation.tsx +7 -5
  115. package/src/ui/collection_editor/properties/validation/NumberPropertyValidation.tsx +10 -7
  116. package/src/ui/collection_editor/properties/validation/StringPropertyValidation.tsx +11 -9
  117. package/src/ui/collection_editor/properties/validation/ValidationPanel.tsx +5 -2
  118. package/src/useCollectionEditorPlugin.tsx +53 -22
  119. package/src/utils/validateCollectionJson.ts +380 -0
@@ -0,0 +1,380 @@
1
+ import { EntityCollection } from "@firecms/core";
2
+
3
+ /**
4
+ * Valid dataType values for properties
5
+ */
6
+ const VALID_DATA_TYPES = [
7
+ "string",
8
+ "number",
9
+ "boolean",
10
+ "date",
11
+ "geopoint",
12
+ "reference",
13
+ "array",
14
+ "map"
15
+ ] as const;
16
+
17
+ type DataType = typeof VALID_DATA_TYPES[number];
18
+
19
+ /**
20
+ * Validation error with path and message
21
+ */
22
+ export interface CollectionValidationError {
23
+ path: string;
24
+ message: string;
25
+ }
26
+
27
+ /**
28
+ * Result of collection JSON validation
29
+ */
30
+ export interface CollectionValidationResult {
31
+ valid: boolean;
32
+ errors: CollectionValidationError[];
33
+ collection?: EntityCollection;
34
+ }
35
+
36
+ /**
37
+ * Validates a property object recursively
38
+ */
39
+ function validateProperty(
40
+ property: any,
41
+ path: string,
42
+ errors: CollectionValidationError[]
43
+ ): void {
44
+ if (typeof property !== "object" || property === null) {
45
+ errors.push({
46
+ path,
47
+ message: "Property must be an object"
48
+ });
49
+ return;
50
+ }
51
+
52
+ // Check dataType
53
+ if (!property.dataType) {
54
+ errors.push({
55
+ path: `${path}.dataType`,
56
+ message: "Required field is missing"
57
+ });
58
+ } else if (!VALID_DATA_TYPES.includes(property.dataType)) {
59
+ errors.push({
60
+ path: `${path}.dataType`,
61
+ message: `Invalid value "${property.dataType}", expected one of: ${VALID_DATA_TYPES.join(", ")}`
62
+ });
63
+ }
64
+
65
+ // Validate name if present
66
+ if (property.name !== undefined && typeof property.name !== "string") {
67
+ errors.push({
68
+ path: `${path}.name`,
69
+ message: "Must be a string"
70
+ });
71
+ }
72
+
73
+ // Validate array "of" property
74
+ if (property.dataType === "array") {
75
+ if (property.of) {
76
+ if (Array.isArray(property.of)) {
77
+ property.of.forEach((ofProp: any, index: number) => {
78
+ validateProperty(ofProp, `${path}.of[${index}]`, errors);
79
+ });
80
+ } else {
81
+ validateProperty(property.of, `${path}.of`, errors);
82
+ }
83
+ }
84
+ // oneOf validation
85
+ if (property.oneOf) {
86
+ if (typeof property.oneOf !== "object") {
87
+ errors.push({
88
+ path: `${path}.oneOf`,
89
+ message: "Must be an object"
90
+ });
91
+ } else if (property.oneOf.properties) {
92
+ validateProperties(property.oneOf.properties, `${path}.oneOf.properties`, errors);
93
+ }
94
+ }
95
+ }
96
+
97
+ // Validate map properties
98
+ if (property.dataType === "map" && property.properties) {
99
+ validateProperties(property.properties, `${path}.properties`, errors);
100
+ }
101
+
102
+ // Validate reference path
103
+ if (property.dataType === "reference") {
104
+ if (property.path !== undefined && typeof property.path !== "string") {
105
+ errors.push({
106
+ path: `${path}.path`,
107
+ message: "Must be a string"
108
+ });
109
+ }
110
+ }
111
+
112
+ // Validate storage config for string
113
+ if (property.dataType === "string" && property.storage) {
114
+ if (typeof property.storage !== "object") {
115
+ errors.push({
116
+ path: `${path}.storage`,
117
+ message: "Must be an object"
118
+ });
119
+ }
120
+ }
121
+
122
+ // Validate enumValues if present
123
+ if (property.enumValues !== undefined) {
124
+ if (!Array.isArray(property.enumValues) && typeof property.enumValues !== "object") {
125
+ errors.push({
126
+ path: `${path}.enumValues`,
127
+ message: "Must be an array or object"
128
+ });
129
+ }
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Validates a properties object (collection of property definitions)
135
+ */
136
+ function validateProperties(
137
+ properties: any,
138
+ path: string,
139
+ errors: CollectionValidationError[]
140
+ ): void {
141
+ if (typeof properties !== "object" || properties === null) {
142
+ errors.push({
143
+ path,
144
+ message: "Must be an object"
145
+ });
146
+ return;
147
+ }
148
+
149
+ for (const [key, property] of Object.entries(properties)) {
150
+ validateProperty(property, `${path}.${key}`, errors);
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Validates optional collection fields
156
+ */
157
+ function validateOptionalFields(
158
+ collection: any,
159
+ errors: CollectionValidationError[]
160
+ ): void {
161
+ // String fields
162
+ const stringFields = [
163
+ "singularName",
164
+ "description",
165
+ "group",
166
+ "databaseId"
167
+ ];
168
+ for (const field of stringFields) {
169
+ if (collection[field] !== undefined && typeof collection[field] !== "string") {
170
+ errors.push({
171
+ path: field,
172
+ message: "Must be a string"
173
+ });
174
+ }
175
+ }
176
+
177
+ // Boolean fields
178
+ const booleanFields = [
179
+ "collectionGroup",
180
+ "textSearchEnabled",
181
+ "selectionEnabled",
182
+ "inlineEditing",
183
+ "hideFromNavigation",
184
+ "hideIdFromForm",
185
+ "hideIdFromCollection",
186
+ "formAutoSave",
187
+ "editable",
188
+ "alwaysApplyDefaultValues",
189
+ "includeJsonView",
190
+ "history"
191
+ ];
192
+ for (const field of booleanFields) {
193
+ if (collection[field] !== undefined && typeof collection[field] !== "boolean") {
194
+ errors.push({
195
+ path: field,
196
+ message: "Must be a boolean"
197
+ });
198
+ }
199
+ }
200
+
201
+ // Icon can be string or object (React node)
202
+ if (collection.icon !== undefined &&
203
+ typeof collection.icon !== "string" &&
204
+ typeof collection.icon !== "object") {
205
+ errors.push({
206
+ path: "icon",
207
+ message: "Must be a string (icon key) or object"
208
+ });
209
+ }
210
+
211
+ // propertiesOrder must be array of strings
212
+ if (collection.propertiesOrder !== undefined) {
213
+ if (!Array.isArray(collection.propertiesOrder)) {
214
+ errors.push({
215
+ path: "propertiesOrder",
216
+ message: "Must be an array of strings"
217
+ });
218
+ } else if (!collection.propertiesOrder.every((item: any) => typeof item === "string")) {
219
+ errors.push({
220
+ path: "propertiesOrder",
221
+ message: "All items must be strings"
222
+ });
223
+ }
224
+ }
225
+
226
+ // subcollections must be array
227
+ if (collection.subcollections !== undefined) {
228
+ if (!Array.isArray(collection.subcollections)) {
229
+ errors.push({
230
+ path: "subcollections",
231
+ message: "Must be an array"
232
+ });
233
+ } else {
234
+ collection.subcollections.forEach((sub: any, index: number) => {
235
+ const subErrors: CollectionValidationError[] = [];
236
+ validateCollectionObject(sub, subErrors);
237
+ subErrors.forEach(err => {
238
+ errors.push({
239
+ path: `subcollections[${index}].${err.path}`,
240
+ message: err.message
241
+ });
242
+ });
243
+ });
244
+ }
245
+ }
246
+
247
+ // defaultViewMode validation
248
+ const validViewModes = ["table", "cards", "kanban"];
249
+ if (collection.defaultViewMode !== undefined) {
250
+ if (!validViewModes.includes(collection.defaultViewMode)) {
251
+ errors.push({
252
+ path: "defaultViewMode",
253
+ message: `Invalid value, expected one of: ${validViewModes.join(", ")}`
254
+ });
255
+ }
256
+ }
257
+
258
+ // kanban config validation
259
+ if (collection.kanban !== undefined) {
260
+ if (typeof collection.kanban !== "object" || collection.kanban === null) {
261
+ errors.push({
262
+ path: "kanban",
263
+ message: "Must be an object"
264
+ });
265
+ } else if (collection.kanban.columnProperty !== undefined &&
266
+ typeof collection.kanban.columnProperty !== "string") {
267
+ errors.push({
268
+ path: "kanban.columnProperty",
269
+ message: "Must be a string"
270
+ });
271
+ }
272
+ }
273
+ }
274
+
275
+ /**
276
+ * Validates a collection object
277
+ */
278
+ function validateCollectionObject(
279
+ collection: any,
280
+ errors: CollectionValidationError[]
281
+ ): void {
282
+ // Required fields
283
+ if (!collection.id) {
284
+ errors.push({
285
+ path: "id",
286
+ message: "Required field is missing"
287
+ });
288
+ } else if (typeof collection.id !== "string") {
289
+ errors.push({
290
+ path: "id",
291
+ message: "Must be a string"
292
+ });
293
+ }
294
+
295
+ if (!collection.name) {
296
+ errors.push({
297
+ path: "name",
298
+ message: "Required field is missing"
299
+ });
300
+ } else if (typeof collection.name !== "string") {
301
+ errors.push({
302
+ path: "name",
303
+ message: "Must be a string"
304
+ });
305
+ }
306
+
307
+ if (!collection.path) {
308
+ errors.push({
309
+ path: "path",
310
+ message: "Required field is missing"
311
+ });
312
+ } else if (typeof collection.path !== "string") {
313
+ errors.push({
314
+ path: "path",
315
+ message: "Must be a string"
316
+ });
317
+ }
318
+
319
+ // Properties validation
320
+ if (collection.properties !== undefined) {
321
+ validateProperties(collection.properties, "properties", errors);
322
+ }
323
+
324
+ // Optional fields
325
+ validateOptionalFields(collection, errors);
326
+ }
327
+
328
+ /**
329
+ * Validates a JSON string representing a collection configuration.
330
+ * Returns detailed validation errors if the JSON is invalid or doesn't match
331
+ * the expected collection schema.
332
+ */
333
+ export function validateCollectionJson(jsonString: string): CollectionValidationResult {
334
+ const errors: CollectionValidationError[] = [];
335
+
336
+ // Try to parse JSON
337
+ let parsed: any;
338
+ try {
339
+ parsed = JSON.parse(jsonString);
340
+ } catch (e: any) {
341
+ // Try to extract line/column info from the error
342
+ const match = e.message.match(/position (\d+)/);
343
+ let message = "Invalid JSON syntax";
344
+ if (match) {
345
+ const position = parseInt(match[1], 10);
346
+ const lines = jsonString.substring(0, position).split("\n");
347
+ const line = lines.length;
348
+ const column = lines[lines.length - 1].length + 1;
349
+ message = `Invalid JSON syntax at line ${line}, column ${column}: ${e.message}`;
350
+ } else {
351
+ message = `Invalid JSON syntax: ${e.message}`;
352
+ }
353
+ return {
354
+ valid: false,
355
+ errors: [{
356
+ path: "",
357
+ message
358
+ }]
359
+ };
360
+ }
361
+
362
+ // Validate collection structure
363
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
364
+ return {
365
+ valid: false,
366
+ errors: [{
367
+ path: "",
368
+ message: "Collection must be an object"
369
+ }]
370
+ };
371
+ }
372
+
373
+ validateCollectionObject(parsed, errors);
374
+
375
+ return {
376
+ valid: errors.length === 0,
377
+ errors,
378
+ collection: errors.length === 0 ? parsed as EntityCollection : undefined
379
+ };
380
+ }